Narrative web: lightbox feature (#1410)
Deature request: #012801 Discourse: https://gramps.discourse.group/t/narrated-website-browsing-media-in-person-page/3195
This commit is contained in:
parent
37e0f8968b
commit
e4264f837f
158
data/css/lightbox.css
Normal file
158
data/css/lightbox.css
Normal file
@ -0,0 +1,158 @@
|
||||
/*
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright 2023- Serge Noiraud
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
**************************************************************************************************
|
||||
GRAMPS cascading style sheet for common lightbox
|
||||
Style Name: n/a (used by many different styles)
|
||||
Style Author: Serge Noiraud based on W3C: https://www.w3schools.com/howto/howto_js_lightbox.asp
|
||||
**************************************************************************************************/
|
||||
|
||||
.MediaRow {
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.MediaRow > .MediaColumn {
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
.MediaRow:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.MediaColumn {
|
||||
float: left;
|
||||
width: 15%;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
/* The Modal (background) */
|
||||
.ModalClass {
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 900;
|
||||
padding-top: 5px;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* Modal Content */
|
||||
.ModalContent {
|
||||
position: relative;
|
||||
background-color: #fefefe;
|
||||
margin: auto;
|
||||
padding: 0;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
/* The Close Button */
|
||||
.MediaClose {
|
||||
color: black;
|
||||
position: absolute;
|
||||
z-index: 960;
|
||||
top: 10px;
|
||||
right: 1%;
|
||||
font-size: 48px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.MediaClose:hover,
|
||||
.MediaClose:focus {
|
||||
color: #999;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.MediaSlide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.MediaCursor {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Next & previous buttons */
|
||||
.MediaPrev,
|
||||
.MediaNext {
|
||||
cursor: pointer;
|
||||
position: fixed;
|
||||
width: auto;
|
||||
top: 300px;
|
||||
padding: 16px;
|
||||
margin-top: -50px;
|
||||
color: black;
|
||||
font-weight: bold;
|
||||
font-size: 48px;
|
||||
transition: 0.6s ease;
|
||||
border-radius: 0 3px 3px 0;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
/* Position the "next button" to the right */
|
||||
.MediaNext {
|
||||
right: 2%;
|
||||
border-radius: 3px 0 0 3px;
|
||||
}
|
||||
|
||||
/* On hover, add a black background color with a little bit see-through */
|
||||
.MediaPrev:hover,
|
||||
.MediaNext:hover {
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* Number text (1/3 etc) */
|
||||
.MediaNumber {
|
||||
font-size: 36px;
|
||||
font-weight: bold;
|
||||
padding: 8px 12px;
|
||||
position: absolute;
|
||||
color: black;
|
||||
background-color: rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
/* Less whitespace on smaller real estate. */
|
||||
@media only screen and (max-width: 1080px) {
|
||||
.MediaNumber {
|
||||
font-size: 12px;
|
||||
}
|
||||
.MediaPrev,
|
||||
.MediaNext {
|
||||
top: 100px;
|
||||
font-size: 24px;
|
||||
}
|
||||
.MediaClose {
|
||||
font-size: 36px;
|
||||
}
|
||||
}
|
||||
|
||||
img.hover-shadow {
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
.hover-shadow:hover {
|
||||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
||||
}
|
60
data/css/lightbox.js
Normal file
60
data/css/lightbox.js
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright 2023- Serge Noiraud
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
**************************************************************************************************
|
||||
GRAMPS javascript for lightbox feature
|
||||
Style Name: n/a (used by many different styles)
|
||||
Javascript Author: Serge Noiraud based on W3C: https://www.w3schools.com/howto/howto_js_lightbox.asp
|
||||
**************************************************************************************************/
|
||||
|
||||
// Open the Modal
|
||||
function openModal() {
|
||||
document.getElementById("MediaModal").style.display = "block";
|
||||
}
|
||||
|
||||
// Close the Modal
|
||||
function closeModal() {
|
||||
document.getElementById("MediaModal").style.display = "none";
|
||||
}
|
||||
|
||||
var slideIndex = 1;
|
||||
showSlides(slideIndex);
|
||||
|
||||
// Next/previous controls
|
||||
function plusSlides(n) {
|
||||
showSlides(slideIndex += n);
|
||||
}
|
||||
|
||||
// Thumbnail image controls
|
||||
function currentSlide(n) {
|
||||
showSlides(slideIndex = n);
|
||||
}
|
||||
|
||||
function showSlides(n) {
|
||||
var i;
|
||||
var slides = document.getElementsByClassName("MediaSlide");
|
||||
if (n > slides.length) {slideIndex = 1}
|
||||
if (n < 1) {slideIndex = slides.length}
|
||||
for (i = 0; i < slides.length; i++) {
|
||||
slides[i].style.display = "none";
|
||||
}
|
||||
slides[slideIndex-1].style.display = "block";
|
||||
}
|
@ -2268,15 +2268,19 @@ class BasePage:
|
||||
|
||||
# make referenced images have the same order as in media list:
|
||||
photolist_handles = {}
|
||||
self.max_img = 0
|
||||
for mediaref in photolist:
|
||||
photolist_handles[mediaref.get_reference_handle()] = mediaref
|
||||
photo_handle = mediaref.get_reference_handle()
|
||||
photo = self.r_db.get_media_from_handle(photo_handle)
|
||||
mime_type = photo.get_mime_type()
|
||||
if "image" in mime_type:
|
||||
self.max_img += 1
|
||||
photolist_ordered = []
|
||||
for photoref in copy.copy(object_.get_media_list()):
|
||||
if photoref.ref in photolist_handles:
|
||||
photo = photolist_handles[photoref.ref]
|
||||
photolist_ordered.append(photo)
|
||||
# and add any that are left (should there be any?)
|
||||
photolist_ordered += photolist
|
||||
|
||||
# begin individualgallery division and section title
|
||||
with Html("div", class_="subsection", id="indivgallery") as section:
|
||||
@ -2288,46 +2292,120 @@ class BasePage:
|
||||
with Html("div", style="display:%s" % disp,
|
||||
id="toggle_media") as toggle:
|
||||
section += toggle
|
||||
with Html("div", id="medias") as medias:
|
||||
displayed = []
|
||||
# Create the list of media for lightbox
|
||||
for mediaref in photolist_ordered:
|
||||
|
||||
photo_handle = mediaref.get_reference_handle()
|
||||
photo = self.r_db.get_media_from_handle(photo_handle)
|
||||
|
||||
if photo_handle in displayed:
|
||||
continue
|
||||
mime_type = photo.get_mime_type()
|
||||
|
||||
# get media description
|
||||
descr = photo.get_description()
|
||||
|
||||
if mime_type:
|
||||
mime_type = photo.get_mime_type()
|
||||
if mime_type and "image" not in mime_type:
|
||||
try:
|
||||
# create thumbnail url
|
||||
# extension needs to be added as it is not
|
||||
# already there
|
||||
url = (self.report.build_url_fname(photo_handle,
|
||||
"thumb",
|
||||
True,
|
||||
image=True) +
|
||||
".png")
|
||||
url_thumb = (self.report.build_url_fname(
|
||||
photo_handle, "thumb", True,
|
||||
image=True) + ".png")
|
||||
# begin hyperlink
|
||||
toggle += self.media_link(photo_handle, url,
|
||||
descr,
|
||||
medias += self.media_link(photo_handle,
|
||||
url_thumb, descr,
|
||||
uplink=self.uplink,
|
||||
usedescr=True)
|
||||
except (IOError, OSError) as msg:
|
||||
self.r_user.warn(_("Could not add photo to page"),
|
||||
str(msg))
|
||||
else:
|
||||
elif not mime_type:
|
||||
try:
|
||||
# begin hyperlink
|
||||
toggle += self.doc_link(photo_handle, descr,
|
||||
medias += self.doc_link(photo_handle, descr,
|
||||
uplink=self.uplink)
|
||||
except (IOError, OSError) as msg:
|
||||
self.r_user.warn(_("Could not add photo to page"),
|
||||
str(msg))
|
||||
# First part for lightbox
|
||||
lightbox = 0
|
||||
with Html("div", class_="MediaRow") as lightboxes_1:
|
||||
for mediaref in photolist_ordered:
|
||||
photo_handle = mediaref.get_reference_handle()
|
||||
photo = self.r_db.get_media_from_handle(photo_handle)
|
||||
mime_type = photo.get_mime_type()
|
||||
if mime_type and "image" in mime_type:
|
||||
with Html("div", class_="MediaColumn") as boxes:
|
||||
lightboxes_1 += boxes
|
||||
try:
|
||||
url_thumb = (self.report.build_url_fname(
|
||||
photo_handle, "thumb",
|
||||
True, image=True) + ".png")
|
||||
# begin hyperlink
|
||||
lightbox += 1
|
||||
boxes += Html("img", src=url_thumb,
|
||||
inline=True,
|
||||
onclick="openModal();currentSlide(%d)" % lightbox,
|
||||
class_="hover-shadow")
|
||||
except (IOError, OSError) as msg:
|
||||
self.r_user.warn(_("Could not add photo to page"),
|
||||
str(msg))
|
||||
# Second part for lightbox
|
||||
lightbox = 0
|
||||
with Html("div", id="MediaModal",
|
||||
class_="ModalClass") as lightboxes_2:
|
||||
lightboxes_2 += Html("span", "×",
|
||||
onclick="closeModal()",
|
||||
class_="MediaClose MediaCursor",
|
||||
inline=True)
|
||||
with Html("div", class_="ModalContent") as lb_content:
|
||||
lightboxes_2 += lb_content
|
||||
lb_content += Html("a", "❮",
|
||||
class_="MediaPrev",
|
||||
onclick="plusSlides(-1)")
|
||||
lb_content += Html("a", "❯",
|
||||
class_="MediaNext",
|
||||
onclick="plusSlides(1)")
|
||||
for mediaref in photolist_ordered:
|
||||
photo_handle = mediaref.get_reference_handle()
|
||||
photo = self.r_db.get_media_from_handle(photo_handle)
|
||||
mime_type = photo.get_mime_type()
|
||||
if mime_type and "image" in mime_type:
|
||||
with Html("div", class_="MediaSlide") as box:
|
||||
lb_content += box
|
||||
lightbox += 1
|
||||
image_number = "%d/%d - %s" % (lightbox,
|
||||
self.max_img, photo.get_description())
|
||||
box += Html("div", image_number,
|
||||
class_="MediaNumber")
|
||||
try:
|
||||
thb_img = (
|
||||
self.report.build_url_fname(
|
||||
photo_handle, "img", True,
|
||||
image=True) + self.ext)
|
||||
fname_, ext = os.path.splitext(photo.get_path())
|
||||
url_img = (self.report.build_url_fname(photo_handle,
|
||||
"images",
|
||||
True,
|
||||
image=True) + ext)
|
||||
|
||||
box += Html("a", href=thb_img) + (
|
||||
Html("img", src=url_img,
|
||||
style="width:100%")
|
||||
)
|
||||
except (IOError, OSError) as msg:
|
||||
self.r_user.warn(_("Could not add photo to page"),
|
||||
str(msg))
|
||||
displayed.append(photo_handle)
|
||||
if lightboxes_1:
|
||||
toggle += lightboxes_1
|
||||
toggle += lightboxes_2
|
||||
if lightbox < len(photolist):
|
||||
toggle += Html("h3",
|
||||
self._("Other media: vidéos, pdfs..."),
|
||||
inline=True)
|
||||
if medias:
|
||||
toggle += medias
|
||||
|
||||
# add fullclear for proper styling
|
||||
section += FULLCLEAR
|
||||
|
@ -395,7 +395,6 @@ class EventPages(BasePage):
|
||||
|
||||
ldatec = event.get_change_time()
|
||||
event_media_list = event.get_media_list()
|
||||
|
||||
self.uplink = True
|
||||
subdirs = True
|
||||
evt_type = self._(event.get_type().xml_str())
|
||||
@ -404,7 +403,19 @@ class EventPages(BasePage):
|
||||
|
||||
output_file, sio = self.report.create_file(event_handle, "evt")
|
||||
result = self.write_header(self._("Events"))
|
||||
eventpage, dummy_head, dummy_body, outerwrapper = result
|
||||
eventpage, head, dummy_body, outerwrapper = result
|
||||
if event_media_list and self.create_media:
|
||||
if self.the_lang and not self.usecms:
|
||||
fname = "/".join(["..", "css", "lightbox.css"])
|
||||
jsname = "/".join(["..", "css", "lightbox.js"])
|
||||
else:
|
||||
fname = "/".join(["css", "lightbox.css"])
|
||||
jsname = "/".join(["css", "lightbox.js"])
|
||||
url = self.report.build_url_fname(fname, None, self.uplink)
|
||||
head += Html("link", href=url, type="text/css",
|
||||
media="screen", rel="stylesheet")
|
||||
url = self.report.build_url_fname(jsname, None, self.uplink)
|
||||
head += Html("script", src=url, type="text/javascript", inline=True)
|
||||
|
||||
# start event detail division
|
||||
with Html("div", class_="content", id="EventDetail") as eventdetail:
|
||||
|
@ -390,7 +390,7 @@ class FamilyPages(BasePage):
|
||||
|
||||
output_file, sio = self.report.create_file(family.get_handle(), "fam")
|
||||
result = self.write_header(family_name)
|
||||
familydetailpage, dummy_head, dummy_body, outerwrapper = result
|
||||
familydetailpage, head, dummy_body, outerwrapper = result
|
||||
|
||||
# begin FamilyDetaill division
|
||||
with Html("div", class_="content",
|
||||
@ -400,6 +400,18 @@ class FamilyPages(BasePage):
|
||||
# family media list for initial thumbnail
|
||||
if self.create_media:
|
||||
media_list = family.get_media_list()
|
||||
if media_list:
|
||||
if self.the_lang and not self.usecms:
|
||||
fname = "/".join(["..", "css", "lightbox.css"])
|
||||
jsname = "/".join(["..", "css", "lightbox.js"])
|
||||
else:
|
||||
fname = "/".join(["css", "lightbox.css"])
|
||||
jsname = "/".join(["css", "lightbox.js"])
|
||||
url = self.report.build_url_fname(fname, None, self.uplink)
|
||||
head += Html("link", href=url, type="text/css",
|
||||
media="screen", rel="stylesheet")
|
||||
url = self.report.build_url_fname(jsname, None, self.uplink)
|
||||
head += Html("script", src=url, type="text/javascript", inline=True)
|
||||
# If Event pages are not being created, then we need to display
|
||||
# the family event media here
|
||||
if not self.inc_events:
|
||||
@ -429,7 +441,6 @@ class FamilyPages(BasePage):
|
||||
relationshipdetail += families
|
||||
|
||||
# display additional images as gallery
|
||||
if self.create_media and media_list:
|
||||
addgallery = self.disp_add_img_as_gallery(media_list, family)
|
||||
if addgallery:
|
||||
relationshipdetail += addgallery
|
||||
|
@ -1150,6 +1150,12 @@ class NavWebReport(Report):
|
||||
fname = CSS["behaviour"]["filename"]
|
||||
self.copy_file(fname, "behaviour.css", "css")
|
||||
|
||||
# copy lightbox style sheet and javascript
|
||||
fname = CSS["lightbox"]["filename"]
|
||||
self.copy_file(fname, "lightbox.css", "css")
|
||||
fname = CSS["lightbox_js"]["filename"]
|
||||
self.copy_file(fname, "lightbox.js", "css")
|
||||
|
||||
# copy Menu Layout Style Sheet if Blue or Visually is being
|
||||
# used as the stylesheet?
|
||||
if CSS[self.css]["navigation"]:
|
||||
|
@ -510,7 +510,7 @@ class PersonPages(BasePage):
|
||||
output_file, sio = self.report.create_file(person.get_handle(), "ppl")
|
||||
self.uplink = True
|
||||
result = self.write_header(self.sort_name)
|
||||
indivdetpage, dummy_head, dummy_body, outerwrapper = result
|
||||
indivdetpage, head, dummy_body, outerwrapper = result
|
||||
|
||||
# begin individualdetail division
|
||||
with Html("div", class_="content",
|
||||
@ -587,6 +587,18 @@ class PersonPages(BasePage):
|
||||
media_list += event.get_media_list()
|
||||
|
||||
# display additional images as gallery
|
||||
if photo_list and self.create_media:
|
||||
if self.the_lang and not self.usecms:
|
||||
fname = "/".join(["..", "css", "lightbox.css"])
|
||||
jsname = "/".join(["..", "css", "lightbox.js"])
|
||||
else:
|
||||
fname = "/".join(["css", "lightbox.css"])
|
||||
jsname = "/".join(["css", "lightbox.js"])
|
||||
url = self.report.build_url_fname(fname, None, self.uplink)
|
||||
head += Html("link", href=url, type="text/css",
|
||||
media="screen", rel="stylesheet")
|
||||
url = self.report.build_url_fname(jsname, None, self.uplink)
|
||||
head += Html("script", src=url, type="text/javascript", inline=True)
|
||||
sect7 = self.disp_add_img_as_gallery(media_list, person)
|
||||
if sect7 is not None:
|
||||
individualdetail += sect7
|
||||
|
@ -375,7 +375,18 @@ class PlacePages(BasePage):
|
||||
outerwrapper += placedetail
|
||||
|
||||
media_list = place.get_media_list()
|
||||
if self.create_media:
|
||||
if media_list and self.create_media:
|
||||
if self.the_lang and not self.usecms:
|
||||
fname = "/".join(["..", "css", "lightbox.css"])
|
||||
jsname = "/".join(["..", "css", "lightbox.js"])
|
||||
else:
|
||||
fname = "/".join(["css", "lightbox.css"])
|
||||
jsname = "/".join(["css", "lightbox.js"])
|
||||
url = self.report.build_url_fname(fname, None, self.uplink)
|
||||
head += Html("link", href=url, type="text/css",
|
||||
media="screen", rel="stylesheet")
|
||||
url = self.report.build_url_fname(jsname, None, self.uplink)
|
||||
head += Html("script", src=url, type="text/javascript", inline=True)
|
||||
thumbnail = self.disp_first_img_as_thumbnail(media_list,
|
||||
place)
|
||||
if thumbnail is not None:
|
||||
|
@ -233,7 +233,7 @@ class SourcePages(BasePage):
|
||||
self.uplink = True
|
||||
result = self.write_header("%s - %s" % (self._('Sources'),
|
||||
self.page_title))
|
||||
sourcepage, dummy_head, dummy_body, outerwrapper = result
|
||||
sourcepage, head, dummy_body, outerwrapper = result
|
||||
|
||||
ldatec = 0
|
||||
# begin source detail division
|
||||
@ -242,6 +242,17 @@ class SourcePages(BasePage):
|
||||
|
||||
media_list = source.get_media_list()
|
||||
if self.create_media and media_list:
|
||||
if self.the_lang and not self.usecms:
|
||||
fname = "/".join(["..", "css", "lightbox.css"])
|
||||
jsname = "/".join(["..", "css", "lightbox.js"])
|
||||
else:
|
||||
fname = "/".join(["css", "lightbox.css"])
|
||||
jsname = "/".join(["css", "lightbox.js"])
|
||||
url = self.report.build_url_fname(fname, None, self.uplink)
|
||||
head += Html("link", href=url, type="text/css",
|
||||
media="screen", rel="stylesheet")
|
||||
url = self.report.build_url_fname(jsname, None, self.uplink)
|
||||
head += Html("script", src=url, type="text/javascript", inline=True)
|
||||
thumbnail = self.disp_first_img_as_thumbnail(media_list,
|
||||
source)
|
||||
if thumbnail is not None:
|
||||
|
@ -107,6 +107,14 @@ def load_on_reg(dbstate, uistate, plugin):
|
||||
["behaviour", 0, "Behaviour",
|
||||
path_css('behaviour.css'), None, [], []],
|
||||
|
||||
# media lightbox style sheet
|
||||
["lightbox", 0, "",
|
||||
path_css('lightbox.css'), None, [], []],
|
||||
|
||||
# media lightbox javascript
|
||||
["lightbox_js", 0, "",
|
||||
path_css('lightbox.js'), None, [], []],
|
||||
|
||||
# NarrativeMap stylesheet/ image for NarrativeWeb place maps
|
||||
["NarrativeMaps", 0, "",
|
||||
path_css("narrative-maps.css"), None, [], []],
|
||||
|
Loading…
Reference in New Issue
Block a user