|
| 1 | +(async () => { |
| 2 | + const localStorageGalleryKey = "imageGalleryNavigation-GalleryID"; |
| 3 | + |
| 4 | + // In order to handle scenarios where an image is in multiple galleries, capture ID of gallery the user is navigating from. |
| 5 | + // If user navigates directly to an image URL and image is in multiple galleries, we will just use the first gallery in list. |
| 6 | + // This may break if user jumps around in browser history, in which case we will fall back to basic scenario of assuming first gallery in list. |
| 7 | + async function setupGalleryImageLinks() { |
| 8 | + document.querySelectorAll("a[href*='/images/']").forEach(function (link) { |
| 9 | + link.addEventListener("click", () => { |
| 10 | + var galleryID = window.location.pathname.split("/")[2]; |
| 11 | + localStorage.setItem(localStorageGalleryKey, galleryID); |
| 12 | + }); |
| 13 | + }); |
| 14 | + } |
| 15 | + |
| 16 | + // On image page, get data about gallery (image's position within gallery, next/prev image IDs), |
| 17 | + // add arrow buttons to page, and register arrow keypress handlers, |
| 18 | + async function setupImageContainer() { |
| 19 | + var imageID = window.location.pathname.split("/")[2]; |
| 20 | + var imageGalleries = await findImage(imageID); |
| 21 | + |
| 22 | + if (imageGalleries != null && imageGalleries.length > 0) { |
| 23 | + // Get first entry in galleries list. |
| 24 | + var galleryID = imageGalleries[0]; |
| 25 | + |
| 26 | + // Check if there is a saved gallery ID and it is in gallery list. If true, use saved ID. |
| 27 | + var savedGalleryId = localStorage.getItem(localStorageGalleryKey); |
| 28 | + if (savedGalleryId != null && imageGalleries.includes(savedGalleryId)) { |
| 29 | + galleryID = savedGalleryId; |
| 30 | + } else { |
| 31 | + localStorage.setItem(localStorageGalleryKey, galleryID); |
| 32 | + } |
| 33 | + |
| 34 | + // Get gallery image list. |
| 35 | + var galleryImages = await findGalleryImages(galleryID); |
| 36 | + var totalImageCount = galleryImages.length; |
| 37 | + var currentImageIndex = galleryImages.indexOf(imageID); |
| 38 | + var nextImageID = |
| 39 | + galleryImages[wrapIndex(currentImageIndex + 1, totalImageCount)]; |
| 40 | + var prevImageID = |
| 41 | + galleryImages[wrapIndex(currentImageIndex - 1, totalImageCount)]; |
| 42 | + |
| 43 | + // Add UI elements. |
| 44 | + insertGalleryToolbar(currentImageIndex, totalImageCount, galleryImages); |
| 45 | + insertArrowButtons(nextImageID, prevImageID); |
| 46 | + insertArrowKeyHandlers(nextImageID, prevImageID); |
| 47 | + } |
| 48 | + } |
| 49 | + |
| 50 | + function insertGalleryToolbar( |
| 51 | + currentImageIndex, |
| 52 | + totalImageCount, |
| 53 | + galleryImages |
| 54 | + ) { |
| 55 | + var galleryToolbar = document.createElement("div"); |
| 56 | + galleryToolbar.innerHTML = `<span class="imageGalleryNav-NavTitle">Gallery Image: </span><input type="number" class="text-input imageGalleryNav-NavInput" value="${ |
| 57 | + currentImageIndex + 1 |
| 58 | + }"> <span class="imageGalleryNav-NavTotal">/ ${totalImageCount}</span>`; |
| 59 | + |
| 60 | + var toolbar = document.querySelector("div.image-toolbar"); |
| 61 | + toolbar.parentNode.insertBefore(galleryToolbar, toolbar.nextSibling); |
| 62 | + |
| 63 | + galleryToolbar.querySelector("input").addEventListener("change", (e) => { |
| 64 | + var imageIndex = e.target.value - 1; |
| 65 | + if (imageIndex < 0 || imageIndex > totalImageCount - 1) { |
| 66 | + e.target.value = currentImageIndex + 1; |
| 67 | + e.target.select(); |
| 68 | + } else { |
| 69 | + var imageID = galleryImages[imageIndex]; |
| 70 | + redirectToImage(imageID); |
| 71 | + } |
| 72 | + }); |
| 73 | + |
| 74 | + galleryToolbar.querySelector("input").addEventListener("focus", (e) => { |
| 75 | + e.target.select(); |
| 76 | + }); |
| 77 | + } |
| 78 | + |
| 79 | + function insertArrowButtons(nextImageID, prevImageID) { |
| 80 | + var leftButton = document.createElement("button"); |
| 81 | + leftButton.className = "imageGalleryNav-leftButton btn btn-primary"; |
| 82 | + leftButton.innerText = "<"; |
| 83 | + leftButton.addEventListener("click", () => { |
| 84 | + redirectToImage(prevImageID); |
| 85 | + }); |
| 86 | + |
| 87 | + var rightButton = document.createElement("button"); |
| 88 | + rightButton.className = "imageGalleryNav-rightButton btn btn-primary"; |
| 89 | + rightButton.innerText = ">"; |
| 90 | + rightButton.addEventListener("click", () => { |
| 91 | + redirectToImage(nextImageID); |
| 92 | + }); |
| 93 | + |
| 94 | + document.querySelector("div.image-container").prepend(leftButton); |
| 95 | + document.querySelector("div.image-container").prepend(rightButton); |
| 96 | + } |
| 97 | + |
| 98 | + function insertArrowKeyHandlers(nextImageID, prevImageID) { |
| 99 | + // TODO: Determine how to cleanup this listener properly, so that we can handle left/right arrow key. |
| 100 | + // // Add keypress handlers for arrow keys. |
| 101 | + // document.addEventListener('keydown', function onKeydownHandler(e) { |
| 102 | + // if (!isTextboxFocused()) { |
| 103 | + // if (e.key === "ArrowRight") { |
| 104 | + // redirectToImage(nextImageID); |
| 105 | + // } else if (e.key === "ArrowLeft") { |
| 106 | + // redirectToImage(prevImageID); |
| 107 | + // } |
| 108 | + // } |
| 109 | + // }); |
| 110 | + } |
| 111 | + |
| 112 | + // *** Utility Functions *** |
| 113 | + |
| 114 | + function redirectToImage(imageID) { |
| 115 | + const baseImagesPath = "/images/"; |
| 116 | + // window.location.href = `${baseImagesPath}${imageID}`; |
| 117 | + window.location.replace(`${baseImagesPath}${imageID}`); |
| 118 | + } |
| 119 | + |
| 120 | + function isTextboxFocused() { |
| 121 | + if ( |
| 122 | + document.activeElement.nodeName == "TEXTAREA" || |
| 123 | + document.activeElement.nodeName == "INPUT" || |
| 124 | + (document.activeElement.nodeName == "DIV" && |
| 125 | + document.activeElement.isContentEditable) |
| 126 | + ) { |
| 127 | + return true; |
| 128 | + } else { |
| 129 | + return false; |
| 130 | + } |
| 131 | + } |
| 132 | + |
| 133 | + function wrapIndex(index, arrayLength) { |
| 134 | + return ((index % arrayLength) + arrayLength) % arrayLength; |
| 135 | + } |
| 136 | + |
| 137 | + // *** GQL Calls *** |
| 138 | + |
| 139 | + // Find Image by ID |
| 140 | + // Return Galleries list (id) |
| 141 | + async function findImage(imageID) { |
| 142 | + const variables = { id: imageID }; |
| 143 | + const query = `query ($id: ID!) { findImage(id: $id) { galleries { id } } }`; |
| 144 | + return await csLib |
| 145 | + .callGQL({ query, variables }) |
| 146 | + .then((data) => data.findImage.galleries.map((item) => item.id)); |
| 147 | + } |
| 148 | + |
| 149 | + // Find Images by Gallery ID |
| 150 | + // Return Images list (id) |
| 151 | + async function findGalleryImages(galleryID) { |
| 152 | + const imageFilter = { |
| 153 | + galleries: { value: galleryID, modifier: "INCLUDES_ALL" }, |
| 154 | + }; |
| 155 | + const findFilter = { per_page: -1, sort: "title" }; |
| 156 | + const variables = { image_filter: imageFilter, filter: findFilter }; |
| 157 | + const query = `query ($image_filter: ImageFilterType!, $filter: FindFilterType!) { findImages(image_filter: $image_filter, filter: $filter) { images { id } } }`; |
| 158 | + return await csLib |
| 159 | + .callGQL({ query, variables }) |
| 160 | + .then((data) => data.findImages.images.map((item) => item.id)); |
| 161 | + } |
| 162 | + |
| 163 | + // Wait for galleries page to load. |
| 164 | + csLib.PathElementListener( |
| 165 | + "/galleries/", |
| 166 | + ".image-card", |
| 167 | + setupGalleryImageLinks |
| 168 | + ); // PathElementListener is from cs-ui-lib.js |
| 169 | + |
| 170 | + // Wait for images page to load. |
| 171 | + csLib.PathElementListener( |
| 172 | + "/images/", |
| 173 | + ".image-container", |
| 174 | + setupImageContainer |
| 175 | + ); // PathElementListener is from cs-ui-lib.js |
| 176 | +})(); |
0 commit comments