Alt Text for Photos
* Add an Alt Text field for users to describe their photos for accessibility. * Alt texts appear on mouse over on Gallery pages, in the lightbox modal (on mouse over or by clicking the ALT button that appears), and in a box on the permalink page below the photo caption. * Max length of Alt Text is 5,000 characters. * Fix a bug with the right-click blocker not working on the lightbox modal.
This commit is contained in:
parent
742a5fa1af
commit
cf6249c415
|
@ -94,6 +94,7 @@ var (
|
||||||
const (
|
const (
|
||||||
MaxPhotoWidth = 1280
|
MaxPhotoWidth = 1280
|
||||||
ProfilePhotoWidth = 512
|
ProfilePhotoWidth = 512
|
||||||
|
AltTextMaxLength = 5000
|
||||||
|
|
||||||
// Quotas for uploaded photos.
|
// Quotas for uploaded photos.
|
||||||
PhotoQuotaUncertified = 6
|
PhotoQuotaUncertified = 6
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"code.nonshy.com/nonshy/website/pkg/chat"
|
"code.nonshy.com/nonshy/website/pkg/chat"
|
||||||
"code.nonshy.com/nonshy/website/pkg/config"
|
"code.nonshy.com/nonshy/website/pkg/config"
|
||||||
|
@ -71,7 +72,8 @@ func Edit() http.HandlerFunc {
|
||||||
// Are we saving the changes?
|
// Are we saving the changes?
|
||||||
if r.Method == http.MethodPost {
|
if r.Method == http.MethodPost {
|
||||||
var (
|
var (
|
||||||
caption = r.FormValue("caption")
|
caption = strings.TrimSpace(r.FormValue("caption"))
|
||||||
|
altText = strings.TrimSpace(r.FormValue("alt_text"))
|
||||||
isExplicit = r.FormValue("explicit") == "true"
|
isExplicit = r.FormValue("explicit") == "true"
|
||||||
isGallery = r.FormValue("gallery") == "true"
|
isGallery = r.FormValue("gallery") == "true"
|
||||||
visibility = models.PhotoVisibility(r.FormValue("visibility"))
|
visibility = models.PhotoVisibility(r.FormValue("visibility"))
|
||||||
|
@ -85,6 +87,10 @@ func Edit() http.HandlerFunc {
|
||||||
goingCircle = visibility == models.PhotoInnerCircle && visibility != photo.Visibility
|
goingCircle = visibility == models.PhotoInnerCircle && visibility != photo.Visibility
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if len(altText) > config.AltTextMaxLength {
|
||||||
|
altText = altText[:config.AltTextMaxLength]
|
||||||
|
}
|
||||||
|
|
||||||
// Respect the Site Gallery throttle in case the user is messing around.
|
// Respect the Site Gallery throttle in case the user is messing around.
|
||||||
if SiteGalleryThrottled {
|
if SiteGalleryThrottled {
|
||||||
isGallery = false
|
isGallery = false
|
||||||
|
@ -99,6 +105,7 @@ func Edit() http.HandlerFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
photo.Caption = caption
|
photo.Caption = caption
|
||||||
|
photo.AltText = altText
|
||||||
photo.Explicit = isExplicit
|
photo.Explicit = isExplicit
|
||||||
photo.Gallery = isGallery
|
photo.Gallery = isGallery
|
||||||
photo.Visibility = visibility
|
photo.Visibility = visibility
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"code.nonshy.com/nonshy/website/pkg/config"
|
"code.nonshy.com/nonshy/website/pkg/config"
|
||||||
"code.nonshy.com/nonshy/website/pkg/log"
|
"code.nonshy.com/nonshy/website/pkg/log"
|
||||||
|
@ -60,7 +61,8 @@ func Upload() http.HandlerFunc {
|
||||||
// Are they POSTing?
|
// Are they POSTing?
|
||||||
if r.Method == http.MethodPost {
|
if r.Method == http.MethodPost {
|
||||||
var (
|
var (
|
||||||
caption = r.PostFormValue("caption")
|
caption = strings.TrimSpace(r.PostFormValue("caption"))
|
||||||
|
altText = strings.TrimSpace(r.PostFormValue("alt_text"))
|
||||||
isExplicit = r.PostFormValue("explicit") == "true"
|
isExplicit = r.PostFormValue("explicit") == "true"
|
||||||
visibility = r.PostFormValue("visibility")
|
visibility = r.PostFormValue("visibility")
|
||||||
isGallery = r.PostFormValue("gallery") == "true"
|
isGallery = r.PostFormValue("gallery") == "true"
|
||||||
|
@ -74,6 +76,10 @@ func Upload() http.HandlerFunc {
|
||||||
isGallery = false
|
isGallery = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(altText) > config.AltTextMaxLength {
|
||||||
|
altText = altText[:config.AltTextMaxLength]
|
||||||
|
}
|
||||||
|
|
||||||
// Are they at quota already?
|
// Are they at quota already?
|
||||||
if photoCount >= photoQuota {
|
if photoCount >= photoQuota {
|
||||||
session.FlashError(w, r, "You have too many photos to upload a new one. Please delete a photo to make room for a new one.")
|
session.FlashError(w, r, "You have too many photos to upload a new one. Please delete a photo to make room for a new one.")
|
||||||
|
@ -135,6 +141,7 @@ func Upload() http.HandlerFunc {
|
||||||
Filename: filename,
|
Filename: filename,
|
||||||
CroppedFilename: cropFilename,
|
CroppedFilename: cropFilename,
|
||||||
Caption: caption,
|
Caption: caption,
|
||||||
|
AltText: altText,
|
||||||
Visibility: models.PhotoVisibility(visibility),
|
Visibility: models.PhotoVisibility(visibility),
|
||||||
Gallery: isGallery,
|
Gallery: isGallery,
|
||||||
Explicit: isExplicit,
|
Explicit: isExplicit,
|
||||||
|
|
|
@ -19,6 +19,7 @@ type Photo struct {
|
||||||
CroppedFilename string // if cropped, e.g. for profile photo
|
CroppedFilename string // if cropped, e.g. for profile photo
|
||||||
Filesize int64
|
Filesize int64
|
||||||
Caption string
|
Caption string
|
||||||
|
AltText string
|
||||||
Flagged bool // photo has been reported by the community
|
Flagged bool // photo has been reported by the community
|
||||||
Visibility PhotoVisibility `gorm:"index"`
|
Visibility PhotoVisibility `gorm:"index"`
|
||||||
Gallery bool `gorm:"index"` // photo appears in the public gallery (if public)
|
Gallery bool `gorm:"index"` // photo appears in the public gallery (if public)
|
||||||
|
|
|
@ -12,6 +12,10 @@ abbr {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cursor-default {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
/* https://stackoverflow.com/questions/12906789/preventing-an-image-from-being-draggable-or-selectable-without-using-js */
|
/* https://stackoverflow.com/questions/12906789/preventing-an-image-from-being-draggable-or-selectable-without-using-js */
|
||||||
user-drag: none;
|
user-drag: none;
|
||||||
|
@ -46,10 +50,11 @@ img {
|
||||||
|
|
||||||
/* Photo modals in addition to Bulma .modal-content */
|
/* Photo modals in addition to Bulma .modal-content */
|
||||||
.photo-modal {
|
.photo-modal {
|
||||||
width: calc(100vw - 40px);
|
max-width: calc(100vw - 40px);
|
||||||
max-height: calc(100vh - 40px);
|
max-height: calc(100vh - 40px);
|
||||||
}
|
}
|
||||||
.photo-modal #detailImg {
|
.photo-modal #detailImg {
|
||||||
|
position: relative;
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center center;
|
background-position: center center;
|
||||||
|
@ -57,6 +62,14 @@ img {
|
||||||
.photo-modal img {
|
.photo-modal img {
|
||||||
max-height: calc(100vh - 50px);
|
max-height: calc(100vh - 50px);
|
||||||
}
|
}
|
||||||
|
.photo-modal .alt-text {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 4px;
|
||||||
|
left: 4px;
|
||||||
|
}
|
||||||
|
.line-breakable {
|
||||||
|
white-space: pre-line;
|
||||||
|
}
|
||||||
|
|
||||||
/* Custom bulma tag colors */
|
/* Custom bulma tag colors */
|
||||||
.tag:not(body).is-private.is-light {
|
.tag:not(body).is-private.is-light {
|
||||||
|
|
|
@ -5,7 +5,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
cls = 'is-active';
|
cls = 'is-active';
|
||||||
|
|
||||||
// Disable context menu on all images.
|
// Disable context menu on all images.
|
||||||
(document.querySelectorAll('img, video') || []).forEach(node => {
|
(document.querySelectorAll('img, video, #detailImg') || []).forEach(node => {
|
||||||
node.addEventListener('contextmenu', (e) => {
|
node.addEventListener('contextmenu', (e) => {
|
||||||
$modal.classList.add(cls);
|
$modal.classList.add(cls);
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
<li><a href="#private-avatar">Can my <strong>Profile Picture be kept private?</strong></a></li>
|
<li><a href="#private-avatar">Can my <strong>Profile Picture be kept private?</strong></a></li>
|
||||||
<li><a href="#profile-visibility">What are the <strong>visibility options</strong> for my profile page?</a></li>
|
<li><a href="#profile-visibility">What are the <strong>visibility options</strong> for my profile page?</a></li>
|
||||||
<li><a href="#delete-messages">How do I delete direct messages (DMs)?</a></li>
|
<li><a href="#delete-messages">How do I delete direct messages (DMs)?</a></li>
|
||||||
<li><a href="#blocking">How does <strong>blocking somebody</strong> work on nonshy?</a> <span class="tag is-success">NEW Jan 5 2024</span></li>
|
<li><a href="#blocking">How does <strong>blocking somebody</strong> work on nonshy?</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
@ -48,11 +48,12 @@
|
||||||
<li><a href="#nudes-required">Do I have to post my nudes here?</a></li>
|
<li><a href="#nudes-required">Do I have to post my nudes here?</a></li>
|
||||||
<li><a href="#face-in-nudes">Do I have to include my face in my nudes?</a></li>
|
<li><a href="#face-in-nudes">Do I have to include my face in my nudes?</a></li>
|
||||||
<li><a href="#site-gallery">What appears on the Site Gallery?</a></li>
|
<li><a href="#site-gallery">What appears on the Site Gallery?</a></li>
|
||||||
<li><a href="#site-gallery-throttle">Why can't I feature my photo on the Site Gallery?</a> <span class="tag is-success">NEW Jan 5 2024</span></li>
|
<li><a href="#site-gallery-throttle">Why can't I feature my photo on the Site Gallery?</a></li>
|
||||||
<li><a href="#other-people">Can I include other people in my photos?</a></li>
|
<li><a href="#other-people">Can I include other people in my photos?</a></li>
|
||||||
<li><a href="#define-explicit">What is considered "explicit" in photos?</a></li>
|
<li><a href="#define-explicit">What is considered "explicit" in photos?</a></li>
|
||||||
<li><a href="#photoshop">Are digitally altered or 'photoshopped' pictures okay?</a></li>
|
<li><a href="#photoshop">Are digitally altered or 'photoshopped' pictures okay?</a></li>
|
||||||
<li><a href="#downloading">Does this site <strong>prevent people from downloading</strong> my pictures?</a> <span class="tag is-success">UPDATED Jan 10 2024</span></li>
|
<li><a href="#downloading">Does this site <strong>prevent people from downloading</strong> my pictures?</a></li>
|
||||||
|
<li><a href="#alt-text">What is <strong>alt text</strong> on photos about?</a> <span class="tag is-success">NEW Mar 10 2024</span></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
@ -705,10 +706,6 @@
|
||||||
|
|
||||||
<h3 id="downloading">Does this site prevent people from downloading my pictures?</h3>
|
<h3 id="downloading">Does this site prevent people from downloading my pictures?</h3>
|
||||||
|
|
||||||
<p>
|
|
||||||
<span class="tag is-success">Updated Jan 10 2024</span>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
As of November 2023, the {{PrettyTitle}} website does discourage the downloading of pictures
|
As of November 2023, the {{PrettyTitle}} website does discourage the downloading of pictures
|
||||||
to the limited extent that a web page is able to. We have a right-click handler (long press
|
to the limited extent that a web page is able to. We have a right-click handler (long press
|
||||||
|
@ -763,6 +760,32 @@
|
||||||
<a href="/contact?subject=report.user">report them</a> and let us know!
|
<a href="/contact?subject=report.user">report them</a> and let us know!
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<h3 id="alt-text">What is alt text on photos about?</h3>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<span class="tag is-success">NEW: March 15 2024</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
When uploading a photo to your gallery, you can write an "alt text" description of the photo
|
||||||
|
to help with accessibility for the visually impaired. The alt text will appear when hovering
|
||||||
|
a mouse cursor over an image, in the lightbox modal on the Gallery page (where a photo appears
|
||||||
|
in full size over a dimmed background), and beneath the photo on its permalink or comments page.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
It is highly recommended to describe your pictures with alt text. Not only does it help
|
||||||
|
{{PrettyTitle}} to be more inclusive to members with disabilities, but it can also just be
|
||||||
|
a lot of fun to write text descriptions of your nude and sexy photos!
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
If your photo includes any text that is relevant to the meaning of the photo (such as a selfie
|
||||||
|
of you standing in front of a nude beach sign), the alt text is a good place to transcribe the
|
||||||
|
text so that it is accessible to members with disabilities and it can be read aloud by their
|
||||||
|
screen reader software or similar.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h1 id="forum-faqs">Forum FAQs</h1>
|
<h1 id="forum-faqs">Forum FAQs</h1>
|
||||||
|
|
||||||
<h3 id="forum-badges">What do the various badges on the forum mean?</h3>
|
<h3 id="forum-badges">What do the various badges on the forum mean?</h3>
|
||||||
|
|
|
@ -175,6 +175,11 @@
|
||||||
would be visible. -->
|
would be visible. -->
|
||||||
<div id="detailImg">
|
<div id="detailImg">
|
||||||
<img style="visibility: hidden">
|
<img style="visibility: hidden">
|
||||||
|
|
||||||
|
<!-- Alt Text button for accessibility -->
|
||||||
|
<button class="button is-small alt-text py-1 px-2">
|
||||||
|
<strong>ALT</strong>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="modal-close is-large" aria-label="close"></button>
|
<button class="modal-close is-large" aria-label="close"></button>
|
||||||
|
@ -512,13 +517,19 @@
|
||||||
<!-- GIF video? -->
|
<!-- GIF video? -->
|
||||||
{{if HasSuffix .Filename ".mp4"}}
|
{{if HasSuffix .Filename ".mp4"}}
|
||||||
<video loop controls controlsList="nodownload"
|
<video loop controls controlsList="nodownload"
|
||||||
|
{{if .AltText}}title="{{.AltText}}"{{end}}
|
||||||
{{if BlurExplicit .}}class="blurred-explicit"
|
{{if BlurExplicit .}}class="blurred-explicit"
|
||||||
{{else if (not (eq ($Root.CurrentUser.GetProfileField "autoplay_gif") "false"))}}autoplay
|
{{else if (not (eq ($Root.CurrentUser.GetProfileField "autoplay_gif") "false"))}}autoplay
|
||||||
{{end}}>
|
{{end}}>
|
||||||
<source src="{{PhotoURL .Filename}}" type="video/mp4">
|
<source src="{{PhotoURL .Filename}}" type="video/mp4">
|
||||||
</video>
|
</video>
|
||||||
{{else}}
|
{{else}}
|
||||||
<img src="{{PhotoURL .Filename}}" loading="lazy"{{if BlurExplicit .}} class="blurred-explicit"{{end}}>
|
<a href="/photo/view?id={{.ID}}" data-url="{{PhotoURL .Filename}}" target="_blank"
|
||||||
|
class="js-modal-trigger" data-target="detail-modal">
|
||||||
|
<img src="{{PhotoURL .Filename}}" loading="lazy"
|
||||||
|
{{if BlurExplicit .}}class="blurred-explicit"{{end}}
|
||||||
|
{{if .AltText}}alt="{{.AltText}}" title="{{.AltText}}"{{end}}>
|
||||||
|
</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -633,6 +644,7 @@
|
||||||
<!-- GIF video? -->
|
<!-- GIF video? -->
|
||||||
{{if HasSuffix .Filename ".mp4"}}
|
{{if HasSuffix .Filename ".mp4"}}
|
||||||
<video loop controls controlsList="nodownload"
|
<video loop controls controlsList="nodownload"
|
||||||
|
{{if .AltText}}title="{{.AltText}}"{{end}}
|
||||||
{{if BlurExplicit .}}class="blurred-explicit"
|
{{if BlurExplicit .}}class="blurred-explicit"
|
||||||
{{else if (not (eq ($Root.CurrentUser.GetProfileField "autoplay_gif") "false"))}}autoplay
|
{{else if (not (eq ($Root.CurrentUser.GetProfileField "autoplay_gif") "false"))}}autoplay
|
||||||
{{end}}>
|
{{end}}>
|
||||||
|
@ -640,9 +652,10 @@
|
||||||
</video>
|
</video>
|
||||||
{{else}}
|
{{else}}
|
||||||
<a href="/photo/view?id={{.ID}}" data-url="{{PhotoURL .Filename}}" target="_blank"
|
<a href="/photo/view?id={{.ID}}" data-url="{{PhotoURL .Filename}}" target="_blank"
|
||||||
class="js-modal-trigger" data-target="detail-modal"
|
class="js-modal-trigger" data-target="detail-modal">
|
||||||
onclick="setModalImage(this.href)">
|
<img src="{{PhotoURL .Filename}}" loading="lazy"
|
||||||
<img src="{{PhotoURL .Filename}}" loading="lazy"{{if BlurExplicit .}} class="blurred-explicit"{{end}}>
|
{{if BlurExplicit .}}class="blurred-explicit"{{end}}
|
||||||
|
{{if .AltText}}alt="{{.AltText}}" title="{{.AltText}}"{{end}}>
|
||||||
</a>
|
</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
@ -749,21 +762,36 @@
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
// Get our modal to trigger it on click of a detail img.
|
// Get our modal to trigger it on click of a detail img.
|
||||||
let $modal = document.querySelector("#detail-modal");
|
let $modal = document.querySelector("#detail-modal"),
|
||||||
|
$altText = $modal.getElementsByTagName("button")[0];
|
||||||
|
|
||||||
|
function setModalImage(url, altText) {
|
||||||
|
let $modalImg = document.querySelector("#detailImg"),
|
||||||
|
$img = $modalImg.getElementsByTagName("img")[0];
|
||||||
|
$img.src = url;
|
||||||
|
$modalImg.style.backgroundImage = `url(${url})`;
|
||||||
|
|
||||||
|
// Alt text?
|
||||||
|
$modalImg.title = altText;
|
||||||
|
$altText.style.display = altText ? "block" : "none";
|
||||||
|
$altText.onclick = (e) => {
|
||||||
|
window.alert(altText);
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
document.querySelectorAll(".js-modal-trigger").forEach(node => {
|
document.querySelectorAll(".js-modal-trigger").forEach(node => {
|
||||||
|
let $img = node.getElementsByTagName("img"),
|
||||||
|
altText = $img[0] != undefined ? $img[0].alt : '';
|
||||||
node.addEventListener("click", (e) => {
|
node.addEventListener("click", (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setModalImage(node.dataset.url);
|
setModalImage(node.dataset.url, altText);
|
||||||
$modal.classList.add("is-active");
|
$modal.classList.add("is-active");
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
function setModalImage(url) {
|
|
||||||
let $modalImg = document.querySelector("#detailImg"),
|
|
||||||
$img = $modalImg.getElementsByTagName("img")[0];
|
|
||||||
$img.src = url;
|
|
||||||
$modalImg.style.backgroundImage = `url(${url})`;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
@ -87,6 +87,14 @@
|
||||||
{{.Photo.Caption}}
|
{{.Photo.Caption}}
|
||||||
{{else}}<em>No caption</em>{{end}}
|
{{else}}<em>No caption</em>{{end}}
|
||||||
|
|
||||||
|
<!-- Alt Text -->
|
||||||
|
{{if .Photo.AltText}}
|
||||||
|
<div class="box my-4 py-3 px-4 is-size-7">
|
||||||
|
<strong class="tag is-grey mr-2 cursor-default">Alt Text</strong>
|
||||||
|
<span class="line-breakable">{{.Photo.AltText}}</span>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
<!-- Admin actions: quick mark photo as explicit -->
|
<!-- Admin actions: quick mark photo as explicit -->
|
||||||
{{if and (.CurrentUser.IsAdmin) (not .Photo.Explicit)}}
|
{{if and (.CurrentUser.IsAdmin) (not .Photo.Explicit)}}
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
|
|
|
@ -223,10 +223,35 @@
|
||||||
<input type="text" class="input"
|
<input type="text" class="input"
|
||||||
name="caption"
|
name="caption"
|
||||||
id="caption"
|
id="caption"
|
||||||
placeholder="Caption"
|
placeholder="A short sentence about this picture"
|
||||||
value="{{.EditPhoto.Caption}}">
|
value="{{.EditPhoto.Caption}}">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="field mb-5">
|
||||||
|
<div class="columns is-mobile mb-0">
|
||||||
|
<div class="column">
|
||||||
|
<label class="label" for="alt_text">Alt Text</label>
|
||||||
|
</div>
|
||||||
|
<div class="column is-narrow">
|
||||||
|
<small id="alt_text_length">0/5000</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<textarea class="textarea"
|
||||||
|
name="alt_text"
|
||||||
|
id="alt_text"
|
||||||
|
cols="40"
|
||||||
|
rows="4"
|
||||||
|
placeholder="Write a description of this photo for the visually impaired"
|
||||||
|
>{{.EditPhoto.AltText}}</textarea>
|
||||||
|
<p class="help">
|
||||||
|
Write a description of this photo to help people with disabilities (such as the
|
||||||
|
vision impaired) to understand the content and meaning of this photo.
|
||||||
|
Max 5,000 characters.
|
||||||
|
<a href="/faq#alt-text" target="_blank">Learn more <i class="fa fa-external-link"></i></a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="field mb-5">
|
<div class="field mb-5">
|
||||||
<label class="label">Photo Visibility</label>
|
<label class="label">Photo Visibility</label>
|
||||||
<div>
|
<div>
|
||||||
|
@ -568,6 +593,29 @@
|
||||||
});
|
});
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
// Alt Text helper.
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
let $altText = document.querySelector("#alt_text"),
|
||||||
|
$length = document.querySelector("#alt_text_length"),
|
||||||
|
maxLength = 5000;
|
||||||
|
|
||||||
|
$altText.maxLength = maxLength;
|
||||||
|
|
||||||
|
$altText.addEventListener("keydown", (e) => {
|
||||||
|
if ($altText.value.length >= maxLength) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let update = () => {
|
||||||
|
$length.innerHTML = `${$altText.value.length}/${maxLength}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
$altText.addEventListener("keyup", update);
|
||||||
|
$altText.addEventListener("change", update);
|
||||||
|
update();
|
||||||
|
});
|
||||||
|
|
||||||
// EditPhoto only: a button to crop their photo to set as a profile pic.
|
// EditPhoto only: a button to crop their photo to set as a profile pic.
|
||||||
function setProfilePhoto() {
|
function setProfilePhoto() {
|
||||||
let $begin = document.querySelector("#editphoto-begin-crop"),
|
let $begin = document.querySelector("#editphoto-begin-crop"),
|
||||||
|
|
Loading…
Reference in New Issue
Block a user