mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-10-24 02:04:04 +08:00
Charts: Unify chart options
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
define([], function() {
|
||||
|
||||
return {
|
||||
title : '',
|
||||
columns : {
|
||||
y : {
|
||||
title : 'Values for y-axis'
|
||||
}
|
||||
},
|
||||
settings : {
|
||||
x_axis_label : {
|
||||
title : 'Axis label (x)',
|
||||
info : 'Provide a label for the \'x\' axis.',
|
||||
type : 'text',
|
||||
init : '',
|
||||
placeholder : 'Axis label'
|
||||
},
|
||||
y_axis_label : {
|
||||
title : 'Axis label (y)',
|
||||
info : 'Provide a label for the \'y\' axis.',
|
||||
type : 'text',
|
||||
init : '',
|
||||
placeholder : 'Axis label'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
// dependencies
|
||||
define([], function() {
|
||||
|
||||
// widget
|
||||
return Backbone.View.extend(
|
||||
{
|
||||
// initialize
|
||||
initialize: function(app, options) {
|
||||
this.app = app;
|
||||
this.options = options;
|
||||
},
|
||||
|
||||
// render
|
||||
draw : function(nvd3_model, chart, request_dictionary, callback)
|
||||
{
|
||||
// request data
|
||||
var self = this;
|
||||
this.app.datasets.request(request_dictionary, function(data) {
|
||||
nv.addGraph(function() {
|
||||
|
||||
nvd3_model.yAxis.tickFormat(d3.format('.1f'))
|
||||
.axisLabel(chart.settings.get('y_axis_label', 'Frequency'))
|
||||
.axisLabelDistance(30);
|
||||
|
||||
nvd3_model.xAxis.tickFormat(d3.format('.2f'))
|
||||
.axisLabel(chart.settings.get('x_axis_label', 'Breaks'));
|
||||
|
||||
if (callback) {
|
||||
callback(nvd3_model);
|
||||
}
|
||||
|
||||
self.options.svg.datum(data)
|
||||
.call(nvd3_model);
|
||||
|
||||
nv.utils.windowResize(nvd3_model.update);
|
||||
|
||||
// set chart state
|
||||
chart.set('state', 'ok');
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
// dependencies
|
||||
define([], function() {
|
||||
define(['plugin/charts/_nvd3/nvd3'], function(NVD3) {
|
||||
|
||||
// widget
|
||||
return Backbone.View.extend(
|
||||
@@ -13,28 +13,8 @@ return Backbone.View.extend(
|
||||
// render
|
||||
draw : function(chart, request_dictionary)
|
||||
{
|
||||
// request data
|
||||
var self = this;
|
||||
this.app.datasets.request(request_dictionary, function(data) {
|
||||
nv.addGraph(function() {
|
||||
self.d3_chart = nv.models.multiBarChart();
|
||||
|
||||
self.d3_chart.yAxis.tickFormat(d3.format('.1f'))
|
||||
.axisLabel(chart.settings.get('y_axis_label', 'Frequency'))
|
||||
.axisLabelDistance(30);
|
||||
|
||||
self.d3_chart.xAxis.tickFormat(d3.format('.2f'))
|
||||
.axisLabel(chart.settings.get('x_axis_label', 'Breaks'));
|
||||
|
||||
self.options.svg.datum(data)
|
||||
.call(self.d3_chart);
|
||||
|
||||
nv.utils.windowResize(self.d3_chart.update);
|
||||
|
||||
// set chart state
|
||||
chart.set('state', 'ok');
|
||||
});
|
||||
});
|
||||
var nvd3 = new NVD3(this.app, this.options);
|
||||
nvd3.draw(nv.models.multiBarChart(), chart, request_dictionary);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,28 +1,7 @@
|
||||
define([], function() {
|
||||
define(['plugin/charts/_nvd3/config'], function(nvd3_config) {
|
||||
|
||||
return {
|
||||
return $.extend(true, {}, nvd3_config, {
|
||||
title : 'Bar diagram',
|
||||
columns : {
|
||||
y : {
|
||||
title : 'Values for y-axis'
|
||||
}
|
||||
},
|
||||
settings : {
|
||||
x_axis_label : {
|
||||
title : 'Axis label (x)',
|
||||
info : 'Provide a label for the \'x\' axis.',
|
||||
type : 'text',
|
||||
init : '',
|
||||
placeholder : 'Axis label'
|
||||
},
|
||||
y_axis_label : {
|
||||
title : 'Axis label (y)',
|
||||
info : 'Provide a label for the \'y\' axis.',
|
||||
type : 'text',
|
||||
init : '',
|
||||
placeholder : 'Axis label'
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,21 +1,14 @@
|
||||
define([], function() {
|
||||
define(['plugin/charts/_nvd3/config'], function(nvd3_config) {
|
||||
|
||||
return {
|
||||
return $.extend(true, {}, nvd3_config, {
|
||||
title : 'Histogram',
|
||||
mode : 'execute',
|
||||
columns : {
|
||||
y : {
|
||||
title : 'Derive frequencies'
|
||||
x : {
|
||||
title : 'Values for x-axis'
|
||||
}
|
||||
},
|
||||
settings : {
|
||||
x_axis_label : {
|
||||
title : 'Axis label (x)',
|
||||
info : 'Provide a label for the \'x\' axis.',
|
||||
type : 'text',
|
||||
init : 'Breaks',
|
||||
placeholder : 'Axis label'
|
||||
},
|
||||
bin_size : {
|
||||
title : 'Number of Bins',
|
||||
info : 'Provide the number of histogram bins. The parsed data will be evenly distributed into bins according to the minimum and maximum values of the dataset.',
|
||||
@@ -25,6 +18,6 @@ return {
|
||||
max : 1000,
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
// dependencies
|
||||
define([], function() {
|
||||
define(['plugin/charts/_nvd3/nvd3'], function(NVD3) {
|
||||
|
||||
// widget
|
||||
return Backbone.View.extend(
|
||||
@@ -9,10 +9,10 @@ return Backbone.View.extend(
|
||||
this.app = app;
|
||||
this.options = options;
|
||||
},
|
||||
|
||||
// draw
|
||||
draw: function(chart, request_dictionary) {
|
||||
|
||||
|
||||
// render
|
||||
draw : function(chart, request_dictionary)
|
||||
{
|
||||
// update request dataset id
|
||||
request_dictionary.id = chart.get('dataset_id_job');
|
||||
|
||||
@@ -25,29 +25,11 @@ return Backbone.View.extend(
|
||||
y: index++
|
||||
}
|
||||
}
|
||||
|
||||
// send request
|
||||
var self = this;
|
||||
this.app.datasets.request(request_dictionary, function(data) {
|
||||
// set chart state
|
||||
chart.set('state', 'ok');
|
||||
|
||||
// draw graph
|
||||
nv.addGraph(function() {
|
||||
self.d3_chart = nv.models.multiBarChart();
|
||||
|
||||
self.d3_chart.xAxis.tickFormat(d3.format('.2f'))
|
||||
.axisLabel('Breaks');
|
||||
|
||||
self.d3_chart.yAxis.tickFormat(d3.format('.3f'))
|
||||
.axisLabel('Frequency')
|
||||
.axisLabelDistance(30);
|
||||
|
||||
self.options.svg.datum(data)
|
||||
.call(self.d3_chart);
|
||||
|
||||
nv.utils.windowResize(self.d3_chart.update);
|
||||
});
|
||||
|
||||
var nvd3 = new NVD3(this.app, this.options);
|
||||
nvd3.draw(nv.models.multiBarChart(), chart, request_dictionary, function(nvd3_model) {
|
||||
nvd3_model.xAxis.tickFormat(d3.format('.2f'));
|
||||
nvd3_model.yAxis.tickFormat(d3.format('.3f'));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
define([], function() {
|
||||
define(['plugin/charts/_nvd3/config'], function(nvd3_config) {
|
||||
|
||||
return {
|
||||
return $.extend(true, {}, nvd3_config, {
|
||||
title : 'Bar diagram (horizontal)',
|
||||
columns : {
|
||||
y : {
|
||||
title : 'Values for y-axis'
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
// dependencies
|
||||
define(['utils/utils'], function(Utils) {
|
||||
define(['plugin/charts/_nvd3/nvd3'], function(NVD3) {
|
||||
|
||||
// widget
|
||||
return Backbone.View.extend(
|
||||
@@ -13,23 +13,9 @@ return Backbone.View.extend(
|
||||
// render
|
||||
draw : function(chart, request_dictionary)
|
||||
{
|
||||
// request data
|
||||
var self = this;
|
||||
this.app.datasets.request(request_dictionary, function(data) {
|
||||
// add graph to screen
|
||||
nv.addGraph(function() {
|
||||
self.d3_chart = nv.models.multiBarHorizontalChart();
|
||||
|
||||
self.d3_chart.xAxis.tickFormat(function() { return ''; });
|
||||
|
||||
self.options.svg.datum(data)
|
||||
.call(self.d3_chart);
|
||||
|
||||
nv.utils.windowResize(self.d3_chart.update);
|
||||
|
||||
// set chart state
|
||||
chart.set('state', 'ok');
|
||||
});
|
||||
var nvd3 = new NVD3(this.app, this.options);
|
||||
nvd3.draw(nv.models.multiBarHorizontalChart(), chart, request_dictionary, function(nvd3_model) {
|
||||
nvd3_model.xAxis.tickFormat(function() { return ''; });
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
define([], function() {
|
||||
define(['plugin/charts/_nvd3/config'], function(nvd3_config) {
|
||||
|
||||
return {
|
||||
return $.extend(true, {}, nvd3_config, {
|
||||
title : 'Line chart',
|
||||
columns : {
|
||||
y : {
|
||||
title : 'Values for y-axis'
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
// dependencies
|
||||
define(['utils/utils'], function(Utils) {
|
||||
define(['plugin/charts/_nvd3/nvd3'], function(NVD3) {
|
||||
|
||||
// widget
|
||||
return Backbone.View.extend(
|
||||
@@ -13,27 +13,8 @@ return Backbone.View.extend(
|
||||
// render
|
||||
draw : function(chart, request_dictionary)
|
||||
{
|
||||
// request data
|
||||
var self = this;
|
||||
this.app.datasets.request(request_dictionary, function(data) {
|
||||
nv.addGraph(function() {
|
||||
self.chart_3d = nv.models.lineChart();
|
||||
|
||||
self.chart_3d.xAxis
|
||||
.tickFormat(d3.format(',f'));
|
||||
|
||||
self.chart_3d.yAxis
|
||||
.tickFormat(d3.format(',.2f'));
|
||||
|
||||
self.options.svg.datum(data)
|
||||
.call(self.chart_3d);
|
||||
|
||||
nv.utils.windowResize(self.chart_3d.update);
|
||||
|
||||
// set chart state
|
||||
chart.set('state', 'ok');
|
||||
});
|
||||
});
|
||||
var nvd3 = new NVD3(this.app, this.options);
|
||||
nvd3.draw(nv.models.lineChart(), chart, request_dictionary);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
define([], function() {
|
||||
define(['plugin/charts/_nvd3/config'], function(nvd3_config) {
|
||||
|
||||
return {
|
||||
title : 'Line chart (with focus)',
|
||||
columns : {
|
||||
y : {
|
||||
title : 'Values for y-axis'
|
||||
}
|
||||
}
|
||||
};
|
||||
return $.extend(true, {}, nvd3_config, {
|
||||
title : 'Line with focus',
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
// dependencies
|
||||
define(['utils/utils'], function(Utils) {
|
||||
define(['plugin/charts/_nvd3/nvd3'], function(NVD3) {
|
||||
|
||||
// widget
|
||||
return Backbone.View.extend(
|
||||
@@ -13,27 +13,8 @@ return Backbone.View.extend(
|
||||
// render
|
||||
draw : function(chart, request_dictionary)
|
||||
{
|
||||
// request data
|
||||
var self = this;
|
||||
this.app.datasets.request(request_dictionary, function(data) {
|
||||
nv.addGraph(function() {
|
||||
self.chart_3d = nv.models.lineWithFocusChart();
|
||||
|
||||
self.chart_3d.xAxis
|
||||
.tickFormat(d3.format(',f'));
|
||||
|
||||
self.chart_3d.yAxis
|
||||
.tickFormat(d3.format(',.2f'));
|
||||
|
||||
self.options.svg.datum(data)
|
||||
.call(self.chart_3d);
|
||||
|
||||
nv.utils.windowResize(self.chart_3d.update);
|
||||
|
||||
// set chart state
|
||||
chart.set('state', 'ok');
|
||||
});
|
||||
});
|
||||
var nvd3 = new NVD3(this.app, this.options);
|
||||
nvd3.draw(nv.models.lineWithFocusChart(), chart, request_dictionary);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
define([], function() {
|
||||
define(['plugin/charts/_nvd3/config'], function(nvd3_config) {
|
||||
|
||||
return {
|
||||
return $.extend(true, {}, nvd3_config, {
|
||||
title : 'Pie chart',
|
||||
columns : {
|
||||
y : {
|
||||
title : 'Values for y-axis'
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,15 +1,12 @@
|
||||
define([], function() {
|
||||
define(['plugin/charts/_nvd3/config'], function(nvd3_config) {
|
||||
|
||||
return {
|
||||
return $.extend(true, {}, nvd3_config, {
|
||||
title : 'Scatter plot',
|
||||
columns : {
|
||||
x : {
|
||||
title : 'Values for x-axis'
|
||||
},
|
||||
y : {
|
||||
title : 'Values for y-axis'
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
// dependencies
|
||||
define(['utils/utils'], function(Utils) {
|
||||
define(['plugin/charts/_nvd3/nvd3'], function(NVD3) {
|
||||
|
||||
// widget
|
||||
return Backbone.View.extend(
|
||||
@@ -13,26 +13,14 @@ return Backbone.View.extend(
|
||||
// render
|
||||
draw : function(chart, request_dictionary)
|
||||
{
|
||||
// request data
|
||||
var self = this;
|
||||
this.app.datasets.request(request_dictionary, function(data) {
|
||||
nv.addGraph(function() {
|
||||
self.d3_chart = nv.models.scatterChart()
|
||||
.showDistX(true)
|
||||
.showDistY(true)
|
||||
.color(d3.scale.category10().range());
|
||||
|
||||
self.d3_chart.xAxis.tickFormat(d3.format('.02f'))
|
||||
self.d3_chart.yAxis.tickFormat(d3.format('.02f'))
|
||||
var nvd3 = new NVD3(this.app, this.options);
|
||||
nvd3.draw(nv.models.scatterChart(), chart, request_dictionary, function(nvd3_model) {
|
||||
nvd3_model.showDistX(true)
|
||||
.showDistY(true)
|
||||
.color(d3.scale.category10().range());
|
||||
nvd3_model.xAxis.tickFormat(d3.format('.02f'));
|
||||
nvd3_model.yAxis.tickFormat(d3.format('.02f'));
|
||||
|
||||
self.options.svg.datum(data)
|
||||
.call(self.d3_chart);
|
||||
|
||||
nv.utils.windowResize(self.d3_chart.update);
|
||||
|
||||
// set chart state
|
||||
chart.set('state', 'ok');
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
define([], function() {
|
||||
define(['plugin/charts/_nvd3/config'], function(nvd3_config) {
|
||||
|
||||
return {
|
||||
return $.extend(true, {}, nvd3_config, {
|
||||
title : 'Stacked area',
|
||||
columns : {
|
||||
y : {
|
||||
title : 'Values for y-axis'
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
// dependencies
|
||||
define(['utils/utils'], function(Utils) {
|
||||
define(['plugin/charts/_nvd3/nvd3'], function(NVD3) {
|
||||
|
||||
// widget
|
||||
return Backbone.View.extend(
|
||||
@@ -13,30 +13,13 @@ return Backbone.View.extend(
|
||||
// render
|
||||
draw : function(chart, request_dictionary)
|
||||
{
|
||||
// request data
|
||||
var self = this;
|
||||
this.app.datasets.request(request_dictionary, function(data) {
|
||||
nv.addGraph(function() {
|
||||
// make plot
|
||||
self.d3_chart = nv.models.stackedAreaChart()
|
||||
.x(function(d) {
|
||||
return d.x
|
||||
})
|
||||
.y(function(d) {
|
||||
return d.y
|
||||
})
|
||||
.clipEdge(true);
|
||||
|
||||
self.d3_chart.xAxis.tickFormat(function() { return ''; });
|
||||
|
||||
self.options.svg.datum(data)
|
||||
.call(self.d3_chart);
|
||||
|
||||
nv.utils.windowResize(self.d3_chart.update);
|
||||
|
||||
// set chart state
|
||||
chart.set('state', 'ok');
|
||||
});
|
||||
var nvd3 = new NVD3(this.app, this.options);
|
||||
nvd3.draw(nv.models.stackedAreaChart(), chart, request_dictionary, function(nvd3_model) {
|
||||
// make plot
|
||||
nvd3_model.x(function(d) { return d.x })
|
||||
.y(function(d) { return d.y })
|
||||
.clipEdge(true);
|
||||
nvd3_model.xAxis.tickFormat(function() { return ''; });
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
/**
|
||||
* Galaxy utilities comprises small functions, which at this point
|
||||
* do not require their own classes/files
|
||||
*/
|
||||
|
||||
// dependencies
|
||||
define(["libs/underscore"], function(_) {
|
||||
|
||||
// generic function to recieve json from url
|
||||
function get (url, success, error) {
|
||||
request('GET', url, {}, success, error);
|
||||
};
|
||||
|
||||
// generic function to send json to url
|
||||
function request (method, url, data, success, error) {
|
||||
|
||||
// encode data into url
|
||||
if (method == 'GET' || method == 'DELETE') {
|
||||
if (url.indexOf('?') == -1) {
|
||||
url += '?';
|
||||
} else {
|
||||
url += '&';
|
||||
}
|
||||
url += $.param(data)
|
||||
}
|
||||
|
||||
// prepare request
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open(method, url, true);
|
||||
xhr.setRequestHeader('Accept', 'application/json');
|
||||
xhr.setRequestHeader('Cache-Control', 'no-cache');
|
||||
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
|
||||
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||
xhr.onloadend = function() {
|
||||
var status = xhr.status;
|
||||
if (status == 200) {
|
||||
try {
|
||||
response = jQuery.parseJSON(xhr.responseText);
|
||||
} catch (e) {
|
||||
response = xhr.responseText;
|
||||
}
|
||||
success && success(response);
|
||||
} else {
|
||||
error && error(status);
|
||||
}
|
||||
};
|
||||
|
||||
// make request
|
||||
if (method == 'GET' || method == 'DELETE') {
|
||||
xhr.send();
|
||||
} else {
|
||||
xhr.send(JSON.stringify(data));
|
||||
}
|
||||
};
|
||||
|
||||
// get css value
|
||||
function cssGetAttribute (classname, name) {
|
||||
// place dummy element
|
||||
var el = $('<div class="' + classname + '"></div>');
|
||||
|
||||
// required append
|
||||
el.appendTo(':eq(0)');
|
||||
|
||||
// get value
|
||||
var value = el.css(name);
|
||||
|
||||
// remove element
|
||||
el.remove();
|
||||
|
||||
// return css value
|
||||
return value;
|
||||
};
|
||||
|
||||
// load css
|
||||
function cssLoadFile (url) {
|
||||
// check if css is already available
|
||||
if (!$('link[href^="' + url + '"]').length)
|
||||
$('<link href="' + galaxy_config.root + url + '" rel="stylesheet">').appendTo('head');
|
||||
};
|
||||
|
||||
// merge
|
||||
function merge (options, optionsDefault) {
|
||||
if (options)
|
||||
return _.defaults(options, optionsDefault);
|
||||
else
|
||||
return optionsDefault;
|
||||
};
|
||||
|
||||
// to string
|
||||
function bytesToString (size, normal_font) {
|
||||
// identify unit
|
||||
var unit = "";
|
||||
if (size >= 100000000000) { size = size / 100000000000; unit = 'TB'; } else
|
||||
if (size >= 100000000) { size = size / 100000000; unit = 'GB'; } else
|
||||
if (size >= 100000) { size = size / 100000; unit = 'MB'; } else
|
||||
if (size >= 100) { size = size / 100; unit = 'KB'; } else
|
||||
if (size > 0) { size = size * 10; unit = 'b'; } else
|
||||
return '<strong>-</strong>';
|
||||
|
||||
// return formatted string
|
||||
var rounded = (Math.round(size) / 10);
|
||||
if (normal_font) {
|
||||
return rounded + ' ' + unit;
|
||||
} else {
|
||||
return '<strong>' + rounded + '</strong> ' + unit;
|
||||
}
|
||||
};
|
||||
|
||||
// unique ide
|
||||
function uuid(){
|
||||
return (new Date().getTime()).toString(36);
|
||||
};
|
||||
|
||||
// wrap
|
||||
function wrap($el) {
|
||||
var wrapper = $('<p></p>');
|
||||
wrapper.append($el);
|
||||
return wrapper;
|
||||
};
|
||||
|
||||
// time
|
||||
function time() {
|
||||
// get date object
|
||||
var d = new Date();
|
||||
|
||||
// format items
|
||||
var hours = (d.getHours() < 10 ? "0" : "") + d.getHours();
|
||||
var minutes = (d.getMinutes() < 10 ? "0" : "") + d.getMinutes()
|
||||
|
||||
// format final stamp
|
||||
var datetime = d.getDate() + "/"
|
||||
+ (d.getMonth() + 1) + "/"
|
||||
+ d.getFullYear() + ", "
|
||||
+ hours + ":"
|
||||
+ minutes;
|
||||
return datetime;
|
||||
};
|
||||
|
||||
// return
|
||||
return {
|
||||
cssLoadFile : cssLoadFile,
|
||||
cssGetAttribute : cssGetAttribute,
|
||||
get : get,
|
||||
merge : merge,
|
||||
bytesToString: bytesToString,
|
||||
uuid: uuid,
|
||||
time: time,
|
||||
wrap: wrap,
|
||||
request: request
|
||||
};
|
||||
|
||||
});
|
||||
@@ -2,7 +2,7 @@
|
||||
define(['plugin/library/table', 'plugin/library/ui', 'utils/utils'],
|
||||
function(Table, Ui, Utils) {
|
||||
|
||||
// chart config
|
||||
// widget
|
||||
return Backbone.View.extend(
|
||||
{
|
||||
// columns
|
||||
|
||||
Reference in New Issue
Block a user