Select Git revision
komise.js 22.81 KiB
class Komise extends Template {
description = "Určeno pro sociální sítě.";
changeableAttributes = [
"logoImage",
"logoIsCenter",
"primaryImage",
"primaryText",
"secondaryText",
"nameText",
"underNameText",
"primaryColorScheme",
"primaryImagePosition",
"iconImage"
];
primaryColorSchemes = [
"black-on-white",
"white-on-black"
];
primaryTextHighlightColorSchemes = [
"black-on-gold"
];
changeableColors = [
"primaryTextColor",
"foregroundColor",
"primaryTextHighlightColor",
"requesterTextColor"
];
iconImage = null;
iconSource = "";
logoIsCenter = false;
aspectRatio = 1;
defaultResolution = 2000;
secondaryText = "";
underNameText = "";
// Canvas
async redrawCanvas() {
if (this.redrawing) {
return;
}
this.redrawing = true;
const primaryRectangleAngle = Math.ceil(this.canvas.height * 0.015);
const primaryRectanglePaddingBottom = Math.ceil(this.canvas.height * 0.01);
const primaryRectanglePaddingTop = 0;
const primaryRectangleAdditionalPaddingWithDiacritics = Math.ceil(this.canvas.height * 0.01);
const primaryRectanglePaddingSides = Math.ceil(this.canvas.width * 0.1);
const highlightPaddingSides = Math.ceil(this.canvas.width * 0.01);
const highlightPaddingBottom = Math.ceil(this.canvas.height * 0.0125);
const highlightPaddingTop = Math.ceil(this.canvas.height * -0.01); // It is, it's how Roboto works.
const logoHeight = Math.ceil(this.canvas.height * 0.055) * this.logoImageZoom;
const logoBottomOffset = Math.ceil(this.canvas.height * 0.06) - (
logoHeight / this.logoImageZoom * (this.logoImageZoom - 1) / 2
);
const logoTextOffsetSide = Math.ceil(this.canvas.width * 0.1);
const nameTextOffsetBottom = Math.ceil(this.canvas.height * 0.08);
let primaryFontSize = Math.ceil(this.canvas.height * 0.13);
const primaryFontLinePadding = 0;
const primaryTextMaxLines = 3;
const primaryTextPaddingBottom = Math.ceil(this.canvas.height * 0.17);
let secondaryFontSize = Math.ceil(this.canvas.height * 0.06);
const secondaryTextPaddingBottom = Math.ceil(this.canvas.height * 0.04);
let nameFontSize = Math.ceil(this.canvas.height * 0.028);
let underNameFontSize = Math.ceil(this.canvas.height * 0.018);
const nameMaxArea = Math.ceil(this.canvas.width * 0.24);
const iconImageOffset = Math.ceil(this.canvas.height * 0.03);
const iconOpacity = 0.125;
// Get primary text split into lines, no more than ``primaryTextMaxLines`` of them
let primaryTextLines = null;
do {
this.context.font = `${this.primaryFontStyle} ${primaryFontSize}px ${this.primaryFont}`;
primaryTextLines = splitStringIntoLines(
this.context,
this.primaryText,
this.canvas.width - primaryRectanglePaddingSides * 2,
primaryTextMaxLines,
true
).reverse();
if (primaryTextLines.length > primaryTextMaxLines) {
primaryFontSize -= 2;
}
} while (primaryTextLines.length > primaryTextMaxLines);
// Clear the canvas
this.context.clearRect(
0, 0,
this.canvas.width, this.canvas.height
);
// Set image
if (this.primaryImage !== null) {
// https://github.com/DonkeyDushan/piratilol/blob/main/src/js/index.js
// Thanks to DonkeyDushan, the guy who made the joke 2021 campaign generator :D
const imageScaleX = this.canvas.width / this.primaryImage.width;
const imageScaleY = this.canvas.height / this.primaryImage.height;
const imageScale = Math.max(imageScaleX, imageScaleY) * this.primaryImageZoom;
// https://stackoverflow.com/a/8529655
// Thanks to alex!
this.context.setTransform(
imageScale,
0, 0,
imageScale,
(this.canvas.width - this.primaryImage.width * imageScale) / 2 + this.primaryImageX * this.primaryImageZoom,
(this.canvas.height - this.primaryImage.height * imageScale) / 2 + this.primaryImageY * this.primaryImageZoom
);
this.context.drawImage(
this.primaryImage,
0, 0
);
this.context.setTransform(); // Reset transformation
}
const firstPrimaryLine = primaryTextLines[primaryTextLines.length - 1].join(" ");
// Create rectangle behind the primary text
const primaryRectangleHeight = (
primaryTextLines.length * (primaryFontSize + primaryFontLinePadding)
+ primaryRectanglePaddingTop
+ primaryRectanglePaddingBottom
+ primaryTextPaddingBottom
+ (
(
firstPrimaryLine.replace(/[a-zA-Z0-9À-ž]+/g, "").length
!== firstPrimaryLine.replace(/[a-zA-Z0-9]+/g, "").length
) ?
primaryRectangleAdditionalPaddingWithDiacritics :
0
)
);
const classRef = this;
// Create background gradient
const gradientLoadPromise = new Promise(
resolve => {
const gradientImage = new Image();
gradientImage.onload = function() {
classRef.context.drawImage(
this,
0, 0,
classRef.canvas.width, classRef.canvas.height - primaryRectangleHeight
);
resolve();
}
gradientImage.src = "static/images/gradient.png";
}
);
await gradientLoadPromise;
if (this.secondaryText !== "") {
this.context.textAlign = "center";
this.context.font = `${secondaryFontSize}px ${this.primaryFont}`;
while (
this.context.measureText(this.secondaryText).width
> (this.canvas.width - 2 * primaryRectanglePaddingSides)
) {
secondaryFontSize -= 2;
this.context.font = `${secondaryFontSize}px ${this.primaryFont}`;
}
this.context.fillStyle = this.secondaryTextColor;
this.context.fillText(
this.secondaryText,
this.canvas.width / 2, this.canvas.height - primaryRectangleHeight - secondaryTextPaddingBottom
);
}
this.context.font = `${this.primaryFontStyle} ${primaryFontSize}px ${this.primaryFont}`;
const foregroundRGB = hexToRgb(this.foregroundColor);
const foregroundLightness = (
0.2126 * foregroundRGB.r
+ 0.7152 * foregroundRGB.g
+ 0.0722 * foregroundRGB.b
)
this.context.beginPath();
const primaryRectangleStartingX = 0;
const primaryRectangleEndingX = this.canvas.width;
this.context.fillStyle = this.foregroundColor;
this.context.moveTo(
0,
this.canvas.height
);
this.context.lineTo(
this.canvas.width,
this.canvas.height
);
this.context.lineTo(
this.canvas.width,
this.canvas.height - primaryRectangleHeight - primaryRectangleAngle
);
this.context.lineTo(
0,
this.canvas.height - primaryRectangleHeight
);
this.context.closePath();
this.context.fill();
function drawIconImage(image) {
const iconHeight = primaryRectangleHeight - 2 * iconImageOffset;
const iconWidth = (image.width * (iconHeight / image.height));
classRef.context.globalAlpha = iconOpacity;
const primaryTextRGB = hexToRgb(classRef.primaryTextColor);
classRef.context.drawImage(
colorizeImage(
image,
iconWidth, iconHeight,
primaryTextRGB.r,
primaryTextRGB.g,
primaryTextRGB.b
),
iconImageOffset, classRef.canvas.height - iconImageOffset - iconHeight,
iconWidth, iconHeight
);
classRef.context.globalAlpha = 1;
}
// Create icon, if there is one
if (this.iconImage !== null) {
drawIconImage(this.iconImage);
} else if (this.iconSource !== null) {
const iconImageLoadPromise = new Promise(
resolve => {
const iconImage = new Image();
iconImage.onload = function() {
drawIconImage(this);
resolve();
}
iconImage.src = this.iconSource;
}
);
await iconImageLoadPromise;
}
// Create primary text
this.context.textAlign = "left";
const useLightHighlightAndUseDarkLogo = (foregroundLightness > 207);
const primaryLineX = this.canvas.width / 2;
let currentPrimaryLineY = (
this.canvas.height
- primaryRectanglePaddingBottom
- primaryFontLinePadding
- primaryTextPaddingBottom
);
let primaryTextHighlightedColor = null;
const lowercasePrimaryTextHighlightColor = this.primaryTextHighlightColor.toLowerCase();
const hasColorOverride = (
lowercasePrimaryTextHighlightColor === "#209a37" ||
lowercasePrimaryTextHighlightColor === "#e63812"
);
if (hasColorOverride) {
if (useLightHighlightAndUseDarkLogo) {
primaryTextHighlightedColor = this.foregroundColor;
} else {
primaryTextHighlightedColor = this.primaryTextColor;
}
} else if (!useLightHighlightAndUseDarkLogo) {
primaryTextHighlightedColor = this.foregroundColor;
} else {
primaryTextHighlightedColor = this.primaryTextColor;
}
this.context.fillStyle = this.primaryTextColor;
for (let line of primaryTextLines) {
let wordPosition = 0;
for (let word of line) {
const previousWords = line.slice(0, wordPosition).join(" ");
const previousWordsWidth = this.context.measureText(
previousWords
+ (
(previousWords.length !== 0) ?
" " : ""
)
).width;
const nextWords = line.slice(wordPosition + 1, line.length).join(" ")
const nextWordsWidth = this.context.measureText(
nextWords
+ (
(nextWords.length !== 0) ?
" " : ""
)
).width;
let currentWordWidth = this.context.measureText(word).width;
for (const word of line.slice(wordPosition + 1, line.length)) {
if (word.isHighlighted) {
currentWordWidth += this.context.measureText(word.toString() + " ").width;
} else {
break;
}
}
if (word.isHighlighted) {
if (
wordPosition === 0 ||
!line[wordPosition - 1].isHighlighted
) {
this.context.fillStyle = this.primaryTextHighlightColor;
this.context.beginPath();
const startingHighlightLineX = (
primaryLineX
+ Math.ceil(previousWordsWidth / 2)
- Math.ceil(nextWordsWidth / 2)
- Math.ceil(this.context.measureText(word).width / 2)
);
this.context.moveTo(
startingHighlightLineX - highlightPaddingSides,
currentPrimaryLineY + highlightPaddingBottom
);
this.context.lineTo(
(
startingHighlightLineX
+ currentWordWidth
+ highlightPaddingSides
),
(
currentPrimaryLineY
+ highlightPaddingBottom
- Math.max(
(currentWordWidth * primaryRectangleAngle)
/ (this.canvas.width - 2 * primaryRectanglePaddingSides)
)
)
);
this.context.lineTo(
(
startingHighlightLineX
+ currentWordWidth
+ highlightPaddingSides
),
(
currentPrimaryLineY
- primaryFontSize
- highlightPaddingTop
- Math.max(
(currentWordWidth * primaryRectangleAngle)
/ (this.canvas.width - 2 * primaryRectanglePaddingSides)
)
)
);
this.context.lineTo(
startingHighlightLineX - highlightPaddingSides,
(
currentPrimaryLineY
- primaryFontSize
- highlightPaddingTop
)
);
this.context.closePath();
this.context.fill();
}
this.context.fillStyle = primaryTextHighlightedColor;
}
this.context.fillText(
word + " ",
(
primaryLineX
+ Math.ceil(previousWordsWidth / 2)
- Math.ceil(nextWordsWidth / 2)
- Math.ceil(this.context.measureText(word).width / 2)
),
currentPrimaryLineY
);
wordPosition++;
this.context.fillStyle = this.primaryTextColor;
}
currentPrimaryLineY -= (primaryFontSize + primaryFontLinePadding);
}
this.context.textAlign = "center";
// Create name, if not empty
if (this.nameText !== "") {
// Create rectangle for name text
this.context.font = `bold ${nameFontSize}px 'Roboto Condensed'`;
while (this.context.measureText(this.nameText).width > nameMaxArea) {
nameFontSize -= 2;
this.context.font = `bold ${nameFontSize}px 'Roboto Condensed'`;
}
this.context.textAlign = "left";
// Create name text itself
this.context.fillStyle = this.primaryTextColor;
this.context.fillText(
this.nameText,
logoTextOffsetSide, this.canvas.height - nameTextOffsetBottom
);
if (this.underNameText !== "") {
this.context.font = `${underNameFontSize}px 'Roboto Condensed'`;
while (this.context.measureText(this.underNameText).width > nameMaxArea) {
underNameFontSize -= 2;
this.context.font = `${underNameFontSize}px 'Roboto Condensed'`;
}
this.context.fillText(
this.underNameText,
logoTextOffsetSide, this.canvas.height - nameTextOffsetBottom + nameFontSize
);
}
}
function drawLogoImage(image) {
const logoWidth = Math.ceil(image.width * (logoHeight / image.height));
classRef.context.drawImage(
image,
(
(!classRef.logoIsCenter) ?
classRef.canvas.width - logoWidth - logoTextOffsetSide :
(classRef.canvas.width - logoWidth) / 2
), classRef.canvas.height - logoHeight - logoBottomOffset,
logoWidth, logoHeight
);
}
if (this.logoImage === null) {
const logoImageLoadPromise = new Promise(
resolve => {
let logoImage = new Image();
logoImage.onload = function() {
drawLogoImage(this);
resolve();
}
if (!useLightHighlightAndUseDarkLogo) {
logoImage.src = classRef.lightLogoDefaultSource;
} else {
logoImage.src = classRef.darkLogoDefaultSource;
}
}
);
await logoImageLoadPromise;
} else {
drawLogoImage(this.logoImage);
}
if (this.requesterText !== "") {
// https://newspaint.wordpress.com/2014/05/22/writing-rotated-text-on-a-javascript-canvas/
// Thanks to newspaint!
this.context.save();
this.context.translate(this.canvas.width - 1, 0);
this.context.rotate(3 * Math.PI / 2);
let requesterFontSize = Math.ceil(this.canvas.width * 0.0175);
do {
this.context.font = `${this.primaryFontStyle} ${requesterFontSize}px ${this.primaryFont}`;
if (
this.context.measureText(this.requesterText).width
> primaryRectangleHeight - (this.canvas.height * 0.03)
) {
requesterFontSize -= 2;
this.context.font = `${this.primaryFontStyle} ${requesterFontSize}px ${this.primaryFont}`;
}
} while (
this.context.measureText(this.requesterText).width
> primaryRectangleHeight - (this.canvas.height * 0.03)
);
this.context.fillStyle = this.requesterTextColor;
this.context.textAlign = "left";
this.context.globalAlpha = 0.6;
this.context.fillText(
this.requesterText,
-this.canvas.height * 0.985, -this.canvas.width * 0.99 + requesterFontSize
);
this.context.globalAlpha = 1;
this.context.restore();
}
this.finalDrawHook();
this.stickerDrawHook();
this.redrawing = false;
}
// Text
async setSecondaryText(text, skipRedraw = false) {
this.secondaryText = text;
if (!skipRedraw) {
await this.redrawCanvas();
}
}
async setUnderNameText(text, skipRedraw = false) {
this.underNameText = text;
if (!skipRedraw) {
await this.redrawCanvas();
}
}
// Icon
async setIconSource(url, skipRedraw = false) {
this.iconSource = url;
if (!skipRedraw) {
await this.redrawCanvas();
}
}
async setIconImageFromInput(imageInput, skipRedraw = false) {
if (imageInput.files.length == 0) {
return;
}
const readPromise = new Promise(
resolve => {
const fileReader = new FileReader();
let classRef = this;
fileReader.onloadend = function(event) {
classRef.iconImage = new Image();
classRef.iconImage.onload = function() {
if (!skipRedraw) {
classRef.redrawCanvas();
}
resolve();
}
classRef.iconImage.src = event.target.result;
}
fileReader.readAsDataURL(imageInput.files[0]);
}
);
await readPromise;
}
async resetIconImage(skipRedraw = false) {
this.iconImage = null;
if (!skipRedraw) {
await this.redrawCanvas();
}
}
// Color schemes
async setPrimaryColorScheme(scheme, skipRedraw = false) {
switch (scheme) {
case "black-on-white":
this.primaryTextColor = "#000000";
this.secondaryTextColor = "#ffffff";
this.foregroundColor = "#ffffff";
this.setPrimaryTextHighlightColorScheme("gold", true);
break;
case "white-on-black":
this.primaryTextColor = "#ffffff";
this.secondaryTextColor = "#ffffff";
this.foregroundColor ="#000000";
this.setPrimaryTextHighlightColorScheme("gold", true);
break;
case "forum-black-on-white":
this.primaryTextColor = "#000000";
this.secondaryTextColor = "#ffffff";
this.foregroundColor = "#ffffff";
this.setPrimaryTextHighlightColorScheme("gold", true);
break;
case "forum-white-on-purple":
this.primaryTextColor = "#ffffff";
this.secondaryTextColor = "#ffffff";
this.foregroundColor = "#962a51";
this.setPrimaryTextHighlightColorScheme("gold", true);
break;
case "zeleni-volary-bystrc-most-black-on-white":
this.primaryTextColor = "#000000";
this.secondaryTextColor = "#ffffff";
this.foregroundColor = "#ffffff";
this.setPrimaryTextHighlightColorScheme("gold", true);
break;
case "zeleni-volary-bystrc-most-white-on-green":
this.primaryTextColor = "#ffffff";
this.secondaryTextColor = "#ffffff";
this.foregroundColor = "#00ad43";
this.setPrimaryTextHighlightColorScheme("gold", true);
break;
case "spolecne-s-piraty-black-on-white":
this.primaryTextColor = "#000000";
this.secondaryTextColor = "#ffffff";
this.foregroundColor = "#ffffff";
this.setPrimaryTextHighlightColorScheme("gold", true);
break;
case "spolecne-s-piraty-white-on-blue":
this.primaryTextColor = "#ffffff";
this.secondaryTextColor = "#ffffff";
this.foregroundColor = "#21274e";
this.setPrimaryTextHighlightColorScheme("gold", true);
break;
case "louny-spolecne-black-on-white":
this.primaryTextColor = "#000000";
this.secondaryTextColor = "#ffffff";
this.foregroundColor = "#ffffff";
this.setPrimaryTextHighlightColorScheme("gold", true);
break;
case "louny-spolecne-white-on-purple":
this.primaryTextColor = "#ffffff";
this.secondaryTextColor = "#ffffff";
this.foregroundColor = "#3e2a5b";
this.setPrimaryTextHighlightColorScheme("gold", true);
break;
case "litomerice-blue-on-white":
this.primaryTextColor = "#123172";
this.secondaryTextColor = "#ffffff";
this.foregroundColor = "#ffffff";
this.setPrimaryTextHighlightColorScheme("litomerice", true);
break;
case "litomerice-white-on-blue":
this.primaryTextColor = "#ffffff";
this.secondaryTextColor = "#ffffff";
this.foregroundColor = "#123172";
this.setPrimaryTextHighlightColorScheme("litomerice", true);
break;
case "stranane-gray-on-yellow":
this.primaryTextColor = "#4d4d4d";
this.secondaryTextColor = "#ffffff";
this.foregroundColor = "#ffd500";
break;
case "stranane-yellow-on-white":
this.primaryTextColor = "#4d4d4d";
this.secondaryTextColor = "#ffffff";
this.foregroundColor = "#ffd500";
this.primaryTextHighlightColor = "#4d4d4d";
break;
case "stranane-white-on-yellow":
this.primaryTextColor = "#4d4d4d";
this.secondaryTextColor = "#ffffff";
this.foregroundColor = "#ffffff";
this.primaryTextHighlightColor = "#ffd500";
break;
case "prusanky-black-on-yellow":
this.primaryTextColor = "#000000";
this.secondaryTextColor = "#ffffff";
this.foregroundColor = "#ffd500";
break;
case "prusanky-yellow-on-white":
this.primaryTextColor = "#000000";
this.secondaryTextColor = "#ffffff";
this.foregroundColor = "#ffd500";
this.primaryTextHighlightColor = "#000000";
break;
case "prusanky-white-on-yellow":
this.primaryTextColor = "#000000";
this.secondaryTextColor = "#ffffff";
this.foregroundColor = "#ffffff";
this.primaryTextHighlightColor = "#ffd500";
break;
case "ujezd-green-on-white":
this.primaryTextColor = "#000000";
this.secondaryTextColor = "#ffffff";
this.foregroundColor = "#8ed4a3";
this.primaryTextHighlightColor = "#ffdd55";
break;
case "ujezd-white-on-green":
this.primaryTextColor = "#000000";
this.secondaryTextColor = "#ffffff";
this.foregroundColor = "#ffffff";
this.primaryTextHighlightColor = "#8ed4a3";
break;
case "cssd-red-on-black":
this.primaryTextColor = "#ffffff";
this.secondaryTextColor = "#ffffff";
this.foregroundColor = "#e63812";
break;
case "cssd-black-on-red":
this.primaryTextColor = "#ffffff";
this.secondaryTextColor = "#ffffff";
this.foregroundColor = "#000000";
break;
case "jilemnice-purple-on-black":
this.primaryTextColor = "#ffffff";
this.secondaryTextColor = "#ffffff";
this.foregroundColor = "#6e1646";
break;
case "jilemnice-black-on-purple":
this.primaryTextColor = "#ffffff";
this.secondaryTextColor = "#ffffff";
this.foregroundColor = "#000000";
break;
case "novarole-white-on-green":
this.primaryTextColor = "#000000";
this.secondaryTextColor = "#ffffff";
this.foregroundColor = "#ffffff";
this.primaryTextHighlightColor = "#a9ce2d";
break;
case "novarole-green-on-white":
this.primaryTextColor = "#000000";
this.secondaryTextColor = "#ffffff";
this.foregroundColor = "#a9ce2d";
this.primaryTextHighlightColor = "#ffcc00";
break;
case "novarole-green-on-black":
this.primaryTextColor = "#000000";
this.secondaryTextColor = "#ffffff";
this.foregroundColor = "#a9ce2d";
this.primaryTextHighlightColor = "#ffcc00";
break;
case "zeleni-melnik-yellow-name-rect":
await this.setPrimaryColorScheme("white-on-black", true);
break;
default:
throw new Error("This scheme does not exist.");
break;
}
this.requesterTextColor = this.primaryTextColor;
if (!skipRedraw) {
await this.redrawCanvas();
}
}
async setPrimaryTextHighlightColorScheme(scheme, skipRedraw = false) {
switch(scheme) {
case "gold":
this.primaryTextHighlightColor = "#ffcc00";
break;
case "litomerice":
this.primaryTextHighlightColor = "#afe87e";
break;
default:
throw new Error("This scheme does not exist.");
break;
}
if (!skipRedraw) {
await this.redrawCanvas();
}
}
async setPrimaryTextHighlightColor(color, skipRedraw = false) {
this.primaryTextHighlightColor = color;
if (!skipRedraw) {
await this.redrawCanvas();
}
}
async setLogoIsCenter(isCenter, skipRedraw = false) {
this.logoIsCenter = isCenter;
if (!skipRedraw) {
await this.redrawCanvas();
}
}
async loadData(
primaryImageInput = null,
primaryText = "",
nameText = "",
primaryColorScheme = "",
resultion = 2000,
skipRedraw = false
) {
await super.loadData(
primaryImageInput,
primaryText,
nameText,
primaryColorScheme,
resultion,
skipRedraw
);
$("#icon-image-selection").val("Otevřená radnice 1");
$("#icon-image-selection").trigger("change");
}
}