Added SVG to PNG conversion tool, Added tool page and code
Update .gitignore, sidebar.php, and 17 more files...
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -25,3 +25,4 @@ commons/trash/
|
||||
scrollbar.scss
|
||||
commons/strings/_*/
|
||||
_unsorted/
|
||||
tools/items/*/*.min.js
|
||||
|
@@ -41,6 +41,10 @@ function printSidebarEntry($url, $title, $icon) {
|
||||
printSidebarEntry(l10n_url_abs('/content/?tags=electronic'), localize("sidebar.text.electronics"), "fad fa-microchip");
|
||||
?>
|
||||
</div>
|
||||
<hr class="subtle">
|
||||
<?php
|
||||
printSidebarEntry(l10n_url_abs('/tools/'), localize("sidebar.text.tools"), "fad fa-tools");
|
||||
?>
|
||||
<hr class="subtle">
|
||||
<?php
|
||||
printSidebarEntry(l10n_url_abs('/links/'), localize("sidebar.text.links"), "fad fa-link");
|
||||
|
@@ -10,7 +10,7 @@ $host_uri = "https://nibblepoker.lu";
|
||||
$dir_commons = dirname(__FILE__);
|
||||
$dir_root = realpath($dir_commons . "/../");
|
||||
$config_dir_content = realpath($dir_commons . "/../" . "content/");
|
||||
$config_dir_tools = realpath($dir_commons . "/../" . "content/");
|
||||
$config_dir_tools = realpath($dir_commons . "/../" . "tools/");
|
||||
|
||||
// Optional features
|
||||
$enable_grids = false;
|
||||
|
@@ -31,7 +31,7 @@ class ContentIndexEntry {
|
||||
$this->priority = is_null($priority) ? 0 : $priority;
|
||||
}
|
||||
|
||||
static function from_json(array $json_data) : ?ContentIndexEntry {
|
||||
static function from_json(array $json_data): ?ContentIndexEntry {
|
||||
if(!key_exists("id", $json_data)) {
|
||||
return null;
|
||||
}
|
||||
@@ -70,7 +70,7 @@ class ContentManager {
|
||||
if(!$this->hasError) {
|
||||
if($this->displayType == ContentDisplayType::SEARCH) {
|
||||
$this->loadRootIndex(realpath($contentRootPath . "/index.json"));
|
||||
} else if($this->displayType == ContentDisplayType::CONTENT) {
|
||||
} elseif($this->displayType == ContentDisplayType::CONTENT) {
|
||||
$this->prepareContentFilePath($contentRootPath);
|
||||
}
|
||||
}
|
||||
@@ -80,7 +80,7 @@ class ContentManager {
|
||||
// Doing some dark magic whose inner workings are lost to times...
|
||||
$requestedUrlPart = explode(
|
||||
"?",
|
||||
explode("#", preg_replace("^\/(content)^", "", $requestedUrl))[0]
|
||||
explode("#", preg_replace("^\/(content|tools)^", "", $requestedUrl))[0]
|
||||
)[0];
|
||||
|
||||
if(strcmp($requestedUrlPart, "/") == 0) {
|
||||
@@ -111,7 +111,7 @@ class ContentManager {
|
||||
}
|
||||
} else {
|
||||
$this->displayType = ContentDisplayType::CONTENT;
|
||||
$this->requestedId = ltrim($requestedUrlPart, "/");
|
||||
$this->requestedId = ltrim(rtrim($requestedUrlPart, "/"), "/");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ class ContentManager {
|
||||
}
|
||||
|
||||
// Sorting entries based on their priority
|
||||
usort($this->rootIndexEntries, function (ContentIndexEntry $a, ContentIndexEntry $b) {
|
||||
usort($this->rootIndexEntries, function(ContentIndexEntry $a, ContentIndexEntry $b) {
|
||||
if($a->priority == $b->priority) {
|
||||
return 0;
|
||||
}
|
||||
@@ -182,7 +182,7 @@ class ContentManager {
|
||||
}
|
||||
|
||||
// Common utilities
|
||||
function get_content_file_path(string $contentRootPath, string $contentId) : ?string {
|
||||
function get_content_file_path(string $contentRootPath, string $contentId): ?string {
|
||||
if(ctype_alnum(str_replace("-", "", $contentId))) {
|
||||
return realpath($contentRootPath . "/items/" . $contentId . ".json");
|
||||
}
|
||||
|
124
commons/content/tools.php
Normal file
124
commons/content/tools.php
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
// Making sure the file is included and not accessed directly.
|
||||
if(basename(__FILE__) == basename($_SERVER["SCRIPT_FILENAME"])) {
|
||||
header('HTTP/1.1 403 Forbidden');
|
||||
die();
|
||||
}
|
||||
|
||||
// Including required helpers.
|
||||
include_once 'commons/config.php';
|
||||
include_once 'commons/langs.php';
|
||||
include_once 'commons/content.php';
|
||||
|
||||
// Required to make headings
|
||||
include_once 'commons/DOM/utils.php';
|
||||
|
||||
// Defining the template types.
|
||||
class ToolInfoFile {
|
||||
public string $domFile;
|
||||
public ?string $langFile;
|
||||
public array $codeFilesPaths;
|
||||
public array $styleFilesPaths;
|
||||
public string $icon;
|
||||
public string $titleKey;
|
||||
public ?string $subTitleKey;
|
||||
|
||||
function __construct(string $domFile, ?string $langFile, ?array $codeFilesPaths, ?array $styleFilesPaths,
|
||||
?string $icon, ?string $titleKey, ?string $subTitleKey) {
|
||||
$this->domFile = $domFile;
|
||||
$this->langFile = $langFile;
|
||||
$this->codeFilesPaths = is_null($codeFilesPaths) ? array() : $codeFilesPaths;
|
||||
$this->styleFilesPaths = is_null($styleFilesPaths) ? array() : $styleFilesPaths;
|
||||
$this->icon = is_null($icon) ? "fad fa-question" : $icon;
|
||||
$this->titleKey = is_null($titleKey) ? "unset" : $titleKey;
|
||||
$this->subTitleKey = $subTitleKey;
|
||||
}
|
||||
|
||||
static function from_json(array $json_data): ?ToolInfoFile {
|
||||
if(!key_exists("dom", $json_data)) {
|
||||
return null;
|
||||
}
|
||||
return new ToolInfoFile(
|
||||
$json_data["dom"],
|
||||
key_exists("lang", $json_data) ? $json_data["lang"] : null,
|
||||
key_exists("code", $json_data) ? $json_data["code"] : null,
|
||||
key_exists("styles", $json_data) ? $json_data["styles"] : null,
|
||||
key_exists("icon", $json_data) ? $json_data["icon"] : null,
|
||||
key_exists("title", $json_data) ? $json_data["title"] : null,
|
||||
key_exists("subtitle", $json_data) ? $json_data["subtitle"] : null
|
||||
);
|
||||
}
|
||||
|
||||
function validateFiles(ContentManager $contentManager): void {
|
||||
if(!(file_exists($this->domFile) && is_file($this->domFile))) {
|
||||
$contentManager->hasError = true;
|
||||
$contentManager->errorMessageKey = "content.error.message.missing.file.dom";
|
||||
return;
|
||||
}
|
||||
|
||||
if(!is_null($this->langFile)) {
|
||||
if(!(file_exists($this->langFile) && is_file($this->langFile))) {
|
||||
$contentManager->hasError = true;
|
||||
$contentManager->errorMessageKey = "content.error.message.missing.file.lang";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
foreach($this->codeFilesPaths as $codeFilePath) {
|
||||
if(!(file_exists($codeFilePath) && is_file($codeFilePath))) {
|
||||
$contentManager->hasError = true;
|
||||
$contentManager->errorMessageKey = "content.error.message.missing.file.code";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
foreach($this->styleFilesPaths as $styleFilePath) {
|
||||
if(!(file_exists($styleFilePath) && is_file($styleFilePath))) {
|
||||
$contentManager->hasError = true;
|
||||
$contentManager->errorMessageKey = "content.error.message.missing.file.style";
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class ToolsContent {
|
||||
static function loadItemIndexFile(ContentManager $contentManager, string $contentRootPath): ?ToolInfoFile {
|
||||
// Preliminary check
|
||||
if(!$contentManager->displayType == ContentDisplayType::CONTENT) {
|
||||
$contentManager->hasError = true;
|
||||
$contentManager->errorMessageKey = "content.error.message.cannot.load.item.as.not.content";
|
||||
return null;
|
||||
}
|
||||
|
||||
// Loading the index file
|
||||
$itemIndexJsonData = json_decode(file_get_contents($contentManager->contentFilepath), true);
|
||||
if(is_null($itemIndexJsonData)) {
|
||||
return null;
|
||||
}
|
||||
$toolInfo = ToolInfoFile::from_json($itemIndexJsonData);
|
||||
unset($itemIndexJsonData);
|
||||
|
||||
// Making paths absolute
|
||||
if(!is_null($toolInfo)) {
|
||||
$toolInfo->domFile = realpath($contentRootPath . "/items/" . $toolInfo->domFile);
|
||||
if(!is_null($toolInfo->langFile)) {
|
||||
$toolInfo->langFile = realpath($contentRootPath . "/items/" . $toolInfo->langFile);
|
||||
}
|
||||
for($iCodeFilePath = 0; $iCodeFilePath < count($toolInfo->codeFilesPaths); $iCodeFilePath++) {
|
||||
$toolInfo->codeFilesPaths[$iCodeFilePath] = realpath(
|
||||
$contentRootPath . "/items/" . $toolInfo->codeFilesPaths[$iCodeFilePath]);
|
||||
}
|
||||
for($iStyleFilePath = 0; $iStyleFilePath < count($toolInfo->styleFilesPaths); $iStyleFilePath++) {
|
||||
$toolInfo->styleFilesPaths[$iStyleFilePath] = realpath(
|
||||
$contentRootPath . "/items/" . $toolInfo->styleFilesPaths[$iStyleFilePath]);
|
||||
}
|
||||
} else {
|
||||
$contentManager->hasError = true;
|
||||
$contentManager->errorMessageKey = "content.error.message.cannot.load";
|
||||
}
|
||||
|
||||
return $toolInfo;
|
||||
}
|
||||
}
|
||||
?>
|
File diff suppressed because one or more lines are too long
@@ -37,6 +37,13 @@
|
||||
"content.error.message.data.no.tags": "No tags found !",
|
||||
"content.error.message.data.no.title": "No title found !",
|
||||
|
||||
"___": "Messages returned by 'commons/content/tools.php'",
|
||||
"_content.error.message.cannot.load.item.as.not.content": "",
|
||||
"_content.error.message.missing.file.dom": "",
|
||||
"_content.error.message.missing.file.lang": "",
|
||||
"_content.error.message.missing.file.code": "",
|
||||
"_content.error.message.missing.file.style": "",
|
||||
|
||||
"content.item.head.title.prefix": "",
|
||||
"content.item.head.title.suffix": " - NibblePoker",
|
||||
|
||||
|
@@ -5,6 +5,7 @@
|
||||
"sidebar.text.applications": "Applications",
|
||||
"sidebar.text.libraries": "Libraries",
|
||||
"sidebar.text.electronics": "Electronics",
|
||||
"sidebar.text.tools": "Tools",
|
||||
"sidebar.text.links": "Links",
|
||||
"sidebar.text.downloads": "Downloads",
|
||||
"sidebar.text.gitea": "Git Repos.",
|
||||
|
@@ -5,6 +5,7 @@
|
||||
"sidebar.text.applications": "Applications",
|
||||
"sidebar.text.libraries": "Librairies",
|
||||
"sidebar.text.electronics": "Électronique",
|
||||
"sidebar.text.tools": "Outils",
|
||||
"sidebar.text.links": "Liens",
|
||||
"sidebar.text.downloads": "Téléchargements",
|
||||
"sidebar.text.gitea": "Dépôts Git",
|
||||
|
@@ -43,3 +43,6 @@
|
||||
.p-mxs {
|
||||
padding: calc(#{$margin-base-size} * 0.375);
|
||||
}
|
||||
.p-xxs {
|
||||
padding: calc(#{$margin-base-size} * 0.25);
|
||||
}
|
||||
|
@@ -21,32 +21,6 @@ table.stylish {
|
||||
border-right: 1.5px solid #{$color-table-border};
|
||||
}
|
||||
|
||||
// Applying .p-xs and .p-s to all cells if needed
|
||||
// See 'core/spacing' for more info.
|
||||
&.table-p-xs {
|
||||
td, th {
|
||||
padding: calc(#{$margin-base-size} * 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
&.table-h-p-s {
|
||||
th {
|
||||
padding: calc(#{$margin-base-size} * 0.75);
|
||||
}
|
||||
}
|
||||
|
||||
&.table-p-s {
|
||||
td, th {
|
||||
padding: calc(#{$margin-base-size} * 0.75);
|
||||
}
|
||||
}
|
||||
|
||||
&.table-v-center {
|
||||
tr, td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
// Fixing border issues when using rounded corners by using a "fake" one using the background's color.
|
||||
// It will look like utter shit when rounded on firefox because its rendering engine cannot clip rounded corners apparently.
|
||||
// I guess that's what being at less than 3% of the market share does to you and your ability to care about basic shit.
|
||||
@@ -54,3 +28,29 @@ table.stylish {
|
||||
background-color: #{$color-border-all};
|
||||
}
|
||||
}
|
||||
|
||||
// Applying .p-xs and .p-s to all cells if needed
|
||||
// See 'core/spacing' for more info.
|
||||
.table-p-xs {
|
||||
td, th {
|
||||
padding: calc(#{$margin-base-size} * 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
.table-h-p-s {
|
||||
th {
|
||||
padding: calc(#{$margin-base-size} * 0.75);
|
||||
}
|
||||
}
|
||||
|
||||
.table-p-s {
|
||||
td, th {
|
||||
padding: calc(#{$margin-base-size} * 0.75);
|
||||
}
|
||||
}
|
||||
|
||||
.table-v-center {
|
||||
tr, td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
@@ -6,6 +6,8 @@ https://nibblepoker.lu/content/youtube-auto-archiver
|
||||
https://nibblepoker.lu/content/excel-worksheet-password-remover
|
||||
https://nibblepoker.lu/content/mc-expanded-iron-bundles
|
||||
https://nibblepoker.lu/content/dotnet-arguments
|
||||
https://nibblepoker.lu/tools/
|
||||
https://nibblepoker.lu/tools/svg-to-png/
|
||||
https://nibblepoker.lu/links/
|
||||
https://nibblepoker.lu/contact/
|
||||
https://nibblepoker.lu/privacy/
|
||||
@@ -17,6 +19,8 @@ https://nibblepoker.lu/en/content/youtube-auto-archiver
|
||||
https://nibblepoker.lu/en/content/excel-worksheet-password-remover
|
||||
https://nibblepoker.lu/en/content/mc-expanded-iron-bundles
|
||||
https://nibblepoker.lu/en/content/dotnet-arguments
|
||||
https://nibblepoker.lu/en/tools/
|
||||
https://nibblepoker.lu/en/tools/svg-to-png/
|
||||
https://nibblepoker.lu/en/links/
|
||||
https://nibblepoker.lu/en/contact/
|
||||
https://nibblepoker.lu/en/privacy/
|
||||
@@ -28,6 +32,8 @@ https://nibblepoker.lu/fr/content/youtube-auto-archiver
|
||||
https://nibblepoker.lu/fr/content/excel-worksheet-password-remover
|
||||
https://nibblepoker.lu/fr/content/mc-expanded-iron-bundles
|
||||
https://nibblepoker.lu/fr/content/dotnet-arguments
|
||||
https://nibblepoker.lu/fr/tools/
|
||||
https://nibblepoker.lu/fr/tools/svg-to-png/
|
||||
https://nibblepoker.lu/fr/links/
|
||||
https://nibblepoker.lu/fr/contact/
|
||||
https://nibblepoker.lu/fr/privacy/
|
||||
|
@@ -1,3 +1,9 @@
|
||||
# Redirecting any URL that starts with "/tools" to the root of this folder.
|
||||
RewriteEngine On
|
||||
RewriteRule ^(.*) index.php [NC]
|
||||
RewriteRule ^\/?(tools\/)?[a-zA-Z0-9\-]+\/?$ index.php [NC]
|
||||
|
||||
# Redirecting any URL that starts with "/content" to the root of this folder.
|
||||
#RewriteEngine On
|
||||
#RewriteRule ^\/tools\/[a-zA-Z0-9\-]+\/?$ index.php [L]
|
||||
|
||||
# ^(?!\/.*\.(js|css)).*$ Should have worked :/
|
||||
|
16
tools/index.json
Normal file
16
tools/index.json
Normal file
@@ -0,0 +1,16 @@
|
||||
[
|
||||
{
|
||||
"id": "svg-to-png",
|
||||
"title": {
|
||||
"en": "SVG to PNG Converter",
|
||||
"fr": "Convertisseur SVG vers PNG"
|
||||
},
|
||||
"preamble": {
|
||||
"en": "",
|
||||
"fr": ""
|
||||
},
|
||||
"_image": "",
|
||||
"tags": ["converter", "svg", "png"],
|
||||
"priority": 100
|
||||
}
|
||||
]
|
185
tools/index.php
Normal file
185
tools/index.php
Normal file
@@ -0,0 +1,185 @@
|
||||
<?php
|
||||
$start_time = microtime(true);
|
||||
|
||||
// Importing required scripts.
|
||||
set_include_path('../');
|
||||
include_once 'commons/config.php';
|
||||
include_once 'commons/langs.php';
|
||||
|
||||
// Preparing the content
|
||||
include_once 'commons/content.php';
|
||||
include_once 'commons/content/tools.php';
|
||||
$contentManager = getContentManager($config_dir_tools);
|
||||
$toolInfo = NULL;
|
||||
if(!$contentManager->hasError && $contentManager->displayType == ContentDisplayType::CONTENT) {
|
||||
$toolInfo = ToolsContent::loadItemIndexFile($contentManager, $config_dir_tools);
|
||||
if(!is_null($toolInfo)) {
|
||||
$toolInfo->validateFiles($contentManager);
|
||||
}
|
||||
// If we still don't have errors, we load the lang file.
|
||||
if(!$contentManager->hasError && !is_null($toolInfo->langFile)) {
|
||||
// FIXME: Refactor the 'langs.php' to include this bit.
|
||||
$toolLangJson = file_get_contents($toolInfo->langFile);
|
||||
$toolLangData = json_decode($toolLangJson, true);
|
||||
unset($toolLangJson);
|
||||
if(array_key_exists($default_language, $toolLangData)) {
|
||||
$lang_data[$default_language] = array_merge($lang_data[$default_language], $toolLangData[$default_language]);
|
||||
}
|
||||
if($default_language != $user_language && array_key_exists($user_language, $toolLangData)) {
|
||||
$lang_data[$user_language] = array_merge($lang_data[$user_language], $toolLangData[$user_language]);
|
||||
}
|
||||
unset($toolLangData);
|
||||
}
|
||||
}
|
||||
$content_error_message = localize($contentManager->errorMessageKey);
|
||||
|
||||
// Checking if an error occurred while loading data and parsing the URL.
|
||||
// And if not, enabling special features.
|
||||
$content_error_code = 200;
|
||||
if($contentManager->hasError) {
|
||||
// TODO: Add condition for the lack of data for an item.
|
||||
if(is_null($contentManager->rootIndexEntries)) {
|
||||
// Failed to get a display type or to extract types.
|
||||
header("HTTP/1.1 400 Bad Request");
|
||||
$content_error_code = 400;
|
||||
} else {
|
||||
//Other error. (No article, ...)
|
||||
header("HTTP/1.1 500 Internal Server Error");
|
||||
$content_error_code = 500;
|
||||
}
|
||||
} else {
|
||||
$enable_code_highlight = true;
|
||||
$enable_glider = true;
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="<?php echo($user_language); ?>">
|
||||
<head>
|
||||
<?php include 'commons/DOM/head.php'; ?>
|
||||
<title><?php print(localize('tools.head.title')); ?></title>
|
||||
<meta name="description" content="<?php print(localize('tools.head.description')); ?>">
|
||||
<meta property="og:title" content="<?php print(localize('tools.og.title')); ?>"/>
|
||||
<meta property="og:type" content="website"/>
|
||||
<meta property="og:url" content="<?php echo($host_uri . l10n_url_abs('/')); ?>"/>
|
||||
<meta property="og:image" content="<?php echo($host_uri); ?>/resources/NibblePoker/images/logos/v2_opengraph.png"/>
|
||||
<meta property="og:image:type" content="image/png"/>
|
||||
<meta property="og:description" content="<?php print(localize('tools.og.description')); ?>"/>
|
||||
</head>
|
||||
<body>
|
||||
<?php
|
||||
include_once 'commons/DOM/utils.php';
|
||||
include 'commons/DOM/body-1.php';
|
||||
$SIDEBAR_ID = 'tools';
|
||||
include 'commons/DOM/sidebar.php';
|
||||
include 'commons/DOM/body-2.php';
|
||||
?>
|
||||
<header class="w-full p-m pl-s">
|
||||
<h1 class="t-size-17 t-w-500">
|
||||
<i class="fad fa-tools t-size-16 mr-s t-muted"></i><?php print(localize("tools.header.title")); ?>
|
||||
</h1>
|
||||
<?php //include 'header-lang.php'; ?>
|
||||
</header>
|
||||
<?php include 'commons/DOM/body-3.php'; ?>
|
||||
<main id="main" class="rl-m border border-r-0 p-l">
|
||||
<?php
|
||||
// Checking if an error occurred.
|
||||
if($content_error_code != 200) {
|
||||
if($contentManager->displayType == ContentDisplayType::SEARCH) {
|
||||
printMainHeader(localize("content.error.heading.main.search"), "fad fa-exclamation-triangle");
|
||||
} elseif($contentManager->displayType == ContentDisplayType::CONTENT) {
|
||||
printMainHeader(localize("content.error.heading.main.content"), "fad fa-exclamation-triangle");
|
||||
} else {
|
||||
printMainHeader(localize("content.error.heading.main.fallback"), "fad fa-exclamation-triangle");
|
||||
}
|
||||
|
||||
echo('<h3 class="mt-m t-size-18 t-center content-error-text mx-auto">' . $content_error_message . '</h3>');
|
||||
|
||||
goto content_printing_end;
|
||||
}
|
||||
|
||||
if($contentManager->displayType == ContentDisplayType::SEARCH) {
|
||||
// We are handling a content search with at least one result.
|
||||
|
||||
// Making the header with the amount of results.
|
||||
printMainHeader(
|
||||
count($contentManager->rootIndexEntries) > 1 ?
|
||||
localize("content.search.heading.main.multiple") :
|
||||
localize("content.search.heading.main.single"),
|
||||
"fad fa-file-search",
|
||||
count($contentManager->rootIndexEntries) . " " . (
|
||||
count($contentManager->rootIndexEntries) > 1 ?
|
||||
localize("content.search.count.multiple") :
|
||||
localize("content.search.count.single")));
|
||||
|
||||
// Printing the entry for each piece of relevant content.
|
||||
$doPrintRuler = false;
|
||||
foreach($contentManager->rootIndexEntries as $current_content) {
|
||||
/** @var ContentIndexEntry $current_content */
|
||||
if($doPrintRuler) {
|
||||
echo('<hr class="subtle">');
|
||||
} else {
|
||||
$doPrintRuler = true;
|
||||
}
|
||||
echo('<div class="p-s">');
|
||||
echo('<a class="casper-link" href="'.l10n_url_abs("/tools/".$current_content->id).'">');
|
||||
echo('<div class="content-search-entry">');
|
||||
echo('<img class="content-search-image mr-s r-l" src="' . $current_content->image . '">');
|
||||
echo('<h3 class="mb-xs">' . $current_content->title[$user_language] . '</h3>');
|
||||
echo('<p>' . $current_content->preamble[$user_language] . '</p>');
|
||||
echo('</div>');
|
||||
echo('</a>');
|
||||
echo('<p class="mt-xs"><i class="fad fa-tags t-size-8"></i>');
|
||||
foreach($current_content->tags as $current_content_tag) {
|
||||
echo('<a href="' . l10n_url_abs("/tools/?tags=".$current_content_tag) .
|
||||
'" class="ml-xs">#' . $current_content_tag . '</a>');
|
||||
}
|
||||
echo('</p>');
|
||||
echo('</div>');
|
||||
}
|
||||
|
||||
// TODO: Print the tags used in the search and others that may be available.
|
||||
} elseif($contentManager->displayType == ContentDisplayType::CONTENT) {
|
||||
// Printing the main heading (Lifted from composer.php in the templates section)
|
||||
echo(getMainHeader(
|
||||
localize($toolInfo->titleKey),
|
||||
$toolInfo->icon,
|
||||
is_null($toolInfo->subTitleKey) ? null : localize($toolInfo->subTitleKey),
|
||||
null,
|
||||
false,
|
||||
null,
|
||||
3,
|
||||
false,
|
||||
true
|
||||
));
|
||||
|
||||
// Printing the content
|
||||
echo('<div class="px-xxs">'); // mt-l
|
||||
include($toolInfo->domFile);
|
||||
echo('</div>');
|
||||
}
|
||||
|
||||
// Label used when there is an error to skip the content printing parts.
|
||||
content_printing_end:
|
||||
?>
|
||||
</main>
|
||||
<?php
|
||||
include 'commons/DOM/body-4.php';
|
||||
include 'commons/DOM/footer.php';
|
||||
include 'commons/DOM/body-5.php';
|
||||
include 'commons/DOM/scripts.php';
|
||||
|
||||
// Including the tool's scripts if required.
|
||||
if(!$contentManager->hasError && $contentManager->displayType == ContentDisplayType::CONTENT) {
|
||||
foreach($toolInfo->codeFilesPaths as $codeFilePath) {
|
||||
echo('<script src="'.substr($codeFilePath, strlen($dir_root)).'"></script>');
|
||||
}
|
||||
}
|
||||
?>
|
||||
</body>
|
||||
</html>
|
||||
<?php
|
||||
$end_time = microtime(true);
|
||||
if($print_execution_timer) {
|
||||
echo("<!-- PHP execution took " . round(($end_time - $start_time) * 1000, 2) . " ms -->");
|
||||
}
|
||||
?>
|
9
tools/items/svg-to-png.json
Normal file
9
tools/items/svg-to-png.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"dom": "svg-to-png/page.php",
|
||||
"lang": "svg-to-png/lang.json",
|
||||
"code": [
|
||||
"svg-to-png/code.min.js"
|
||||
],
|
||||
"icon": "fad fa-exchange-alt",
|
||||
"title": "tool.svg-to-png.title"
|
||||
}
|
70
tools/items/svg-to-png/code.js
Normal file
70
tools/items/svg-to-png/code.js
Normal file
@@ -0,0 +1,70 @@
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const eInputFiles = document.getElementById("tool-svg-to-png-files");
|
||||
const eFileSelectButton = document.getElementById("tool-svg-to-png-btn-select");
|
||||
|
||||
const eTextNoFiles = document.getElementById("tool-svg-to-png-text-none");
|
||||
const eTextHasFiles = document.getElementById("tool-svg-to-png-text-good");
|
||||
const eTextFileCount = document.getElementById("tool-svg-to-png-file-count");
|
||||
|
||||
const eInputWidth = document.getElementById("tool-svg-to-png-width");
|
||||
const eInputHeight = document.getElementById("tool-svg-to-png-height");
|
||||
|
||||
const eFileConvertButton = document.getElementById("tool-svg-to-png-btn-convert");
|
||||
|
||||
// Propagating the button click to the input element
|
||||
eFileSelectButton.onclick = function () {
|
||||
eInputFiles.click();
|
||||
}
|
||||
|
||||
// Handling file selection
|
||||
eInputFiles.addEventListener('change', function(e) {
|
||||
eTextNoFiles.hidden = true;
|
||||
eTextHasFiles.hidden = true;
|
||||
eTextFileCount.innerText = e.target.files.length.toString();
|
||||
if(e.target.files.length > 0) {
|
||||
eTextHasFiles.hidden = false;
|
||||
} else {
|
||||
eTextNoFiles.hidden = false;
|
||||
}
|
||||
});
|
||||
|
||||
// Handling conversion
|
||||
eFileConvertButton.onclick = function () {
|
||||
const canvas = document.getElementById('conversion-canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
if(eInputFiles.files.length === 0) {
|
||||
console.error("No files selected !");
|
||||
return
|
||||
}
|
||||
|
||||
canvas.width = parseInt(eInputWidth.value);
|
||||
canvas.height = parseInt(eInputHeight.value);
|
||||
|
||||
for(let iFile = 0; iFile < eInputFiles.files.length; iFile++) {
|
||||
const imageFile = eInputFiles.files[iFile];
|
||||
const fileReader = new FileReader();
|
||||
|
||||
console.log("Handling: " + imageFile.name);
|
||||
|
||||
fileReader.onload = (function(file) {
|
||||
return function(e) {
|
||||
const image = new Image();
|
||||
image.onload = function() {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
|
||||
const dataURL = canvas.toDataURL('image/png');
|
||||
const link = document.createElement('a');
|
||||
link.download = imageFile.name.replace(".svg", ".png");
|
||||
link.href = dataURL;
|
||||
link.click();
|
||||
};
|
||||
image.src = e.target.result; // Set the image source
|
||||
};
|
||||
})(imageFile);
|
||||
|
||||
fileReader.readAsDataURL(imageFile);
|
||||
}
|
||||
}
|
||||
});
|
32
tools/items/svg-to-png/lang.json
Normal file
32
tools/items/svg-to-png/lang.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"en": {
|
||||
"tool.svg-to-png.title": "SVG to PNG Converter",
|
||||
"tool.svg-to-png.input.title": "File selection",
|
||||
"tool.svg-to-png.options.title": "Options",
|
||||
"tool.svg-to-png.output.title": "Output",
|
||||
|
||||
"tool.svg-to-png.text.no.files": "No files selected.",
|
||||
"tool.svg-to-png.text.has.files": "You selected <span id=\"tool-svg-to-png-file-count\"></span> file(s).",
|
||||
|
||||
"tool.svg-to-png.select.files": "Select File(s)",
|
||||
"tool.svg-to-png.files": "File(s)",
|
||||
"tool.svg-to-png.width": "Width",
|
||||
"tool.svg-to-png.height": "Height",
|
||||
"tool.svg-to-png.convert": "Convert image(s)"
|
||||
},
|
||||
"fr": {
|
||||
"tool.svg-to-png.title": "Convertisseur SVG vers PNG",
|
||||
"tool.svg-to-png.input.title": "Selection de fichier(s)",
|
||||
"tool.svg-to-png.options.title": "Options",
|
||||
"_tool.svg-to-png.output.title": "",
|
||||
|
||||
"tool.svg-to-png.text.no.files": "Aucun fichier sélectionné.",
|
||||
"tool.svg-to-png.text.has.files": "Vous avez sélectionné <span id=\"tool-svg-to-png-file-count\"></span> fichier(s).",
|
||||
|
||||
"tool.svg-to-png.select.files": "Sélection de Fichier(s)",
|
||||
"tool.svg-to-png.files": "Fichier(s)",
|
||||
"tool.svg-to-png.width": "Largeur",
|
||||
"tool.svg-to-png.height": "Hauteur",
|
||||
"tool.svg-to-png.convert": "Convertir vers PNG"
|
||||
}
|
||||
}
|
56
tools/items/svg-to-png/page.php
Normal file
56
tools/items/svg-to-png/page.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
echo(getMainHeader(localize("tool.svg-to-png.input.title"), null, null, null,
|
||||
true, "bkgd-math", 3, false, false, true));
|
||||
|
||||
echo('<table class="table-v-center table-p-xs mt-xs"><tr><td>');
|
||||
|
||||
echo('<label for="tool-svg-to-png-files" hidden>' . localize("tool.svg-to-png.files") . ':</label>');
|
||||
echo('<input type="file" id="tool-svg-to-png-files" name="tool-svg-to-png-files" class="d-none" accept=".svg,image/svg+xml" multiple>');
|
||||
|
||||
echo('<p id="tool-svg-to-png-text-none" class="t-italic px-xxs">' . localize("tool.svg-to-png.text.no.files") . '</p>');
|
||||
echo('<p id="tool-svg-to-png-text-good" class="t-italic px-xxs" hidden>' . localize("tool.svg-to-png.text.has.files") . '</p>');
|
||||
|
||||
echo('</td></tr><tr><td>');
|
||||
|
||||
echo('<button id="tool-svg-to-png-btn-select" class="p-mxs r-s border b-light primary">');
|
||||
echo('<span class="text-monospace"><i class="fad fa-file-search"></i> ');
|
||||
echo(localize("tool.svg-to-png.select.files"));
|
||||
echo('</span></button>');
|
||||
|
||||
echo('</td></tr></table>');
|
||||
|
||||
|
||||
echo(getMainHeader(localize("tool.svg-to-png.options.title"), null, null, null,
|
||||
true, "bkgd-math", 3, false, false, true));
|
||||
|
||||
echo('<table class="table-v-center table-p-xs mt-xs"><tr><td>');
|
||||
echo('<label for="tool-svg-to-png-width">' . localize("tool.svg-to-png.width") . ': </label>');
|
||||
echo('</td><td>');
|
||||
echo('<input type="number" id="tool-svg-to-png-width" name="tool-svg-to-png-width" class="border p-xs r-s" value="256" min="1" max="8192"/>');
|
||||
echo('</td></tr><tr><td>');
|
||||
echo('<label for="tool-svg-to-png-height">' . localize("tool.svg-to-png.height") . ': </label>');
|
||||
echo('</td><td>');
|
||||
echo('<input type="number" id="tool-svg-to-png-height" name="tool-svg-to-png-height" class="border p-xs r-s" value="256" min="1" max="8192"/>');
|
||||
echo('</td></tr></table>');
|
||||
|
||||
|
||||
echo(getMainHeader(localize("tool.svg-to-png.output.title"), null, null, null,
|
||||
true, "bkgd-math", 3, false, false, true));
|
||||
|
||||
echo('<div class="p-s pt-m">');
|
||||
echo('<button id="tool-svg-to-png-btn-convert" class="p-mxs r-s border b-light primary">');
|
||||
echo('<span class="text-monospace"><i class="fad fa-file-search"></i> ');
|
||||
echo(localize("tool.svg-to-png.convert"));
|
||||
echo('</span></button>');
|
||||
|
||||
// TODO: Add 2nd button with aspect ration preservation
|
||||
|
||||
echo('</div>');
|
||||
|
||||
echo('<br>');
|
||||
|
||||
echo('<div class="p-s">');
|
||||
echo('<canvas id="conversion-canvas" width="256" height="256" class="border r-l d-none"></canvas>');
|
||||
echo('</div>');
|
||||
|
||||
?>
|
Reference in New Issue
Block a user