4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
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)
23 * Do not use directly - it does not do anything..
24 * @param {Object} config The config object
29 Roo.bootstrap.Component = function(config){
30 Roo.bootstrap.Component.superclass.constructor.call(this, config);
34 * @event childrenrendered
35 * Fires when the children have been rendered..
36 * @param {Roo.bootstrap.Component} this
38 "childrenrendered" : true
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
50 allowDomMove : false, // to stop relocations in parent onRender...
60 * Initialize Events for the element
62 initEvents : function() { },
68 can_build_overlaid : true,
70 container_method : false,
77 // returns the parent component..
78 return Roo.ComponentMgr.get(this.parentId)
84 onRender : function(ct, position)
86 // Roo.log("Call onRender: " + this.xtype);
88 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
91 if (this.el.attr('xtype')) {
92 this.el.attr('xtypex', this.el.attr('xtype'));
93 this.el.dom.removeAttribute('xtype');
103 var cfg = Roo.apply({}, this.getAutoCreate());
105 cfg.id = this.id || Roo.id();
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];
115 cfg.dataId = this.dataId;
119 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
122 if (this.style) { // fixme needs to support more complex style data.
123 cfg.style = this.style;
127 cfg.name = this.name;
130 this.el = ct.createChild(cfg, position);
133 this.tooltipEl().attr('tooltip', this.tooltip);
136 if(this.tabIndex !== undefined){
137 this.el.dom.setAttribute('tabIndex', this.tabIndex);
144 * Fetch the element to add children to
145 * @return {Roo.Element} defaults to this.el
147 getChildContainer : function()
152 * Fetch the element to display the tooltip on.
153 * @return {Roo.Element} defaults to this.el
155 tooltipEl : function()
160 addxtype : function(tree,cntr)
164 cn = Roo.factory(tree);
165 //Roo.log(['addxtype', cn]);
167 cn.parentType = this.xtype; //??
168 cn.parentId = this.id;
170 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171 if (typeof(cn.container_method) == 'string') {
172 cntr = cn.container_method;
176 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
178 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
180 var build_from_html = Roo.XComponent.build_from_html;
182 var is_body = (tree.xtype == 'Body') ;
184 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186 var self_cntr_el = Roo.get(this[cntr](false));
188 // do not try and build conditional elements
189 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
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);
198 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
201 return this.addxtypeChild(Roo.apply({}, tree),cntr);
204 Roo.log('skipping render');
210 if (!build_from_html) {
214 // this i think handles overlaying multiple children of the same type
215 // with the sam eelement.. - which might be buggy..
217 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
223 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
227 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
234 addxtypeChild : function (tree, cntr, is_body)
236 Roo.debug && Roo.log('addxtypeChild:' + cntr);
238 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
241 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242 (typeof(tree['flexy:foreach']) != 'undefined');
246 skip_children = false;
247 // render the element if it's not BODY.
250 // if parent was disabled, then do not try and create the children..
251 if(!this[cntr](true)){
256 cn = Roo.factory(tree);
258 cn.parentType = this.xtype; //??
259 cn.parentId = this.id;
261 var build_from_html = Roo.XComponent.build_from_html;
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') {
270 var self_cntr_el = Roo.get(this[cntr](false));
271 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
273 //Roo.log(Roo.XComponent.build_from_html);
274 //Roo.log("got echild:");
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.
282 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
283 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
289 //echild.dom.removeAttribute('xtype');
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);
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);
306 Roo.debug && Roo.log('skipping all children');
307 skip_children = true;
312 // actually if flexy:foreach is found, we really want to create
313 // multiple copies here...
315 //Roo.log(this[cntr]());
316 // some elements do not have render methods.. like the layouts...
318 if(this[cntr](true) === false){
323 cn.render && cn.render(this[cntr](true));
326 // then add the element..
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)));
340 if (!tree.items || !tree.items.length) {
342 //Roo.log(["no children", this]);
347 var items = tree.items;
350 //Roo.log(items.length);
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])));
361 //Roo.log("fire childrenrendered");
363 cn.fireEvent('childrenrendered', this);
368 * Show a component - removes 'hidden' class
373 this.el.removeClass('hidden');
377 * Hide a component - adds 'hidden' class
381 if (this.el && !this.el.hasClass('hidden')) {
382 this.el.addClass('hidden');
395 * @class Roo.bootstrap.Body
396 * @extends Roo.bootstrap.Component
397 * Bootstrap Body class
401 * @param {Object} config The config object
404 Roo.bootstrap.Body = function(config){
406 config = config || {};
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);
415 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
417 is_body : true,// just to make sure it's constructed?
422 onRender : function(ct, position)
424 /* Roo.log("Roo.bootstrap.Body - onRender");
425 if (this.cls && this.cls.length) {
426 Roo.get(document.body).addClass(this.cls);
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
457 * @param {Object} config The config object
460 Roo.bootstrap.ButtonGroup = function(config){
461 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
464 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
472 getAutoCreate : function(){
478 cfg.html = this.html || cfg.html;
489 if (['vertical','justified'].indexOf(this.align)!==-1) {
490 cfg.cls = 'btn-group-' + this.align;
492 if (this.align == 'justified') {
493 console.log(this.items);
497 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
498 cfg.cls += ' btn-group-' + this.size;
501 if (this.direction == 'up') {
502 cfg.cls += ' dropup' ;
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)
541 * Create a new button
542 * @param {Object} config The config object
546 Roo.bootstrap.Button = function(config){
547 Roo.bootstrap.Button.superclass.constructor.call(this, config);
548 this.weightClass = ["btn-default",
560 * When a butotn is pressed
561 * @param {Roo.bootstrap.Button} this
562 * @param {Roo.EventObject} e
567 * After the button has been toggles
568 * @param {Roo.EventObject} e
569 * @param {boolean} pressed (also available as button.pressed)
575 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
593 preventDefault: true,
602 getAutoCreate : function(){
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.";
616 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
618 if (this.toggle == true) {
621 cls: 'slider-frame roo-button',
626 'data-off-text':'OFF',
627 cls: 'slider-button',
633 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
634 cfg.cls += ' '+this.weight;
643 cfg["aria-hidden"] = true;
645 cfg.html = "×";
651 if (this.theme==='default') {
652 cfg.cls = 'btn roo-button';
654 //if (this.parentType != 'Navbar') {
655 this.weight = this.weight.length ? this.weight : 'default';
657 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
659 cfg.cls += ' btn-' + this.weight;
661 } else if (this.theme==='glow') {
664 cfg.cls = 'btn-glow roo-button';
666 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
668 cfg.cls += ' ' + this.weight;
674 this.cls += ' inverse';
679 cfg.cls += ' active';
683 cfg.disabled = 'disabled';
687 Roo.log('changing to ul' );
689 this.glyphicon = 'caret';
692 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
694 //gsRoo.log(this.parentType);
695 if (this.parentType === 'Navbar' && !this.parent().bar) {
696 Roo.log('changing to li?');
705 href : this.href || '#'
708 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
709 cfg.cls += ' dropdown';
716 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
718 if (this.glyphicon) {
719 cfg.html = ' ' + cfg.html;
724 cls: 'glyphicon glyphicon-' + this.glyphicon
734 // cfg.cls='btn roo-button';
738 var value = cfg.html;
743 cls: 'glyphicon glyphicon-' + this.glyphicon,
762 cfg.cls += ' dropdown';
763 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
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;
772 if(this.removeClass){
777 cfg.target = this.target;
782 initEvents: function() {
783 // Roo.log('init events?');
784 // Roo.log(this.el.dom);
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));
794 if (this.el.hasClass('roo-button')) {
795 this.el.on('click', this.onClick, this);
797 this.el.select('.roo-button').on('click', this.onClick, this);
800 if(this.removeClass){
801 this.el.on('click', this.onClick, this);
804 this.el.enableDisplayMode();
807 onClick : function(e)
814 Roo.log('button on click ');
815 if(this.preventDefault){
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);
825 this.fireEvent('click', this, e);
829 * Enables this button
833 this.disabled = false;
834 this.el.removeClass('disabled');
838 * Disable this button
842 this.disabled = true;
843 this.el.addClass('disabled');
846 * sets the active state on/off,
847 * @param {Boolean} state (optional) Force a particular state
849 setActive : function(v) {
851 this.el[v ? 'addClass' : 'removeClass']('active');
854 * toggles the current active state
856 toggleActive : function()
858 var active = this.el.hasClass('active');
859 this.setActive(!active);
863 setText : function(str)
865 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
869 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
880 setWeight : function(str)
882 this.el.removeClass(this.weightClass);
883 this.el.addClass('btn-' + str);
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
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
915 * @cfg {String} icon (info-sign|check|...) glyphicon name
917 * @cfg {String} html content of column.
920 * Create a new Column
921 * @param {Object} config The config object
924 Roo.bootstrap.Column = function(config){
925 Roo.bootstrap.Column.superclass.constructor.call(this, config);
928 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
946 getAutoCreate : function(){
947 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
955 ['xs','sm','md','lg'].map(function(size){
956 //Roo.log( size + ':' + settings[size]);
958 if (settings[size+'off'] !== false) {
959 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
962 if (settings[size] === false) {
966 if (!settings[size]) { // 0 = hidden
967 cfg.cls += ' hidden-' + size;
970 cfg.cls += ' col-' + size + '-' + settings[size];
975 cfg.cls += ' hidden';
978 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
979 cfg.cls +=' alert alert-' + this.alert;
983 if (this.html.length) {
984 cfg.html = this.html;
988 if (this.fasize > 1) {
989 fasize = ' fa-' + this.fasize + 'x';
991 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
996 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
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
1037 * Create a new Container
1038 * @param {Object} config The config object
1041 Roo.bootstrap.Container = function(config){
1042 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1048 * After the panel has been expand
1050 * @param {Roo.bootstrap.Container} this
1055 * After the panel has been collapsed
1057 * @param {Roo.bootstrap.Container} this
1062 * When a element is chick
1063 * @param {Roo.bootstrap.Container} this
1064 * @param {Roo.EventObject} e
1070 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1088 getChildContainer : function() {
1094 if (this.panel.length) {
1095 return this.el.select('.panel-body',true).first();
1102 getAutoCreate : function(){
1105 tag : this.tag || 'div',
1109 if (this.jumbotron) {
1110 cfg.cls = 'jumbotron';
1115 // - this is applied by the parent..
1117 // cfg.cls = this.cls + '';
1120 if (this.sticky.length) {
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%');
1128 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1132 if (this.well.length) {
1133 switch (this.well) {
1136 cfg.cls +=' well well-' +this.well;
1145 cfg.cls += ' hidden';
1149 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1150 cfg.cls +=' alert alert-' + this.alert;
1155 if (this.panel.length) {
1156 cfg.cls += ' panel panel-' + this.panel;
1158 if (this.header.length) {
1162 if(this.expandable){
1164 cfg.cls = cfg.cls + ' expandable';
1168 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1176 cls : 'panel-title',
1177 html : (this.expandable ? ' ' : '') + this.header
1181 cls: 'panel-header-right',
1187 cls : 'panel-heading',
1188 style : this.expandable ? 'cursor: pointer' : '',
1196 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1201 if (this.footer.length) {
1203 cls : 'panel-footer',
1212 body.html = this.html || cfg.html;
1213 // prefix with the icons..
1215 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1218 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1223 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1224 cfg.cls = 'container';
1230 initEvents: function()
1232 if(this.expandable){
1233 var headerEl = this.headerEl();
1236 headerEl.on('click', this.onToggleClick, this);
1241 this.el.on('click', this.onClick, this);
1246 onToggleClick : function()
1248 var headerEl = this.headerEl();
1264 if(this.fireEvent('expand', this)) {
1266 this.expanded = true;
1268 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1270 this.el.select('.panel-body',true).first().removeClass('hide');
1272 var toggleEl = this.toggleEl();
1278 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1283 collapse : function()
1285 if(this.fireEvent('collapse', this)) {
1287 this.expanded = false;
1289 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1290 this.el.select('.panel-body',true).first().addClass('hide');
1292 var toggleEl = this.toggleEl();
1298 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1302 toggleEl : function()
1304 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1308 return this.el.select('.panel-heading .fa',true).first();
1311 headerEl : function()
1313 if(!this.el || !this.panel.length || !this.header.length){
1317 return this.el.select('.panel-heading',true).first()
1322 if(!this.el || !this.panel.length){
1326 return this.el.select('.panel-body',true).first()
1329 titleEl : function()
1331 if(!this.el || !this.panel.length || !this.header.length){
1335 return this.el.select('.panel-title',true).first();
1338 setTitle : function(v)
1340 var titleEl = this.titleEl();
1346 titleEl.dom.innerHTML = v;
1349 getTitle : function()
1352 var titleEl = this.titleEl();
1358 return titleEl.dom.innerHTML;
1361 setRightTitle : function(v)
1363 var t = this.el.select('.panel-header-right',true).first();
1369 t.dom.innerHTML = v;
1372 onClick : function(e)
1376 this.fireEvent('click', this, e);
1379 allChildren : function()
1381 var r=new Roo.util.MixedCollection(false, function(o){
1382 return o.id || (o.id = Roo.id());
1384 var iter = function(el) {
1391 Roo.each(el.items,function(e) {
1400 checkEmpty : function()
1402 var items = this.allChildren();
1405 items.each(function(f){
1406 if(f.el.isVisible()) {
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
1440 * Create a new Input
1441 * @param {Object} config The config object
1444 Roo.bootstrap.Img = function(config){
1445 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1451 * The img click event for the img.
1452 * @param {Roo.EventObject} e
1458 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1460 imgResponsive: true,
1470 getAutoCreate : function()
1472 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1473 return this.createSingleImg();
1478 cls: 'roo-image-responsive-group',
1483 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1485 if(!_this[size + 'Url']){
1491 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1492 html: _this.html || cfg.html,
1493 src: _this[size + 'Url']
1496 img.cls += ' roo-image-responsive-' + size;
1498 var s = ['xs', 'sm', 'md', 'lg'];
1500 s.splice(s.indexOf(size), 1);
1502 Roo.each(s, function(ss){
1503 img.cls += ' hidden-' + ss;
1506 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1507 cfg.cls += ' img-' + _this.border;
1511 cfg.alt = _this.alt;
1524 a.target = _this.target;
1528 cfg.cn.push((_this.href) ? a : img);
1535 createSingleImg : function()
1539 cls: (this.imgResponsive) ? 'img-responsive' : '',
1541 src : 'about:blank' // just incase src get's set to undefined?!?
1544 cfg.html = this.html || cfg.html;
1546 cfg.src = this.src || cfg.src;
1548 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1549 cfg.cls += ' img-' + this.border;
1566 a.target = this.target;
1571 return (this.href) ? a : cfg;
1574 initEvents: function()
1577 this.el.on('click', this.onClick, this);
1582 onClick : function(e)
1584 Roo.log('img onclick');
1585 this.fireEvent('click', this, e);
1588 * Sets the url of the image - used to update it
1589 * @param {String} url the url of the image
1592 setSrc : function(url)
1596 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1597 this.el.dom.src = url;
1601 this.el.select('img', true).first().dom.src = url;
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
1627 * @cfg {Boolean} preventDefault (true | false) default false
1631 * Create a new Input
1632 * @param {Object} config The config object
1635 Roo.bootstrap.Link = function(config){
1636 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1642 * The img click event for the img.
1643 * @param {Roo.EventObject} e
1649 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1653 preventDefault: false,
1659 getAutoCreate : function()
1661 var html = this.html || '';
1663 if (this.fa !== false) {
1664 html = '<i class="fa fa-' + this.fa + '"></i>';
1669 // anchor's do not require html/href...
1670 if (this.anchor === false) {
1672 cfg.href = this.href || '#';
1674 cfg.name = this.anchor;
1675 if (this.html !== false || this.fa !== false) {
1678 if (this.href !== false) {
1679 cfg.href = this.href;
1683 if(this.alt !== false){
1688 if(this.target !== false) {
1689 cfg.target = this.target;
1695 initEvents: function() {
1697 if(!this.href || this.preventDefault){
1698 this.el.on('click', this.onClick, this);
1702 onClick : function(e)
1704 if(this.preventDefault){
1707 //Roo.log('img onclick');
1708 this.fireEvent('click', this, e);
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
1728 * Create a new Header
1729 * @param {Object} config The config object
1733 Roo.bootstrap.Header = function(config){
1734 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1737 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1745 getAutoCreate : function(){
1750 tag: 'h' + (1 *this.level),
1751 html: this.html || ''
1763 * Ext JS Library 1.1.1
1764 * Copyright(c) 2006-2007, Ext JS, LLC.
1766 * Originally Released Under LGPL - original licence link has changed is not relivant.
1769 * <script type="text/javascript">
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.
1777 Roo.bootstrap.MenuMgr = function(){
1778 var menus, active, groups = {}, attached = false, lastShow = new Date();
1780 // private - called when first menu is created
1783 active = new Roo.util.MixedCollection();
1784 Roo.get(document).addKeyListener(27, function(){
1785 if(active.length > 0){
1793 if(active && active.length > 0){
1794 var c = active.clone();
1804 if(active.length < 1){
1805 Roo.get(document).un("mouseup", onMouseDown);
1813 var last = active.last();
1814 lastShow = new Date();
1817 Roo.get(document).on("mouseup", onMouseDown);
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);
1830 function onBeforeHide(m){
1832 m.activeChild.hide();
1834 if(m.autoHideTimer){
1835 clearTimeout(m.autoHideTimer);
1836 delete m.autoHideTimer;
1841 function onBeforeShow(m){
1842 var pm = m.parentMenu;
1843 if(!pm && !m.allowOtherMenus){
1845 }else if(pm && pm.activeChild && active != m){
1846 pm.activeChild.hide();
1850 // private this should really trigger on mouseup..
1851 function onMouseDown(e){
1852 Roo.log("on Mouse Up");
1854 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1855 Roo.log("MenuManager hideAll");
1864 function onBeforeCheck(mi, state){
1866 var g = groups[mi.group];
1867 for(var i = 0, l = g.length; i < l; i++){
1869 g[i].setChecked(false);
1878 * Hides all menus that are currently visible
1880 hideAll : function(){
1885 register : function(menu){
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);
1895 if(g && menu.events["checkchange"]){
1899 groups[g].push(menu);
1900 menu.on("checkchange", onCheck);
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.
1909 get : function(menu){
1910 if(typeof menu == "string"){ // menu id
1912 }else if(menu.events){ // menu instance
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);
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);
1932 if(g && menu.events["checkchange"]){
1933 groups[g].remove(menu);
1934 menu.un("checkchange", onCheck);
1939 registerCheckable : function(menuItem){
1940 var g = menuItem.group;
1945 groups[g].push(menuItem);
1946 menuItem.on("beforecheckchange", onBeforeCheck);
1951 unregisterCheckable : function(menuItem){
1952 var g = menuItem.group;
1954 groups[g].remove(menuItem);
1955 menuItem.un("beforecheckchange", onBeforeCheck);
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)
1977 * @param {Object} config The config object
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);
1989 * Fires before this menu is displayed
1990 * @param {Roo.menu.Menu} this
1995 * Fires before this menu is hidden
1996 * @param {Roo.menu.Menu} this
2001 * Fires after this menu is displayed
2002 * @param {Roo.menu.Menu} this
2007 * Fires after this menu is hidden
2008 * @param {Roo.menu.Menu} this
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
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
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
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
2043 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2046 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2050 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2053 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2055 registerMenu : true,
2057 menuItems :false, // stores the menu items..
2067 getChildContainer : function() {
2071 getAutoCreate : function(){
2073 //if (['right'].indexOf(this.align)!==-1) {
2074 // cfg.cn[1].cls += ' pull-right'
2080 cls : 'dropdown-menu' ,
2081 style : 'z-index:1000'
2085 if (this.type === 'submenu') {
2086 cfg.cls = 'submenu active';
2088 if (this.type === 'treeview') {
2089 cfg.cls = 'treeview-menu';
2094 initEvents : function() {
2096 // Roo.log("ADD event");
2097 // Roo.log(this.triggerEl.dom);
2099 this.triggerEl.on('click', this.onTriggerClick, this);
2101 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2103 this.triggerEl.addClass('dropdown-toggle');
2106 this.el.on('touchstart' , this.onTouch, this);
2108 this.el.on('click' , this.onClick, this);
2110 this.el.on("mouseover", this.onMouseOver, this);
2111 this.el.on("mouseout", this.onMouseOut, this);
2115 findTargetItem : function(e)
2117 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2121 //Roo.log(t); Roo.log(t.id);
2123 //Roo.log(this.menuitems);
2124 return this.menuitems.get(t.id);
2126 //return this.items.get(t.menuItemId);
2132 onTouch : function(e)
2134 Roo.log("menu.onTouch");
2135 //e.stopEvent(); this make the user popdown broken
2139 onClick : function(e)
2141 Roo.log("menu.onClick");
2143 var t = this.findTargetItem(e);
2144 if(!t || t.isContainer){
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;
2156 this.setActiveItem(t, true);
2164 Roo.log('pass click event');
2168 this.fireEvent("click", this, t, e);
2172 if(!t.href.length || t.href == '#'){
2173 (function() { _this.hide(); }).defer(100);
2178 onMouseOver : function(e){
2179 var t = this.findTargetItem(e);
2182 // if(t.canActivate && !t.disabled){
2183 // this.setActiveItem(t, true);
2187 this.fireEvent("mouseover", this, e, t);
2189 isVisible : function(){
2190 return !this.hidden;
2192 onMouseOut : function(e){
2193 var t = this.findTargetItem(e);
2196 // if(t == this.activeItem && t.shouldDeactivate(e)){
2197 // this.activeItem.deactivate();
2198 // delete this.activeItem;
2201 this.fireEvent("mouseout", this, e, t);
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)
2212 show : function(el, pos, parentMenu){
2213 this.parentMenu = parentMenu;
2217 this.fireEvent("beforeshow", this);
2218 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
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)
2225 showAt : function(xy, parentMenu, /* private: */_e){
2226 this.parentMenu = parentMenu;
2231 this.fireEvent("beforeshow", this);
2232 //xy = this.el.adjustForConstraints(xy);
2236 this.hideMenuItems();
2237 this.hidden = false;
2238 this.triggerEl.addClass('open');
2240 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2241 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2244 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2249 this.fireEvent("show", this);
2255 this.doFocus.defer(50, this);
2259 doFocus : function(){
2261 this.focusEl.focus();
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)
2269 hide : function(deep)
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;
2279 this.triggerEl.removeClass('open');;
2281 this.fireEvent("hide", this);
2283 if(deep === true && this.parentMenu){
2284 this.parentMenu.hide(true);
2288 onTriggerClick : function(e)
2290 Roo.log('trigger click');
2292 var target = e.getTarget();
2294 Roo.log(target.nodeName.toLowerCase());
2296 if(target.nodeName.toLowerCase() === 'i'){
2302 onTriggerPress : function(e)
2304 Roo.log('trigger press');
2305 //Roo.log(e.getTarget());
2306 // Roo.log(this.triggerEl.dom);
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?');
2315 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2319 if (this.isVisible()) {
2324 this.show(this.triggerEl, false, false);
2327 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2334 hideMenuItems : function()
2336 Roo.log("hide Menu Items");
2340 //$(backdrop).remove()
2341 this.el.select('.open',true).each(function(aa) {
2343 aa.removeClass('open');
2344 //var parent = getParent($(this))
2345 //var relatedTarget = { relatedTarget: this }
2347 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2348 //if (e.isDefaultPrevented()) return
2349 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2352 addxtypeChild : function (tree, cntr) {
2353 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2355 this.menuitems.add(comp);
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.
2389 * Create a new MenuItem
2390 * @param {Object} config The config object
2394 Roo.bootstrap.MenuItem = function(config){
2395 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2400 * The raw click event for the entire grid.
2401 * @param {Roo.bootstrap.MenuItem} this
2402 * @param {Roo.EventObject} e
2408 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2412 preventDefault: false,
2413 isContainer : false,
2417 getAutoCreate : function(){
2419 if(this.isContainer){
2422 cls: 'dropdown-menu-item'
2436 if (this.fa !== false) {
2439 cls : 'fa fa-' + this.fa
2448 cls: 'dropdown-menu-item',
2451 if (this.parent().type == 'treeview') {
2452 cfg.cls = 'treeview-menu';
2455 cfg.cls += ' active';
2460 anc.href = this.href || cfg.cn[0].href ;
2461 ctag.html = this.html || cfg.cn[0].html ;
2465 initEvents: function()
2467 if (this.parent().type == 'treeview') {
2468 this.el.select('a').on('click', this.onClick, this);
2472 this.menu.parentType = this.xtype;
2473 this.menu.triggerEl = this.el;
2474 this.menu = this.addxtype(Roo.apply({}, this.menu));
2478 onClick : function(e)
2480 Roo.log('item on click ');
2482 if(this.preventDefault){
2485 //this.parent().hideMenuItems();
2487 this.fireEvent('click', this, e);
2506 * @class Roo.bootstrap.MenuSeparator
2507 * @extends Roo.bootstrap.Component
2508 * Bootstrap MenuSeparator class
2511 * Create a new MenuItem
2512 * @param {Object} config The config object
2516 Roo.bootstrap.MenuSeparator = function(config){
2517 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2520 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2522 getAutoCreate : function(){
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
2557 * Create a new Modal Dialog
2558 * @param {Object} config The config object
2561 Roo.bootstrap.Modal = function(config){
2562 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2567 * The raw btnclick event for the button
2568 * @param {Roo.EventObject} e
2573 * Fire when dialog resize
2574 * @param {Roo.bootstrap.Modal} this
2575 * @param {Roo.EventObject} e
2579 this.buttons = this.buttons || [];
2582 this.tmpl = Roo.factory(this.tmpl);
2587 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2589 title : 'test dialog',
2599 specificTitle: false,
2601 buttonPosition: 'right',
2620 onRender : function(ct, position)
2622 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2625 var cfg = Roo.apply({}, this.getAutoCreate());
2628 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2630 //if (!cfg.name.length) {
2634 cfg.cls += ' ' + this.cls;
2637 cfg.style = this.style;
2639 this.el = Roo.get(document.body).createChild(cfg, position);
2641 //var type = this.el.dom.type;
2644 if(this.tabIndex !== undefined){
2645 this.el.dom.setAttribute('tabIndex', this.tabIndex);
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();
2655 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2656 this.maskEl.enableDisplayMode("block");
2658 //this.el.addClass("x-dlg-modal");
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) };
2669 var btn = Roo.factory(b);
2671 btn.render(this.el.select('.modal-footer div').first());
2675 // render the children.
2678 if(typeof(this.items) != 'undefined'){
2679 var items = this.items;
2682 for(var i =0;i < items.length;i++) {
2683 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2687 this.items = nitems;
2689 // where are these used - they used to be body/close/footer
2693 //this.el.addClass([this.fieldClass, this.cls]);
2697 getAutoCreate : function(){
2702 html : this.html || ''
2707 cls : 'modal-title',
2711 if(this.specificTitle){
2717 if (this.allow_close) {
2729 if(this.size.length){
2730 size = 'modal-' + this.size;
2735 style : 'display: none',
2738 cls: "modal-dialog " + size,
2741 cls : "modal-content",
2744 cls : 'modal-header',
2749 cls : 'modal-footer',
2753 cls: 'btn-' + this.buttonPosition
2770 modal.cls += ' fade';
2776 getChildContainer : function() {
2781 getButtonContainer : function() {
2782 return this.el.select('.modal-footer div',true).first();
2785 initEvents : function()
2787 if (this.allow_close) {
2788 this.closeEl.on('click', this.hide, this);
2790 Roo.EventManager.onWindowResize(this.resize, this, true);
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;
2805 setSize : function(w,h)
2815 if (!this.rendered) {
2819 this.el.setStyle('display', 'block');
2821 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2824 this.el.addClass('in');
2827 this.el.addClass('in');
2831 // not sure how we can show data in here..
2833 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2836 Roo.get(document.body).addClass("x-body-masked");
2838 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2839 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2844 this.fireEvent('show', this);
2846 // set zindex here - otherwise it appears to be ignored...
2847 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2850 this.items.forEach( function(e) {
2851 e.layout ? e.layout() : false;
2859 if(this.fireEvent("beforehide", this) !== false){
2861 Roo.get(document.body).removeClass("x-body-masked");
2862 this.el.removeClass('in');
2863 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2865 if(this.animate){ // why
2867 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2869 this.el.setStyle('display', 'none');
2871 this.fireEvent('hide', this);
2875 addButton : function(str, cb)
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) };
2886 var btn = Roo.factory(b);
2888 btn.render(this.el.select('.modal-footer div').first());
2894 setDefaultButton : function(btn)
2896 //this.el.select('.modal-footer').()
2900 resizeTo: function(w,h)
2904 this.dialogEl.setWidth(w);
2905 if (this.diff === false) {
2906 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2909 this.bodyEl.setHeight(h-this.diff);
2911 this.fireEvent('resize', this);
2914 setContentSize : function(w, h)
2918 onButtonClick: function(btn,e)
2921 this.fireEvent('btnclick', btn.name, e);
2924 * Set the title of the Dialog
2925 * @param {String} str new Title
2927 setTitle: function(str) {
2928 this.titleEl.dom.innerHTML = str;
2931 * Set the body of the Dialog
2932 * @param {String} str new Title
2934 setBody: function(str) {
2935 this.bodyEl.dom.innerHTML = str;
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.
2941 applyBody: function(obj)
2944 Roo.log("Error - using apply Body without a template");
2947 this.tmpl.overwrite(this.bodyEl, obj);
2953 Roo.apply(Roo.bootstrap.Modal, {
2955 * Button config that displays a single OK button
2964 * Button config that displays Yes and No buttons
2980 * Button config that displays OK and Cancel buttons
2995 * Button config that displays Yes, No and Cancel buttons
3019 * messagebox - can be used as a replace
3023 * @class Roo.MessageBox
3024 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3028 Roo.Msg.alert('Status', 'Changes saved successfully.');
3030 // Prompt for user data:
3031 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3033 // process text value...
3037 // Show a dialog using config options:
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,
3048 Roo.bootstrap.MessageBox = function(){
3049 var dlg, opt, mask, waitTimer;
3050 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3051 var buttons, activeTextEl, bwidth;
3055 var handleButton = function(button){
3057 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3061 var handleHide = function(){
3063 dlg.el.removeClass(opt.cls);
3066 // Roo.TaskMgr.stop(waitTimer);
3067 // waitTimer = null;
3072 var updateButtons = function(b){
3075 buttons["ok"].hide();
3076 buttons["cancel"].hide();
3077 buttons["yes"].hide();
3078 buttons["no"].hide();
3079 //dlg.footer.dom.style.display = 'none';
3082 dlg.footerEl.dom.style.display = '';
3083 for(var k in buttons){
3084 if(typeof buttons[k] != "function"){
3087 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3088 width += buttons[k].el.getWidth()+15;
3098 var handleEsc = function(d, k, e){
3099 if(opt && opt.closable !== false){
3109 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3110 * @return {Roo.BasicDialog} The BasicDialog element
3112 getDialog : function(){
3114 dlg = new Roo.bootstrap.Modal( {
3117 //constraintoviewport:false,
3119 //collapsible : false,
3124 //buttonAlign:"center",
3125 closeClick : function(){
3126 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3129 handleButton("cancel");
3134 dlg.on("hide", handleHide);
3136 //dlg.addKeyListener(27, handleEsc);
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"));
3145 bodyEl = dlg.bodyEl.createChild({
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"> </div></div></div>'
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){
3158 }else if(opt.buttons.yes){
3159 handleButton("yes");
3163 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3164 textareaEl.enableDisplayMode();
3165 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3166 progressEl.enableDisplayMode();
3168 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3169 var pf = progressEl.dom.firstChild;
3171 pp = Roo.get(pf.firstChild);
3172 pp.setHeight(pf.offsetHeight);
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 '&#160;')
3183 * @return {Roo.MessageBox} This message box
3185 updateText : function(text)
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()
3191 msgEl.innerHTML = text || ' ';
3193 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3194 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3196 Math.min(opt.width || cw , this.maxWidth),
3197 Math.max(opt.minWidth || this.minWidth, bwidth)
3200 activeTextEl.setWidth(w);
3202 if(dlg.isVisible()){
3203 dlg.fixedcenter = false;
3205 // to big, make it scroll. = But as usual stupid IE does not support
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');
3212 bodyEl.dom.style.height = '';
3213 bodyEl.dom.style.overflowY = '';
3216 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3218 bodyEl.dom.style.overflowX = '';
3221 dlg.setContentSize(w, bodyEl.getHeight());
3222 if(dlg.isVisible()){
3223 dlg.fixedcenter = true;
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
3235 updateProgress : function(value, text){
3237 this.updateText(text);
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));
3248 * Returns true if the message box is currently displayed
3249 * @return {Boolean} True if the message box is visible, else false
3251 isVisible : function(){
3252 return dlg && dlg.isVisible();
3256 * Hides the message box if it is displayed
3259 if(this.isVisible()){
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:
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 ' ')
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
3310 msg: 'Please enter your address:',
3312 buttons: Roo.MessageBox.OKCANCEL,
3315 animEl: 'addAddressBtn'
3318 * @param {Object} config Configuration options
3319 * @return {Roo.MessageBox} This message box
3321 show : function(options)
3324 // this causes nightmares if you show one dialog after another
3325 // especially on callbacks..
3327 if(this.isVisible()){
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";
3337 var d = this.getDialog();
3339 d.setTitle(opt.title || " ");
3340 d.closeEl.setDisplayed(opt.closable !== false);
3341 activeTextEl = textboxEl;
3342 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3347 textareaEl.setHeight(typeof opt.multiline == "number" ?
3348 opt.multiline : this.defaultTextHeight);
3349 activeTextEl = textareaEl;
3358 progressEl.setDisplayed(opt.progress === true);
3359 this.updateProgress(0);
3360 activeTextEl.dom.value = opt.value || "";
3362 dlg.setDefaultButton(activeTextEl);
3364 var bs = opt.buttons;
3368 }else if(bs && bs.yes){
3369 db = buttons["yes"];
3371 dlg.setDefaultButton(db);
3373 bwidth = updateButtons(opt.buttons);
3374 this.updateText(opt.msg);
3376 d.el.addClass(opt.cls);
3378 d.proxyDrag = opt.proxyDrag === true;
3379 d.modal = opt.modal !== false;
3380 d.mask = opt.modal !== false ? mask : false;
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);
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
3398 progress : function(title, msg){
3405 minWidth: this.minProgressWidth,
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
3422 alert : function(title, msg, fn, scope)
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
3444 wait : function(msg, title){
3455 waitTimer = Roo.TaskMgr.start({
3457 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
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
3474 confirm : function(title, msg, fn, scope){
3478 buttons: this.YESNO,
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
3500 prompt : function(title, msg, fn, scope, multiline){
3504 buttons: this.OKCANCEL,
3509 multiline: multiline,
3516 * Button config that displays a single OK button
3521 * Button config that displays Yes and No buttons
3524 YESNO : {yes:true, no:true},
3526 * Button config that displays OK and Cancel buttons
3529 OKCANCEL : {ok:true, cancel:true},
3531 * Button config that displays Yes, No and Cancel buttons
3534 YESNOCANCEL : {yes:true, no:true, cancel:true},
3537 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3540 defaultTextHeight : 75,
3542 * The maximum width in pixels of the message box (defaults to 600)
3547 * The minimum width in pixels of the message box (defaults to 100)
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)
3556 minProgressWidth : 250,
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?";
3573 * Shorthand for {@link Roo.MessageBox}
3575 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3576 Roo.Msg = Roo.Msg || Roo.MessageBox;
3585 * @class Roo.bootstrap.Navbar
3586 * @extends Roo.bootstrap.Component
3587 * Bootstrap Navbar class
3590 * Create a new Navbar
3591 * @param {Object} config The config object
3595 Roo.bootstrap.Navbar = function(config){
3596 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3600 * @event beforetoggle
3601 * Fire before toggle the menu
3602 * @param {Roo.EventObject} e
3604 "beforetoggle" : true
3608 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3617 getAutoCreate : function(){
3620 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3624 initEvents :function ()
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');
3639 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3641 var size = this.el.getSize();
3642 this.maskEl.setSize(size.width, size.height);
3643 this.maskEl.enableDisplayMode("block");
3652 getChildContainer : function()
3654 if (this.el.select('.collapse').getCount()) {
3655 return this.el.select('.collapse',true).first();
3688 * @class Roo.bootstrap.NavSimplebar
3689 * @extends Roo.bootstrap.Navbar
3690 * Bootstrap Sidebar class
3692 * @cfg {Boolean} inverse is inverted color
3694 * @cfg {String} type (nav | pills | tabs)
3695 * @cfg {Boolean} arrangement stacked | justified
3696 * @cfg {String} align (left | right) alignment
3698 * @cfg {Boolean} main (true|false) main nav bar? default false
3699 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3701 * @cfg {String} tag (header|footer|nav|div) default is nav
3707 * Create a new Sidebar
3708 * @param {Object} config The config object
3712 Roo.bootstrap.NavSimplebar = function(config){
3713 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3716 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3732 getAutoCreate : function(){
3736 tag : this.tag || 'div',
3749 this.type = this.type || 'nav';
3750 if (['tabs','pills'].indexOf(this.type)!==-1) {
3751 cfg.cn[0].cls += ' nav-' + this.type
3755 if (this.type!=='nav') {
3756 Roo.log('nav type must be nav/tabs/pills')
3758 cfg.cn[0].cls += ' navbar-nav'
3764 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3765 cfg.cn[0].cls += ' nav-' + this.arrangement;
3769 if (this.align === 'right') {
3770 cfg.cn[0].cls += ' navbar-right';
3774 cfg.cls += ' navbar-inverse';
3801 * @class Roo.bootstrap.NavHeaderbar
3802 * @extends Roo.bootstrap.NavSimplebar
3803 * Bootstrap Sidebar class
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..
3814 * Create a new Sidebar
3815 * @param {Object} config The config object
3819 Roo.bootstrap.NavHeaderbar = function(config){
3820 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3824 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3831 desktopCenter : false,
3834 getAutoCreate : function(){
3837 tag: this.nav || 'nav',
3844 if (this.desktopCenter) {
3845 cn.push({cls : 'container', cn : []});
3852 cls: 'navbar-header',
3857 cls: 'navbar-toggle',
3858 'data-toggle': 'collapse',
3863 html: 'Toggle navigation'
3885 cls: 'collapse navbar-collapse',
3889 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3891 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3892 cfg.cls += ' navbar-' + this.position;
3894 // tag can override this..
3896 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3899 if (this.brand !== '') {
3902 href: this.brand_href ? this.brand_href : '#',
3903 cls: 'navbar-brand',
3911 cfg.cls += ' main-nav';
3919 getHeaderChildContainer : function()
3921 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3922 return this.el.select('.navbar-header',true).first();
3925 return this.getChildContainer();
3929 initEvents : function()
3931 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3933 if (this.autohide) {
3938 Roo.get(document).on('scroll',function(e) {
3939 var ns = Roo.get(document).getScroll().top;
3940 var os = prevScroll;
3944 ft.removeClass('slideDown');
3945 ft.addClass('slideUp');
3948 ft.removeClass('slideUp');
3949 ft.addClass('slideDown');
3970 * @class Roo.bootstrap.NavSidebar
3971 * @extends Roo.bootstrap.Navbar
3972 * Bootstrap Sidebar class
3975 * Create a new Sidebar
3976 * @param {Object} config The config object
3980 Roo.bootstrap.NavSidebar = function(config){
3981 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3984 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3986 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3988 getAutoCreate : function(){
3993 cls: 'sidebar sidebar-nav'
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.
4025 * Create a new nav group
4026 * @param {Object} config The config object
4029 Roo.bootstrap.NavGroup = function(config){
4030 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4033 Roo.bootstrap.NavGroup.register(this);
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
4047 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4058 getAutoCreate : function()
4060 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4067 if (['tabs','pills'].indexOf(this.type)!==-1) {
4068 cfg.cls += ' nav-' + this.type
4070 if (this.type!=='nav') {
4071 Roo.log('nav type must be nav/tabs/pills')
4073 cfg.cls += ' navbar-nav'
4076 if (this.parent() && this.parent().sidebar) {
4079 cls: 'dashboard-menu sidebar-menu'
4085 if (this.form === true) {
4091 if (this.align === 'right') {
4092 cfg.cls += ' navbar-right';
4094 cfg.cls += ' navbar-left';
4098 if (this.align === 'right') {
4099 cfg.cls += ' navbar-right';
4103 cfg.cls += ' navbar-inverse';
4111 * sets the active Navigation item
4112 * @param {Roo.bootstrap.NavItem} the new current navitem
4114 setActiveItem : function(item)
4117 Roo.each(this.navItems, function(v){
4122 v.setActive(false, true);
4129 item.setActive(true, true);
4130 this.fireEvent('changed', this, item, prev);
4135 * gets the active Navigation item
4136 * @return {Roo.bootstrap.NavItem} the current navitem
4138 getActive : function()
4142 Roo.each(this.navItems, function(v){
4153 indexOfNav : function()
4157 Roo.each(this.navItems, function(v,i){
4168 * adds a Navigation item
4169 * @param {Roo.bootstrap.NavItem} the navitem to add
4171 addItem : function(cfg)
4173 var cn = new Roo.bootstrap.NavItem(cfg);
4175 cn.parentId = this.id;
4176 cn.onRender(this.el, null);
4180 * register a Navigation item
4181 * @param {Roo.bootstrap.NavItem} the navitem to add
4183 register : function(item)
4185 this.navItems.push( item);
4186 item.navId = this.navId;
4191 * clear all the Navigation item
4194 clearAll : function()
4197 this.el.dom.innerHTML = '';
4200 getNavItem: function(tabId)
4203 Roo.each(this.navItems, function(e) {
4204 if (e.tabId == tabId) {
4214 setActiveNext : function()
4216 var i = this.indexOfNav(this.getActive());
4217 if (i > this.navItems.length) {
4220 this.setActiveItem(this.navItems[i+1]);
4222 setActivePrev : function()
4224 var i = this.indexOfNav(this.getActive());
4228 this.setActiveItem(this.navItems[i-1]);
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;
4240 getWasActive : function ()
4243 Roo.each(this.navItems, function(e) {
4258 Roo.apply(Roo.bootstrap.NavGroup, {
4262 * register a Navigation Group
4263 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4265 register : function(navgrp)
4267 this.groups[navgrp.navId] = navgrp;
4271 * fetch a Navigation Group based on the navigation ID
4272 * @param {string} the navgroup to add
4273 * @returns {Roo.bootstrap.NavGroup} the navgroup
4275 get: function(navId) {
4276 if (typeof(this.groups[navId]) == 'undefined') {
4278 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4280 return this.groups[navId] ;
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
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
4313 * Create a new Navbar Item
4314 * @param {Object} config The config object
4316 Roo.bootstrap.NavItem = function(config){
4317 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4322 * The raw click event for the entire grid.
4323 * @param {Roo.EventObject} e
4328 * Fires when the active item active state changes
4329 * @param {Roo.bootstrap.NavItem} this
4330 * @param {boolean} state the new state
4336 * Fires when scroll to element
4337 * @param {Roo.bootstrap.NavItem} this
4338 * @param {Object} options
4339 * @param {Roo.EventObject} e
4347 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4355 preventDefault : false,
4362 getAutoCreate : function(){
4371 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4373 if (this.disabled) {
4374 cfg.cls += ' disabled';
4377 if (this.href || this.html || this.glyphicon || this.icon) {
4381 href : this.href || "#",
4382 html: this.html || ''
4387 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4390 if(this.glyphicon) {
4391 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4396 cfg.cn[0].html += " <span class='caret'></span>";
4400 if (this.badge !== '') {
4402 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4410 initEvents: function()
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));
4418 this.el.select('a',true).on('click', this.onClick, this);
4420 if(this.tagtype == 'span'){
4421 this.el.select('span',true).on('click', this.onClick, this);
4424 // at this point parent should be available..
4425 this.parent().register(this);
4428 onClick : function(e)
4430 if (e.getTarget('.dropdown-menu-item')) {
4431 // did you click on a menu itemm.... - then don't trigger onclick..
4436 this.preventDefault ||
4439 Roo.log("NavItem - prevent Default?");
4443 if (this.disabled) {
4447 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4448 if (tg && tg.transition) {
4449 Roo.log("waiting for the transitionend");
4455 //Roo.log("fire event clicked");
4456 if(this.fireEvent('click', this, e) === false){
4460 if(this.tagtype == 'span'){
4464 //Roo.log(this.href);
4465 var ael = this.el.select('a',true).first();
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.
4473 Roo.log("NavItem - prevent Default?");
4475 this.scrollToElement(e);
4479 var p = this.parent();
4481 if (['tabs','pills'].indexOf(p.type)!==-1) {
4482 if (typeof(p.setActiveItem) !== 'undefined') {
4483 p.setActiveItem(this);
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');
4494 isActive: function () {
4497 setActive : function(state, fire, is_was_active)
4499 if (this.active && !state && this.navId) {
4500 this.was_active = true;
4501 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4503 nv.clearWasActive(this);
4507 this.active = state;
4510 this.el.removeClass('active');
4511 } else if (!this.el.hasClass('active')) {
4512 this.el.addClass('active');
4515 this.fireEvent('changed', this, state);
4518 // show a panel if it's registered and related..
4520 if (!this.navId || !this.tabId || !state || is_was_active) {
4524 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4528 var pan = tg.getPanelByName(this.tabId);
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);
4536 var onav = nv.getWasActive();
4538 onav.setActive(true, false, true);
4547 // this should not be here...
4548 setDisabled : function(state)
4550 this.disabled = state;
4552 this.el.removeClass('disabled');
4553 } else if (!this.el.hasClass('disabled')) {
4554 this.el.addClass('disabled');
4560 * Fetch the element to display the tooltip on.
4561 * @return {Roo.Element} defaults to this.el
4563 tooltipEl : function()
4565 return this.el.select('' + this.tagtype + '', true).first();
4568 scrollToElement : function(e)
4570 var c = document.body;
4573 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4575 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4576 c = document.documentElement;
4579 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4585 var o = target.calcOffsetsTo(c);
4592 this.fireEvent('scrollto', this, options, e);
4594 Roo.get(c).scrollTo('top', options.value, true);
4607 * <span> icon </span>
4608 * <span> text </span>
4609 * <span>badge </span>
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)
4623 * Create a new Navbar Button
4624 * @param {Object} config The config object
4626 Roo.bootstrap.NavSidebarItem = function(config){
4627 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4632 * The raw click event for the entire grid.
4633 * @param {Roo.EventObject} e
4638 * Fires when the active item active state changes
4639 * @param {Roo.bootstrap.NavSidebarItem} this
4640 * @param {boolean} state the new state
4648 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4650 badgeWeight : 'default',
4656 buttonWeight : 'default',
4662 getAutoCreate : function(){
4667 href : this.href || '#',
4673 if(this.buttonView){
4676 href : this.href || '#',
4677 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4690 cfg.cls += ' active';
4693 if (this.disabled) {
4694 cfg.cls += ' disabled';
4697 cfg.cls += ' open x-open';
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 }) ;
4705 if(!this.buttonView){
4708 html : this.html || ''
4715 if (this.badge !== '') {
4716 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4722 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4725 a.cls += ' dropdown-toggle treeview' ;
4731 initEvents : function()
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));
4739 this.el.on('click', this.onClick, this);
4741 if(this.badge !== ''){
4742 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4747 onClick : function(e)
4754 if(this.preventDefault){
4758 this.fireEvent('click', this);
4761 disable : function()
4763 this.setDisabled(true);
4768 this.setDisabled(false);
4771 setDisabled : function(state)
4773 if(this.disabled == state){
4777 this.disabled = state;
4780 this.el.addClass('disabled');
4784 this.el.removeClass('disabled');
4789 setActive : function(state)
4791 if(this.active == state){
4795 this.active = state;
4798 this.el.addClass('active');
4802 this.el.removeClass('active');
4807 isActive: function ()
4812 setBadge : function(str)
4818 this.badgeEl.dom.innerHTML = str;
4835 * @class Roo.bootstrap.Row
4836 * @extends Roo.bootstrap.Component
4837 * Bootstrap Row class (contains columns...)
4841 * @param {Object} config The config object
4844 Roo.bootstrap.Row = function(config){
4845 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4848 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4850 getAutoCreate : function(){
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
4879 * Create a new Element
4880 * @param {Object} config The config object
4883 Roo.bootstrap.Element = function(config){
4884 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4890 * When a element is chick
4891 * @param {Roo.bootstrap.Element} this
4892 * @param {Roo.EventObject} e
4898 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4903 preventDefault: false,
4906 getAutoCreate : function(){
4917 initEvents: function()
4919 Roo.bootstrap.Element.superclass.initEvents.call(this);
4922 this.el.on('click', this.onClick, this);
4927 onClick : function(e)
4929 if(this.preventDefault){
4933 this.fireEvent('click', this, e);
4936 getValue : function()
4938 return this.el.dom.innerHTML;
4941 setValue : function(value)
4943 this.el.dom.innerHTML = value;
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
4965 * Create a new Pagination
4966 * @param {Object} config The config object
4969 Roo.bootstrap.Pagination = function(config){
4970 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4973 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4979 getAutoCreate : function(){
4985 cfg.cls += ' inverse';
4991 cfg.cls += " " + this.cls;
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
5020 * Create a new PaginationItem
5021 * @param {Object} config The config object
5025 Roo.bootstrap.PaginationItem = function(config){
5026 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5031 * The raw click event for the entire grid.
5032 * @param {Roo.EventObject} e
5038 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5042 preventDefault: true,
5047 getAutoCreate : function(){
5053 href : this.href ? this.href : '#',
5054 html : this.html ? this.html : ''
5064 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5068 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5074 initEvents: function() {
5076 this.el.on('click', this.onClick, this);
5079 onClick : function(e)
5081 Roo.log('PaginationItem on click ');
5082 if(this.preventDefault){
5090 this.fireEvent('click', this, e);
5106 * @class Roo.bootstrap.Slider
5107 * @extends Roo.bootstrap.Component
5108 * Bootstrap Slider class
5111 * Create a new Slider
5112 * @param {Object} config The config object
5115 Roo.bootstrap.Slider = function(config){
5116 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5119 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5121 getAutoCreate : function(){
5125 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5129 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5141 * Ext JS Library 1.1.1
5142 * Copyright(c) 2006-2007, Ext JS, LLC.
5144 * Originally Released Under LGPL - original licence link has changed is not relivant.
5147 * <script type="text/javascript">
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.
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}
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.
5172 * @param {Object} config An Array of column config objects. See this class's
5173 * config objects for details.
5175 Roo.grid.ColumnModel = function(config){
5177 * The config passed into the constructor
5179 this.config = config;
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++){
5187 if(typeof c.dataIndex == "undefined"){
5190 if(typeof c.renderer == "string"){
5191 c.renderer = Roo.util.Format[c.renderer];
5193 if(typeof c.id == "undefined"){
5196 if(c.editor && c.editor.xtype){
5197 c.editor = Roo.factory(c.editor, Roo.grid);
5199 if(c.editor && c.editor.isFormField){
5200 c.editor = new Roo.grid.GridEditor(c.editor);
5202 this.lookup[c.id] = c;
5206 * The width of columns which have no width specified (defaults to 100)
5209 this.defaultWidth = 100;
5212 * Default sortable of columns which have no sortable specified (defaults to false)
5215 this.defaultSortable = false;
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
5225 "widthchange": true,
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
5233 "headerchange": true,
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
5241 "hiddenchange": true,
5243 * @event columnmoved
5244 * Fires when a column is moved.
5245 * @param {ColumnModel} this
5246 * @param {Number} oldIndex
5247 * @param {Number} newIndex
5249 "columnmoved" : true,
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
5257 "columnlockchange" : true
5259 Roo.grid.ColumnModel.superclass.constructor.call(this);
5261 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5263 * @cfg {String} header The header text to display in the Grid view.
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.
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.
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}.
5280 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5283 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5286 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5289 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
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
5298 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5301 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5304 * @cfg {String} cursor (Optional)
5307 * @cfg {String} tooltip (Optional)
5310 * @cfg {Number} xs (Optional)
5313 * @cfg {Number} sm (Optional)
5316 * @cfg {Number} md (Optional)
5319 * @cfg {Number} lg (Optional)
5322 * Returns the id of the column at the specified index.
5323 * @param {Number} index The column index
5324 * @return {String} the id
5326 getColumnId : function(index){
5327 return this.config[index].id;
5331 * Returns the column for a specified id.
5332 * @param {String} id The column id
5333 * @return {Object} the column
5335 getColumnById : function(id){
5336 return this.lookup[id];
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
5345 getColumnByDataIndex: function(dataIndex){
5346 var index = this.findColumnIndex(dataIndex);
5347 return index > -1 ? this.config[index] : false;
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
5355 getIndexById : function(id){
5356 for(var i = 0, len = this.config.length; i < len; i++){
5357 if(this.config[i].id == id){
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
5370 findColumnIndex : function(dataIndex){
5371 for(var i = 0, len = this.config.length; i < len; i++){
5372 if(this.config[i].dataIndex == dataIndex){
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);
5388 isLocked : function(colIndex){
5389 return this.config[colIndex].locked === true;
5392 setLocked : function(colIndex, value, suppressEvent){
5393 if(this.isLocked(colIndex) == value){
5396 this.config[colIndex].locked = value;
5398 this.fireEvent("columnlockchange", this, colIndex, value);
5402 getTotalLockedWidth : function(){
5404 for(var i = 0; i < this.config.length; i++){
5405 if(this.isLocked(i) && !this.isHidden(i)){
5406 this.totalWidth += this.getColumnWidth(i);
5412 getLockedCount : function(){
5413 for(var i = 0, len = this.config.length; i < len; i++){
5414 if(!this.isLocked(i)){
5419 return this.config.length;
5423 * Returns the number of columns.
5426 getColumnCount : function(visibleOnly){
5427 if(visibleOnly === true){
5429 for(var i = 0, len = this.config.length; i < len; i++){
5430 if(!this.isHidden(i)){
5436 return this.config.length;
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
5445 getColumnsBy : function(fn, scope){
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){
5457 * Returns true if the specified column is sortable.
5458 * @param {Number} col The column index
5461 isSortable : function(col){
5462 if(typeof this.config[col].sortable == "undefined"){
5463 return this.defaultSortable;
5465 return this.config[col].sortable;
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}.
5473 getRenderer : function(col){
5474 if(!this.config[col].renderer){
5475 return Roo.grid.ColumnModel.defaultRenderer;
5477 return this.config[col].renderer;
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>
5495 setRenderer : function(col, fn){
5496 this.config[col].renderer = fn;
5500 * Returns the width for the specified column.
5501 * @param {Number} col The column index
5504 getColumnWidth : function(col){
5505 return this.config[col].width * 1 || this.defaultWidth;
5509 * Sets the width for a column.
5510 * @param {Number} col The column index
5511 * @param {Number} width The new width
5513 setColumnWidth : function(col, width, suppressEvent){
5514 this.config[col].width = width;
5515 this.totalWidth = null;
5517 this.fireEvent("widthchange", this, col, width);
5522 * Returns the total width of all columns.
5523 * @param {Boolean} includeHidden True to include hidden column widths
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);
5535 return this.totalWidth;
5539 * Returns the header for the specified column.
5540 * @param {Number} col The column index
5543 getColumnHeader : function(col){
5544 return this.config[col].header;
5548 * Sets the header for a column.
5549 * @param {Number} col The column index
5550 * @param {String} header The new header
5552 setColumnHeader : function(col, header){
5553 this.config[col].header = header;
5554 this.fireEvent("headerchange", this, col, header);
5558 * Returns the tooltip for the specified column.
5559 * @param {Number} col The column index
5562 getColumnTooltip : function(col){
5563 return this.config[col].tooltip;
5566 * Sets the tooltip for a column.
5567 * @param {Number} col The column index
5568 * @param {String} tooltip The new tooltip
5570 setColumnTooltip : function(col, tooltip){
5571 this.config[col].tooltip = tooltip;
5575 * Returns the dataIndex for the specified column.
5576 * @param {Number} col The column index
5579 getDataIndex : function(col){
5580 return this.config[col].dataIndex;
5584 * Sets the dataIndex for a column.
5585 * @param {Number} col The column index
5586 * @param {Number} dataIndex The new dataIndex
5588 setDataIndex : function(col, dataIndex){
5589 this.config[col].dataIndex = dataIndex;
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..?
5600 isCellEditable : function(colIndex, rowIndex){
5601 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
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
5611 getCellEditor : function(colIndex, rowIndex){
5612 return this.config[colIndex].editor;
5616 * Sets if a column is editable.
5617 * @param {Number} col The column index
5618 * @param {Boolean} editable True if the column is editable
5620 setEditable : function(col, editable){
5621 this.config[col].editable = editable;
5626 * Returns true if the column is hidden.
5627 * @param {Number} colIndex The column index
5630 isHidden : function(colIndex){
5631 return this.config[colIndex].hidden;
5636 * Returns true if the column width cannot be changed
5638 isFixed : function(colIndex){
5639 return this.config[colIndex].fixed;
5643 * Returns true if the column can be resized
5646 isResizable : function(colIndex){
5647 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5650 * Sets if a column is hidden.
5651 * @param {Number} colIndex The column index
5652 * @param {Boolean} hidden True if the column is hidden
5654 setHidden : function(colIndex, hidden){
5655 this.config[colIndex].hidden = hidden;
5656 this.totalWidth = null;
5657 this.fireEvent("hiddenchange", this, colIndex, hidden);
5661 * Sets the editor for a column.
5662 * @param {Number} col The column index
5663 * @param {Object} editor The editor object
5665 setEditor : function(col, editor){
5666 this.config[col].editor = editor;
5670 Roo.grid.ColumnModel.defaultRenderer = function(value)
5672 if(typeof value == "object") {
5675 if(typeof value == "string" && value.length < 1){
5679 return String.format("{0}", value);
5682 // Alias for backwards compatibility
5683 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5686 * Ext JS Library 1.1.1
5687 * Copyright(c) 2006-2007, Ext JS, LLC.
5689 * Originally Released Under LGPL - original licence link has changed is not relivant.
5692 * <script type="text/javascript">
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.
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
5706 Roo.LoadMask = function(el, config){
5707 this.el = Roo.get(el);
5708 Roo.apply(this, config);
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;
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;
5724 Roo.LoadMask.prototype = {
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.
5732 * The text to display in a centered loading message box (defaults to 'Loading...')
5736 * @cfg {String} msgCls
5737 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5739 msgCls : 'x-mask-loading',
5742 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5748 * Disables the mask to prevent it from being displayed
5750 disable : function(){
5751 this.disabled = true;
5755 * Enables the mask so that it can be displayed
5757 enable : function(){
5758 this.disabled = false;
5761 onLoadException : function()
5765 if (typeof(arguments[3]) != 'undefined') {
5766 Roo.MessageBox.alert("Error loading",arguments[3]);
5770 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5771 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5778 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5783 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5787 onBeforeLoad : function(){
5789 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5794 destroy : function(){
5796 this.store.un('beforeload', this.onBeforeLoad, this);
5797 this.store.un('load', this.onLoad, this);
5798 this.store.un('loadexception', this.onLoadException, this);
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);
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)
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)
5846 * Create a new Table
5847 * @param {Object} config The config object
5850 Roo.bootstrap.Table = function(config){
5851 Roo.bootstrap.Table.superclass.constructor.call(this, config);
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;
5861 this.sm = this.sm || {xtype: 'RowSelectionModel'};
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;
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;
5875 this.store= Roo.factory(this.store, Roo.data);
5876 this.ds = this.store;
5877 this.ds.xmodule = this.xmodule || false;
5880 if (this.footer && this.store) {
5881 this.footer.dataSource = this.ds;
5882 this.footer = Roo.factory(this.footer);
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
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
5906 "celldblclick" : true,
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
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
5924 "rowdblclick" : true,
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
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
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.
5953 * @event rowsrendered
5954 * Fires when all the rows have been rendered
5955 * @param {Roo.bootstrap.Table} this
5957 'rowsrendered' : true,
5959 * @event contextmenu
5960 * The raw contextmenu event for the entire grid.
5961 * @param {Roo.EventObject} e
5963 "contextmenu" : true,
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
5971 "rowcontextmenu" : true,
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
5980 "cellcontextmenu" : true,
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
5988 "headercontextmenu" : true
5992 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6018 rowSelection : false,
6019 cellSelection : false,
6022 // Roo.Element - the tbody
6024 // Roo.Element - thead element
6027 container: false, // used by gridpanel...
6031 getAutoCreate : function()
6033 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6040 if (this.scrollBody) {
6041 cfg.cls += ' table-body-fixed';
6044 cfg.cls += ' table-striped';
6048 cfg.cls += ' table-hover';
6050 if (this.bordered) {
6051 cfg.cls += ' table-bordered';
6053 if (this.condensed) {
6054 cfg.cls += ' table-condensed';
6056 if (this.responsive) {
6057 cfg.cls += ' table-responsive';
6061 cfg.cls+= ' ' +this.cls;
6064 // this lot should be simplifed...
6067 cfg.align=this.align;
6070 cfg.bgcolor=this.bgcolor;
6073 cfg.border=this.border;
6075 if (this.cellpadding) {
6076 cfg.cellpadding=this.cellpadding;
6078 if (this.cellspacing) {
6079 cfg.cellspacing=this.cellspacing;
6082 cfg.frame=this.frame;
6085 cfg.rules=this.rules;
6087 if (this.sortable) {
6088 cfg.sortable=this.sortable;
6091 cfg.summary=this.summary;
6094 cfg.width=this.width;
6097 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6100 if(this.store || this.cm){
6101 if(this.headerShow){
6102 cfg.cn.push(this.renderHeader());
6105 cfg.cn.push(this.renderBody());
6107 if(this.footerShow){
6108 cfg.cn.push(this.renderFooter());
6110 // where does this come from?
6111 //cfg.cls+= ' TableGrid';
6114 return { cn : [ cfg ] };
6117 initEvents : function()
6119 if(!this.store || !this.cm){
6122 if (this.selModel) {
6123 this.selModel.initEvents();
6127 //Roo.log('initEvents with ds!!!!');
6129 this.mainBody = this.el.select('tbody', true).first();
6130 this.mainHead = this.el.select('thead', true).first();
6137 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6138 e.on('click', _this.sort, _this);
6141 this.mainBody.on("click", this.onClick, this);
6142 this.mainBody.on("dblclick", this.onDblClick, this);
6144 // why is this done????? = it breaks dialogs??
6145 //this.parent().el.setStyle('position', 'relative');
6149 this.footer.parentId = this.id;
6150 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6153 this.el.select('tfoot tr td').first().addClass('hide');
6157 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
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);
6165 this.el.on("contextmenu", this.onContextMenu, this);
6167 this.mainBody.on('scroll', this.onBodyScroll, this);
6169 this.cm.on("headerchange", this.onHeaderChange, this);
6173 onContextMenu : function(e, t)
6175 this.processEvent("contextmenu", e);
6178 processEvent : function(name, e)
6180 if (name != 'touchstart' ) {
6181 this.fireEvent(name, e);
6184 var t = e.getTarget();
6186 var cell = Roo.get(t);
6192 if(cell.findParent('tfoot', false, true)){
6196 if(cell.findParent('thead', false, true)){
6198 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6199 cell = Roo.get(t).findParent('th', false, true);
6201 Roo.log("failed to find th in thead?");
6202 Roo.log(e.getTarget());
6207 var cellIndex = cell.dom.cellIndex;
6209 var ename = name == 'touchstart' ? 'click' : name;
6210 this.fireEvent("header" + ename, this, cellIndex, e);
6215 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6216 cell = Roo.get(t).findParent('td', false, true);
6218 Roo.log("failed to find th in tbody?");
6219 Roo.log(e.getTarget());
6224 var row = cell.findParent('tr', false, true);
6225 var cellIndex = cell.dom.cellIndex;
6226 var rowIndex = row.dom.rowIndex - 1;
6230 this.fireEvent("row" + name, this, rowIndex, e);
6234 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6240 onMouseover : function(e, el)
6242 var cell = Roo.get(el);
6248 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6249 cell = cell.findParent('td', false, true);
6252 var row = cell.findParent('tr', false, true);
6253 var cellIndex = cell.dom.cellIndex;
6254 var rowIndex = row.dom.rowIndex - 1; // start from 0
6256 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6260 onMouseout : function(e, el)
6262 var cell = Roo.get(el);
6268 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6269 cell = cell.findParent('td', false, true);
6272 var row = cell.findParent('tr', false, true);
6273 var cellIndex = cell.dom.cellIndex;
6274 var rowIndex = row.dom.rowIndex - 1; // start from 0
6276 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6280 onClick : function(e, el)
6282 var cell = Roo.get(el);
6284 if(!cell || (!this.cellSelection && !this.rowSelection)){
6288 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6289 cell = cell.findParent('td', false, true);
6292 if(!cell || typeof(cell) == 'undefined'){
6296 var row = cell.findParent('tr', false, true);
6298 if(!row || typeof(row) == 'undefined'){
6302 var cellIndex = cell.dom.cellIndex;
6303 var rowIndex = this.getRowIndex(row);
6305 // why??? - should these not be based on SelectionModel?
6306 if(this.cellSelection){
6307 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6310 if(this.rowSelection){
6311 this.fireEvent('rowclick', this, row, rowIndex, e);
6317 onDblClick : function(e,el)
6319 var cell = Roo.get(el);
6321 if(!cell || (!this.cellSelection && !this.rowSelection)){
6325 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6326 cell = cell.findParent('td', false, true);
6329 if(!cell || typeof(cell) == 'undefined'){
6333 var row = cell.findParent('tr', false, true);
6335 if(!row || typeof(row) == 'undefined'){
6339 var cellIndex = cell.dom.cellIndex;
6340 var rowIndex = this.getRowIndex(row);
6342 if(this.cellSelection){
6343 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6346 if(this.rowSelection){
6347 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6351 sort : function(e,el)
6353 var col = Roo.get(el);
6355 if(!col.hasClass('sortable')){
6359 var sort = col.attr('sort');
6362 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6366 this.store.sortInfo = {field : sort, direction : dir};
6369 Roo.log("calling footer first");
6370 this.footer.onClick('first');
6373 this.store.load({ params : { start : 0 } });
6377 renderHeader : function()
6385 this.totalWidth = 0;
6387 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6389 var config = cm.config[i];
6394 html: cm.getColumnHeader(i)
6399 if(typeof(config.sortable) != 'undefined' && config.sortable){
6401 c.html = '<i class="glyphicon"></i>' + c.html;
6404 if(typeof(config.lgHeader) != 'undefined'){
6405 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6408 if(typeof(config.mdHeader) != 'undefined'){
6409 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6412 if(typeof(config.smHeader) != 'undefined'){
6413 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6416 if(typeof(config.xsHeader) != 'undefined'){
6417 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6424 if(typeof(config.tooltip) != 'undefined'){
6425 c.tooltip = config.tooltip;
6428 if(typeof(config.colspan) != 'undefined'){
6429 c.colspan = config.colspan;
6432 if(typeof(config.hidden) != 'undefined' && config.hidden){
6433 c.style += ' display:none;';
6436 if(typeof(config.dataIndex) != 'undefined'){
6437 c.sort = config.dataIndex;
6442 if(typeof(config.align) != 'undefined' && config.align.length){
6443 c.style += ' text-align:' + config.align + ';';
6446 if(typeof(config.width) != 'undefined'){
6447 c.style += ' width:' + config.width + 'px;';
6448 this.totalWidth += config.width;
6450 this.totalWidth += 100; // assume minimum of 100 per column?
6453 if(typeof(config.cls) != 'undefined'){
6454 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6457 ['xs','sm','md','lg'].map(function(size){
6459 if(typeof(config[size]) == 'undefined'){
6463 if (!config[size]) { // 0 = hidden
6464 c.cls += ' hidden-' + size;
6468 c.cls += ' col-' + size + '-' + config[size];
6478 renderBody : function()
6488 colspan : this.cm.getColumnCount()
6498 renderFooter : function()
6508 colspan : this.cm.getColumnCount()
6522 // Roo.log('ds onload');
6527 var ds = this.store;
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) {
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']);
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']);
6543 var tbody = this.mainBody;
6545 if(ds.getCount() > 0){
6546 ds.data.each(function(d,rowIndex){
6547 var row = this.renderRow(cm, ds, rowIndex);
6549 tbody.createChild(row);
6553 if(row.cellObjects.length){
6554 Roo.each(row.cellObjects, function(r){
6555 _this.renderCellObject(r);
6562 Roo.each(this.el.select('tbody td', true).elements, function(e){
6563 e.on('mouseover', _this.onMouseover, _this);
6566 Roo.each(this.el.select('tbody td', true).elements, function(e){
6567 e.on('mouseout', _this.onMouseout, _this);
6569 this.fireEvent('rowsrendered', this);
6570 //if(this.loadMask){
6571 // this.maskEl.hide();
6578 onUpdate : function(ds,record)
6580 this.refreshRow(record);
6584 onRemove : function(ds, record, index, isUpdate){
6585 if(isUpdate !== true){
6586 this.fireEvent("beforerowremoved", this, index, record);
6588 var bt = this.mainBody.dom;
6590 var rows = this.el.select('tbody > tr', true).elements;
6592 if(typeof(rows[index]) != 'undefined'){
6593 bt.removeChild(rows[index].dom);
6596 // if(bt.rows[index]){
6597 // bt.removeChild(bt.rows[index]);
6600 if(isUpdate !== true){
6601 //this.stripeRows(index);
6602 //this.syncRowHeights(index, index);
6604 this.fireEvent("rowremoved", this, index, record);
6608 onAdd : function(ds, records, rowIndex)
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);
6624 refreshRow : function(record){
6625 var ds = this.store, index;
6626 if(typeof record == 'number'){
6628 record = ds.getAt(index);
6630 index = ds.indexOf(record);
6632 this.insertRow(ds, index, true);
6634 this.onRemove(ds, record, index+1, true);
6636 //this.syncRowHeights(index, index);
6638 this.fireEvent("rowupdated", this, index, record);
6641 insertRow : function(dm, rowIndex, isUpdate){
6644 this.fireEvent("beforerowsinserted", this, rowIndex);
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));
6653 if(row.cellObjects.length){
6654 Roo.each(row.cellObjects, function(r){
6655 _this.renderCellObject(r);
6660 this.fireEvent("rowsinserted", this, rowIndex);
6661 //this.syncRowHeights(firstRow, lastRow);
6662 //this.stripeRows(firstRow);
6669 getRowDom : function(rowIndex)
6671 var rows = this.el.select('tbody > tr', true).elements;
6673 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6676 // returns the object tree for a tr..
6679 renderRow : function(cm, ds, rowIndex)
6682 var d = ds.getAt(rowIndex);
6689 var cellObjects = [];
6691 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6692 var config = cm.config[i];
6694 var renderer = cm.getRenderer(i);
6698 if(typeof(renderer) !== 'undefined'){
6699 value = renderer(d.data[cm.getDataIndex(i)], false, d);
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.
6704 if(typeof(value) === 'object'){
6714 rowIndex : rowIndex,
6719 this.fireEvent('rowclass', this, rowcfg);
6723 cls : rowcfg.rowClass,
6725 html: (typeof(value) === 'object') ? '' : value
6732 if(typeof(config.colspan) != 'undefined'){
6733 td.colspan = config.colspan;
6736 if(typeof(config.hidden) != 'undefined' && config.hidden){
6737 td.style += ' display:none;';
6740 if(typeof(config.align) != 'undefined' && config.align.length){
6741 td.style += ' text-align:' + config.align + ';';
6744 if(typeof(config.width) != 'undefined'){
6745 td.style += ' width:' + config.width + 'px;';
6748 if(typeof(config.cursor) != 'undefined'){
6749 td.style += ' cursor:' + config.cursor + ';';
6752 if(typeof(config.cls) != 'undefined'){
6753 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6756 ['xs','sm','md','lg'].map(function(size){
6758 if(typeof(config[size]) == 'undefined'){
6762 if (!config[size]) { // 0 = hidden
6763 td.cls += ' hidden-' + size;
6767 td.cls += ' col-' + size + '-' + config[size];
6775 row.cellObjects = cellObjects;
6783 onBeforeLoad : function()
6785 //Roo.log('ds onBeforeLoad');
6789 //if(this.loadMask){
6790 // this.maskEl.show();
6798 this.el.select('tbody', true).first().dom.innerHTML = '';
6801 * Show or hide a row.
6802 * @param {Number} rowIndex to show or hide
6803 * @param {Boolean} state hide
6805 setRowVisibility : function(rowIndex, state)
6807 var bt = this.mainBody.dom;
6809 var rows = this.el.select('tbody > tr', true).elements;
6811 if(typeof(rows[rowIndex]) == 'undefined'){
6814 rows[rowIndex].dom.style.display = state ? '' : 'none';
6818 getSelectionModel : function(){
6820 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6822 return this.selModel;
6825 * Render the Roo.bootstrap object from renderder
6827 renderCellObject : function(r)
6831 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6833 var t = r.cfg.render(r.container);
6836 Roo.each(r.cfg.cn, function(c){
6838 container: t.getChildContainer(),
6841 _this.renderCellObject(child);
6846 getRowIndex : function(row)
6850 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6861 * Returns the grid's underlying element = used by panel.Grid
6862 * @return {Element} The element
6864 getGridEl : function(){
6868 * Forces a resize - used by panel.Grid
6869 * @return {Element} The element
6871 autoSize : function()
6873 //var ctr = Roo.get(this.container.dom.parentElement);
6874 var ctr = Roo.get(this.el.dom);
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();
6880 var cw = ctr.getWidth();
6884 tbd.setSize(ctr.getWidth(),
6885 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6887 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6890 cw = Math.max(cw, this.totalWidth);
6891 this.getGridEl().select('tr',true).setWidth(cw);
6892 // resize 'expandable coloumn?
6894 return; // we doe not have a view in this design..
6897 onBodyScroll: function()
6899 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6901 this.mainHead.setStyle({
6902 'position' : 'relative',
6903 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6909 var scrollHeight = this.mainBody.dom.scrollHeight;
6911 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6913 var height = this.mainBody.getHeight();
6915 if(scrollHeight - height == scrollTop) {
6917 var total = this.ds.getTotalCount();
6919 if(this.footer.cursor + this.footer.pageSize < total){
6921 this.footer.ds.load({
6923 start : this.footer.cursor + this.footer.pageSize,
6924 limit : this.footer.pageSize
6934 onHeaderChange : function()
6937 var header = this.renderHeader();
6938 var table = this.el.select('table', true).first();
6940 this.mainHead.remove();
6941 this.mainHead = table.createChild(header, this.mainBody, false);
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
6977 * Create a new TableCell
6978 * @param {Object} config The config object
6981 Roo.bootstrap.TableCell = function(config){
6982 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6985 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7005 getAutoCreate : function(){
7006 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7026 cfg.align=this.align
7032 cfg.bgcolor=this.bgcolor
7035 cfg.charoff=this.charoff
7038 cfg.colspan=this.colspan
7041 cfg.headers=this.headers
7044 cfg.height=this.height
7047 cfg.nowrap=this.nowrap
7050 cfg.rowspan=this.rowspan
7053 cfg.scope=this.scope
7056 cfg.valign=this.valign
7059 cfg.width=this.width
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
7088 * Create a new TableRow
7089 * @param {Object} config The config object
7092 Roo.bootstrap.TableRow = function(config){
7093 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7096 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7104 getAutoCreate : function(){
7105 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7115 cfg.align = this.align;
7118 cfg.bgcolor = this.bgcolor;
7121 cfg.charoff = this.charoff;
7124 cfg.valign = this.valign;
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
7152 * Create a new TableBody
7153 * @param {Object} config The config object
7156 Roo.bootstrap.TableBody = function(config){
7157 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7160 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7168 getAutoCreate : function(){
7169 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7183 cfg.align = this.align;
7186 cfg.charoff = this.charoff;
7189 cfg.valign = this.valign;
7196 // initEvents : function()
7203 // this.store = Roo.factory(this.store, Roo.data);
7204 // this.store.on('load', this.onLoad, this);
7206 // this.store.load();
7210 // onLoad: function ()
7212 // this.fireEvent('load', this);
7222 * Ext JS Library 1.1.1
7223 * Copyright(c) 2006-2007, Ext JS, LLC.
7225 * Originally Released Under LGPL - original licence link has changed is not relivant.
7228 * <script type="text/javascript">
7231 // as we use this in bootstrap.
7232 Roo.namespace('Roo.form');
7234 * @class Roo.form.Action
7235 * Internal Class used to handle form actions
7237 * @param {Roo.form.BasicForm} el The form element or its id
7238 * @param {Object} config Configuration options
7243 // define the action interface
7244 Roo.form.Action = function(form, options){
7246 this.options = options || {};
7249 * Client Validation Failed
7252 Roo.form.Action.CLIENT_INVALID = 'client';
7254 * Server Validation Failed
7257 Roo.form.Action.SERVER_INVALID = 'server';
7259 * Connect to Server Failed
7262 Roo.form.Action.CONNECT_FAILURE = 'connect';
7264 * Reading Data from Server Failed
7267 Roo.form.Action.LOAD_FAILURE = 'load';
7269 Roo.form.Action.prototype = {
7271 failureType : undefined,
7272 response : undefined,
7276 run : function(options){
7281 success : function(response){
7286 handleResponse : function(response){
7290 // default connection failure
7291 failure : function(response){
7293 this.response = response;
7294 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7295 this.form.afterAction(this, false);
7298 processResponse : function(response){
7299 this.response = response;
7300 if(!response.responseText){
7303 this.result = this.handleResponse(response);
7307 // utility functions used internally
7308 getUrl : function(appendParams){
7309 var url = this.options.url || this.form.url || this.form.el.dom.action;
7311 var p = this.getParams();
7313 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7319 getMethod : function(){
7320 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7323 getParams : function(){
7324 var bp = this.form.baseParams;
7325 var p = this.options.params;
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);
7333 p = Roo.urlEncode(bp);
7338 createCallback : function(){
7340 success: this.success,
7341 failure: this.failure,
7343 timeout: (this.form.timeout*1000),
7344 upload: this.form.fileUpload ? this.success : undefined
7349 Roo.form.Action.Submit = function(form, options){
7350 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7353 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7356 haveProgress : false,
7357 uploadComplete : false,
7359 // uploadProgress indicator.
7360 uploadProgress : function()
7362 if (!this.form.progressUrl) {
7366 if (!this.haveProgress) {
7367 Roo.MessageBox.progress("Uploading", "Uploading");
7369 if (this.uploadComplete) {
7370 Roo.MessageBox.hide();
7374 this.haveProgress = true;
7376 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7378 var c = new Roo.data.Connection();
7380 url : this.form.progressUrl,
7385 success : function(req){
7386 //console.log(data);
7390 rdata = Roo.decode(req.responseText)
7392 Roo.log("Invalid data from server..");
7396 if (!rdata || !rdata.success) {
7398 Roo.MessageBox.alert(Roo.encode(rdata));
7401 var data = rdata.data;
7403 if (this.uploadComplete) {
7404 Roo.MessageBox.hide();
7409 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7410 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7413 this.uploadProgress.defer(2000,this);
7416 failure: function(data) {
7417 Roo.log('progress url failed ');
7428 // run get Values on the form, so it syncs any secondary forms.
7429 this.form.getValues();
7431 var o = this.options;
7432 var method = this.getMethod();
7433 var isPost = method == 'POST';
7434 if(o.clientValidation === false || this.form.isValid()){
7436 if (this.form.progressUrl) {
7437 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7438 (new Date() * 1) + '' + Math.random());
7443 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7444 form:this.form.el.dom,
7445 url:this.getUrl(!isPost),
7447 params:isPost ? this.getParams() : null,
7448 isUpload: this.form.fileUpload
7451 this.uploadProgress();
7453 }else if (o.clientValidation !== false){ // client validation failed
7454 this.failureType = Roo.form.Action.CLIENT_INVALID;
7455 this.form.afterAction(this, false);
7459 success : function(response)
7461 this.uploadComplete= true;
7462 if (this.haveProgress) {
7463 Roo.MessageBox.hide();
7467 var result = this.processResponse(response);
7468 if(result === true || result.success){
7469 this.form.afterAction(this, true);
7473 this.form.markInvalid(result.errors);
7474 this.failureType = Roo.form.Action.SERVER_INVALID;
7476 this.form.afterAction(this, false);
7478 failure : function(response)
7480 this.uploadComplete= true;
7481 if (this.haveProgress) {
7482 Roo.MessageBox.hide();
7485 this.response = response;
7486 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7487 this.form.afterAction(this, false);
7490 handleResponse : function(response){
7491 if(this.form.errorReader){
7492 var rs = this.form.errorReader.read(response);
7495 for(var i = 0, len = rs.records.length; i < len; i++) {
7496 var r = rs.records[i];
7500 if(errors.length < 1){
7504 success : rs.success,
7510 ret = Roo.decode(response.responseText);
7514 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
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;
7529 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7534 Roo.Ajax.request(Roo.apply(
7535 this.createCallback(), {
7536 method:this.getMethod(),
7537 url:this.getUrl(false),
7538 params:this.getParams()
7542 success : function(response){
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);
7550 this.form.clearInvalid();
7551 this.form.setValues(result.data);
7552 this.form.afterAction(this, true);
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;
7560 success : rs.success,
7564 return Roo.decode(response.responseText);
7568 Roo.form.Action.ACTION_TYPES = {
7569 'load' : Roo.form.Action.Load,
7570 'submit' : Roo.form.Action.Submit
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)
7590 * @param {Object} config The config object
7594 Roo.bootstrap.Form = function(config){
7596 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7598 Roo.bootstrap.Form.popover.apply();
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
7607 clientvalidation: true,
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
7616 * @event actionfailed
7617 * Fires when an action fails.
7618 * @param {Form} this
7619 * @param {Action} action The action that failed
7621 actionfailed : true,
7623 * @event actioncomplete
7624 * Fires when an action is completed.
7625 * @param {Form} this
7626 * @param {Action} action The action that completed
7628 actioncomplete : true
7632 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
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.
7641 * The URL to use for form actions if one isn't supplied in the action options.
7644 * @cfg {Boolean} fileUpload
7645 * Set to true if this form is a file upload.
7649 * @cfg {Object} baseParams
7650 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7654 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7658 * @cfg {Sting} align (left|right) for navbar forms
7663 activeAction : null,
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.
7670 waitMsgTarget : false,
7675 * @cfg {Boolean} errorMask (true|false) default false
7680 * @cfg {Number} maskOffset Default 100
7685 * @cfg {Boolean} maskBody
7689 getAutoCreate : function(){
7693 method : this.method || 'POST',
7694 id : this.id || Roo.id(),
7697 if (this.parent().xtype.match(/^Nav/)) {
7698 cfg.cls = 'navbar-form navbar-' + this.align;
7702 if (this.labelAlign == 'left' ) {
7703 cfg.cls += ' form-horizontal';
7709 initEvents : function()
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) {
7717 // we might need to allow it for textareas.. and some other items.
7718 // check e.getTarget().
7720 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7724 Roo.log("keypress blocked");
7732 onSubmit : function(e){
7737 * Returns true if client-side validation on the form is successful.
7740 isValid : function(){
7741 var items = this.getItems();
7745 items.each(function(f){
7751 if(!target && f.el.isVisible(true)){
7757 if(this.errorMask && !valid){
7758 Roo.bootstrap.Form.popover.mask(this, target);
7765 * Returns true if any fields in this form have changed since their original load.
7768 isDirty : function(){
7770 var items = this.getItems();
7771 items.each(function(f){
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):
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)
7795 * @return {BasicForm} this
7797 doAction : function(action, options){
7798 if(typeof action == 'string'){
7799 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7801 if(this.fireEvent('beforeaction', this, action) !== false){
7802 this.beforeAction(action);
7803 action.run.defer(100, action);
7809 beforeAction : function(action){
7810 var o = action.options;
7815 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7817 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7820 // not really supported yet.. ??
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');
7828 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7834 afterAction : function(action, success){
7835 this.activeAction = null;
7836 var o = action.options;
7841 Roo.get(document.body).unmask();
7847 //if(this.waitMsgTarget === true){
7848 // this.el.unmask();
7849 //}else if(this.waitMsgTarget){
7850 // this.waitMsgTarget.unmask();
7852 // Roo.MessageBox.updateProgress(1);
7853 // Roo.MessageBox.hide();
7860 Roo.callback(o.success, o.scope, [this, action]);
7861 this.fireEvent('actioncomplete', this, action);
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.
7870 (typeof(action.result) != 'undefined') &&
7871 (typeof(action.result.errors) != 'undefined') &&
7872 (typeof(action.result.errors.needs_confirm) != 'undefined')
7875 Roo.log("not supported yet");
7878 Roo.MessageBox.confirm(
7879 "Change requires confirmation",
7880 action.result.errorMsg,
7885 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
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");
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"
7908 this.fireEvent('actionfailed', this, action);
7913 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7914 * @param {String} id The value to search for
7917 findField : function(id){
7918 var items = this.getItems();
7919 var field = items.get(id);
7921 items.each(function(f){
7922 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7929 return field || null;
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
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);
7942 f.markInvalid(fieldError.msg);
7948 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7949 field.markInvalid(errors[id]);
7953 //Roo.each(this.childForms || [], function (f) {
7954 // f.markInvalid(errors);
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
7965 setValues : function(values){
7966 if(values instanceof Array){ // array of objects
7967 for(var i = 0, len = values.length; i < len; i++){
7969 var f = this.findField(v.id);
7971 f.setValue(v.value);
7972 if(this.trackResetOnLoad){
7973 f.originalValue = f.getValue();
7977 }else{ // object hash
7980 if(typeof values[id] != 'function' && (field = this.findField(id))){
7982 if (field.setFromData &&
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)
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);
7996 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
7998 field.setFromData(values);
8001 field.setValue(values[id]);
8005 if(this.trackResetOnLoad){
8006 field.originalValue = field.getValue();
8012 //Roo.each(this.childForms || [], function (f) {
8013 // f.setValues(values);
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
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());
8035 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8036 if(asString === true){
8039 return Roo.urlDecode(fs);
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.
8047 getFieldValues : function(with_hidden)
8049 var items = this.getItems();
8051 items.each(function(f){
8057 var v = f.getValue();
8059 if (f.inputType =='radio') {
8060 if (typeof(ret[f.getName()]) == 'undefined') {
8061 ret[f.getName()] = ''; // empty..
8064 if (!f.el.dom.checked) {
8072 if(f.xtype == 'MoneyField'){
8073 ret[f.currencyName] = f.getCurrency();
8076 // not sure if this supported any more..
8077 if ((typeof(v) == 'object') && f.getRawValue) {
8078 v = f.getRawValue() ; // dates..
8080 // combo boxes where name != hiddenName...
8081 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8082 ret[f.name] = f.getRawValue();
8084 ret[f.getName()] = v;
8091 * Clears all invalid messages in this form.
8092 * @return {BasicForm} this
8094 clearInvalid : function(){
8095 var items = this.getItems();
8097 items.each(function(f){
8106 * @return {BasicForm} this
8109 var items = this.getItems();
8110 items.each(function(f){
8114 Roo.each(this.childForms || [], function (f) {
8122 getItems : function()
8124 var r=new Roo.util.MixedCollection(false, function(o){
8125 return o.id || (o.id = Roo.id());
8127 var iter = function(el) {
8134 Roo.each(el.items,function(e) {
8145 Roo.apply(Roo.bootstrap.Form, {
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)
8178 this.maskEl.top.enableDisplayMode("block");
8179 this.maskEl.left.enableDisplayMode("block");
8180 this.maskEl.bottom.enableDisplayMode("block");
8181 this.maskEl.right.enableDisplayMode("block");
8183 this.toolTip = new Roo.bootstrap.Tooltip({
8184 cls : 'roo-form-error-popover',
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']
8193 this.toolTip.render(Roo.get(document.body));
8195 this.toolTip.el.enableDisplayMode("block");
8197 Roo.get(document.body).on('click', function(){
8201 Roo.get(document.body).on('touchstart', function(){
8205 this.isApplied = true
8208 mask : function(form, target)
8212 this.target = target;
8214 if(!this.form.errorMask || !target.el){
8218 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8220 Roo.log(scrollable);
8222 var ot = this.target.el.calcOffsetsTo(scrollable);
8224 var scrollTo = ot[1] - this.form.maskOffset;
8226 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8228 scrollable.scrollTo('top', scrollTo);
8230 var box = this.target.el.getBox();
8232 var zIndex = Roo.bootstrap.Modal.zIndex++;
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();
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();
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();
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();
8263 this.toolTip.bindEl = this.target.el;
8265 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8267 var tip = this.target.blankText;
8269 if(this.target.getValue() !== '' ) {
8271 if (this.target.invalidText.length) {
8272 tip = this.target.invalidText;
8273 } else if (this.target.regexText.length){
8274 tip = this.target.regexText;
8278 this.toolTip.show(tip);
8280 this.intervalID = window.setInterval(function() {
8281 Roo.bootstrap.Form.popover.unmask();
8284 window.onwheel = function(){ return false;};
8286 (function(){ this.isMasked = true; }).defer(500, this);
8292 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8296 this.maskEl.top.setStyle('position', 'absolute');
8297 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8298 this.maskEl.top.hide();
8300 this.maskEl.left.setStyle('position', 'absolute');
8301 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8302 this.maskEl.left.hide();
8304 this.maskEl.bottom.setStyle('position', 'absolute');
8305 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8306 this.maskEl.bottom.hide();
8308 this.maskEl.right.setStyle('position', 'absolute');
8309 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8310 this.maskEl.right.hide();
8312 this.toolTip.hide();
8314 this.toolTip.el.hide();
8316 window.onwheel = function(){ return true;};
8318 if(this.intervalID){
8319 window.clearInterval(this.intervalID);
8320 this.intervalID = false;
8323 this.isMasked = false;
8333 * Ext JS Library 1.1.1
8334 * Copyright(c) 2006-2007, Ext JS, LLC.
8336 * Originally Released Under LGPL - original licence link has changed is not relivant.
8339 * <script type="text/javascript">
8342 * @class Roo.form.VTypes
8343 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
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;
8353 // All these messages and functions are configurable
8356 * The function used to validate email addresses
8357 * @param {String} value The email address
8359 'email' : function(v){
8360 return email.test(v);
8363 * The error text to display when the email validation function returns false
8366 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8368 * The keystroke filter mask to be applied on email input
8371 'emailMask' : /[a-z0-9_\.\-@]/i,
8374 * The function used to validate URLs
8375 * @param {String} value The URL
8377 'url' : function(v){
8381 * The error text to display when the url validation function returns false
8384 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8387 * The function used to validate alpha values
8388 * @param {String} value The value
8390 'alpha' : function(v){
8391 return alpha.test(v);
8394 * The error text to display when the alpha validation function returns false
8397 'alphaText' : 'This field should only contain letters and _',
8399 * The keystroke filter mask to be applied on alpha input
8402 'alphaMask' : /[a-z_]/i,
8405 * The function used to validate alphanumeric values
8406 * @param {String} value The value
8408 'alphanum' : function(v){
8409 return alphanum.test(v);
8412 * The error text to display when the alphanumeric validation function returns false
8415 'alphanumText' : 'This field should only contain letters, numbers and _',
8417 * The keystroke filter mask to be applied on alphanumeric input
8420 'alphanumMask' : /[a-z0-9_]/i
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
8456 * @cfg {String} align (left|center|right) Default left
8457 * @cfg {Boolean} forceFeedback (true|false) Default false
8463 * Create a new Input
8464 * @param {Object} config The config object
8467 Roo.bootstrap.Input = function(config){
8469 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8474 * Fires when this field receives input focus.
8475 * @param {Roo.form.Field} this
8480 * Fires when this field loses input focus.
8481 * @param {Roo.form.Field} this
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
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
8502 * Fires after the field has been marked as invalid.
8503 * @param {Roo.form.Field} this
8504 * @param {String} msg The validation message
8509 * Fires after the field has been validated with no errors.
8510 * @param {Roo.form.Field} this
8515 * Fires after the key up
8516 * @param {Roo.form.Field} this
8517 * @param {Roo.EventObject} e The event Object
8523 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8525 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8526 automatic validation (defaults to "keyup").
8528 validationEvent : "keyup",
8530 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8532 validateOnBlur : true,
8534 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8536 validationDelay : 250,
8538 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8540 focusClass : "x-form-focus", // not needed???
8544 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8546 invalidClass : "has-warning",
8549 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8551 validClass : "has-success",
8554 * @cfg {Boolean} hasFeedback (true|false) default true
8559 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8561 invalidFeedbackClass : "glyphicon-warning-sign",
8564 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8566 validFeedbackClass : "glyphicon-ok",
8569 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8571 selectOnFocus : false,
8574 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8578 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8583 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8585 disableKeyFilter : false,
8588 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8592 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8596 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8598 blankText : "Please complete this mandatory field",
8601 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8605 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8607 maxLength : Number.MAX_VALUE,
8609 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8611 minLengthText : "The minimum length for this field is {0}",
8613 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8615 maxLengthText : "The maximum length for this field is {0}",
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.
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}.
8631 * @cfg {String} regexText -- Depricated - use Invalid Text
8636 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8642 autocomplete: false,
8661 formatedValue : false,
8662 forceFeedback : false,
8664 indicatorpos : 'left',
8671 parentLabelAlign : function()
8674 while (parent.parent()) {
8675 parent = parent.parent();
8676 if (typeof(parent.labelAlign) !='undefined') {
8677 return parent.labelAlign;
8684 getAutoCreate : function()
8686 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8692 if(this.inputType != 'hidden'){
8693 cfg.cls = 'form-group' //input-group
8699 type : this.inputType,
8701 cls : 'form-control',
8702 placeholder : this.placeholder || '',
8703 autocomplete : this.autocomplete || 'new-password'
8707 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8710 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8711 input.maxLength = this.maxLength;
8714 if (this.disabled) {
8715 input.disabled=true;
8718 if (this.readOnly) {
8719 input.readonly=true;
8723 input.name = this.name;
8727 input.cls += ' input-' + this.size;
8731 ['xs','sm','md','lg'].map(function(size){
8732 if (settings[size]) {
8733 cfg.cls += ' col-' + size + '-' + settings[size];
8737 var inputblock = input;
8741 cls: 'glyphicon form-control-feedback'
8744 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8747 cls : 'has-feedback',
8755 if (this.before || this.after) {
8758 cls : 'input-group',
8762 if (this.before && typeof(this.before) == 'string') {
8764 inputblock.cn.push({
8766 cls : 'roo-input-before input-group-addon',
8770 if (this.before && typeof(this.before) == 'object') {
8771 this.before = Roo.factory(this.before);
8773 inputblock.cn.push({
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?
8780 inputblock.cn.push(input);
8782 if (this.after && typeof(this.after) == 'string') {
8783 inputblock.cn.push({
8785 cls : 'roo-input-after input-group-addon',
8789 if (this.after && typeof(this.after) == 'object') {
8790 this.after = Roo.factory(this.after);
8792 inputblock.cn.push({
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?
8799 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8800 inputblock.cls += ' has-feedback';
8801 inputblock.cn.push(feedback);
8805 if (align ==='left' && this.fieldLabel.length) {
8807 cfg.cls += ' roo-form-group-label-left';
8812 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8813 tooltip : 'This field is required'
8818 cls : 'control-label',
8819 html : this.fieldLabel
8830 var labelCfg = cfg.cn[1];
8831 var contentCfg = cfg.cn[2];
8833 if(this.indicatorpos == 'right'){
8838 cls : 'control-label',
8842 html : this.fieldLabel
8846 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8847 tooltip : 'This field is required'
8860 labelCfg = cfg.cn[0];
8861 contentCfg = cfg.cn[1];
8865 if(this.labelWidth > 12){
8866 labelCfg.style = "width: " + this.labelWidth + 'px';
8869 if(this.labelWidth < 13 && this.labelmd == 0){
8870 this.labelmd = this.labelWidth;
8873 if(this.labellg > 0){
8874 labelCfg.cls += ' col-lg-' + this.labellg;
8875 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8878 if(this.labelmd > 0){
8879 labelCfg.cls += ' col-md-' + this.labelmd;
8880 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8883 if(this.labelsm > 0){
8884 labelCfg.cls += ' col-sm-' + this.labelsm;
8885 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8888 if(this.labelxs > 0){
8889 labelCfg.cls += ' col-xs-' + this.labelxs;
8890 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8894 } else if ( this.fieldLabel.length) {
8899 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8900 tooltip : 'This field is required'
8904 //cls : 'input-group-addon',
8905 html : this.fieldLabel
8913 if(this.indicatorpos == 'right'){
8918 //cls : 'input-group-addon',
8919 html : this.fieldLabel
8924 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8925 tooltip : 'This field is required'
8945 if (this.parentType === 'Navbar' && this.parent().bar) {
8946 cfg.cls += ' navbar-form';
8949 if (this.parentType === 'NavGroup') {
8950 cfg.cls += ' navbar-form';
8958 * return the real input element.
8960 inputEl: function ()
8962 return this.el.select('input.form-control',true).first();
8965 tooltipEl : function()
8967 return this.inputEl();
8970 indicatorEl : function()
8972 var indicator = this.el.select('i.roo-required-indicator',true).first();
8982 setDisabled : function(v)
8984 var i = this.inputEl().dom;
8986 i.removeAttribute('disabled');
8990 i.setAttribute('disabled','true');
8992 initEvents : function()
8995 this.inputEl().on("keydown" , this.fireKey, this);
8996 this.inputEl().on("focus", this.onFocus, this);
8997 this.inputEl().on("blur", this.onBlur, this);
8999 this.inputEl().relayEvent('keyup', this);
9001 this.indicator = this.indicatorEl();
9004 this.indicator.addClass('invisible');
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);
9015 else if(this.validationEvent !== false){
9016 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9019 if(this.selectOnFocus){
9020 this.on("focus", this.preFocus, this);
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);
9026 this.inputEl().relayEvent('keypress', this);
9029 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9030 this.el.on("click", this.autoSize, this);
9033 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9034 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9037 if (typeof(this.before) == 'object') {
9038 this.before.render(this.el.select('.roo-input-before',true).first());
9040 if (typeof(this.after) == 'object') {
9041 this.after.render(this.el.select('.roo-input-after',true).first());
9046 filterValidation : function(e){
9047 if(!e.isNavKeyPress()){
9048 this.validationTask.delay(this.validationDelay);
9052 * Validates the field value
9053 * @return {Boolean} True if the value is valid, else false
9055 validate : function(){
9056 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9057 if(this.disabled || this.validateValue(this.getRawValue())){
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
9073 validateValue : function(value){
9074 if(value.length < 1) { // if it's blank
9075 if(this.allowBlank){
9078 return this.inputEl().hasClass('hide') ? true : false;
9081 if(value.length < this.minLength){
9084 if(value.length > this.maxLength){
9088 var vt = Roo.form.VTypes;
9089 if(!vt[this.vtype](value, this)){
9093 if(typeof this.validator == "function"){
9094 var msg = this.validator(value);
9098 if (typeof(msg) == 'string') {
9099 this.invalidText = msg;
9103 if(this.regex && !this.regex.test(value)){
9113 fireKey : function(e){
9114 //Roo.log('field ' + e.getKey());
9115 if(e.isNavKeyPress()){
9116 this.fireEvent("specialkey", this, e);
9119 focus : function (selectText){
9121 this.inputEl().focus();
9122 if(selectText === true){
9123 this.inputEl().dom.select();
9129 onFocus : function(){
9130 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9131 // this.el.addClass(this.focusClass);
9134 this.hasFocus = true;
9135 this.startValue = this.getValue();
9136 this.fireEvent("focus", this);
9140 beforeBlur : Roo.emptyFn,
9144 onBlur : function(){
9146 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9147 //this.el.removeClass(this.focusClass);
9149 this.hasFocus = false;
9150 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9153 var v = this.getValue();
9154 if(String(v) !== String(this.startValue)){
9155 this.fireEvent('change', this, v, this.startValue);
9157 this.fireEvent("blur", this);
9161 * Resets the current field value to the originally loaded value and clears any validation messages
9164 this.setValue(this.originalValue);
9168 * Returns the name of the field
9169 * @return {Mixed} name The name field
9171 getName: function(){
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
9178 getValue : function(){
9180 var v = this.inputEl().getValue();
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
9188 getRawValue : function(){
9189 var v = this.inputEl().getValue();
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
9198 setRawValue : function(v){
9199 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9202 selectText : function(start, end){
9203 var v = this.getRawValue();
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);
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
9223 setValue : function(v){
9226 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9232 processValue : function(value){
9233 if(this.stripCharsRe){
9234 var newValue = value.replace(this.stripCharsRe, '');
9235 if(newValue !== value){
9236 this.setRawValue(newValue);
9243 preFocus : function(){
9245 if(this.selectOnFocus){
9246 this.inputEl().dom.select();
9249 filterKeys : function(e){
9251 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9254 var c = e.getCharCode(), cc = String.fromCharCode(c);
9255 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9258 if(!this.maskRe.test(cc)){
9263 * Clear any invalid styles/messages for this field
9265 clearInvalid : function(){
9267 if(!this.el || this.preventMark){ // not rendered
9272 this.el.removeClass(this.invalidClass);
9274 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9276 var feedback = this.el.select('.form-control-feedback', true).first();
9279 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9284 this.fireEvent('valid', this);
9288 * Mark this field as valid
9290 markValid : function()
9292 if(!this.el || this.preventMark){ // not rendered...
9296 this.el.removeClass([this.invalidClass, this.validClass]);
9298 var feedback = this.el.select('.form-control-feedback', true).first();
9301 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9305 this.indicator.removeClass('visible');
9306 this.indicator.addClass('invisible');
9313 if(this.allowBlank && !this.getRawValue().length){
9317 this.el.addClass(this.validClass);
9319 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9321 var feedback = this.el.select('.form-control-feedback', true).first();
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]);
9330 this.fireEvent('valid', this);
9334 * Mark this field as invalid
9335 * @param {String} msg The validation message
9337 markInvalid : function(msg)
9339 if(!this.el || this.preventMark){ // not rendered
9343 this.el.removeClass([this.invalidClass, this.validClass]);
9345 var feedback = this.el.select('.form-control-feedback', true).first();
9348 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9355 if(this.allowBlank && !this.getRawValue().length){
9360 this.indicator.removeClass('invisible');
9361 this.indicator.addClass('visible');
9364 this.el.addClass(this.invalidClass);
9366 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9368 var feedback = this.el.select('.form-control-feedback', true).first();
9371 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9373 if(this.getValue().length || this.forceFeedback){
9374 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9381 this.fireEvent('invalid', this, msg);
9384 SafariOnKeyDown : function(event)
9386 // this is a workaround for a password hang bug on chrome/ webkit.
9387 if (this.inputEl().dom.type != 'password') {
9391 var isSelectAll = false;
9393 if(this.inputEl().dom.selectionEnd > 0){
9394 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9396 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9397 event.preventDefault();
9402 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9404 event.preventDefault();
9405 // this is very hacky as keydown always get's upper case.
9407 var cc = String.fromCharCode(event.getCharCode());
9408 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
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')){
9419 if(tag == 'textarea'){
9422 }else if(Roo.isOpera){
9426 if(tag == 'textarea'){
9434 setFieldLabel : function(v)
9441 var ar = this.el.select('label > span',true);
9443 if (ar.elements.length) {
9444 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9445 this.fieldLabel = v;
9449 var br = this.el.select('label',true);
9451 if(br.elements.length) {
9452 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9453 this.fieldLabel = v;
9457 Roo.log('Cannot Found any of label > span || label in input');
9461 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9462 this.fieldLabel = v;
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
9487 * Create a new TextArea
9488 * @param {Object} config The config object
9491 Roo.bootstrap.TextArea = function(config){
9492 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9496 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9506 getAutoCreate : function(){
9508 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9514 if(this.inputType != 'hidden'){
9515 cfg.cls = 'form-group' //input-group
9523 value : this.value || '',
9524 html: this.html || '',
9525 cls : 'form-control',
9526 placeholder : this.placeholder || ''
9530 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9531 input.maxLength = this.maxLength;
9535 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9539 input.cols = this.cols;
9542 if (this.readOnly) {
9543 input.readonly = true;
9547 input.name = this.name;
9551 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9555 ['xs','sm','md','lg'].map(function(size){
9556 if (settings[size]) {
9557 cfg.cls += ' col-' + size + '-' + settings[size];
9561 var inputblock = input;
9563 if(this.hasFeedback && !this.allowBlank){
9567 cls: 'glyphicon form-control-feedback'
9571 cls : 'has-feedback',
9580 if (this.before || this.after) {
9583 cls : 'input-group',
9587 inputblock.cn.push({
9589 cls : 'input-group-addon',
9594 inputblock.cn.push(input);
9596 if(this.hasFeedback && !this.allowBlank){
9597 inputblock.cls += ' has-feedback';
9598 inputblock.cn.push(feedback);
9602 inputblock.cn.push({
9604 cls : 'input-group-addon',
9611 if (align ==='left' && this.fieldLabel.length) {
9616 cls : 'control-label',
9617 html : this.fieldLabel
9628 if(this.labelWidth > 12){
9629 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9632 if(this.labelWidth < 13 && this.labelmd == 0){
9633 this.labelmd = this.labelWidth;
9636 if(this.labellg > 0){
9637 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9638 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9641 if(this.labelmd > 0){
9642 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9643 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9646 if(this.labelsm > 0){
9647 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9648 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9651 if(this.labelxs > 0){
9652 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9653 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9656 } else if ( this.fieldLabel.length) {
9661 //cls : 'input-group-addon',
9662 html : this.fieldLabel
9680 if (this.disabled) {
9681 input.disabled=true;
9688 * return the real textarea element.
9690 inputEl: function ()
9692 return this.el.select('textarea.form-control',true).first();
9696 * Clear any invalid styles/messages for this field
9698 clearInvalid : function()
9701 if(!this.el || this.preventMark){ // not rendered
9705 var label = this.el.select('label', true).first();
9706 var icon = this.el.select('i.fa-star', true).first();
9712 this.el.removeClass(this.invalidClass);
9714 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9716 var feedback = this.el.select('.form-control-feedback', true).first();
9719 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9724 this.fireEvent('valid', this);
9728 * Mark this field as valid
9730 markValid : function()
9732 if(!this.el || this.preventMark){ // not rendered
9736 this.el.removeClass([this.invalidClass, this.validClass]);
9738 var feedback = this.el.select('.form-control-feedback', true).first();
9741 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9744 if(this.disabled || this.allowBlank){
9748 var label = this.el.select('label', true).first();
9749 var icon = this.el.select('i.fa-star', true).first();
9755 this.el.addClass(this.validClass);
9757 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9759 var feedback = this.el.select('.form-control-feedback', true).first();
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]);
9768 this.fireEvent('valid', this);
9772 * Mark this field as invalid
9773 * @param {String} msg The validation message
9775 markInvalid : function(msg)
9777 if(!this.el || this.preventMark){ // not rendered
9781 this.el.removeClass([this.invalidClass, this.validClass]);
9783 var feedback = this.el.select('.form-control-feedback', true).first();
9786 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9789 if(this.disabled || this.allowBlank){
9793 var label = this.el.select('label', true).first();
9794 var icon = this.el.select('i.fa-star', true).first();
9796 if(!this.getValue().length && label && !icon){
9797 this.el.createChild({
9799 cls : 'text-danger fa fa-lg fa-star',
9800 tooltip : 'This field is required',
9801 style : 'margin-right:5px;'
9805 this.el.addClass(this.invalidClass);
9807 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9809 var feedback = this.el.select('.form-control-feedback', true).first();
9812 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9814 if(this.getValue().length || this.forceFeedback){
9815 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9822 this.fireEvent('invalid', this, msg);
9830 * trigger field - base class for combo..
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:
9842 var trigger = new Roo.bootstrap.TriggerField();
9843 trigger.onTriggerClick = myTriggerFn;
9844 trigger.applyTo('my-field');
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/
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)
9858 Roo.bootstrap.TriggerField = function(config){
9859 this.mimicing = false;
9860 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9863 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9865 * @cfg {String} triggerClass A CSS class to apply to the trigger
9868 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9873 * @cfg {Boolean} removable (true|false) special filter default false
9877 /** @cfg {Boolean} grow @hide */
9878 /** @cfg {Number} growMin @hide */
9879 /** @cfg {Number} growMax @hide */
9885 autoSize: Roo.emptyFn,
9892 actionMode : 'wrap',
9897 getAutoCreate : function(){
9899 var align = this.labelAlign || this.parentLabelAlign();
9904 cls: 'form-group' //input-group
9911 type : this.inputType,
9912 cls : 'form-control',
9913 autocomplete: 'new-password',
9914 placeholder : this.placeholder || ''
9918 input.name = this.name;
9921 input.cls += ' input-' + this.size;
9924 if (this.disabled) {
9925 input.disabled=true;
9928 var inputblock = input;
9930 if(this.hasFeedback && !this.allowBlank){
9934 cls: 'glyphicon form-control-feedback'
9937 if(this.removable && !this.editable && !this.tickable){
9939 cls : 'has-feedback',
9945 cls : 'roo-combo-removable-btn close'
9952 cls : 'has-feedback',
9961 if(this.removable && !this.editable && !this.tickable){
9963 cls : 'roo-removable',
9969 cls : 'roo-combo-removable-btn close'
9976 if (this.before || this.after) {
9979 cls : 'input-group',
9983 inputblock.cn.push({
9985 cls : 'input-group-addon',
9990 inputblock.cn.push(input);
9992 if(this.hasFeedback && !this.allowBlank){
9993 inputblock.cls += ' has-feedback';
9994 inputblock.cn.push(feedback);
9998 inputblock.cn.push({
10000 cls : 'input-group-addon',
10013 cls: 'form-hidden-field'
10027 cls: 'form-hidden-field'
10031 cls: 'roo-select2-choices',
10035 cls: 'roo-select2-search-field',
10048 cls: 'roo-select2-container input-group',
10053 // cls: 'typeahead typeahead-long dropdown-menu',
10054 // style: 'display:none'
10059 if(!this.multiple && this.showToggleBtn){
10065 if (this.caret != false) {
10068 cls: 'fa fa-' + this.caret
10075 cls : 'input-group-addon btn dropdown-toggle',
10080 cls: 'combobox-clear',
10094 combobox.cls += ' roo-select2-container-multi';
10097 if (align ==='left' && this.fieldLabel.length) {
10099 cfg.cls += ' roo-form-group-label-left';
10104 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10105 tooltip : 'This field is required'
10110 cls : 'control-label',
10111 html : this.fieldLabel
10123 var labelCfg = cfg.cn[1];
10124 var contentCfg = cfg.cn[2];
10126 if(this.indicatorpos == 'right'){
10131 cls : 'control-label',
10135 html : this.fieldLabel
10139 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10140 tooltip : 'This field is required'
10153 labelCfg = cfg.cn[0];
10154 contentCfg = cfg.cn[1];
10157 if(this.labelWidth > 12){
10158 labelCfg.style = "width: " + this.labelWidth + 'px';
10161 if(this.labelWidth < 13 && this.labelmd == 0){
10162 this.labelmd = this.labelWidth;
10165 if(this.labellg > 0){
10166 labelCfg.cls += ' col-lg-' + this.labellg;
10167 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10170 if(this.labelmd > 0){
10171 labelCfg.cls += ' col-md-' + this.labelmd;
10172 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10175 if(this.labelsm > 0){
10176 labelCfg.cls += ' col-sm-' + this.labelsm;
10177 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10180 if(this.labelxs > 0){
10181 labelCfg.cls += ' col-xs-' + this.labelxs;
10182 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10185 } else if ( this.fieldLabel.length) {
10186 // Roo.log(" label");
10190 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10191 tooltip : 'This field is required'
10195 //cls : 'input-group-addon',
10196 html : this.fieldLabel
10204 if(this.indicatorpos == 'right'){
10212 html : this.fieldLabel
10216 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10217 tooltip : 'This field is required'
10230 // Roo.log(" no label && no align");
10237 ['xs','sm','md','lg'].map(function(size){
10238 if (settings[size]) {
10239 cfg.cls += ' col-' + size + '-' + settings[size];
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');
10260 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10263 getResizeEl : function(){
10264 return this.inputEl();
10268 getPositionEl : function(){
10269 return this.inputEl();
10273 alignErrorIcon : function(){
10274 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10278 initEvents : function(){
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);
10289 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10293 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10296 if(this.removable && !this.editable && !this.tickable){
10297 var close = this.closeTriggerEl();
10300 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10301 close.on('click', this.removeBtnClick, this, close);
10305 //this.trigger.addClassOnOver('x-form-trigger-over');
10306 //this.trigger.addClassOnClick('x-form-trigger-click');
10309 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10313 closeTriggerEl : function()
10315 var close = this.el.select('.roo-combo-removable-btn', true).first();
10316 return close ? close : false;
10319 removeBtnClick : function(e, h, el)
10321 e.preventDefault();
10323 if(this.fireEvent("remove", this) !== false){
10325 this.fireEvent("afterremove", this)
10329 createList : function()
10331 this.list = Roo.get(document.body).createChild({
10333 cls: 'typeahead typeahead-long dropdown-menu',
10334 style: 'display:none'
10337 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10342 initTrigger : function(){
10347 onDestroy : function(){
10349 this.trigger.removeAllListeners();
10350 // this.trigger.remove();
10353 // this.wrap.remove();
10355 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10359 onFocus : function(){
10360 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
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);
10374 checkTab : function(e){
10375 if(e.getKey() == e.TAB){
10376 this.triggerBlur();
10381 onBlur : function(){
10386 mimicBlur : function(e, t){
10388 if(!this.wrap.contains(t) && this.validateBlur()){
10389 this.triggerBlur();
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);
10401 //this.wrap.removeClass('x-trigger-wrap-focus');
10402 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
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){
10412 onDisable : function(){
10413 this.inputEl().dom.disabled = true;
10414 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10416 // this.wrap.addClass('x-item-disabled');
10421 onEnable : function(){
10422 this.inputEl().dom.disabled = false;
10423 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10425 // this.el.removeClass('x-item-disabled');
10430 onShow : function(){
10431 var ae = this.getActionEl();
10434 ae.dom.style.display = '';
10435 ae.dom.style.visibility = 'visible';
10441 onHide : function(){
10442 var ae = this.getActionEl();
10443 ae.dom.style.display = 'none';
10447 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10448 * by an implementing function.
10450 * @param {EventObject} e
10452 onTriggerClick : Roo.emptyFn
10456 * Ext JS Library 1.1.1
10457 * Copyright(c) 2006-2007, Ext JS, LLC.
10459 * Originally Released Under LGPL - original licence link has changed is not relivant.
10462 * <script type="text/javascript">
10467 * @class Roo.data.SortTypes
10469 * Defines the default sorting (casting?) comparison functions used when sorting data.
10471 Roo.data.SortTypes = {
10473 * Default sort that does nothing
10474 * @param {Mixed} s The value being converted
10475 * @return {Mixed} The comparison value
10477 none : function(s){
10482 * The regular expression used to strip tags
10486 stripTagsRE : /<\/?[^>]+>/gi,
10489 * Strips all HTML tags to sort on text only
10490 * @param {Mixed} s The value being converted
10491 * @return {String} The comparison value
10493 asText : function(s){
10494 return String(s).replace(this.stripTagsRE, "");
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
10502 asUCText : function(s){
10503 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10507 * Case insensitive string
10508 * @param {Mixed} s The value being converted
10509 * @return {String} The comparison value
10511 asUCString : function(s) {
10512 return String(s).toUpperCase();
10517 * @param {Mixed} s The value being converted
10518 * @return {Number} The comparison value
10520 asDate : function(s) {
10524 if(s instanceof Date){
10525 return s.getTime();
10527 return Date.parse(String(s));
10532 * @param {Mixed} s The value being converted
10533 * @return {Float} The comparison value
10535 asFloat : function(s) {
10536 var val = parseFloat(String(s).replace(/,/g, ""));
10545 * @param {Mixed} s The value being converted
10546 * @return {Number} The comparison value
10548 asInt : function(s) {
10549 var val = parseInt(String(s).replace(/,/g, ""));
10557 * Ext JS Library 1.1.1
10558 * Copyright(c) 2006-2007, Ext JS, LLC.
10560 * Originally Released Under LGPL - original licence link has changed is not relivant.
10563 * <script type="text/javascript">
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>
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
10576 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
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.
10585 Roo.data.Record = function(data, id){
10586 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
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>
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>
10617 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
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'}
10629 var myNewRecord = new TopicRecord({
10630 title: 'Do my job please',
10633 lastPost: new Date(),
10634 lastPoster: 'Animal',
10635 excerpt: 'No way dude!'
10637 myStore.add(myNewRecord);
10642 Roo.data.Record.create = function(o){
10643 var f = function(){
10644 f.superclass.constructor.apply(this, arguments);
10646 Roo.extend(f, Roo.data.Record);
10647 var p = f.prototype;
10648 p.fields = new Roo.util.MixedCollection(false, function(field){
10651 for(var i = 0, len = o.length; i < len; i++){
10652 p.fields.add(new Roo.data.Field(o[i]));
10654 f.getField = function(name){
10655 return p.fields.get(name);
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';
10665 Roo.data.Record.prototype = {
10667 * Readonly flag - true if this record has been modified.
10676 join : function(store){
10677 this.store = store;
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.
10685 set : function(name, value){
10686 if(this.data[name] == value){
10690 if(!this.modified){
10691 this.modified = {};
10693 if(typeof this.modified[name] == 'undefined'){
10694 this.modified[name] = this.data[name];
10696 this.data[name] = value;
10697 if(!this.editing && this.store){
10698 this.store.afterEdit(this);
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.
10707 get : function(name){
10708 return this.data[name];
10712 beginEdit : function(){
10713 this.editing = true;
10714 this.modified = {};
10718 cancelEdit : function(){
10719 this.editing = false;
10720 delete this.modified;
10724 endEdit : function(){
10725 this.editing = false;
10726 if(this.dirty && this.store){
10727 this.store.afterEdit(this);
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.
10736 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10737 * of reject operations.
10739 reject : function(){
10740 var m = this.modified;
10742 if(typeof m[n] != "function"){
10743 this.data[n] = m[n];
10746 this.dirty = false;
10747 delete this.modified;
10748 this.editing = false;
10750 this.store.afterReject(this);
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.
10758 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10759 * of commit operations.
10761 commit : function(){
10762 this.dirty = false;
10763 delete this.modified;
10764 this.editing = false;
10766 this.store.afterCommit(this);
10771 hasError : function(){
10772 return this.error != null;
10776 clearError : function(){
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
10785 copy : function(newId) {
10786 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10790 * Ext JS Library 1.1.1
10791 * Copyright(c) 2006-2007, Ext JS, LLC.
10793 * Originally Released Under LGPL - original licence link has changed is not relivant.
10796 * <script type="text/javascript">
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>
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>
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.
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.
10817 Roo.data.Store = function(config){
10818 this.data = new Roo.util.MixedCollection(false);
10819 this.data.getKey = function(o){
10822 this.baseParams = {};
10824 this.paramNames = {
10829 "multisort" : "_multisort"
10832 if(config && config.data){
10833 this.inlineData = config.data;
10834 delete config.data;
10837 Roo.apply(this, config);
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;
10845 if(this.reader.onMetaChange){
10846 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10850 if(this.recordType){
10851 this.fields = this.recordType.prototype.fields;
10853 this.modified = [];
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
10862 datachanged : true,
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
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
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
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:
10893 Roo.data.Record.EDIT
10894 Roo.data.Record.REJECT
10895 Roo.data.Record.COMMIT
10901 * Fires when the data cache has been cleared.
10902 * @param {Store} this
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)
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)
10920 beforeloadadd : true,
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
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
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)
10941 loadexception : true
10945 this.proxy = Roo.factory(this.proxy, Roo.data);
10946 this.proxy.xmodule = this.xmodule || false;
10947 this.relayEvents(this.proxy, ["loadexception"]);
10949 this.sortToggle = {};
10950 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10952 Roo.data.Store.superclass.constructor.call(this);
10954 if(this.inlineData){
10955 this.loadData(this.inlineData);
10956 delete this.inlineData;
10960 Roo.extend(Roo.data.Store, Roo.util.Observable, {
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.
10967 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10970 * @cfg {Array} data Inline data to be loaded when the store is initialized.
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.
10977 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10978 * on any HTTP request
10981 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10984 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
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).
10991 remoteSort : false,
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).
10997 pruneModifiedRecords : false,
11000 lastOptions : null,
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.
11006 add : function(records){
11007 records = [].concat(records);
11008 for(var i = 0, len = records.length; i < len; i++){
11009 records[i].join(this);
11011 var index = this.data.length;
11012 this.data.addAll(records);
11013 this.fireEvent("add", this, records, index);
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.
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);
11026 this.fireEvent("remove", this, record, index);
11030 * Remove all Records from the Store and fires the clear event.
11032 removeAll : function(){
11034 if(this.pruneModifiedRecords){
11035 this.modified = [];
11037 this.fireEvent("clear", this);
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.
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);
11051 this.fireEvent("add", this, records, index);
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.
11059 indexOf : function(record){
11060 return this.data.indexOf(record);
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.
11068 indexOfId : function(id){
11069 return this.data.indexOfKey(id);
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.
11077 getById : function(id){
11078 return this.data.key(id);
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.
11086 getAt : function(index){
11087 return this.data.itemAt(index);
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
11096 getRange : function(start, end){
11097 return this.data.getRange(start, end);
11101 storeOptions : function(o){
11102 o = Roo.apply({}, o);
11105 this.lastOptions = o;
11109 * Loads the Record cache from the configured Proxy using the configured Reader.
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.
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>
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>
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;
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;
11144 if (this.multiSort) {
11145 var pn = this.paramNames;
11146 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11149 this.proxy.load(p, this.reader, this.loadRecords, this, options);
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).
11160 reload : function(options){
11161 this.load(Roo.applyIf(options||{}, this.lastOptions));
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);
11171 if(options.callback){
11172 options.callback.call(options.scope || this, [], options, false);
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);
11182 // loadmask wil be hooked into this..
11183 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11186 var r = o.records, t = o.totalRecords || r.length;
11188 this.fireEvent("beforeloadadd", this, r, options, o);
11190 if(!options || options.add !== true){
11191 if(this.pruneModifiedRecords){
11192 this.modified = [];
11194 for(var i = 0, len = r.length; i < len; i++){
11198 this.data = this.snapshot;
11199 delete this.snapshot;
11202 this.data.addAll(r);
11203 this.totalLength = t;
11205 this.fireEvent("datachanged", this);
11207 this.totalLength = Math.max(t, this.data.length+r.length);
11211 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11213 var e = new Roo.data.Record({});
11215 e.set(this.parent.displayField, this.parent.emptyTitle);
11216 e.set(this.parent.valueField, '');
11221 this.fireEvent("load", this, r, options, o);
11222 if(options.callback){
11223 options.callback.call(options.scope || this, r, options, true);
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.
11235 loadData : function(o, append){
11236 var r = this.reader.readRecords(o);
11237 this.loadRecords(r, {add: append}, true);
11241 * Gets the number of cached records.
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>
11247 getCount : function(){
11248 return this.data.length || 0;
11252 * Gets the total number of records in the dataset as returned by the server.
11254 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11255 * the dataset size</em>
11257 getTotalCount : function(){
11258 return this.totalLength || 0;
11262 * Returns the sort state of the Store as an object with two properties:
11264 field {String} The name of the field by which the Records are sorted
11265 direction {String} The sort order, "ASC" or "DESC"
11268 getSortState : function(){
11269 return this.sortInfo;
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);
11281 this.data.sort(s.direction, fn);
11282 if(this.snapshot && this.snapshot != this.data){
11283 this.snapshot.sort(s.direction, fn);
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")
11293 setDefaultSort : function(field, dir){
11294 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
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")
11304 sort : function(fieldName, dir){
11305 var f = this.fields.get(fieldName);
11307 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11309 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11310 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11315 this.sortToggle[f.name] = dir;
11316 this.sortInfo = {field: f.name, direction: dir};
11317 if(!this.remoteSort){
11319 this.fireEvent("datachanged", this);
11321 this.load(this.lastOptions);
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).
11331 each : function(fn, scope){
11332 this.data.each(fn, scope);
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.
11340 getModifiedRecords : function(){
11341 return this.modified;
11345 createFilterFn : function(property, value, anyMatch){
11346 if(!value.exec){ // not a regex
11347 value = String(value);
11348 if(value.length == 0){
11351 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11353 return function(r){
11354 return value.test(r.data[property]);
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
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;
11370 for(var i = start; i <= end; i++){
11371 v += (rs[i].data[property] || 0);
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
11383 filter : function(property, value, anyMatch){
11384 var fn = this.createFilterFn(property, value, anyMatch);
11385 return fn ? this.filterBy(fn) : this.clearFilter();
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)
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);
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
11409 query : function(property, value, anyMatch){
11410 var fn = this.createFilterFn(property, value, anyMatch);
11411 return fn ? this.queryBy(fn) : this.data.clone();
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
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
11422 queryBy : function(fn, scope){
11423 var data = this.snapshot || this.data;
11424 return data.filterBy(fn, scope||this);
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
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];
11441 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
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
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);
11464 afterEdit : function(record){
11465 if(this.modified.indexOf(record) == -1){
11466 this.modified.push(record);
11468 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11472 afterReject : function(record){
11473 this.modified.remove(record);
11474 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11478 afterCommit : function(record){
11479 this.modified.remove(record);
11480 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
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.
11487 commitChanges : function(){
11488 var m = this.modified.slice(0);
11489 this.modified = [];
11490 for(var i = 0, len = m.length; i < len; i++){
11496 * Cancel outstanding changes on all changed records.
11498 rejectChanges : function(){
11499 var m = this.modified.slice(0);
11500 this.modified = [];
11501 for(var i = 0, len = m.length; i < len; i++){
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);
11515 moveIndex : function(data, type)
11517 var index = this.indexOf(data);
11519 var newIndex = index + type;
11523 this.insert(newIndex, data);
11528 * Ext JS Library 1.1.1
11529 * Copyright(c) 2006-2007, Ext JS, LLC.
11531 * Originally Released Under LGPL - original licence link has changed is not relivant.
11534 * <script type="text/javascript">
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
11545 * @param {Object} config
11547 Roo.data.SimpleStore = function(config){
11548 Roo.data.SimpleStore.superclass.constructor.call(this, {
11550 reader: new Roo.data.ArrayReader({
11553 Roo.data.Record.create(config.fields)
11555 proxy : new Roo.data.MemoryProxy(config.data)
11559 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11561 * Ext JS Library 1.1.1
11562 * Copyright(c) 2006-2007, Ext JS, LLC.
11564 * Originally Released Under LGPL - original licence link has changed is not relivant.
11567 * <script type="text/javascript">
11572 * @extends Roo.data.Store
11573 * @class Roo.data.JsonStore
11574 * Small helper class to make creating Stores for JSON data easier. <br/>
11576 var store = new Roo.data.JsonStore({
11577 url: 'get-images.php',
11579 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
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.
11586 * @param {Object} config
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)
11594 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11596 * Ext JS Library 1.1.1
11597 * Copyright(c) 2006-2007, Ext JS, LLC.
11599 * Originally Released Under LGPL - original licence link has changed is not relivant.
11602 * <script type="text/javascript">
11606 Roo.data.Field = function(config){
11607 if(typeof config == "string"){
11608 config = {name: config};
11610 Roo.apply(this, config);
11613 this.type = "auto";
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];
11622 // set default sortType for strings and dates
11623 if(!this.sortType){
11626 this.sortType = st.asUCString;
11629 this.sortType = st.asDate;
11632 this.sortType = st.none;
11637 var stripRe = /[\$,%]/g;
11639 // prebuilt conversion function for this field, instead of
11640 // switching every time we're reading a value
11642 var cv, dateFormat = this.dateFormat;
11647 cv = function(v){ return v; };
11650 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11654 return v !== undefined && v !== null && v !== '' ?
11655 parseInt(String(v).replace(stripRe, ""), 10) : '';
11660 return v !== undefined && v !== null && v !== '' ?
11661 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11666 cv = function(v){ return v === true || v === "true" || v == 1; };
11673 if(v instanceof Date){
11677 if(dateFormat == "timestamp"){
11678 return new Date(v*1000);
11680 return Date.parseDate(v, dateFormat);
11682 var parsed = Date.parse(v);
11683 return parsed ? new Date(parsed) : null;
11692 Roo.data.Field.prototype = {
11700 * Ext JS Library 1.1.1
11701 * Copyright(c) 2006-2007, Ext JS, LLC.
11703 * Originally Released Under LGPL - original licence link has changed is not relivant.
11706 * <script type="text/javascript">
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.
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.
11718 Roo.data.DataReader = function(meta, recordType){
11722 this.recordType = recordType instanceof Array ?
11723 Roo.data.Record.create(recordType) : recordType;
11726 Roo.data.DataReader.prototype = {
11728 * Create an empty record
11729 * @param {Object} data (optional) - overlay some values
11730 * @return {Roo.data.Record} record created.
11732 newRow : function(d) {
11734 this.recordType.prototype.fields.each(function(c) {
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;
11744 return new this.recordType(Roo.apply(da, d));
11749 * Ext JS Library 1.1.1
11750 * Copyright(c) 2006-2007, Ext JS, LLC.
11752 * Originally Released Under LGPL - original licence link has changed is not relivant.
11755 * <script type="text/javascript">
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>
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>
11768 * Custom implementations must implement the load method as described in
11769 * {@link Roo.data.HttpProxy#load}.
11771 Roo.data.DataProxy = function(){
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.
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.
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.
11796 loadexception : true
11798 Roo.data.DataProxy.superclass.constructor.call(this);
11801 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11804 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11808 * Ext JS Library 1.1.1
11809 * Copyright(c) 2006-2007, Ext JS, LLC.
11811 * Originally Released Under LGPL - original licence link has changed is not relivant.
11814 * <script type="text/javascript">
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.
11821 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11823 Roo.data.MemoryProxy = function(data){
11827 Roo.data.MemoryProxy.superclass.constructor.call(this);
11831 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
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>
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.
11850 load : function(params, reader, callback, scope, arg){
11851 params = params || {};
11854 result = reader.readRecords(this.data);
11856 this.fireEvent("loadexception", this, arg, null, e);
11857 callback.call(scope, null, arg, false);
11860 callback.call(scope, result, arg, true);
11864 update : function(params, records){
11869 * Ext JS Library 1.1.1
11870 * Copyright(c) 2006-2007, Ext JS, LLC.
11872 * Originally Released Under LGPL - original licence link has changed is not relivant.
11875 * <script type="text/javascript">
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>
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>
11886 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
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".
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.
11895 Roo.data.HttpProxy = function(conn){
11896 Roo.data.HttpProxy.superclass.constructor.call(this);
11897 // is conn a conn config or a real conn?
11899 this.useAjax = !conn || !conn.events;
11903 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11904 // thse are take from connection...
11907 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
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)
11914 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11915 * to each request made by this object. (defaults to undefined)
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)
11921 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11924 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11930 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
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.
11938 getConnection : function(){
11939 return this.useAjax ? Roo.Ajax : this.conn;
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>
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.
11959 load : function(params, reader, callback, scope, arg){
11960 if(this.fireEvent("beforeload", this, params) !== false){
11962 params : params || {},
11964 callback : callback,
11969 callback : this.loadResponse,
11973 Roo.applyIf(o, this.conn);
11974 if(this.activeRequest){
11975 Roo.Ajax.abort(this.activeRequest);
11977 this.activeRequest = Roo.Ajax.request(o);
11979 this.conn.request(o);
11982 callback.call(scope||this, null, arg, false);
11987 loadResponse : function(o, success, response){
11988 delete this.activeRequest;
11990 this.fireEvent("loadexception", this, o, response);
11991 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11996 result = o.reader.read(response);
11998 this.fireEvent("loadexception", this, o, response, e);
11999 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12003 this.fireEvent("load", this, o, o.request.arg);
12004 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12008 update : function(dataSet){
12013 updateResponse : function(dataSet){
12018 * Ext JS Library 1.1.1
12019 * Copyright(c) 2006-2007, Ext JS, LLC.
12021 * Originally Released Under LGPL - original licence link has changed is not relivant.
12024 * <script type="text/javascript">
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>
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>
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 <script> tag.<br><br>
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:
12044 boolean scriptTag = false;
12045 String cb = request.getParameter("callback");
12048 response.setContentType("text/javascript");
12050 response.setContentType("application/x-json");
12052 Writer out = response.getWriter();
12054 out.write(cb + "(");
12056 out.print(dataBlock.toJsonString());
12063 * @param {Object} config A configuration object.
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];
12071 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12073 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12075 * @cfg {String} url The URL from which to request the data object.
12078 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
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.
12087 callbackParam : "callback",
12089 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12090 * name to the request.
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>
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.
12111 load : function(params, reader, callback, scope, arg){
12112 if(this.fireEvent("beforeload", this, params) !== false){
12114 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12116 var url = this.url;
12117 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12119 url += "&_dc=" + (new Date().getTime());
12121 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12124 cb : "stcCallback"+transId,
12125 scriptId : "stcScript"+transId,
12129 callback : callback,
12135 window[trans.cb] = function(o){
12136 conn.handleResponse(o, trans);
12139 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12141 if(this.autoAbort !== false){
12145 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
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);
12153 this.trans = trans;
12155 callback.call(scope||this, null, arg, false);
12160 isLoading : function(){
12161 return this.trans ? true : false;
12165 * Abort the current server request.
12167 abort : function(){
12168 if(this.isLoading()){
12169 this.destroyTrans(this.trans);
12174 destroyTrans : function(trans, isLoaded){
12175 this.head.removeChild(document.getElementById(trans.scriptId));
12176 clearTimeout(trans.timeoutId);
12178 window[trans.cb] = undefined;
12180 delete window[trans.cb];
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;
12187 delete window[trans.cb];
12194 handleResponse : function(o, trans){
12195 this.trans = false;
12196 this.destroyTrans(trans, true);
12199 result = trans.reader.readRecords(o);
12201 this.fireEvent("loadexception", this, o, trans.arg, e);
12202 trans.callback.call(trans.scope||window, null, trans.arg, false);
12205 this.fireEvent("load", this, o, trans.arg);
12206 trans.callback.call(trans.scope||window, result, trans.arg, true);
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);
12218 * Ext JS Library 1.1.1
12219 * Copyright(c) 2006-2007, Ext JS, LLC.
12221 * Originally Released Under LGPL - original licence link has changed is not relivant.
12224 * <script type="text/javascript">
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.
12233 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12234 * in the reply previously.
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.
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)
12250 * This would consume a JSON file like this:
12252 { 'results': 2, 'rows': [
12253 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12254 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
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
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}.
12270 Roo.data.JsonReader = function(meta, recordType){
12273 // set some defaults:
12274 Roo.applyIf(meta, {
12275 totalProperty: 'total',
12276 successProperty : 'success',
12281 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12283 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
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.
12290 metaFromRemote : false,
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.
12297 read : function(response){
12298 var json = response.responseText;
12300 var o = /* eval:var:o */ eval("("+json+")");
12302 throw {message: "JsonReader.read: Json object not found"};
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);
12313 return this.readRecords(o);
12316 // private function a store will implement
12317 onMetaChange : function(meta, recordType, o){
12324 simpleAccess: function(obj, subsc) {
12331 getJsonAccessor: function(){
12333 return function(expr) {
12335 return(re.test(expr))
12336 ? new Function("obj", "return obj." + expr)
12341 return Roo.emptyFn;
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.
12353 readRecords : function(o){
12355 * After any data loads, the raw JSON data is available for further custom processing.
12359 var s = this.meta, Record = this.recordType,
12360 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12362 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12364 if(s.totalProperty) {
12365 this.getTotal = this.getJsonAccessor(s.totalProperty);
12367 if(s.successProperty) {
12368 this.getSuccess = this.getJsonAccessor(s.successProperty);
12370 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12372 var g = this.getJsonAccessor(s.id);
12373 this.getId = function(rec) {
12375 return (r === undefined || r === "") ? null : r;
12378 this.getId = function(){return null;};
12381 for(var jj = 0; jj < fl; jj++){
12383 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12384 this.ef[jj] = this.getJsonAccessor(map);
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);
12395 if(s.successProperty){
12396 var vs = this.getSuccess(o);
12397 if(vs === false || vs === 'false'){
12402 for(var i = 0; i < c; i++){
12405 var id = this.getId(n);
12406 for(var j = 0; j < fl; j++){
12408 var v = this.ef[j](n);
12410 Roo.log('missing convert for ' + f.name);
12414 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12416 var record = new Record(values, id);
12418 records[i] = record;
12424 totalRecords : totalRecords
12429 * Ext JS Library 1.1.1
12430 * Copyright(c) 2006-2007, Ext JS, LLC.
12432 * Originally Released Under LGPL - original licence link has changed is not relivant.
12435 * <script type="text/javascript">
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>
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.
12452 var myReader = new Roo.data.ArrayReader({
12453 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12457 * This would consume an Array like this:
12459 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12461 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
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}.
12470 Roo.data.ArrayReader = function(meta, recordType){
12471 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12474 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
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.
12481 readRecords : function(o){
12482 var sid = this.meta ? this.meta.id : null;
12483 var recordType = this.recordType, fields = recordType.prototype.fields;
12486 for(var i = 0; i < root.length; i++){
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;
12495 values[f.name] = v;
12497 var record = new recordType(values, id);
12499 records[records.length] = record;
12503 totalRecords : records.length
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 ''
12526 * Create a new ComboBox.
12527 * @param {Object} config Configuration options
12529 Roo.bootstrap.ComboBox = function(config){
12530 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12534 * Fires when the dropdown list is expanded
12535 * @param {Roo.bootstrap.ComboBox} combo This combo box
12540 * Fires when the dropdown list is collapsed
12541 * @param {Roo.bootstrap.ComboBox} combo This combo box
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
12551 'beforeselect' : true,
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
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
12570 'beforequery': true,
12573 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12574 * @param {Roo.bootstrap.ComboBox} combo This combo box
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)
12586 * Fires when the remove value from the combobox array
12587 * @param {Roo.bootstrap.ComboBox} combo This combo box
12591 * @event afterremove
12592 * Fires when the remove value from the combobox array
12593 * @param {Roo.bootstrap.ComboBox} combo This combo box
12595 'afterremove' : true,
12597 * @event specialfilter
12598 * Fires when specialfilter
12599 * @param {Roo.bootstrap.ComboBox} combo This combo box
12601 'specialfilter' : true,
12604 * Fires when tick the element
12605 * @param {Roo.bootstrap.ComboBox} combo This combo box
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 .
12614 'touchviewdisplay' : true
12619 this.tickItems = [];
12621 this.selectedIndex = -1;
12622 if(this.mode == 'local'){
12623 if(config.queryDelay === undefined){
12624 this.queryDelay = 10;
12626 if(config.minChars === undefined){
12632 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
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)
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"})
12643 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
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)
12651 * @cfg {String/Roo.Template} tpl The template to use to render the output
12655 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12657 listWidth: undefined,
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')
12662 displayField: undefined,
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.
12670 valueField: undefined,
12672 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
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)
12680 hiddenName: undefined,
12682 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12686 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12688 selectedClass: 'active',
12691 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
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')
12698 listAlign: 'tl-bl?',
12700 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
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')
12707 triggerAction: 'query',
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)
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)
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')
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)
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)
12732 selectOnFocus:false,
12734 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12736 queryParam: 'query',
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...')
12741 loadingText: 'Loading...',
12743 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12747 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
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)
12756 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12760 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
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)
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)
12772 forceSelection:false,
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)
12777 typeAheadDelay : 250,
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)
12782 valueNotFoundText : undefined,
12784 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12786 blockFocus : false,
12789 * @cfg {Boolean} disableClear Disable showing of clear button.
12791 disableClear : false,
12793 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12795 alwaysQuery : false,
12798 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12803 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12805 invalidClass : "has-warning",
12808 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12810 validClass : "has-success",
12813 * @cfg {Boolean} specialFilter (true|false) special filter default false
12815 specialFilter : false,
12818 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12820 mobileTouchView : true,
12823 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12825 useNativeIOS : false,
12827 ios_options : false,
12839 btnPosition : 'right',
12840 triggerList : true,
12841 showToggleBtn : true,
12843 emptyResultText: 'Empty',
12844 triggerText : 'Select',
12847 // element that contains real text value.. (when hidden is used..)
12849 getAutoCreate : function()
12854 * Render classic select for iso
12857 if(Roo.isIOS && this.useNativeIOS){
12858 cfg = this.getAutoCreateNativeIOS();
12866 if(Roo.isTouch && this.mobileTouchView){
12867 cfg = this.getAutoCreateTouchView();
12874 if(!this.tickable){
12875 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12880 * ComboBox with tickable selections
12883 var align = this.labelAlign || this.parentLabelAlign();
12886 cls : 'form-group roo-combobox-tickable' //input-group
12889 var btn_text_select = '';
12890 var btn_text_done = '';
12891 var btn_text_cancel = '';
12893 if (this.btn_text_show) {
12894 btn_text_select = 'Select';
12895 btn_text_done = 'Done';
12896 btn_text_cancel = 'Cancel';
12901 cls : 'tickable-buttons',
12906 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12907 //html : this.triggerText
12908 html: btn_text_select
12914 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12916 html: btn_text_done
12922 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12924 html: btn_text_cancel
12930 buttons.cn.unshift({
12932 cls: 'roo-select2-search-field-input'
12938 Roo.each(buttons.cn, function(c){
12940 c.cls += ' btn-' + _this.size;
12943 if (_this.disabled) {
12954 cls: 'form-hidden-field'
12958 cls: 'roo-select2-choices',
12962 cls: 'roo-select2-search-field',
12973 cls: 'roo-select2-container input-group roo-select2-container-multi',
12978 // cls: 'typeahead typeahead-long dropdown-menu',
12979 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12984 if(this.hasFeedback && !this.allowBlank){
12988 cls: 'glyphicon form-control-feedback'
12991 combobox.cn.push(feedback);
12995 if (align ==='left' && this.fieldLabel.length) {
12997 cfg.cls += ' roo-form-group-label-left';
13002 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13003 tooltip : 'This field is required'
13008 cls : 'control-label',
13009 html : this.fieldLabel
13021 var labelCfg = cfg.cn[1];
13022 var contentCfg = cfg.cn[2];
13025 if(this.indicatorpos == 'right'){
13031 cls : 'control-label',
13035 html : this.fieldLabel
13039 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13040 tooltip : 'This field is required'
13055 labelCfg = cfg.cn[0];
13056 contentCfg = cfg.cn[1];
13060 if(this.labelWidth > 12){
13061 labelCfg.style = "width: " + this.labelWidth + 'px';
13064 if(this.labelWidth < 13 && this.labelmd == 0){
13065 this.labelmd = this.labelWidth;
13068 if(this.labellg > 0){
13069 labelCfg.cls += ' col-lg-' + this.labellg;
13070 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13073 if(this.labelmd > 0){
13074 labelCfg.cls += ' col-md-' + this.labelmd;
13075 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13078 if(this.labelsm > 0){
13079 labelCfg.cls += ' col-sm-' + this.labelsm;
13080 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13083 if(this.labelxs > 0){
13084 labelCfg.cls += ' col-xs-' + this.labelxs;
13085 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13089 } else if ( this.fieldLabel.length) {
13090 // Roo.log(" label");
13094 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13095 tooltip : 'This field is required'
13099 //cls : 'input-group-addon',
13100 html : this.fieldLabel
13105 if(this.indicatorpos == 'right'){
13109 //cls : 'input-group-addon',
13110 html : this.fieldLabel
13114 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13115 tooltip : 'This field is required'
13124 // Roo.log(" no label && no align");
13131 ['xs','sm','md','lg'].map(function(size){
13132 if (settings[size]) {
13133 cfg.cls += ' col-' + size + '-' + settings[size];
13141 _initEventsCalled : false,
13144 initEvents: function()
13146 if (this._initEventsCalled) { // as we call render... prevent looping...
13149 this._initEventsCalled = true;
13152 throw "can not find store for combo";
13155 this.indicator = this.indicatorEl();
13157 this.store = Roo.factory(this.store, Roo.data);
13158 this.store.parent = this;
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;}
13171 this.rendered = false;
13173 this.render(this.parent().getChildContainer(true), k);
13176 if(Roo.isIOS && this.useNativeIOS){
13177 this.initIOSView();
13185 if(Roo.isTouch && this.mobileTouchView){
13186 this.initTouchView();
13191 this.initTickableEvents();
13195 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13197 if(this.hiddenName){
13199 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13201 this.hiddenField.dom.value =
13202 this.hiddenValue !== undefined ? this.hiddenValue :
13203 this.value !== undefined ? this.value : '';
13205 // prevent input submission
13206 this.el.dom.removeAttribute('name');
13207 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13212 // this.el.dom.setAttribute('autocomplete', 'off');
13215 var cls = 'x-combo-list';
13217 //this.list = new Roo.Layer({
13218 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13224 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13225 _this.list.setWidth(lw);
13228 this.list.on('mouseover', this.onViewOver, this);
13229 this.list.on('mousemove', this.onViewMove, this);
13230 this.list.on('scroll', this.onViewScroll, this);
13233 this.list.swallowEvent('mousewheel');
13234 this.assetHeight = 0;
13237 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13238 this.assetHeight += this.header.getHeight();
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'));
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);
13252 this.footer = this.list.createChild({cls:cls+'-ft'});
13253 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13254 {pageSize: this.pageSize});
13258 if (this.pageTb && this.allowBlank && !this.disableClear) {
13260 this.pageTb.add(new Roo.Toolbar.Fill(), {
13261 cls: 'x-btn-icon x-btn-clear',
13263 handler: function()
13266 _this.clearValue();
13267 _this.onSelect(false, -1);
13272 this.assetHeight += this.footer.getHeight();
13277 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13280 this.view = new Roo.View(this.list, this.tpl, {
13281 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13283 //this.view.wrapEl.setDisplayed(false);
13284 this.view.on('click', this.onViewClick, this);
13287 this.store.on('beforeload', this.onBeforeLoad, this);
13288 this.store.on('load', this.onLoad, this);
13289 this.store.on('loadexception', this.onLoadException, this);
13291 if(this.resizable){
13292 this.resizer = new Roo.Resizable(this.list, {
13293 pinned:true, handles:'se'
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();
13301 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13304 if(!this.editable){
13305 this.editable = true;
13306 this.setEditable(false);
13311 if (typeof(this.events.add.listeners) != 'undefined') {
13313 this.addicon = this.wrap.createChild(
13314 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13316 this.addicon.on('click', function(e) {
13317 this.fireEvent('add', this);
13320 if (typeof(this.events.edit.listeners) != 'undefined') {
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');
13327 this.editicon.on('click', function(e) {
13329 // we fire even if inothing is selected..
13330 this.fireEvent('edit', this, this.lastData );
13336 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13337 "up" : function(e){
13338 this.inKeyMode = true;
13342 "down" : function(e){
13343 if(!this.isExpanded()){
13344 this.onTriggerClick();
13346 this.inKeyMode = true;
13351 "enter" : function(e){
13352 // this.onViewClick();
13356 if(this.fireEvent("specialkey", this, e)){
13357 this.onViewClick(false);
13363 "esc" : function(e){
13367 "tab" : function(e){
13370 if(this.fireEvent("specialkey", this, e)){
13371 this.onViewClick(false);
13379 doRelay : function(foo, bar, hname){
13380 if(hname == 'down' || this.scope.isExpanded()){
13381 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13390 this.queryDelay = Math.max(this.queryDelay || 10,
13391 this.mode == 'local' ? 10 : 250);
13394 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13396 if(this.typeAhead){
13397 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13399 if(this.editable !== false){
13400 this.inputEl().on("keyup", this.onKeyUp, this);
13402 if(this.forceSelection){
13403 this.inputEl().on('blur', this.doForce, this);
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();
13412 initTickableEvents: function()
13416 if(this.hiddenName){
13418 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13420 this.hiddenField.dom.value =
13421 this.hiddenValue !== undefined ? this.hiddenValue :
13422 this.value !== undefined ? this.value : '';
13424 // prevent input submission
13425 this.el.dom.removeAttribute('name');
13426 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13431 // this.list = this.el.select('ul.dropdown-menu',true).first();
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});
13439 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13440 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13442 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13443 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13445 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13446 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13448 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13449 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13450 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13453 this.cancelBtn.hide();
13458 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13459 _this.list.setWidth(lw);
13462 this.list.on('mouseover', this.onViewOver, this);
13463 this.list.on('mousemove', this.onViewMove, this);
13465 this.list.on('scroll', this.onViewScroll, this);
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>';
13471 this.view = new Roo.View(this.list, this.tpl, {
13472 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13475 //this.view.wrapEl.setDisplayed(false);
13476 this.view.on('click', this.onViewClick, this);
13480 this.store.on('beforeload', this.onBeforeLoad, this);
13481 this.store.on('load', this.onLoad, this);
13482 this.store.on('loadexception', this.onLoadException, this);
13485 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13486 "up" : function(e){
13487 this.inKeyMode = true;
13491 "down" : function(e){
13492 this.inKeyMode = true;
13496 "enter" : function(e){
13497 if(this.fireEvent("specialkey", this, e)){
13498 this.onViewClick(false);
13504 "esc" : function(e){
13505 this.onTickableFooterButtonClick(e, false, false);
13508 "tab" : function(e){
13509 this.fireEvent("specialkey", this, e);
13511 this.onTickableFooterButtonClick(e, false, false);
13518 doRelay : function(e, fn, key){
13519 if(this.scope.isExpanded()){
13520 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13529 this.queryDelay = Math.max(this.queryDelay || 10,
13530 this.mode == 'local' ? 10 : 250);
13533 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13535 if(this.typeAhead){
13536 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13539 if(this.editable !== false){
13540 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13543 this.indicator = this.indicatorEl();
13545 if(this.indicator){
13546 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13547 this.indicator.hide();
13552 onDestroy : function(){
13554 this.view.setStore(null);
13555 this.view.el.removeAllListeners();
13556 this.view.el.remove();
13557 this.view.purgeListeners();
13560 this.list.dom.innerHTML = '';
13564 this.store.un('beforeload', this.onBeforeLoad, this);
13565 this.store.un('load', this.onLoad, this);
13566 this.store.un('loadexception', this.onLoadException, this);
13568 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13572 fireKey : function(e){
13573 if(e.isNavKeyPress() && !this.list.isVisible()){
13574 this.fireEvent("specialkey", this, e);
13579 onResize: function(w, h){
13580 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13582 // if(typeof w != 'number'){
13583 // // we do not handle it!?!?
13586 // var tw = this.trigger.getWidth();
13587 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13588 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13590 // this.inputEl().setWidth( this.adjustWidth('input', x));
13592 // //this.trigger.setStyle('left', x+'px');
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'));
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
13610 setEditable : function(value){
13611 if(value == this.editable){
13614 this.editable = value;
13616 this.inputEl().dom.setAttribute('readOnly', true);
13617 this.inputEl().on('mousedown', this.onTriggerClick, this);
13618 this.inputEl().addClass('x-combo-noedit');
13620 this.inputEl().dom.setAttribute('readOnly', false);
13621 this.inputEl().un('mousedown', this.onTriggerClick, this);
13622 this.inputEl().removeClass('x-combo-noedit');
13628 onBeforeLoad : function(combo,opts){
13629 if(!this.hasFocus){
13633 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13635 this.restrictHeight();
13636 this.selectedIndex = -1;
13640 onLoad : function(){
13642 this.hasQuery = false;
13644 if(!this.hasFocus){
13648 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13649 this.loading.hide();
13652 if(this.store.getCount() > 0){
13655 this.restrictHeight();
13656 if(this.lastQuery == this.allQuery){
13657 if(this.editable && !this.tickable){
13658 this.inputEl().dom.select();
13662 !this.selectByValue(this.value, true) &&
13665 !this.store.lastOptions ||
13666 typeof(this.store.lastOptions.add) == 'undefined' ||
13667 this.store.lastOptions.add != true
13670 this.select(0, true);
13673 if(this.autoFocus){
13676 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13677 this.taTask.delay(this.typeAheadDelay);
13681 this.onEmptyResults();
13687 onLoadException : function()
13689 this.hasQuery = false;
13691 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13692 this.loading.hide();
13695 if(this.tickable && this.editable){
13700 // only causes errors at present
13701 //Roo.log(this.store.reader.jsonData);
13702 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13704 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
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;
13717 if(selStart != len){
13718 this.setRawValue(newValue);
13719 this.selectText(selStart, newValue.length);
13725 onSelect : function(record, index){
13727 if(this.fireEvent('beforeselect', this, record, index) !== false){
13729 this.setFromData(index > -1 ? record.data : false);
13732 this.fireEvent('select', this, record, index);
13737 * Returns the currently selected field value or empty string if no value is set.
13738 * @return {String} value The selected value
13740 getValue : function()
13742 if(Roo.isIOS && this.useNativeIOS){
13743 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13747 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13750 if(this.valueField){
13751 return typeof this.value != 'undefined' ? this.value : '';
13753 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13757 getRawValue : function()
13759 if(Roo.isIOS && this.useNativeIOS){
13760 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13763 var v = this.inputEl().getValue();
13769 * Clears any text/value currently set in the field
13771 clearValue : function(){
13773 if(this.hiddenField){
13774 this.hiddenField.dom.value = '';
13777 this.setRawValue('');
13778 this.lastSelectionText = '';
13779 this.lastData = false;
13781 var close = this.closeTriggerEl();
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
13798 setValue : function(v)
13800 if(Roo.isIOS && this.useNativeIOS){
13801 this.setIOSValue(v);
13811 if(this.valueField){
13812 var r = this.findRecord(this.valueField, v);
13814 text = r.data[this.displayField];
13815 }else if(this.valueNotFoundText !== undefined){
13816 text = this.valueNotFoundText;
13819 this.lastSelectionText = text;
13820 if(this.hiddenField){
13821 this.hiddenField.dom.value = v;
13823 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13826 var close = this.closeTriggerEl();
13829 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13835 * @property {Object} the last set data for the element
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?
13843 setFromData : function(o){
13850 var dv = ''; // display value
13851 var vv = ''; // value value..
13853 if (this.displayField) {
13854 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13856 // this is an error condition!!!
13857 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13860 if(this.valueField){
13861 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13864 var close = this.closeTriggerEl();
13867 if(dv.length || vv * 1 > 0){
13869 this.blockFocus=true;
13875 if(this.hiddenField){
13876 this.hiddenField.dom.value = vv;
13878 this.lastSelectionText = dv;
13879 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
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);
13893 reset : function(){
13894 // overridden so that last data is reset..
13901 this.setValue(this.originalValue);
13902 //this.clearInvalid();
13903 this.lastData = false;
13905 this.view.clearSelections();
13911 findRecord : function(prop, value){
13913 if(this.store.getCount() > 0){
13914 this.store.each(function(r){
13915 if(r.data[prop] == value){
13925 getName: function()
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 || '');
13933 onViewMove : function(e, t){
13934 this.inKeyMode = false;
13938 onViewOver : function(e, t){
13939 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13942 var item = this.view.findItemFromChild(t);
13945 var index = this.view.indexOf(item);
13946 this.select(index, false);
13951 onViewClick : function(view, doFocus, el, e)
13953 var index = this.view.getSelectedIndexes()[0];
13955 var r = this.store.getAt(index);
13959 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13966 Roo.each(this.tickItems, function(v,k){
13968 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13970 _this.tickItems.splice(k, 1);
13972 if(typeof(e) == 'undefined' && view == false){
13973 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
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);
13989 if(typeof(e) == 'undefined' && view == false){
13990 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13997 this.onSelect(r, index);
13999 if(doFocus !== false && !this.blockFocus){
14000 this.inputEl().focus();
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();
14018 onEmptyResults : function(){
14020 if(this.tickable && this.editable){
14021 this.hasFocus = false;
14022 this.restrictHeight();
14030 * Returns true if the dropdown list is expanded, else false.
14032 isExpanded : function(){
14033 return this.list.isVisible();
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
14044 selectByValue : function(v, scrollIntoView){
14045 if(v !== undefined && v !== null){
14046 var r = this.findRecord(this.valueField || this.displayField, v);
14048 this.select(this.store.indexOf(r), scrollIntoView);
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)
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);
14068 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14071 this.list.scrollChildIntoView(el, false);
14077 selectNext : function(){
14078 var ct = this.store.getCount();
14080 if(this.selectedIndex == -1){
14082 }else if(this.selectedIndex < ct-1){
14083 this.select(this.selectedIndex+1);
14089 selectPrev : function(){
14090 var ct = this.store.getCount();
14092 if(this.selectedIndex == -1){
14094 }else if(this.selectedIndex != 0){
14095 this.select(this.selectedIndex-1);
14101 onKeyUp : function(e){
14102 if(this.editable !== false && !e.isSpecialKey()){
14103 this.lastKey = e.getKey();
14104 this.dqTask.delay(this.queryDelay);
14109 validateBlur : function(){
14110 return !this.list || !this.list.isVisible();
14114 initQuery : function(){
14116 var v = this.getRawValue();
14118 if(this.tickable && this.editable){
14119 v = this.tickableInputEl().getValue();
14126 doForce : function(){
14127 if(this.inputEl().dom.value.length > 0){
14128 this.inputEl().dom.value =
14129 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
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)
14142 doQuery : function(q, forceAll){
14144 if(q === undefined || q === null){
14149 forceAll: forceAll,
14153 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14158 forceAll = qe.forceAll;
14159 if(forceAll === true || (q.length >= this.minChars)){
14161 this.hasQuery = true;
14163 if(this.lastQuery != q || this.alwaysQuery){
14164 this.lastQuery = q;
14165 if(this.mode == 'local'){
14166 this.selectedIndex = -1;
14168 this.store.clearFilter();
14171 if(this.specialFilter){
14172 this.fireEvent('specialfilter', this);
14177 this.store.filter(this.displayField, q);
14180 this.store.fireEvent("datachanged", this.store);
14187 this.store.baseParams[this.queryParam] = q;
14189 var options = {params : this.getParams(q)};
14192 options.add = true;
14193 options.params.start = this.page * this.pageSize;
14196 this.store.load(options);
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
14206 this.selectedIndex = -1;
14211 this.loadNext = false;
14215 getParams : function(q){
14217 //p[this.queryParam] = q;
14221 p.limit = this.pageSize;
14227 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14229 collapse : function(){
14230 if(!this.isExpanded()){
14236 this.hasFocus = false;
14240 this.cancelBtn.hide();
14241 this.trigger.show();
14244 this.tickableInputEl().dom.value = '';
14245 this.tickableInputEl().blur();
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);
14255 this.fireEvent('collapse', this);
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;
14266 if (in_combo || in_list || is_list) {
14267 //e.stopPropagation();
14272 this.onTickableFooterButtonClick(e, false, false);
14280 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14282 expand : function(){
14284 if(this.isExpanded() || !this.hasFocus){
14288 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14289 this.list.setWidth(lw);
14295 this.restrictHeight();
14299 this.tickItems = Roo.apply([], this.item);
14302 this.cancelBtn.show();
14303 this.trigger.hide();
14306 this.tickableInputEl().focus();
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);
14317 this.fireEvent('expand', this);
14321 // Implements the default empty TriggerField.onTriggerClick function
14322 onTriggerClick : function(e)
14324 Roo.log('trigger click');
14326 if(this.disabled || !this.triggerList){
14331 this.loadNext = false;
14333 if(this.isExpanded()){
14335 if (!this.blockFocus) {
14336 this.inputEl().focus();
14340 this.hasFocus = true;
14341 if(this.triggerAction == 'all') {
14342 this.doQuery(this.allQuery, true);
14344 this.doQuery(this.getRawValue());
14346 if (!this.blockFocus) {
14347 this.inputEl().focus();
14352 onTickableTriggerClick : function(e)
14359 this.loadNext = false;
14360 this.hasFocus = true;
14362 if(this.triggerAction == 'all') {
14363 this.doQuery(this.allQuery, true);
14365 this.doQuery(this.getRawValue());
14369 onSearchFieldClick : function(e)
14371 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14372 this.onTickableFooterButtonClick(e, false, false);
14376 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14381 this.loadNext = false;
14382 this.hasFocus = true;
14384 if(this.triggerAction == 'all') {
14385 this.doQuery(this.allQuery, true);
14387 this.doQuery(this.getRawValue());
14391 listKeyPress : function(e)
14393 //Roo.log('listkeypress');
14394 // scroll to first matching element based on key pres..
14395 if (e.isSpecialKey()) {
14398 var k = String.fromCharCode(e.getKey()).toUpperCase();
14401 var csel = this.view.getSelectedNodes();
14402 var cselitem = false;
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) {
14412 this.store.each(function(v) {
14414 // start at existing selection.
14415 if (cselitem.id == v.id) {
14421 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14422 match = this.store.indexOf(v);
14428 if (match === false) {
14429 return true; // no more action?
14432 this.view.select(match);
14433 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14434 sn.scrollIntoView(sn.dom.parentNode, false);
14437 onViewScroll : function(e, t){
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){
14443 this.hasQuery = true;
14445 this.loading = this.list.select('.loading', true).first();
14447 if(this.loading === null){
14448 this.list.createChild({
14450 cls: 'loading roo-select2-more-results roo-select2-active',
14451 html: 'Loading more results...'
14454 this.loading = this.list.select('.loading', true).first();
14456 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14458 this.loading.hide();
14461 this.loading.show();
14466 this.loadNext = true;
14468 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14473 addItem : function(o)
14475 var dv = ''; // display value
14477 if (this.displayField) {
14478 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14480 // this is an error condition!!!
14481 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14488 var choice = this.choices.createChild({
14490 cls: 'roo-select2-search-choice',
14499 cls: 'roo-select2-search-choice-close fa fa-times',
14504 }, this.searchField);
14506 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14508 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14516 this.inputEl().dom.value = '';
14521 onRemoveItem : function(e, _self, o)
14523 e.preventDefault();
14525 this.lastItem = Roo.apply([], this.item);
14527 var index = this.item.indexOf(o.data) * 1;
14530 Roo.log('not this item?!');
14534 this.item.splice(index, 1);
14539 this.fireEvent('remove', this, e);
14545 syncValue : function()
14547 if(!this.item.length){
14554 Roo.each(this.item, function(i){
14555 if(_this.valueField){
14556 value.push(i[_this.valueField]);
14563 this.value = value.join(',');
14565 if(this.hiddenField){
14566 this.hiddenField.dom.value = this.value;
14569 this.store.fireEvent("datachanged", this.store);
14574 clearItem : function()
14576 if(!this.multiple){
14582 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14590 if(this.tickable && !Roo.isTouch){
14591 this.view.refresh();
14595 inputEl: function ()
14597 if(Roo.isIOS && this.useNativeIOS){
14598 return this.el.select('select.roo-ios-select', true).first();
14601 if(Roo.isTouch && this.mobileTouchView){
14602 return this.el.select('input.form-control',true).first();
14606 return this.searchField;
14609 return this.el.select('input.form-control',true).first();
14612 onTickableFooterButtonClick : function(e, btn, el)
14614 e.preventDefault();
14616 this.lastItem = Roo.apply([], this.item);
14618 if(btn && btn.name == 'cancel'){
14619 this.tickItems = Roo.apply([], this.item);
14628 Roo.each(this.tickItems, function(o){
14636 validate : function()
14638 var v = this.getRawValue();
14641 v = this.getValue();
14644 if(this.disabled || this.allowBlank || v.length){
14649 this.markInvalid();
14653 tickableInputEl : function()
14655 if(!this.tickable || !this.editable){
14656 return this.inputEl();
14659 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14663 getAutoCreateTouchView : function()
14668 cls: 'form-group' //input-group
14674 type : this.inputType,
14675 cls : 'form-control x-combo-noedit',
14676 autocomplete: 'new-password',
14677 placeholder : this.placeholder || '',
14682 input.name = this.name;
14686 input.cls += ' input-' + this.size;
14689 if (this.disabled) {
14690 input.disabled = true;
14701 inputblock.cls += ' input-group';
14703 inputblock.cn.unshift({
14705 cls : 'input-group-addon',
14710 if(this.removable && !this.multiple){
14711 inputblock.cls += ' roo-removable';
14713 inputblock.cn.push({
14716 cls : 'roo-combo-removable-btn close'
14720 if(this.hasFeedback && !this.allowBlank){
14722 inputblock.cls += ' has-feedback';
14724 inputblock.cn.push({
14726 cls: 'glyphicon form-control-feedback'
14733 inputblock.cls += (this.before) ? '' : ' input-group';
14735 inputblock.cn.push({
14737 cls : 'input-group-addon',
14748 cls: 'form-hidden-field'
14762 cls: 'form-hidden-field'
14766 cls: 'roo-select2-choices',
14770 cls: 'roo-select2-search-field',
14783 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14789 if(!this.multiple && this.showToggleBtn){
14796 if (this.caret != false) {
14799 cls: 'fa fa-' + this.caret
14806 cls : 'input-group-addon btn dropdown-toggle',
14811 cls: 'combobox-clear',
14825 combobox.cls += ' roo-select2-container-multi';
14828 var align = this.labelAlign || this.parentLabelAlign();
14830 if (align ==='left' && this.fieldLabel.length) {
14835 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14836 tooltip : 'This field is required'
14840 cls : 'control-label',
14841 html : this.fieldLabel
14852 var labelCfg = cfg.cn[1];
14853 var contentCfg = cfg.cn[2];
14856 if(this.indicatorpos == 'right'){
14861 cls : 'control-label',
14865 html : this.fieldLabel
14869 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14870 tooltip : 'This field is required'
14883 labelCfg = cfg.cn[0];
14884 contentCfg = cfg.cn[1];
14889 if(this.labelWidth > 12){
14890 labelCfg.style = "width: " + this.labelWidth + 'px';
14893 if(this.labelWidth < 13 && this.labelmd == 0){
14894 this.labelmd = this.labelWidth;
14897 if(this.labellg > 0){
14898 labelCfg.cls += ' col-lg-' + this.labellg;
14899 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14902 if(this.labelmd > 0){
14903 labelCfg.cls += ' col-md-' + this.labelmd;
14904 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14907 if(this.labelsm > 0){
14908 labelCfg.cls += ' col-sm-' + this.labelsm;
14909 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14912 if(this.labelxs > 0){
14913 labelCfg.cls += ' col-xs-' + this.labelxs;
14914 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14918 } else if ( this.fieldLabel.length) {
14922 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14923 tooltip : 'This field is required'
14927 cls : 'control-label',
14928 html : this.fieldLabel
14939 if(this.indicatorpos == 'right'){
14943 cls : 'control-label',
14944 html : this.fieldLabel,
14948 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14949 tooltip : 'This field is required'
14966 var settings = this;
14968 ['xs','sm','md','lg'].map(function(size){
14969 if (settings[size]) {
14970 cfg.cls += ' col-' + size + '-' + settings[size];
14977 initTouchView : function()
14979 this.renderTouchView();
14981 this.touchViewEl.on('scroll', function(){
14982 this.el.dom.scrollTop = 0;
14985 this.originalValue = this.getValue();
14987 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14989 this.inputEl().on("click", this.showTouchView, this);
14990 if (this.triggerEl) {
14991 this.triggerEl.on("click", this.showTouchView, this);
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);
14998 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15000 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15001 this.store.on('load', this.onTouchViewLoad, this);
15002 this.store.on('loadexception', this.onTouchViewLoadException, this);
15004 if(this.hiddenName){
15006 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15008 this.hiddenField.dom.value =
15009 this.hiddenValue !== undefined ? this.hiddenValue :
15010 this.value !== undefined ? this.value : '';
15012 this.el.dom.removeAttribute('name');
15013 this.hiddenField.dom.setAttribute('name', this.hiddenName);
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();
15021 if(this.removable && !this.multiple){
15022 var close = this.closeTriggerEl();
15024 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15025 close.on('click', this.removeBtnClick, this, close);
15029 * fix the bug in Safari iOS8
15031 this.inputEl().on("focus", function(e){
15032 document.activeElement.blur();
15040 renderTouchView : function()
15042 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15043 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15045 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15046 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
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');
15052 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15053 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15055 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15056 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15060 showTouchView : function()
15066 this.touchViewHeaderEl.hide();
15068 if(this.modalTitle.length){
15069 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15070 this.touchViewHeaderEl.show();
15073 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15074 this.touchViewEl.show();
15076 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15078 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15079 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15081 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15083 if(this.modalTitle.length){
15084 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15087 this.touchViewBodyEl.setHeight(bodyHeight);
15091 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15093 this.touchViewEl.addClass('in');
15096 this.doTouchViewQuery();
15100 hideTouchView : function()
15102 this.touchViewEl.removeClass('in');
15106 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15108 this.touchViewEl.setStyle('display', 'none');
15113 setTouchViewValue : function()
15120 Roo.each(this.tickItems, function(o){
15125 this.hideTouchView();
15128 doTouchViewQuery : function()
15137 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15141 if(!this.alwaysQuery || this.mode == 'local'){
15142 this.onTouchViewLoad();
15149 onTouchViewBeforeLoad : function(combo,opts)
15155 onTouchViewLoad : function()
15157 if(this.store.getCount() < 1){
15158 this.onTouchViewEmptyResults();
15162 this.clearTouchView();
15164 var rawValue = this.getRawValue();
15166 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15168 this.tickItems = [];
15170 this.store.data.each(function(d, rowIndex){
15171 var row = this.touchViewListGroup.createChild(template);
15173 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15174 row.addClass(d.data.cls);
15177 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15180 html : d.data[this.displayField]
15183 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15184 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15187 row.removeClass('selected');
15188 if(!this.multiple && this.valueField &&
15189 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15192 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15193 row.addClass('selected');
15196 if(this.multiple && this.valueField &&
15197 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15201 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15202 this.tickItems.push(d.data);
15205 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15209 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15211 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15213 if(this.modalTitle.length){
15214 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15217 var listHeight = this.touchViewListGroup.getHeight();
15221 if(firstChecked && listHeight > bodyHeight){
15222 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15227 onTouchViewLoadException : function()
15229 this.hideTouchView();
15232 onTouchViewEmptyResults : function()
15234 this.clearTouchView();
15236 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15238 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15242 clearTouchView : function()
15244 this.touchViewListGroup.dom.innerHTML = '';
15247 onTouchViewClick : function(e, el, o)
15249 e.preventDefault();
15252 var rowIndex = o.rowIndex;
15254 var r = this.store.getAt(rowIndex);
15256 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
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');
15263 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15265 this.setFromData(r.data);
15267 var close = this.closeTriggerEl();
15273 this.hideTouchView();
15275 this.fireEvent('select', this, r, rowIndex);
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);
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);
15292 getAutoCreateNativeIOS : function()
15295 cls: 'form-group' //input-group,
15300 cls : 'roo-ios-select'
15304 combobox.name = this.name;
15307 if (this.disabled) {
15308 combobox.disabled = true;
15311 var settings = this;
15313 ['xs','sm','md','lg'].map(function(size){
15314 if (settings[size]) {
15315 cfg.cls += ' col-' + size + '-' + settings[size];
15325 initIOSView : function()
15327 this.store.on('load', this.onIOSViewLoad, this);
15332 onIOSViewLoad : function()
15334 if(this.store.getCount() < 1){
15338 this.clearIOSView();
15340 if(this.allowBlank) {
15342 var default_text = '-- SELECT --';
15344 if(this.placeholder.length){
15345 default_text = this.placeholder;
15348 if(this.emptyTitle.length){
15349 default_text += ' - ' + this.emptyTitle + ' -';
15352 var opt = this.inputEl().createChild({
15355 html : default_text
15359 o[this.valueField] = 0;
15360 o[this.displayField] = default_text;
15362 this.ios_options.push({
15369 this.store.data.each(function(d, rowIndex){
15373 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15374 html = d.data[this.displayField];
15379 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15380 value = d.data[this.valueField];
15389 if(this.value == d.data[this.valueField]){
15390 option['selected'] = true;
15393 var opt = this.inputEl().createChild(option);
15395 this.ios_options.push({
15402 this.inputEl().on('change', function(){
15403 this.fireEvent('select', this);
15408 clearIOSView: function()
15410 this.inputEl().dom.innerHTML = '';
15412 this.ios_options = [];
15415 setIOSValue: function(v)
15419 if(!this.ios_options){
15423 Roo.each(this.ios_options, function(opts){
15425 opts.el.dom.removeAttribute('selected');
15427 if(opts.data[this.valueField] != v){
15431 opts.el.dom.setAttribute('selected', true);
15437 * @cfg {Boolean} grow
15441 * @cfg {Number} growMin
15445 * @cfg {Number} growMax
15454 Roo.apply(Roo.bootstrap.ComboBox, {
15458 cls: 'modal-header',
15480 cls: 'list-group-item',
15484 cls: 'roo-combobox-list-group-item-value'
15488 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15502 listItemCheckbox : {
15504 cls: 'list-group-item',
15508 cls: 'roo-combobox-list-group-item-value'
15512 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15528 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15533 cls: 'modal-footer',
15541 cls: 'col-xs-6 text-left',
15544 cls: 'btn btn-danger roo-touch-view-cancel',
15550 cls: 'col-xs-6 text-right',
15553 cls: 'btn btn-success roo-touch-view-ok',
15564 Roo.apply(Roo.bootstrap.ComboBox, {
15566 touchViewTemplate : {
15568 cls: 'modal fade roo-combobox-touch-view',
15572 cls: 'modal-dialog',
15573 style : 'position:fixed', // we have to fix position....
15577 cls: 'modal-content',
15579 Roo.bootstrap.ComboBox.header,
15580 Roo.bootstrap.ComboBox.body,
15581 Roo.bootstrap.ComboBox.footer
15590 * Ext JS Library 1.1.1
15591 * Copyright(c) 2006-2007, Ext JS, LLC.
15593 * Originally Released Under LGPL - original licence link has changed is not relivant.
15596 * <script type="text/javascript">
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:
15606 var store = new Roo.data.Store(...);
15608 var view = new Roo.View({
15610 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15612 singleSelect: true,
15613 selectedClass: "ydataview-selected",
15617 // listen for node click?
15618 view.on("click", function(vw, index, node, e){
15619 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15623 dataModel.load("foobar.xml");
15625 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
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>
15630 * Note: old style constructor is still suported (container, template, config)
15633 * Create a new View
15634 * @param {Object} config The config object
15637 Roo.View = function(config, depreciated_tpl, depreciated_config){
15639 this.parent = false;
15641 if (typeof(depreciated_tpl) == 'undefined') {
15642 // new way.. - universal constructor.
15643 Roo.apply(this, config);
15644 this.el = Roo.get(this.el);
15647 this.el = Roo.get(config);
15648 this.tpl = depreciated_tpl;
15649 Roo.apply(this, depreciated_config);
15651 this.wrapEl = this.el.wrap().wrap();
15652 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15655 if(typeof(this.tpl) == "string"){
15656 this.tpl = new Roo.Template(this.tpl);
15658 // support xtype ctors..
15659 this.tpl = new Roo.factory(this.tpl, Roo);
15663 this.tpl.compile();
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
15675 "beforeclick" : true,
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
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
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
15702 "contextmenu" : true,
15704 * @event selectionchange
15705 * Fires when the selected nodes change.
15706 * @param {Roo.View} this
15707 * @param {Array} selections Array of the selected nodes
15709 "selectionchange" : true,
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
15718 "beforeselect" : true,
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)
15725 "preparedata" : true
15733 "click": this.onClick,
15734 "dblclick": this.onDblClick,
15735 "contextmenu": this.onContextMenu,
15739 this.selections = [];
15741 this.cmp = new Roo.CompositeElementLite([]);
15743 this.store = Roo.factory(this.store, Roo.data);
15744 this.setStore(this.store, true);
15747 if ( this.footer && this.footer.xtype) {
15749 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15751 this.footer.dataSource = this.store;
15752 this.footer.container = fctr;
15753 this.footer = Roo.factory(this.footer, Roo);
15754 fctr.insertFirst(this.el);
15756 // this is a bit insane - as the paging toolbar seems to detach the el..
15757 // dom.parentNode.parentNode.parentNode
15758 // they get detached?
15762 Roo.View.superclass.constructor.call(this);
15767 Roo.extend(Roo.View, Roo.util.Observable, {
15770 * @cfg {Roo.data.Store} store Data store to load data from.
15775 * @cfg {String|Roo.Element} el The container element.
15780 * @cfg {String|Roo.Template} tpl The template used by this View
15784 * @cfg {String} dataName the named area of the template to use as the data area
15785 * Works with domtemplates roo-name="name"
15789 * @cfg {String} selectedClass The css class to add to selected nodes
15791 selectedClass : "x-view-selected",
15793 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15798 * @cfg {String} text to display on mask (default Loading)
15802 * @cfg {Boolean} multiSelect Allow multiple selection
15804 multiSelect : false,
15806 * @cfg {Boolean} singleSelect Allow single selection
15808 singleSelect: false,
15811 * @cfg {Boolean} toggleSelect - selecting
15813 toggleSelect : false,
15816 * @cfg {Boolean} tickable - selecting
15821 * Returns the element this view is bound to.
15822 * @return {Roo.Element}
15824 getEl : function(){
15825 return this.wrapEl;
15831 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15833 refresh : function(){
15834 //Roo.log('refresh');
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> ?????
15848 this.clearSelections();
15849 this.el.update("");
15851 var records = this.store.getRange();
15852 if(records.length < 1) {
15854 // is this valid?? = should it render a template??
15856 this.el.update(this.emptyText);
15860 if (this.dataName) {
15861 this.el.update(t.apply(this.store.meta)); //????
15862 el = this.el.child('.roo-tpl-' + this.dataName);
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]);
15869 var d = Roo.apply({}, data);
15872 Roo.apply(d, {'roo-id' : Roo.id()});
15876 Roo.each(this.parent.item, function(item){
15877 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15880 Roo.apply(d, {'roo-data-checked' : 'checked'});
15884 html[html.length] = Roo.util.Format.trim(
15886 t.applySubtemplate(this.dataName, d, this.store.meta) :
15893 el.update(html.join(""));
15894 this.nodes = el.dom.childNodes;
15895 this.updateIndexes(0);
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).
15906 prepareData : function(data, index, record)
15908 this.fireEvent("preparedata", this, data, index, record);
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);
15925 onAdd : function(ds, records, index)
15927 //Roo.log(['on Add', ds, records, index] );
15928 this.clearSelections();
15929 if(this.nodes.length == 0){
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]);
15937 this.tpl.insertBefore(n, d);
15940 this.tpl.append(this.el, d);
15943 this.updateIndexes(index);
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) :
15953 el.dom.removeChild(this.nodes[index]);
15954 this.updateIndexes(index);
15958 * Refresh an individual node.
15959 * @param {Number} index
15961 refreshNode : function(index){
15962 this.onUpdate(this.store, this.store.getAt(index));
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;
15975 * Changes the data store this view uses and refresh the view.
15976 * @param {Store} store
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);
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);
16006 * onbeforeLoad - masks the loading area.
16009 onBeforeLoad : function(store,opts)
16011 //Roo.log('onBeforeLoad');
16013 this.el.update("");
16015 this.el.mask(this.mask ? this.mask : "Loading" );
16017 onLoad : function ()
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
16028 findItemFromChild : function(node){
16029 var el = this.dataName ?
16030 this.el.child('.roo-tpl-' + this.dataName,true) :
16033 if(!node || node.parentNode == el){
16036 var p = node.parentNode;
16037 while(p && p != el){
16038 if(p.parentNode == el){
16047 onClick : function(e){
16048 var item = this.findItemFromChild(e.getTarget());
16050 var index = this.indexOf(item);
16051 if(this.onItemClick(item, index, e) !== false){
16052 this.fireEvent("click", this, index, item, e);
16055 this.clearSelections();
16060 onContextMenu : function(e){
16061 var item = this.findItemFromChild(e.getTarget());
16063 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16068 onDblClick : function(e){
16069 var item = this.findItemFromChild(e.getTarget());
16071 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16075 onItemClick : function(item, index, e)
16077 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16080 if (this.toggleSelect) {
16081 var m = this.isSelected(item) ? 'unselect' : 'select';
16084 _t[m](item, true, false);
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);
16091 this.select(item, this.multiSelect && e.ctrlKey);
16092 this.lastSelection = item;
16095 if(!this.tickable){
16096 e.preventDefault();
16104 * Get the number of selected nodes.
16107 getSelectionCount : function(){
16108 return this.selections.length;
16112 * Get the currently selected nodes.
16113 * @return {Array} An array of HTMLElements
16115 getSelectedNodes : function(){
16116 return this.selections;
16120 * Get the indexes of the selected nodes.
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);
16132 * Clear all selections
16133 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
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);
16147 * Returns true if the passed node is selected
16148 * @param {HTMLElement/Number} node The node or node index
16149 * @return {Boolean}
16151 isSelected : function(node){
16152 var s = this.selections;
16156 node = this.getNode(node);
16157 return s.indexOf(node) !== -1;
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
16166 select : function(nodeInfo, keepExisting, suppressEvent){
16167 if(nodeInfo instanceof Array){
16169 this.clearSelections(true);
16171 for(var i = 0, len = nodeInfo.length; i < len; i++){
16172 this.select(nodeInfo[i], true, true);
16176 var node = this.getNode(nodeInfo);
16177 if(!node || this.isSelected(node)){
16178 return; // already selected.
16181 this.clearSelections(true);
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);
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
16200 unselect : function(nodeInfo, keepExisting, suppressEvent)
16202 if(nodeInfo instanceof Array){
16203 Roo.each(this.selections, function(s) {
16204 this.unselect(s, nodeInfo);
16208 var node = this.getNode(nodeInfo);
16209 if(!node || !this.isSelected(node)){
16210 //Roo.log("not selected");
16211 return; // not selected.
16215 Roo.each(this.selections, function(s) {
16217 Roo.fly(node).removeClass(this.selectedClass);
16224 this.selections= ns;
16225 this.fireEvent("selectionchange", this, this.selections);
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
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];
16243 * Gets a range template nodes.
16244 * @param {Number} startIndex
16245 * @param {Number} endIndex
16246 * @return {Array} An array of nodes
16248 getNodes : function(start, end){
16249 var ns = this.nodes;
16250 start = start || 0;
16251 end = typeof end == "undefined" ? ns.length - 1 : end;
16254 for(var i = start; i <= end; i++){
16258 for(var i = start; i >= end; i--){
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
16270 indexOf : function(node){
16271 node = this.getNode(node);
16272 if(typeof node.nodeIndex == "number"){
16273 return node.nodeIndex;
16275 var ns = this.nodes;
16276 for(var i = 0, len = ns.length; i < len; i++){
16287 * based on jquery fullcalendar
16291 Roo.bootstrap = Roo.bootstrap || {};
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
16300 * Create a new Container
16301 * @param {Object} config The config object
16306 Roo.bootstrap.Calendar = function(config){
16307 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16311 * Fires when a date is selected
16312 * @param {DatePicker} this
16313 * @param {Date} date The selected date
16317 * @event monthchange
16318 * Fires when the displayed month changes
16319 * @param {DatePicker} this
16320 * @param {Date} date The selected month
16322 'monthchange': true,
16324 * @event evententer
16325 * Fires when mouse over an event
16326 * @param {Calendar} this
16327 * @param {event} Event
16329 'evententer': true,
16331 * @event eventleave
16332 * Fires when the mouse leaves an
16333 * @param {Calendar} this
16336 'eventleave': true,
16338 * @event eventclick
16339 * Fires when the mouse click an
16340 * @param {Calendar} this
16349 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16352 * @cfg {Number} startDay
16353 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16361 getAutoCreate : function(){
16364 var fc_button = function(name, corner, style, content ) {
16365 return Roo.apply({},{
16367 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16369 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16372 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16383 style : 'width:100%',
16390 cls : 'fc-header-left',
16392 fc_button('prev', 'left', 'arrow', '‹' ),
16393 fc_button('next', 'right', 'arrow', '›' ),
16394 { tag: 'span', cls: 'fc-header-space' },
16395 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16403 cls : 'fc-header-center',
16407 cls: 'fc-header-title',
16410 html : 'month / year'
16418 cls : 'fc-header-right',
16420 /* fc_button('month', 'left', '', 'month' ),
16421 fc_button('week', '', '', 'week' ),
16422 fc_button('day', 'right', '', 'day' )
16434 header = this.header;
16437 var cal_heads = function() {
16439 // fixme - handle this.
16441 for (var i =0; i < Date.dayNames.length; i++) {
16442 var d = Date.dayNames[i];
16445 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16446 html : d.substring(0,3)
16450 ret[0].cls += ' fc-first';
16451 ret[6].cls += ' fc-last';
16454 var cal_cell = function(n) {
16457 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16462 cls: 'fc-day-number',
16466 cls: 'fc-day-content',
16470 style: 'position: relative;' // height: 17px;
16482 var cal_rows = function() {
16485 for (var r = 0; r < 6; r++) {
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()));
16497 row.cn[0].cls+=' fc-first';
16498 row.cn[0].cn[0].style = 'min-height:90px';
16499 row.cn[6].cls+=' fc-last';
16503 ret[0].cls += ' fc-first';
16504 ret[4].cls += ' fc-prev-last';
16505 ret[5].cls += ' fc-last';
16512 cls: 'fc-border-separate',
16513 style : 'width:100%',
16521 cls : 'fc-first fc-last',
16539 cls : 'fc-content',
16540 style : "position: relative;",
16543 cls : 'fc-view fc-view-month fc-grid',
16544 style : 'position: relative',
16545 unselectable : 'on',
16548 cls : 'fc-event-container',
16549 style : 'position:absolute;z-index:8;top:0;left:0;'
16567 initEvents : function()
16570 throw "can not find store for calendar";
16576 style: "text-align:center",
16580 style: "background-color:white;width:50%;margin:250 auto",
16584 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16595 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
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();
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);
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');
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');
16620 this.on('monthchange', this.onMonthChange, this);
16622 this.update(new Date().clearTime());
16625 resize : function() {
16626 var sz = this.el.getSize();
16628 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16629 this.el.select('.fc-day-content div',true).setHeight(34);
16634 showPrevMonth : function(e){
16635 this.update(this.activeDate.add("mo", -1));
16637 showToday : function(e){
16638 this.update(new Date().clearTime());
16641 showNextMonth : function(e){
16642 this.update(this.activeDate.add("mo", 1));
16646 showPrevYear : function(){
16647 this.update(this.activeDate.add("y", -1));
16651 showNextYear : function(){
16652 this.update(this.activeDate.add("y", 1));
16657 update : function(date)
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');
16666 // this.fireEvent('monthchange', this, date);
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){}
16683 var days = date.getDaysInMonth();
16685 var firstOfMonth = date.getFirstDateOfMonth();
16686 var startingPos = firstOfMonth.getDay()-this.startDay;
16688 if(startingPos < this.startDay){
16692 var pm = date.add(Date.MONTH, -1);
16693 var prevStart = pm.getDaysInMonth()-startingPos;
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');
16699 var cells = this.cells.elements;
16700 var textEls = this.textNodes;
16702 Roo.each(cells, function(cell){
16703 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16706 days += startingPos;
16708 // convert everything to numbers so it's fast
16709 var day = 86400000;
16710 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16713 //Roo.log(prevStart);
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;
16725 var setCellClass = function(cal, cell){
16729 //Roo.log('set Cell Class');
16731 var t = d.getTime();
16735 cell.dateValue = t;
16737 cell.className += " fc-today";
16738 cell.className += " fc-state-highlight";
16739 cell.title = cal.todayText;
16742 // disable highlight in other month..
16743 //cell.className += " fc-state-highlight";
16748 cell.className = " fc-state-disabled";
16749 cell.title = cal.minText;
16753 cell.className = " fc-state-disabled";
16754 cell.title = cal.maxText;
16758 if(ddays.indexOf(d.getDay()) != -1){
16759 cell.title = ddaysText;
16760 cell.className = " fc-state-disabled";
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";
16771 if (!cell.initialClassName) {
16772 cell.initialClassName = cell.dom.className;
16775 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16780 for(; i < startingPos; i++) {
16781 textEls[i].innerHTML = (++prevStart);
16782 d.setDate(d.getDate()+1);
16784 cells[i].className = "fc-past fc-other-month";
16785 setCellClass(this, cells[i]);
16790 for(; i < days; i++){
16791 intDay = i - startingPos + 1;
16792 textEls[i].innerHTML = (intDay);
16793 d.setDate(d.getDate()+1);
16795 cells[i].className = ''; // "x-date-active";
16796 setCellClass(this, cells[i]);
16800 for(; i < 42; i++) {
16801 textEls[i].innerHTML = (++extraDays);
16802 d.setDate(d.getDate()+1);
16804 cells[i].className = "fc-future fc-other-month";
16805 setCellClass(this, cells[i]);
16808 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16810 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
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();
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');
16820 this.fireEvent('monthchange', this, date);
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]);
16843 findCell : function(dt) {
16844 dt = dt.clearTime().getTime();
16846 this.cells.each(function(c){
16847 //Roo.log("check " +c.dateValue + '?=' + dt);
16848 if(c.dateValue == dt){
16858 findCells : function(ev) {
16859 var s = ev.start.clone().clearTime().getTime();
16861 var e= ev.end.clone().clearTime().getTime();
16864 this.cells.each(function(c){
16865 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16867 if(c.dateValue > e){
16870 if(c.dateValue < s){
16879 // findBestRow: function(cells)
16883 // for (var i =0 ; i < cells.length;i++) {
16884 // ret = Math.max(cells[i].rows || 0,ret);
16891 addItem : function(ev)
16893 // look for vertical location slot in
16894 var cells = this.findCells(ev);
16896 // ev.row = this.findBestRow(cells);
16898 // work out the location.
16902 for(var i =0; i < cells.length; i++) {
16904 cells[i].row = cells[0].row;
16907 cells[i].row = cells[i].row + 1;
16917 if (crow.start.getY() == cells[i].getY()) {
16919 crow.end = cells[i];
16936 cells[0].events.push(ev);
16938 this.calevents.push(ev);
16941 clearEvents: function() {
16943 if(!this.calevents){
16947 Roo.each(this.cells.elements, function(c){
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);
16961 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16967 renderEvents: function()
16971 this.cells.each(function(c) {
16980 if(c.row != c.events.length){
16981 r = 4 - (4 - (c.row - c.events.length));
16984 c.events = ev.slice(0, r);
16985 c.more = ev.slice(r);
16987 if(c.more.length && c.more.length == 1){
16988 c.events.push(c.more.pop());
16991 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16995 this.cells.each(function(c) {
16997 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17000 for (var e = 0; e < c.events.length; e++){
17001 var ev = c.events[e];
17002 var rows = ev.rows;
17004 for(var i = 0; i < rows.length; i++) {
17006 // how many rows should it span..
17009 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17010 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17012 unselectable : "on",
17015 cls: 'fc-event-inner',
17019 // cls: 'fc-event-time',
17020 // html : cells.length > 1 ? '' : ev.time
17024 cls: 'fc-event-title',
17025 html : String.format('{0}', ev.title)
17032 cls: 'ui-resizable-handle ui-resizable-e',
17033 html : '  '
17040 cfg.cls += ' fc-event-start';
17042 if ((i+1) == rows.length) {
17043 cfg.cls += ' fc-event-end';
17046 var ctr = _this.el.select('.fc-event-container',true).first();
17047 var cg = ctr.createChild(cfg);
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();
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);
17056 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17057 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17058 cg.on('click', _this.onEventClick, _this, ev);
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",
17074 cls: 'fc-event-inner',
17078 cls: 'fc-event-title',
17086 cls: 'ui-resizable-handle ui-resizable-e',
17087 html : '  '
17093 var ctr = _this.el.select('.fc-event-container',true).first();
17094 var cg = ctr.createChild(cfg);
17096 var sbox = c.select('.fc-day-content',true).first().getBox();
17097 var ebox = c.select('.fc-day-content',true).first().getBox();
17099 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17100 cg.setWidth(ebox.right - sbox.x -2);
17102 cg.on('click', _this.onMoreEventClick, _this, c.more);
17112 onEventEnter: function (e, el,event,d) {
17113 this.fireEvent('evententer', this, el, event);
17116 onEventLeave: function (e, el,event,d) {
17117 this.fireEvent('eventleave', this, el, event);
17120 onEventClick: function (e, el,event,d) {
17121 this.fireEvent('eventclick', this, el, event);
17124 onMonthChange: function () {
17128 onMoreEventClick: function(e, el, more)
17132 this.calpopover.placement = 'right';
17133 this.calpopover.setTitle('More');
17135 this.calpopover.setContent('');
17137 var ctr = this.calpopover.el.select('.popover-content', true).first();
17139 Roo.each(more, function(m){
17141 cls : 'fc-event-hori fc-event-draggable',
17144 var cg = ctr.createChild(cfg);
17146 cg.on('click', _this.onEventClick, _this, m);
17149 this.calpopover.show(el);
17154 onLoad: function ()
17156 this.calevents = [];
17159 if(this.store.getCount() > 0){
17160 this.store.data.each(function(d){
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
17173 this.renderEvents();
17175 if(this.calevents.length && this.loadMask){
17176 this.maskEl.hide();
17180 onBeforeLoad: function()
17182 this.clearEvents();
17184 this.maskEl.show();
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
17209 * Create a new Popover
17210 * @param {Object} config The config object
17213 Roo.bootstrap.Popover = function(config){
17214 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17220 * After the popover show
17222 * @param {Roo.bootstrap.Popover} this
17227 * After the popover hide
17229 * @param {Roo.bootstrap.Popover} this
17235 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17237 title: 'Fill in a title',
17240 placement : 'right',
17241 trigger : 'hover', // hover
17247 can_build_overlaid : false,
17249 getChildContainer : function()
17251 return this.el.select('.popover-content',true).first();
17254 getAutoCreate : function(){
17257 cls : 'popover roo-dynamic',
17258 style: 'display:block',
17264 cls : 'popover-inner',
17268 cls: 'popover-title',
17272 cls : 'popover-content',
17283 setTitle: function(str)
17286 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17288 setContent: function(str)
17291 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17293 // as it get's added to the bottom of the page.
17294 onRender : function(ct, position)
17296 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17298 var cfg = Roo.apply({}, this.getAutoCreate());
17302 cfg.cls += ' ' + this.cls;
17305 cfg.style = this.style;
17307 //Roo.log("adding to ");
17308 this.el = Roo.get(document.body).createChild(cfg, position);
17309 // Roo.log(this.el);
17314 initEvents : function()
17316 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17317 this.el.enableDisplayMode('block');
17319 if (this.over === false) {
17322 if (this.triggers === false) {
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) {
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';
17335 on_el.on(eventIn ,this.enter, this);
17336 on_el.on(eventOut, this.leave, this);
17347 toggle : function () {
17348 this.hoverState == 'in' ? this.leave() : this.enter();
17351 enter : function () {
17353 clearTimeout(this.timeout);
17355 this.hoverState = 'in';
17357 if (!this.delay || !this.delay.show) {
17362 this.timeout = setTimeout(function () {
17363 if (_t.hoverState == 'in') {
17366 }, this.delay.show)
17369 leave : function() {
17370 clearTimeout(this.timeout);
17372 this.hoverState = 'out';
17374 if (!this.delay || !this.delay.hide) {
17379 this.timeout = setTimeout(function () {
17380 if (_t.hoverState == 'out') {
17383 }, this.delay.hide)
17386 show : function (on_el)
17389 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
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;
17397 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17398 if (!this.title.length) {
17399 this.el.select('.popover-title',true).hide();
17402 var placement = typeof this.placement == 'function' ?
17403 this.placement.call(this, this.el, on_el) :
17406 var autoToken = /\s?auto?\s?/i;
17407 var autoPlace = autoToken.test(placement);
17409 placement = placement.replace(autoToken, '') || 'top';
17413 //this.el.setXY([0,0]);
17415 this.el.dom.style.display='block';
17416 this.el.addClass(placement);
17418 //this.el.appendTo(on_el);
17420 var p = this.getPosition();
17421 var box = this.el.getBox();
17426 var align = Roo.bootstrap.Popover.alignment[placement];
17429 this.el.alignTo(on_el, align[0],align[1]);
17430 //var arrow = this.el.select('.arrow',true).first();
17431 //arrow.set(align[2],
17433 this.el.addClass('in');
17436 if (this.el.hasClass('fade')) {
17440 this.hoverState = 'in';
17442 this.fireEvent('show', this);
17447 this.el.setXY([0,0]);
17448 this.el.removeClass('in');
17450 this.hoverState = null;
17452 this.fireEvent('hide', this);
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']
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
17480 * Create a new Progress
17481 * @param {Object} config The config object
17484 Roo.bootstrap.Progress = function(config){
17485 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17488 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17493 getAutoCreate : function(){
17501 cfg.cls += ' progress-striped';
17505 cfg.cls += ' active';
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
17537 * Create a new ProgressBar
17538 * @param {Object} config The config object
17541 Roo.bootstrap.ProgressBar = function(config){
17542 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17545 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17549 aria_valuemax : 100,
17555 getAutoCreate : function()
17560 cls: 'progress-bar',
17561 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17573 cfg.role = this.role;
17576 if(this.aria_valuenow){
17577 cfg['aria-valuenow'] = this.aria_valuenow;
17580 if(this.aria_valuemin){
17581 cfg['aria-valuemin'] = this.aria_valuemin;
17584 if(this.aria_valuemax){
17585 cfg['aria-valuemax'] = this.aria_valuemax;
17588 if(this.label && !this.sr_only){
17589 cfg.html = this.label;
17593 cfg.cls += ' progress-bar-' + this.panel;
17599 update : function(aria_valuenow)
17601 this.aria_valuenow = aria_valuenow;
17603 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
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
17629 * Create a new TabGroup
17630 * @param {Object} config The config object
17633 Roo.bootstrap.TabGroup = function(config){
17634 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17636 this.navId = Roo.id();
17639 Roo.bootstrap.TabGroup.register(this);
17643 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17646 transition : false,
17651 slideOnTouch : false,
17654 getAutoCreate : function()
17656 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17658 cfg.cls += ' tab-content';
17660 if (this.carousel) {
17661 cfg.cls += ' carousel slide';
17664 cls : 'carousel-inner',
17668 if(this.bullets && !Roo.isTouch){
17671 cls : 'carousel-bullets',
17675 if(this.bullets_cls){
17676 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17683 cfg.cn[0].cn.push(bullets);
17686 if(this.showarrow){
17687 cfg.cn[0].cn.push({
17689 class : 'carousel-arrow',
17693 class : 'carousel-prev',
17697 class : 'fa fa-chevron-left'
17703 class : 'carousel-next',
17707 class : 'fa fa-chevron-right'
17720 initEvents: function()
17722 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17723 // this.el.on("touchstart", this.onTouchStart, this);
17726 if(this.autoslide){
17729 this.slideFn = window.setInterval(function() {
17730 _this.showPanelNext();
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);
17742 // onTouchStart : function(e, el, o)
17744 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17748 // this.showPanelNext();
17752 getChildContainer : function()
17754 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17758 * register a Navigation item
17759 * @param {Roo.bootstrap.NavItem} the navitem to add
17761 register : function(item)
17763 this.tabs.push( item);
17764 item.navId = this.navId; // not really needed..
17769 getActivePanel : function()
17772 Roo.each(this.tabs, function(t) {
17782 getPanelByName : function(n)
17785 Roo.each(this.tabs, function(t) {
17786 if (t.tabId == n) {
17794 indexOfPanel : function(p)
17797 Roo.each(this.tabs, function(t,i) {
17798 if (t.tabId == p.tabId) {
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.)
17811 showPanel : function (pan)
17813 if(this.transition || typeof(pan) == 'undefined'){
17814 Roo.log("waiting for the transitionend");
17818 if (typeof(pan) == 'number') {
17819 pan = this.tabs[pan];
17822 if (typeof(pan) == 'string') {
17823 pan = this.getPanelByName(pan);
17826 var cur = this.getActivePanel();
17829 Roo.log('pan or acitve pan is undefined');
17833 if (pan.tabId == this.getActivePanel().tabId) {
17837 if (false === cur.fireEvent('beforedeactivate')) {
17841 if(this.bullets > 0 && !Roo.isTouch){
17842 this.setActiveBullet(this.indexOfPanel(pan));
17845 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
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);
17856 cur.el.on('transitionend', function() {
17857 Roo.log("trans end?");
17859 pan.el.removeClass([lr,dir]);
17860 pan.setActive(true);
17862 cur.el.removeClass([lr]);
17863 cur.setActive(false);
17865 _this.transition = false;
17867 }, this, { single: true } );
17872 cur.setActive(false);
17873 pan.setActive(true);
17878 showPanelNext : function()
17880 var i = this.indexOfPanel(this.getActivePanel());
17882 if (i >= this.tabs.length - 1 && !this.autoslide) {
17886 if (i >= this.tabs.length - 1 && this.autoslide) {
17890 this.showPanel(this.tabs[i+1]);
17893 showPanelPrev : function()
17895 var i = this.indexOfPanel(this.getActivePanel());
17897 if (i < 1 && !this.autoslide) {
17901 if (i < 1 && this.autoslide) {
17902 i = this.tabs.length;
17905 this.showPanel(this.tabs[i-1]);
17909 addBullet: function()
17911 if(!this.bullets || Roo.isTouch){
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);
17923 bullet.on('click', (function(e, el, o, ii, t){
17925 e.preventDefault();
17927 this.showPanel(ii);
17929 if(this.autoslide && this.slideFn){
17930 clearInterval(this.slideFn);
17931 this.slideFn = window.setInterval(function() {
17932 _this.showPanelNext();
17936 }).createDelegate(this, [i, bullet], true));
17941 setActiveBullet : function(i)
17947 Roo.each(this.el.select('.bullet', true).elements, function(el){
17948 el.removeClass('selected');
17951 var bullet = this.el.select('.bullet-' + i, true).first();
17957 bullet.addClass('selected');
17968 Roo.apply(Roo.bootstrap.TabGroup, {
17972 * register a Navigation Group
17973 * @param {Roo.bootstrap.NavGroup} the navgroup to add
17975 register : function(navgrp)
17977 this.groups[navgrp.navId] = navgrp;
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
17986 get: function(navId) {
17987 if (typeof(this.groups[navId]) == 'undefined') {
17988 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17990 return this.groups[navId] ;
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..
18016 * Create a new TabPanel
18017 * @param {Object} config The config object
18020 Roo.bootstrap.TabPanel = function(config){
18021 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18025 * Fires when the active status changes
18026 * @param {Roo.bootstrap.TabPanel} this
18027 * @param {Boolean} state the new state
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
18038 'beforedeactivate': true
18041 this.tabId = this.tabId || Roo.id();
18045 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18053 getAutoCreate : function(){
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 || ''
18062 cfg.cls += ' active';
18066 cfg.tabId = this.tabId;
18073 initEvents: function()
18075 var p = this.parent();
18077 this.navId = this.navId || p.navId;
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);
18085 var i = tg.tabs.length - 1;
18087 if(this.active && tg.bullets > 0 && i < tg.bullets){
18088 tg.setActiveBullet(i);
18092 this.el.on('click', this.onClick, this);
18095 this.el.on("touchstart", this.onTouchStart, this);
18096 this.el.on("touchmove", this.onTouchMove, this);
18097 this.el.on("touchend", this.onTouchEnd, this);
18102 onRender : function(ct, position)
18104 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18107 setActive : function(state)
18109 Roo.log("panel - set active " + this.tabId + "=" + state);
18111 this.active = state;
18113 this.el.removeClass('active');
18115 } else if (!this.el.hasClass('active')) {
18116 this.el.addClass('active');
18119 this.fireEvent('changed', this, state);
18122 onClick : function(e)
18124 e.preventDefault();
18126 if(!this.href.length){
18130 window.location.href = this.href;
18139 onTouchStart : function(e)
18141 this.swiping = false;
18143 this.startX = e.browserEvent.touches[0].clientX;
18144 this.startY = e.browserEvent.touches[0].clientY;
18147 onTouchMove : function(e)
18149 this.swiping = true;
18151 this.endX = e.browserEvent.touches[0].clientX;
18152 this.endY = e.browserEvent.touches[0].clientY;
18155 onTouchEnd : function(e)
18162 var tabGroup = this.parent();
18164 if(this.endX > this.startX){ // swiping right
18165 tabGroup.showPanelPrev();
18169 if(this.startX > this.endX){ // swiping left
18170 tabGroup.showPanelNext();
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)
18203 * @cfg {Boolean} keyboardNavigation default true
18204 * @cfg {String} language default en
18207 * Create a new DateField
18208 * @param {Object} config The config object
18211 Roo.bootstrap.DateField = function(config){
18212 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18216 * Fires when this field show.
18217 * @param {Roo.bootstrap.DateField} this
18218 * @param {Mixed} date The date value
18223 * Fires when this field hide.
18224 * @param {Roo.bootstrap.DateField} this
18225 * @param {Mixed} date The date value
18230 * Fires when select a date.
18231 * @param {Roo.bootstrap.DateField} this
18232 * @param {Mixed} date The date value
18236 * @event beforeselect
18237 * Fires when before select a date.
18238 * @param {Roo.bootstrap.DateField} this
18239 * @param {Mixed} date The date value
18241 beforeselect : true
18245 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
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').
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').
18258 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18266 todayHighlight : false,
18272 keyboardNavigation: true,
18274 calendarWeeks: false,
18276 startDate: -Infinity,
18280 daysOfWeekDisabled: [],
18284 singleMode : false,
18286 UTCDate: function()
18288 return new Date(Date.UTC.apply(Date, arguments));
18291 UTCToday: function()
18293 var today = new Date();
18294 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18297 getDate: function() {
18298 var d = this.getUTCDate();
18299 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18302 getUTCDate: function() {
18306 setDate: function(d) {
18307 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18310 setUTCDate: function(d) {
18312 this.setValue(this.formatDate(this.date));
18315 onRender: function(ct, position)
18318 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
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";
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;
18332 if (typeof(this.minViewMode === 'string')) {
18333 switch (this.minViewMode) {
18335 this.minViewMode = 1;
18338 this.minViewMode = 2;
18341 this.minViewMode = 0;
18346 if (typeof(this.viewMode === 'string')) {
18347 switch (this.viewMode) {
18360 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18362 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18364 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18366 this.picker().on('mousedown', this.onMousedown, this);
18367 this.picker().on('click', this.onClick, this);
18369 this.picker().addClass('datepicker-dropdown');
18371 this.startViewMode = this.viewMode;
18373 if(this.singleMode){
18374 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18375 v.setVisibilityMode(Roo.Element.DISPLAY);
18379 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18380 v.setStyle('width', '189px');
18384 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18385 if(!this.calendarWeeks){
18390 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18391 v.attr('colspan', function(i, val){
18392 return parseInt(val) + 1;
18397 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18399 this.setStartDate(this.startDate);
18400 this.setEndDate(this.endDate);
18402 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18409 if(this.isInline) {
18414 picker : function()
18416 return this.pickerEl;
18417 // return this.el.select('.datepicker', true).first();
18420 fillDow: function()
18422 var dowCnt = this.weekStart;
18431 if(this.calendarWeeks){
18439 while (dowCnt < this.weekStart + 7) {
18443 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18447 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18450 fillMonths: function()
18453 var months = this.picker().select('>.datepicker-months td', true).first();
18455 months.dom.innerHTML = '';
18461 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18464 months.createChild(month);
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;
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);
18478 this.viewDate = new Date(this.date);
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();
18496 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18498 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18500 // this.picker.select('>tfoot th.today').
18501 // .text(dates[this.language].today)
18502 // .toggle(this.todayBtn !== false);
18504 this.updateNavArrows();
18507 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18509 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18511 prevMonth.setUTCDate(day);
18513 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18515 var nextMonth = new Date(prevMonth);
18517 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18519 nextMonth = nextMonth.valueOf();
18521 var fillMonths = false;
18523 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18525 while(prevMonth.valueOf() < nextMonth) {
18528 if (prevMonth.getUTCDay() === this.weekStart) {
18530 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
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.
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;
18551 fillMonths.cn.push({
18559 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18561 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18564 if (this.todayHighlight &&
18565 prevMonth.getUTCFullYear() == today.getFullYear() &&
18566 prevMonth.getUTCMonth() == today.getMonth() &&
18567 prevMonth.getUTCDate() == today.getDate()) {
18568 clsName += ' today';
18571 if (currentDate && prevMonth.valueOf() === currentDate) {
18572 clsName += ' active';
18575 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18576 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18577 clsName += ' disabled';
18580 fillMonths.cn.push({
18582 cls: 'day ' + clsName,
18583 html: prevMonth.getDate()
18586 prevMonth.setDate(prevMonth.getDate()+1);
18589 var currentYear = this.date && this.date.getUTCFullYear();
18590 var currentMonth = this.date && this.date.getUTCMonth();
18592 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18594 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18595 v.removeClass('active');
18597 if(currentYear === year && k === currentMonth){
18598 v.addClass('active');
18601 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18602 v.addClass('disabled');
18608 year = parseInt(year/10, 10) * 10;
18610 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18612 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18615 for (var i = -1; i < 11; i++) {
18616 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18618 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18626 showMode: function(dir)
18629 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18632 Roo.each(this.picker().select('>div',true).elements, function(v){
18633 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18636 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18641 if(this.isInline) {
18645 this.picker().removeClass(['bottom', 'top']);
18647 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18649 * place to the top of element!
18653 this.picker().addClass('top');
18654 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18659 this.picker().addClass('bottom');
18661 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18664 parseDate : function(value)
18666 if(!value || value instanceof Date){
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');
18673 if(!v && this.altFormats){
18674 if(!this.altFormatsArray){
18675 this.altFormatsArray = this.altFormats.split("|");
18677 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18678 v = Date.parseDate(value, this.altFormatsArray[i]);
18684 formatDate : function(date, fmt)
18686 return (!date || !(date instanceof Date)) ?
18687 date : date.dateFormat(fmt || this.format);
18690 onFocus : function()
18692 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18696 onBlur : function()
18698 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18700 var d = this.inputEl().getValue();
18709 this.picker().show();
18713 this.fireEvent('show', this, this.date);
18718 if(this.isInline) {
18721 this.picker().hide();
18722 this.viewMode = this.startViewMode;
18725 this.fireEvent('hide', this, this.date);
18729 onMousedown: function(e)
18731 e.stopPropagation();
18732 e.preventDefault();
18737 Roo.bootstrap.DateField.superclass.keyup.call(this);
18741 setValue: function(v)
18743 if(this.fireEvent('beforeselect', this, v) !== false){
18744 var d = new Date(this.parseDate(v) ).clearTime();
18746 if(isNaN(d.getTime())){
18747 this.date = this.viewDate = '';
18748 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18752 v = this.formatDate(d);
18754 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18756 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18760 this.fireEvent('select', this, this.date);
18764 getValue: function()
18766 return this.formatDate(this.date);
18769 fireKey: function(e)
18771 if (!this.picker().isVisible()){
18772 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18778 var dateChanged = false,
18780 newDate, newViewDate;
18785 e.preventDefault();
18789 if (!this.keyboardNavigation) {
18792 dir = e.keyCode == 37 ? -1 : 1;
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);
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);
18806 if (this.dateWithinRange(newDate)){
18807 this.date = newDate;
18808 this.viewDate = newViewDate;
18809 this.setValue(this.formatDate(this.date));
18811 e.preventDefault();
18812 dateChanged = true;
18817 if (!this.keyboardNavigation) {
18820 dir = e.keyCode == 38 ? -1 : 1;
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);
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);
18833 if (this.dateWithinRange(newDate)){
18834 this.date = newDate;
18835 this.viewDate = newViewDate;
18836 this.setValue(this.formatDate(this.date));
18838 e.preventDefault();
18839 dateChanged = true;
18843 this.setValue(this.formatDate(this.date));
18845 e.preventDefault();
18848 this.setValue(this.formatDate(this.date));
18862 onClick: function(e)
18864 e.stopPropagation();
18865 e.preventDefault();
18867 var target = e.getTarget();
18869 if(target.nodeName.toLowerCase() === 'i'){
18870 target = Roo.get(target).dom.parentNode;
18873 var nodeName = target.nodeName;
18874 var className = target.className;
18875 var html = target.innerHTML;
18876 //Roo.log(nodeName);
18878 switch(nodeName.toLowerCase()) {
18880 switch(className) {
18886 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18887 switch(this.viewMode){
18889 this.viewDate = this.moveMonth(this.viewDate, dir);
18893 this.viewDate = this.moveYear(this.viewDate, dir);
18899 var date = new Date();
18900 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18902 this.setValue(this.formatDate(this.date));
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));
18914 var year = parseInt(html, 10) || 0;
18915 this.viewDate.setUTCFullYear(year);
18919 if(this.singleMode){
18920 this.setValue(this.formatDate(this.viewDate));
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();
18937 if (className.indexOf('old') > -1) {
18944 } else if (className.indexOf('new') > -1) {
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);
18956 //Roo.log(this.formatDate(this.date));
18957 this.setValue(this.formatDate(this.date));
18964 setStartDate: function(startDate)
18966 this.startDate = startDate || -Infinity;
18967 if (this.startDate !== -Infinity) {
18968 this.startDate = this.parseDate(this.startDate);
18971 this.updateNavArrows();
18974 setEndDate: function(endDate)
18976 this.endDate = endDate || Infinity;
18977 if (this.endDate !== Infinity) {
18978 this.endDate = this.parseDate(this.endDate);
18981 this.updateNavArrows();
18984 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18986 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18987 if (typeof(this.daysOfWeekDisabled) !== 'object') {
18988 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18990 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18991 return parseInt(d, 10);
18994 this.updateNavArrows();
18997 updateNavArrows: function()
18999 if(this.singleMode){
19003 var d = new Date(this.viewDate),
19004 year = d.getUTCFullYear(),
19005 month = d.getUTCMonth();
19007 Roo.each(this.picker().select('.prev', true).elements, function(v){
19009 switch (this.viewMode) {
19012 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19018 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19025 Roo.each(this.picker().select('.next', true).elements, function(v){
19027 switch (this.viewMode) {
19030 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19036 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19044 moveMonth: function(date, dir)
19049 var new_date = new Date(date.valueOf()),
19050 day = new_date.getUTCDate(),
19051 month = new_date.getUTCMonth(),
19052 mag = Math.abs(dir),
19054 dir = dir > 0 ? 1 : -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)
19060 return new_date.getUTCMonth() == month;
19062 // If going forward one month, make sure month is as expected
19063 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19065 return new_date.getUTCMonth() != new_month;
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;
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);
19079 // ...then reset the day, keeping it in the new month
19080 new_month = new_date.getUTCMonth();
19081 new_date.setUTCDate(day);
19083 return new_month != new_date.getUTCMonth();
19086 // Common date-resetting loop -- if date is beyond end of month, make it
19089 new_date.setUTCDate(--day);
19090 new_date.setUTCMonth(new_month);
19095 moveYear: function(date, dir)
19097 return this.moveMonth(date, dir*12);
19100 dateWithinRange: function(date)
19102 return date >= this.startDate && date <= this.endDate;
19108 this.picker().remove();
19111 validateValue : function(value)
19113 if(value.length < 1) {
19114 if(this.allowBlank){
19120 if(value.length < this.minLength){
19123 if(value.length > this.maxLength){
19127 var vt = Roo.form.VTypes;
19128 if(!vt[this.vtype](value, this)){
19132 if(typeof this.validator == "function"){
19133 var msg = this.validator(value);
19139 if(this.regex && !this.regex.test(value)){
19143 if(typeof(this.parseDate(value)) == 'undefined'){
19147 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19151 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19161 Roo.apply(Roo.bootstrap.DateField, {
19172 html: '<i class="fa fa-arrow-left"/>'
19182 html: '<i class="fa fa-arrow-right"/>'
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"],
19241 navFnc: 'FullYear',
19246 navFnc: 'FullYear',
19251 Roo.apply(Roo.bootstrap.DateField, {
19255 cls: 'datepicker dropdown-menu roo-dynamic',
19259 cls: 'datepicker-days',
19263 cls: 'table-condensed',
19265 Roo.bootstrap.DateField.head,
19269 Roo.bootstrap.DateField.footer
19276 cls: 'datepicker-months',
19280 cls: 'table-condensed',
19282 Roo.bootstrap.DateField.head,
19283 Roo.bootstrap.DateField.content,
19284 Roo.bootstrap.DateField.footer
19291 cls: 'datepicker-years',
19295 cls: 'table-condensed',
19297 Roo.bootstrap.DateField.head,
19298 Roo.bootstrap.DateField.content,
19299 Roo.bootstrap.DateField.footer
19318 * @class Roo.bootstrap.TimeField
19319 * @extends Roo.bootstrap.Input
19320 * Bootstrap DateField class
19324 * Create a new TimeField
19325 * @param {Object} config The config object
19328 Roo.bootstrap.TimeField = function(config){
19329 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19333 * Fires when this field show.
19334 * @param {Roo.bootstrap.DateField} thisthis
19335 * @param {Mixed} date The date value
19340 * Fires when this field hide.
19341 * @param {Roo.bootstrap.DateField} this
19342 * @param {Mixed} date The date value
19347 * Fires when select a date.
19348 * @param {Roo.bootstrap.DateField} this
19349 * @param {Mixed} date The date value
19355 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
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').
19364 onRender: function(ct, position)
19367 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19369 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19371 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19373 this.pop = this.picker().select('>.datepicker-time',true).first();
19374 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19376 this.picker().on('mousedown', this.onMousedown, this);
19377 this.picker().on('click', this.onClick, this);
19379 this.picker().addClass('datepicker-dropdown');
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);
19393 fireKey: function(e){
19394 if (!this.picker().isVisible()){
19395 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19401 e.preventDefault();
19409 this.onTogglePeriod();
19412 this.onIncrementMinutes();
19415 this.onDecrementMinutes();
19424 onClick: function(e) {
19425 e.stopPropagation();
19426 e.preventDefault();
19429 picker : function()
19431 return this.el.select('.datepicker', true).first();
19434 fillTime: function()
19436 var time = this.pop.select('tbody', true).first();
19438 time.dom.innerHTML = '';
19453 cls: 'hours-up glyphicon glyphicon-chevron-up'
19473 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19494 cls: 'timepicker-hour',
19509 cls: 'timepicker-minute',
19524 cls: 'btn btn-primary period',
19546 cls: 'hours-down glyphicon glyphicon-chevron-down'
19566 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19584 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19591 var hours = this.time.getHours();
19592 var minutes = this.time.getMinutes();
19605 hours = hours - 12;
19609 hours = '0' + hours;
19613 minutes = '0' + minutes;
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;
19624 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19626 var cls = ['bottom'];
19628 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19635 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19640 this.picker().addClass(cls.join('-'));
19644 Roo.each(cls, function(c){
19646 _this.picker().setTop(_this.inputEl().getHeight());
19650 _this.picker().setTop(0 - _this.picker().getHeight());
19655 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19659 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19666 onFocus : function()
19668 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19672 onBlur : function()
19674 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19680 this.picker().show();
19685 this.fireEvent('show', this, this.date);
19690 this.picker().hide();
19693 this.fireEvent('hide', this, this.date);
19696 setTime : function()
19699 this.setValue(this.time.format(this.format));
19701 this.fireEvent('select', this, this.date);
19706 onMousedown: function(e){
19707 e.stopPropagation();
19708 e.preventDefault();
19711 onIncrementHours: function()
19713 Roo.log('onIncrementHours');
19714 this.time = this.time.add(Date.HOUR, 1);
19719 onDecrementHours: function()
19721 Roo.log('onDecrementHours');
19722 this.time = this.time.add(Date.HOUR, -1);
19726 onIncrementMinutes: function()
19728 Roo.log('onIncrementMinutes');
19729 this.time = this.time.add(Date.MINUTE, 1);
19733 onDecrementMinutes: function()
19735 Roo.log('onDecrementMinutes');
19736 this.time = this.time.add(Date.MINUTE, -1);
19740 onTogglePeriod: function()
19742 Roo.log('onTogglePeriod');
19743 this.time = this.time.add(Date.HOUR, 12);
19750 Roo.apply(Roo.bootstrap.TimeField, {
19780 cls: 'btn btn-info ok',
19792 Roo.apply(Roo.bootstrap.TimeField, {
19796 cls: 'datepicker dropdown-menu',
19800 cls: 'datepicker-time',
19804 cls: 'table-condensed',
19806 Roo.bootstrap.TimeField.content,
19807 Roo.bootstrap.TimeField.footer
19826 * @class Roo.bootstrap.MonthField
19827 * @extends Roo.bootstrap.Input
19828 * Bootstrap MonthField class
19830 * @cfg {String} language default en
19833 * Create a new MonthField
19834 * @param {Object} config The config object
19837 Roo.bootstrap.MonthField = function(config){
19838 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19843 * Fires when this field show.
19844 * @param {Roo.bootstrap.MonthField} this
19845 * @param {Mixed} date The date value
19850 * Fires when this field hide.
19851 * @param {Roo.bootstrap.MonthField} this
19852 * @param {Mixed} date The date value
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
19866 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
19868 onRender: function(ct, position)
19871 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
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";
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;
19884 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19886 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19888 this.picker().on('mousedown', this.onMousedown, this);
19889 this.picker().on('click', this.onClick, this);
19891 this.picker().addClass('datepicker-dropdown');
19893 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19894 v.setStyle('width', '189px');
19901 if(this.isInline) {
19907 setValue: function(v, suppressEvent)
19909 var o = this.getValue();
19911 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19915 if(suppressEvent !== true){
19916 this.fireEvent('select', this, o, v);
19921 getValue: function()
19926 onClick: function(e)
19928 e.stopPropagation();
19929 e.preventDefault();
19931 var target = e.getTarget();
19933 if(target.nodeName.toLowerCase() === 'i'){
19934 target = Roo.get(target).dom.parentNode;
19937 var nodeName = target.nodeName;
19938 var className = target.className;
19939 var html = target.innerHTML;
19941 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19945 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19947 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19953 picker : function()
19955 return this.pickerEl;
19958 fillMonths: function()
19961 var months = this.picker().select('>.datepicker-months td', true).first();
19963 months.dom.innerHTML = '';
19969 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19972 months.createChild(month);
19981 if(typeof(this.vIndex) == 'undefined' && this.value.length){
19982 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19985 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19986 e.removeClass('active');
19988 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19989 e.addClass('active');
19996 if(this.isInline) {
20000 this.picker().removeClass(['bottom', 'top']);
20002 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20004 * place to the top of element!
20008 this.picker().addClass('top');
20009 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20014 this.picker().addClass('bottom');
20016 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20019 onFocus : function()
20021 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20025 onBlur : function()
20027 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20029 var d = this.inputEl().getValue();
20038 this.picker().show();
20039 this.picker().select('>.datepicker-months', true).first().show();
20043 this.fireEvent('show', this, this.date);
20048 if(this.isInline) {
20051 this.picker().hide();
20052 this.fireEvent('hide', this, this.date);
20056 onMousedown: function(e)
20058 e.stopPropagation();
20059 e.preventDefault();
20064 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20068 fireKey: function(e)
20070 if (!this.picker().isVisible()){
20071 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20082 e.preventDefault();
20086 dir = e.keyCode == 37 ? -1 : 1;
20088 this.vIndex = this.vIndex + dir;
20090 if(this.vIndex < 0){
20094 if(this.vIndex > 11){
20098 if(isNaN(this.vIndex)){
20102 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20108 dir = e.keyCode == 38 ? -1 : 1;
20110 this.vIndex = this.vIndex + dir * 4;
20112 if(this.vIndex < 0){
20116 if(this.vIndex > 11){
20120 if(isNaN(this.vIndex)){
20124 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20129 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20130 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20134 e.preventDefault();
20137 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20138 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20154 this.picker().remove();
20159 Roo.apply(Roo.bootstrap.MonthField, {
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"]
20184 Roo.apply(Roo.bootstrap.MonthField, {
20188 cls: 'datepicker dropdown-menu roo-dynamic',
20192 cls: 'datepicker-months',
20196 cls: 'table-condensed',
20198 Roo.bootstrap.DateField.content
20218 * @class Roo.bootstrap.CheckBox
20219 * @extends Roo.bootstrap.Input
20220 * Bootstrap CheckBox class
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
20232 * Create a new CheckBox
20233 * @param {Object} config The config object
20236 Roo.bootstrap.CheckBox = function(config){
20237 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
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
20249 * Fires when the element is click.
20250 * @param {Roo.bootstrap.CheckBox} this This input
20257 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20259 inputType: 'checkbox',
20268 getAutoCreate : function()
20270 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20276 cfg.cls = 'form-group ' + this.inputType; //input-group
20279 cfg.cls += ' ' + this.inputType + '-inline';
20285 type : this.inputType,
20286 value : this.inputValue,
20287 cls : 'roo-' + this.inputType, //'form-box',
20288 placeholder : this.placeholder || ''
20292 if(this.inputType != 'radio'){
20296 cls : 'roo-hidden-value',
20297 value : this.checked ? this.inputValue : this.valueOff
20302 if (this.weight) { // Validity check?
20303 cfg.cls += " " + this.inputType + "-" + this.weight;
20306 if (this.disabled) {
20307 input.disabled=true;
20311 input.checked = this.checked;
20316 input.name = this.name;
20318 if(this.inputType != 'radio'){
20319 hidden.name = this.name;
20320 input.name = '_hidden_' + this.name;
20325 input.cls += ' input-' + this.size;
20330 ['xs','sm','md','lg'].map(function(size){
20331 if (settings[size]) {
20332 cfg.cls += ' col-' + size + '-' + settings[size];
20336 var inputblock = input;
20338 if (this.before || this.after) {
20341 cls : 'input-group',
20346 inputblock.cn.push({
20348 cls : 'input-group-addon',
20353 inputblock.cn.push(input);
20355 if(this.inputType != 'radio'){
20356 inputblock.cn.push(hidden);
20360 inputblock.cn.push({
20362 cls : 'input-group-addon',
20369 if (align ==='left' && this.fieldLabel.length) {
20370 // Roo.log("left and has label");
20375 cls : 'control-label',
20376 html : this.fieldLabel
20386 if(this.labelWidth > 12){
20387 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20390 if(this.labelWidth < 13 && this.labelmd == 0){
20391 this.labelmd = this.labelWidth;
20394 if(this.labellg > 0){
20395 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20396 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20399 if(this.labelmd > 0){
20400 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20401 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20404 if(this.labelsm > 0){
20405 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20406 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20409 if(this.labelxs > 0){
20410 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20411 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20414 } else if ( this.fieldLabel.length) {
20415 // Roo.log(" label");
20419 tag: this.boxLabel ? 'span' : 'label',
20421 cls: 'control-label box-input-label',
20422 //cls : 'input-group-addon',
20423 html : this.fieldLabel
20432 // Roo.log(" no label && no align");
20433 cfg.cn = [ inputblock ] ;
20439 var boxLabelCfg = {
20441 //'for': id, // box label is handled by onclick - so no for...
20443 html: this.boxLabel
20447 boxLabelCfg.tooltip = this.tooltip;
20450 cfg.cn.push(boxLabelCfg);
20453 if(this.inputType != 'radio'){
20454 cfg.cn.push(hidden);
20462 * return the real input element.
20464 inputEl: function ()
20466 return this.el.select('input.roo-' + this.inputType,true).first();
20468 hiddenEl: function ()
20470 return this.el.select('input.roo-hidden-value',true).first();
20473 labelEl: function()
20475 return this.el.select('label.control-label',true).first();
20477 /* depricated... */
20481 return this.labelEl();
20484 boxLabelEl: function()
20486 return this.el.select('label.box-label',true).first();
20489 initEvents : function()
20491 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20493 this.inputEl().on('click', this.onClick, this);
20495 if (this.boxLabel) {
20496 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20499 this.startValue = this.getValue();
20502 Roo.bootstrap.CheckBox.register(this);
20506 onClick : function(e)
20508 if(this.fireEvent('click', this, e) !== false){
20509 this.setChecked(!this.checked);
20514 setChecked : function(state,suppressEvent)
20516 this.startValue = this.getValue();
20518 if(this.inputType == 'radio'){
20520 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20521 e.dom.checked = false;
20524 this.inputEl().dom.checked = true;
20526 this.inputEl().dom.value = this.inputValue;
20528 if(suppressEvent !== true){
20529 this.fireEvent('check', this, true);
20537 this.checked = state;
20539 this.inputEl().dom.checked = state;
20542 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20544 if(suppressEvent !== true){
20545 this.fireEvent('check', this, state);
20551 getValue : function()
20553 if(this.inputType == 'radio'){
20554 return this.getGroupValue();
20557 return this.hiddenEl().dom.value;
20561 getGroupValue : function()
20563 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20567 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20570 setValue : function(v,suppressEvent)
20572 if(this.inputType == 'radio'){
20573 this.setGroupValue(v, suppressEvent);
20577 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20582 setGroupValue : function(v, suppressEvent)
20584 this.startValue = this.getValue();
20586 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20587 e.dom.checked = false;
20589 if(e.dom.value == v){
20590 e.dom.checked = true;
20594 if(suppressEvent !== true){
20595 this.fireEvent('check', this, true);
20603 validate : function()
20607 (this.inputType == 'radio' && this.validateRadio()) ||
20608 (this.inputType == 'checkbox' && this.validateCheckbox())
20614 this.markInvalid();
20618 validateRadio : function()
20620 if(this.allowBlank){
20626 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20627 if(!e.dom.checked){
20639 validateCheckbox : function()
20642 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20643 //return (this.getValue() == this.inputValue) ? true : false;
20646 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20654 for(var i in group){
20655 if(group[i].el.isVisible(true)){
20663 for(var i in group){
20668 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20675 * Mark this field as valid
20677 markValid : function()
20681 this.fireEvent('valid', this);
20683 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20686 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
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);
20703 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20704 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20708 var group = Roo.bootstrap.CheckBox.get(this.groupId);
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);
20721 * Mark this field as invalid
20722 * @param {String} msg The validation message
20724 markInvalid : function(msg)
20726 if(this.allowBlank){
20732 this.fireEvent('invalid', this, msg);
20734 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20737 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20741 label.markInvalid();
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);
20754 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20755 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20759 var group = Roo.bootstrap.CheckBox.get(this.groupId);
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);
20772 clearInvalid : function()
20774 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20776 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20778 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20780 if (label && label.iconEl) {
20781 label.iconEl.removeClass(label.validClass);
20782 label.iconEl.removeClass(label.invalidClass);
20786 disable : function()
20788 if(this.inputType != 'radio'){
20789 Roo.bootstrap.CheckBox.superclass.disable.call(this);
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;
20802 this.disabled = true;
20803 this.fireEvent("disable", this);
20807 enable : function()
20809 if(this.inputType != 'radio'){
20810 Roo.bootstrap.CheckBox.superclass.enable.call(this);
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;
20823 this.disabled = false;
20824 this.fireEvent("enable", this);
20828 setBoxLabel : function(v)
20833 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20839 Roo.apply(Roo.bootstrap.CheckBox, {
20844 * register a CheckBox Group
20845 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20847 register : function(checkbox)
20849 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20850 this.groups[checkbox.groupId] = {};
20853 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20857 this.groups[checkbox.groupId][checkbox.name] = checkbox;
20861 * fetch a CheckBox Group based on the group ID
20862 * @param {string} the group ID
20863 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20865 get: function(groupId) {
20866 if (typeof(this.groups[groupId]) == 'undefined') {
20870 return this.groups[groupId] ;
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
20890 * Create a new Radio
20891 * @param {Object} config The config object
20893 Roo.bootstrap.Radio = function(config){
20894 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20898 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20904 getAutoCreate : function()
20908 cls : 'form-group radio',
20913 html : this.boxLabel
20921 initEvents : function()
20923 this.parent().register(this);
20925 this.el.on('click', this.onClick, this);
20929 onClick : function()
20931 this.setChecked(true);
20934 setChecked : function(state, suppressEvent)
20936 this.parent().setValue(this.value, suppressEvent);
20940 setBoxLabel : function(v)
20945 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20960 * @class Roo.bootstrap.SecurePass
20961 * @extends Roo.bootstrap.Input
20962 * Bootstrap SecurePass class
20966 * Create a new SecurePass
20967 * @param {Object} config The config object
20970 Roo.bootstrap.SecurePass = function (config) {
20971 // these go here, so the translation tool can replace them..
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."
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"
20994 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20997 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20999 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
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."
21017 * @cfg {String/Object} Label for the strength meter (defaults to
21018 * 'Password strength:')
21023 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21024 * ['Weak', 'Medium', 'Strong'])
21027 pwdStrengths: false,
21040 initEvents: function ()
21042 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21044 if (this.el.is('input[type=password]') && Roo.isSafari) {
21045 this.el.on('keydown', this.SafariOnKeyDown, this);
21048 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21051 onRender: function (ct, position)
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});
21057 this.trigger.createChild({
21062 cls: 'roo-password-meter-grey col-xs-12',
21065 //width: this.meterWidth + 'px'
21069 cls: 'roo-password-meter-text'
21075 if (this.hideTrigger) {
21076 this.trigger.setDisplayed(false);
21078 this.setSize(this.width || '', this.height || '');
21081 onDestroy: function ()
21083 if (this.trigger) {
21084 this.trigger.removeAllListeners();
21085 this.trigger.remove();
21088 this.wrap.remove();
21090 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21093 checkStrength: function ()
21095 var pwd = this.inputEl().getValue();
21096 if (pwd == this._lastPwd) {
21101 if (this.ClientSideStrongPassword(pwd)) {
21103 } else if (this.ClientSideMediumPassword(pwd)) {
21105 } else if (this.ClientSideWeakPassword(pwd)) {
21111 Roo.log('strength1: ' + strength);
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]);
21119 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21121 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21123 this._lastPwd = pwd;
21127 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21129 this._lastPwd = '';
21131 var pm = this.trigger.child('div/div');
21132 pm.removeClass(this.meterClass);
21133 pm.addClass('roo-password-meter-grey');
21136 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21139 this.inputEl().dom.type='password';
21142 validateValue: function (value)
21145 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21148 if (value.length == 0) {
21149 if (this.allowBlank) {
21150 this.clearInvalid();
21154 this.markInvalid(this.errors.PwdEmpty);
21155 this.errorMsg = this.errors.PwdEmpty;
21163 if ('[\x21-\x7e]*'.match(value)) {
21164 this.markInvalid(this.errors.PwdBadChar);
21165 this.errorMsg = this.errors.PwdBadChar;
21168 if (value.length < 6) {
21169 this.markInvalid(this.errors.PwdShort);
21170 this.errorMsg = this.errors.PwdShort;
21173 if (value.length > 16) {
21174 this.markInvalid(this.errors.PwdLong);
21175 this.errorMsg = this.errors.PwdLong;
21179 if (this.ClientSideStrongPassword(value)) {
21181 } else if (this.ClientSideMediumPassword(value)) {
21183 } else if (this.ClientSideWeakPassword(value)) {
21190 if (strength < 2) {
21191 //this.markInvalid(this.errors.TooWeak);
21192 this.errorMsg = this.errors.TooWeak;
21197 console.log('strength2: ' + strength);
21199 //var pm = this.trigger.child('div/div/div').dom;
21201 var pm = this.trigger.child('div/div');
21202 pm.removeClass(this.meterClass);
21203 pm.addClass(this.meterClass[strength]);
21205 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21207 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21209 this.errorMsg = '';
21213 CharacterSetChecks: function (type)
21216 this.fResult = false;
21219 isctype: function (character, type)
21222 case this.kCapitalLetter:
21223 if (character >= 'A' && character <= 'Z') {
21228 case this.kSmallLetter:
21229 if (character >= 'a' && character <= 'z') {
21235 if (character >= '0' && character <= '9') {
21240 case this.kPunctuation:
21241 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21252 IsLongEnough: function (pwd, size)
21254 return !(pwd == null || isNaN(size) || pwd.length < size);
21257 SpansEnoughCharacterSets: function (word, nb)
21259 if (!this.IsLongEnough(word, nb))
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)
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;
21279 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21280 if (characterSetChecks[nCharSet].fResult) {
21285 if (nCharSets < nb) {
21291 ClientSideStrongPassword: function (pwd)
21293 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21296 ClientSideMediumPassword: function (pwd)
21298 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21301 ClientSideWeakPassword: function (pwd)
21303 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21306 })//<script type="text/javascript">
21309 * Based Ext JS Library 1.1.1
21310 * Copyright(c) 2006-2007, Ext JS, LLC.
21316 * @class Roo.HtmlEditorCore
21317 * @extends Roo.Component
21318 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21320 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21323 Roo.HtmlEditorCore = function(config){
21326 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21331 * @event initialize
21332 * Fires when the editor is fully initialized (including the iframe)
21333 * @param {Roo.HtmlEditorCore} this
21338 * Fires when the editor is first receives the focus. Any insertion must wait
21339 * until after this event.
21340 * @param {Roo.HtmlEditorCore} this
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
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
21361 * Fires when the textarea is updated with content from the editor iframe.
21362 * @param {Roo.HtmlEditorCore} this
21363 * @param {String} html
21368 * Fires when the iframe editor is updated with content from the textarea.
21369 * @param {Roo.HtmlEditorCore} this
21370 * @param {String} html
21375 * @event editorevent
21376 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21377 * @param {Roo.HtmlEditorCore} this
21383 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21385 // defaults : white / black...
21386 this.applyBlacklists();
21393 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21397 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21403 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21408 * @cfg {Number} height (in pixels)
21412 * @cfg {Number} width (in pixels)
21417 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21420 stylesheets: false,
21425 // private properties
21426 validationEvent : false,
21428 initialized : false,
21430 sourceEditMode : false,
21431 onFocus : Roo.emptyFn,
21433 hideMode:'offsets',
21437 // blacklist + whitelisted elements..
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).
21448 getDocMarkup : function(){
21452 // inherit styels from page...??
21453 if (this.stylesheets === false) {
21455 Roo.get(document.head).select('style').each(function(node) {
21456 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21459 Roo.get(document.head).select('link').each(function(node) {
21460 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21463 } else if (!this.stylesheets.length) {
21465 st = '<style type="text/css">' +
21466 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21469 st = '<style type="text/css">' +
21474 st += '<style type="text/css">' +
21475 'IMG { cursor: pointer } ' +
21478 var cls = 'roo-htmleditor-body';
21480 if(this.bodyCls.length){
21481 cls += ' ' + this.bodyCls;
21484 return '<html><head>' + st +
21485 //<style type="text/css">' +
21486 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21488 ' </head><body class="' + cls + '"></body></html>';
21492 onRender : function(ct, position)
21495 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21496 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21499 this.el.dom.style.border = '0 none';
21500 this.el.dom.setAttribute('tabIndex', -1);
21501 this.el.addClass('x-hidden hide');
21505 if(Roo.isIE){ // fix IE 1px bogus margin
21506 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21510 this.frameId = Roo.id();
21514 var iframe = this.owner.wrap.createChild({
21516 cls: 'form-control', // bootstrap..
21518 name: this.frameId,
21519 frameBorder : 'no',
21520 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21525 this.iframe = iframe.dom;
21527 this.assignDocWin();
21529 this.doc.designMode = 'on';
21532 this.doc.write(this.getDocMarkup());
21536 var task = { // must defer to wait for browser to be ready
21538 //console.log("run task?" + this.doc.readyState);
21539 this.assignDocWin();
21540 if(this.doc.body || this.doc.readyState == 'complete'){
21542 this.doc.designMode="on";
21546 Roo.TaskMgr.stop(task);
21547 this.initEditor.defer(10, this);
21554 Roo.TaskMgr.start(task);
21559 onResize : function(w, h)
21561 Roo.log('resize: ' +w + ',' + h );
21562 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21566 if(typeof w == 'number'){
21568 this.iframe.style.width = w + 'px';
21570 if(typeof h == 'number'){
21572 this.iframe.style.height = h + 'px';
21574 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21581 * Toggles the editor between standard and source edit mode.
21582 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21584 toggleSourceEdit : function(sourceEditMode){
21586 this.sourceEditMode = sourceEditMode === true;
21588 if(this.sourceEditMode){
21590 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21593 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21594 //this.iframe.className = '';
21597 //this.setSize(this.owner.wrap.getSize());
21598 //this.fireEvent('editmodechange', this, this.sourceEditMode);
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
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, '');
21617 if(html == ' '){
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.
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;
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;
21637 html = '<div style="'+m[0]+'">' + html + '</div>';
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();
21646 (cc >= 0x4E00 && cc < 0xA000 ) ||
21647 (cc >= 0x3400 && cc < 0x4E00 ) ||
21648 (cc >= 0xf900 && cc < 0xfb00 )
21654 if(this.owner.fireEvent('beforesync', this, html) !== false){
21655 this.el.dom.value = html;
21656 this.owner.fireEvent('sync', this, html);
21662 * Protected method that will not generally be called directly. Pushes the value of the textarea
21663 * into the iframe editor.
21665 pushValue : function(){
21666 if(this.initialized){
21667 var v = this.el.dom.value.trim();
21669 // if(v.length < 1){
21673 if(this.owner.fireEvent('beforepush', this, v) !== false){
21674 var d = (this.doc.body || this.doc.documentElement);
21676 this.cleanUpPaste();
21677 this.el.dom.value = d.innerHTML;
21678 this.owner.fireEvent('push', this, v);
21684 deferFocus : function(){
21685 this.focus.defer(10, this);
21689 focus : function(){
21690 if(this.win && !this.sourceEditMode){
21697 assignDocWin: function()
21699 var iframe = this.iframe;
21702 this.doc = iframe.contentWindow.document;
21703 this.win = iframe.contentWindow;
21705 // if (!Roo.get(this.frameId)) {
21708 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21709 // this.win = Roo.get(this.frameId).dom.contentWindow;
21711 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21715 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21716 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21721 initEditor : function(){
21722 //console.log("INIT EDITOR");
21723 this.assignDocWin();
21727 this.doc.designMode="on";
21729 this.doc.write(this.getDocMarkup());
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');
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,
21752 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21754 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21755 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21757 this.initialized = true;
21759 this.owner.fireEvent('initialize', this);
21764 onDestroy : function(){
21770 //for (var i =0; i < this.toolbars.length;i++) {
21771 // // fixme - ask toolbars for heights?
21772 // this.toolbars[i].onDestroy();
21775 //this.wrap.dom.innerHTML = '';
21776 //this.wrap.remove();
21781 onFirstFocus : function(){
21783 this.assignDocWin();
21786 this.activated = true;
21789 if(Roo.isGecko){ // prevent silly gecko errors
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));
21799 this.execCmd('useCSS', true);
21800 this.execCmd('styleWithCSS', false);
21803 this.owner.fireEvent('activate', this);
21807 adjustFont: function(btn){
21808 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21809 //if(Roo.isSafari){ // safari
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];
21822 v = Math.max(1, v+adjust);
21824 this.execCmd('FontSize', v );
21827 onEditorEvent : function(e)
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
21834 insertTag : function(tg)
21836 // could be a bit smarter... -> wrap the current selected tRoo..
21837 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21839 range = this.createRange(this.getSelection());
21840 var wrappingNode = this.doc.createElement(tg.toLowerCase());
21841 wrappingNode.appendChild(range.extractContents());
21842 range.insertNode(wrappingNode);
21849 this.execCmd("formatblock", tg);
21853 insertText : function(txt)
21857 var range = this.createRange();
21858 range.deleteContents();
21859 //alert(Sender.getAttribute('label'));
21861 range.insertNode(this.doc.createTextNode(txt));
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)
21872 relayCmd : function(cmd, value){
21874 this.execCmd(cmd, value);
21875 this.owner.fireEvent('editorevent', this);
21876 //this.updateToolbar();
21877 this.owner.deferFocus();
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)
21887 execCmd : function(cmd, value){
21888 this.doc.execCommand(cmd, false, value === undefined ? null : value);
21895 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21897 * @param {String} text | dom node..
21899 insertAtCursor : function(text)
21902 if(!this.activated){
21908 var r = this.doc.selection.createRange();
21919 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21923 // from jquery ui (MIT licenced)
21925 var win = this.win;
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);
21936 // no firefox support
21937 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21938 this.execCmd('InsertHTML', txt);
21947 mozKeyPress : function(e){
21949 var c = e.getCharCode(), cmd;
21952 c = String.fromCharCode(c).toLowerCase();
21966 this.cleanUpPaste.defer(100, this);
21974 e.preventDefault();
21982 fixKeys : function(){ // load time branching for fastest keydown performance
21984 return function(e){
21985 var k = e.getKey(), r;
21988 r = this.doc.selection.createRange();
21991 r.pasteHTML('    ');
21998 r = this.doc.selection.createRange();
22000 var target = r.parentElement();
22001 if(!target || target.tagName.toLowerCase() != 'li'){
22003 r.pasteHTML('<br />');
22009 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22010 this.cleanUpPaste.defer(100, this);
22016 }else if(Roo.isOpera){
22017 return function(e){
22018 var k = e.getKey();
22022 this.execCmd('InsertHTML','    ');
22025 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22026 this.cleanUpPaste.defer(100, this);
22031 }else if(Roo.isSafari){
22032 return function(e){
22033 var k = e.getKey();
22037 this.execCmd('InsertText','\t');
22041 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22042 this.cleanUpPaste.defer(100, this);
22050 getAllAncestors: function()
22052 var p = this.getSelectedNode();
22055 a.push(p); // push blank onto stack..
22056 p = this.getParentElement();
22060 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22064 a.push(this.doc.body);
22068 lastSelNode : false,
22071 getSelection : function()
22073 this.assignDocWin();
22074 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22077 getSelectedNode: function()
22079 // this may only work on Gecko!!!
22081 // should we cache this!!!!
22086 var range = this.createRange(this.getSelection()).cloneRange();
22089 var parent = range.parentElement();
22091 var testRange = range.duplicate();
22092 testRange.moveToElementText(parent);
22093 if (testRange.inRange(range)) {
22096 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22099 parent = parent.parentElement;
22104 // is ancestor a text element.
22105 var ac = range.commonAncestorContainer;
22106 if (ac.nodeType == 3) {
22107 ac = ac.parentNode;
22110 var ar = ac.childNodes;
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 ?
22119 // fullly contained node.
22121 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
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]);
22132 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22137 has_other_nodes = true;
22139 if (!nodes.length && other_nodes.length) {
22140 nodes= other_nodes;
22142 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22148 createRange: function(sel)
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") {
22155 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22157 return this.doc.createRange();
22160 return this.doc.createRange();
22163 getParentElement: function()
22166 this.assignDocWin();
22167 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22169 var range = this.createRange(sel);
22172 var p = range.commonAncestorContainer;
22173 while (p.nodeType == 3) { // text node
22184 * Range intersection.. the hard stuff...
22188 * [ -- selected range --- ]
22192 * if end is before start or hits it. fail.
22193 * if start is after end or hits it fail.
22195 * if either hits (but other is outside. - then it's not
22201 // @see http://www.thismuchiknow.co.uk/?p=64.
22202 rangeIntersectsNode : function(range, node)
22204 var nodeRange = node.ownerDocument.createRange();
22206 nodeRange.selectNode(node);
22208 nodeRange.selectNodeContents(node);
22211 var rangeStartRange = range.cloneRange();
22212 rangeStartRange.collapse(true);
22214 var rangeEndRange = range.cloneRange();
22215 rangeEndRange.collapse(false);
22217 var nodeStartRange = nodeRange.cloneRange();
22218 nodeStartRange.collapse(true);
22220 var nodeEndRange = nodeRange.cloneRange();
22221 nodeEndRange.collapse(false);
22223 return rangeStartRange.compareBoundaryPoints(
22224 Range.START_TO_START, nodeEndRange) == -1 &&
22225 rangeEndRange.compareBoundaryPoints(
22226 Range.START_TO_START, nodeStartRange) == 1;
22230 rangeCompareNode : function(range, node)
22232 var nodeRange = node.ownerDocument.createRange();
22234 nodeRange.selectNode(node);
22236 nodeRange.selectNodeContents(node);
22240 range.collapse(true);
22242 nodeRange.collapse(true);
22244 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22245 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22247 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22249 var nodeIsBefore = ss == 1;
22250 var nodeIsAfter = ee == -1;
22252 if (nodeIsBefore && nodeIsAfter) {
22255 if (!nodeIsBefore && nodeIsAfter) {
22256 return 1; //right trailed.
22259 if (nodeIsBefore && !nodeIsAfter) {
22260 return 2; // left trailed.
22266 // private? - in a new class?
22267 cleanUpPaste : function()
22269 // cleans up the whole document..
22270 Roo.log('cleanuppaste');
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;
22280 cleanWordChars : function(input) {// change the chars to hex code
22281 var he = Roo.HtmlEditorCore;
22283 var output = input;
22284 Roo.each(he.swapCodes, function(sw) {
22285 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22287 output = output.replace(swapper, sw[1]);
22294 cleanUpChildren : function (n)
22296 if (!n.childNodes.length) {
22299 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22300 this.cleanUpChild(n.childNodes[i]);
22307 cleanUpChild : function (node)
22310 //console.log(node);
22311 if (node.nodeName == "#text") {
22312 // clean up silly Windows -- stuff?
22315 if (node.nodeName == "#comment") {
22316 node.parentNode.removeChild(node);
22317 // clean up silly Windows -- stuff?
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..
22324 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22326 node.parentNode.removeChild(node);
22331 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
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..
22336 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22337 // remove_keep_children = true;
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);
22348 node.parentNode.removeChild(node);
22352 if (!node.attributes || !node.attributes.length) {
22353 this.cleanUpChildren(node);
22357 function cleanAttr(n,v)
22360 if (v.match(/^\./) || v.match(/^\//)) {
22363 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22366 if (v.match(/^#/)) {
22369 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22370 node.removeAttribute(n);
22374 var cwhite = this.cwhite;
22375 var cblack = this.cblack;
22377 function cleanStyle(n,v)
22379 if (v.match(/expression/)) { //XSS?? should we even bother..
22380 node.removeAttribute(n);
22384 var parts = v.split(/;/);
22387 Roo.each(parts, function(p) {
22388 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22392 var l = p.split(':').shift().replace(/\s+/g,'');
22393 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22395 if ( cwhite.length && cblack.indexOf(l) > -1) {
22396 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22397 //node.removeAttribute(n);
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);
22414 if (clean.length) {
22415 node.setAttribute(n, clean.join(';'));
22417 node.removeAttribute(n);
22423 for (var i = node.attributes.length-1; i > -1 ; i--) {
22424 var a = node.attributes[i];
22427 if (a.name.toLowerCase().substr(0,2)=='on') {
22428 node.removeAttribute(a.name);
22431 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22432 node.removeAttribute(a.name);
22435 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22436 cleanAttr(a.name,a.value); // fixme..
22439 if (a.name == 'style') {
22440 cleanStyle(a.name,a.value);
22443 /// clean up MS crap..
22444 // tecnically this should be a list of valid class'es..
22447 if (a.name == 'class') {
22448 if (a.value.match(/^Mso/)) {
22449 node.className = '';
22452 if (a.value.match(/^body$/)) {
22453 node.className = '';
22464 this.cleanUpChildren(node);
22470 * Clean up MS wordisms...
22472 cleanWord : function(node)
22477 this.cleanWord(this.doc.body);
22480 if (node.nodeName == "#text") {
22481 // clean up silly Windows -- stuff?
22484 if (node.nodeName == "#comment") {
22485 node.parentNode.removeChild(node);
22486 // clean up silly Windows -- stuff?
22490 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22491 node.parentNode.removeChild(node);
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);
22502 node.parentNode.removeChild(node);
22503 this.iterateChildren(node, this.cleanWord);
22507 if (node.className.length) {
22509 var cn = node.className.split(/\W+/);
22511 Roo.each(cn, function(cls) {
22512 if (cls.match(/Mso[a-zA-Z]+/)) {
22517 node.className = cna.length ? cna.join(' ') : '';
22519 node.removeAttribute("class");
22523 if (node.hasAttribute("lang")) {
22524 node.removeAttribute("lang");
22527 if (node.hasAttribute("style")) {
22529 var styles = node.getAttribute("style").split(";");
22531 Roo.each(styles, function(s) {
22532 if (!s.match(/:/)) {
22535 var kv = s.split(":");
22536 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22539 // what ever is left... we allow.
22542 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22543 if (!nstyle.length) {
22544 node.removeAttribute('style');
22547 this.iterateChildren(node, this.cleanWord);
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.
22557 iterateChildren : function(node, fn)
22559 if (!node.childNodes.length) {
22562 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22563 fn.call(this, node.childNodes[i])
22569 * cleanTableWidths.
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..
22575 cleanTableWidths : function(node)
22580 this.cleanTableWidths(this.doc.body);
22585 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22588 Roo.log(node.tagName);
22589 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22590 this.iterateChildren(node, this.cleanTableWidths);
22593 if (node.hasAttribute('width')) {
22594 node.removeAttribute('width');
22598 if (node.hasAttribute("style")) {
22601 var styles = node.getAttribute("style").split(";");
22603 Roo.each(styles, function(s) {
22604 if (!s.match(/:/)) {
22607 var kv = s.split(":");
22608 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22611 // what ever is left... we allow.
22614 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22615 if (!nstyle.length) {
22616 node.removeAttribute('style');
22620 this.iterateChildren(node, this.cleanTableWidths);
22628 domToHTML : function(currentElement, depth, nopadtext) {
22630 depth = depth || 0;
22631 nopadtext = nopadtext || false;
22633 if (!currentElement) {
22634 return this.domToHTML(this.doc.body);
22637 //Roo.log(currentElement);
22639 var allText = false;
22640 var nodeName = currentElement.nodeName;
22641 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22643 if (nodeName == '#text') {
22645 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22650 if (nodeName != 'BODY') {
22653 // Prints the node tagName, such as <A>, <IMG>, etc
22656 for(i = 0; i < currentElement.attributes.length;i++) {
22658 var aname = currentElement.attributes.item(i).name;
22659 if (!currentElement.attributes.item(i).value.length) {
22662 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22665 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22674 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22677 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22682 // Traverse the tree
22684 var currentElementChild = currentElement.childNodes.item(i);
22685 var allText = true;
22686 var innerHTML = '';
22688 while (currentElementChild) {
22689 // Formatting code (indent the tree so it looks nice on the screen)
22690 var nopad = nopadtext;
22691 if (lastnode == 'SPAN') {
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( " " );
22701 innerHTML += toadd;
22704 currentElementChild = currentElement.childNodes.item(i);
22710 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22712 // Recursively traverse the tree structure of the child node
22713 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22714 lastnode = currentElementChild.nodeName;
22716 currentElementChild=currentElement.childNodes.item(i);
22722 // The remaining code is mostly for formatting the tree
22723 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22728 ret+= "</"+tagName+">";
22734 applyBlacklists : function()
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 : [];
22741 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22742 if (b.indexOf(tag) > -1) {
22745 this.white.push(tag);
22749 Roo.each(w, function(tag) {
22750 if (b.indexOf(tag) > -1) {
22753 if (this.white.indexOf(tag) > -1) {
22756 this.white.push(tag);
22761 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22762 if (w.indexOf(tag) > -1) {
22765 this.black.push(tag);
22769 Roo.each(b, function(tag) {
22770 if (w.indexOf(tag) > -1) {
22773 if (this.black.indexOf(tag) > -1) {
22776 this.black.push(tag);
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 : [];
22786 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22787 if (b.indexOf(tag) > -1) {
22790 this.cwhite.push(tag);
22794 Roo.each(w, function(tag) {
22795 if (b.indexOf(tag) > -1) {
22798 if (this.cwhite.indexOf(tag) > -1) {
22801 this.cwhite.push(tag);
22806 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22807 if (w.indexOf(tag) > -1) {
22810 this.cblack.push(tag);
22814 Roo.each(b, function(tag) {
22815 if (w.indexOf(tag) > -1) {
22818 if (this.cblack.indexOf(tag) > -1) {
22821 this.cblack.push(tag);
22826 setStylesheets : function(stylesheets)
22828 if(typeof(stylesheets) == 'string'){
22829 Roo.get(this.iframe.contentDocument.head).createChild({
22831 rel : 'stylesheet',
22840 Roo.each(stylesheets, function(s) {
22845 Roo.get(_this.iframe.contentDocument.head).createChild({
22847 rel : 'stylesheet',
22856 removeStylesheets : function()
22860 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22865 setStyle : function(style)
22867 Roo.get(this.iframe.contentDocument.head).createChild({
22876 // hide stuff that is not compatible
22890 * @event specialkey
22894 * @cfg {String} fieldClass @hide
22897 * @cfg {String} focusClass @hide
22900 * @cfg {String} autoCreate @hide
22903 * @cfg {String} inputType @hide
22906 * @cfg {String} invalidClass @hide
22909 * @cfg {String} invalidText @hide
22912 * @cfg {String} msgFx @hide
22915 * @cfg {String} validateOnBlur @hide
22919 Roo.HtmlEditorCore.white = [
22920 'area', 'br', 'img', 'input', 'hr', 'wbr',
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',
22928 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
22931 'dir', 'menu', 'ol', 'ul', 'dl',
22937 Roo.HtmlEditorCore.black = [
22938 // 'embed', 'object', // enable - backend responsiblity to clean thiese
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..
22945 Roo.HtmlEditorCore.clean = [
22946 'script', 'style', 'title', 'xml'
22948 Roo.HtmlEditorCore.remove = [
22953 Roo.HtmlEditorCore.ablack = [
22957 Roo.HtmlEditorCore.aclean = [
22958 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
22962 Roo.HtmlEditorCore.pwhite= [
22963 'http', 'https', 'mailto'
22966 // white listed style attributes.
22967 Roo.HtmlEditorCore.cwhite= [
22968 // 'text-align', /// default is to allow most things..
22974 // black listed style attributes.
22975 Roo.HtmlEditorCore.cblack= [
22976 // 'font-size' -- this can be set by the project
22980 Roo.HtmlEditorCore.swapCodes =[
22999 * @class Roo.bootstrap.HtmlEditor
23000 * @extends Roo.bootstrap.TextArea
23001 * Bootstrap HtmlEditor class
23004 * Create a new HtmlEditor
23005 * @param {Object} config The config object
23008 Roo.bootstrap.HtmlEditor = function(config){
23009 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23010 if (!this.toolbars) {
23011 this.toolbars = [];
23014 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23017 * @event initialize
23018 * Fires when the editor is fully initialized (including the iframe)
23019 * @param {HtmlEditor} this
23024 * Fires when the editor is first receives the focus. Any insertion must wait
23025 * until after this event.
23026 * @param {HtmlEditor} this
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
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
23047 * Fires when the textarea is updated with content from the editor iframe.
23048 * @param {HtmlEditor} this
23049 * @param {String} html
23054 * Fires when the iframe editor is updated with content from the textarea.
23055 * @param {HtmlEditor} this
23056 * @param {String} html
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.
23065 editmodechange: true,
23067 * @event editorevent
23068 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23069 * @param {HtmlEditor} this
23073 * @event firstfocus
23074 * Fires when on first focus - needed by toolbars..
23075 * @param {HtmlEditor} this
23080 * Auto save the htmlEditor value as a file into Events
23081 * @param {HtmlEditor} this
23085 * @event savedpreview
23086 * preview the saved version of htmlEditor
23087 * @param {HtmlEditor} this
23094 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23098 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23103 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23108 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23113 * @cfg {Number} height (in pixels)
23117 * @cfg {Number} width (in pixels)
23122 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23125 stylesheets: false,
23130 // private properties
23131 validationEvent : false,
23133 initialized : false,
23136 onFocus : Roo.emptyFn,
23138 hideMode:'offsets',
23140 tbContainer : false,
23144 toolbarContainer :function() {
23145 return this.wrap.select('.x-html-editor-tb',true).first();
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
23154 createToolbar : function(){
23155 Roo.log('renewing');
23156 Roo.log("create toolbars");
23158 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23159 this.toolbars[0].render(this.toolbarContainer());
23163 // if (!editor.toolbars || !editor.toolbars.length) {
23164 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
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);
23178 onRender : function(ct, position)
23180 // Roo.log("Call onRender: " + this.xtype);
23182 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23184 this.wrap = this.inputEl().wrap({
23185 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23188 this.editorcore.onRender(ct, position);
23190 if (this.resizable) {
23191 this.resizeEl = new Roo.Resizable(this.wrap, {
23195 minHeight : this.height,
23196 height: this.height,
23197 handles : this.resizable,
23200 resize : function(r, w, h) {
23201 _t.onResize(w,h); // -something
23207 this.createToolbar(this);
23210 if(!this.width && this.resizable){
23211 this.setSize(this.wrap.getSize());
23213 if (this.resizeEl) {
23214 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23215 // should trigger onReize..
23221 onResize : function(w, h)
23223 Roo.log('resize: ' +w + ',' + h );
23224 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
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));
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();
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));
23254 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23255 this.editorcore.onResize(ew,eh);
23260 * Toggles the editor between standard and source edit mode.
23261 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23263 toggleSourceEdit : function(sourceEditMode)
23265 this.editorcore.toggleSourceEdit(sourceEditMode);
23267 if(this.editorcore.sourceEditMode){
23268 Roo.log('editor - showing textarea');
23271 // Roo.log(this.syncValue());
23273 this.inputEl().removeClass(['hide', 'x-hidden']);
23274 this.inputEl().dom.removeAttribute('tabIndex');
23275 this.inputEl().focus();
23277 Roo.log('editor - hiding textarea');
23279 // Roo.log(this.pushValue());
23282 this.inputEl().addClass(['hide', 'x-hidden']);
23283 this.inputEl().dom.setAttribute('tabIndex', -1);
23284 //this.deferFocus();
23287 if(this.resizable){
23288 this.setSize(this.wrap.getSize());
23291 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23294 // private (for BoxComponent)
23295 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23297 // private (for BoxComponent)
23298 getResizeEl : function(){
23302 // private (for BoxComponent)
23303 getPositionEl : function(){
23308 initEvents : function(){
23309 this.originalValue = this.getValue();
23313 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23316 // markInvalid : Roo.emptyFn,
23318 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23321 // clearInvalid : Roo.emptyFn,
23323 setValue : function(v){
23324 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23325 this.editorcore.pushValue();
23330 deferFocus : function(){
23331 this.focus.defer(10, this);
23335 focus : function(){
23336 this.editorcore.focus();
23342 onDestroy : function(){
23348 for (var i =0; i < this.toolbars.length;i++) {
23349 // fixme - ask toolbars for heights?
23350 this.toolbars[i].onDestroy();
23353 this.wrap.dom.innerHTML = '';
23354 this.wrap.remove();
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();
23369 syncValue : function()
23371 this.editorcore.syncValue();
23374 pushValue : function()
23376 this.editorcore.pushValue();
23380 // hide stuff that is not compatible
23394 * @event specialkey
23398 * @cfg {String} fieldClass @hide
23401 * @cfg {String} focusClass @hide
23404 * @cfg {String} autoCreate @hide
23407 * @cfg {String} inputType @hide
23410 * @cfg {String} invalidClass @hide
23413 * @cfg {String} invalidText @hide
23416 * @cfg {String} msgFx @hide
23419 * @cfg {String} validateOnBlur @hide
23428 Roo.namespace('Roo.bootstrap.htmleditor');
23430 * @class Roo.bootstrap.HtmlEditorToolbar1
23435 new Roo.bootstrap.HtmlEditor({
23438 new Roo.bootstrap.HtmlEditorToolbar1({
23439 disable : { fonts: 1 , format: 1, ..., ... , ...],
23445 * @cfg {Object} disable List of elements to disable..
23446 * @cfg {Array} btns List of additional buttons.
23450 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23453 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23456 Roo.apply(this, config);
23458 // default disabled, based on 'good practice'..
23459 this.disable = this.disable || {};
23460 Roo.applyIf(this.disable, {
23463 specialElements : true
23465 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23467 this.editor = config.editor;
23468 this.editorcore = config.editor.editorcore;
23470 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23472 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23473 // dont call parent... till later.
23475 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23480 editorcore : false,
23485 "h1","h2","h3","h4","h5","h6",
23487 "abbr", "acronym", "address", "cite", "samp", "var",
23491 onRender : function(ct, position)
23493 // Roo.log("Call onRender: " + this.xtype);
23495 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23497 this.el.dom.style.marginBottom = '0';
23499 var editorcore = this.editorcore;
23500 var editor= this.editor;
23503 var btn = function(id,cmd , toggle, handler, html){
23505 var event = toggle ? 'toggle' : 'click';
23510 xns: Roo.bootstrap,
23513 enableToggle:toggle !== false,
23515 pressed : toggle ? false : null,
23518 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23519 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23525 // var cb_box = function...
23530 xns: Roo.bootstrap,
23531 glyphicon : 'font',
23535 xns: Roo.bootstrap,
23539 Roo.each(this.formats, function(f) {
23540 style.menu.items.push({
23542 xns: Roo.bootstrap,
23543 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23548 editorcore.insertTag(this.tagname);
23555 children.push(style);
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);
23569 btn('list','insertunorderedlist',true);
23570 btn('pencil', false,true, function(btn){
23572 this.toggleSourceEdit(btn.pressed);
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]);
23585 xns: Roo.bootstrap,
23590 xns: Roo.bootstrap,
23595 cog.menu.items.push({
23597 xns: Roo.bootstrap,
23598 html : Clean styles,
23603 editorcore.insertTag(this.tagname);
23612 this.xtype = 'NavSimplebar';
23614 for(var i=0;i< children.length;i++) {
23616 this.buttons.add(this.addxtypeChild(children[i]));
23620 editor.on('editorevent', this.updateToolbar, this);
23622 onBtnClick : function(id)
23624 this.editorcore.relayCmd(id);
23625 this.editorcore.focus();
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.
23632 updateToolbar: function(){
23634 if(!this.editorcore.activated){
23635 this.editor.onFirstFocus(); // is this neeed?
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'));
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'));
23649 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23650 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23653 var ans = this.editorcore.getAllAncestors();
23654 if (this.formatCombo) {
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) {
23662 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23670 // hides menus... - so this cant be on a menu...
23671 Roo.bootstrap.MenuMgr.hideAll();
23673 Roo.bootstrap.MenuMgr.hideAll();
23674 //this.editorsyncValue();
23676 onFirstFocus: function() {
23677 this.buttons.each(function(item){
23681 toggleSourceEdit : function(sourceEditMode){
23684 if(sourceEditMode){
23685 Roo.log("disabling buttons");
23686 this.buttons.each( function(item){
23687 if(item.cmd != 'pencil'){
23693 Roo.log("enabling buttons");
23694 if(this.editorcore.initialized){
23695 this.buttons.each( function(item){
23701 Roo.log("calling toggole on editor");
23702 // tell the editor that it's been pressed..
23703 this.editor.toggleSourceEdit(sourceEditMode);
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.
23719 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23720 this.locked = false;
23721 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
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){
23733 * Locks the selections.
23736 this.locked = true;
23740 * Unlocks the selections.
23742 unlock : function(){
23743 this.locked = false;
23747 * Returns true if the selections are locked.
23748 * @return {Boolean}
23750 isLocked : function(){
23751 return this.locked;
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.
23760 * @param {Object} config
23763 Roo.bootstrap.Table.RowSelectionModel = function(config){
23764 Roo.apply(this, config);
23765 this.selections = new Roo.util.MixedCollection(false, function(o){
23770 this.lastActive = false;
23774 * @event selectionchange
23775 * Fires when the selection changes
23776 * @param {SelectionModel} this
23778 "selectionchange" : true,
23780 * @event afterselectionchange
23781 * Fires after the selection changes (eg. by key press or clicking)
23782 * @param {SelectionModel} this
23784 "afterselectionchange" : true,
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
23792 "beforerowselect" : true,
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
23800 "rowselect" : true,
23802 * @event rowdeselect
23803 * Fires when a row is deselected.
23804 * @param {SelectionModel} this
23805 * @param {Number} rowIndex The selected index
23807 "rowdeselect" : true
23809 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23810 this.locked = false;
23813 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
23815 * @cfg {Boolean} singleSelect
23816 * True to allow selection of only one row at a time (defaults to false)
23818 singleSelect : false,
23821 initEvents : function()
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);
23829 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23830 this.grid.on("rowclick", this.handleMouseDown, this);
23832 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23833 "up" : function(e){
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){
23844 this.selectFirstRow();
23846 this.fireEvent("afterselectionchange", this);
23848 "down" : function(e){
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){
23859 this.selectFirstRow();
23861 this.fireEvent("afterselectionchange", this);
23865 this.grid.store.on('load', function(){
23866 this.selections.clear();
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);
23877 onRefresh : function()
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){
23891 onRemove : function(v, index, r){
23892 this.selections.remove(r);
23896 onRowUpdated : function(v, index, r){
23897 if(this.isSelected(r)){
23898 v.onRowSelect(index);
23904 * @param {Array} records The records to select
23905 * @param {Boolean} keepExisting (optional) True to keep existing selections
23907 selectRecords : function(records, keepExisting)
23910 this.clearSelections();
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);
23919 * Gets the number of selected rows.
23922 getCount : function(){
23923 return this.selections.length;
23927 * Selects the first row in the grid.
23929 selectFirstRow : function(){
23934 * Select the last row.
23935 * @param {Boolean} keepExisting (optional) True to keep existing selections
23937 selectLastRow : function(keepExisting){
23938 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23939 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23943 * Selects the row immediately following the last selected row.
23944 * @param {Boolean} keepExisting (optional) True to keep existing selections
23946 selectNext : function(keepExisting)
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);
23955 * Selects the row that precedes the last selected row.
23956 * @param {Boolean} keepExisting (optional) True to keep existing selections
23958 selectPrevious : function(keepExisting){
23960 this.selectRow(this.last-1, keepExisting);
23961 this.grid.getView().focusRow(this.last);
23966 * Returns the selected records
23967 * @return {Array} Array of selected records
23969 getSelections : function(){
23970 return [].concat(this.selections.items);
23974 * Returns the first selected record.
23977 getSelected : function(){
23978 return this.selections.itemAt(0);
23983 * Clears all selections.
23985 clearSelections : function(fast)
23991 var ds = this.grid.store;
23992 var s = this.selections;
23993 s.each(function(r){
23994 this.deselectRow(ds.indexOfId(r.id));
23998 this.selections.clear();
24005 * Selects all rows.
24007 selectAll : function(){
24011 this.selections.clear();
24012 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24013 this.selectRow(i, true);
24018 * Returns True if there is a selection.
24019 * @return {Boolean}
24021 hasSelection : function(){
24022 return this.selections.length > 0;
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}
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);
24036 * Returns True if the specified record id is selected.
24037 * @param {String} id The id of record to check
24038 * @return {Boolean}
24040 isIdSelected : function(id){
24041 return (this.selections.key(id) ? true : false);
24046 handleMouseDBClick : function(e, t){
24050 handleMouseDown : function(e, t)
24052 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24053 if(this.isLocked() || rowIndex < 0 ){
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
24063 var isSelected = this.isSelected(rowIndex);
24064 //Roo.log("select row:" + rowIndex);
24066 this.deselectRow(rowIndex);
24068 this.selectRow(rowIndex, true);
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);
24083 this.fireEvent("afterselectionchange", this);
24086 handleDragableRowClick : function(grid, rowIndex, e)
24088 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24089 this.selectRow(rowIndex, false);
24090 grid.view.focusRow(rowIndex);
24091 this.fireEvent("afterselectionchange", this);
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
24100 selectRows : function(rows, keepExisting){
24102 this.clearSelections();
24104 for(var i = 0, len = rows.length; i < len; i++){
24105 this.selectRow(rows[i], true);
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
24115 selectRange : function(startRow, endRow, keepExisting){
24120 this.clearSelections();
24122 if(startRow <= endRow){
24123 for(var i = startRow; i <= endRow; i++){
24124 this.selectRow(i, true);
24127 for(var i = startRow; i >= endRow; i--){
24128 this.selectRow(i, true);
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
24138 deselectRange : function(startRow, endRow, preventViewNotify){
24142 for(var i = startRow; i <= endRow; i++){
24143 this.deselectRow(i, preventViewNotify);
24149 * @param {Number} row The index of the row to select
24150 * @param {Boolean} keepExisting (optional) True to keep existing selections
24152 selectRow : function(index, keepExisting, preventViewNotify)
24154 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24157 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24158 if(!keepExisting || this.singleSelect){
24159 this.clearSelections();
24162 var r = this.grid.store.getAt(index);
24163 //console.log('selectRow - record id :' + r.id);
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)
24171 proxy.addClass('bg-info info');
24173 this.fireEvent("rowselect", this, index, r);
24174 this.fireEvent("selectionchange", this);
24180 * @param {Number} row The index of the row to deselect
24182 deselectRow : function(index, preventViewNotify)
24187 if(this.last == index){
24190 if(this.lastActive == index){
24191 this.lastActive = false;
24194 var r = this.grid.store.getAt(index);
24199 this.selections.remove(r);
24200 //.console.log('deselectRow - record id :' + r.id);
24201 if(!preventViewNotify){
24203 var proxy = new Roo.Element(
24204 this.grid.getRowDom(index)
24206 proxy.removeClass('bg-info info');
24208 this.fireEvent("rowdeselect", this, index);
24209 this.fireEvent("selectionchange", this);
24213 restoreLast : function(){
24215 this.last = this._last;
24220 acceptsNav : function(row, col, cm){
24221 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24225 onEditorKey : function(field, e){
24226 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24231 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24233 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24235 }else if(k == e.ENTER && !e.ctrlKey){
24239 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24241 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24243 }else if(k == e.ESC){
24247 g.startEditing(newCell[0], newCell[1]);
24253 * Ext JS Library 1.1.1
24254 * Copyright(c) 2006-2007, Ext JS, LLC.
24256 * Originally Released Under LGPL - original licence link has changed is not relivant.
24259 * <script type="text/javascript">
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.
24267 * Create a new PagingToolbar
24268 * @param {Object} config The config object
24269 * @param {Roo.data.Store} store
24271 Roo.bootstrap.PagingToolbar = function(config)
24273 // old args format still supported... - xtype is prefered..
24274 // created from xtype...
24276 this.ds = config.dataSource;
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;
24284 this.toolbarItems = [];
24285 if (config.items) {
24286 this.toolbarItems = config.items;
24289 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24294 this.bind(this.ds);
24297 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24301 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24303 * @cfg {Roo.data.Store} dataSource
24304 * The underlying data store providing the paged data
24307 * @cfg {String/HTMLElement/Element} container
24308 * container The id or element that will contain the toolbar
24311 * @cfg {Boolean} displayInfo
24312 * True to display the displayMsg (defaults to false)
24315 * @cfg {Number} pageSize
24316 * The number of records to display per page (defaults to 20)
24320 * @cfg {String} displayMsg
24321 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24323 displayMsg : 'Displaying {0} - {1} of {2}',
24325 * @cfg {String} emptyMsg
24326 * The message to display when no records are found (defaults to "No data to display")
24328 emptyMsg : 'No data to display',
24330 * Customizable piece of the default paging text (defaults to "Page")
24333 beforePageText : "Page",
24335 * Customizable piece of the default paging text (defaults to "of %0")
24338 afterPageText : "of {0}",
24340 * Customizable piece of the default paging text (defaults to "First Page")
24343 firstText : "First Page",
24345 * Customizable piece of the default paging text (defaults to "Previous Page")
24348 prevText : "Previous Page",
24350 * Customizable piece of the default paging text (defaults to "Next Page")
24353 nextText : "Next Page",
24355 * Customizable piece of the default paging text (defaults to "Last Page")
24358 lastText : "Last Page",
24360 * Customizable piece of the default paging text (defaults to "Refresh")
24363 refreshText : "Refresh",
24367 onRender : function(ct, position)
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
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();
24384 Roo.each(_this.buttons, function(e){ // this might need to use render????
24385 Roo.factory(e).onRender(_this.el, null);
24389 Roo.each(_this.toolbarItems, function(e) {
24390 _this.navgroup.addItem(e);
24394 this.first = this.navgroup.addItem({
24395 tooltip: this.firstText,
24397 icon : 'fa fa-backward',
24399 preventDefault: true,
24400 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24403 this.prev = this.navgroup.addItem({
24404 tooltip: this.prevText,
24406 icon : 'fa fa-step-backward',
24408 preventDefault: true,
24409 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24411 //this.addSeparator();
24414 var field = this.navgroup.addItem( {
24416 cls : 'x-paging-position',
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>'
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();});
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,
24434 html : ' <i class="fa fa-step-forward">',
24436 preventDefault: true,
24437 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24439 this.last = this.navgroup.addItem({
24440 tooltip: this.lastText,
24441 icon : 'fa fa-forward',
24444 preventDefault: true,
24445 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
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"]) }
24458 updateInfo : function(){
24459 if(this.displayEl){
24460 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24461 var msg = count == 0 ?
24465 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24467 this.displayEl.update(msg);
24472 onLoad : function(ds, r, o)
24474 this.cursor = o.params.start ? o.params.start : 0;
24476 var d = this.getPageData(),
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();
24492 getPageData : function(){
24493 var total = this.ds.getTotalCount();
24496 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24497 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24502 onLoadError : function(){
24503 this.loading.enable();
24507 onPagingKeydown : function(e){
24508 var k = e.getKey();
24509 var d = this.getPageData();
24511 var v = this.field.dom.value, pageNum;
24512 if(!v || isNaN(pageNum = parseInt(v, 10))){
24513 this.field.dom.value = d.activePage;
24516 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24517 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
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))
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}});
24527 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
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) {
24534 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24535 this.field.dom.value = d.activePage;
24538 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
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}});
24549 beforeLoad : function(){
24551 this.loading.disable();
24556 onClick : function(which){
24565 ds.load({params:{start: 0, limit: this.pageSize}});
24568 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24571 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
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}});
24580 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24586 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24587 * @param {Roo.data.Store} store The data store to unbind
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;
24599 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24600 * @param {Roo.data.Store} store The data store to bind
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);
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
24628 * Create a new Element
24629 * @param {Object} config The config object
24632 Roo.bootstrap.MessageBar = function(config){
24633 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24636 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24642 beforeClass: 'bootstrap-sticky-wrap',
24644 getAutoCreate : function(){
24648 cls: 'alert alert-dismissable alert-' + this.weight,
24653 html: this.html || ''
24659 cfg.cls += ' alert-messages-fixed';
24673 onRender : function(ct, position)
24675 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24678 var cfg = Roo.apply({}, this.getAutoCreate());
24682 cfg.cls += ' ' + this.cls;
24685 cfg.style = this.style;
24687 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24689 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24692 this.el.select('>button.close').on('click', this.hide, this);
24698 if (!this.rendered) {
24704 this.fireEvent('show', this);
24710 if (!this.rendered) {
24716 this.fireEvent('hide', this);
24719 update : function()
24721 // var e = this.el.dom.firstChild;
24723 // if(this.closable){
24724 // e = e.nextSibling;
24727 // e.data = this.html || '';
24729 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24745 * @class Roo.bootstrap.Graph
24746 * @extends Roo.bootstrap.Component
24747 * Bootstrap Graph class
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
24760 -opts (object) options for the chart
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?)
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
24768 o stretch (boolean)
24770 -opts (object) options for the pie
24773 o startAngle (number)
24774 o endAngle (number)
24778 * Create a new Input
24779 * @param {Object} config The config object
24782 Roo.bootstrap.Graph = function(config){
24783 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24789 * The img click event for the img.
24790 * @param {Roo.EventObject} e
24796 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
24807 //g_colors: this.colors,
24814 getAutoCreate : function(){
24825 onRender : function(ct,position){
24828 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24830 if (typeof(Raphael) == 'undefined') {
24831 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24835 this.raphael = Raphael(this.el.dom);
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" };
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);
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"});
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,
24861 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24863 // this.load(null,xdata,{
24864 // axis : "0 0 1 1",
24865 // axisxlabels : xdata
24870 load : function(graphtype,xdata,opts)
24872 this.raphael.clear();
24874 graphtype = this.graphtype;
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);
24883 fout = function () {
24884 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24886 pfin = function() {
24887 this.sector.stop();
24888 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24891 this.label[0].stop();
24892 this.label[0].attr({ r: 7.5 });
24893 this.label[1].attr({ "font-weight": 800 });
24896 pfout = function() {
24897 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24900 this.label[0].animate({ r: 5 }, 500, "bounce");
24901 this.label[1].attr({ "font-weight": 400 });
24907 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24910 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24913 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
24914 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24916 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24923 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24928 setTitle: function(o)
24933 initEvents: function() {
24936 this.el.on('click', this.onClick, this);
24940 onClick : function(e)
24942 Roo.log('img onclick');
24943 this.fireEvent('click', this, e);
24955 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
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
24968 * Create a new NumberBox
24969 * @param {Object} config The config object
24973 Roo.bootstrap.dash.NumberBox = function(config){
24974 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24978 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
24987 getAutoCreate : function(){
24991 cls : 'small-box ',
24999 cls : 'roo-headline',
25000 html : this.headline
25004 cls : 'roo-content',
25005 html : this.content
25019 cls : 'ion ' + this.icon
25028 cls : 'small-box-footer',
25029 href : this.fhref || '#',
25033 cfg.cn.push(footer);
25040 onRender : function(ct,position){
25041 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25048 setHeadline: function (value)
25050 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25053 setFooter: function (value, href)
25055 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25058 this.el.select('a.small-box-footer',true).first().attr('href', href);
25063 setContent: function (value)
25065 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25068 initEvents: function()
25082 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
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
25094 * Create a new TabBox
25095 * @param {Object} config The config object
25099 Roo.bootstrap.dash.TabBox = function(config){
25100 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25105 * When a pane is added
25106 * @param {Roo.bootstrap.dash.TabPane} pane
25110 * @event activatepane
25111 * When a pane is activated
25112 * @param {Roo.bootstrap.dash.TabPane} pane
25114 "activatepane" : true
25122 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25127 tabScrollable : false,
25129 getChildContainer : function()
25131 return this.el.select('.tab-content', true).first();
25134 getAutoCreate : function(){
25138 cls: 'pull-left header',
25146 cls: 'fa ' + this.icon
25152 cls: 'nav nav-tabs pull-right',
25158 if(this.tabScrollable){
25165 cls: 'nav nav-tabs pull-right',
25176 cls: 'nav-tabs-custom',
25181 cls: 'tab-content no-padding',
25189 initEvents : function()
25191 //Roo.log('add add pane handler');
25192 this.on('addpane', this.onAddPane, this);
25195 * Updates the box title
25196 * @param {String} html to set the title to.
25198 setTitle : function(value)
25200 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25202 onAddPane : function(pane)
25204 this.panes.push(pane);
25205 //Roo.log('addpane');
25207 // tabs are rendere left to right..
25208 if(!this.showtabs){
25212 var ctr = this.el.select('.nav-tabs', true).first();
25215 var existing = ctr.select('.nav-tab',true);
25216 var qty = existing.getCount();;
25219 var tab = ctr.createChild({
25221 cls : 'nav-tab' + (qty ? '' : ' active'),
25229 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25232 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25234 pane.el.addClass('active');
25239 onTabClick : function(ev,un,ob,pane)
25241 //Roo.log('tab - prev default');
25242 ev.preventDefault();
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');
25258 getActivePane : function()
25261 Roo.each(this.panes, function(p) {
25262 if(p.el.hasClass('active')){
25283 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
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
25293 * Create a new TabPane
25294 * @param {Object} config The config object
25297 Roo.bootstrap.dash.TabPane = function(config){
25298 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25304 * When a pane is activated
25305 * @param {Roo.bootstrap.dash.TabPane} pane
25312 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25317 // the tabBox that this is attached to.
25320 getAutoCreate : function()
25328 cfg.cls += ' active';
25333 initEvents : function()
25335 //Roo.log('trigger add pane handler');
25336 this.parent().fireEvent('addpane', this)
25340 * Updates the tab title
25341 * @param {String} html to set the title to.
25343 setTitle: function(str)
25349 this.tab.select('a', true).first().dom.innerHTML = str;
25366 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
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
25379 * Create a new Menu
25380 * @param {Object} config The config object
25384 Roo.bootstrap.menu.Menu = function(config){
25385 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25389 * @event beforeshow
25390 * Fires before this menu is displayed
25391 * @param {Roo.bootstrap.menu.Menu} this
25395 * @event beforehide
25396 * Fires before this menu is hidden
25397 * @param {Roo.bootstrap.menu.Menu} this
25402 * Fires after this menu is displayed
25403 * @param {Roo.bootstrap.menu.Menu} this
25408 * Fires after this menu is hidden
25409 * @param {Roo.bootstrap.menu.Menu} this
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
25423 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25427 weight : 'default',
25432 getChildContainer : function() {
25433 if(this.isSubMenu){
25437 return this.el.select('ul.dropdown-menu', true).first();
25440 getAutoCreate : function()
25445 cls : 'roo-menu-text',
25453 cls : 'fa ' + this.icon
25464 cls : 'dropdown-button btn btn-' + this.weight,
25469 cls : 'dropdown-toggle btn btn-' + this.weight,
25479 cls : 'dropdown-menu'
25485 if(this.pos == 'top'){
25486 cfg.cls += ' dropup';
25489 if(this.isSubMenu){
25492 cls : 'dropdown-menu'
25499 onRender : function(ct, position)
25501 this.isSubMenu = ct.hasClass('dropdown-submenu');
25503 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25506 initEvents : function()
25508 if(this.isSubMenu){
25512 this.hidden = true;
25514 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25515 this.triggerEl.on('click', this.onTriggerPress, this);
25517 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25518 this.buttonEl.on('click', this.onClick, this);
25524 if(this.isSubMenu){
25528 return this.el.select('ul.dropdown-menu', true).first();
25531 onClick : function(e)
25533 this.fireEvent("click", this, e);
25536 onTriggerPress : function(e)
25538 if (this.isVisible()) {
25545 isVisible : function(){
25546 return !this.hidden;
25551 this.fireEvent("beforeshow", this);
25553 this.hidden = false;
25554 this.el.addClass('open');
25556 Roo.get(document).on("mouseup", this.onMouseUp, this);
25558 this.fireEvent("show", this);
25565 this.fireEvent("beforehide", this);
25567 this.hidden = true;
25568 this.el.removeClass('open');
25570 Roo.get(document).un("mouseup", this.onMouseUp);
25572 this.fireEvent("hide", this);
25575 onMouseUp : function()
25589 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
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
25605 * Create a new Item
25606 * @param {Object} config The config object
25610 Roo.bootstrap.menu.Item = function(config){
25611 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25615 * Fires when the mouse is hovering over this menu
25616 * @param {Roo.bootstrap.menu.Item} this
25617 * @param {Roo.EventObject} e
25622 * Fires when the mouse exits this menu
25623 * @param {Roo.bootstrap.menu.Item} this
25624 * @param {Roo.EventObject} e
25630 * The raw click event for the entire grid.
25631 * @param {Roo.EventObject} e
25637 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25642 preventDefault: true,
25647 getAutoCreate : function()
25652 cls : 'roo-menu-item-text',
25660 cls : 'fa ' + this.icon
25669 href : this.href || '#',
25676 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25680 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25682 if(this.pos == 'left'){
25683 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25690 initEvents : function()
25692 this.el.on('mouseover', this.onMouseOver, this);
25693 this.el.on('mouseout', this.onMouseOut, this);
25695 this.el.select('a', true).first().on('click', this.onClick, this);
25699 onClick : function(e)
25701 if(this.preventDefault){
25702 e.preventDefault();
25705 this.fireEvent("click", this, e);
25708 onMouseOver : function(e)
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);
25714 this.fireEvent("mouseover", this, e);
25717 onMouseOut : function(e)
25719 this.fireEvent("mouseout", this, e);
25731 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25734 * @class Roo.bootstrap.menu.Separator
25735 * @extends Roo.bootstrap.Component
25736 * Bootstrap Separator class
25739 * Create a new Separator
25740 * @param {Object} config The config object
25744 Roo.bootstrap.menu.Separator = function(config){
25745 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25748 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
25750 getAutoCreate : function(){
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.
25776 * It needs to add support for additional attributes like tooltip-position
25779 * Create a new Toolti
25780 * @param {Object} config The config object
25783 Roo.bootstrap.Tooltip = function(config){
25784 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25786 this.alignment = Roo.bootstrap.Tooltip.alignment;
25788 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25789 this.alignment = config.alignment;
25794 Roo.apply(Roo.bootstrap.Tooltip, {
25796 * @function init initialize tooltip monitoring.
25800 currentTip : false,
25801 currentRegion : false,
25807 Roo.get(document).on('mouseover', this.enter ,this);
25808 Roo.get(document).on('mouseout', this.leave, this);
25811 this.currentTip = new Roo.bootstrap.Tooltip();
25814 enter : function(ev)
25816 var dom = ev.getTarget();
25818 //Roo.log(['enter',dom]);
25819 var el = Roo.fly(dom);
25820 if (this.currentEl) {
25822 //Roo.log(this.currentEl);
25823 //Roo.log(this.currentEl.contains(dom));
25824 if (this.currentEl == el) {
25827 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25833 if (this.currentTip.el) {
25834 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25838 if(!el || el.dom == document){
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) {
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.");
25856 //Roo.log("child element over..");
25859 this.currentEl = bindEl;
25860 this.currentTip.bind(bindEl);
25861 this.currentRegion = Roo.lib.Region.getRegion(dom);
25862 this.currentTip.enter();
25865 leave : function(ev)
25867 var dom = ev.getTarget();
25868 //Roo.log(['leave',dom]);
25869 if (!this.currentEl) {
25874 if (dom != this.currentEl.dom) {
25877 var xy = ev.getXY();
25878 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
25881 // only activate leave if mouse cursor is outside... bounding box..
25886 if (this.currentTip) {
25887 this.currentTip.leave();
25889 //Roo.log('clear currentEl');
25890 this.currentEl = false;
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']
25904 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
25909 delay : null, // can be { show : 300 , hide: 500}
25913 hoverState : null, //???
25915 placement : 'bottom',
25919 getAutoCreate : function(){
25926 cls : 'tooltip-arrow'
25929 cls : 'tooltip-inner'
25936 bind : function(el)
25942 enter : function () {
25944 if (this.timeout != null) {
25945 clearTimeout(this.timeout);
25948 this.hoverState = 'in';
25949 //Roo.log("enter - show");
25950 if (!this.delay || !this.delay.show) {
25955 this.timeout = setTimeout(function () {
25956 if (_t.hoverState == 'in') {
25959 }, this.delay.show);
25963 clearTimeout(this.timeout);
25965 this.hoverState = 'out';
25966 if (!this.delay || !this.delay.hide) {
25972 this.timeout = setTimeout(function () {
25973 //Roo.log("leave - timeout");
25975 if (_t.hoverState == 'out') {
25977 Roo.bootstrap.Tooltip.currentEl = false;
25982 show : function (msg)
25985 this.render(document.body);
25988 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25990 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25992 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25994 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25996 var placement = typeof this.placement == 'function' ?
25997 this.placement.call(this, this.el, on_el) :
26000 var autoToken = /\s?auto?\s?/i;
26001 var autoPlace = autoToken.test(placement);
26003 placement = placement.replace(autoToken, '') || 'top';
26007 //this.el.setXY([0,0]);
26009 //this.el.dom.style.display='block';
26011 //this.el.appendTo(on_el);
26013 var p = this.getPosition();
26014 var box = this.el.getBox();
26020 var align = this.alignment[placement];
26022 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26024 if(placement == 'top' || placement == 'bottom'){
26026 placement = 'right';
26029 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26030 placement = 'left';
26033 var scroll = Roo.select('body', true).first().getScroll();
26035 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26041 this.el.alignTo(this.bindEl, align[0],align[1]);
26042 //var arrow = this.el.select('.arrow',true).first();
26043 //arrow.set(align[2],
26045 this.el.addClass(placement);
26047 this.el.addClass('in fade');
26049 this.hoverState = null;
26051 if (this.el.hasClass('fade')) {
26062 //this.el.setXY([0,0]);
26063 this.el.removeClass('in');
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
26098 * Create a new LocationPicker
26099 * @param {Object} config The config object
26103 Roo.bootstrap.LocationPicker = function(config){
26105 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26110 * Fires when the picker initialized.
26111 * @param {Roo.bootstrap.LocationPicker} this
26112 * @param {Google Location} location
26116 * @event positionchanged
26117 * Fires when the picker position changed.
26118 * @param {Roo.bootstrap.LocationPicker} this
26119 * @param {Google Location} location
26121 positionchanged : true,
26124 * Fires when the map resize.
26125 * @param {Roo.bootstrap.LocationPicker} this
26130 * Fires when the map show.
26131 * @param {Roo.bootstrap.LocationPicker} this
26136 * Fires when the map hide.
26137 * @param {Roo.bootstrap.LocationPicker} this
26142 * Fires when click the map.
26143 * @param {Roo.bootstrap.LocationPicker} this
26144 * @param {Map event} e
26148 * @event mapRightClick
26149 * Fires when right click the map.
26150 * @param {Roo.bootstrap.LocationPicker} this
26151 * @param {Map event} e
26153 mapRightClick : true,
26155 * @event markerClick
26156 * Fires when click the marker.
26157 * @param {Roo.bootstrap.LocationPicker} this
26158 * @param {Map event} e
26160 markerClick : true,
26162 * @event markerRightClick
26163 * Fires when right click the marker.
26164 * @param {Roo.bootstrap.LocationPicker} this
26165 * @param {Map event} e
26167 markerRightClick : true,
26169 * @event OverlayViewDraw
26170 * Fires when OverlayView Draw
26171 * @param {Roo.bootstrap.LocationPicker} this
26173 OverlayViewDraw : true,
26175 * @event OverlayViewOnAdd
26176 * Fires when OverlayView Draw
26177 * @param {Roo.bootstrap.LocationPicker} this
26179 OverlayViewOnAdd : true,
26181 * @event OverlayViewOnRemove
26182 * Fires when OverlayView Draw
26183 * @param {Roo.bootstrap.LocationPicker} this
26185 OverlayViewOnRemove : true,
26187 * @event OverlayViewShow
26188 * Fires when OverlayView Draw
26189 * @param {Roo.bootstrap.LocationPicker} this
26190 * @param {Pixel} cpx
26192 OverlayViewShow : true,
26194 * @event OverlayViewHide
26195 * Fires when OverlayView Draw
26196 * @param {Roo.bootstrap.LocationPicker} this
26198 OverlayViewHide : true,
26200 * @event loadexception
26201 * Fires when load google lib failed.
26202 * @param {Roo.bootstrap.LocationPicker} this
26204 loadexception : true
26209 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26211 gMapContext: false,
26217 mapTypeControl: false,
26218 disableDoubleClickZoom: false,
26220 streetViewControl: false,
26224 enableAutocomplete: false,
26225 enableReverseGeocode: true,
26228 getAutoCreate: function()
26233 cls: 'roo-location-picker'
26239 initEvents: function(ct, position)
26241 if(!this.el.getWidth() || this.isApplied()){
26245 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26250 initial: function()
26252 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26253 this.fireEvent('loadexception', this);
26257 if(!this.mapTypeId){
26258 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26261 this.gMapContext = this.GMapContext();
26263 this.initOverlayView();
26265 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26269 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26270 _this.setPosition(_this.gMapContext.marker.position);
26273 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26274 _this.fireEvent('mapClick', this, event);
26278 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26279 _this.fireEvent('mapRightClick', this, event);
26283 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26284 _this.fireEvent('markerClick', this, event);
26288 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26289 _this.fireEvent('markerRightClick', this, event);
26293 this.setPosition(this.gMapContext.location);
26295 this.fireEvent('initial', this, this.gMapContext.location);
26298 initOverlayView: function()
26302 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26306 _this.fireEvent('OverlayViewDraw', _this);
26311 _this.fireEvent('OverlayViewOnAdd', _this);
26314 onRemove: function()
26316 _this.fireEvent('OverlayViewOnRemove', _this);
26319 show: function(cpx)
26321 _this.fireEvent('OverlayViewShow', _this, cpx);
26326 _this.fireEvent('OverlayViewHide', _this);
26332 fromLatLngToContainerPixel: function(event)
26334 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26337 isApplied: function()
26339 return this.getGmapContext() == false ? false : true;
26342 getGmapContext: function()
26344 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26347 GMapContext: function()
26349 var position = new google.maps.LatLng(this.latitude, this.longitude);
26351 var _map = new google.maps.Map(this.el.dom, {
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
26365 var _marker = new google.maps.Marker({
26366 position: position,
26368 title: this.markerTitle,
26369 draggable: this.draggable
26376 location: position,
26377 radius: this.radius,
26378 locationName: this.locationName,
26379 addressComponents: {
26380 formatted_address: null,
26381 addressLine1: null,
26382 addressLine2: null,
26384 streetNumber: null,
26388 stateOrProvince: null
26391 domContainer: this.el.dom,
26392 geodecoder: new google.maps.Geocoder()
26396 drawCircle: function(center, radius, options)
26398 if (this.gMapContext.circle != null) {
26399 this.gMapContext.circle.setMap(null);
26403 options = Roo.apply({}, options, {
26404 strokeColor: "#0000FF",
26405 strokeOpacity: .35,
26407 fillColor: "#0000FF",
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;
26421 setPosition: function(location)
26423 this.gMapContext.location = location;
26424 this.gMapContext.marker.setPosition(location);
26425 this.gMapContext.map.panTo(location);
26426 this.drawCircle(location, this.gMapContext.radius, {});
26430 if (this.gMapContext.settings.enableReverseGeocode) {
26431 this.gMapContext.geodecoder.geocode({
26432 latLng: this.gMapContext.location
26433 }, function(results, status) {
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);
26439 _this.fireEvent('positionchanged', this, location);
26446 this.fireEvent('positionchanged', this, location);
26451 google.maps.event.trigger(this.gMapContext.map, "resize");
26453 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26455 this.fireEvent('resize', this);
26458 setPositionByLatLng: function(latitude, longitude)
26460 this.setPosition(new google.maps.LatLng(latitude, longitude));
26463 getCurrentPosition: function()
26466 latitude: this.gMapContext.location.lat(),
26467 longitude: this.gMapContext.location.lng()
26471 getAddressName: function()
26473 return this.gMapContext.locationName;
26476 getAddressComponents: function()
26478 return this.gMapContext.addressComponents;
26481 address_component_from_google_geocode: function(address_components)
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;
26506 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26507 result.addressLine2 = "";
26511 setZoomLevel: function(zoom)
26513 this.gMapContext.map.setZoom(zoom);
26526 this.fireEvent('show', this);
26537 this.fireEvent('hide', this);
26542 Roo.apply(Roo.bootstrap.LocationPicker, {
26544 OverlayView : function(map, options)
26546 options = options || {};
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
26569 * Create a new alert
26570 * @param {Object} config The config object
26574 Roo.bootstrap.Alert = function(config){
26575 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26579 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26586 getAutoCreate : function()
26595 cls : 'roo-alert-icon'
26600 cls : 'roo-alert-title',
26605 cls : 'roo-alert-text',
26612 cfg.cn[0].cls += ' fa ' + this.faicon;
26616 cfg.cls += ' alert-' + this.weight;
26622 initEvents: function()
26624 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26627 setTitle : function(str)
26629 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26632 setText : function(str)
26634 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26637 setWeight : function(weight)
26640 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26643 this.weight = weight;
26645 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26648 setIcon : function(icon)
26651 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26654 this.faicon = icon;
26656 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
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...'
26694 * Create a new UploadCropbox
26695 * @param {Object} config The config object
26698 Roo.bootstrap.UploadCropbox = function(config){
26699 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26703 * @event beforeselectfile
26704 * Fire before select file
26705 * @param {Roo.bootstrap.UploadCropbox} this
26707 "beforeselectfile" : true,
26710 * Fire after initEvent
26711 * @param {Roo.bootstrap.UploadCropbox} this
26716 * Fire after initEvent
26717 * @param {Roo.bootstrap.UploadCropbox} this
26718 * @param {String} data
26723 * Fire when preparing the file data
26724 * @param {Roo.bootstrap.UploadCropbox} this
26725 * @param {Object} file
26730 * Fire when get exception
26731 * @param {Roo.bootstrap.UploadCropbox} this
26732 * @param {XMLHttpRequest} xhr
26734 "exception" : true,
26736 * @event beforeloadcanvas
26737 * Fire before load the canvas
26738 * @param {Roo.bootstrap.UploadCropbox} this
26739 * @param {String} src
26741 "beforeloadcanvas" : true,
26744 * Fire when trash image
26745 * @param {Roo.bootstrap.UploadCropbox} this
26750 * Fire when download the image
26751 * @param {Roo.bootstrap.UploadCropbox} this
26755 * @event footerbuttonclick
26756 * Fire when footerbuttonclick
26757 * @param {Roo.bootstrap.UploadCropbox} this
26758 * @param {String} type
26760 "footerbuttonclick" : true,
26764 * @param {Roo.bootstrap.UploadCropbox} this
26769 * Fire when rotate the image
26770 * @param {Roo.bootstrap.UploadCropbox} this
26771 * @param {String} pos
26776 * Fire when inspect the file
26777 * @param {Roo.bootstrap.UploadCropbox} this
26778 * @param {Object} file
26783 * Fire when xhr upload the file
26784 * @param {Roo.bootstrap.UploadCropbox} this
26785 * @param {Object} data
26790 * Fire when arrange the file data
26791 * @param {Roo.bootstrap.UploadCropbox} this
26792 * @param {Object} formData
26797 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26800 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
26802 emptyText : 'Click to upload image',
26803 rotateNotify : 'Image is too small to rotate',
26804 errorTimeout : 3000,
26818 cropType : 'image/jpeg',
26820 canvasLoaded : false,
26821 isDocument : false,
26823 paramName : 'imageUpload',
26825 loadingText : 'Loading...',
26828 getAutoCreate : function()
26832 cls : 'roo-upload-cropbox',
26836 cls : 'roo-upload-cropbox-selector',
26841 cls : 'roo-upload-cropbox-body',
26842 style : 'cursor:pointer',
26846 cls : 'roo-upload-cropbox-preview'
26850 cls : 'roo-upload-cropbox-thumb'
26854 cls : 'roo-upload-cropbox-empty-notify',
26855 html : this.emptyText
26859 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26860 html : this.rotateNotify
26866 cls : 'roo-upload-cropbox-footer',
26869 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26879 onRender : function(ct, position)
26881 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26883 if (this.buttons.length) {
26885 Roo.each(this.buttons, function(bb) {
26887 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26889 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26895 this.maskEl = this.el;
26899 initEvents : function()
26901 this.urlAPI = (window.createObjectURL && window) ||
26902 (window.URL && URL.revokeObjectURL && URL) ||
26903 (window.webkitURL && webkitURL);
26905 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26906 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26908 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26909 this.selectorEl.hide();
26911 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26912 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
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();
26918 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26919 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
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();
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();
26929 this.setThumbBoxSize();
26935 this.fireEvent('initial', this);
26942 window.addEventListener("resize", function() { _this.resize(); } );
26944 this.bodyEl.on('click', this.beforeSelectFile, this);
26947 this.bodyEl.on('touchstart', this.onTouchStart, this);
26948 this.bodyEl.on('touchmove', this.onTouchMove, this);
26949 this.bodyEl.on('touchend', this.onTouchEnd, this);
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);
26960 this.selectorEl.on('change', this.onFileSelected, this);
26966 this.baseScale = 1;
26968 this.baseRotate = 1;
26969 this.dragable = false;
26970 this.pinching = false;
26973 this.cropData = false;
26974 this.notifyEl.dom.innerHTML = this.emptyText;
26976 this.selectorEl.dom.value = '';
26980 resize : function()
26982 if(this.fireEvent('resize', this) != false){
26983 this.setThumbBoxPosition();
26984 this.setCanvasPosition();
26988 onFooterButtonClick : function(e, el, o, type)
26991 case 'rotate-left' :
26992 this.onRotateLeft(e);
26994 case 'rotate-right' :
26995 this.onRotateRight(e);
26998 this.beforeSelectFile(e);
27013 this.fireEvent('footerbuttonclick', this, type);
27016 beforeSelectFile : function(e)
27018 e.preventDefault();
27020 if(this.fireEvent('beforeselectfile', this) != false){
27021 this.selectorEl.dom.click();
27025 onFileSelected : function(e)
27027 e.preventDefault();
27029 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27033 var file = this.selectorEl.dom.files[0];
27035 if(this.fireEvent('inspect', this, file) != false){
27036 this.prepare(file);
27041 trash : function(e)
27043 this.fireEvent('trash', this);
27046 download : function(e)
27048 this.fireEvent('download', this);
27051 loadCanvas : function(src)
27053 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27057 this.imageEl = document.createElement('img');
27061 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27063 this.imageEl.src = src;
27067 onLoadCanvas : function()
27069 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27070 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27072 this.bodyEl.un('click', this.beforeSelectFile, this);
27074 this.notifyEl.hide();
27075 this.thumbEl.show();
27076 this.footerEl.show();
27078 this.baseRotateLevel();
27080 if(this.isDocument){
27081 this.setThumbBoxSize();
27084 this.setThumbBoxPosition();
27086 this.baseScaleLevel();
27092 this.canvasLoaded = true;
27095 this.maskEl.unmask();
27100 setCanvasPosition : function()
27102 if(!this.canvasEl){
27106 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27107 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27109 this.previewEl.setLeft(pw);
27110 this.previewEl.setTop(ph);
27114 onMouseDown : function(e)
27118 this.dragable = true;
27119 this.pinching = false;
27121 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27122 this.dragable = false;
27126 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27127 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27131 onMouseMove : function(e)
27135 if(!this.canvasLoaded){
27139 if (!this.dragable){
27143 var minX = Math.ceil(this.thumbEl.getLeft(true));
27144 var minY = Math.ceil(this.thumbEl.getTop(true));
27146 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27147 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27149 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27150 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27152 x = x - this.mouseX;
27153 y = y - this.mouseY;
27155 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27156 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27158 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27159 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27161 this.previewEl.setLeft(bgX);
27162 this.previewEl.setTop(bgY);
27164 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27165 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27168 onMouseUp : function(e)
27172 this.dragable = false;
27175 onMouseWheel : function(e)
27179 this.startScale = this.scale;
27181 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27183 if(!this.zoomable()){
27184 this.scale = this.startScale;
27193 zoomable : function()
27195 var minScale = this.thumbEl.getWidth() / this.minWidth;
27197 if(this.minWidth < this.minHeight){
27198 minScale = this.thumbEl.getHeight() / this.minHeight;
27201 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27202 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27206 (this.rotate == 0 || this.rotate == 180) &&
27208 width > this.imageEl.OriginWidth ||
27209 height > this.imageEl.OriginHeight ||
27210 (width < this.minWidth && height < this.minHeight)
27218 (this.rotate == 90 || this.rotate == 270) &&
27220 width > this.imageEl.OriginWidth ||
27221 height > this.imageEl.OriginHeight ||
27222 (width < this.minHeight && height < this.minWidth)
27229 !this.isDocument &&
27230 (this.rotate == 0 || this.rotate == 180) &&
27232 width < this.minWidth ||
27233 width > this.imageEl.OriginWidth ||
27234 height < this.minHeight ||
27235 height > this.imageEl.OriginHeight
27242 !this.isDocument &&
27243 (this.rotate == 90 || this.rotate == 270) &&
27245 width < this.minHeight ||
27246 width > this.imageEl.OriginWidth ||
27247 height < this.minWidth ||
27248 height > this.imageEl.OriginHeight
27258 onRotateLeft : function(e)
27260 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27262 var minScale = this.thumbEl.getWidth() / this.minWidth;
27264 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27265 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27267 this.startScale = this.scale;
27269 while (this.getScaleLevel() < minScale){
27271 this.scale = this.scale + 1;
27273 if(!this.zoomable()){
27278 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27279 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27284 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27291 this.scale = this.startScale;
27293 this.onRotateFail();
27298 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27300 if(this.isDocument){
27301 this.setThumbBoxSize();
27302 this.setThumbBoxPosition();
27303 this.setCanvasPosition();
27308 this.fireEvent('rotate', this, 'left');
27312 onRotateRight : function(e)
27314 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27316 var minScale = this.thumbEl.getWidth() / this.minWidth;
27318 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27319 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27321 this.startScale = this.scale;
27323 while (this.getScaleLevel() < minScale){
27325 this.scale = this.scale + 1;
27327 if(!this.zoomable()){
27332 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27333 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27338 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27345 this.scale = this.startScale;
27347 this.onRotateFail();
27352 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27354 if(this.isDocument){
27355 this.setThumbBoxSize();
27356 this.setThumbBoxPosition();
27357 this.setCanvasPosition();
27362 this.fireEvent('rotate', this, 'right');
27365 onRotateFail : function()
27367 this.errorEl.show(true);
27371 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27376 this.previewEl.dom.innerHTML = '';
27378 var canvasEl = document.createElement("canvas");
27380 var contextEl = canvasEl.getContext("2d");
27382 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27383 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27384 var center = this.imageEl.OriginWidth / 2;
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;
27392 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27394 contextEl.translate(center, center);
27395 contextEl.rotate(this.rotate * Math.PI / 180);
27397 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27399 this.canvasEl = document.createElement("canvas");
27401 this.contextEl = this.canvasEl.getContext("2d");
27403 switch (this.rotate) {
27406 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27407 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27409 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27414 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27415 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
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);
27422 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27427 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27428 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
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);
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);
27440 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27441 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
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);
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);
27455 this.previewEl.appendChild(this.canvasEl);
27457 this.setCanvasPosition();
27462 if(!this.canvasLoaded){
27466 var imageCanvas = document.createElement("canvas");
27468 var imageContext = imageCanvas.getContext("2d");
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;
27473 var center = imageCanvas.width / 2;
27475 imageContext.translate(center, center);
27477 imageContext.rotate(this.rotate * Math.PI / 180);
27479 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27481 var canvas = document.createElement("canvas");
27483 var context = canvas.getContext("2d");
27485 canvas.width = this.minWidth;
27486 canvas.height = this.minHeight;
27488 switch (this.rotate) {
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());
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());
27497 var targetWidth = this.minWidth - 2 * x;
27498 var targetHeight = this.minHeight - 2 * y;
27502 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27503 scale = targetWidth / width;
27506 if(x > 0 && y == 0){
27507 scale = targetHeight / height;
27510 if(x > 0 && y > 0){
27511 scale = targetWidth / width;
27513 if(width < height){
27514 scale = targetHeight / height;
27518 context.scale(scale, scale);
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));
27523 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27524 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27526 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
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());
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());
27537 var targetWidth = this.minWidth - 2 * x;
27538 var targetHeight = this.minHeight - 2 * y;
27542 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27543 scale = targetWidth / width;
27546 if(x > 0 && y == 0){
27547 scale = targetHeight / height;
27550 if(x > 0 && y > 0){
27551 scale = targetWidth / width;
27553 if(width < height){
27554 scale = targetHeight / height;
27558 context.scale(scale, scale);
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));
27563 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27564 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27566 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27568 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
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());
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());
27579 var targetWidth = this.minWidth - 2 * x;
27580 var targetHeight = this.minHeight - 2 * y;
27584 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27585 scale = targetWidth / width;
27588 if(x > 0 && y == 0){
27589 scale = targetHeight / height;
27592 if(x > 0 && y > 0){
27593 scale = targetWidth / width;
27595 if(width < height){
27596 scale = targetHeight / height;
27600 context.scale(scale, scale);
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));
27605 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27606 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
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;
27611 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
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());
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());
27622 var targetWidth = this.minWidth - 2 * x;
27623 var targetHeight = this.minHeight - 2 * y;
27627 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27628 scale = targetWidth / width;
27631 if(x > 0 && y == 0){
27632 scale = targetHeight / height;
27635 if(x > 0 && y > 0){
27636 scale = targetWidth / width;
27638 if(width < height){
27639 scale = targetHeight / height;
27643 context.scale(scale, scale);
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));
27648 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27649 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27651 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27653 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27660 this.cropData = canvas.toDataURL(this.cropType);
27662 if(this.fireEvent('crop', this, this.cropData) !== false){
27663 this.process(this.file, this.cropData);
27670 setThumbBoxSize : function()
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);
27678 this.minWidth = width;
27679 this.minHeight = height;
27681 if(this.rotate == 90 || this.rotate == 270){
27682 this.minWidth = height;
27683 this.minHeight = width;
27688 width = Math.ceil(this.minWidth * height / this.minHeight);
27690 if(this.minWidth > this.minHeight){
27692 height = Math.ceil(this.minHeight * width / this.minWidth);
27695 this.thumbEl.setStyle({
27696 width : width + 'px',
27697 height : height + 'px'
27704 setThumbBoxPosition : function()
27706 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27707 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27709 this.thumbEl.setLeft(x);
27710 this.thumbEl.setTop(y);
27714 baseRotateLevel : function()
27716 this.baseRotate = 1;
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
27723 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27726 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27730 baseScaleLevel : function()
27734 if(this.isDocument){
27736 if(this.baseRotate == 6 || this.baseRotate == 8){
27738 height = this.thumbEl.getHeight();
27739 this.baseScale = height / this.imageEl.OriginWidth;
27741 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27742 width = this.thumbEl.getWidth();
27743 this.baseScale = width / this.imageEl.OriginHeight;
27749 height = this.thumbEl.getHeight();
27750 this.baseScale = height / this.imageEl.OriginHeight;
27752 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27753 width = this.thumbEl.getWidth();
27754 this.baseScale = width / this.imageEl.OriginWidth;
27760 if(this.baseRotate == 6 || this.baseRotate == 8){
27762 width = this.thumbEl.getHeight();
27763 this.baseScale = width / this.imageEl.OriginHeight;
27765 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27766 height = this.thumbEl.getWidth();
27767 this.baseScale = height / this.imageEl.OriginHeight;
27770 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27771 height = this.thumbEl.getWidth();
27772 this.baseScale = height / this.imageEl.OriginHeight;
27774 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27775 width = this.thumbEl.getHeight();
27776 this.baseScale = width / this.imageEl.OriginWidth;
27783 width = this.thumbEl.getWidth();
27784 this.baseScale = width / this.imageEl.OriginWidth;
27786 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27787 height = this.thumbEl.getHeight();
27788 this.baseScale = height / this.imageEl.OriginHeight;
27791 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27793 height = this.thumbEl.getHeight();
27794 this.baseScale = height / this.imageEl.OriginHeight;
27796 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27797 width = this.thumbEl.getWidth();
27798 this.baseScale = width / this.imageEl.OriginWidth;
27806 getScaleLevel : function()
27808 return this.baseScale * Math.pow(1.1, this.scale);
27811 onTouchStart : function(e)
27813 if(!this.canvasLoaded){
27814 this.beforeSelectFile(e);
27818 var touches = e.browserEvent.touches;
27824 if(touches.length == 1){
27825 this.onMouseDown(e);
27829 if(touches.length != 2){
27835 for(var i = 0, finger; finger = touches[i]; i++){
27836 coords.push(finger.pageX, finger.pageY);
27839 var x = Math.pow(coords[0] - coords[2], 2);
27840 var y = Math.pow(coords[1] - coords[3], 2);
27842 this.startDistance = Math.sqrt(x + y);
27844 this.startScale = this.scale;
27846 this.pinching = true;
27847 this.dragable = false;
27851 onTouchMove : function(e)
27853 if(!this.pinching && !this.dragable){
27857 var touches = e.browserEvent.touches;
27864 this.onMouseMove(e);
27870 for(var i = 0, finger; finger = touches[i]; i++){
27871 coords.push(finger.pageX, finger.pageY);
27874 var x = Math.pow(coords[0] - coords[2], 2);
27875 var y = Math.pow(coords[1] - coords[3], 2);
27877 this.endDistance = Math.sqrt(x + y);
27879 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27881 if(!this.zoomable()){
27882 this.scale = this.startScale;
27890 onTouchEnd : function(e)
27892 this.pinching = false;
27893 this.dragable = false;
27897 process : function(file, crop)
27900 this.maskEl.mask(this.loadingText);
27903 this.xhr = new XMLHttpRequest();
27905 file.xhr = this.xhr;
27907 this.xhr.open(this.method, this.url, true);
27910 "Accept": "application/json",
27911 "Cache-Control": "no-cache",
27912 "X-Requested-With": "XMLHttpRequest"
27915 for (var headerName in headers) {
27916 var headerValue = headers[headerName];
27918 this.xhr.setRequestHeader(headerName, headerValue);
27924 this.xhr.onload = function()
27926 _this.xhrOnLoad(_this.xhr);
27929 this.xhr.onerror = function()
27931 _this.xhrOnError(_this.xhr);
27934 var formData = new FormData();
27936 formData.append('returnHTML', 'NO');
27939 formData.append('crop', crop);
27942 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27943 formData.append(this.paramName, file, file.name);
27946 if(typeof(file.filename) != 'undefined'){
27947 formData.append('filename', file.filename);
27950 if(typeof(file.mimetype) != 'undefined'){
27951 formData.append('mimetype', file.mimetype);
27954 if(this.fireEvent('arrange', this, formData) != false){
27955 this.xhr.send(formData);
27959 xhrOnLoad : function(xhr)
27962 this.maskEl.unmask();
27965 if (xhr.readyState !== 4) {
27966 this.fireEvent('exception', this, xhr);
27970 var response = Roo.decode(xhr.responseText);
27972 if(!response.success){
27973 this.fireEvent('exception', this, xhr);
27977 var response = Roo.decode(xhr.responseText);
27979 this.fireEvent('upload', this, response);
27983 xhrOnError : function()
27986 this.maskEl.unmask();
27989 Roo.log('xhr on error');
27991 var response = Roo.decode(xhr.responseText);
27997 prepare : function(file)
28000 this.maskEl.mask(this.loadingText);
28006 if(typeof(file) === 'string'){
28007 this.loadCanvas(file);
28011 if(!file || !this.urlAPI){
28016 this.cropType = file.type;
28020 if(this.fireEvent('prepare', this, this.file) != false){
28022 var reader = new FileReader();
28024 reader.onload = function (e) {
28025 if (e.target.error) {
28026 Roo.log(e.target.error);
28030 var buffer = e.target.result,
28031 dataView = new DataView(buffer),
28033 maxOffset = dataView.byteLength - 4,
28037 if (dataView.getUint16(0) === 0xffd8) {
28038 while (offset < maxOffset) {
28039 markerBytes = dataView.getUint16(offset);
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.');
28048 if(markerBytes == 0xffe1){
28049 _this.parseExifData(
28056 offset += markerLength;
28066 var url = _this.urlAPI.createObjectURL(_this.file);
28068 _this.loadCanvas(url);
28073 reader.readAsArrayBuffer(this.file);
28079 parseExifData : function(dataView, offset, length)
28081 var tiffOffset = offset + 10,
28085 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28086 // No Exif data, might be XMP data instead
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
28095 if (tiffOffset + 8 > dataView.byteLength) {
28096 Roo.log('Invalid Exif data: Invalid segment size.');
28099 // Check for the two null bytes:
28100 if (dataView.getUint16(offset + 8) !== 0x0000) {
28101 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28104 // Check the byte alignment:
28105 switch (dataView.getUint16(tiffOffset)) {
28107 littleEndian = true;
28110 littleEndian = false;
28113 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
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.');
28121 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28122 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28124 this.parseExifTags(
28127 tiffOffset + dirOffset,
28132 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28137 if (dirOffset + 6 > dataView.byteLength) {
28138 Roo.log('Invalid Exif data: Invalid directory offset.');
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.');
28147 for (i = 0; i < tagsNumber; i += 1) {
28151 dirOffset + 2 + 12 * i, // tag offset
28155 // Return the offset to the next directory:
28156 return dataView.getUint32(dirEndOffset, littleEndian);
28159 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28161 var tag = dataView.getUint16(offset, littleEndian);
28163 this.exif[tag] = this.getExifValue(
28167 dataView.getUint16(offset + 2, littleEndian), // tag type
28168 dataView.getUint32(offset + 4, littleEndian), // tag length
28173 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28175 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28184 Roo.log('Invalid Exif data: Invalid tag type.');
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.');
28197 if (length === 1) {
28198 return tagType.getValue(dataView, dataOffset, littleEndian);
28201 for (i = 0; i < length; i += 1) {
28202 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28205 if (tagType.ascii) {
28207 // Concatenate the chars:
28208 for (i = 0; i < values.length; i += 1) {
28210 // Ignore the terminating NULL byte(s):
28211 if (c === '\u0000') {
28223 Roo.apply(Roo.bootstrap.UploadCropbox, {
28225 'Orientation': 0x0112
28229 1: 0, //'top-left',
28231 3: 180, //'bottom-right',
28232 // 4: 'bottom-left',
28234 6: 90, //'right-top',
28235 // 7: 'right-bottom',
28236 8: 270 //'left-bottom'
28240 // byte, 8-bit unsigned int:
28242 getValue: function (dataView, dataOffset) {
28243 return dataView.getUint8(dataOffset);
28247 // ascii, 8-bit byte:
28249 getValue: function (dataView, dataOffset) {
28250 return String.fromCharCode(dataView.getUint8(dataOffset));
28255 // short, 16 bit int:
28257 getValue: function (dataView, dataOffset, littleEndian) {
28258 return dataView.getUint16(dataOffset, littleEndian);
28262 // long, 32 bit int:
28264 getValue: function (dataView, dataOffset, littleEndian) {
28265 return dataView.getUint32(dataOffset, littleEndian);
28269 // rational = two long values, first is numerator, second is denominator:
28271 getValue: function (dataView, dataOffset, littleEndian) {
28272 return dataView.getUint32(dataOffset, littleEndian) /
28273 dataView.getUint32(dataOffset + 4, littleEndian);
28277 // slong, 32 bit signed int:
28279 getValue: function (dataView, dataOffset, littleEndian) {
28280 return dataView.getInt32(dataOffset, littleEndian);
28284 // srational, two slongs, first is numerator, second is denominator:
28286 getValue: function (dataView, dataOffset, littleEndian) {
28287 return dataView.getInt32(dataOffset, littleEndian) /
28288 dataView.getInt32(dataOffset + 4, littleEndian);
28298 cls : 'btn-group roo-upload-cropbox-rotate-left',
28299 action : 'rotate-left',
28303 cls : 'btn btn-default',
28304 html : '<i class="fa fa-undo"></i>'
28310 cls : 'btn-group roo-upload-cropbox-picture',
28311 action : 'picture',
28315 cls : 'btn btn-default',
28316 html : '<i class="fa fa-picture-o"></i>'
28322 cls : 'btn-group roo-upload-cropbox-rotate-right',
28323 action : 'rotate-right',
28327 cls : 'btn btn-default',
28328 html : '<i class="fa fa-repeat"></i>'
28336 cls : 'btn-group roo-upload-cropbox-rotate-left',
28337 action : 'rotate-left',
28341 cls : 'btn btn-default',
28342 html : '<i class="fa fa-undo"></i>'
28348 cls : 'btn-group roo-upload-cropbox-download',
28349 action : 'download',
28353 cls : 'btn btn-default',
28354 html : '<i class="fa fa-download"></i>'
28360 cls : 'btn-group roo-upload-cropbox-crop',
28365 cls : 'btn btn-default',
28366 html : '<i class="fa fa-crop"></i>'
28372 cls : 'btn-group roo-upload-cropbox-trash',
28377 cls : 'btn btn-default',
28378 html : '<i class="fa fa-trash"></i>'
28384 cls : 'btn-group roo-upload-cropbox-rotate-right',
28385 action : 'rotate-right',
28389 cls : 'btn btn-default',
28390 html : '<i class="fa fa-repeat"></i>'
28398 cls : 'btn-group roo-upload-cropbox-rotate-left',
28399 action : 'rotate-left',
28403 cls : 'btn btn-default',
28404 html : '<i class="fa fa-undo"></i>'
28410 cls : 'btn-group roo-upload-cropbox-rotate-right',
28411 action : 'rotate-right',
28415 cls : 'btn btn-default',
28416 html : '<i class="fa fa-repeat"></i>'
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)
28449 * Create a new DocumentManager
28450 * @param {Object} config The config object
28453 Roo.bootstrap.DocumentManager = function(config){
28454 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28457 this.delegates = [];
28462 * Fire when initial the DocumentManager
28463 * @param {Roo.bootstrap.DocumentManager} this
28468 * inspect selected file
28469 * @param {Roo.bootstrap.DocumentManager} this
28470 * @param {File} file
28475 * Fire when xhr load exception
28476 * @param {Roo.bootstrap.DocumentManager} this
28477 * @param {XMLHttpRequest} xhr
28479 "exception" : true,
28481 * @event afterupload
28482 * Fire when xhr load exception
28483 * @param {Roo.bootstrap.DocumentManager} this
28484 * @param {XMLHttpRequest} xhr
28486 "afterupload" : true,
28489 * prepare the form data
28490 * @param {Roo.bootstrap.DocumentManager} this
28491 * @param {Object} formData
28496 * Fire when remove the file
28497 * @param {Roo.bootstrap.DocumentManager} this
28498 * @param {Object} file
28503 * Fire after refresh the file
28504 * @param {Roo.bootstrap.DocumentManager} this
28509 * Fire after click the image
28510 * @param {Roo.bootstrap.DocumentManager} this
28511 * @param {Object} file
28516 * Fire when upload a image and editable set to true
28517 * @param {Roo.bootstrap.DocumentManager} this
28518 * @param {Object} file
28522 * @event beforeselectfile
28523 * Fire before select file
28524 * @param {Roo.bootstrap.DocumentManager} this
28526 "beforeselectfile" : true,
28529 * Fire before process file
28530 * @param {Roo.bootstrap.DocumentManager} this
28531 * @param {Object} file
28535 * @event previewrendered
28536 * Fire when preview rendered
28537 * @param {Roo.bootstrap.DocumentManager} this
28538 * @param {Object} file
28540 "previewrendered" : true
28545 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28554 paramName : 'imageUpload',
28555 toolTipName : 'filename',
28558 labelAlign : 'left',
28568 getAutoCreate : function()
28570 var managerWidget = {
28572 cls : 'roo-document-manager',
28576 cls : 'roo-document-manager-selector',
28581 cls : 'roo-document-manager-uploader',
28585 cls : 'roo-document-manager-upload-btn',
28586 html : '<i class="fa fa-plus"></i>'
28597 cls : 'column col-md-12',
28602 if(this.fieldLabel.length){
28607 cls : 'column col-md-12',
28608 html : this.fieldLabel
28612 cls : 'column col-md-12',
28617 if(this.labelAlign == 'left'){
28622 html : this.fieldLabel
28631 if(this.labelWidth > 12){
28632 content[0].style = "width: " + this.labelWidth + 'px';
28635 if(this.labelWidth < 13 && this.labelmd == 0){
28636 this.labelmd = this.labelWidth;
28639 if(this.labellg > 0){
28640 content[0].cls += ' col-lg-' + this.labellg;
28641 content[1].cls += ' col-lg-' + (12 - this.labellg);
28644 if(this.labelmd > 0){
28645 content[0].cls += ' col-md-' + this.labelmd;
28646 content[1].cls += ' col-md-' + (12 - this.labelmd);
28649 if(this.labelsm > 0){
28650 content[0].cls += ' col-sm-' + this.labelsm;
28651 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28654 if(this.labelxs > 0){
28655 content[0].cls += ' col-xs-' + this.labelxs;
28656 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28664 cls : 'row clearfix',
28672 initEvents : function()
28674 this.managerEl = this.el.select('.roo-document-manager', true).first();
28675 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28677 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28678 this.selectorEl.hide();
28681 this.selectorEl.attr('multiple', 'multiple');
28684 this.selectorEl.on('change', this.onFileSelected, this);
28686 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28687 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28689 this.uploader.on('click', this.onUploaderClick, this);
28691 this.renderProgressDialog();
28695 window.addEventListener("resize", function() { _this.refresh(); } );
28697 this.fireEvent('initial', this);
28700 renderProgressDialog : function()
28704 this.progressDialog = new Roo.bootstrap.Modal({
28705 cls : 'roo-document-manager-progress-dialog',
28706 allow_close : false,
28716 btnclick : function() {
28717 _this.uploadCancel();
28723 this.progressDialog.render(Roo.get(document.body));
28725 this.progress = new Roo.bootstrap.Progress({
28726 cls : 'roo-document-manager-progress',
28731 this.progress.render(this.progressDialog.getChildContainer());
28733 this.progressBar = new Roo.bootstrap.ProgressBar({
28734 cls : 'roo-document-manager-progress-bar',
28737 aria_valuemax : 12,
28741 this.progressBar.render(this.progress.getChildContainer());
28744 onUploaderClick : function(e)
28746 e.preventDefault();
28748 if(this.fireEvent('beforeselectfile', this) != false){
28749 this.selectorEl.dom.click();
28754 onFileSelected : function(e)
28756 e.preventDefault();
28758 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28762 Roo.each(this.selectorEl.dom.files, function(file){
28763 if(this.fireEvent('inspect', this, file) != false){
28764 this.files.push(file);
28774 this.selectorEl.dom.value = '';
28776 if(!this.files || !this.files.length){
28780 if(this.boxes > 0 && this.files.length > this.boxes){
28781 this.files = this.files.slice(0, this.boxes);
28784 this.uploader.show();
28786 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28787 this.uploader.hide();
28796 Roo.each(this.files, function(file){
28798 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28799 var f = this.renderPreview(file);
28804 if(file.type.indexOf('image') != -1){
28805 this.delegates.push(
28807 _this.process(file);
28808 }).createDelegate(this)
28816 _this.process(file);
28817 }).createDelegate(this)
28822 this.files = files;
28824 this.delegates = this.delegates.concat(docs);
28826 if(!this.delegates.length){
28831 this.progressBar.aria_valuemax = this.delegates.length;
28838 arrange : function()
28840 if(!this.delegates.length){
28841 this.progressDialog.hide();
28846 var delegate = this.delegates.shift();
28848 this.progressDialog.show();
28850 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28852 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28857 refresh : function()
28859 this.uploader.show();
28861 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28862 this.uploader.hide();
28865 Roo.isTouch ? this.closable(false) : this.closable(true);
28867 this.fireEvent('refresh', this);
28870 onRemove : function(e, el, o)
28872 e.preventDefault();
28874 this.fireEvent('remove', this, o);
28878 remove : function(o)
28882 Roo.each(this.files, function(file){
28883 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28892 this.files = files;
28899 Roo.each(this.files, function(file){
28904 file.target.remove();
28913 onClick : function(e, el, o)
28915 e.preventDefault();
28917 this.fireEvent('click', this, o);
28921 closable : function(closable)
28923 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28925 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28937 xhrOnLoad : function(xhr)
28939 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28943 if (xhr.readyState !== 4) {
28945 this.fireEvent('exception', this, xhr);
28949 var response = Roo.decode(xhr.responseText);
28951 if(!response.success){
28953 this.fireEvent('exception', this, xhr);
28957 var file = this.renderPreview(response.data);
28959 this.files.push(file);
28963 this.fireEvent('afterupload', this, xhr);
28967 xhrOnError : function(xhr)
28969 Roo.log('xhr on error');
28971 var response = Roo.decode(xhr.responseText);
28978 process : function(file)
28980 if(this.fireEvent('process', this, file) !== false){
28981 if(this.editable && file.type.indexOf('image') != -1){
28982 this.fireEvent('edit', this, file);
28986 this.uploadStart(file, false);
28993 uploadStart : function(file, crop)
28995 this.xhr = new XMLHttpRequest();
28997 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29002 file.xhr = this.xhr;
29004 this.managerEl.createChild({
29006 cls : 'roo-document-manager-loading',
29010 tooltip : file.name,
29011 cls : 'roo-document-manager-thumb',
29012 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29018 this.xhr.open(this.method, this.url, true);
29021 "Accept": "application/json",
29022 "Cache-Control": "no-cache",
29023 "X-Requested-With": "XMLHttpRequest"
29026 for (var headerName in headers) {
29027 var headerValue = headers[headerName];
29029 this.xhr.setRequestHeader(headerName, headerValue);
29035 this.xhr.onload = function()
29037 _this.xhrOnLoad(_this.xhr);
29040 this.xhr.onerror = function()
29042 _this.xhrOnError(_this.xhr);
29045 var formData = new FormData();
29047 formData.append('returnHTML', 'NO');
29050 formData.append('crop', crop);
29053 formData.append(this.paramName, file, file.name);
29060 if(this.fireEvent('prepare', this, formData, options) != false){
29062 if(options.manually){
29066 this.xhr.send(formData);
29070 this.uploadCancel();
29073 uploadCancel : function()
29079 this.delegates = [];
29081 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29088 renderPreview : function(file)
29090 if(typeof(file.target) != 'undefined' && file.target){
29094 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29096 var previewEl = this.managerEl.createChild({
29098 cls : 'roo-document-manager-preview',
29102 tooltip : file[this.toolTipName],
29103 cls : 'roo-document-manager-thumb',
29104 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29109 html : '<i class="fa fa-times-circle"></i>'
29114 var close = previewEl.select('button.close', true).first();
29116 close.on('click', this.onRemove, this, file);
29118 file.target = previewEl;
29120 var image = previewEl.select('img', true).first();
29124 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29126 image.on('click', this.onClick, this, file);
29128 this.fireEvent('previewrendered', this, file);
29134 onPreviewLoad : function(file, image)
29136 if(typeof(file.target) == 'undefined' || !file.target){
29140 var width = image.dom.naturalWidth || image.dom.width;
29141 var height = image.dom.naturalHeight || image.dom.height;
29143 if(width > height){
29144 file.target.addClass('wide');
29148 file.target.addClass('tall');
29153 uploadFromSource : function(file, crop)
29155 this.xhr = new XMLHttpRequest();
29157 this.managerEl.createChild({
29159 cls : 'roo-document-manager-loading',
29163 tooltip : file.name,
29164 cls : 'roo-document-manager-thumb',
29165 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29171 this.xhr.open(this.method, this.url, true);
29174 "Accept": "application/json",
29175 "Cache-Control": "no-cache",
29176 "X-Requested-With": "XMLHttpRequest"
29179 for (var headerName in headers) {
29180 var headerValue = headers[headerName];
29182 this.xhr.setRequestHeader(headerName, headerValue);
29188 this.xhr.onload = function()
29190 _this.xhrOnLoad(_this.xhr);
29193 this.xhr.onerror = function()
29195 _this.xhrOnError(_this.xhr);
29198 var formData = new FormData();
29200 formData.append('returnHTML', 'NO');
29202 formData.append('crop', crop);
29204 if(typeof(file.filename) != 'undefined'){
29205 formData.append('filename', file.filename);
29208 if(typeof(file.mimetype) != 'undefined'){
29209 formData.append('mimetype', file.mimetype);
29214 if(this.fireEvent('prepare', this, formData) != false){
29215 this.xhr.send(formData);
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)
29232 * Create a new DocumentViewer
29233 * @param {Object} config The config object
29236 Roo.bootstrap.DocumentViewer = function(config){
29237 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29242 * Fire after initEvent
29243 * @param {Roo.bootstrap.DocumentViewer} this
29249 * @param {Roo.bootstrap.DocumentViewer} this
29254 * Fire after download button
29255 * @param {Roo.bootstrap.DocumentViewer} this
29260 * Fire after trash button
29261 * @param {Roo.bootstrap.DocumentViewer} this
29268 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29270 showDownload : true,
29274 getAutoCreate : function()
29278 cls : 'roo-document-viewer',
29282 cls : 'roo-document-viewer-body',
29286 cls : 'roo-document-viewer-thumb',
29290 cls : 'roo-document-viewer-image'
29298 cls : 'roo-document-viewer-footer',
29301 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29305 cls : 'btn-group roo-document-viewer-download',
29309 cls : 'btn btn-default',
29310 html : '<i class="fa fa-download"></i>'
29316 cls : 'btn-group roo-document-viewer-trash',
29320 cls : 'btn btn-default',
29321 html : '<i class="fa fa-trash"></i>'
29334 initEvents : function()
29336 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29337 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29339 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29340 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29342 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29343 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29345 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29346 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29348 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29349 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29351 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29352 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29354 this.bodyEl.on('click', this.onClick, this);
29355 this.downloadBtn.on('click', this.onDownload, this);
29356 this.trashBtn.on('click', this.onTrash, this);
29358 this.downloadBtn.hide();
29359 this.trashBtn.hide();
29361 if(this.showDownload){
29362 this.downloadBtn.show();
29365 if(this.showTrash){
29366 this.trashBtn.show();
29369 if(!this.showDownload && !this.showTrash) {
29370 this.footerEl.hide();
29375 initial : function()
29377 this.fireEvent('initial', this);
29381 onClick : function(e)
29383 e.preventDefault();
29385 this.fireEvent('click', this);
29388 onDownload : function(e)
29390 e.preventDefault();
29392 this.fireEvent('download', this);
29395 onTrash : function(e)
29397 e.preventDefault();
29399 this.fireEvent('trash', this);
29411 * @class Roo.bootstrap.NavProgressBar
29412 * @extends Roo.bootstrap.Component
29413 * Bootstrap NavProgressBar class
29416 * Create a new nav progress bar
29417 * @param {Object} config The config object
29420 Roo.bootstrap.NavProgressBar = function(config){
29421 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29423 this.bullets = this.bullets || [];
29425 // Roo.bootstrap.NavProgressBar.register(this);
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
29439 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29444 getAutoCreate : function()
29446 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29450 cls : 'roo-navigation-bar-group',
29454 cls : 'roo-navigation-top-bar'
29458 cls : 'roo-navigation-bullets-bar',
29462 cls : 'roo-navigation-bar'
29469 cls : 'roo-navigation-bottom-bar'
29479 initEvents: function()
29484 onRender : function(ct, position)
29486 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29488 if(this.bullets.length){
29489 Roo.each(this.bullets, function(b){
29498 addItem : function(cfg)
29500 var item = new Roo.bootstrap.NavProgressItem(cfg);
29502 item.parentId = this.id;
29503 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29506 var top = new Roo.bootstrap.Element({
29508 cls : 'roo-navigation-bar-text'
29511 var bottom = new Roo.bootstrap.Element({
29513 cls : 'roo-navigation-bar-text'
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);
29519 var topText = new Roo.bootstrap.Element({
29521 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29524 var bottomText = new Roo.bootstrap.Element({
29526 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29529 topText.onRender(top.el, null);
29530 bottomText.onRender(bottom.el, null);
29533 item.bottomEl = bottom;
29536 this.barItems.push(item);
29541 getActive : function()
29543 var active = false;
29545 Roo.each(this.barItems, function(v){
29547 if (!v.isActive()) {
29559 setActiveItem : function(item)
29563 Roo.each(this.barItems, function(v){
29564 if (v.rid == item.rid) {
29568 if (v.isActive()) {
29569 v.setActive(false);
29574 item.setActive(true);
29576 this.fireEvent('changed', this, item, prev);
29579 getBarItem: function(rid)
29583 Roo.each(this.barItems, function(e) {
29584 if (e.rid != rid) {
29595 indexOfItem : function(item)
29599 Roo.each(this.barItems, function(v, i){
29601 if (v.rid != item.rid) {
29612 setActiveNext : function()
29614 var i = this.indexOfItem(this.getActive());
29616 if (i > this.barItems.length) {
29620 this.setActiveItem(this.barItems[i+1]);
29623 setActivePrev : function()
29625 var i = this.indexOfItem(this.getActive());
29631 this.setActiveItem(this.barItems[i-1]);
29634 format : function()
29636 if(!this.barItems.length){
29640 var width = 100 / this.barItems.length;
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 + '%');
29654 * Nav Progress Item
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
29670 * Create a new NavProgressItem
29671 * @param {Object} config The config object
29673 Roo.bootstrap.NavProgressItem = function(config){
29674 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29679 * The raw click event for the entire grid.
29680 * @param {Roo.bootstrap.NavProgressItem} this
29681 * @param {Roo.EventObject} e
29688 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29694 position : 'bottom',
29697 getAutoCreate : function()
29699 var iconCls = 'roo-navigation-bar-item-icon';
29701 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29705 cls: 'roo-navigation-bar-item',
29715 cfg.cls += ' active';
29718 cfg.cls += ' disabled';
29724 disable : function()
29726 this.setDisabled(true);
29729 enable : function()
29731 this.setDisabled(false);
29734 initEvents: function()
29736 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29738 this.iconEl.on('click', this.onClick, this);
29741 onClick : function(e)
29743 e.preventDefault();
29749 if(this.fireEvent('click', this, e) === false){
29753 this.parent().setActiveItem(this);
29756 isActive: function ()
29758 return this.active;
29761 setActive : function(state)
29763 if(this.active == state){
29767 this.active = state;
29770 this.el.addClass('active');
29774 this.el.removeClass('active');
29779 setDisabled : function(state)
29781 if(this.disabled == state){
29785 this.disabled = state;
29788 this.el.addClass('disabled');
29792 this.el.removeClass('disabled');
29795 tooltipEl : function()
29797 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
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
29824 * Create a new FieldLabel
29825 * @param {Object} config The config object
29828 Roo.bootstrap.FieldLabel = function(config){
29829 Roo.bootstrap.Element.superclass.constructor.call(this, config);
29834 * Fires after the field has been marked as invalid.
29835 * @param {Roo.form.FieldLabel} this
29836 * @param {String} msg The validation message
29841 * Fires after the field has been validated with no errors.
29842 * @param {Roo.form.FieldLabel} this
29848 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
29855 invalidClass : 'has-warning',
29856 validClass : 'has-success',
29857 iconTooltip : 'This field is required',
29858 indicatorpos : 'left',
29860 getAutoCreate : function(){
29864 cls : 'roo-bootstrap-field-label ' + this.cls,
29869 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
29870 tooltip : this.iconTooltip
29879 if(this.indicatorpos == 'right'){
29882 cls : 'roo-bootstrap-field-label ' + this.cls,
29891 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
29892 tooltip : this.iconTooltip
29901 initEvents: function()
29903 Roo.bootstrap.Element.superclass.initEvents.call(this);
29905 this.indicator = this.indicatorEl();
29907 if(this.indicator){
29908 this.indicator.removeClass('visible');
29909 this.indicator.addClass('invisible');
29912 Roo.bootstrap.FieldLabel.register(this);
29915 indicatorEl : function()
29917 var indicator = this.el.select('i.roo-required-indicator',true).first();
29928 * Mark this field as valid
29930 markValid : function()
29932 if(this.indicator){
29933 this.indicator.removeClass('visible');
29934 this.indicator.addClass('invisible');
29937 this.el.removeClass(this.invalidClass);
29939 this.el.addClass(this.validClass);
29941 this.fireEvent('valid', this);
29945 * Mark this field as invalid
29946 * @param {String} msg The validation message
29948 markInvalid : function(msg)
29950 if(this.indicator){
29951 this.indicator.removeClass('invisible');
29952 this.indicator.addClass('visible');
29955 this.el.removeClass(this.validClass);
29957 this.el.addClass(this.invalidClass);
29959 this.fireEvent('invalid', this, msg);
29965 Roo.apply(Roo.bootstrap.FieldLabel, {
29970 * register a FieldLabel Group
29971 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29973 register : function(label)
29975 if(this.groups.hasOwnProperty(label.target)){
29979 this.groups[label.target] = label;
29983 * fetch a FieldLabel Group based on the target
29984 * @param {string} target
29985 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29987 get: function(target) {
29988 if (typeof(this.groups[target]) == 'undefined') {
29992 return this.groups[target] ;
30001 * page DateSplitField.
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)
30029 * Create a new DateSplitField
30030 * @param {Object} config The config object
30033 Roo.bootstrap.DateSplitField = function(config){
30034 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30040 * getting the data of years
30041 * @param {Roo.bootstrap.DateSplitField} this
30042 * @param {Object} years
30047 * getting the data of days
30048 * @param {Roo.bootstrap.DateSplitField} this
30049 * @param {Object} days
30054 * Fires after the field has been marked as invalid.
30055 * @param {Roo.form.Field} this
30056 * @param {String} msg The validation message
30061 * Fires after the field has been validated with no errors.
30062 * @param {Roo.form.Field} this
30068 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30071 labelAlign : 'top',
30073 dayAllowBlank : false,
30074 monthAllowBlank : false,
30075 yearAllowBlank : false,
30076 dayPlaceholder : '',
30077 monthPlaceholder : '',
30078 yearPlaceholder : '',
30082 isFormField : true,
30088 getAutoCreate : function()
30092 cls : 'row roo-date-split-field-group',
30097 cls : 'form-hidden-field roo-date-split-field-group-value',
30103 var labelCls = 'col-md-12';
30104 var contentCls = 'col-md-4';
30106 if(this.fieldLabel){
30110 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30114 html : this.fieldLabel
30119 if(this.labelAlign == 'left'){
30121 if(this.labelWidth > 12){
30122 label.style = "width: " + this.labelWidth + 'px';
30125 if(this.labelWidth < 13 && this.labelmd == 0){
30126 this.labelmd = this.labelWidth;
30129 if(this.labellg > 0){
30130 labelCls = ' col-lg-' + this.labellg;
30131 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30134 if(this.labelmd > 0){
30135 labelCls = ' col-md-' + this.labelmd;
30136 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30139 if(this.labelsm > 0){
30140 labelCls = ' col-sm-' + this.labelsm;
30141 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30144 if(this.labelxs > 0){
30145 labelCls = ' col-xs-' + this.labelxs;
30146 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30150 label.cls += ' ' + labelCls;
30152 cfg.cn.push(label);
30155 Roo.each(['day', 'month', 'year'], function(t){
30158 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30165 inputEl: function ()
30167 return this.el.select('.roo-date-split-field-group-value', true).first();
30170 onRender : function(ct, position)
30174 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30176 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30178 this.dayField = new Roo.bootstrap.ComboBox({
30179 allowBlank : this.dayAllowBlank,
30180 alwaysQuery : true,
30181 displayField : 'value',
30184 forceSelection : true,
30186 placeholder : this.dayPlaceholder,
30187 selectOnFocus : true,
30188 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30189 triggerAction : 'all',
30191 valueField : 'value',
30192 store : new Roo.data.SimpleStore({
30193 data : (function() {
30195 _this.fireEvent('days', _this, days);
30198 fields : [ 'value' ]
30201 select : function (_self, record, index)
30203 _this.setValue(_this.getValue());
30208 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30210 this.monthField = new Roo.bootstrap.MonthField({
30211 after : '<i class=\"fa fa-calendar\"></i>',
30212 allowBlank : this.monthAllowBlank,
30213 placeholder : this.monthPlaceholder,
30216 render : function (_self)
30218 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30219 e.preventDefault();
30223 select : function (_self, oldvalue, newvalue)
30225 _this.setValue(_this.getValue());
30230 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30232 this.yearField = new Roo.bootstrap.ComboBox({
30233 allowBlank : this.yearAllowBlank,
30234 alwaysQuery : true,
30235 displayField : 'value',
30238 forceSelection : true,
30240 placeholder : this.yearPlaceholder,
30241 selectOnFocus : true,
30242 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30243 triggerAction : 'all',
30245 valueField : 'value',
30246 store : new Roo.data.SimpleStore({
30247 data : (function() {
30249 _this.fireEvent('years', _this, years);
30252 fields : [ 'value' ]
30255 select : function (_self, record, index)
30257 _this.setValue(_this.getValue());
30262 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30265 setValue : function(v, format)
30267 this.inputEl.dom.value = v;
30269 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30271 var d = Date.parseDate(v, f);
30278 this.setDay(d.format(this.dayFormat));
30279 this.setMonth(d.format(this.monthFormat));
30280 this.setYear(d.format(this.yearFormat));
30287 setDay : function(v)
30289 this.dayField.setValue(v);
30290 this.inputEl.dom.value = this.getValue();
30295 setMonth : function(v)
30297 this.monthField.setValue(v, true);
30298 this.inputEl.dom.value = this.getValue();
30303 setYear : function(v)
30305 this.yearField.setValue(v);
30306 this.inputEl.dom.value = this.getValue();
30311 getDay : function()
30313 return this.dayField.getValue();
30316 getMonth : function()
30318 return this.monthField.getValue();
30321 getYear : function()
30323 return this.yearField.getValue();
30326 getValue : function()
30328 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30330 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30340 this.inputEl.dom.value = '';
30345 validate : function()
30347 var d = this.dayField.validate();
30348 var m = this.monthField.validate();
30349 var y = this.yearField.validate();
30354 (!this.dayAllowBlank && !d) ||
30355 (!this.monthAllowBlank && !m) ||
30356 (!this.yearAllowBlank && !y)
30361 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30370 this.markInvalid();
30375 markValid : function()
30378 var label = this.el.select('label', true).first();
30379 var icon = this.el.select('i.fa-star', true).first();
30385 this.fireEvent('valid', this);
30389 * Mark this field as invalid
30390 * @param {String} msg The validation message
30392 markInvalid : function(msg)
30395 var label = this.el.select('label', true).first();
30396 var icon = this.el.select('i.fa-star', true).first();
30398 if(label && !icon){
30399 this.el.select('.roo-date-split-field-label', true).createChild({
30401 cls : 'text-danger fa fa-lg fa-star',
30402 tooltip : 'This field is required',
30403 style : 'margin-right:5px;'
30407 this.fireEvent('invalid', this, msg);
30410 clearInvalid : function()
30412 var label = this.el.select('label', true).first();
30413 var icon = this.el.select('i.fa-star', true).first();
30419 this.fireEvent('valid', this);
30422 getName: function()
30432 * http://masonry.desandro.com
30434 * The idea is to render all the bricks based on vertical width...
30436 * The original code extends 'outlayer' - we might need to use that....
30442 * @class Roo.bootstrap.LayoutMasonry
30443 * @extends Roo.bootstrap.Component
30444 * Bootstrap Layout Masonry class
30447 * Create a new Element
30448 * @param {Object} config The config object
30451 Roo.bootstrap.LayoutMasonry = function(config){
30453 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30457 Roo.bootstrap.LayoutMasonry.register(this);
30463 * Fire after layout the items
30464 * @param {Roo.bootstrap.LayoutMasonry} this
30465 * @param {Roo.EventObject} e
30472 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30475 * @cfg {Boolean} isLayoutInstant = no animation?
30477 isLayoutInstant : false, // needed?
30480 * @cfg {Number} boxWidth width of the columns
30485 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30490 * @cfg {Number} padWidth padding below box..
30495 * @cfg {Number} gutter gutter width..
30500 * @cfg {Number} maxCols maximum number of columns
30506 * @cfg {Boolean} isAutoInitial defalut true
30508 isAutoInitial : true,
30513 * @cfg {Boolean} isHorizontal defalut false
30515 isHorizontal : false,
30517 currentSize : null,
30523 bricks: null, //CompositeElement
30527 _isLayoutInited : false,
30529 // isAlternative : false, // only use for vertical layout...
30532 * @cfg {Number} alternativePadWidth padding below box..
30534 alternativePadWidth : 50,
30536 selectedBrick : [],
30538 getAutoCreate : function(){
30540 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30544 cls: 'blog-masonary-wrapper ' + this.cls,
30546 cls : 'mas-boxes masonary'
30553 getChildContainer: function( )
30555 if (this.boxesEl) {
30556 return this.boxesEl;
30559 this.boxesEl = this.el.select('.mas-boxes').first();
30561 return this.boxesEl;
30565 initEvents : function()
30569 if(this.isAutoInitial){
30570 Roo.log('hook children rendered');
30571 this.on('childrenrendered', function() {
30572 Roo.log('children rendered');
30578 initial : function()
30580 this.selectedBrick = [];
30582 this.currentSize = this.el.getBox(true);
30584 Roo.EventManager.onWindowResize(this.resize, this);
30586 if(!this.isAutoInitial){
30594 //this.layout.defer(500,this);
30598 resize : function()
30600 var cs = this.el.getBox(true);
30603 this.currentSize.width == cs.width &&
30604 this.currentSize.x == cs.x &&
30605 this.currentSize.height == cs.height &&
30606 this.currentSize.y == cs.y
30608 Roo.log("no change in with or X or Y");
30612 this.currentSize = cs;
30618 layout : function()
30620 this._resetLayout();
30622 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30624 this.layoutItems( isInstant );
30626 this._isLayoutInited = true;
30628 this.fireEvent('layout', this);
30632 _resetLayout : function()
30634 if(this.isHorizontal){
30635 this.horizontalMeasureColumns();
30639 this.verticalMeasureColumns();
30643 verticalMeasureColumns : function()
30645 this.getContainerWidth();
30647 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30648 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30652 var boxWidth = this.boxWidth + this.padWidth;
30654 if(this.containerWidth < this.boxWidth){
30655 boxWidth = this.containerWidth
30658 var containerWidth = this.containerWidth;
30660 var cols = Math.floor(containerWidth / boxWidth);
30662 this.cols = Math.max( cols, 1 );
30664 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30666 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30668 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30670 this.colWidth = boxWidth + avail - this.padWidth;
30672 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30673 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30676 horizontalMeasureColumns : function()
30678 this.getContainerWidth();
30680 var boxWidth = this.boxWidth;
30682 if(this.containerWidth < boxWidth){
30683 boxWidth = this.containerWidth;
30686 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30688 this.el.setHeight(boxWidth);
30692 getContainerWidth : function()
30694 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30697 layoutItems : function( isInstant )
30699 Roo.log(this.bricks);
30701 var items = Roo.apply([], this.bricks);
30703 if(this.isHorizontal){
30704 this._horizontalLayoutItems( items , isInstant );
30708 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30709 // this._verticalAlternativeLayoutItems( items , isInstant );
30713 this._verticalLayoutItems( items , isInstant );
30717 _verticalLayoutItems : function ( items , isInstant)
30719 if ( !items || !items.length ) {
30724 ['xs', 'xs', 'xs', 'tall'],
30725 ['xs', 'xs', 'tall'],
30726 ['xs', 'xs', 'sm'],
30727 ['xs', 'xs', 'xs'],
30733 ['sm', 'xs', 'xs'],
30737 ['tall', 'xs', 'xs', 'xs'],
30738 ['tall', 'xs', 'xs'],
30750 Roo.each(items, function(item, k){
30752 switch (item.size) {
30753 // these layouts take up a full box,
30764 boxes.push([item]);
30787 var filterPattern = function(box, length)
30795 var pattern = box.slice(0, length);
30799 Roo.each(pattern, function(i){
30800 format.push(i.size);
30803 Roo.each(standard, function(s){
30805 if(String(s) != String(format)){
30814 if(!match && length == 1){
30819 filterPattern(box, length - 1);
30823 queue.push(pattern);
30825 box = box.slice(length, box.length);
30827 filterPattern(box, 4);
30833 Roo.each(boxes, function(box, k){
30839 if(box.length == 1){
30844 filterPattern(box, 4);
30848 this._processVerticalLayoutQueue( queue, isInstant );
30852 // _verticalAlternativeLayoutItems : function( items , isInstant )
30854 // if ( !items || !items.length ) {
30858 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
30862 _horizontalLayoutItems : function ( items , isInstant)
30864 if ( !items || !items.length || items.length < 3) {
30870 var eItems = items.slice(0, 3);
30872 items = items.slice(3, items.length);
30875 ['xs', 'xs', 'xs', 'wide'],
30876 ['xs', 'xs', 'wide'],
30877 ['xs', 'xs', 'sm'],
30878 ['xs', 'xs', 'xs'],
30884 ['sm', 'xs', 'xs'],
30888 ['wide', 'xs', 'xs', 'xs'],
30889 ['wide', 'xs', 'xs'],
30902 Roo.each(items, function(item, k){
30904 switch (item.size) {
30915 boxes.push([item]);
30939 var filterPattern = function(box, length)
30947 var pattern = box.slice(0, length);
30951 Roo.each(pattern, function(i){
30952 format.push(i.size);
30955 Roo.each(standard, function(s){
30957 if(String(s) != String(format)){
30966 if(!match && length == 1){
30971 filterPattern(box, length - 1);
30975 queue.push(pattern);
30977 box = box.slice(length, box.length);
30979 filterPattern(box, 4);
30985 Roo.each(boxes, function(box, k){
30991 if(box.length == 1){
30996 filterPattern(box, 4);
31003 var pos = this.el.getBox(true);
31007 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31009 var hit_end = false;
31011 Roo.each(queue, function(box){
31015 Roo.each(box, function(b){
31017 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31027 Roo.each(box, function(b){
31029 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31032 mx = Math.max(mx, b.x);
31036 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31040 Roo.each(box, function(b){
31042 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31056 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
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
31065 _processVerticalLayoutQueue : function( queue, isInstant )
31067 var pos = this.el.getBox(true);
31072 for (var i = 0; i < this.cols; i++){
31076 Roo.each(queue, function(box, k){
31078 var col = k % this.cols;
31080 Roo.each(box, function(b,kk){
31082 b.el.position('absolute');
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'));
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'));
31092 b.el.setWidth(width);
31093 b.el.setHeight(height);
31095 b.el.select('iframe',true).setSize(width,height);
31099 for (var i = 0; i < this.cols; i++){
31101 if(maxY[i] < maxY[col]){
31106 col = Math.min(col, i);
31110 x = pos.x + col * (this.colWidth + this.padWidth);
31114 var positions = [];
31116 switch (box.length){
31118 positions = this.getVerticalOneBoxColPositions(x, y, box);
31121 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31124 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31127 positions = this.getVerticalFourBoxColPositions(x, y, box);
31133 Roo.each(box, function(b,kk){
31135 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31137 var sz = b.el.getSize();
31139 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31147 for (var i = 0; i < this.cols; i++){
31148 mY = Math.max(mY, maxY[i]);
31151 this.el.setHeight(mY - pos.y);
31155 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31157 // var pos = this.el.getBox(true);
31160 // var maxX = pos.right;
31162 // var maxHeight = 0;
31164 // Roo.each(items, function(item, k){
31168 // item.el.position('absolute');
31170 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31172 // item.el.setWidth(width);
31174 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31176 // item.el.setHeight(height);
31179 // item.el.setXY([x, y], isInstant ? false : true);
31181 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31184 // y = y + height + this.alternativePadWidth;
31186 // maxHeight = maxHeight + height + this.alternativePadWidth;
31190 // this.el.setHeight(maxHeight);
31194 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31196 var pos = this.el.getBox(true);
31201 var maxX = pos.right;
31203 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31205 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31207 Roo.each(queue, function(box, k){
31209 Roo.each(box, function(b, kk){
31211 b.el.position('absolute');
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'));
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'));
31221 b.el.setWidth(width);
31222 b.el.setHeight(height);
31230 var positions = [];
31232 switch (box.length){
31234 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31237 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31240 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31243 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31249 Roo.each(box, function(b,kk){
31251 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31253 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31261 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31263 Roo.each(eItems, function(b,k){
31265 b.size = (k == 0) ? 'sm' : 'xs';
31266 b.x = (k == 0) ? 2 : 1;
31267 b.y = (k == 0) ? 2 : 1;
31269 b.el.position('absolute');
31271 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31273 b.el.setWidth(width);
31275 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31277 b.el.setHeight(height);
31281 var positions = [];
31284 x : maxX - this.unitWidth * 2 - this.gutter,
31289 x : maxX - this.unitWidth,
31290 y : minY + (this.unitWidth + this.gutter) * 2
31294 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31298 Roo.each(eItems, function(b,k){
31300 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31306 getVerticalOneBoxColPositions : function(x, y, box)
31310 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31312 if(box[0].size == 'md-left'){
31316 if(box[0].size == 'md-right'){
31321 x : x + (this.unitWidth + this.gutter) * rand,
31328 getVerticalTwoBoxColPositions : function(x, y, box)
31332 if(box[0].size == 'xs'){
31336 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31340 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31354 x : x + (this.unitWidth + this.gutter) * 2,
31355 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31362 getVerticalThreeBoxColPositions : function(x, y, box)
31366 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31374 x : x + (this.unitWidth + this.gutter) * 1,
31379 x : x + (this.unitWidth + this.gutter) * 2,
31387 if(box[0].size == 'xs' && box[1].size == 'xs'){
31396 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31400 x : x + (this.unitWidth + this.gutter) * 1,
31414 x : x + (this.unitWidth + this.gutter) * 2,
31419 x : x + (this.unitWidth + this.gutter) * 2,
31420 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31427 getVerticalFourBoxColPositions : function(x, y, box)
31431 if(box[0].size == 'xs'){
31440 y : y + (this.unitHeight + this.gutter) * 1
31445 y : y + (this.unitHeight + this.gutter) * 2
31449 x : x + (this.unitWidth + this.gutter) * 1,
31463 x : x + (this.unitWidth + this.gutter) * 2,
31468 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31469 y : y + (this.unitHeight + this.gutter) * 1
31473 x : x + (this.unitWidth + this.gutter) * 2,
31474 y : y + (this.unitWidth + this.gutter) * 2
31481 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31485 if(box[0].size == 'md-left'){
31487 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31494 if(box[0].size == 'md-right'){
31496 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31497 y : minY + (this.unitWidth + this.gutter) * 1
31503 var rand = Math.floor(Math.random() * (4 - box[0].y));
31506 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31507 y : minY + (this.unitWidth + this.gutter) * rand
31514 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31518 if(box[0].size == 'xs'){
31521 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
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)
31535 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31540 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31541 y : minY + (this.unitWidth + this.gutter) * 2
31548 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31552 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31555 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31560 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31561 y : minY + (this.unitWidth + this.gutter) * 1
31565 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31566 y : minY + (this.unitWidth + this.gutter) * 2
31573 if(box[0].size == 'xs' && box[1].size == 'xs'){
31576 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
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),
31586 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31587 y : minY + (this.unitWidth + this.gutter) * 1
31595 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31600 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31601 y : minY + (this.unitWidth + this.gutter) * 2
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
31613 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31617 if(box[0].size == 'xs'){
31620 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
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),
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),
31635 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31636 y : minY + (this.unitWidth + this.gutter) * 1
31644 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31649 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31650 y : minY + (this.unitWidth + this.gutter) * 2
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
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
31668 * remove a Masonry Brick
31669 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31671 removeBrick : function(brick_id)
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);
31687 * adds a Masonry Brick
31688 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31690 addBrick : function(cfg)
31692 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31693 //this.register(cn);
31694 cn.parentId = this.id;
31695 cn.onRender(this.el, null);
31700 * register a Masonry Brick
31701 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31704 register : function(brick)
31706 this.bricks.push(brick);
31707 brick.masonryId = this.id;
31711 * clear all the Masonry Brick
31713 clearAll : function()
31716 //this.getChildContainer().dom.innerHTML = "";
31717 this.el.dom.innerHTML = '';
31720 getSelected : function()
31722 if (!this.selectedBrick) {
31726 return this.selectedBrick;
31730 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31734 * register a Masonry Layout
31735 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31738 register : function(layout)
31740 this.groups[layout.id] = layout;
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
31748 get: function(layout_id) {
31749 if (typeof(this.groups[layout_id]) == 'undefined') {
31752 return this.groups[layout_id] ;
31764 * http://masonry.desandro.com
31766 * The idea is to render all the bricks based on vertical width...
31768 * The original code extends 'outlayer' - we might need to use that....
31774 * @class Roo.bootstrap.LayoutMasonryAuto
31775 * @extends Roo.bootstrap.Component
31776 * Bootstrap Layout Masonry class
31779 * Create a new Element
31780 * @param {Object} config The config object
31783 Roo.bootstrap.LayoutMasonryAuto = function(config){
31784 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31787 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
31790 * @cfg {Boolean} isFitWidth - resize the width..
31792 isFitWidth : false, // options..
31794 * @cfg {Boolean} isOriginLeft = left align?
31796 isOriginLeft : true,
31798 * @cfg {Boolean} isOriginTop = top align?
31800 isOriginTop : false,
31802 * @cfg {Boolean} isLayoutInstant = no animation?
31804 isLayoutInstant : false, // needed?
31806 * @cfg {Boolean} isResizingContainer = not sure if this is used..
31808 isResizingContainer : true,
31810 * @cfg {Number} columnWidth width of the columns
31816 * @cfg {Number} maxCols maximum number of columns
31821 * @cfg {Number} padHeight padding below box..
31827 * @cfg {Boolean} isAutoInitial defalut true
31830 isAutoInitial : true,
31836 initialColumnWidth : 0,
31837 currentSize : null,
31839 colYs : null, // array.
31846 bricks: null, //CompositeElement
31847 cols : 0, // array?
31848 // element : null, // wrapped now this.el
31849 _isLayoutInited : null,
31852 getAutoCreate : function(){
31856 cls: 'blog-masonary-wrapper ' + this.cls,
31858 cls : 'mas-boxes masonary'
31865 getChildContainer: function( )
31867 if (this.boxesEl) {
31868 return this.boxesEl;
31871 this.boxesEl = this.el.select('.mas-boxes').first();
31873 return this.boxesEl;
31877 initEvents : function()
31881 if(this.isAutoInitial){
31882 Roo.log('hook children rendered');
31883 this.on('childrenrendered', function() {
31884 Roo.log('children rendered');
31891 initial : function()
31893 this.reloadItems();
31895 this.currentSize = this.el.getBox(true);
31897 /// was window resize... - let's see if this works..
31898 Roo.EventManager.onWindowResize(this.resize, this);
31900 if(!this.isAutoInitial){
31905 this.layout.defer(500,this);
31908 reloadItems: function()
31910 this.bricks = this.el.select('.masonry-brick', true);
31912 this.bricks.each(function(b) {
31913 //Roo.log(b.getSize());
31914 if (!b.attr('originalwidth')) {
31915 b.attr('originalwidth', b.getSize().width);
31920 Roo.log(this.bricks.elements.length);
31923 resize : function()
31926 var cs = this.el.getBox(true);
31928 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31929 Roo.log("no change in with or X");
31932 this.currentSize = cs;
31936 layout : function()
31939 this._resetLayout();
31940 //this._manageStamps();
31942 // don't animate first layout
31943 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31944 this.layoutItems( isInstant );
31946 // flag for initalized
31947 this._isLayoutInited = true;
31950 layoutItems : function( isInstant )
31952 //var items = this._getItemsForLayout( this.items );
31953 // original code supports filtering layout items.. we just ignore it..
31955 this._layoutItems( this.bricks , isInstant );
31957 this._postLayout();
31959 _layoutItems : function ( items , isInstant)
31961 //this.fireEvent( 'layout', this, items );
31964 if ( !items || !items.elements.length ) {
31965 // no items, emit event with empty array
31970 items.each(function(item) {
31971 Roo.log("layout item");
31973 // get x/y object from method
31974 var position = this._getItemLayoutPosition( item );
31976 position.item = item;
31977 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31978 queue.push( position );
31981 this._processLayoutQueue( queue );
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
31989 _processLayoutQueue : function( queue )
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);
32000 * Any logic you want to do after each layout,
32001 * i.e. size the container
32003 _postLayout : function()
32005 this.resizeContainer();
32008 resizeContainer : function()
32010 if ( !this.isResizingContainer ) {
32013 var size = this._getContainerSize();
32015 this.el.setSize(size.width,size.height);
32016 this.boxesEl.setSize(size.width,size.height);
32022 _resetLayout : function()
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();
32028 this.measureColumns();
32034 this.colYs.push( 0 );
32040 measureColumns : function()
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());
32051 // columnWidth fall back to item of first element
32052 Roo.log("set column width?");
32053 this.initialColumnWidth = this.columnWidth ;
32055 // if first elem has no width, default to size of container
32060 if (this.initialColumnWidth) {
32061 this.columnWidth = this.initialColumnWidth;
32066 // column width is fixed at the top - however if container width get's smaller we should
32069 // this bit calcs how man columns..
32071 var columnWidth = this.columnWidth += this.gutter;
32073 // calculate columns
32074 var containerWidth = this.containerWidth + this.gutter;
32076 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32077 // fix rounding errors, typically with gutters
32078 var excess = columnWidth - containerWidth % columnWidth;
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;
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'
32092 var padNeeded = (1+this.cols) * this.padWidth;
32094 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32096 this.columnWidth += padExtra
32097 //this.padWidth = Math.floor(padavail / ( this.cols));
32099 // adjust colum width so that padding is fixed??
32101 // we have 3 columns ... total = width * 3
32102 // we have X left over... that should be used by
32104 //if (this.expandC) {
32112 getContainerWidth : function()
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
32119 var size = getSize( container ); //FIXME
32120 this.containerWidth = size && size.innerWidth; //FIXME
32123 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32127 _getItemLayoutPosition : function( item ) // what is item?
32129 // we resize the item to our columnWidth..
32131 item.setWidth(this.columnWidth);
32132 item.autoBoxAdjust = false;
32134 var sz = item.getSize();
32136 // how many columns does this brick span
32137 var remainder = this.containerWidth % this.columnWidth;
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 );
32144 // normally this should be '1' as we dont' currently allow multi width columns..
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 ]);
32151 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32153 // position the brick
32155 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32156 y: this.currentSize.y + minimumY + this.padHeight
32160 // apply setHeight to necessary columns
32161 var setHeight = minimumY + sz.height + this.padHeight;
32162 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32164 var setSpan = this.cols + 1 - colGroup.length;
32165 for ( var i = 0; i < setSpan; i++ ) {
32166 this.colYs[ shortColIndex + i ] = setHeight ;
32173 * @param {Number} colSpan - number of columns the element spans
32174 * @returns {Array} colGroup
32176 _getColGroup : function( colSpan )
32178 if ( colSpan < 2 ) {
32179 // if brick spans only one column, use all the column Ys
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 );
32196 _manageStamp : function( stamp )
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 );
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 );
32211 // set colYs to bottom of the stamp
32212 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32215 for ( var i = firstCol; i <= lastCol; i++ ) {
32216 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32221 _getContainerSize : function()
32223 this.maxY = Math.max.apply( Math, this.colYs );
32228 if ( this.isFitWidth ) {
32229 size.width = this._getContainerFitWidth();
32235 _getContainerFitWidth : function()
32237 var unusedCols = 0;
32238 // count unused columns
32241 if ( this.colYs[i] !== 0 ) {
32246 // fit container to columns that have been used
32247 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32250 needsResizeLayout : function()
32252 var previousWidth = this.containerWidth;
32253 this.getContainerWidth();
32254 return previousWidth !== this.containerWidth;
32269 * @class Roo.bootstrap.MasonryBrick
32270 * @extends Roo.bootstrap.Component
32271 * Bootstrap MasonryBrick class
32274 * Create a new MasonryBrick
32275 * @param {Object} config The config object
32278 Roo.bootstrap.MasonryBrick = function(config){
32280 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32282 Roo.bootstrap.MasonryBrick.register(this);
32288 * When a MasonryBrick is clcik
32289 * @param {Roo.bootstrap.MasonryBrick} this
32290 * @param {Roo.EventObject} e
32296 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32299 * @cfg {String} title
32303 * @cfg {String} html
32307 * @cfg {String} bgimage
32311 * @cfg {String} videourl
32315 * @cfg {String} cls
32319 * @cfg {String} href
32323 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32328 * @cfg {String} placetitle (center|bottom)
32333 * @cfg {Boolean} isFitContainer defalut true
32335 isFitContainer : true,
32338 * @cfg {Boolean} preventDefault defalut false
32340 preventDefault : false,
32343 * @cfg {Boolean} inverse defalut false
32345 maskInverse : false,
32347 getAutoCreate : function()
32349 if(!this.isFitContainer){
32350 return this.getSplitAutoCreate();
32353 var cls = 'masonry-brick masonry-brick-full';
32355 if(this.href.length){
32356 cls += ' masonry-brick-link';
32359 if(this.bgimage.length){
32360 cls += ' masonry-brick-image';
32363 if(this.maskInverse){
32364 cls += ' mask-inverse';
32367 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32368 cls += ' enable-mask';
32372 cls += ' masonry-' + this.size + '-brick';
32375 if(this.placetitle.length){
32377 switch (this.placetitle) {
32379 cls += ' masonry-center-title';
32382 cls += ' masonry-bottom-title';
32389 if(!this.html.length && !this.bgimage.length){
32390 cls += ' masonry-center-title';
32393 if(!this.html.length && this.bgimage.length){
32394 cls += ' masonry-bottom-title';
32399 cls += ' ' + this.cls;
32403 tag: (this.href.length) ? 'a' : 'div',
32408 cls: 'masonry-brick-mask'
32412 cls: 'masonry-brick-paragraph',
32418 if(this.href.length){
32419 cfg.href = this.href;
32422 var cn = cfg.cn[1].cn;
32424 if(this.title.length){
32427 cls: 'masonry-brick-title',
32432 if(this.html.length){
32435 cls: 'masonry-brick-text',
32440 if (!this.title.length && !this.html.length) {
32441 cfg.cn[1].cls += ' hide';
32444 if(this.bgimage.length){
32447 cls: 'masonry-brick-image-view',
32452 if(this.videourl.length){
32453 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32454 // youtube support only?
32457 cls: 'masonry-brick-image-view',
32460 allowfullscreen : true
32468 getSplitAutoCreate : function()
32470 var cls = 'masonry-brick masonry-brick-split';
32472 if(this.href.length){
32473 cls += ' masonry-brick-link';
32476 if(this.bgimage.length){
32477 cls += ' masonry-brick-image';
32481 cls += ' masonry-' + this.size + '-brick';
32484 switch (this.placetitle) {
32486 cls += ' masonry-center-title';
32489 cls += ' masonry-bottom-title';
32492 if(!this.bgimage.length){
32493 cls += ' masonry-center-title';
32496 if(this.bgimage.length){
32497 cls += ' masonry-bottom-title';
32503 cls += ' ' + this.cls;
32507 tag: (this.href.length) ? 'a' : 'div',
32512 cls: 'masonry-brick-split-head',
32516 cls: 'masonry-brick-paragraph',
32523 cls: 'masonry-brick-split-body',
32529 if(this.href.length){
32530 cfg.href = this.href;
32533 if(this.title.length){
32534 cfg.cn[0].cn[0].cn.push({
32536 cls: 'masonry-brick-title',
32541 if(this.html.length){
32542 cfg.cn[1].cn.push({
32544 cls: 'masonry-brick-text',
32549 if(this.bgimage.length){
32550 cfg.cn[0].cn.push({
32552 cls: 'masonry-brick-image-view',
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({
32562 cls: 'masonry-brick-image-view',
32565 allowfullscreen : true
32572 initEvents: function()
32574 switch (this.size) {
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);
32612 this.el.on('mouseenter' ,this.enter, this);
32613 this.el.on('mouseleave', this.leave, this);
32614 this.el.on('click', this.onClick, this);
32617 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32618 this.parent().bricks.push(this);
32623 onClick: function(e, el)
32625 var time = this.endTimer - this.startTimer;
32626 // Roo.log(e.preventDefault());
32629 e.preventDefault();
32634 if(!this.preventDefault){
32638 e.preventDefault();
32640 if (this.activcClass != '') {
32641 this.selectBrick();
32644 this.fireEvent('click', this);
32647 enter: function(e, el)
32649 e.preventDefault();
32651 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32655 if(this.bgimage.length && this.html.length){
32656 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32660 leave: function(e, el)
32662 e.preventDefault();
32664 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32668 if(this.bgimage.length && this.html.length){
32669 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32673 onTouchStart: function(e, el)
32675 // e.preventDefault();
32677 this.touchmoved = false;
32679 if(!this.isFitContainer){
32683 if(!this.bgimage.length || !this.html.length){
32687 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32689 this.timer = new Date().getTime();
32693 onTouchMove: function(e, el)
32695 this.touchmoved = true;
32698 onContextMenu : function(e,el)
32700 e.preventDefault();
32701 e.stopPropagation();
32705 onTouchEnd: function(e, el)
32707 // e.preventDefault();
32709 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32716 if(!this.bgimage.length || !this.html.length){
32718 if(this.href.length){
32719 window.location.href = this.href;
32725 if(!this.isFitContainer){
32729 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32731 window.location.href = this.href;
32734 //selection on single brick only
32735 selectBrick : function() {
32737 if (!this.parentId) {
32741 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32742 var index = m.selectedBrick.indexOf(this.id);
32745 m.selectedBrick.splice(index,1);
32746 this.el.removeClass(this.activeClass);
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);
32755 m.selectedBrick = [];
32757 m.selectedBrick.push(this.id);
32758 this.el.addClass(this.activeClass);
32764 Roo.apply(Roo.bootstrap.MasonryBrick, {
32767 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32769 * register a Masonry Brick
32770 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32773 register : function(brick)
32775 //this.groups[brick.id] = brick;
32776 this.groups.add(brick.id, brick);
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
32784 get: function(brick_id)
32786 // if (typeof(this.groups[brick_id]) == 'undefined') {
32789 // return this.groups[brick_id] ;
32791 if(this.groups.key(brick_id)) {
32792 return this.groups.key(brick_id);
32810 * @class Roo.bootstrap.Brick
32811 * @extends Roo.bootstrap.Component
32812 * Bootstrap Brick class
32815 * Create a new Brick
32816 * @param {Object} config The config object
32819 Roo.bootstrap.Brick = function(config){
32820 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32826 * When a Brick is click
32827 * @param {Roo.bootstrap.Brick} this
32828 * @param {Roo.EventObject} e
32834 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
32837 * @cfg {String} title
32841 * @cfg {String} html
32845 * @cfg {String} bgimage
32849 * @cfg {String} cls
32853 * @cfg {String} href
32857 * @cfg {String} video
32861 * @cfg {Boolean} square
32865 getAutoCreate : function()
32867 var cls = 'roo-brick';
32869 if(this.href.length){
32870 cls += ' roo-brick-link';
32873 if(this.bgimage.length){
32874 cls += ' roo-brick-image';
32877 if(!this.html.length && !this.bgimage.length){
32878 cls += ' roo-brick-center-title';
32881 if(!this.html.length && this.bgimage.length){
32882 cls += ' roo-brick-bottom-title';
32886 cls += ' ' + this.cls;
32890 tag: (this.href.length) ? 'a' : 'div',
32895 cls: 'roo-brick-paragraph',
32901 if(this.href.length){
32902 cfg.href = this.href;
32905 var cn = cfg.cn[0].cn;
32907 if(this.title.length){
32910 cls: 'roo-brick-title',
32915 if(this.html.length){
32918 cls: 'roo-brick-text',
32925 if(this.bgimage.length){
32928 cls: 'roo-brick-image-view',
32936 initEvents: function()
32938 if(this.title.length || this.html.length){
32939 this.el.on('mouseenter' ,this.enter, this);
32940 this.el.on('mouseleave', this.leave, this);
32943 Roo.EventManager.onWindowResize(this.resize, this);
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);
32954 onImageLoad : function()
32959 resize : function()
32961 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32963 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32965 if(this.bgimage.length){
32966 var image = this.el.select('.roo-brick-image-view', true).first();
32968 image.setWidth(paragraph.getWidth());
32971 image.setHeight(paragraph.getWidth());
32974 this.el.setHeight(image.getHeight());
32975 paragraph.setHeight(image.getHeight());
32981 enter: function(e, el)
32983 e.preventDefault();
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);
32991 leave: function(e, el)
32993 e.preventDefault();
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);
33013 * @class Roo.bootstrap.NumberField
33014 * @extends Roo.bootstrap.Input
33015 * Bootstrap NumberField class
33021 * Create a new NumberField
33022 * @param {Object} config The config object
33025 Roo.bootstrap.NumberField = function(config){
33026 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33029 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33032 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33034 allowDecimals : true,
33036 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33038 decimalSeparator : ".",
33040 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33042 decimalPrecision : 2,
33044 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33046 allowNegative : true,
33048 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33050 minValue : Number.NEGATIVE_INFINITY,
33052 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33054 maxValue : Number.MAX_VALUE,
33056 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33058 minText : "The minimum value for this field is {0}",
33060 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33062 maxText : "The maximum value for this field is {0}",
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")
33067 nanText : "{0} is not a valid number",
33069 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
33073 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33075 thousandsDelimiter : false,
33077 * @cfg {String} valueAlign alignment of value
33079 valueAlign : "left",
33081 getAutoCreate : function()
33083 var hiddenInput = {
33087 cls: 'hidden-number-input'
33091 hiddenInput.name = this.name;
33096 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33098 this.name = hiddenInput.name;
33100 if(cfg.cn.length > 0) {
33101 cfg.cn.push(hiddenInput);
33108 initEvents : function()
33110 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33112 var allowed = "0123456789";
33114 if(this.allowDecimals){
33115 allowed += this.decimalSeparator;
33118 if(this.allowNegative){
33122 if(this.thousandsDelimiter) {
33126 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33128 var keyPress = function(e){
33130 var k = e.getKey();
33132 var c = e.getCharCode();
33135 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33136 allowed.indexOf(String.fromCharCode(c)) === -1
33142 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33146 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33151 this.el.on("keypress", keyPress, this);
33154 validateValue : function(value)
33157 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33161 var num = this.parseValue(value);
33164 this.markInvalid(String.format(this.nanText, value));
33168 if(num < this.minValue){
33169 this.markInvalid(String.format(this.minText, this.minValue));
33173 if(num > this.maxValue){
33174 this.markInvalid(String.format(this.maxText, this.maxValue));
33181 getValue : function()
33183 var v = this.hiddenEl().getValue();
33185 return this.fixPrecision(this.parseValue(v));
33188 parseValue : function(value)
33190 if(this.thousandsDelimiter) {
33192 r = new RegExp(",", "g");
33193 value = value.replace(r, "");
33196 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33197 return isNaN(value) ? '' : value;
33200 fixPrecision : function(value)
33202 if(this.thousandsDelimiter) {
33204 r = new RegExp(",", "g");
33205 value = value.replace(r, "");
33208 var nan = isNaN(value);
33210 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33211 return nan ? '' : value;
33213 return parseFloat(value).toFixed(this.decimalPrecision);
33216 setValue : function(v)
33218 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33224 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33226 this.inputEl().dom.value = Roo.util.Format.number(v, this.decimalPrecision,
33227 this.thousandsDelimiter || ''
33230 if(this.allowBlank && !v) {
33231 this.inputEl().dom.value = '';
33238 decimalPrecisionFcn : function(v)
33240 return Math.floor(v);
33243 beforeBlur : function()
33249 var v = this.parseValue(this.getRawValue());
33258 hiddenEl : function()
33260 return this.el.select('input.hidden-number-input',true).first();
33272 * @class Roo.bootstrap.DocumentSlider
33273 * @extends Roo.bootstrap.Component
33274 * Bootstrap DocumentSlider class
33277 * Create a new DocumentViewer
33278 * @param {Object} config The config object
33281 Roo.bootstrap.DocumentSlider = function(config){
33282 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33289 * Fire after initEvent
33290 * @param {Roo.bootstrap.DocumentSlider} this
33295 * Fire after update
33296 * @param {Roo.bootstrap.DocumentSlider} this
33302 * @param {Roo.bootstrap.DocumentSlider} this
33308 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33314 getAutoCreate : function()
33318 cls : 'roo-document-slider',
33322 cls : 'roo-document-slider-header',
33326 cls : 'roo-document-slider-header-title'
33332 cls : 'roo-document-slider-body',
33336 cls : 'roo-document-slider-prev',
33340 cls : 'fa fa-chevron-left'
33346 cls : 'roo-document-slider-thumb',
33350 cls : 'roo-document-slider-image'
33356 cls : 'roo-document-slider-next',
33360 cls : 'fa fa-chevron-right'
33372 initEvents : function()
33374 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33375 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33377 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33378 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33380 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33381 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33383 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33384 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33386 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33387 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33389 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33390 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33392 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33393 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33395 this.thumbEl.on('click', this.onClick, this);
33397 this.prevIndicator.on('click', this.prev, this);
33399 this.nextIndicator.on('click', this.next, this);
33403 initial : function()
33405 if(this.files.length){
33406 this.indicator = 1;
33410 this.fireEvent('initial', this);
33413 update : function()
33415 this.imageEl.attr('src', this.files[this.indicator - 1]);
33417 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33419 this.prevIndicator.show();
33421 if(this.indicator == 1){
33422 this.prevIndicator.hide();
33425 this.nextIndicator.show();
33427 if(this.indicator == this.files.length){
33428 this.nextIndicator.hide();
33431 this.thumbEl.scrollTo('top');
33433 this.fireEvent('update', this);
33436 onClick : function(e)
33438 e.preventDefault();
33440 this.fireEvent('click', this);
33445 e.preventDefault();
33447 this.indicator = Math.max(1, this.indicator - 1);
33454 e.preventDefault();
33456 this.indicator = Math.min(this.files.length, this.indicator + 1);
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
33477 * Create a new RadioSet
33478 * @param {Object} config The config object
33481 Roo.bootstrap.RadioSet = function(config){
33483 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33487 Roo.bootstrap.RadioSet.register(this);
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
33501 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33509 indicatorpos : 'left',
33511 getAutoCreate : function()
33515 cls : 'roo-radio-set-label',
33519 html : this.fieldLabel
33524 if(this.indicatorpos == 'left'){
33527 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33528 tooltip : 'This field is required'
33533 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33534 tooltip : 'This field is required'
33540 cls : 'roo-radio-set-items'
33543 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33545 if (align === 'left' && this.fieldLabel.length) {
33548 cls : "roo-radio-set-right",
33554 if(this.labelWidth > 12){
33555 label.style = "width: " + this.labelWidth + 'px';
33558 if(this.labelWidth < 13 && this.labelmd == 0){
33559 this.labelmd = this.labelWidth;
33562 if(this.labellg > 0){
33563 label.cls += ' col-lg-' + this.labellg;
33564 items.cls += ' col-lg-' + (12 - this.labellg);
33567 if(this.labelmd > 0){
33568 label.cls += ' col-md-' + this.labelmd;
33569 items.cls += ' col-md-' + (12 - this.labelmd);
33572 if(this.labelsm > 0){
33573 label.cls += ' col-sm-' + this.labelsm;
33574 items.cls += ' col-sm-' + (12 - this.labelsm);
33577 if(this.labelxs > 0){
33578 label.cls += ' col-xs-' + this.labelxs;
33579 items.cls += ' col-xs-' + (12 - this.labelxs);
33585 cls : 'roo-radio-set',
33589 cls : 'roo-radio-set-input',
33592 value : this.value ? this.value : ''
33599 if(this.weight.length){
33600 cfg.cls += ' roo-radio-' + this.weight;
33604 cfg.cls += ' roo-radio-set-inline';
33608 ['xs','sm','md','lg'].map(function(size){
33609 if (settings[size]) {
33610 cfg.cls += ' col-' + size + '-' + settings[size];
33618 initEvents : function()
33620 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33621 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33623 if(!this.fieldLabel.length){
33624 this.labelEl.hide();
33627 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33628 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33630 this.indicatorEl().addClass('invisible');
33632 this.originalValue = this.getValue();
33636 inputEl: function ()
33638 return this.el.select('.roo-radio-set-input', true).first();
33641 getChildContainer : function()
33643 return this.itemsEl;
33646 register : function(item)
33648 this.radioes.push(item);
33652 validate : function()
33656 Roo.each(this.radioes, function(i){
33665 if(this.allowBlank) {
33669 if(this.disabled || valid){
33674 this.markInvalid();
33679 markValid : function()
33681 if(this.labelEl.isVisible(true)){
33682 this.indicatorEl().removeClass('visible');
33683 this.indicatorEl().addClass('invisible');
33686 this.el.removeClass([this.invalidClass, this.validClass]);
33687 this.el.addClass(this.validClass);
33689 this.fireEvent('valid', this);
33692 markInvalid : function(msg)
33694 if(this.allowBlank || this.disabled){
33698 if(this.labelEl.isVisible(true)){
33699 this.indicatorEl().removeClass('invisible');
33700 this.indicatorEl().addClass('visible');
33703 this.el.removeClass([this.invalidClass, this.validClass]);
33704 this.el.addClass(this.invalidClass);
33706 this.fireEvent('invalid', this, msg);
33710 setValue : function(v, suppressEvent)
33712 if(this.value === v){
33719 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33722 Roo.each(this.radioes, function(i){
33724 i.el.removeClass('checked');
33727 Roo.each(this.radioes, function(i){
33729 if(i.value === v || i.value.toString() === v.toString()){
33731 i.el.addClass('checked');
33733 if(suppressEvent !== true){
33734 this.fireEvent('check', this, i);
33745 clearInvalid : function(){
33747 if(!this.el || this.preventMark){
33751 this.el.removeClass([this.invalidClass]);
33753 this.fireEvent('valid', this);
33758 Roo.apply(Roo.bootstrap.RadioSet, {
33762 register : function(set)
33764 this.groups[set.name] = set;
33767 get: function(name)
33769 if (typeof(this.groups[name]) == 'undefined') {
33773 return this.groups[name] ;
33779 * Ext JS Library 1.1.1
33780 * Copyright(c) 2006-2007, Ext JS, LLC.
33782 * Originally Released Under LGPL - original licence link has changed is not relivant.
33785 * <script type="text/javascript">
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).
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);
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).
33813 Roo.bootstrap.SplitBar = function(cfg){
33818 // dragElement : elm
33819 // resizingElement: el,
33821 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33822 // placement : Roo.bootstrap.SplitBar.LEFT ,
33823 // existingProxy ???
33826 this.el = Roo.get(cfg.dragElement, true);
33827 this.el.dom.unselectable = "on";
33829 this.resizingEl = Roo.get(cfg.resizingElement, true);
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
33837 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33840 * The minimum size of the resizing element. (Defaults to 0)
33846 * The maximum size of the resizing element. (Defaults to 2000)
33849 this.maxSize = 2000;
33852 * Whether to animate the transition to the new size
33855 this.animate = false;
33858 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33861 this.useShim = false;
33866 if(!cfg.existingProxy){
33868 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33870 this.proxy = Roo.get(cfg.existingProxy).dom;
33873 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33876 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33879 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33882 this.dragSpecs = {};
33885 * @private The adapter to use to positon and resize elements
33887 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33888 this.adapter.init(this);
33890 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
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");
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");
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
33910 * Fires when the splitter is moved
33911 * @param {Roo.bootstrap.SplitBar} this
33912 * @param {Number} newSize the new width or height
33916 * @event beforeresize
33917 * Fires before the splitter is dragged
33918 * @param {Roo.bootstrap.SplitBar} this
33920 "beforeresize" : true,
33922 "beforeapply" : true
33925 Roo.util.Observable.call(this);
33928 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33929 onStartProxyDrag : function(x, y){
33930 this.fireEvent("beforeresize", this);
33932 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
33934 o.enableDisplayMode("block");
33935 // all splitbars share the same overlay
33936 Roo.bootstrap.SplitBar.prototype.overlay = o;
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
33952 this.dd.setYConstraint(0, 0);
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
33961 this.dragSpecs.startSize = size;
33962 this.dragSpecs.startPoint = [x, y];
33963 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33967 * @private Called after the drag operation by the DDProxy
33969 onEndProxyDrag : function(e){
33970 Roo.get(this.proxy).setDisplayed(false);
33971 var endPoint = Roo.lib.Event.getXY(e);
33973 this.overlay.hide();
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]
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]
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);
34000 * Get the adapter this SplitBar uses
34001 * @return The adapter object
34003 getAdapter : function(){
34004 return this.adapter;
34008 * Set the adapter this SplitBar uses
34009 * @param {Object} adapter A SplitBar adapter object
34011 setAdapter : function(adapter){
34012 this.adapter = adapter;
34013 this.adapter.init(this);
34017 * Gets the minimum size for the resizing element
34018 * @return {Number} The minimum size
34020 getMinimumSize : function(){
34021 return this.minSize;
34025 * Sets the minimum size for the resizing element
34026 * @param {Number} minSize The minimum size
34028 setMinimumSize : function(minSize){
34029 this.minSize = minSize;
34033 * Gets the maximum size for the resizing element
34034 * @return {Number} The maximum size
34036 getMaximumSize : function(){
34037 return this.maxSize;
34041 * Sets the maximum size for the resizing element
34042 * @param {Number} maxSize The maximum size
34044 setMaximumSize : function(maxSize){
34045 this.maxSize = maxSize;
34049 * Sets the initialize size for the resizing element
34050 * @param {Number} size The initial size
34052 setCurrentSize : function(size){
34053 var oldAnimate = this.animate;
34054 this.animate = false;
34055 this.adapter.setElementSize(this, size);
34056 this.animate = oldAnimate;
34060 * Destroy this splitbar.
34061 * @param {Boolean} removeEl True to remove the element
34063 destroy : function(removeEl){
34065 this.shim.remove();
34068 this.proxy.parentNode.removeChild(this.proxy);
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.
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);
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.
34092 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34095 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34096 // do nothing for now
34097 init : function(s){
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
34104 getElementSize : function(s){
34105 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34106 return s.resizingEl.getWidth();
34108 return s.resizingEl.getHeight();
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
34118 setElementSize : function(s, newSize, onComplete){
34119 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34121 s.resizingEl.setWidth(newSize);
34123 onComplete(s, newSize);
34126 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34131 s.resizingEl.setHeight(newSize);
34133 onComplete(s, newSize);
34136 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
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.
34150 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34151 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34152 this.container = Roo.get(container);
34155 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34156 init : function(s){
34157 this.basic.init(s);
34160 getElementSize : function(s){
34161 return this.basic.getElementSize(s);
34164 setElementSize : function(s, newSize, onComplete){
34165 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34168 moveSplitter : function(s){
34169 var yes = Roo.bootstrap.SplitBar;
34170 switch(s.placement){
34172 s.el.setX(s.resizingEl.getRight());
34175 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34178 s.el.setY(s.resizingEl.getBottom());
34181 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34188 * Orientation constant - Create a vertical SplitBar
34192 Roo.bootstrap.SplitBar.VERTICAL = 1;
34195 * Orientation constant - Create a horizontal SplitBar
34199 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34202 * Placement constant - The resizing element is to the left of the splitter element
34206 Roo.bootstrap.SplitBar.LEFT = 1;
34209 * Placement constant - The resizing element is to the right of the splitter element
34213 Roo.bootstrap.SplitBar.RIGHT = 2;
34216 * Placement constant - The resizing element is positioned above the splitter element
34220 Roo.bootstrap.SplitBar.TOP = 3;
34223 * Placement constant - The resizing element is positioned under splitter element
34227 Roo.bootstrap.SplitBar.BOTTOM = 4;
34228 Roo.namespace("Roo.bootstrap.layout");/*
34230 * Ext JS Library 1.1.1
34231 * Copyright(c) 2006-2007, Ext JS, LLC.
34233 * Originally Released Under LGPL - original licence link has changed is not relivant.
34236 * <script type="text/javascript">
34240 * @class Roo.bootstrap.layout.Manager
34241 * @extends Roo.bootstrap.Component
34242 * Base class for layout managers.
34244 Roo.bootstrap.layout.Manager = function(config)
34246 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34252 /** false to disable window resize monitoring @type Boolean */
34253 this.monitorWindowResize = true;
34258 * Fires when a layout is performed.
34259 * @param {Roo.LayoutManager} this
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)
34268 "regionresized" : true,
34270 * @event regioncollapsed
34271 * Fires when a region is collapsed.
34272 * @param {Roo.LayoutRegion} region The collapsed region
34274 "regioncollapsed" : true,
34276 * @event regionexpanded
34277 * Fires when a region is expanded.
34278 * @param {Roo.LayoutRegion} region The expanded region
34280 "regionexpanded" : true
34282 this.updating = false;
34285 this.el = Roo.get(config.el);
34291 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34296 monitorWindowResize : true,
34302 onRender : function(ct, position)
34305 this.el = Roo.get(ct);
34308 //this.fireEvent('render',this);
34312 initEvents: function()
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');
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);
34333 * Returns true if this layout is currently being updated
34334 * @return {Boolean}
34336 isUpdating : function(){
34337 return this.updating;
34341 * Suspend the LayoutManager from doing auto-layouts while
34342 * making multiple add or remove calls
34344 beginUpdate : function(){
34345 this.updating = true;
34349 * Restore auto-layouts and optionally disable the manager from performing a layout
34350 * @param {Boolean} noLayout true to disable a layout update
34352 endUpdate : function(noLayout){
34353 this.updating = false;
34359 layout: function(){
34363 onRegionResized : function(region, newSize){
34364 this.fireEvent("regionresized", region, newSize);
34368 onRegionCollapsed : function(region){
34369 this.fireEvent("regioncollapsed", region);
34372 onRegionExpanded : function(region){
34373 this.fireEvent("regionexpanded", region);
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)}
34381 getViewSize : function()
34384 if(this.el.dom != document.body){
34385 size = this.el.getSize();
34387 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34389 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34390 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34395 * Returns the Element this layout is bound to.
34396 * @return {Roo.Element}
34398 getEl : function(){
34403 * Returns the specified region.
34404 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34405 * @return {Roo.LayoutRegion}
34407 getRegion : function(target){
34408 return this.regions[target.toLowerCase()];
34411 onWindowResize : function(){
34412 if(this.monitorWindowResize){
34419 * Ext JS Library 1.1.1
34420 * Copyright(c) 2006-2007, Ext JS, LLC.
34422 * Originally Released Under LGPL - original licence link has changed is not relivant.
34425 * <script type="text/javascript">
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>
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>
34439 * Create a new Border
34440 * @param {Object} config Configuration options
34442 Roo.bootstrap.layout.Border = function(config){
34443 config = config || {};
34444 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34448 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34449 if(config[region]){
34450 config[region].region = region;
34451 this.addRegion(config[region]);
34457 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34459 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
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
34466 addRegion : function(config)
34468 if(!this.regions[config.region]){
34469 var r = this.factory(config);
34470 this.bindRegion(r);
34472 return this.regions[config.region];
34476 bindRegion : function(r){
34477 this.regions[r.config.region] = r;
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);
34489 * Performs a layout update.
34491 layout : function()
34493 if(this.updating) {
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)
34504 var size = this.getViewSize();
34505 var w = size.width;
34506 var h = size.height;
34511 //var x = 0, y = 0;
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");
34522 if(north && north.isVisible()){
34523 var b = north.getBox();
34524 var m = north.getMargins();
34525 b.width = w - (m.left+m.right);
34528 centerY = b.height + b.y + m.bottom;
34529 centerH -= centerY;
34530 north.updateBox(this.safeBox(b));
34532 if(south && south.isVisible()){
34533 var b = south.getBox();
34534 var m = south.getMargins();
34535 b.width = w - (m.left+m.right);
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));
34542 if(west && west.isVisible()){
34543 var b = west.getBox();
34544 var m = west.getMargins();
34545 b.height = centerH - (m.top+m.bottom);
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));
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));
34564 var m = center.getMargins();
34566 x: centerX + m.left,
34567 y: centerY + m.top,
34568 width: centerW - (m.left+m.right),
34569 height: centerH - (m.top+m.bottom)
34571 //if(this.hideOnLayout){
34572 //center.el.setStyle("display", "block");
34574 center.updateBox(this.safeBox(centerBox));
34577 this.fireEvent("layout", this);
34581 safeBox : function(box){
34582 box.width = Math.max(0, box.width);
34583 box.height = Math.max(0, box.height);
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
34593 add : function(target, panel){
34595 target = target.toLowerCase();
34596 return this.regions[target].add(panel);
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
34605 remove : function(target, panel){
34606 target = target.toLowerCase();
34607 return this.regions[target].remove(panel);
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
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);
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
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);
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
34651 restoreState : function(provider){
34653 provider = Roo.state.Manager;
34655 var sm = new Roo.LayoutStateManager();
34656 sm.init(this, provider);
34662 * Adds a xtype elements to the layout.
34666 xtype : 'ContentPanel',
34673 xtype : 'NestedLayoutPanel',
34679 items : [ ... list of content panels or nested layout panels.. ]
34683 * @param {Object} cfg Xtype definition of item to add.
34685 addxtype : function(cfg)
34687 // basically accepts a pannel...
34688 // can accept a layout region..!?!?
34689 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34692 // theory? children can only be panels??
34694 //if (!cfg.xtype.match(/Panel$/)) {
34699 if (typeof(cfg.region) == 'undefined') {
34700 Roo.log("Failed to add Panel, region was not set");
34704 var region = cfg.region;
34710 xitems = cfg.items;
34717 case 'Content': // ContentPanel (el, cfg)
34718 case 'Scroll': // ContentPanel (el, cfg)
34720 cfg.autoCreate = true;
34721 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34723 // var el = this.el.createChild();
34724 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34727 this.add(region, ret);
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);
34739 // create a new Layout (which is a Border Layout...
34741 var clayout = cfg.layout;
34742 clayout.el = this.el.createChild();
34743 clayout.items = clayout.items || [];
34747 // replace this exitems with the clayout ones..
34748 xitems = clayout.items;
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;
34754 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
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...
34765 // needs grid and region
34767 //var el = this.getRegion(region).el.createChild();
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);
34775 if (region == 'center' && this.active ) {
34776 cfg.background = false;
34779 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34781 this.add(region, ret);
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);
34791 // cfg.grid.render(el);
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.
34809 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34811 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34812 this.add(region, ret);
34816 throw "Can not add '" + cfg.xtype + "' to Border";
34822 this.beginUpdate();
34826 Roo.each(xitems, function(i) {
34827 region = nb && i.region ? i.region : false;
34829 var add = ret.addxtype(i);
34832 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34833 if (!i.background) {
34834 abn[region] = nb[region] ;
34841 // make the last non-background panel active..
34842 //if (nb) { Roo.log(abn); }
34845 for(var r in abn) {
34846 region = this.getRegion(r);
34848 // tried using nb[r], but it does not work..
34850 region.showPanel(abn[r]);
34861 factory : function(cfg)
34864 var validRegions = Roo.bootstrap.layout.Border.regions;
34866 var target = cfg.region;
34869 var r = Roo.bootstrap.layout;
34873 return new r.North(cfg);
34875 return new r.South(cfg);
34877 return new r.East(cfg);
34879 return new r.West(cfg);
34881 return new r.Center(cfg);
34883 throw 'Layout region "'+target+'" not supported.';
34890 * Ext JS Library 1.1.1
34891 * Copyright(c) 2006-2007, Ext JS, LLC.
34893 * Originally Released Under LGPL - original licence link has changed is not relivant.
34896 * <script type="text/javascript">
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?
34911 Roo.bootstrap.layout.Basic = function(config){
34913 this.mgr = config.mgr;
34915 this.position = config.region;
34917 var skipConfig = config.skipConfig;
34921 * @scope Roo.BasicLayoutRegion
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
34931 "beforeremove" : true,
34933 * @event invalidated
34934 * Fires when the layout for this region is changed.
34935 * @param {Roo.LayoutRegion} this
34937 "invalidated" : true,
34939 * @event visibilitychange
34940 * Fires when this region is shown or hidden
34941 * @param {Roo.LayoutRegion} this
34942 * @param {Boolean} visibility true or false
34944 "visibilitychange" : true,
34946 * @event paneladded
34947 * Fires when a panel is added.
34948 * @param {Roo.LayoutRegion} this
34949 * @param {Roo.ContentPanel} panel The panel
34951 "paneladded" : true,
34953 * @event panelremoved
34954 * Fires when a panel is removed.
34955 * @param {Roo.LayoutRegion} this
34956 * @param {Roo.ContentPanel} panel The panel
34958 "panelremoved" : true,
34960 * @event beforecollapse
34961 * Fires when this region before collapse.
34962 * @param {Roo.LayoutRegion} this
34964 "beforecollapse" : true,
34967 * Fires when this region is collapsed.
34968 * @param {Roo.LayoutRegion} this
34970 "collapsed" : true,
34973 * Fires when this region is expanded.
34974 * @param {Roo.LayoutRegion} this
34979 * Fires when this region is slid into view.
34980 * @param {Roo.LayoutRegion} this
34982 "slideshow" : true,
34985 * Fires when this region slides out of view.
34986 * @param {Roo.LayoutRegion} this
34988 "slidehide" : true,
34990 * @event panelactivated
34991 * Fires when a panel is activated.
34992 * @param {Roo.LayoutRegion} this
34993 * @param {Roo.ContentPanel} panel The activated panel
34995 "panelactivated" : true,
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)
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);
35008 this.activePanel = null;
35009 // ensure listeners are added...
35011 if (config.listeners || config.events) {
35012 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35013 listeners : config.listeners || {},
35014 events : config.events || {}
35018 if(skipConfig !== true){
35019 this.applyConfig(config);
35023 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35025 getPanelId : function(p){
35029 applyConfig : function(config){
35030 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35031 this.config = config;
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
35040 resizeTo : function(newSize){
35041 var el = this.el ? this.el :
35042 (this.activePanel ? this.activePanel.getEl() : null);
35044 switch(this.position){
35047 el.setWidth(newSize);
35048 this.fireEvent("resized", this, newSize);
35052 el.setHeight(newSize);
35053 this.fireEvent("resized", this, newSize);
35059 getBox : function(){
35060 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35063 getMargins : function(){
35064 return this.margins;
35067 updateBox : function(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);
35076 * Returns the container element for this region.
35077 * @return {Roo.Element}
35079 getEl : function(){
35080 return this.activePanel;
35084 * Returns true if this region is currently visible.
35085 * @return {Boolean}
35087 isVisible : function(){
35088 return this.activePanel ? true : false;
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);
35097 this.activePanel = panel;
35098 panel.setActiveState(true);
35100 panel.setSize(this.box.width, this.box.height);
35102 this.fireEvent("panelactivated", this, panel);
35103 this.fireEvent("invalidated");
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
35111 showPanel : function(panel){
35112 panel = this.getPanel(panel);
35114 this.setActivePanel(panel);
35120 * Get the active panel for this region.
35121 * @return {Roo.ContentPanel} The active panel or null
35123 getActivePanel : function(){
35124 return this.activePanel;
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)
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]);
35139 if(this.hasPanel(panel)){
35140 this.showPanel(panel);
35143 var el = panel.getEl();
35144 if(el.dom.parentNode != this.mgr.el.dom){
35145 this.mgr.el.dom.appendChild(el.dom);
35147 if(panel.setRegion){
35148 panel.setRegion(this);
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);
35158 this.fireEvent("paneladded", this, panel);
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}
35167 hasPanel : function(panel){
35168 if(typeof panel == "object"){ // must be panel obj
35169 panel = panel.getId();
35171 return this.getPanel(panel) ? true : false;
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
35180 remove : function(panel, preservePanel){
35181 panel = this.getPanel(panel);
35186 this.fireEvent("beforeremove", this, panel, e);
35187 if(e.cancel === true){
35190 var panelId = panel.getId();
35191 this.panels.removeKey(panelId);
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}
35200 getPanel : function(id){
35201 if(typeof id == "object"){ // must be panel obj
35204 return this.panels.get(id);
35208 * Returns this regions position (north/south/east/west/center).
35211 getPosition: function(){
35212 return this.position;
35216 * Ext JS Library 1.1.1
35217 * Copyright(c) 2006-2007, Ext JS, LLC.
35219 * Originally Released Under LGPL - original licence link has changed is not relivant.
35222 * <script type="text/javascript">
35226 * @class Roo.bootstrap.layout.Region
35227 * @extends Roo.bootstrap.layout.Basic
35228 * This class represents a region in a layout manager.
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.
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
35256 * @cfg {string} cls Extra CSS classes to add to region
35258 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35259 * @cfg {string} region the region that it inhabits..
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)
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
35269 Roo.bootstrap.layout.Region = function(config)
35271 this.applyConfig(config);
35273 var mgr = config.mgr;
35274 var pos = config.region;
35275 config.skipConfig = true;
35276 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35279 this.onRender(mgr.el);
35282 this.visible = true;
35283 this.collapsed = false;
35284 this.unrendered_panels = [];
35287 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
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({
35296 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35300 onRender: function(ctr, pos)
35302 var dh = Roo.DomHelper;
35303 /** This region's container element
35304 * @type Roo.Element */
35305 this.el = dh.append(ctr.dom, {
35307 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35309 /** This region's title element
35310 * @type Roo.Element */
35312 this.titleEl = dh.append(this.el.dom,
35315 unselectable: "on",
35316 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35318 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35319 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
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);
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();
35333 this.createBody(this.config);
35334 if(this.config.hideWhenEmpty){
35336 this.on("paneladded", this.validateVisibility, this);
35337 this.on("panelremoved", this.validateVisibility, this);
35339 if(this.autoScroll){
35340 this.bodyEl.setStyle("overflow", "auto");
35342 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35344 //if(c.titlebar !== false){
35345 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35346 this.titleEl.hide();
35348 this.titleEl.show();
35349 if(this.config.title){
35350 this.titleTextEl.innerHTML = this.config.title;
35354 if(this.config.collapsed){
35355 this.collapse(true);
35357 if(this.config.hidden){
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]);
35365 this.unrendered_panels = null;
35371 applyConfig : function(c)
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();
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();
35390 /** This region's collapsed element
35391 * @type Roo.Element */
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"}]}
35398 if(c.floatable !== false){
35399 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35400 this.collapsedEl.on("click", this.collapseClick, this);
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;
35408 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35409 this.expandBtn.on("click", this.expand, this);
35413 if(this.collapseBtn){
35414 this.collapseBtn.setVisible(c.collapsible == true);
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});
35422 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35425 this.bottomTabs = c.tabPosition != "top";
35427 this.autoScroll = c.autoScroll || false;
35432 this.duration = c.duration || .30;
35433 this.slideDuration = c.slideDuration || .45;
35438 * Returns true if this region is currently visible.
35439 * @return {Boolean}
35441 isVisible : function(){
35442 return this.visible;
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, "&#160;")
35449 //setCollapsedTitle : function(title){
35450 // title = title || " ";
35451 // if(this.collapsedTitleTextEl){
35452 // this.collapsedTitleTextEl.innerHTML = title;
35456 getBox : function(){
35458 // if(!this.collapsed){
35459 b = this.el.getBox(false, true);
35461 // b = this.collapsedEl.getBox(false, true);
35466 getMargins : function(){
35467 return this.margins;
35468 //return this.collapsed ? this.cmargins : this.margins;
35471 highlight : function(){
35472 this.el.addClass("x-layout-panel-dragover");
35475 unhighlight : function(){
35476 this.el.removeClass("x-layout-panel-dragover");
35479 updateBox : function(box)
35481 if (!this.bodyEl) {
35482 return; // not rendered yet..
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);
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);
35496 this.tabs.autoSizeTabs();
35500 updateBody : function(w, h)
35503 this.el.setWidth(w);
35504 w -= this.el.getBorderWidth("rl");
35505 if(this.config.adjustments){
35506 w += this.config.adjustments[0];
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];
35516 this.bodyEl.setHeight(h);
35518 h = this.tabs.syncHeight(h);
35521 if(this.panelSize){
35522 w = w !== null ? w : this.panelSize.width;
35523 h = h !== null ? h : this.panelSize.height;
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);
35532 if(Roo.isIE && this.tabs){
35533 this.tabs.el.repaint();
35538 * Returns the container element for this region.
35539 * @return {Roo.Element}
35541 getEl : function(){
35546 * Hides this region.
35549 //if(!this.collapsed){
35550 this.el.dom.style.left = "-2000px";
35553 // this.collapsedEl.dom.style.left = "-2000px";
35554 // this.collapsedEl.hide();
35556 this.visible = false;
35557 this.fireEvent("visibilitychange", this, false);
35561 * Shows this region if it was previously hidden.
35564 //if(!this.collapsed){
35567 // this.collapsedEl.show();
35569 this.visible = true;
35570 this.fireEvent("visibilitychange", this, true);
35573 closeClicked : function(){
35574 if(this.activePanel){
35575 this.remove(this.activePanel);
35579 collapseClick : function(e){
35581 e.stopPropagation();
35584 e.stopPropagation();
35590 * Collapses this region.
35591 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35594 collapse : function(skipAnim, skipCheck = false){
35595 if(this.collapsed) {
35599 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35601 this.collapsed = true;
35603 this.split.el.hide();
35605 if(this.config.animate && skipAnim !== true){
35606 this.fireEvent("invalidated", this);
35607 this.animateCollapse();
35609 this.el.setLocation(-20000,-20000);
35611 this.collapsedEl.show();
35612 this.fireEvent("collapsed", this);
35613 this.fireEvent("invalidated", this);
35619 animateCollapse : function(){
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)
35629 expand : function(e, skipAnim){
35631 e.stopPropagation();
35633 if(!this.collapsed || this.el.hasActiveFx()) {
35637 this.afterSlideIn();
35640 this.collapsed = false;
35641 if(this.config.animate && skipAnim !== true){
35642 this.animateExpand();
35646 this.split.el.show();
35648 this.collapsedEl.setLocation(-2000,-2000);
35649 this.collapsedEl.hide();
35650 this.fireEvent("invalidated", this);
35651 this.fireEvent("expanded", this);
35655 animateExpand : function(){
35659 initTabs : function()
35661 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
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
35670 if(this.config.hideTabs){
35671 ts.stripWrap.setDisplayed(false);
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);
35684 initPanelAsTab : function(panel){
35685 var ti = this.tabs.addTab(
35689 this.config.closeOnTab && panel.isClosable(),
35692 if(panel.tabTip !== undefined){
35693 ti.setTooltip(panel.tabTip);
35695 ti.on("activate", function(){
35696 this.setActivePanel(panel);
35699 if(this.config.closeOnTab){
35700 ti.on("beforeclose", function(t, e){
35702 this.remove(panel);
35706 panel.tabItem = ti;
35711 updatePanelTitle : function(panel, title)
35713 if(this.activePanel == panel){
35714 this.updateTitle(title);
35717 var ti = this.tabs.getTab(panel.getEl().id);
35719 if(panel.tabTip !== undefined){
35720 ti.setTooltip(panel.tabTip);
35725 updateTitle : function(title){
35726 if(this.titleTextEl && !this.config.title){
35727 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
35731 setActivePanel : function(panel)
35733 panel = this.getPanel(panel);
35734 if(this.activePanel && this.activePanel != panel){
35735 if(this.activePanel.setActiveState(false) === false){
35739 this.activePanel = panel;
35740 panel.setActiveState(true);
35741 if(this.panelSize){
35742 panel.setSize(this.panelSize.width, this.panelSize.height);
35745 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35747 this.updateTitle(panel.getTitle());
35749 this.fireEvent("invalidated", this);
35751 this.fireEvent("panelactivated", this, panel);
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
35759 showPanel : function(panel)
35761 panel = this.getPanel(panel);
35764 var tab = this.tabs.getTab(panel.getEl().id);
35765 if(tab.isHidden()){
35766 this.tabs.unhideTab(tab.id);
35770 this.setActivePanel(panel);
35777 * Get the active panel for this region.
35778 * @return {Roo.ContentPanel} The active panel or null
35780 getActivePanel : function(){
35781 return this.activePanel;
35784 validateVisibility : function(){
35785 if(this.panels.getCount() < 1){
35786 this.updateTitle(" ");
35787 this.closeBtn.hide();
35790 if(!this.isVisible()){
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)
35801 add : function(panel)
35803 if(arguments.length > 1){
35804 for(var i = 0, len = arguments.length; i < len; i++) {
35805 this.add(arguments[i]);
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);
35819 if(this.hasPanel(panel)){
35820 this.showPanel(panel);
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);
35832 this.fireEvent("paneladded", this, panel);
35839 this.initPanelAsTab(panel);
35843 if(panel.background !== true){
35844 this.tabs.activate(panel.getEl().id);
35846 this.fireEvent("paneladded", this, panel);
35851 * Hides the tab for the specified panel.
35852 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35854 hidePanel : function(panel){
35855 if(this.tabs && (panel = this.getPanel(panel))){
35856 this.tabs.hideTab(panel.getEl().id);
35861 * Unhides the tab for a previously hidden panel.
35862 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35864 unhidePanel : function(panel){
35865 if(this.tabs && (panel = this.getPanel(panel))){
35866 this.tabs.unhideTab(panel.getEl().id);
35870 clearPanels : function(){
35871 while(this.panels.getCount() > 0){
35872 this.remove(this.panels.first());
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
35882 remove : function(panel, preservePanel)
35884 panel = this.getPanel(panel);
35889 this.fireEvent("beforeremove", this, panel, e);
35890 if(e.cancel === true){
35893 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35894 var panelId = panel.getId();
35895 this.panels.removeKey(panelId);
35897 document.body.appendChild(panel.getEl().dom);
35900 this.tabs.removeTab(panel.getEl().id);
35901 }else if (!preservePanel){
35902 this.bodyEl.dom.removeChild(panel.getEl().dom);
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);
35911 this.updateTitle(p.getTitle());
35913 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35914 this.setActivePanel(p);
35916 panel.setRegion(null);
35917 if(this.activePanel == panel){
35918 this.activePanel = null;
35920 if(this.config.autoDestroy !== false && preservePanel !== true){
35921 try{panel.destroy();}catch(e){}
35923 this.fireEvent("panelremoved", this, panel);
35928 * Returns the TabPanel component used by this region
35929 * @return {Roo.TabPanel}
35931 getTabs : function(){
35935 createTool : function(parentEl, className){
35936 var btn = Roo.DomHelper.append(parentEl, {
35938 cls: "x-layout-tools-button",
35941 cls: "roo-layout-tools-button-inner " + className,
35945 btn.addClassOnOver("roo-layout-tools-button-over");
35950 * Ext JS Library 1.1.1
35951 * Copyright(c) 2006-2007, Ext JS, LLC.
35953 * Originally Released Under LGPL - original licence link has changed is not relivant.
35956 * <script type="text/javascript">
35962 * @class Roo.SplitLayoutRegion
35963 * @extends Roo.LayoutRegion
35964 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35966 Roo.bootstrap.layout.Split = function(config){
35967 this.cursor = config.cursor;
35968 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35971 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35973 splitTip : "Drag to resize.",
35974 collapsibleSplitTip : "Drag to resize. Double click to hide.",
35975 useSplitTips : false,
35977 applyConfig : function(config){
35978 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35981 onRender : function(ctr,pos) {
35983 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35984 if(!this.config.split){
35989 var splitEl = Roo.DomHelper.append(ctr.dom, {
35991 id: this.el.id + "-split",
35992 cls: "roo-layout-split roo-layout-split-"+this.position,
35995 /** The SplitBar for this region
35996 * @type Roo.SplitBar */
35997 // does not exist yet...
35998 Roo.log([this.position, this.orientation]);
36000 this.split = new Roo.bootstrap.SplitBar({
36001 dragElement : splitEl,
36002 resizingElement: this.el,
36003 orientation : this.orientation
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;
36012 //if(config.collapsible){
36013 // this.split.el.on("dblclick", this.collapse, this);
36016 if(typeof this.config.minSize != "undefined"){
36017 this.split.minSize = this.config.minSize;
36019 if(typeof this.config.maxSize != "undefined"){
36020 this.split.maxSize = this.config.maxSize;
36022 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36023 this.hideSplitter();
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());
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());
36040 onSplitMove : function(split, newSize){
36041 this.fireEvent("resized", this, newSize);
36045 * Returns the {@link Roo.SplitBar} for this region.
36046 * @return {Roo.SplitBar}
36048 getSplitBar : function(){
36053 this.hideSplitter();
36054 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36057 hideSplitter : function(){
36059 this.split.el.setLocation(-2000,-2000);
36060 this.split.el.hide();
36066 this.split.el.show();
36068 Roo.bootstrap.layout.Split.superclass.show.call(this);
36071 beforeSlide: function(){
36072 if(Roo.isGecko){// firefox overflow auto bug workaround
36073 this.bodyEl.clip();
36075 this.tabs.bodyEl.clip();
36077 if(this.activePanel){
36078 this.activePanel.getEl().clip();
36080 if(this.activePanel.beforeSlide){
36081 this.activePanel.beforeSlide();
36087 afterSlide : function(){
36088 if(Roo.isGecko){// firefox overflow auto bug workaround
36089 this.bodyEl.unclip();
36091 this.tabs.bodyEl.unclip();
36093 if(this.activePanel){
36094 this.activePanel.getEl().unclip();
36095 if(this.activePanel.afterSlide){
36096 this.activePanel.afterSlide();
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)){
36112 "mouseover" : function(e){
36118 this.el.on(this.autoHideHd);
36122 clearAutoHide : function(){
36123 if(this.autoHide !== false){
36124 this.el.un("mouseout", this.autoHideHd.mouseout);
36125 this.el.un("mouseover", this.autoHideHd.mouseover);
36129 clearMonitor : function(){
36130 Roo.get(document).un("click", this.slideInIf, this);
36133 // these names are backwards but not changed for compat
36134 slideOut : function(){
36135 if(this.isSlid || this.el.hasActiveFx()){
36138 this.isSlid = true;
36139 if(this.collapseBtn){
36140 this.collapseBtn.hide();
36142 this.closeBtnState = this.closeBtn.getStyle('display');
36143 this.closeBtn.hide();
36145 this.stickBtn.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(){
36154 this.initAutoHide();
36155 Roo.get(document).on("click", this.slideInIf, this);
36156 this.fireEvent("slideshow", this);
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();
36171 this.closeBtn.setStyle('display', this.closeBtnState);
36173 this.stickBtn.hide();
36175 this.fireEvent("slidehide", this);
36178 slideIn : function(cb){
36179 if(!this.isSlid || this.el.hasActiveFx()){
36183 this.isSlid = false;
36184 this.beforeSlide();
36185 this.el.slideOut(this.getSlideAnchor(), {
36186 callback: function(){
36187 this.el.setLeftTop(-10000, -10000);
36189 this.afterSlideIn();
36197 slideInIf : function(e){
36198 if(!e.within(this.el)){
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});
36212 this.el.setLocation(-10000,-10000);
36214 this.fireEvent("collapsed", this);
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({
36228 this.el.slideIn(this.getSlideAnchor(), {
36229 callback : function(){
36230 this.el.setStyle("z-index", "");
36233 this.split.el.show();
36235 this.fireEvent("invalidated", this);
36236 this.fireEvent("expanded", this);
36264 getAnchor : function(){
36265 return this.anchors[this.position];
36268 getCollapseAnchor : function(){
36269 return this.canchors[this.position];
36272 getSlideAnchor : function(){
36273 return this.sanchors[this.position];
36276 getAlignAdj : function(){
36277 var cm = this.cmargins;
36278 switch(this.position){
36294 getExpandAdj : function(){
36295 var c = this.collapsedEl, cm = this.cmargins;
36296 switch(this.position){
36298 return [-(cm.right+c.getWidth()+cm.left), 0];
36301 return [cm.right+c.getWidth()+cm.left, 0];
36304 return [0, -(cm.top+cm.bottom+c.getHeight())];
36307 return [0, cm.top+cm.bottom+c.getHeight()];
36313 * Ext JS Library 1.1.1
36314 * Copyright(c) 2006-2007, Ext JS, LLC.
36316 * Originally Released Under LGPL - original licence link has changed is not relivant.
36319 * <script type="text/javascript">
36322 * These classes are private internal classes
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;
36332 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36334 // center panel can't be hidden
36338 // center panel can't be hidden
36341 getMinWidth: function(){
36342 return this.minWidth;
36345 getMinHeight: function(){
36346 return this.minHeight;
36359 Roo.bootstrap.layout.North = function(config)
36361 config.region = 'north';
36362 config.cursor = 'n-resize';
36364 Roo.bootstrap.layout.Split.call(this, config);
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");
36372 var size = config.initialSize || config.height;
36373 if(typeof size != "undefined"){
36374 this.el.setHeight(size);
36377 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36379 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36383 getBox : function(){
36384 if(this.collapsed){
36385 return this.collapsedEl.getBox();
36387 var box = this.el.getBox();
36389 box.height += this.split.el.getHeight();
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);
36401 if(this.collapsed){
36402 this.updateBody(box.width, null);
36404 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36412 Roo.bootstrap.layout.South = function(config){
36413 config.region = 'south';
36414 config.cursor = 's-resize';
36415 Roo.bootstrap.layout.Split.call(this, config);
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");
36421 var size = config.initialSize || config.height;
36422 if(typeof size != "undefined"){
36423 this.el.setHeight(size);
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();
36433 var box = this.el.getBox();
36435 var sh = this.split.el.getHeight();
36442 updateBox : function(box){
36443 if(this.split && !this.collapsed){
36444 var sh = this.split.el.getHeight();
36447 this.split.el.setLeft(box.x);
36448 this.split.el.setTop(box.y-sh);
36449 this.split.el.setWidth(box.width);
36451 if(this.collapsed){
36452 this.updateBody(box.width, null);
36454 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36458 Roo.bootstrap.layout.East = function(config){
36459 config.region = "east";
36460 config.cursor = "e-resize";
36461 Roo.bootstrap.layout.Split.call(this, config);
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");
36467 var size = config.initialSize || config.width;
36468 if(typeof size != "undefined"){
36469 this.el.setWidth(size);
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();
36478 var box = this.el.getBox();
36480 var sw = this.split.el.getWidth();
36487 updateBox : function(box){
36488 if(this.split && !this.collapsed){
36489 var sw = this.split.el.getWidth();
36491 this.split.el.setLeft(box.x);
36492 this.split.el.setTop(box.y);
36493 this.split.el.setHeight(box.height);
36496 if(this.collapsed){
36497 this.updateBody(null, box.height);
36499 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36503 Roo.bootstrap.layout.West = function(config){
36504 config.region = "west";
36505 config.cursor = "w-resize";
36507 Roo.bootstrap.layout.Split.call(this, config);
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");
36515 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36516 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36518 onRender: function(ctr, pos)
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);
36527 getBox : function(){
36528 if(this.collapsed){
36529 return this.collapsedEl.getBox();
36531 var box = this.el.getBox();
36533 box.width += this.split.el.getWidth();
36538 updateBox : function(box){
36539 if(this.split && !this.collapsed){
36540 var sw = this.split.el.getWidth();
36542 this.split.el.setLeft(box.x+box.width);
36543 this.split.el.setTop(box.y);
36544 this.split.el.setHeight(box.height);
36546 if(this.collapsed){
36547 this.updateBody(null, box.height);
36549 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36552 Roo.namespace("Roo.bootstrap.panel");/*
36554 * Ext JS Library 1.1.1
36555 * Copyright(c) 2006-2007, Ext JS, LLC.
36557 * Originally Released Under LGPL - original licence link has changed is not relivant.
36560 * <script type="text/javascript">
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
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)
36590 Roo.bootstrap.panel.Content = function( config){
36592 this.tpl = config.tpl || false;
36594 var el = config.el;
36595 var content = config.content;
36597 if(config.autoCreate){ // xtype is available if this is called from factory
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;
36606 this.el = Roo.DomHelper.append(document.body,
36607 config.autoCreate, true);
36609 var elcfg = { tag: "div",
36610 cls: "roo-layout-inactive-content",
36614 elcfg.html = config.html;
36618 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36621 this.closable = false;
36622 this.loaded = false;
36623 this.active = false;
36626 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36628 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36630 this.wrapEl = this.el; //this.el.wrap();
36632 if (config.toolbar.items) {
36633 ti = config.toolbar.items ;
36634 delete config.toolbar.items ;
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])));
36643 this.toolbar.items = nitems;
36644 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36645 delete config.toolbar;
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();
36655 this.footer.container = this.wrapEl.createChild();
36657 this.footer = Roo.factory(this.footer, Roo);
36662 if(typeof config == "string"){
36663 this.title = config;
36665 Roo.apply(this, config);
36669 this.resizeEl = Roo.get(this.resizeEl, true);
36671 this.resizeEl = this.el;
36673 // handle view.xtype
36681 * Fires when this panel is activated.
36682 * @param {Roo.ContentPanel} this
36686 * @event deactivate
36687 * Fires when this panel is activated.
36688 * @param {Roo.ContentPanel} this
36690 "deactivate" : true,
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
36703 * Fires when this tab is created
36704 * @param {Roo.ContentPanel} this
36715 if(this.autoScroll){
36716 this.resizeEl.setStyle("overflow", "auto");
36718 // fix randome scrolling
36719 //this.el.on('scroll', function() {
36720 // Roo.log('fix random scolling');
36721 // this.scrollTo('top',0);
36724 content = content || this.content;
36726 this.setContent(content);
36728 if(config && config.url){
36729 this.setUrl(this.url, this.params, this.loadOnce);
36734 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
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, '');
36743 this.fireEvent('render', this);
36746 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36750 setRegion : function(region){
36751 this.region = region;
36752 this.setActiveClass(region && !this.background);
36756 setActiveClass: function(state)
36759 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36760 this.el.setStyle('position','relative');
36762 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36763 this.el.setStyle('position', 'absolute');
36768 * Returns the toolbar for this Panel if one was configured.
36769 * @return {Roo.Toolbar}
36771 getToolbar : function(){
36772 return this.toolbar;
36775 setActiveState : function(active)
36777 this.active = active;
36778 this.setActiveClass(active);
36780 if(this.fireEvent("deactivate", this) === false){
36785 this.fireEvent("activate", this);
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
36793 setContent : function(content, loadScripts){
36794 this.el.update(content, loadScripts);
36797 ignoreResize : function(w, h){
36798 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36801 this.lastSize = {width: w, height: h};
36806 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36807 * @return {Roo.UpdateManager} The UpdateManager
36809 getUpdateManager : function(){
36810 return this.el.getUpdateManager();
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:
36817 url: "your-url.php",
36818 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36819 callback: yourFunction,
36820 scope: yourObject, //(optional scope)
36823 text: "Loading...",
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&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
36836 var um = this.el.getUpdateManager();
36837 um.update.apply(um, arguments);
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
36849 setUrl : function(url, params, loadOnce){
36850 if(this.refreshDelegate){
36851 this.removeListener("activate", this.refreshDelegate);
36853 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36854 this.on("activate", this.refreshDelegate);
36855 return this.el.getUpdateManager();
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));
36865 _setLoaded : function(){
36866 this.loaded = true;
36870 * Returns this panel's id
36873 getId : function(){
36878 * Returns this panel's element - used by regiosn to add.
36879 * @return {Roo.Element}
36881 getEl : function(){
36882 return this.wrapEl || this.el;
36887 adjustForComponents : function(width, height)
36889 //Roo.log('adjustForComponents ');
36890 if(this.resizeEl != this.el){
36891 width -= this.el.getFrameWidth('lr');
36892 height -= this.el.getFrameWidth('tb');
36895 var te = this.toolbar.getEl();
36896 te.setWidth(width);
36897 height -= te.getHeight();
36900 var te = this.footer.getEl();
36901 te.setWidth(width);
36902 height -= te.getHeight();
36906 if(this.adjustments){
36907 width += this.adjustments[0];
36908 height += this.adjustments[1];
36910 return {"width": width, "height": height};
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);
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);
36925 * Returns this panel's title
36928 getTitle : function(){
36930 if (typeof(this.title) != 'object') {
36935 for (var k in this.title) {
36936 if (!this.title.hasOwnProperty(k)) {
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>";
36946 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36953 * Set this panel's title
36954 * @param {String} title
36956 setTitle : function(title){
36957 this.title = title;
36959 this.region.updatePanelTitle(this, title);
36964 * Returns true is this panel was configured to be closable
36965 * @return {Boolean}
36967 isClosable : function(){
36968 return this.closable;
36971 beforeSlide : function(){
36973 this.resizeEl.clip();
36976 afterSlide : function(){
36978 this.resizeEl.unclip();
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.
36986 refresh : function(){
36987 if(this.refreshDelegate){
36988 this.loaded = false;
36989 this.refreshDelegate();
36994 * Destroys this panel
36996 destroy : function(){
36997 this.el.removeAllListeners();
36998 var tempEl = document.createElement("span");
36999 tempEl.appendChild(this.el.dom);
37000 tempEl.innerHTML = "";
37006 * form - if the content panel contains a form - this is a reference to it.
37007 * @type {Roo.form.Form}
37011 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37012 * This contains a reference to it.
37018 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37028 * @param {Object} cfg Xtype definition of item to add.
37032 getChildContainer: function () {
37033 return this.getEl();
37038 var ret = new Roo.factory(cfg);
37043 if (cfg.xtype.match(/^Form$/)) {
37046 //if (this.footer) {
37047 // el = this.footer.container.insertSibling(false, 'before');
37049 el = this.el.createChild();
37052 this.form = new Roo.form.Form(cfg);
37055 if ( this.form.allItems.length) {
37056 this.form.render(el.dom);
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''
37064 cfg.el = this.el.appendChild(document.createElement("div"));
37067 var ret = new Roo.factory(cfg);
37069 ret.render && ret.render(false, ''); // render blank..
37079 * @class Roo.bootstrap.panel.Grid
37080 * @extends Roo.bootstrap.panel.Content
37082 * Create a new GridPanel.
37083 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37084 * @param {Object} config A the config object
37090 Roo.bootstrap.panel.Grid = function(config)
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);
37097 config.el = this.wrapper;
37098 //this.el = this.wrapper;
37100 if (config.container) {
37101 // ctor'ed from a Border/panel.grid
37104 this.wrapper.setStyle("overflow", "hidden");
37105 this.wrapper.addClass('roo-grid-container');
37110 if(config.toolbar){
37111 var tool_el = this.wrapper.createChild();
37112 this.toolbar = Roo.factory(config.toolbar);
37114 if (config.toolbar.items) {
37115 ti = config.toolbar.items ;
37116 delete config.toolbar.items ;
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])));
37125 this.toolbar.items = nitems;
37127 delete config.toolbar;
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;
37136 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
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");
37148 this.grid.render(this.wrapper);
37149 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37152 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37153 // ??? needed ??? config.el = this.wrapper;
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) {
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);
37174 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37175 getId : function(){
37176 return this.grid.id;
37180 * Returns the grid for this panel
37181 * @return {Roo.bootstrap.Table}
37183 getGrid : function(){
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);
37194 var thd = grid.getGridEl().select('thead',true).first();
37195 var tbd = grid.getGridEl().select('tbody', true).first();
37197 tbd.setSize(width, height - thd.getHeight());
37206 beforeSlide : function(){
37207 this.grid.getView().scroller.clip();
37210 afterSlide : function(){
37211 this.grid.getView().scroller.unclip();
37214 destroy : function(){
37215 this.grid.destroy();
37217 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37222 * @class Roo.bootstrap.panel.Nest
37223 * @extends Roo.bootstrap.panel.Content
37225 * Create a new Panel, that can contain a layout.Border.
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
37231 Roo.bootstrap.panel.Nest = function(config)
37233 // construct with only one argument..
37234 /* FIXME - implement nicer consturctors
37235 if (layout.layout) {
37237 layout = config.layout;
37238 delete config.layout;
37240 if (layout.xtype && !layout.getEl) {
37241 // then layout needs constructing..
37242 layout = Roo.factory(layout, Roo);
37246 config.el = config.layout.getEl();
37248 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37250 config.layout.monitorWindowResize = false; // turn off autosizing
37251 this.layout = config.layout;
37252 this.layout.getEl().addClass("roo-layout-nested-layout");
37259 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
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);
37268 el.setSize(size.width, size.height);
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();
37280 // activate all subpanels if not currently active..
37282 setActiveState : function(active){
37283 this.active = active;
37284 this.setActiveClass(active);
37287 this.fireEvent("deactivate", this);
37291 this.fireEvent("activate", this);
37292 // not sure if this should happen before or after..
37293 if (!this.layout) {
37294 return; // should not happen..
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());
37304 if (!reg.panels.length) {
37307 reg.showPanel(reg.getPanel(0));
37316 * Returns the nested BorderLayout for this panel
37317 * @return {Roo.BorderLayout}
37319 getLayout : function(){
37320 return this.layout;
37324 * Adds a xtype elements to the layout of the nested panel
37328 xtype : 'ContentPanel',
37335 xtype : 'NestedLayoutPanel',
37341 items : [ ... list of content panels or nested layout panels.. ]
37345 * @param {Object} cfg Xtype definition of item to add.
37347 addxtype : function(cfg) {
37348 return this.layout.addxtype(cfg);
37353 * Ext JS Library 1.1.1
37354 * Copyright(c) 2006-2007, Ext JS, LLC.
37356 * Originally Released Under LGPL - original licence link has changed is not relivant.
37359 * <script type="text/javascript">
37362 * @class Roo.TabPanel
37363 * @extends Roo.util.Observable
37364 * A lightweight tab container.
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");
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.");
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);
37384 // Use setUrl for Ajax loading
37385 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37386 tab3.setUrl("ajax2.htm", null, true);
37389 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37392 jtabs.activate("jtabs-1");
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.
37399 Roo.bootstrap.panel.Tabs = function(config){
37401 * The container element for this TabPanel.
37402 * @type Roo.Element
37404 this.el = Roo.get(config.el);
37407 if(typeof config == "boolean"){
37408 this.tabPosition = config ? "bottom" : "top";
37410 Roo.apply(this, config);
37414 if(this.tabPosition == "bottom"){
37415 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37416 this.el.addClass("roo-tabs-bottom");
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);
37422 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37424 if(this.tabPosition != "bottom"){
37425 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37426 * @type Roo.Element
37428 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37429 this.el.addClass("roo-tabs-top");
37433 this.bodyEl.setStyle("position", "relative");
37435 this.active = null;
37436 this.activateDelegate = this.activate.createDelegate(this);
37441 * Fires when the active tab changes
37442 * @param {Roo.TabPanel} this
37443 * @param {Roo.TabPanelItem} activePanel The new active tab
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
37453 "beforetabchange" : true
37456 Roo.EventManager.onWindowResize(this.onResize, this);
37457 this.cpad = this.el.getPadding("lr");
37458 this.hiddenCount = 0;
37461 // toolbar on the tabbar support...
37462 if (this.toolbar) {
37463 alert("no toolbar support yet");
37464 this.toolbar = false;
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%');
37479 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37482 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37484 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37486 tabPosition : "top",
37488 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37490 currentTabWidth : 0,
37492 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37496 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37500 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37502 preferredTabWidth : 175,
37504 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37506 resizeTabs : false,
37508 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37510 monitorResize : true,
37512 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
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
37524 addTab : function(id, text, content, closable, tpl)
37526 var item = new Roo.bootstrap.panel.TabItem({
37530 closable : closable,
37533 this.addTabItem(item);
37535 item.setContent(content);
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}
37545 getTab : function(id){
37546 return this.items[id];
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.
37553 hideTab : function(id){
37554 var t = this.items[id];
37557 this.hiddenCount++;
37558 this.autoSizeTabs();
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.
37566 unhideTab : function(id){
37567 var t = this.items[id];
37569 t.setHidden(false);
37570 this.hiddenCount--;
37571 this.autoSizeTabs();
37576 * Adds an existing {@link Roo.TabPanelItem}.
37577 * @param {Roo.TabPanelItem} item The TabPanelItem to add
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();
37586 // item.autoSize();
37591 * Removes a {@link Roo.TabPanelItem}.
37592 * @param {String/Number} id The id or index of the TabPanelItem to remove.
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);
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);
37609 items.splice(index, 1);
37610 delete this.items[tab.id];
37611 tab.fireEvent("close", tab);
37612 tab.purgeListeners();
37613 this.autoSizeTabs();
37616 getNextAvailable : function(start){
37617 var items = this.items;
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()){
37627 // if one isn't found select the previous tab (on the left)
37630 var item = items[--index];
37631 if(item && !item.isHidden()){
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.
37642 disableTab : function(id){
37643 var tab = this.items[id];
37644 if(tab && this.active != tab){
37650 * Enables a {@link Roo.TabPanelItem} that is disabled.
37651 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37653 enableTab : function(id){
37654 var tab = this.items[id];
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.
37663 activate : function(id){
37664 var tab = this.items[id];
37668 if(tab == this.active || tab.disabled){
37672 this.fireEvent("beforetabchange", this, e, tab);
37673 if(e.cancel !== true && !tab.disabled){
37675 this.active.hide();
37677 this.active = this.items[id];
37678 this.active.show();
37679 this.fireEvent("tabchange", this, this.active);
37685 * Gets the active {@link Roo.TabPanelItem}.
37686 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37688 getActiveTab : function(){
37689 return this.active;
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
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);
37705 onResize : function(){
37706 if(this.monitorResize){
37707 this.autoSizeTabs();
37712 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37714 beginUpdate : function(){
37715 this.updating = true;
37719 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37721 endUpdate : function(){
37722 this.updating = false;
37723 this.autoSizeTabs();
37727 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
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) {
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();
37746 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37749 if(this.currentTabWidth < this.preferredTabWidth){
37750 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37756 * Returns the number of tabs in this TabPanel.
37759 getCount : function(){
37760 return this.items.length;
37764 * Resizes all the tabs to the passed width
37765 * @param {Number} The new width
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);
37777 * Destroys this TabPanel
37778 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
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();
37785 if(removeEl === true){
37786 this.el.update("");
37791 createStrip : function(container)
37793 var strip = document.createElement("nav");
37794 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37795 container.appendChild(strip);
37799 createStripList : function(strip)
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;
37809 createBody : function(container)
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);
37818 createItemBody :function(bodyEl, id){
37819 var body = Roo.getDom(id);
37821 body = document.createElement("div");
37824 //Roo.fly(body).addClass("x-tabs-item-body");
37825 Roo.fly(body).addClass("tab-pane");
37826 bodyEl.insertBefore(body, bodyEl.firstChild);
37830 createStripElements : function(stripEl, text, closable, tpl)
37832 var td = document.createElement("li"); // was td..
37835 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37838 stripEl.appendChild(td);
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"> </div></em></span></a>'
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};
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>'
37860 // this.tabTpl = new Roo.Template(
37861 // '<a href="#">' +
37862 // '<span unselectable="on"' +
37863 // (this.disableTooltips ? '' : ' title="{text}"') +
37864 // ' >{text}</span></a>'
37870 var template = tpl || this.tabTpl || false;
37874 template = new Roo.Template(
37876 '<span unselectable="on"' +
37877 (this.disableTooltips ? '' : ' title="{text}"') +
37878 ' >{text}</span></a>'
37882 switch (typeof(template)) {
37886 template = new Roo.Template(template);
37892 var el = template.overwrite(td, {"text": text});
37894 var inner = el.getElementsByTagName("span")[0];
37896 return {"el": el, "inner": inner};
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)
37912 Roo.bootstrap.panel.TabItem = function(config){
37914 * The {@link Roo.TabPanel} this TabPanelItem belongs to
37915 * @type Roo.TabPanel
37917 this.tabPanel = config.panel;
37919 * The id for this TabPanelItem
37922 this.id = config.id;
37924 this.disabled = false;
37926 this.text = config.text;
37928 this.loaded = false;
37929 this.closable = config.closable;
37932 * The body element for this TabPanelItem.
37933 * @type Roo.Element
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();
37941 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
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);
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);
37960 * Fires when this tab becomes the active tab.
37961 * @param {Roo.TabPanel} tabPanel The parent TabPanel
37962 * @param {Roo.TabPanelItem} this
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.
37971 "beforeclose": true,
37974 * Fires when this tab is closed.
37975 * @param {Roo.TabPanelItem} this
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
37984 "deactivate" : true
37986 this.hidden = false;
37988 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37991 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37993 purgeListeners : function(){
37994 Roo.util.Observable.prototype.purgeListeners.call(this);
37995 this.el.removeAllListeners();
37998 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38001 this.pnode.addClass("active");
38004 this.tabPanel.stripWrap.repaint();
38006 this.fireEvent("activate", this.tabPanel, this);
38010 * Returns true if this tab is the active tab.
38011 * @return {Boolean}
38013 isActive : function(){
38014 return this.tabPanel.getActiveTab() == this;
38018 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38021 this.pnode.removeClass("active");
38023 this.fireEvent("deactivate", this.tabPanel, this);
38026 hideAction : function(){
38027 this.bodyEl.hide();
38028 this.bodyEl.setStyle("position", "absolute");
38029 this.bodyEl.setLeft("-20000px");
38030 this.bodyEl.setTop("-20000px");
38033 showAction : function(){
38034 this.bodyEl.setStyle("position", "relative");
38035 this.bodyEl.setTop("");
38036 this.bodyEl.setLeft("");
38037 this.bodyEl.show();
38041 * Set the tooltip for the tab.
38042 * @param {String} tooltip The tab's tooltip
38044 setTooltip : function(text){
38045 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38046 this.textEl.dom.qtip = text;
38047 this.textEl.dom.removeAttribute('title');
38049 this.textEl.dom.title = text;
38053 onTabClick : function(e){
38054 e.preventDefault();
38055 this.tabPanel.activate(this.id);
38058 onTabMouseDown : function(e){
38059 e.preventDefault();
38060 this.tabPanel.activate(this.id);
38063 getWidth : function(){
38064 return this.inner.getWidth();
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);
38075 * Show or hide the tab
38076 * @param {Boolean} hidden True to hide or false to show.
38078 setHidden : function(hidden){
38079 this.hidden = hidden;
38080 this.pnode.setStyle("display", hidden ? "none" : "");
38084 * Returns true if this tab is "hidden"
38085 * @return {Boolean}
38087 isHidden : function(){
38088 return this.hidden;
38092 * Returns the text for this tab
38095 getText : function(){
38099 autoSize : function(){
38100 //this.el.beginMeasure();
38101 this.textEl.setWidth(1);
38103 * #2804 [new] Tabs in Roojs
38104 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38106 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38107 //this.el.endMeasure();
38111 * Sets the text for the tab (Note: this also sets the tooltip text)
38112 * @param {String} text The tab's text and tooltip
38114 setText : function(text){
38116 this.textEl.update(text);
38117 this.setTooltip(text);
38118 //if(!this.tabPanel.resizeTabs){
38119 // this.autoSize();
38123 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38125 activate : function(){
38126 this.tabPanel.activate(this.id);
38130 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38132 disable : function(){
38133 if(this.tabPanel.active != this){
38134 this.disabled = true;
38135 this.pnode.addClass("disabled");
38140 * Enables this TabPanelItem if it was previously disabled.
38142 enable : function(){
38143 this.disabled = false;
38144 this.pnode.removeClass("disabled");
38148 * Sets the content for this TabPanelItem.
38149 * @param {String} content The content
38150 * @param {Boolean} loadScripts true to look for and load scripts
38152 setContent : function(content, loadScripts){
38153 this.bodyEl.update(content, loadScripts);
38157 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38158 * @return {Roo.UpdateManager} The UpdateManager
38160 getUpdateManager : function(){
38161 return this.bodyEl.getUpdateManager();
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
38171 setUrl : function(url, params, loadOnce){
38172 if(this.refreshDelegate){
38173 this.un('activate', this.refreshDelegate);
38175 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38176 this.on("activate", this.refreshDelegate);
38177 return this.bodyEl.getUpdateManager();
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));
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.
38193 refresh : function(){
38194 if(this.refreshDelegate){
38195 this.loaded = false;
38196 this.refreshDelegate();
38201 _setLoaded : function(){
38202 this.loaded = true;
38206 closeClick : function(e){
38209 this.fireEvent("beforeclose", this, o);
38210 if(o.cancel !== true){
38211 this.tabPanel.removeTab(this.id);
38215 * The text displayed in the tooltip for the close icon.
38218 closeText : "Close this tab"
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
38228 Roo.bootstrap.PhoneInputData = function() {
38231 "Afghanistan (افغانستان)",
38236 "Albania (Shqipëri)",
38241 "Algeria (الجزائر)",
38266 "Antigua and Barbuda",
38276 "Armenia (Հայաստան)",
38292 "Austria (Österreich)",
38297 "Azerbaijan (Azərbaycan)",
38307 "Bahrain (البحرين)",
38312 "Bangladesh (বাংলাদেশ)",
38322 "Belarus (Беларусь)",
38327 "Belgium (België)",
38357 "Bosnia and Herzegovina (Босна и Херцеговина)",
38372 "British Indian Ocean Territory",
38377 "British Virgin Islands",
38387 "Bulgaria (България)",
38397 "Burundi (Uburundi)",
38402 "Cambodia (កម្ពុជា)",
38407 "Cameroon (Cameroun)",
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"]
38419 "Cape Verde (Kabu Verdi)",
38424 "Caribbean Netherlands",
38435 "Central African Republic (République centrafricaine)",
38455 "Christmas Island",
38461 "Cocos (Keeling) Islands",
38472 "Comoros (جزر القمر)",
38477 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38482 "Congo (Republic) (Congo-Brazzaville)",
38502 "Croatia (Hrvatska)",
38523 "Czech Republic (Česká republika)",
38528 "Denmark (Danmark)",
38543 "Dominican Republic (República Dominicana)",
38547 ["809", "829", "849"]
38565 "Equatorial Guinea (Guinea Ecuatorial)",
38585 "Falkland Islands (Islas Malvinas)",
38590 "Faroe Islands (Føroyar)",
38611 "French Guiana (Guyane française)",
38616 "French Polynesia (Polynésie française)",
38631 "Georgia (საქართველო)",
38636 "Germany (Deutschland)",
38656 "Greenland (Kalaallit Nunaat)",
38693 "Guinea-Bissau (Guiné Bissau)",
38718 "Hungary (Magyarország)",
38723 "Iceland (Ísland)",
38743 "Iraq (العراق)",
38759 "Israel (ישראל)",
38786 "Jordan (الأردن)",
38791 "Kazakhstan (Казахстан)",
38812 "Kuwait (الكويت)",
38817 "Kyrgyzstan (Кыргызстан)",
38827 "Latvia (Latvija)",
38832 "Lebanon (لبنان)",
38847 "Libya (ليبيا)",
38857 "Lithuania (Lietuva)",
38872 "Macedonia (FYROM) (Македонија)",
38877 "Madagascar (Madagasikara)",
38907 "Marshall Islands",
38917 "Mauritania (موريتانيا)",
38922 "Mauritius (Moris)",
38943 "Moldova (Republica Moldova)",
38953 "Mongolia (Монгол)",
38958 "Montenegro (Crna Gora)",
38968 "Morocco (المغرب)",
38974 "Mozambique (Moçambique)",
38979 "Myanmar (Burma) (မြန်မာ)",
38984 "Namibia (Namibië)",
38999 "Netherlands (Nederland)",
39004 "New Caledonia (Nouvelle-Calédonie)",
39039 "North Korea (조선 민주주의 인민 공화국)",
39044 "Northern Mariana Islands",
39060 "Pakistan (پاکستان)",
39070 "Palestine (فلسطين)",
39080 "Papua New Guinea",
39122 "Réunion (La Réunion)",
39128 "Romania (România)",
39144 "Saint Barthélemy",
39155 "Saint Kitts and Nevis",
39165 "Saint Martin (Saint-Martin (partie française))",
39171 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39176 "Saint Vincent and the Grenadines",
39191 "São Tomé and Príncipe (São Tomé e Príncipe)",
39196 "Saudi Arabia (المملكة العربية السعودية)",
39201 "Senegal (Sénégal)",
39231 "Slovakia (Slovensko)",
39236 "Slovenia (Slovenija)",
39246 "Somalia (Soomaaliya)",
39256 "South Korea (대한민국)",
39261 "South Sudan (جنوب السودان)",
39271 "Sri Lanka (ශ්රී ලංකාව)",
39276 "Sudan (السودان)",
39286 "Svalbard and Jan Mayen",
39297 "Sweden (Sverige)",
39302 "Switzerland (Schweiz)",
39307 "Syria (سوريا)",
39352 "Trinidad and Tobago",
39357 "Tunisia (تونس)",
39362 "Turkey (Türkiye)",
39372 "Turks and Caicos Islands",
39382 "U.S. Virgin Islands",
39392 "Ukraine (Україна)",
39397 "United Arab Emirates (الإمارات العربية المتحدة)",
39419 "Uzbekistan (Oʻzbekiston)",
39429 "Vatican City (Città del Vaticano)",
39440 "Vietnam (Việt Nam)",
39445 "Wallis and Futuna (Wallis-et-Futuna)",
39450 "Western Sahara (الصحراء الغربية)",
39456 "Yemen (اليمن)",
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
39488 * @class Roo.bootstrap.PhoneInput
39489 * @extends Roo.bootstrap.TriggerField
39490 * An input with International dial-code selection
39492 * @cfg {String} defaultDialCode default '+852'
39493 * @cfg {Array} preferedCountries default []
39496 * Create a new PhoneInput.
39497 * @param {Object} config Configuration options
39500 Roo.bootstrap.PhoneInput = function(config) {
39501 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39504 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39506 listWidth: undefined,
39508 selectedClass: 'active',
39510 invalidClass : "has-warning",
39512 validClass: 'has-success',
39514 allowed: '0123456789',
39517 * @cfg {String} defaultDialCode The default dial code when initializing the input
39519 defaultDialCode: '+852',
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
39524 preferedCountries: false,
39526 getAutoCreate : function()
39528 var data = Roo.bootstrap.PhoneInputData();
39529 var align = this.labelAlign || this.parentLabelAlign();
39532 this.allCountries = [];
39533 this.dialCodeMapping = [];
39535 for (var i = 0; i < data.length; i++) {
39537 this.allCountries[i] = {
39541 priority: c[3] || 0,
39542 areaCodes: c[4] || null
39544 this.dialCodeMapping[c[2]] = {
39547 priority: c[3] || 0,
39548 areaCodes: c[4] || null
39560 cls : 'form-control tel-input',
39561 autocomplete: 'new-password'
39564 var hiddenInput = {
39567 cls: 'hidden-tel-input'
39571 hiddenInput.name = this.name;
39574 if (this.disabled) {
39575 input.disabled = true;
39578 var flag_container = {
39595 cls: this.hasFeedback ? 'has-feedback' : '',
39601 cls: 'dial-code-holder',
39608 cls: 'roo-select2-container input-group',
39615 if (this.fieldLabel.length) {
39618 tooltip: 'This field is required'
39624 cls: 'control-label',
39630 html: this.fieldLabel
39633 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39639 if(this.indicatorpos == 'right') {
39640 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39647 if(align == 'left') {
39655 if(this.labelWidth > 12){
39656 label.style = "width: " + this.labelWidth + 'px';
39658 if(this.labelWidth < 13 && this.labelmd == 0){
39659 this.labelmd = this.labelWidth;
39661 if(this.labellg > 0){
39662 label.cls += ' col-lg-' + this.labellg;
39663 input.cls += ' col-lg-' + (12 - this.labellg);
39665 if(this.labelmd > 0){
39666 label.cls += ' col-md-' + this.labelmd;
39667 container.cls += ' col-md-' + (12 - this.labelmd);
39669 if(this.labelsm > 0){
39670 label.cls += ' col-sm-' + this.labelsm;
39671 container.cls += ' col-sm-' + (12 - this.labelsm);
39673 if(this.labelxs > 0){
39674 label.cls += ' col-xs-' + this.labelxs;
39675 container.cls += ' col-xs-' + (12 - this.labelxs);
39685 var settings = this;
39687 ['xs','sm','md','lg'].map(function(size){
39688 if (settings[size]) {
39689 cfg.cls += ' col-' + size + '-' + settings[size];
39693 this.store = new Roo.data.Store({
39694 proxy : new Roo.data.MemoryProxy({}),
39695 reader : new Roo.data.JsonReader({
39706 'name' : 'dialCode',
39710 'name' : 'priority',
39714 'name' : 'areaCodes',
39721 if(!this.preferedCountries) {
39722 this.preferedCountries = [
39729 var p = this.preferedCountries.reverse();
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);
39743 this.store.proxy.data = {
39745 data: this.allCountries
39751 initEvents : function()
39754 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39756 this.indicator = this.indicatorEl();
39757 this.flag = this.flagEl();
39758 this.dialCodeHolder = this.dialCodeHolderEl();
39760 this.trigger = this.el.select('div.flag-box',true).first();
39761 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39766 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39767 _this.list.setWidth(lw);
39770 this.list.on('mouseover', this.onViewOver, this);
39771 this.list.on('mousemove', this.onViewMove, this);
39772 this.inputEl().on("keyup", this.onKeyUp, this);
39774 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39776 this.view = new Roo.View(this.list, this.tpl, {
39777 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39780 this.view.on('click', this.onViewClick, this);
39781 this.setValue(this.defaultDialCode);
39784 onTriggerClick : function(e)
39786 Roo.log('trigger click');
39791 if(this.isExpanded()){
39793 this.hasFocus = false;
39795 this.store.load({});
39796 this.hasFocus = true;
39801 isExpanded : function()
39803 return this.list.isVisible();
39806 collapse : function()
39808 if(!this.isExpanded()){
39812 Roo.get(document).un('mousedown', this.collapseIf, this);
39813 Roo.get(document).un('mousewheel', this.collapseIf, this);
39814 this.fireEvent('collapse', this);
39818 expand : function()
39822 if(this.isExpanded() || !this.hasFocus){
39826 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39827 this.list.setWidth(lw);
39830 this.restrictHeight();
39832 Roo.get(document).on('mousedown', this.collapseIf, this);
39833 Roo.get(document).on('mousewheel', this.collapseIf, this);
39835 this.fireEvent('expand', this);
39838 restrictHeight : function()
39840 this.list.alignTo(this.inputEl(), this.listAlign);
39841 this.list.alignTo(this.inputEl(), this.listAlign);
39844 onViewOver : function(e, t)
39846 if(this.inKeyMode){
39849 var item = this.view.findItemFromChild(t);
39852 var index = this.view.indexOf(item);
39853 this.select(index, false);
39858 onViewClick : function(view, doFocus, el, e)
39860 var index = this.view.getSelectedIndexes()[0];
39862 var r = this.store.getAt(index);
39865 this.onSelect(r, index);
39867 if(doFocus !== false && !this.blockFocus){
39868 this.inputEl().focus();
39872 onViewMove : function(e, t)
39874 this.inKeyMode = false;
39877 select : function(index, scrollIntoView)
39879 this.selectedIndex = index;
39880 this.view.select(index);
39881 if(scrollIntoView !== false){
39882 var el = this.view.getNode(index);
39884 this.list.scrollChildIntoView(el, false);
39889 createList : function()
39891 this.list = Roo.get(document.body).createChild({
39893 cls: 'typeahead typeahead-long dropdown-menu tel-list',
39894 style: 'display:none'
39896 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
39899 collapseIf : function(e)
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;
39905 if (in_combo || in_list || is_list) {
39911 onSelect : function(record, index)
39913 if(this.fireEvent('beforeselect', this, record, index) !== false){
39915 this.setFlagClass(record.data.iso2);
39916 this.setDialCode(record.data.dialCode);
39917 this.hasFocus = false;
39919 this.fireEvent('select', this, record, index);
39923 flagEl : function()
39925 var flag = this.el.select('div.flag',true).first();
39932 dialCodeHolderEl : function()
39934 var d = this.el.select('input.dial-code-holder',true).first();
39941 setDialCode : function(v)
39943 this.dialCodeHolder.dom.value = '+'+v;
39946 setFlagClass : function(n)
39948 this.flag.dom.className = 'flag '+n;
39951 getValue : function()
39953 var v = this.inputEl().getValue();
39954 if(this.dialCodeHolder) {
39955 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
39960 setValue : function(v)
39962 var d = this.getDialCode(v);
39964 //invalid dial code
39965 if(v.length == 0 || !d || d.length == 0) {
39967 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
39968 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
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();
39982 getDialCode : function(v = '')
39984 if (v.length == 0) {
39985 return this.dialCodeHolder.dom.value;
39989 if (v.charAt(0) != "+") {
39992 var numericChars = "";
39993 for (var i = 1; i < v.length; i++) {
39994 var c = v.charAt(i);
39997 if (this.dialCodeMapping[numericChars]) {
39998 dialCode = v.substr(1, i);
40000 if (numericChars.length == 4) {
40010 this.setValue(this.defaultDialCode);
40014 hiddenEl : function()
40016 return this.el.select('input.hidden-tel-input',true).first();
40019 onKeyUp : function(e){
40021 var k = e.getKey();
40022 var c = e.getCharCode();
40025 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40026 this.allowed.indexOf(String.fromCharCode(c)) === -1
40031 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40034 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40038 this.setValue(this.getValue());
40043 * @class Roo.bootstrap.MoneyField
40044 * @extends Roo.bootstrap.ComboBox
40045 * Bootstrap MoneyField class
40048 * Create a new MoneyField.
40049 * @param {Object} config Configuration options
40052 Roo.bootstrap.MoneyField = function(config) {
40054 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40058 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40061 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40063 allowDecimals : true,
40065 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40067 decimalSeparator : ".",
40069 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40071 decimalPrecision : 0,
40073 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40075 allowNegative : true,
40077 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40079 minValue : Number.NEGATIVE_INFINITY,
40081 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40083 maxValue : Number.MAX_VALUE,
40085 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40087 minText : "The minimum value for this field is {0}",
40089 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40091 maxText : "The maximum value for this field is {0}",
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")
40096 nanText : "{0} is not a valid number",
40098 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40102 * @cfg {String} defaults currency of the MoneyField
40103 * value should be in lkey
40105 defaultCurrency : false,
40107 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40109 thousandsDelimiter : false,
40119 getAutoCreate : function()
40121 var align = this.labelAlign || this.parentLabelAlign();
40133 cls : 'form-control roo-money-amount-input',
40134 autocomplete: 'new-password'
40137 var hiddenInput = {
40141 cls: 'hidden-number-input'
40145 hiddenInput.name = this.name;
40148 if (this.disabled) {
40149 input.disabled = true;
40152 var clg = 12 - this.inputlg;
40153 var cmd = 12 - this.inputmd;
40154 var csm = 12 - this.inputsm;
40155 var cxs = 12 - this.inputxs;
40159 cls : 'row roo-money-field',
40163 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40167 cls: 'roo-select2-container input-group',
40171 cls : 'form-control roo-money-currency-input',
40172 autocomplete: 'new-password',
40174 name : this.currencyName
40178 cls : 'input-group-addon',
40192 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40196 cls: this.hasFeedback ? 'has-feedback' : '',
40207 if (this.fieldLabel.length) {
40210 tooltip: 'This field is required'
40216 cls: 'control-label',
40222 html: this.fieldLabel
40225 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40231 if(this.indicatorpos == 'right') {
40232 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40239 if(align == 'left') {
40247 if(this.labelWidth > 12){
40248 label.style = "width: " + this.labelWidth + 'px';
40250 if(this.labelWidth < 13 && this.labelmd == 0){
40251 this.labelmd = this.labelWidth;
40253 if(this.labellg > 0){
40254 label.cls += ' col-lg-' + this.labellg;
40255 input.cls += ' col-lg-' + (12 - this.labellg);
40257 if(this.labelmd > 0){
40258 label.cls += ' col-md-' + this.labelmd;
40259 container.cls += ' col-md-' + (12 - this.labelmd);
40261 if(this.labelsm > 0){
40262 label.cls += ' col-sm-' + this.labelsm;
40263 container.cls += ' col-sm-' + (12 - this.labelsm);
40265 if(this.labelxs > 0){
40266 label.cls += ' col-xs-' + this.labelxs;
40267 container.cls += ' col-xs-' + (12 - this.labelxs);
40278 var settings = this;
40280 ['xs','sm','md','lg'].map(function(size){
40281 if (settings[size]) {
40282 cfg.cls += ' col-' + size + '-' + settings[size];
40289 initEvents : function()
40291 this.indicator = this.indicatorEl();
40293 this.initCurrencyEvent();
40295 this.initNumberEvent();
40298 initCurrencyEvent : function()
40301 throw "can not find store for combo";
40304 this.store = Roo.factory(this.store, Roo.data);
40305 this.store.parent = this;
40309 this.triggerEl = this.el.select('.input-group-addon', true).first();
40311 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40316 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40317 _this.list.setWidth(lw);
40320 this.list.on('mouseover', this.onViewOver, this);
40321 this.list.on('mousemove', this.onViewMove, this);
40322 this.list.on('scroll', this.onViewScroll, this);
40325 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40328 this.view = new Roo.View(this.list, this.tpl, {
40329 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40332 this.view.on('click', this.onViewClick, this);
40334 this.store.on('beforeload', this.onBeforeLoad, this);
40335 this.store.on('load', this.onLoad, this);
40336 this.store.on('loadexception', this.onLoadException, this);
40338 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40339 "up" : function(e){
40340 this.inKeyMode = true;
40344 "down" : function(e){
40345 if(!this.isExpanded()){
40346 this.onTriggerClick();
40348 this.inKeyMode = true;
40353 "enter" : function(e){
40356 if(this.fireEvent("specialkey", this, e)){
40357 this.onViewClick(false);
40363 "esc" : function(e){
40367 "tab" : function(e){
40370 if(this.fireEvent("specialkey", this, e)){
40371 this.onViewClick(false);
40379 doRelay : function(foo, bar, hname){
40380 if(hname == 'down' || this.scope.isExpanded()){
40381 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40389 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40393 initNumberEvent : function(e)
40395 this.inputEl().on("keydown" , this.fireKey, this);
40396 this.inputEl().on("focus", this.onFocus, this);
40397 this.inputEl().on("blur", this.onBlur, this);
40399 this.inputEl().relayEvent('keyup', this);
40401 if(this.indicator){
40402 this.indicator.addClass('invisible');
40405 this.originalValue = this.getValue();
40407 if(this.validationEvent == 'keyup'){
40408 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40409 this.inputEl().on('keyup', this.filterValidation, this);
40411 else if(this.validationEvent !== false){
40412 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40415 if(this.selectOnFocus){
40416 this.on("focus", this.preFocus, this);
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);
40422 this.inputEl().relayEvent('keypress', this);
40425 var allowed = "0123456789";
40427 if(this.allowDecimals){
40428 allowed += this.decimalSeparator;
40431 if(this.allowNegative){
40435 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40437 var keyPress = function(e){
40439 var k = e.getKey();
40441 var c = e.getCharCode();
40444 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40445 allowed.indexOf(String.fromCharCode(c)) === -1
40451 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40455 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40460 this.inputEl().on("keypress", keyPress, this);
40464 onTriggerClick : function(e)
40471 this.loadNext = false;
40473 if(this.isExpanded()){
40478 this.hasFocus = true;
40480 if(this.triggerAction == 'all') {
40481 this.doQuery(this.allQuery, true);
40485 this.doQuery(this.getRawValue());
40488 getCurrency : function()
40490 var v = this.currencyEl().getValue();
40495 restrictHeight : function()
40497 this.list.alignTo(this.currencyEl(), this.listAlign);
40498 this.list.alignTo(this.currencyEl(), this.listAlign);
40501 onViewClick : function(view, doFocus, el, e)
40503 var index = this.view.getSelectedIndexes()[0];
40505 var r = this.store.getAt(index);
40508 this.onSelect(r, index);
40512 onSelect : function(record, index){
40514 if(this.fireEvent('beforeselect', this, record, index) !== false){
40516 this.setFromCurrencyData(index > -1 ? record.data : false);
40520 this.fireEvent('select', this, record, index);
40524 setFromCurrencyData : function(o)
40528 this.lastCurrency = o;
40530 if (this.currencyField) {
40531 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40533 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40536 this.lastSelectionText = currency;
40538 //setting default currency
40539 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40540 this.setCurrency(this.defaultCurrency);
40544 this.setCurrency(currency);
40547 setFromData : function(o)
40551 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40553 this.setFromCurrencyData(c);
40558 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40560 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40563 this.setValue(value);
40567 setCurrency : function(v)
40569 this.currencyValue = v;
40572 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40577 setValue : function(v)
40579 v = this.fixPrecision(v);
40581 v = String(v).replace(".", this.decimalSeparator);
40587 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40589 this.inputEl().dom.value = Roo.util.Format.number(v, this.decimalPrecision,
40590 this.thousandsDelimiter || ','
40593 if(this.allowBlank && !v) {
40594 this.inputEl().dom.value = '';
40601 getRawValue : function()
40603 var v = this.inputEl().getValue();
40608 getValue : function()
40610 return this.fixPrecision(this.parseValue(this.getRawValue()));
40613 parseValue : function(value)
40615 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40616 return isNaN(value) ? '' : value;
40619 fixPrecision : function(value)
40621 var nan = isNaN(value);
40623 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40624 return nan ? '' : value;
40627 return parseFloat(value).toFixed(this.decimalPrecision);
40630 decimalPrecisionFcn : function(v)
40632 return Math.floor(v);
40635 validateValue : function(value)
40637 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40641 var num = this.parseValue(value);
40644 this.markInvalid(String.format(this.nanText, value));
40648 if(num < this.minValue){
40649 this.markInvalid(String.format(this.minText, this.minValue));
40653 if(num > this.maxValue){
40654 this.markInvalid(String.format(this.maxText, this.maxValue));
40661 validate : function()
40663 if(this.disabled || this.allowBlank){
40668 var currency = this.getCurrency();
40670 if(this.validateValue(this.getRawValue()) && currency.length){
40675 this.markInvalid();
40679 getName: function()
40684 beforeBlur : function()
40690 var v = this.parseValue(this.getRawValue());
40697 onBlur : function()
40701 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40702 //this.el.removeClass(this.focusClass);
40705 this.hasFocus = false;
40707 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40711 var v = this.getValue();
40713 if(String(v) !== String(this.startValue)){
40714 this.fireEvent('change', this, v, this.startValue);
40717 this.fireEvent("blur", this);
40720 inputEl : function()
40722 return this.el.select('.roo-money-amount-input', true).first();
40725 currencyEl : function()
40727 return this.el.select('.roo-money-currency-input', true).first();
40730 hiddenEl : function()
40732 return this.el.select('input.hidden-number-input',true).first();