Merge pull request #1094 from kiwix/downloadBtn

More conspicuous download button
This commit is contained in:
Kelson 2024-09-11 11:42:59 +02:00 committed by GitHub
commit f5c91cc272
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 173 additions and 115 deletions

View File

@ -4,6 +4,7 @@ skin/magnet.png
skin/feed.svg
skin/langSelector.svg
skin/download.png
skin/download-white.svg
skin/hash.png
skin/search-icon.svg
skin/iso6391To3.js

View File

@ -0,0 +1,6 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
<g id="SVGRepo_iconCarrier"> <g id="Interface / Download"> <path id="Vector" d="M6 21H18M12 3V17M12 17L17 12M12 17L7 12" stroke="#f6f5f4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </g> </g>
</svg>

After

Width:  |  Height:  |  Size: 546 B

View File

@ -152,23 +152,24 @@
.book__link {
text-decoration: none;
grid-column: 1 / 3;
grid-row: 1 / 3;
}
.book__wrapper {
--tile-width: 250px;
--tile-border-width: 1px;
--tile-border-radius: 3px;
color: #444343;
height: 248px;
width: 250px;
height: 258px;
width: var(--tile-width);
margin: 15px;
background-color: #f7f7f7;
border: 1px solid #ececec;
border-radius: 3px;
display: grid;
grid-template-columns: 65px 1fr;
grid-template-rows: 70px 120px 1fr 1fr;
grid-gap: 5px;
border: var(--tile-border-width) solid #ececec;
border-radius: var(--tile-border-radius);
display: flex;
flex-direction: column;
justify-content: space-between;
transition: transform 0.25s;
gap: 4px;
}
.book__wrapper:hover {
@ -177,8 +178,12 @@
.book__link__wrapper {
display: grid;
grid-template-areas:
"bookIcon bookHeader"
"bookDesc bookDesc"
;
grid-template-columns: 65px 1fr;
grid-template-rows: 70px 120px 1fr 1fr;
grid-template-rows: 70px 120px;
}
.book__icon {
@ -192,6 +197,7 @@
width: 48px;
height: 48px;
margin: 10px 0 0 10px;
grid-area: bookIcon;
}
.book__header {
@ -203,6 +209,7 @@
height: 100%;
align-items: center;
align-content: center;
grid-area: bookHeader;
}
.book__title {
@ -213,28 +220,36 @@
}
.book__download {
font-size: 1.1rem;
margin: 3px 0;
cursor: pointer;
font-size: 1.2rem;
background: #00b4e4;
padding: 8px;
border-radius: 0 0 var(--tile-border-radius) var(--tile-border-radius);
display: flex;
align-items: center;
justify-content: center;
position: relative;
width: var(--tile-width);
left: calc(0px - var(--tile-border-width));
text-decoration: none;
}
.book__download > img {
height: 16px;
width: 16px;
}
.book__download > span {
cursor: pointer;
text-decoration: none;
position: relative;
padding: 0 3px 1px;
font-family: roboto;
background: #00b4e4;
color: white;
box-shadow: 0 0 0 0;
border-radius: 2px;
}
.book__download > span:hover {
box-shadow: 0 5px 5px rgba(0, 0, 0, 0.1)
.book__download:hover {
background: royalblue;
}
.book__description {
grid-column: 1 / 3;
margin: 10px 10px 5px;
overflow: hidden;
display: -webkit-box;
@ -245,6 +260,13 @@
font-size: 1.2rem;
color: #4d4d4d;
line-height: 1.25;
grid-area: bookDesc;
}
.book__meta {
display: flex;
justify-content: space-between;
padding: 0 10px 4px;
}
.book__languageTag {
@ -257,7 +279,6 @@
color: black;
height: 25px;
width: 25px;
margin: 10px auto 0 10px;
border-radius: 5px;
font-size: 0.85rem;
}
@ -268,8 +289,6 @@
font-size: 1.1rem;
justify-content: flex-end;
font-family: roboto;
margin-right: 10px;
margin-top: 10px;
overflow: hidden;
}

View File

@ -139,6 +139,19 @@
return spanElement.outerHTML;
}
function downloadButtonText(zimSize) {
return $t("download") + (zimSize ? ` - ${zimSize}`: '');
}
function downloadButtonHtml(url, zimSize) {
return url
? `<div class="book__download" data-link="${url}" data-size="${zimSize}">
<img src="${root}/skin/download-white.svg?KIWIXCACHEID">
<span ">${downloadButtonText(zimSize)}</span>
</div>`
: '';
}
function generateBookHtml(book, sort = false) {
const link = book.querySelector('link[type="text/html"]').getAttribute('href');
let iconUrl;
@ -183,6 +196,7 @@
}
const faviconAttr = iconUrl != undefined ? `style="background-image: url('${iconUrl}')"` : '';
const languageAttr = langCode != '' ? `title="${language}" aria-label="${language}"` : 'style="background-color: transparent"';
divTag.innerHTML = `
<div class="book__wrapper">
<a class="book__link" href="${viewerLink}" data-hover="Preview">
@ -190,13 +204,15 @@
<div class="book__icon" ${faviconAttr}></div>
<div class="book__header">
<div id="book__title">${title}</div>
${downloadLink ? `<div class="book__download"><span data-link="${downloadLink}">${$t("download")} ${humanFriendlyZimSize ? ` - ${humanFriendlyZimSize}</span></div>`: ''}` : ''}
</div>
<div class="book__description" title="${description}">${description}</div>
</div>
</a>
<div class="book__meta">
<div class="book__languageTag" ${languageAttr}>${getLanguageCodeToDisplay(langCode)}</div>
<div class="book__tags"><div class="book__tags--wrapper">${tagHtml}</div></div>
</div>
${downloadButtonHtml(downloadLink, humanFriendlyZimSize)}
</div></div>`;
return divTag;
}
@ -282,6 +298,7 @@
}
function insertModal(button) {
const downloadSize = button.getAttribute('data-size');
const downloadLink = button.getAttribute('data-link');
button.addEventListener('click', async (event) => {
event.preventDefault();
@ -291,7 +308,7 @@
<div class="modal-heading">
<div class="modal-title">
<div>
Download
${downloadButtonText(downloadSize)}
</div>
</div>
<div onclick="closeModal()" class="modal-close-button">
@ -451,7 +468,7 @@
booksToDelete.forEach(book => {iso.remove(book);});
books.forEach((book) => {
iso.insert(generateBookHtml(book, sort))
const downloadButton = document.querySelector(`[data-id="${getInnerHtml(book, 'id')}"] .book__download span`);
const downloadButton = document.querySelector(`[data-id="${getInnerHtml(book, 'id')}"] .book__download`);
if (downloadButton) {
insertModal(downloadButton);
}

View File

@ -50,20 +50,11 @@
pointer-events: none;
}
.book__link__wrapper {
grid-column: 1 / 3;
grid-row: 1 / 3;
}
.book__link {
grid-row: 2 / 3;
}
.kiwixHomeBody__results {
flex-basis: 100%;
}
#book__title>a, .book__download a {
#book__title>a {
text-decoration: none;
all: unset;
}
@ -117,25 +108,32 @@
<h3 class="kiwixHomeBody__results">{{translations.count-of-matching-books}}</h3>
{{#books}}
<div class="book__wrapper">
<div class="book__link__wrapper">
<div class="book__icon" {{faviconAttr}}></div>
<div class="book__header">
<div id="book__title"><a href="{{root}}/content/{{id}}">{{title}}</a></div>
{{#downloadAvailable}}
<div class="book__download"><span><a href="{{root}}/nojs/download/{{id}}">{{translations.download}}</a></span></div>
{{/downloadAvailable}}
</div>
<a class="book__link" href="{{root}}/content/{{id}}" title="{{translations.preview-book}}" aria-label="{{translations.preview-book}}">
<div class="book__description" title="{{description}}">{{description}}</div>
</a>
</div>
<div class="book__languageTag" {{languageAttr}}>{{langCode}}</div>
<div class="book__tags"><div class="book__tags--wrapper">
{{#tagList}}
<span class="tag__link" aria-label='{{tag}}' title='{{tag}}'>{{tag}}</span>
{{/tagList}}
<a class="book__link" href="{{root}}/content/{{id}}" title="{{translations.preview-book}}" aria-label="{{translations.preview-book}}">
<div class="book__link__wrapper">
<div class="book__icon" {{faviconAttr}}></div>
<div class="book__header">
<div id="book__title">{{title}}</div>
</div>
</div>
<div class="book__description" title="{{description}}">{{description}}</div>
</div>
</a>
<div class="book__meta">
<div class="book__languageTag" {{languageAttr}}>{{langCode}}</div>
<div class="book__tags"><div class="book__tags--wrapper">
{{#tagList}}
<span class="tag__link" aria-label='{{tag}}' title='{{tag}}'>{{tag}}</span>
{{/tagList}}
</div>
</div>
</div>
{{#downloadAvailable}}
<div>
<a class="book__download" href="{{root}}/nojs/download/{{id}}">
<img src="{{root}}/skin/download-white.svg?KIWIXCACHEID">
<span>{{translations.download}}</span>
</a>
</div>
{{/downloadAvailable}}
</div>
{{/books}}
</div>

View File

@ -1033,7 +1033,7 @@ TEST_F(LibraryServerTest, no_name_mapper_catalog_v2_individual_entry_access)
" />\n" \
" <link\n" \
" type=\"text/css\"\n" \
" href=\"/ROOT%23%3F/skin/index.css?cacheid=2781f198\"\n" \
" href=\"/ROOT%23%3F/skin/index.css?cacheid=ae79e41a\"\n" \
" rel=\"Stylesheet\"\n" \
" />\n" \
" <link rel=\"apple-touch-icon\" sizes=\"180x180\" href=\"/ROOT%23%3F/skin/favicon/apple-touch-icon.png?cacheid=f86f8df3\">\n" \
@ -1066,17 +1066,10 @@ TEST_F(LibraryServerTest, no_name_mapper_catalog_v2_individual_entry_access)
" .tag__link {\n" \
" pointer-events: none;\n" \
" }\n\n" \
" .book__link__wrapper {\n" \
" grid-column: 1 / 3;\n" \
" grid-row: 1 / 3;\n" \
" }\n\n" \
" .book__link {\n" \
" grid-row: 2 / 3;\n" \
" }\n\n" \
" .kiwixHomeBody__results {\n" \
" flex-basis: 100%;\n" \
" }\n\n" \
" #book__title>a, .book__download a {\n" \
" #book__title>a {\n" \
" text-decoration: none;\n" \
" all: unset;\n" \
" }\n" \
@ -1087,62 +1080,83 @@ TEST_F(LibraryServerTest, no_name_mapper_catalog_v2_individual_entry_access)
#define CHARLES_RAY_BOOK_HTML \
" <div class=\"book__wrapper\">\n" \
" <div class=\"book__link__wrapper\">\n" \
" <div class=\"book__icon\" style=background-image:url(/ROOT%23%3F/catalog/v2/illustration/charlesray/?size=48)></div>\n" \
" <div class=\"book__header\">\n" \
" <div id=\"book__title\"><a href=\"/ROOT%23%3F/content/zimfile%26other\">Charles, Ray</a></div>\n" \
" <div class=\"book__download\"><span><a href=\"/ROOT%23%3F/nojs/download/zimfile%26other\">Download</a></span></div>\n" \
" </div>\n" \
" <a class=\"book__link\" href=\"/ROOT%23%3F/content/zimfile%26other\" title=\"Preview\" aria-label=\"Preview\">\n" \
" <div class=\"book__description\" title=\"Wikipedia articles about Ray Charles\">Wikipedia articles about Ray Charles</div>\n" \
" </a>\n" \
" </div>\n" \
" <div class=\"book__languageTag\" >fra</div>\n" \
" <div class=\"book__tags\"><div class=\"book__tags--wrapper\">\n" \
" <span class=\"tag__link\" aria-label='unittest' title='unittest'>unittest</span>\n" \
" <span class=\"tag__link\" aria-label='wikipedia' title='wikipedia'>wikipedia</span>\n" \
" <a class=\"book__link\" href=\"/ROOT%23%3F/content/zimfile%26other\" title=\"Preview\" aria-label=\"Preview\">\n" \
" <div class=\"book__link__wrapper\">\n" \
" <div class=\"book__icon\" style=background-image:url(/ROOT%23%3F/catalog/v2/illustration/charlesray/?size=48)></div>\n" \
" <div class=\"book__header\">\n" \
" <div id=\"book__title\">Charles, Ray</div>\n" \
" </div>\n" \
" <div class=\"book__description\" title=\"Wikipedia articles about Ray Charles\">Wikipedia articles about Ray Charles</div>\n" \
" </div>\n" \
" </a>\n" \
" <div class=\"book__meta\">\n" \
" <div class=\"book__languageTag\" >fra</div>\n" \
" <div class=\"book__tags\"><div class=\"book__tags--wrapper\">\n" \
" <span class=\"tag__link\" aria-label='unittest' title='unittest'>unittest</span>\n" \
" <span class=\"tag__link\" aria-label='wikipedia' title='wikipedia'>wikipedia</span>\n" \
" </div>\n" \
" </div>\n" \
" </div>\n" \
" <div>\n" \
" <a class=\"book__download\" href=\"/ROOT%23%3F/nojs/download/zimfile%26other\">\n" \
" <img src=\"/ROOT%23%3F/skin/download-white.svg?cacheid=079ab989\">\n" \
" <span>Download</span>\n" \
" </a>\n" \
" </div>\n" \
" </div>\n" \
" </div>\n"
#define RAY_CHARLES_BOOK_HTML \
" <div class=\"book__wrapper\">\n" \
" <div class=\"book__link__wrapper\">\n" \
" <div class=\"book__icon\" style=background-image:url(/ROOT%23%3F/catalog/v2/illustration/raycharles/?size=48)></div>\n" \
" <div class=\"book__header\">\n" \
" <div id=\"book__title\"><a href=\"/ROOT%23%3F/content/zimfile_raycharles\">Ray Charles</a></div>\n" \
" <div class=\"book__download\"><span><a href=\"/ROOT%23%3F/nojs/download/zimfile_raycharles\">Download</a></span></div>\n" \
" </div>\n" \
" <a class=\"book__link\" href=\"/ROOT%23%3F/content/zimfile_raycharles\" title=\"Preview\" aria-label=\"Preview\">\n" \
" <div class=\"book__description\" title=\"Wikipedia articles about Ray Charles\">Wikipedia articles about Ray Charles</div>\n" \
" </a>\n" \
" </div>\n" \
" <div class=\"book__languageTag\" >eng</div>\n" \
" <div class=\"book__tags\"><div class=\"book__tags--wrapper\">\n" \
" <span class=\"tag__link\" aria-label='public_tag_without_a_value' title='public_tag_without_a_value'>public_tag_without_a_value</span>\n" \
" <span class=\"tag__link\" aria-label='wikipedia' title='wikipedia'>wikipedia</span>\n" \
" <a class=\"book__link\" href=\"/ROOT%23%3F/content/zimfile_raycharles\" title=\"Preview\" aria-label=\"Preview\">\n" \
" <div class=\"book__link__wrapper\">\n" \
" <div class=\"book__icon\" style=background-image:url(/ROOT%23%3F/catalog/v2/illustration/raycharles/?size=48)></div>\n" \
" <div class=\"book__header\">\n" \
" <div id=\"book__title\">Ray Charles</div>\n" \
" </div>\n" \
" <div class=\"book__description\" title=\"Wikipedia articles about Ray Charles\">Wikipedia articles about Ray Charles</div>\n" \
" </div>\n" \
" </a>\n" \
" <div class=\"book__meta\">\n" \
" <div class=\"book__languageTag\" >eng</div>\n" \
" <div class=\"book__tags\"><div class=\"book__tags--wrapper\">\n" \
" <span class=\"tag__link\" aria-label='public_tag_without_a_value' title='public_tag_without_a_value'>public_tag_without_a_value</span>\n" \
" <span class=\"tag__link\" aria-label='wikipedia' title='wikipedia'>wikipedia</span>\n" \
" </div>\n" \
" </div>\n" \
" </div>\n" \
" <div>\n" \
" <a class=\"book__download\" href=\"/ROOT%23%3F/nojs/download/zimfile_raycharles\">\n" \
" <img src=\"/ROOT%23%3F/skin/download-white.svg?cacheid=079ab989\">\n" \
" <span>Download</span>\n" \
" </a>\n" \
" </div>\n" \
" </div>\n" \
" </div>\n"
#define RAY_CHARLES_UNCTZ_BOOK_HTML \
" <div class=\"book__wrapper\">\n" \
" <div class=\"book__link__wrapper\">\n" \
" <div class=\"book__icon\" style=background-image:url(/ROOT%23%3F/catalog/v2/illustration/raycharles_uncategorized/?size=48)></div>\n" \
" <div class=\"book__header\">\n" \
" <div id=\"book__title\"><a href=\"/ROOT%23%3F/content/zimfile_raycharles_uncategorized\">Ray (uncategorized) Charles</a></div>\n" \
" <div class=\"book__download\"><span><a href=\"/ROOT%23%3F/nojs/download/zimfile_raycharles_uncategorized\">Download</a></span></div>\n" \
" </div>\n" \
" <a class=\"book__link\" href=\"/ROOT%23%3F/content/zimfile_raycharles_uncategorized\" title=\"Preview\" aria-label=\"Preview\">\n" \
" <div class=\"book__description\" title=\"No category is assigned to this library entry.\">No category is assigned to this library entry.</div>\n" \
" </a>\n" \
" </div>\n" \
" <div class=\"book__languageTag\" >rus,eng</div>\n" \
" <div class=\"book__tags\"><div class=\"book__tags--wrapper\">\n" \
" <span class=\"tag__link\" aria-label='public_tag_with_a_value:value_of_a_public_tag' title='public_tag_with_a_value:value_of_a_public_tag'>public_tag_with_a_value:value_of_a_public_tag</span>\n" \
" <span class=\"tag__link\" aria-label='wikipedia' title='wikipedia'>wikipedia</span>\n" \
" <a class=\"book__link\" href=\"/ROOT%23%3F/content/zimfile_raycharles_uncategorized\" title=\"Preview\" aria-label=\"Preview\">\n" \
" <div class=\"book__link__wrapper\">\n" \
" <div class=\"book__icon\" style=background-image:url(/ROOT%23%3F/catalog/v2/illustration/raycharles_uncategorized/?size=48)></div>\n" \
" <div class=\"book__header\">\n" \
" <div id=\"book__title\">Ray (uncategorized) Charles</div>\n" \
" </div>\n" \
" <div class=\"book__description\" title=\"No category is assigned to this library entry.\">No category is assigned to this library entry.</div>\n" \
" </div>\n" \
" </a>\n" \
" <div class=\"book__meta\">\n" \
" <div class=\"book__languageTag\" >rus,eng</div>\n" \
" <div class=\"book__tags\"><div class=\"book__tags--wrapper\">\n" \
" <span class=\"tag__link\" aria-label='public_tag_with_a_value:value_of_a_public_tag' title='public_tag_with_a_value:value_of_a_public_tag'>public_tag_with_a_value:value_of_a_public_tag</span>\n" \
" <span class=\"tag__link\" aria-label='wikipedia' title='wikipedia'>wikipedia</span>\n" \
" </div>\n" \
" </div>\n" \
" </div>\n" \
" <div>\n" \
" <a class=\"book__download\" href=\"/ROOT%23%3F/nojs/download/zimfile_raycharles_uncategorized\">\n" \
" <img src=\"/ROOT%23%3F/skin/download-white.svg?cacheid=079ab989\">\n" \
" <span>Download</span>\n" \
" </a>\n" \
" </div>\n" \
" </div>\n" \
" </div>\n"
#define FINAL_HTML_TEXT \

View File

@ -61,9 +61,9 @@ const ResourceCollection resources200Compressible{
{ DYNAMIC_CONTENT, "/ROOT%23%3F/skin/i18n.js" },
{ STATIC_CONTENT, "/ROOT%23%3F/skin/i18n.js?cacheid=071abc9a" },
{ DYNAMIC_CONTENT, "/ROOT%23%3F/skin/index.css" },
{ STATIC_CONTENT, "/ROOT%23%3F/skin/index.css?cacheid=2781f198" },
{ STATIC_CONTENT, "/ROOT%23%3F/skin/index.css?cacheid=ae79e41a" },
{ DYNAMIC_CONTENT, "/ROOT%23%3F/skin/index.js" },
{ STATIC_CONTENT, "/ROOT%23%3F/skin/index.js?cacheid=480ca6b4" },
{ STATIC_CONTENT, "/ROOT%23%3F/skin/index.js?cacheid=8f4b6a1e" },
{ DYNAMIC_CONTENT, "/ROOT%23%3F/skin/iso6391To3.js" },
{ STATIC_CONTENT, "/ROOT%23%3F/skin/iso6391To3.js?cacheid=ecde2bb3" },
{ DYNAMIC_CONTENT, "/ROOT%23%3F/skin/isotope.pkgd.min.js" },
@ -114,6 +114,8 @@ const ResourceCollection resources200Uncompressible{
{ STATIC_CONTENT, "/ROOT%23%3F/skin/caret.png?cacheid=22b942b4" },
{ DYNAMIC_CONTENT, "/ROOT%23%3F/skin/download.png" },
{ STATIC_CONTENT, "/ROOT%23%3F/skin/download.png?cacheid=a39aa502" },
{ DYNAMIC_CONTENT, "/ROOT%23%3F/skin/download-white.svg" },
{ STATIC_CONTENT, "/ROOT%23%3F/skin/download-white.svg?cacheid=079ab989"},
{ DYNAMIC_CONTENT, "/ROOT%23%3F/skin/favicon/android-chrome-192x192.png" },
{ STATIC_CONTENT, "/ROOT%23%3F/skin/favicon/android-chrome-192x192.png?cacheid=bfac158b" },
{ DYNAMIC_CONTENT, "/ROOT%23%3F/skin/favicon/android-chrome-512x512.png" },
@ -279,7 +281,7 @@ TEST_F(ServerTest, CacheIdsOfStaticResources)
{
/* url */ "/ROOT%23%3F/",
R"EXPECTEDRESULT( href="/ROOT%23%3F/skin/kiwix.css?cacheid=2158fad9"
href="/ROOT%23%3F/skin/index.css?cacheid=2781f198"
href="/ROOT%23%3F/skin/index.css?cacheid=ae79e41a"
<link rel="apple-touch-icon" sizes="180x180" href="/ROOT%23%3F/skin/favicon/apple-touch-icon.png?cacheid=f86f8df3">
<link rel="icon" type="image/png" sizes="32x32" href="/ROOT%23%3F/skin/favicon/favicon-32x32.png?cacheid=79ded625">
<link rel="icon" type="image/png" sizes="16x16" href="/ROOT%23%3F/skin/favicon/favicon-16x16.png?cacheid=a986fedc">
@ -292,7 +294,7 @@ R"EXPECTEDRESULT( href="/ROOT%23%3F/skin/kiwix.css?cacheid=2158fad9"
<script type="text/javascript" src="/ROOT%23%3F/skin/languages.js?cacheid=ee7d95b5" defer></script>
<script src="/ROOT%23%3F/skin/isotope.pkgd.min.js?cacheid=2e48d392" defer></script>
<script src="/ROOT%23%3F/skin/iso6391To3.js?cacheid=ecde2bb3"></script>
<script type="text/javascript" src="/ROOT%23%3F/skin/index.js?cacheid=480ca6b4" defer></script>
<script type="text/javascript" src="/ROOT%23%3F/skin/index.js?cacheid=8f4b6a1e" defer></script>
<img src="/ROOT%23%3F/skin/feed.svg?cacheid=055b333f"
<img src="/ROOT%23%3F/skin/langSelector.svg?cacheid=00b59961"
)EXPECTEDRESULT"
@ -310,7 +312,8 @@ R"EXPECTEDRESULT( background-image: url('../skin/search-icon.svg?cacheid=b10a
},
{
/* url */ "/ROOT%23%3F/skin/index.js",
R"EXPECTEDRESULT( <img src="${root}/skin/download.png?cacheid=a39aa502" alt="${$t("direct-download-alt-text")}" />
R"EXPECTEDRESULT( <img src="${root}/skin/download-white.svg?cacheid=079ab989">
<img src="${root}/skin/download.png?cacheid=a39aa502" alt="${$t("direct-download-alt-text")}" />
<img src="${root}/skin/hash.png?cacheid=f836e872" alt="${$t("hash-download-alt-text")}" />
<img src="${root}/skin/magnet.png?cacheid=73b6bddf" alt="${$t("magnet-alt-text")}" />
<img src="${root}/skin/bittorrent.png?cacheid=4f5c6882" alt="${$t("torrent-download-alt-text")}" />