Move web related stuff to root
							
								
								
									
										46
									
								
								root/Album.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,46 @@ | ||||
| /* Copyright (C) 2021  Alexander Rosenberg | ||||
|  * This file is AGPL v3. See LICENSE file for more information | ||||
|  */ | ||||
| .album-wrapper { | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|     align-items: center; | ||||
|     color: inherit; | ||||
|     text-decoration: inherit; | ||||
|     height: min-content; | ||||
|     margin-bottom: 3em; | ||||
|     margin-left: 3em; | ||||
|     margin-right: 3em; | ||||
| } | ||||
|  | ||||
| .album-thumbnail { | ||||
|     width: 20vw; | ||||
|     height: 20vw; | ||||
|     object-fit: cover; | ||||
|     user-select: none; | ||||
|     pointer-events: none; | ||||
| } | ||||
|  | ||||
| .album-title { | ||||
|     font-size: large; | ||||
|     word-wrap: normal; | ||||
|     text-align: center; | ||||
| } | ||||
|  | ||||
| .album-length { | ||||
|     font-size: medium; | ||||
| } | ||||
|  | ||||
| .sidebar-image-wrapper { | ||||
|     width: 10vw; | ||||
|     height: 10vw; | ||||
|     cursor: pointer; | ||||
| } | ||||
|  | ||||
| .sidebar-image { | ||||
|     object-fit: cover; | ||||
|     height: 100%; | ||||
|     width: 100%; | ||||
|     user-select: none; | ||||
|     pointer-events: none; | ||||
| } | ||||
							
								
								
									
										197
									
								
								root/Album.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,197 @@ | ||||
| <?php | ||||
| /* Copyright (C) 2021  Alexander Rosenberg | ||||
|  * This file is AGPL v3. See LICENSE file for more information | ||||
|  */ | ||||
| class Album { | ||||
|     private const PATH_PREFIX = 'albums'; | ||||
|     private const THUMB_PREFIX = 'thumbnails'; | ||||
|     private const NUMBER_REGEX = '/^[0-9]+(\\.[0-9]+)?$/'; | ||||
|  | ||||
|     private $name; | ||||
|     private $title; | ||||
|     private $thumbnail; | ||||
|     private $images; | ||||
|     private $metadata; | ||||
|  | ||||
|     function __construct($name) { | ||||
|         $this->name = $name; | ||||
|         if (!file_exists($this->get_path())) { | ||||
|             $this->invalidate(); | ||||
|         } else { | ||||
|             $this->read(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private function read() { | ||||
|         $content = file_get_contents($this->get_path() . '/data.json'); | ||||
|         if ($content == null) { | ||||
|             $this->invalidate(); | ||||
|             return; | ||||
|         } | ||||
|         $root = json_decode($content); | ||||
|         if ($root == null) { | ||||
|             $this->invalidate(); | ||||
|             return; | ||||
|         } | ||||
|         $this->title = $root->{'title'}; | ||||
|         $this->thumbnail = $root->{'thumbnail'}; | ||||
|         $this->images = $root->{'images'}; | ||||
|         $this->metadata = $root->{'metadata'}; | ||||
|     } | ||||
|  | ||||
|     private function invalidate() { | ||||
|         $this->title = null; | ||||
|         $this->thumbnail = null; | ||||
|         $this->images = null; | ||||
|     } | ||||
|  | ||||
|     function is_valid() { | ||||
|         if ($this->title == null || $this->thumbnail == null || $this->images == null) { | ||||
|             return false; | ||||
|         } | ||||
|         if (!is_string($this->title) || !is_string($this->thumbnail) || !is_array($this->images)) { | ||||
|             return false; | ||||
|         } | ||||
|         foreach ($this->images as $image) { | ||||
|             if (!is_string($image)) { | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|         unset($image); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     function get_name() { | ||||
|         return $this->name; | ||||
|     } | ||||
|  | ||||
|     function get_path() { | ||||
|         return $this::PATH_PREFIX . '/' . $this->name; | ||||
|     } | ||||
|  | ||||
|     function get_title() { | ||||
|         return $this->title; | ||||
|     } | ||||
|  | ||||
|     function get_thumbnail() { | ||||
|         return $this->get_path() . '/' . $this->thumbnail; | ||||
|     } | ||||
|  | ||||
|     function get_small_thumbnail() { | ||||
|         return $this::THUMB_PREFIX . '/small/' . $this->get_name() . '/' . $this->thumbnail; | ||||
|     } | ||||
|  | ||||
|     function get_images() { | ||||
|         return $this->images; | ||||
|     } | ||||
|  | ||||
|     function get_html() { | ||||
|         $out = '<a href="view.php?album=' . urlencode($this->get_name()) . '" class="album-wrapper">'; | ||||
|         if (file_exists($this->get_thumbnail())) { | ||||
|             $type = mime_content_type($this->get_thumbnail()); | ||||
|         } | ||||
|         $category = explode('/', $type)[0]; | ||||
|         if (!file_exists($this->get_thumbnail())) { | ||||
|             $out .= '<img class="album-thumbnail" src="missing-image-icon.svg"/>'; | ||||
|         } else if ($category == 'audio') { | ||||
|             $out .= '<img class="album-thumbnail" src="music-icon.svg"/>'; | ||||
|         } else { | ||||
|             $out .= '<img class="album-thumbnail" src="' . htmlspecialchars($this->get_small_thumbnail(), ENT_QUOTES | ENT_HTML5) . '"/>'; | ||||
|         } | ||||
|         $album_len = count($this->get_images()); | ||||
|         if ($album_len == 1) { | ||||
|             $out .= '<div class="album-length">' . $album_len . ' Image</div>'; | ||||
|         } else { | ||||
|             $out .= '<div class="album-length">' . $album_len . ' Images</div>'; | ||||
|         } | ||||
|         $out .= '<div class="album-title">' . htmlspecialchars($this->get_title(), ENT_QUOTES | ENT_HTML5) . '</div>'; | ||||
|         $out .= '</a>'; | ||||
|         return $out; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     function get_metadata($image) { | ||||
|         if ($image == null || !in_array($image, $this->images)) { | ||||
|             return null; | ||||
|         } | ||||
|         if ($this->metadata == null || !property_exists($this->metadata, $image)) { | ||||
|             return [ | ||||
|                 'description' => '', | ||||
|                 'date' => '', | ||||
|                 'time' => '', | ||||
|                 'latitude' => '', | ||||
|                 'longitude' => '' | ||||
|             ]; | ||||
|         } | ||||
|         $description = $this->metadata->{$image}->{'description'}; | ||||
|         $date = $this->metadata->{$image}->{'date'}; | ||||
|         $time = $this->metadata->{$image}->{'time'}; | ||||
|         $lat= $this->metadata->{$image}->{'latitude'}; | ||||
|         $lon = $this->metadata->{$image}->{'longitude'}; | ||||
|         if (preg_match($this::NUMBER_REGEX, $lat) === false || preg_match($this::NUMBER_REGEX, $lon) === false) { | ||||
|             $lat = null; | ||||
|             $lon = null; | ||||
|         } | ||||
|         return [ | ||||
|             'description' => $description == null ? '' : htmlspecialchars($description, ENT_QUOTES | ENT_HTML5), | ||||
|             'date' => $date == null ? '' : htmlspecialchars($date, ENT_QUOTES | ENT_HTML5), | ||||
|             'time' => $time == null ? '' : htmlspecialchars($time, ENT_QUOTES | ENT_HTML5), | ||||
|             'latitude' => $lat == null ? '' : htmlspecialchars($lat, ENT_QUOTES | ENT_HTML5), | ||||
|             'longitude' => $lon == null ? '' : htmlspecialchars($lon, ENT_QUOTES | ENT_HTML5) | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     function get_image_index($image) { | ||||
|         for ($i = 0; $i < count($this->images); ++$i) { | ||||
|             if ($image == $this->images[$i]) { | ||||
|                 return $i; | ||||
|             } | ||||
|         } | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     function get_image_entry($image) { | ||||
|         if ($image == null || !in_array($image, $this->images)) { | ||||
|             return null; | ||||
|         } | ||||
|         $index = $this->get_image_index($image); | ||||
|         $rpath = $this->get_path() . '/' . $image; | ||||
|         $path = htmlspecialchars($rpath); | ||||
|         if (file_exists($rpath)) { | ||||
|             $file_size = number_format(filesize($path)); | ||||
|             $rtype = mime_content_type($path); | ||||
|         } else { | ||||
|             $file_size = 0; | ||||
|             $rtype = "Unknown"; | ||||
|         } | ||||
|         $type = htmlspecialchars($rtype); | ||||
|         $category = explode('/', $type)[0]; | ||||
|         $name = htmlspecialchars(basename($path)); | ||||
|         $metadata = $this->get_metadata($image); | ||||
|         $entry = '<div class="sidebar-image-wrapper" onclick="set_main_image(' | ||||
|                . strval($index) . ')" ' . "data-size='$file_size' data-type='$type' data-name='$name' data-desc='" | ||||
|                . $metadata['description'] . "' data-date='" . $metadata['date'] . "' data-time='" . $metadata['time'] . "' " | ||||
|                . "data-lat='" . $metadata['latitude'] . "' data-lon='" . $metadata['longitude'] . "'>"; | ||||
|         if (!file_exists($rpath)) { | ||||
|             $entry .= '<img style="display: none" class="sidebar-image" data-src="missing-image-icon.svg"/>'; | ||||
|         } else if ($category == 'video') { | ||||
|             $thumb_name = htmlspecialchars($this->get_name() . '/' . $image); | ||||
|             $entry .= '<img style="display: none" class="sidebar-image" '; | ||||
|             $entry .= 'data-src="' . $thumb_name . '" '; | ||||
|             $entry .= 'data-type="' . $type . '" '; | ||||
|             $entry .= 'data-videopath="' . $path . '"/>'; | ||||
|         } else if ($category == 'audio') { | ||||
|             $entry .= '<img class="sidebar-image" src="music-icon.svg" '; | ||||
|             $entry .= 'data-audiotype="' . $type . '" '; | ||||
|             $entry .= 'data-audiopath="' . $path . '"/>'; | ||||
|         } else { | ||||
|             list($width, $height) = getimagesize($rpath); | ||||
|             $path = htmlspecialchars($this->get_name() . '/' . $image); | ||||
|             $entry .= '<img style="display: none" class="sidebar-image" data-src="' . $path . '" ' . | ||||
|                'data-size="' . $width . 'x' . $height . '" />'; | ||||
|         } | ||||
|         $entry .= '</div>'; | ||||
|         return $entry; | ||||
|     } | ||||
| } | ||||
| ?> | ||||
							
								
								
									
										62
									
								
								root/change-size-button.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,62 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <!-- Created with Inkscape (http://www.inkscape.org/) --> | ||||
|  | ||||
| <svg | ||||
|    width="500" | ||||
|    height="500" | ||||
|    viewBox="0 0 132.29166 132.29167" | ||||
|    version="1.1" | ||||
|    id="svg5" | ||||
|    inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20, custom)" | ||||
|    sodipodi:docname="change-size-button.svg" | ||||
|    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||||
|    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||||
|    xmlns="http://www.w3.org/2000/svg" | ||||
|    xmlns:svg="http://www.w3.org/2000/svg"> | ||||
|   <sodipodi:namedview | ||||
|      id="namedview7" | ||||
|      pagecolor="#ffffff" | ||||
|      bordercolor="#999999" | ||||
|      borderopacity="1" | ||||
|      inkscape:pageshadow="0" | ||||
|      inkscape:pageopacity="0" | ||||
|      inkscape:pagecheckerboard="true" | ||||
|      inkscape:document-units="mm" | ||||
|      showgrid="false" | ||||
|      units="px" | ||||
|      width="500px" | ||||
|      inkscape:snap-others="true" | ||||
|      inkscape:zoom="0.97531385" | ||||
|      inkscape:cx="242.9987" | ||||
|      inkscape:cy="246.07464" | ||||
|      inkscape:window-width="1280" | ||||
|      inkscape:window-height="739" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="0" | ||||
|      inkscape:window-maximized="1" | ||||
|      inkscape:current-layer="layer1" /> | ||||
|   <defs | ||||
|      id="defs2" /> | ||||
|   <g | ||||
|      inkscape:label="main-layer" | ||||
|      inkscape:groupmode="layer" | ||||
|      id="layer1"> | ||||
|     <g | ||||
|        id="g2850" | ||||
|        inkscape:label="boxes" | ||||
|        transform="matrix(1.2562814,0,0,1.2562813,-16.619556,-16.619554)" | ||||
|        style="fill:#ffffff;fill-opacity:1"> | ||||
|       <path | ||||
|          id="rect846" | ||||
|          style="opacity:0.996191;fill:#ffffff;fill-opacity:1;stroke-width:18.4294;stroke-linejoin:round" | ||||
|          inkscape:label="large-image" | ||||
|          d="M 80,50 C 63.38,50 50,63.38 50,80 v 240 c 0,16.62 13.38,30 30,30 H 187.5 V 315 H 115 C 98.38,315 85,301.62 85,285 V 115 C 85,98.38 98.38,85 115,85 h 170 c 16.62,0 30,13.38 30,30 v 72.5 h 35 V 80 C 350,63.38 336.62,50 320,50 Z M 315,237.5 V 285 c 0,16.62 -13.38,30 -30,30 h -47.5 v 35 H 320 c 16.62,0 30,-13.38 30,-30 v -82.5 z" | ||||
|          transform="scale(0.26458333)" /> | ||||
|       <path | ||||
|          id="rect846-6" | ||||
|          style="opacity:0.996191;fill:#ffffff;fill-opacity:1;stroke-width:4.06343;stroke-linejoin:round" | ||||
|          d="m 59.002083,52.3875 c -3.664479,0 -6.614583,2.950104 -6.614583,6.614584 v 52.916666 c 0,3.66449 2.950104,6.61459 6.614583,6.61459 h 52.916667 c 3.66447,0 6.61458,-2.9501 6.61458,-6.61459 V 59.002084 c 0,-3.66448 -2.95011,-6.614584 -6.61458,-6.614584 z m 7.717013,7.717014 h 37.482634 c 3.66448,0 6.61458,2.950105 6.61458,6.614584 v 37.482652 c 0,3.66448 -2.9501,6.61458 -6.61458,6.61458 H 66.719096 c -3.66448,0 -6.614583,-2.9501 -6.614583,-6.61458 V 66.719098 c 0,-3.664479 2.950103,-6.614584 6.614583,-6.614584 z" | ||||
|          inkscape:label="small-image" /> | ||||
|     </g> | ||||
|   </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 2.7 KiB | 
							
								
								
									
										115
									
								
								root/contract-button.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,115 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <!-- Created with Inkscape (http://www.inkscape.org/) --> | ||||
|  | ||||
| <svg | ||||
|    width="500" | ||||
|    height="500" | ||||
|    viewBox="0 0 132.29166 132.29167" | ||||
|    version="1.1" | ||||
|    id="svg5" | ||||
|    inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20, custom)" | ||||
|    sodipodi:docname="contract-button.svg" | ||||
|    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||||
|    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||||
|    xmlns="http://www.w3.org/2000/svg" | ||||
|    xmlns:svg="http://www.w3.org/2000/svg"> | ||||
|   <sodipodi:namedview | ||||
|      id="namedview7" | ||||
|      pagecolor="#ffffff" | ||||
|      bordercolor="#999999" | ||||
|      borderopacity="1" | ||||
|      inkscape:pageshadow="0" | ||||
|      inkscape:pageopacity="0" | ||||
|      inkscape:pagecheckerboard="true" | ||||
|      inkscape:document-units="mm" | ||||
|      showgrid="false" | ||||
|      units="px" | ||||
|      width="793.70082px" | ||||
|      inkscape:zoom="0.94964927" | ||||
|      inkscape:cx="300.63731" | ||||
|      inkscape:cy="387.51149" | ||||
|      inkscape:window-width="1280" | ||||
|      inkscape:window-height="739" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="0" | ||||
|      inkscape:window-maximized="1" | ||||
|      inkscape:current-layer="layer1" /> | ||||
|   <defs | ||||
|      id="defs2" /> | ||||
|   <g | ||||
|      inkscape:label="Layer 1" | ||||
|      inkscape:groupmode="layer" | ||||
|      id="layer1"> | ||||
|     <g | ||||
|        id="g1705" | ||||
|        transform="matrix(-0.80287355,0.80287357,-0.80287355,-0.80287357,72.25862,44.158047)" | ||||
|        style="fill:#ffffff;fill-opacity:1"> | ||||
|       <g | ||||
|          id="g835"> | ||||
|         <rect | ||||
|            style="opacity:0.996191;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:12.3978;stroke-linejoin:round" | ||||
|            id="rect846" | ||||
|            width="15" | ||||
|            height="45" | ||||
|            x="10" | ||||
|            y="20" /> | ||||
|         <path | ||||
|            style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.466681px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" | ||||
|            d="M 0,20 H 35 L 17.499999,0 Z" | ||||
|            id="path1354" | ||||
|            sodipodi:nodetypes="cccc" /> | ||||
|       </g> | ||||
|     </g> | ||||
|     <g | ||||
|        id="g1705-3" | ||||
|        transform="matrix(-0.80287357,-0.80287355,0.80287357,-0.80287355,88.13362,72.25862)" | ||||
|        style="fill:#ffffff;fill-opacity:1"> | ||||
|       <rect | ||||
|          style="opacity:0.996191;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:12.3978;stroke-linejoin:round" | ||||
|          id="rect846-6" | ||||
|          width="15" | ||||
|          height="45" | ||||
|          x="10" | ||||
|          y="20" /> | ||||
|       <path | ||||
|          style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.466681px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" | ||||
|          d="M 0,20 H 35 L 17.499999,0 Z" | ||||
|          id="path1354-7" | ||||
|          sodipodi:nodetypes="cccc" /> | ||||
|     </g> | ||||
|     <g | ||||
|        id="g1705-5" | ||||
|        transform="matrix(0.80287357,0.80287355,-0.80287357,0.80287355,44.158047,60.033047)" | ||||
|        style="fill:#ffffff;fill-opacity:1"> | ||||
|       <rect | ||||
|          style="opacity:0.996191;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:12.3978;stroke-linejoin:round" | ||||
|          id="rect846-3" | ||||
|          width="15" | ||||
|          height="45" | ||||
|          x="10" | ||||
|          y="20" /> | ||||
|       <path | ||||
|          style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.466681px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" | ||||
|          d="M 0,20 H 35 L 17.499999,0 Z" | ||||
|          id="path1354-5" | ||||
|          sodipodi:nodetypes="cccc" /> | ||||
|     </g> | ||||
|     <g | ||||
|        id="g1705-6" | ||||
|        transform="matrix(0.80287355,-0.80287357,0.80287355,0.80287357,60.033047,88.13362)" | ||||
|        style="fill:#ffffff;fill-opacity:1"> | ||||
|       <rect | ||||
|          style="opacity:0.996191;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:12.3978;stroke-linejoin:round" | ||||
|          id="rect846-2" | ||||
|          width="15" | ||||
|          height="45" | ||||
|          x="10" | ||||
|          y="20" /> | ||||
|       <path | ||||
|          style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.466681px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" | ||||
|          d="M 0,20 H 35 L 17.499999,0 Z" | ||||
|          id="path1354-9" | ||||
|          sodipodi:nodetypes="cccc" /> | ||||
|     </g> | ||||
|   </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 3.9 KiB | 
							
								
								
									
										196
									
								
								root/download-button.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,196 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <svg | ||||
|    xmlns:dc="http://purl.org/dc/elements/1.1/" | ||||
|    xmlns:cc="http://creativecommons.org/ns#" | ||||
|    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | ||||
|    xmlns:svg="http://www.w3.org/2000/svg" | ||||
|    xmlns="http://www.w3.org/2000/svg" | ||||
|    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||||
|    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||||
|    width="100mm" | ||||
|    height="75mm" | ||||
|    viewBox="0 0 100 75" | ||||
|    version="1.1" | ||||
|    id="svg8" | ||||
|    inkscape:version="1.0.2 (e86c870879, 2021-01-15)" | ||||
|    sodipodi:docname="Download Button.svg"> | ||||
|   <defs | ||||
|      id="defs2"> | ||||
|     <clipPath | ||||
|        clipPathUnits="userSpaceOnUse" | ||||
|        id="clipPath959"> | ||||
|       <rect | ||||
|          style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.182671" | ||||
|          id="rect961" | ||||
|          width="12.5" | ||||
|          height="12.5" | ||||
|          x="8.8817842e-16" | ||||
|          y="87.5" /> | ||||
|     </clipPath> | ||||
|     <clipPath | ||||
|        clipPathUnits="userSpaceOnUse" | ||||
|        id="clipPath965"> | ||||
|       <rect | ||||
|          style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.242196" | ||||
|          id="rect967" | ||||
|          width="12.5" | ||||
|          height="12.5" | ||||
|          x="87.5" | ||||
|          y="87.5" /> | ||||
|     </clipPath> | ||||
|     <clipPath | ||||
|        clipPathUnits="userSpaceOnUse" | ||||
|        id="clipPath959-5"> | ||||
|       <rect | ||||
|          style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.182671" | ||||
|          id="rect961-3" | ||||
|          width="12.5" | ||||
|          height="12.5" | ||||
|          x="8.8817842e-16" | ||||
|          y="87.5" /> | ||||
|     </clipPath> | ||||
|     <clipPath | ||||
|        clipPathUnits="userSpaceOnUse" | ||||
|        id="clipPath959-5-6"> | ||||
|       <rect | ||||
|          style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.182671" | ||||
|          id="rect961-3-2" | ||||
|          width="12.5" | ||||
|          height="12.5" | ||||
|          x="8.8817842e-16" | ||||
|          y="87.5" /> | ||||
|     </clipPath> | ||||
|   </defs> | ||||
|   <sodipodi:namedview | ||||
|      id="base" | ||||
|      pagecolor="#ffffff" | ||||
|      bordercolor="#666666" | ||||
|      borderopacity="1.0" | ||||
|      inkscape:pageopacity="0" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:zoom="0.91626869" | ||||
|      inkscape:cx="181.08656" | ||||
|      inkscape:cy="196.28656" | ||||
|      inkscape:document-units="mm" | ||||
|      inkscape:current-layer="layer1" | ||||
|      inkscape:document-rotation="0" | ||||
|      showgrid="false" | ||||
|      inkscape:pagecheckerboard="true" | ||||
|      inkscape:window-width="1280" | ||||
|      inkscape:window-height="745" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="0" | ||||
|      inkscape:window-maximized="1" /> | ||||
|   <metadata | ||||
|      id="metadata5"> | ||||
|     <rdf:RDF> | ||||
|       <cc:Work | ||||
|          rdf:about=""> | ||||
|         <dc:format>image/svg+xml</dc:format> | ||||
|         <dc:type | ||||
|            rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | ||||
|         <dc:title></dc:title> | ||||
|       </cc:Work> | ||||
|     </rdf:RDF> | ||||
|   </metadata> | ||||
|   <g | ||||
|      inkscape:label="Layer 1" | ||||
|      inkscape:groupmode="layer" | ||||
|      id="layer1"> | ||||
|     <g | ||||
|        id="g871" | ||||
|        transform="matrix(0.69495404,0,0,0.75529368,15.252298,-15.529369)" | ||||
|        style="stroke:none;stroke-width:1.03244;stroke-miterlimit:4;stroke-dasharray:none"> | ||||
|       <path | ||||
|          sodipodi:type="star" | ||||
|          style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.03244;stroke-miterlimit:4;stroke-dasharray:none" | ||||
|          id="path40" | ||||
|          sodipodi:sides="3" | ||||
|          sodipodi:cx="50" | ||||
|          sodipodi:cy="75.076759" | ||||
|          sodipodi:r1="24.923243" | ||||
|          sodipodi:r2="12.461621" | ||||
|          sodipodi:arg1="-2.6179939" | ||||
|          sodipodi:arg2="-1.5707963" | ||||
|          inkscape:flatsided="false" | ||||
|          inkscape:rounded="0" | ||||
|          inkscape:randomized="0" | ||||
|          d="M 28.415839,62.615139 50,62.615138 l 21.584161,0 L 60.79208,81.30757 50.000001,100 39.207919,81.30757 Z" | ||||
|          inkscape:transform-center-y="6.23081" /> | ||||
|       <rect | ||||
|          style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.03244;stroke-miterlimit:4;stroke-dasharray:none" | ||||
|          id="rect867" | ||||
|          width="10.427769" | ||||
|          height="58.878593" | ||||
|          x="44.786118" | ||||
|          y="20.560703" /> | ||||
|     </g> | ||||
|     <rect | ||||
|        style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:19.8955;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" | ||||
|        id="rect877" | ||||
|        width="12.5" | ||||
|        height="35" | ||||
|        x="8.8817842e-16" | ||||
|        y="27.499981" /> | ||||
|     <rect | ||||
|        style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:41.5415;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" | ||||
|        id="rect899" | ||||
|        width="75" | ||||
|        height="12.5" | ||||
|        x="12.5" | ||||
|        y="62.499981" /> | ||||
|     <rect | ||||
|        style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:19.8955;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" | ||||
|        id="rect877-6" | ||||
|        width="12.5" | ||||
|        height="35" | ||||
|        x="87.5" | ||||
|        y="27.499981" /> | ||||
|     <circle | ||||
|        style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.431958" | ||||
|        id="path923" | ||||
|        cx="12.5" | ||||
|        cy="87.5" | ||||
|        r="12.5" | ||||
|        clip-path="url(#clipPath959)" | ||||
|        transform="translate(0,-25.000005)" /> | ||||
|     <circle | ||||
|        style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.431958" | ||||
|        id="path923-7" | ||||
|        cx="87.5" | ||||
|        cy="87.5" | ||||
|        r="12.5" | ||||
|        clip-path="url(#clipPath965)" | ||||
|        transform="translate(0,-25.000005)" /> | ||||
|     <circle | ||||
|        style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.431958" | ||||
|        id="path923-5" | ||||
|        cx="12.5" | ||||
|        cy="87.5" | ||||
|        r="12.5" | ||||
|        clip-path="url(#clipPath959-5)" | ||||
|        transform="rotate(90,42.499982,57.500018)" /> | ||||
|     <circle | ||||
|        style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.431958" | ||||
|        id="path923-5-9" | ||||
|        cx="12.5" | ||||
|        cy="87.5" | ||||
|        r="12.5" | ||||
|        clip-path="url(#clipPath959-5-6)" | ||||
|        transform="rotate(180,50,57.500018)" /> | ||||
|     <rect | ||||
|        style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.336751" | ||||
|        id="rect1039" | ||||
|        width="15" | ||||
|        height="12.5" | ||||
|        x="12.5" | ||||
|        y="14.999983" /> | ||||
|     <rect | ||||
|        style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.336751" | ||||
|        id="rect1039-1" | ||||
|        width="15" | ||||
|        height="12.5" | ||||
|        x="72.5" | ||||
|        y="14.999983" /> | ||||
|   </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 6.0 KiB | 
							
								
								
									
										112
									
								
								root/expand-button.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,112 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <!-- Created with Inkscape (http://www.inkscape.org/) --> | ||||
|  | ||||
| <svg | ||||
|    width="500" | ||||
|    height="500" | ||||
|    viewBox="0 0 132.29166 132.29167" | ||||
|    version="1.1" | ||||
|    id="svg5" | ||||
|    inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20, custom)" | ||||
|    sodipodi:docname="expand-button.svg" | ||||
|    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||||
|    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||||
|    xmlns="http://www.w3.org/2000/svg" | ||||
|    xmlns:svg="http://www.w3.org/2000/svg"> | ||||
|   <sodipodi:namedview | ||||
|      id="namedview7" | ||||
|      pagecolor="#ffffff" | ||||
|      bordercolor="#999999" | ||||
|      borderopacity="1" | ||||
|      inkscape:pageshadow="0" | ||||
|      inkscape:pageopacity="0" | ||||
|      inkscape:pagecheckerboard="true" | ||||
|      inkscape:document-units="mm" | ||||
|      showgrid="false" | ||||
|      units="px" | ||||
|      width="793.70082px" | ||||
|      inkscape:zoom="0.94964927" | ||||
|      inkscape:cx="249.03931" | ||||
|      inkscape:cy="247.45978" | ||||
|      inkscape:window-width="1280" | ||||
|      inkscape:window-height="739" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="0" | ||||
|      inkscape:window-maximized="1" | ||||
|      inkscape:current-layer="layer1" /> | ||||
|   <defs | ||||
|      id="defs2" /> | ||||
|   <g | ||||
|      inkscape:label="Layer 1" | ||||
|      inkscape:groupmode="layer" | ||||
|      id="layer1"> | ||||
|     <g | ||||
|        id="g1705" | ||||
|        transform="matrix(0.80287355,-0.80287357,0.80287355,0.80287357,-14.050286,14.050286)" | ||||
|        style="fill:#ffffff;fill-opacity:1"> | ||||
|       <rect | ||||
|          style="opacity:0.996191;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:12.3978;stroke-linejoin:round" | ||||
|          id="rect846" | ||||
|          width="15" | ||||
|          height="45" | ||||
|          x="10" | ||||
|          y="20" /> | ||||
|       <path | ||||
|          style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.466681px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" | ||||
|          d="M 0,20 H 35 L 17.499999,0 Z" | ||||
|          id="path1354" | ||||
|          sodipodi:nodetypes="cccc" /> | ||||
|     </g> | ||||
|     <g | ||||
|        id="g1705-3" | ||||
|        transform="matrix(0.80287357,0.80287355,-0.80287357,0.80287355,118.24138,-14.050286)" | ||||
|        style="fill:#ffffff;fill-opacity:1"> | ||||
|       <rect | ||||
|          style="opacity:0.996191;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:12.3978;stroke-linejoin:round" | ||||
|          id="rect846-6" | ||||
|          width="15" | ||||
|          height="45" | ||||
|          x="10" | ||||
|          y="20" /> | ||||
|       <path | ||||
|          style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.466681px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" | ||||
|          d="M 0,20 H 35 L 17.499999,0 Z" | ||||
|          id="path1354-7" | ||||
|          sodipodi:nodetypes="cccc" /> | ||||
|     </g> | ||||
|     <g | ||||
|        id="g1705-5" | ||||
|        transform="matrix(-0.80287357,-0.80287355,0.80287357,-0.80287355,14.050286,146.34196)" | ||||
|        style="fill:#ffffff;fill-opacity:1"> | ||||
|       <rect | ||||
|          style="opacity:0.996191;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:12.3978;stroke-linejoin:round" | ||||
|          id="rect846-3" | ||||
|          width="15" | ||||
|          height="45" | ||||
|          x="10" | ||||
|          y="20" /> | ||||
|       <path | ||||
|          style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.466681px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" | ||||
|          d="M 0,20 H 35 L 17.499999,0 Z" | ||||
|          id="path1354-5" | ||||
|          sodipodi:nodetypes="cccc" /> | ||||
|     </g> | ||||
|     <g | ||||
|        id="g1705-6" | ||||
|        transform="matrix(-0.80287355,0.80287357,-0.80287355,-0.80287357,146.34196,118.24138)" | ||||
|        style="fill:#ffffff;fill-opacity:1"> | ||||
|       <rect | ||||
|          style="opacity:0.996191;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:12.3978;stroke-linejoin:round" | ||||
|          id="rect846-2" | ||||
|          width="15" | ||||
|          height="45" | ||||
|          x="10" | ||||
|          y="20" /> | ||||
|       <path | ||||
|          style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.466681px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" | ||||
|          d="M 0,20 H 35 L 17.499999,0 Z" | ||||
|          id="path1354-9" | ||||
|          sodipodi:nodetypes="cccc" /> | ||||
|     </g> | ||||
|   </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 3.8 KiB | 
							
								
								
									
										75
									
								
								root/favicon.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,75 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <svg | ||||
|    width="500" | ||||
|    height="500" | ||||
|    viewBox="0 0 132.29166 132.29167" | ||||
|    version="1.1" | ||||
|    id="svg8" | ||||
|    inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20, custom)" | ||||
|    sodipodi:docname="favicon.svg" | ||||
|    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||||
|    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||||
|    xmlns="http://www.w3.org/2000/svg" | ||||
|    xmlns:svg="http://www.w3.org/2000/svg" | ||||
|    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | ||||
|    xmlns:cc="http://creativecommons.org/ns#" | ||||
|    xmlns:dc="http://purl.org/dc/elements/1.1/"> | ||||
|   <defs | ||||
|      id="defs2" /> | ||||
|   <sodipodi:namedview | ||||
|      id="base" | ||||
|      pagecolor="#ffffff" | ||||
|      bordercolor="#666666" | ||||
|      borderopacity="1.0" | ||||
|      inkscape:pageopacity="0.0" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:zoom="0.7" | ||||
|      inkscape:cx="-18.571429" | ||||
|      inkscape:cy="250" | ||||
|      inkscape:document-units="mm" | ||||
|      inkscape:current-layer="layer1" | ||||
|      inkscape:document-rotation="0" | ||||
|      showgrid="false" | ||||
|      units="px" | ||||
|      inkscape:pagecheckerboard="true" | ||||
|      borderlayer="true" | ||||
|      inkscape:window-width="1280" | ||||
|      inkscape:window-height="739" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="0" | ||||
|      inkscape:window-maximized="1" /> | ||||
|   <metadata | ||||
|      id="metadata5"> | ||||
|     <rdf:RDF> | ||||
|       <cc:Work | ||||
|          rdf:about=""> | ||||
|         <dc:format>image/svg+xml</dc:format> | ||||
|         <dc:type | ||||
|            rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | ||||
|       </cc:Work> | ||||
|     </rdf:RDF> | ||||
|   </metadata> | ||||
|   <g | ||||
|      inkscape:label="Frame" | ||||
|      inkscape:groupmode="layer" | ||||
|      id="layer1"> | ||||
|     <rect | ||||
|        style="opacity:0.996191;fill:#ffffff;fill-opacity:1;stroke:#c18f00;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal" | ||||
|        id="rect848" | ||||
|        width="75.478668" | ||||
|        height="108.57991" | ||||
|        x="28.4065" | ||||
|        y="11.855878" /> | ||||
|     <text | ||||
|        xml:space="preserve" | ||||
|        style="font-style:normal;font-weight:normal;font-size:88.1944px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583" | ||||
|        x="36.001266" | ||||
|        y="98.292862" | ||||
|        id="text877"><tspan | ||||
|          sodipodi:role="line" | ||||
|          id="tspan875" | ||||
|          x="36.001266" | ||||
|          y="98.292862" | ||||
|          style="font-size:88.1944px;stroke-width:0.264583">A</tspan></text> | ||||
|   </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 2.4 KiB | 
							
								
								
									
										50
									
								
								root/get-album-entries.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,50 @@ | ||||
| <?php | ||||
| /* Copyright (C) 2021  Alexander Rosenberg | ||||
|  * This file is AGPL v3. See LICENSE file for more information | ||||
|  */ | ||||
| require 'library.php'; | ||||
| require 'Album.php'; | ||||
|  | ||||
| header("Content-Type: text/html"); | ||||
|  | ||||
| $method = $_SERVER['REQUEST_METHOD']; | ||||
|  | ||||
| function get_albums() { | ||||
|     global $LIBRARY_INFO; | ||||
|     $albums = []; | ||||
|     $start = $_GET['start']; | ||||
|     $end = $_GET['end']; | ||||
|     if (!ctype_digit($start) || !ctype_digit($end) || $start > $end) { | ||||
|         echo 'Invalid range!'; | ||||
|         http_response_code(400); | ||||
|         exit(0); | ||||
|     } | ||||
|     $len = count($LIBRARY_INFO['albums']); | ||||
|     if ($start >= $len) { | ||||
|         return array(); | ||||
|     } | ||||
|     if ($end >= $len) { | ||||
|         $end  = $len - 1; | ||||
|     } | ||||
|     $albums = []; | ||||
|     for ($i = $start; $i <= $end; ++$i) { | ||||
|         $albums[] = new Album($LIBRARY_INFO['albums'][$i]); | ||||
|     } | ||||
|     return $albums; | ||||
| } | ||||
|  | ||||
| switch ($method) { | ||||
|     case 'HEAD': | ||||
|     case 'GET': | ||||
|         $albums = get_albums(); | ||||
|         foreach ($albums as $album) { | ||||
|             echo $album->get_html(); | ||||
|         } | ||||
|         break; | ||||
|     default: | ||||
|         echo 'Method not permitted'; | ||||
|         http_response_code(405); | ||||
|         exit(0); | ||||
| } | ||||
|  | ||||
| ?> | ||||
							
								
								
									
										13
									
								
								root/global.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,13 @@ | ||||
| /* Copyright (C) 2021  Alexander Rosenberg | ||||
|  * This file is AGPL v3. See LICENSE file for more information | ||||
|  */ | ||||
| body { | ||||
|     background: #111111; | ||||
|     color: white; | ||||
|     font-family: sans-serif; | ||||
|     overflow-x: hidden; | ||||
| } | ||||
|  | ||||
| .error-message { | ||||
|     color: #ff0000; | ||||
| } | ||||
							
								
								
									
										68
									
								
								root/hd.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,68 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <!-- Created with Inkscape (http://www.inkscape.org/) --> | ||||
|  | ||||
| <svg | ||||
|    width="500" | ||||
|    height="500" | ||||
|    viewBox="0 0 132.29166 132.29167" | ||||
|    version="1.1" | ||||
|    id="svg5" | ||||
|    inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20, custom)" | ||||
|    sodipodi:docname="hd.svg" | ||||
|    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||||
|    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||||
|    xmlns="http://www.w3.org/2000/svg" | ||||
|    xmlns:svg="http://www.w3.org/2000/svg"> | ||||
|   <sodipodi:namedview | ||||
|      id="namedview7" | ||||
|      pagecolor="#ffffff" | ||||
|      bordercolor="#666666" | ||||
|      borderopacity="1.0" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:pageopacity="0.0" | ||||
|      inkscape:pagecheckerboard="true" | ||||
|      inkscape:document-units="mm" | ||||
|      showgrid="false" | ||||
|      units="px" | ||||
|      width="500px" | ||||
|      inkscape:zoom="0.97993827" | ||||
|      inkscape:cx="234.19843" | ||||
|      inkscape:cy="244.91339" | ||||
|      inkscape:window-width="1280" | ||||
|      inkscape:window-height="800" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="0" | ||||
|      inkscape:window-maximized="1" | ||||
|      inkscape:current-layer="layer1" /> | ||||
|   <defs | ||||
|      id="defs2" /> | ||||
|   <g | ||||
|      inkscape:label="Layer 1" | ||||
|      inkscape:groupmode="layer" | ||||
|      id="layer1"> | ||||
|     <text | ||||
|        xml:space="preserve" | ||||
|        style="font-style:normal;font-weight:normal;font-size:146.976px;line-height:1.25;font-family:sans-serif;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:3.6744" | ||||
|        x="-14.424895" | ||||
|        y="107.14634" | ||||
|        id="text2115" | ||||
|        transform="scale(0.80992277,1.2346856)"><tspan | ||||
|          sodipodi:role="line" | ||||
|          style="stroke-width:3.6744;fill:#ffffff;fill-opacity:1" | ||||
|          x="-14.424895" | ||||
|          y="107.14634" | ||||
|          id="tspan2117">H</tspan></text> | ||||
|     <text | ||||
|        xml:space="preserve" | ||||
|        style="font-style:normal;font-weight:normal;font-size:139.958px;line-height:1.25;font-family:sans-serif;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:3.49892" | ||||
|        x="72.028801" | ||||
|        y="102.02992" | ||||
|        id="text4367" | ||||
|        transform="scale(0.77124741,1.2966008)"><tspan | ||||
|          sodipodi:role="line" | ||||
|          style="stroke-width:3.49892;fill:#ffffff;fill-opacity:1" | ||||
|          x="72.028801" | ||||
|          y="102.02992" | ||||
|          id="tspan4369">D</tspan></text> | ||||
|   </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 2.2 KiB | 
							
								
								
									
										27
									
								
								root/index.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,27 @@ | ||||
| /* Copyright (C) 2021  Alexander Rosenberg | ||||
|  * This file is AGPL v3. See LICENSE file for more information | ||||
|  */ | ||||
| body { | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|     justify-content: center; | ||||
| } | ||||
|  | ||||
| header { | ||||
|     width: 100%; | ||||
|     margin-bottom: 1em; | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
|     justify-content: center; | ||||
| } | ||||
|  | ||||
| #library-name { | ||||
|     text-align: center; | ||||
|     font-size: xx-large; | ||||
| } | ||||
|  | ||||
| #thumbnail-grid-wrapper { | ||||
|     display: flex; | ||||
|     flex-wrap: wrap; | ||||
|     justify-content: center; | ||||
| } | ||||
							
								
								
									
										113
									
								
								root/index.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,113 @@ | ||||
| <!DOCTYPE html> | ||||
| <!-- Copyright (C) 2021  Alexander Rosenberg | ||||
|    - This file is AGPL v3. See LICENSE file for more information | ||||
|   --> | ||||
| <html> | ||||
|     <head> | ||||
|         <?php | ||||
|         const INITIAL_ALBUMS = 15; | ||||
|         require 'library.php'; | ||||
|         require 'Album.php'; | ||||
|         if ($_SERVER['REQUEST_METHOD'] != 'GET' && $_SERVER['REQUEST_METHOD'] != 'HEAD') { | ||||
|             http_response_code(406); | ||||
|             display_error_message('Request method not supported'); | ||||
|             exit(0); | ||||
|         } | ||||
|         ?> | ||||
|         <title><?php $LIBRARY_INFO['name'] ?></title> | ||||
|         <link rel="stylesheet" href="global.css" /> | ||||
|         <link rel="stylesheet" href="index.css" /> | ||||
|         <link rel="stylesheet" href="Album.css" /> | ||||
|         <link rel="icon" type="image/svg" href="favicon.svg" /> | ||||
|     </head> | ||||
|     <body> | ||||
|         <header> | ||||
|             <div id="library-name"> | ||||
|                 <?php | ||||
|                 echo htmlspecialchars($LIBRARY_INFO['name'], ENT_QUOTES | ENT_HTML5); | ||||
|                 ?> | ||||
|             </div> | ||||
|         </header> | ||||
|         <div id="thumbnail-grid-wrapper"> | ||||
|             <?php | ||||
|             $size = min(count($LIBRARY_INFO['albums']), INITIAL_ALBUMS); | ||||
|             for ($i = 0; $i < $size; ++$i) { | ||||
|                 $album = new Album($LIBRARY_INFO['albums'][$i]); | ||||
|                 if (!$album->is_valid()) { | ||||
|                     display_error_message("Could not open album: '" . $album->get_name() . "'"); | ||||
|                 } else { | ||||
|                     echo $album->get_html(); | ||||
|                 } | ||||
|             } | ||||
|             ?> | ||||
|         </div> | ||||
|         <script> | ||||
|         var last_index = <?php echo $size; ?> - 1; | ||||
|         var thumbnail_wrapper = document.getElementById('thumbnail-grid-wrapper'); | ||||
|         var last_album = thumbnail_wrapper.children[last_index]; | ||||
|         var all_images = <?php echo $size < INITIAL_ALBUMS ? 'true' : 'false'; ?>; | ||||
|  | ||||
|         function get_more_images() { | ||||
|             if (!all_images) { | ||||
|                 var req = new XMLHttpRequest(); | ||||
|                 req.open('GET', 'get-album-entries.php?start=' + (last_index + 1) + '&end=' + (last_index + <?php echo INITIAL_ALBUMS; ?>)); | ||||
|  | ||||
|                 req.onreadystatechange = function() { | ||||
|                     var DONE = 4; | ||||
|                     var OK = 200; | ||||
|                     if (req.readyState === DONE) { | ||||
|                         if (req.status === OK) { | ||||
|                             if (req.responseText.length === 0) { | ||||
|                                 all_images = true; | ||||
|                             } else { | ||||
|                                 observer.unobserve(last_album); | ||||
|                                 last_index += <?php echo INITIAL_ALBUMS; ?>; | ||||
|                                 thumbnail_wrapper.innerHTML += req.responseText; | ||||
|                                 last_album = thumbnail_wrapper.children[last_index]; | ||||
|                                 observer.observe(last_album); | ||||
|                             } | ||||
|                         } else { | ||||
|                             thumbnail_wrapper.innerHTML += '<div style="font-size: 2vw; color: red;">Could not connect to server</div>'; | ||||
|                             all_images = true; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 req.send(null); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         var observer = new IntersectionObserver(function(entries) { | ||||
|             if(entries[0].isIntersecting === true) { | ||||
|                 get_more_images(); | ||||
|             } | ||||
|         }, { threshold: [0] }); | ||||
|  | ||||
|         observer.observe(last_album); | ||||
|  | ||||
|         // Handle resize stuff | ||||
|         function windowResizeHandler() { | ||||
|              albums = document.querySelectorAll('.album-wrapper'); | ||||
|              if (window.innerWidth < 1000) { | ||||
|                  albums.forEach((e) => { | ||||
|                      img = e.firstChild; | ||||
|                      c = e.children[1]; | ||||
|                      t = e.lastChild; | ||||
|                      img.style.width = "85vw"; | ||||
|                      img.style.height = "85vw"; | ||||
|                  }); | ||||
|              } else { | ||||
|                  albums.forEach((e) => { | ||||
|                      img = e.firstChild; | ||||
|                      c = e.children[1]; | ||||
|                      t = e.lastChild; | ||||
|                      img.style.width = "20vw"; | ||||
|                      img.style.height = "20vw"; | ||||
|                  }); | ||||
|              } | ||||
|         } | ||||
|  | ||||
|         window.addEventListener("resize", windowResizeHandler, true); | ||||
|         window.onload = windowResizeHandler; | ||||
|         </script> | ||||
|     </body> | ||||
| </html> | ||||
							
								
								
									
										51
									
								
								root/library.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,51 @@ | ||||
| <?php | ||||
| /* Copyright (C) 2021  Alexander Rosenberg | ||||
|  * This file is AGPL v3. See LICENSE file for more information | ||||
|  */ | ||||
| const CONFIG_PATH = 'config.json'; | ||||
|  | ||||
| function display_error_message($msg, $html_tags = '') { | ||||
|     echo '<div ' . $html_tags . ' class=".error-message">' . htmlspecialchars($msg, ENT_QUOTES | ENT_HTML5) . '</div>'; | ||||
| } | ||||
|  | ||||
| $LIBRARY_INFO = []; | ||||
|  | ||||
| function read_global_config() { | ||||
|     if (!file_exists(CONFIG_PATH)) { | ||||
|         display_error_message('Could not find config file!'); | ||||
|         exit(0); | ||||
|     } | ||||
|     $config_text = file_get_contents(CONFIG_PATH); | ||||
|     if ($config_text == null) { | ||||
|         display_error_message('Could not read config file!'); | ||||
|         exit(0); | ||||
|     } | ||||
|     $config_json = json_decode($config_text); | ||||
|     if ($config_json == null) { | ||||
|         display_error_message(json_last_error_msg() . '! Could not decode config file!'); | ||||
|         exit(0); | ||||
|     } | ||||
|     $library_name = $config_json->{'library_name'}; | ||||
|     if ($library_name == null) { | ||||
|         $library_name = "An Unknown Person's Library"; | ||||
|     } | ||||
|     $library_albums = $config_json->{'albums'}; | ||||
|     if ($library_albums == null) { | ||||
|         $library_albums = []; | ||||
|     } | ||||
|     $photos_license = $config_json->{'license'}; | ||||
|     if ($photos_license == null) { | ||||
|         $photos_license = "Photos may be copyrighted content"; | ||||
|     } else { | ||||
|         $photos_license = "Photos are " . $photos_license; | ||||
|     } | ||||
|     global $LIBRARY_INFO; | ||||
|     $LIBRARY_INFO = [ | ||||
|         'name' => $library_name, | ||||
|         'albums' => $library_albums, | ||||
|         'license' => $photos_license | ||||
|     ]; | ||||
| } | ||||
|  | ||||
| read_global_config(); | ||||
| ?> | ||||
							
								
								
									
										94
									
								
								root/missing-image-icon.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,94 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <!-- Created with Inkscape (http://www.inkscape.org/) --> | ||||
|  | ||||
| <svg | ||||
|    width="2000" | ||||
|    height="2000" | ||||
|    viewBox="0 0 529.16665 529.16668" | ||||
|    version="1.1" | ||||
|    id="svg5" | ||||
|    inkscape:version="1.1 (c4e8f9ed74, 2021-05-24)" | ||||
|    sodipodi:docname="missing-image-icon.svg" | ||||
|    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||||
|    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||||
|    xmlns="http://www.w3.org/2000/svg" | ||||
|    xmlns:svg="http://www.w3.org/2000/svg"> | ||||
|   <sodipodi:namedview | ||||
|      id="namedview7" | ||||
|      pagecolor="#ffffff" | ||||
|      bordercolor="#999999" | ||||
|      borderopacity="1" | ||||
|      inkscape:pageshadow="0" | ||||
|      inkscape:pageopacity="0" | ||||
|      inkscape:pagecheckerboard="true" | ||||
|      inkscape:document-units="mm" | ||||
|      showgrid="false" | ||||
|      units="px" | ||||
|      width="2000px" | ||||
|      inkscape:zoom="0.23741232" | ||||
|      inkscape:cx="246.40676" | ||||
|      inkscape:cy="1010.8995" | ||||
|      inkscape:window-width="1280" | ||||
|      inkscape:window-height="739" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="0" | ||||
|      inkscape:window-maximized="1" | ||||
|      inkscape:current-layer="layer1" /> | ||||
|   <defs | ||||
|      id="defs2" /> | ||||
|   <g | ||||
|      inkscape:groupmode="layer" | ||||
|      id="layer2" | ||||
|      inkscape:label="Background" | ||||
|      sodipodi:insensitive="true"> | ||||
|     <rect | ||||
|        style="opacity:0.996191;vector-effect:non-scaling-stroke;fill:#ffffff;fill-opacity:1;stroke-width:0.0341276;stroke-linejoin:round;-inkscape-stroke:hairline" | ||||
|        id="rect1175" | ||||
|        width="529.16669" | ||||
|        height="529.16669" | ||||
|        x="0" | ||||
|        y="0" /> | ||||
|   </g> | ||||
|   <g | ||||
|      inkscape:label="Layer 1" | ||||
|      inkscape:groupmode="layer" | ||||
|      id="layer1"> | ||||
|     <g | ||||
|        id="g8714" | ||||
|        transform="translate(0,-65.087535)"> | ||||
|       <path | ||||
|          id="path2633" | ||||
|          style="opacity:0.996191;vector-effect:non-scaling-stroke;fill:#000d00;fill-opacity:1;stroke-width:0.0319981;stroke-linejoin:round;-inkscape-stroke:hairline" | ||||
|          inkscape:transform-center-x="1.9380874e-06" | ||||
|          inkscape:transform-center-y="-43.206048" | ||||
|          d="M 264.58358,134.96515 189.74832,264.58342 114.91304,394.20154 H 264.58358 414.25365 L 339.41837,264.58342 Z m 0,33.50828 59.86792,103.69464 59.86794,103.6945 H 264.58358 144.84725 l 59.86793,-103.6945 z" /> | ||||
|       <text | ||||
|          xml:space="preserve" | ||||
|          style="font-style:normal;font-weight:normal;font-size:123.472px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583" | ||||
|          x="239.83469" | ||||
|          y="338.69376" | ||||
|          id="text4801"><tspan | ||||
|            sodipodi:role="line" | ||||
|            id="tspan4799" | ||||
|            style="font-size:123.472px;stroke-width:0.264583" | ||||
|            x="239.83469" | ||||
|            y="338.69376">!</tspan></text> | ||||
|     </g> | ||||
|     <text | ||||
|        xml:space="preserve" | ||||
|        style="font-style:normal;font-weight:normal;font-size:50.8px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583" | ||||
|        x="266.9646" | ||||
|        y="420.64963" | ||||
|        id="text11314"><tspan | ||||
|          sodipodi:role="line" | ||||
|          id="tspan11312" | ||||
|          style="font-size:50.8px;text-align:center;text-anchor:middle;stroke-width:0.264583" | ||||
|          x="266.9646" | ||||
|          y="420.64963">This image could</tspan><tspan | ||||
|          sodipodi:role="line" | ||||
|          style="font-size:50.8px;text-align:center;text-anchor:middle;stroke-width:0.264583" | ||||
|          x="266.9646" | ||||
|          y="484.14963" | ||||
|          id="tspan14556">not be loaded!</tspan></text> | ||||
|   </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 3.4 KiB | 
							
								
								
									
										104
									
								
								root/music-icon.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,104 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <!-- Created with Inkscape (http://www.inkscape.org/) --> | ||||
|  | ||||
| <svg | ||||
|    width="500" | ||||
|    height="500" | ||||
|    viewBox="0 0 132.29166 132.29167" | ||||
|    version="1.1" | ||||
|    id="svg5" | ||||
|    inkscape:version="1.1 (c4e8f9ed74, 2021-05-24)" | ||||
|    sodipodi:docname="music-icon.svg" | ||||
|    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||||
|    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||||
|    xmlns="http://www.w3.org/2000/svg" | ||||
|    xmlns:svg="http://www.w3.org/2000/svg"> | ||||
|   <sodipodi:namedview | ||||
|      id="namedview7" | ||||
|      pagecolor="#ffffff" | ||||
|      bordercolor="#999999" | ||||
|      borderopacity="1" | ||||
|      inkscape:pageshadow="0" | ||||
|      inkscape:pageopacity="0" | ||||
|      inkscape:pagecheckerboard="0" | ||||
|      inkscape:document-units="mm" | ||||
|      showgrid="false" | ||||
|      units="px" | ||||
|      width="500px" | ||||
|      inkscape:zoom="1.004" | ||||
|      inkscape:cx="250.00001" | ||||
|      inkscape:cy="250.00001" | ||||
|      inkscape:window-width="1280" | ||||
|      inkscape:window-height="739" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="0" | ||||
|      inkscape:window-maximized="1" | ||||
|      inkscape:current-layer="g1967" /> | ||||
|   <defs | ||||
|      id="defs2" /> | ||||
|   <g | ||||
|      inkscape:label="Background" | ||||
|      inkscape:groupmode="layer" | ||||
|      id="layer1" | ||||
|      style="display:inline" | ||||
|      sodipodi:insensitive="true"> | ||||
|     <rect | ||||
|        style="opacity:0.996191;vector-effect:non-scaling-stroke;fill:#c8c8c8;fill-opacity:1;stroke-width:0.044817;stroke-linejoin:round;-inkscape-stroke:hairline" | ||||
|        id="rect846" | ||||
|        width="132.29167" | ||||
|        height="132.29167" | ||||
|        x="-3.5527137e-15" | ||||
|        y="0" /> | ||||
|   </g> | ||||
|   <g | ||||
|      inkscape:groupmode="layer" | ||||
|      id="layer2" | ||||
|      inkscape:label="Note"> | ||||
|     <g | ||||
|        id="g1967" | ||||
|        transform="matrix(1.2177424,0,0,1.2177424,-5.2957739,-6.4550655)"> | ||||
|       <g | ||||
|          id="g1974" | ||||
|          transform="translate(8.6298989,0.97075423)"> | ||||
|         <rect | ||||
|            style="opacity:0.996191;vector-effect:non-scaling-stroke;fill:#3c3c3c;fill-opacity:1;stroke-width:0.0180491;stroke-linejoin:round;-inkscape-stroke:hairline" | ||||
|            id="rect1275" | ||||
|            width="43.454731" | ||||
|            height="6.5182095" | ||||
|            x="19.516083" | ||||
|            y="48.806805" | ||||
|            transform="rotate(-15)" /> | ||||
|         <rect | ||||
|            style="opacity:0.996191;vector-effect:non-scaling-stroke;fill:#3c3c3c;fill-opacity:1;stroke-width:0.0117983;stroke-linejoin:round;-inkscape-stroke:hairline" | ||||
|            id="rect1599" | ||||
|            width="2.6458333" | ||||
|            height="39.6875" | ||||
|            x="31.483219" | ||||
|            y="42.092621" /> | ||||
|         <rect | ||||
|            style="opacity:0.996191;vector-effect:non-scaling-stroke;fill:#3c3c3c;fill-opacity:1;stroke-width:0.0117983;stroke-linejoin:round;-inkscape-stroke:hairline" | ||||
|            id="rect1599-3" | ||||
|            width="2.6458333" | ||||
|            height="39.6875" | ||||
|            x="73.457268" | ||||
|            y="30.845707" /> | ||||
|         <ellipse | ||||
|            style="opacity:0.996191;vector-effect:non-scaling-stroke;fill:#3c3c3c;fill-opacity:1;stroke-width:0.0252576;stroke-linejoin:round;-inkscape-stroke:hairline" | ||||
|            id="path1833" | ||||
|            cx="6.6031189" | ||||
|            cy="87.67186" | ||||
|            rx="5.2916665" | ||||
|            ry="3.3072917" | ||||
|            transform="rotate(-15)" /> | ||||
|         <ellipse | ||||
|            style="opacity:0.996191;vector-effect:non-scaling-stroke;fill:#3c3c3c;fill-opacity:1;stroke-width:0.0252576;stroke-linejoin:round;-inkscape-stroke:hairline" | ||||
|            id="path1833-6" | ||||
|            cx="49.975643" | ||||
|            cy="87.832359" | ||||
|            rx="5.2916665" | ||||
|            ry="3.3072917" | ||||
|            transform="rotate(-15)" /> | ||||
|       </g> | ||||
|     </g> | ||||
|   </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 3.5 KiB | 
							
								
								
									
										73
									
								
								root/sd.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,73 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <!-- Created with Inkscape (http://www.inkscape.org/) --> | ||||
|  | ||||
| <svg | ||||
|    width="500" | ||||
|    height="500" | ||||
|    viewBox="0 0 132.29166 132.29167" | ||||
|    version="1.1" | ||||
|    id="svg5" | ||||
|    inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20, custom)" | ||||
|    sodipodi:docname="sd.svg" | ||||
|    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||||
|    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||||
|    xmlns="http://www.w3.org/2000/svg" | ||||
|    xmlns:svg="http://www.w3.org/2000/svg"> | ||||
|   <sodipodi:namedview | ||||
|      id="namedview7" | ||||
|      pagecolor="#ffffff" | ||||
|      bordercolor="#666666" | ||||
|      borderopacity="1.0" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:pageopacity="0.0" | ||||
|      inkscape:pagecheckerboard="true" | ||||
|      inkscape:document-units="mm" | ||||
|      showgrid="false" | ||||
|      units="px" | ||||
|      width="500px" | ||||
|      inkscape:zoom="0.97993827" | ||||
|      inkscape:cx="258.68976" | ||||
|      inkscape:cy="183.68504" | ||||
|      inkscape:window-width="1280" | ||||
|      inkscape:window-height="800" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="0" | ||||
|      inkscape:window-maximized="1" | ||||
|      inkscape:current-layer="layer1" /> | ||||
|   <defs | ||||
|      id="defs2" /> | ||||
|   <g | ||||
|      inkscape:label="Layer 1" | ||||
|      inkscape:groupmode="layer" | ||||
|      id="layer1"> | ||||
|     <text | ||||
|        xml:space="preserve" | ||||
|        style="font-style:normal;font-weight:normal;font-size:150.148px;line-height:1.25;font-family:sans-serif;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:3.75371" | ||||
|        x="-9.8974504" | ||||
|        y="111.43797" | ||||
|        id="text5502" | ||||
|        transform="scale(0.85843703,1.1649078)"><tspan | ||||
|          sodipodi:role="line" | ||||
|          id="tspan5500" | ||||
|          style="stroke-width:3.75371;fill:#ffffff;fill-opacity:1" | ||||
|          x="-9.8974504" | ||||
|          y="111.43797">S</tspan><tspan | ||||
|          sodipodi:role="line" | ||||
|          style="stroke-width:3.75371;fill:#ffffff;fill-opacity:1" | ||||
|          x="-9.8974504" | ||||
|          y="299.12296" | ||||
|          id="tspan8258" /></text> | ||||
|     <text | ||||
|        xml:space="preserve" | ||||
|        style="font-style:normal;font-weight:normal;font-size:139.957px;line-height:1.25;font-family:sans-serif;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:3.49894" | ||||
|        x="72.028877" | ||||
|        y="102.02939" | ||||
|        id="text17082" | ||||
|        transform="scale(0.77124571,1.2966036)"><tspan | ||||
|          sodipodi:role="line" | ||||
|          id="tspan17080" | ||||
|          style="stroke-width:3.49894;fill:#ffffff;fill-opacity:1" | ||||
|          x="72.028877" | ||||
|          y="102.02939">D</tspan></text> | ||||
|   </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 2.4 KiB | 
							
								
								
									
										255
									
								
								root/view.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,255 @@ | ||||
| /* Copyright (C) 2021  Alexander Rosenberg | ||||
|  * This file is AGPL v3. See LICENSE file for more information | ||||
|  */ | ||||
| #close-button { | ||||
| 	cursor: pointer; | ||||
|     position: absolute; | ||||
|     color: inherit; | ||||
|     text-decoration: inherit; | ||||
|     font-size: xx-large; | ||||
|     top: 1vh; | ||||
|     left: 2vw; | ||||
|     user-select: none; | ||||
| } | ||||
|  | ||||
| #info-button-wrapper { | ||||
|     visibility: hidden; | ||||
|     position: absolute; | ||||
|     font-size: xx-large; | ||||
|     top: 1vh; | ||||
|     right: 2vw; | ||||
|     user-select: none; | ||||
|     display: flex; | ||||
|     justify-content: center; | ||||
|     align-items: center; | ||||
|     flex-wrap: nowrap; | ||||
|     width: min-content; | ||||
|     height: min-content; | ||||
| } | ||||
|  | ||||
| #download-button { | ||||
| 	cursor: pointer; | ||||
|     display: none; | ||||
|     color: inherit; | ||||
|     text-decoration: inherit; | ||||
|     margin-right: 1vw; | ||||
|     border: 1px solid transparent; | ||||
|     padding: 2px; | ||||
| } | ||||
|  | ||||
| #download-button-image { | ||||
|     object-fit: contain; | ||||
| } | ||||
|  | ||||
| #info-button { | ||||
| 	cursor: pointer; | ||||
|     height: max-content; | ||||
|     border: 1px solid white; | ||||
|     border-radius: 50%; | ||||
|     display: flex; | ||||
|     justify-content: center; | ||||
|     align-items: center; | ||||
|     margin: 3px; | ||||
|     margin-right: 1vw; | ||||
| } | ||||
|  | ||||
| #expand-button { | ||||
|     cursor: pointer; | ||||
|     display: none; | ||||
|     object-fit: contain; | ||||
|     border: 1px solid transparent; | ||||
|     padding: 2px; | ||||
| } | ||||
|  | ||||
| #size-button { | ||||
|     cursor: pointer; | ||||
|     display: none; | ||||
|     object-fit: contain; | ||||
|     margin-right: 1vw; | ||||
|     border: 1px solid transparent; | ||||
|     padding: 2px; | ||||
| } | ||||
|  | ||||
| body { | ||||
|     display: flex; | ||||
|     margin-left: 2vw; | ||||
|     margin-right: 2vw; | ||||
|     flex-wrap: wrap; | ||||
|     justify-content: center; | ||||
| } | ||||
|  | ||||
| #album-title { | ||||
|     min-height: 10vh; | ||||
|     font-size: xx-large; | ||||
|     margin-bottom: 2vh; | ||||
|     font-weight: bold; | ||||
|     text-align: center; | ||||
|     width: 100%; | ||||
| } | ||||
|  | ||||
| #sidebar-wrapper { | ||||
|     height: 80vh; | ||||
|     display: flex; | ||||
|     justify-content: center; | ||||
| } | ||||
|  | ||||
| #sidebar { | ||||
|     height: 100%; | ||||
|     width: min-content; | ||||
|     display: grid; | ||||
|     grid-template-columns: repeat(2, auto); | ||||
|     grid-auto-rows: max-content; | ||||
|     overflow: scroll; | ||||
|     justify-content: center; | ||||
| } | ||||
|  | ||||
| #image-and-info-wrapper { | ||||
|     width: 70vw; | ||||
|     height: max-content; | ||||
|     display: flex; | ||||
|     flex-wrap: wrap; | ||||
|     justify-content: center; | ||||
|     margin-left: 2vw; | ||||
| } | ||||
|  | ||||
| #main-image-wrapper { | ||||
|     width: 100%; | ||||
|     height: 76vh; | ||||
| 	overflow: hidden; | ||||
| } | ||||
|  | ||||
| .main-viewer { | ||||
|     position: initial; | ||||
|     width: 100%; | ||||
|     height: 100%; | ||||
|     object-fit: contain; | ||||
|     user-select: none; | ||||
| } | ||||
|  | ||||
| #image-nav-wrapper { | ||||
|     width: 40%; | ||||
|     display: flex; | ||||
|     justify-content: space-between; | ||||
| } | ||||
|  | ||||
| #current-image-text { | ||||
|     font-size: medium; | ||||
|     display: flex; | ||||
|     justify-content: center; | ||||
|     align-items: center; | ||||
| } | ||||
|  | ||||
| #prev-image-button { | ||||
|     font-size: xx-large; | ||||
|     height: min-content; | ||||
|     width: min-content; | ||||
|     user-select: none; | ||||
|     cursor: pointer; | ||||
| } | ||||
|  | ||||
| #next-image-button { | ||||
|     font-size: xx-large; | ||||
|     height: min-content; | ||||
|     width: min-content; | ||||
|     user-select: none; | ||||
|     cursor: pointer; | ||||
| } | ||||
|  | ||||
| #image-description { | ||||
|     width: 90%; | ||||
|     font-size: medium; | ||||
|     word-wrap: normal; | ||||
| } | ||||
|  | ||||
| #metadata-window { | ||||
|     display: none; | ||||
|     position: absolute; | ||||
|     top: 10vh; | ||||
|     right: 2vw; | ||||
|     width: 40vw; | ||||
|     height: min-content; | ||||
|     background-color: #222222; | ||||
|     padding: 1vw; | ||||
| } | ||||
|  | ||||
| #close-metadata-window-button-wrapper { | ||||
|     width: 100%; | ||||
|     display: flex; | ||||
|     justify-content: flex-end; | ||||
| } | ||||
|  | ||||
| #close-metadata-window-button { | ||||
|     margin-top: 0.5vh; | ||||
|     margin-right: 0.5vw; | ||||
|     width: min-content; | ||||
|     height: min-content; | ||||
|     font-size: medium; | ||||
|     user-select: none; | ||||
|     cursor: pointer; | ||||
| } | ||||
|  | ||||
| .metadata-prop-wrapper { | ||||
|     width: 100%; | ||||
|     height: min-content; | ||||
|     font-size: larger; | ||||
|     margin-top: 3%; | ||||
|     margin-bottom: 3%; | ||||
|     display: flex; | ||||
|     flex-wrap: nowrap; | ||||
| } | ||||
|  | ||||
| .metadata-prop-desc { | ||||
|     color: lightgray; | ||||
|     width: 50%; | ||||
|     word-wrap: normal; | ||||
| } | ||||
|  | ||||
| .metadata-prop-value { | ||||
|     color: white; | ||||
|     width: 50%; | ||||
|     word-wrap: normal; | ||||
| } | ||||
|  | ||||
| #map-wrapper { | ||||
|     width: 100%; | ||||
|     height: max-content; | ||||
| } | ||||
|  | ||||
| #source-links { | ||||
|     text-align: center; | ||||
| } | ||||
|  | ||||
| .metadata-link { | ||||
|     display: inline; | ||||
|     font-size: small; | ||||
|     color: lightgray; | ||||
|     text-decoration: underline; | ||||
|     text-align: center; | ||||
| } | ||||
|  | ||||
| #main-audio-image { | ||||
|     height: 80%; | ||||
|     width: 100%; | ||||
| } | ||||
|  | ||||
| #main-audio { | ||||
|     height: 20%; | ||||
|     width: 100%; | ||||
| } | ||||
|  | ||||
| #loading-overlay { | ||||
| 	width: 100%; | ||||
| 	height: 100%; | ||||
| 	position: relative; | ||||
| 	top: -100%; | ||||
| 	background: #000000c0; | ||||
| } | ||||
|  | ||||
| #loading-text { | ||||
| 	width: 100%; | ||||
| 	height: 100%; | ||||
| 	display: flex; | ||||
| 	align-items: center; | ||||
| 	justify-content: center; | ||||
| 	font-size: x-large; | ||||
| } | ||||
							
								
								
									
										580
									
								
								root/view.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,580 @@ | ||||
| <!DOCTYPE htm> | ||||
| <!-- Copyright (C) 2021  Alexander Rosenberg | ||||
|    - This file is AGPL v3. See LICENSE file for more information | ||||
|   --> | ||||
| <html> | ||||
|     <?php | ||||
|     require 'library.php'; | ||||
|     require 'Album.php'; | ||||
|     if ($_SERVER['REQUEST_METHOD'] != 'GET' && $_SERVER['REQUEST_METHOD'] != 'HEAD') { | ||||
|         http_response_code(406); | ||||
|         display_error_message('Request method not supported'); | ||||
|         exit(0); | ||||
|     } | ||||
|     if (!isset($_GET['album']) || !in_array($_GET['album'], $LIBRARY_INFO['albums'])) { | ||||
|         http_response_code(404); | ||||
|         display_error_message('No album specified'); | ||||
|         exit(0); | ||||
|     } | ||||
|     $album = new Album($_GET['album']); | ||||
|     if (!$album->is_valid()) { | ||||
|         http_response_code(404); | ||||
|         display_error_message('Album not found'); | ||||
|         exit(0); | ||||
|     } | ||||
|     if (count($album->get_images()) == 0) { | ||||
|         http_response_code(400); | ||||
|         display_error_message('Album empty'); | ||||
|         exit(0); | ||||
|     } | ||||
|     $image = 0; | ||||
|     if (isset($_GET['image'])) { | ||||
|         $image = $_GET['image']; | ||||
|         if (!ctype_digit($image)) { | ||||
|             http_response_code(404); | ||||
|             display_error_message('Image not found in album'); | ||||
|             exit(0); | ||||
|         } else if ($image >= count($album->get_images())) { | ||||
|             $image = count($album->get_images()) - 1; | ||||
|         } | ||||
|     } | ||||
|     ?> | ||||
|     <head> | ||||
|         <title><?php echo htmlspecialchars($album->get_title() . ' - ' . $LIBRARY_INFO['name'], ENT_QUOTES | ENT_HTML5); ?></title> | ||||
|         <link rel="stylesheet" href="global.css" /> | ||||
|         <link rel="stylesheet" href="view.css" /> | ||||
|         <link rel="stylesheet" href="Album.css" /> | ||||
|         <link rel="icon" type="image/svg" href="favicon.svg" /> | ||||
|     </head> | ||||
|     <body> | ||||
|         <a id="close-button" href="/">X</a> | ||||
|         <div id="info-button-wrapper"> | ||||
|             <a id="download-button" title="Download image" download><img id="download-button-image" src="download-button.svg" /></a> | ||||
|             <div id="info-button" title="Image info" tabindex="0" onclick="show_metadata_window()">i</div> | ||||
|             <img id="size-button" title="Toggle HD view" tabindex="0" onclick="toggle_size()" src="sd.svg" /> | ||||
|             <img id="expand-button" title="Toggle sidebar" tabindex="0" onclick="toggle_expand()" src="expand-button.svg" /> | ||||
|         </div> | ||||
|         <div id="album-title"><?php echo htmlspecialchars($album->get_title(), ENT_QUOTES | ENT_HTML5); ?></div> | ||||
|         <div id="sidebar-wrapper"> | ||||
|             <div id="sidebar"> | ||||
|                 <?php | ||||
|                 foreach ($album->get_images() as $i) { | ||||
|                     echo $album->get_image_entry($i); | ||||
|                 } | ||||
|                 ?> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div id="image-and-info-wrapper"> | ||||
|             <div id="main-image-wrapper"> | ||||
|                 <img class="main-viewer" id="main-image" onload="media_load_handler()" /> | ||||
|                 <video style="display: none;" class="main-viewer" id="main-video" onload="media_load_handler()" controls></video> | ||||
|                 <img style="display: none;" id="main-audio-image" src="music-icon.svg"/> | ||||
|                 <audio style="display: none;" class="main-viewer" id="main-audio" onload="media_load_handler()" controls></audio> | ||||
| 				<div id="loading-overlay"> | ||||
| 					<div id="loading-text">Loading...</div> | ||||
| 				</div> | ||||
|             </div> | ||||
|             <div id="image-nav-wrapper"> | ||||
|                 <div id="prev-image-button" onclick="change_image(-1)" tabindex="0"><</div> | ||||
|                 <div id="current-image-text"><?php echo htmlspecialchars($image + 1 . ' / ' . count($album->get_images()), ENT_QUOTES | ENT_HTML5); ?></div> | ||||
|                 <div id="next-image-button" onclick="change_image(1)" tabindex="0">></div> | ||||
|             </div> | ||||
|             <div id="image-description"></div> | ||||
|         </div> | ||||
|         <div id="metadata-window"> | ||||
|             <div id="close-metadata-window-button-wrapper"> | ||||
|                 <div id="close-metadata-window-button" onclick="hide_metadata_window()">X</div> | ||||
|             </div> | ||||
|             <div class="metadata-prop-wrapper"> | ||||
|                 <div class="metadata-prop-desc" id="date-prop-name">Date Taken:</div> | ||||
|                 <div class="metadata-prop-value" id="date-prop"></div> | ||||
|             </div> | ||||
|             <div class="metadata-prop-wrapper"> | ||||
|                 <div class="metadata-prop-desc" id="time-prop-name">Time Taken:</div> | ||||
|                 <div class="metadata-prop-value" id="time-prop"></div> | ||||
|             </div> | ||||
|             <div class="metadata-prop-wrapper"> | ||||
|                 <div class="metadata-prop-desc">File Name:</div> | ||||
|                 <div class="metadata-prop-value" id="file-name-prop"></div> | ||||
|             </div> | ||||
|             <div class="metadata-prop-wrapper"> | ||||
|                 <div class="metadata-prop-desc" id="type-prop-name">Image Type:</div> | ||||
|                 <div class="metadata-prop-value" id="image-type-prop"></div> | ||||
|             </div> | ||||
|             <div class="metadata-prop-wrapper"> | ||||
|                 <div class="metadata-prop-desc">File Size:</div> | ||||
|                 <div class="metadata-prop-value" id="file-size-prop"></div> | ||||
|             </div> | ||||
|             <div class="metadata-prop-wrapper" id="res-prop-wrapper"> | ||||
|                 <div class="metadata-prop-desc" id="res-prop-name">Image Size:</div> | ||||
|                 <div class="metadata-prop-value" id="image-size-prop"></div> | ||||
|             </div> | ||||
|             <div id="map-wrapper"> | ||||
|                 <iframe id="metadata-map" width="100%" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" style="border: 1px solid black; height: 40vh;"></iframe> | ||||
|                 <div id="source-links"> | ||||
|                     <a class="metadata-link" id="view-map-link" target="_blank">View Larger Map</a> | ||||
|                     · | ||||
|                     <a class="metadata-link" target="_blank" href="https://www.gnu.org/licenses/agpl-3.0.en.html">Page License</a> | ||||
|                     · | ||||
|                     <a class="metadata-link" target="_blank" href="https://gitlab.com/zander671/art-museum">Page Source</a> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|         <script type="text/javascript"> | ||||
|          var PAGE_SIZE = Math.floor((window.innerHeight * 0.8) / 75) * 2; | ||||
|          const MAX_IMAGES = <?php echo count($album->get_images()); ?>; | ||||
|          const MAP_BASE_URL = "https://www.openstreetmap.org/#map=16/"; | ||||
|  | ||||
| 		 var jump_count = 0; | ||||
|          var current_image = <?php echo $image; ?>; | ||||
|          var img_lat = 0; | ||||
|          var img_lon = 0; | ||||
|          var full_size = false; | ||||
| 		 var enable_touch = false; | ||||
|          var expanded = false; | ||||
|  | ||||
|          var album_title = document.getElementById('album-title'); | ||||
|          var info_button = document.getElementById('info-button'); | ||||
|          var info_button_wrapper = document.getElementById('info-button-wrapper'); | ||||
|          var download_button_image = document.getElementById('download-button-image'); | ||||
|          var expand_button = document.getElementById('expand-button'); | ||||
|          var size_button = document.getElementById('size-button'); | ||||
|          var album_title = document.getElementById('album-title'); | ||||
|          var image_and_info_wrapper = document.getElementById('image-and-info-wrapper'); | ||||
|          var main_image_wrapper = document.getElementById('main-image-wrapper'); | ||||
|          var main_image = document.getElementById('main-image'); | ||||
|          var main_video = document.getElementById('main-video'); | ||||
|          var main_audio = document.getElementById('main-audio'); | ||||
|          var main_audio_image = document.getElementById('main-audio-image'); | ||||
|          var download_button = document.getElementById('download-button'); | ||||
|          var sidebar = document.getElementById('sidebar'); | ||||
|          var sidebar_wrapper = document.getElementById('sidebar-wrapper'); | ||||
|          var prev_image_button = document.getElementById('prev-image-button'); | ||||
|          var next_image_button = document.getElementById('next-image-button'); | ||||
|          var current_image_text = document.getElementById('current-image-text'); | ||||
|          var image_description = document.getElementById('image-description'); | ||||
|          var date_prop_name = document.getElementById('date-prop-name'); | ||||
|          var date_prop = document.getElementById('date-prop'); | ||||
|          var time_prop_name = document.getElementById('time-prop-name'); | ||||
|          var time_prop = document.getElementById('time-prop'); | ||||
|          var file_name_prop = document.getElementById('file-name-prop'); | ||||
|          var image_type_prop = document.getElementById('image-type-prop'); | ||||
|          var file_size_prop = document.getElementById('file-size-prop'); | ||||
|          var image_size_prop = document.getElementById('image-size-prop'); | ||||
|          var map_wrapper = document.getElementById('map-wrapper'); | ||||
|          var map_elem = document.getElementById('metadata-map'); | ||||
|          var view_map_link = document.getElementById('view-map-link'); | ||||
|          var metadata_window = document.getElementById('metadata-window'); | ||||
|          var type_prop_name = document.getElementById('type-prop-name'); | ||||
|          var res_prop_name = document.getElementById('res-prop-name'); | ||||
|          var res_prop_wrapper = document.getElementById('res-prop-wrapper'); | ||||
| 		 var loading_overlay = document.getElementById('loading-overlay'); | ||||
|  | ||||
| 		 /* the following 2 functions source: | ||||
| 			https://stackoverflow.com/questions/57776001/how-to-detect-ipad-pro-as-ipad-using-javascript */ | ||||
| 		 function isIOS() { | ||||
| 			 if (/iPad|iPhone|iPod/.test(navigator.platform)) { | ||||
| 				 return true; | ||||
| 			 } else { | ||||
| 				 return navigator.maxTouchPoints && | ||||
| 						navigator.maxTouchPoints > 2 && | ||||
| 						/MacIntel/.test(navigator.platform); | ||||
| 			 } | ||||
| 		 } | ||||
|  | ||||
| 		 function isIpadOS() { | ||||
| 			 return navigator.maxTouchPoints && | ||||
| 					navigator.maxTouchPoints > 2 && | ||||
| 					/MacIntel/.test(navigator.platform); | ||||
| 		 } | ||||
|  | ||||
|  | ||||
|          function show_metadata_window() { | ||||
|              metadata_window.style.display = "initial"; | ||||
|          } | ||||
|  | ||||
|          function hide_metadata_window() { | ||||
|              set_map_location(img_lat, img_lon); | ||||
|              metadata_window.style.display = "none"; | ||||
|          } | ||||
|  | ||||
|          function get_map_source_string(lat, lon, zoom = 15) { | ||||
|              return "https://www.openstreetmap.org/export/embed.html?bbox=" + lon + "," + lat + "," + lon + "," + lat + "&layer=mapnik&marker=" + lat + "," + lon; | ||||
|          } | ||||
|  | ||||
|          function get_map_href_string(lat, lon, zoom = 15) { | ||||
|              return "http://www.openstreetmap.org/?mlat=" + lat + "&mlon=" + lon + "&layers=M#map=" + zoom + "/" + lat + "/" + lon; | ||||
|          } | ||||
|  | ||||
|          function set_map_location(lat, lon, zoom = 15) { | ||||
|              map_elem.src = get_map_source_string(lat, lon, zoom); | ||||
|              view_map_link.href = get_map_href_string(lat, lon, zoom); | ||||
|          } | ||||
|  | ||||
|          function deg_to_dec(loc) { | ||||
|              return (+loc[0]) + (loc[1] / 60) + (loc[2] / 3600); | ||||
|          } | ||||
|  | ||||
| 		 function image_ready(ie) { | ||||
| 			 return ie.complete; | ||||
| 		 } | ||||
|  | ||||
|          // Main image must be fully loaded | ||||
|          function update_image_metadata(ie) { | ||||
|              if (!image_ready(ie)) { | ||||
|                  ie.onload = function() { update_image_metadata(ie) }; | ||||
|              } else { | ||||
|                  date_prop_name.textContent = 'Date Taken: '; | ||||
|                  time_prop_name.textContent = 'Time Taken: '; | ||||
|                  type_prop_name.textContent = 'Image Type: '; | ||||
|                  res_prop_wrapper.style.display = 'flex'; | ||||
| 				 res_prop_name.textContent = 'Image Size: '; | ||||
| 				 image_description.textContent = ie.parentElement.getAttribute('data-desc'); | ||||
|                  var date = ie.parentElement.getAttribute('data-date'); | ||||
|                  if (date === '') { | ||||
|                      date = 'Unknown'; | ||||
|                  } | ||||
|                  var time = ie.parentElement.getAttribute('data-time'); | ||||
|                  if (time === '') { | ||||
|                      time = 'Unknown'; | ||||
|                  } | ||||
|                  date_prop.textContent = date; | ||||
|                  time_prop.textContent = time; | ||||
|                  var lat = ie.parentElement.getAttribute('data-lat'); | ||||
|                  var lon = ie.parentElement.getAttribute('data-lon'); | ||||
|                  if (lat === '' || lon === '') { | ||||
|                      map_wrapper.style.display = "none"; | ||||
|                  } else { | ||||
|                      img_lat = lat; | ||||
|                      img_lon = lon; | ||||
|                      set_map_location(img_lat, img_lon); | ||||
|                      map_wrapper.style.display = "initial"; | ||||
|                  } | ||||
|                  file_name_prop.textContent = ie.parentElement.getAttribute('data-name'); | ||||
|                  file_size_prop.textContent = ie.parentElement.getAttribute('data-size') + " B"; | ||||
|                  image_type_prop.textContent = ie.parentElement.getAttribute('data-type'); | ||||
|                  image_size_prop.textContent = ie.getAttribute('data-size'); | ||||
|              } | ||||
|          } | ||||
|  | ||||
| 		 function video_ready(ve) { | ||||
| 			 return ve.readyState > 3; | ||||
| 		 } | ||||
|  | ||||
|          // Main video must be fully loaded | ||||
|          function update_video_metadata(ve) { | ||||
|              if (!video_ready(ve)) { | ||||
|                  ve.onloadedmetadata = function () { update_video_metadata(ve) }; | ||||
|              } else { | ||||
|                  date_prop_name.textContent = 'Date Recorded: '; | ||||
|                  time_prop_name.textContent = 'Time Recorded: '; | ||||
|                  type_prop_name.textContent = 'Video Type: '; | ||||
|                  res_prop_wrapper.style.display = 'flex'; | ||||
|                  res_prop_name.textContent = 'Video Size: '; | ||||
|                  image_description.textContent = ve.parentElement.getAttribute('data-desc'); | ||||
|                  var date = ve.parentElement.getAttribute('data-date'); | ||||
|                  if (date === '') { | ||||
|                      date = 'Unknown'; | ||||
|                  } | ||||
|                  var time = ve.parentElement.getAttribute('data-time'); | ||||
|                  if (time === '') { | ||||
|                      time = 'Unknown'; | ||||
|                  } | ||||
|                  date_prop.textContent = date; | ||||
|                  time_prop.textContent = time; | ||||
|                  var lat = ve.parentElement.getAttribute('data-lat'); | ||||
|                  var lon = ve.parentElement.getAttribute('data-lon'); | ||||
|                  if (lat === '' || lon === '') { | ||||
|                      map_wrapper.style.display = "none"; | ||||
|                  } else { | ||||
|                      img_lat = lat; | ||||
|                      img_lon = lon; | ||||
|                      set_map_location(img_lat, img_lon); | ||||
|                      map_wrapper.style.display = "initial"; | ||||
|                  } | ||||
|                  file_name_prop.textContent = ve.parentElement.getAttribute('data-name'); | ||||
|                  file_size_prop.textContent = ve.parentElement.getAttribute('data-size') + " B"; | ||||
|                  image_type_prop.textContent = ve.parentElement.getAttribute('data-type'); | ||||
|                  image_size_prop.textContent = ve.videoWidth + "x" + ve.videoHeight; | ||||
|              } | ||||
|          } | ||||
|  | ||||
| 		 function audio_ready(ae) { | ||||
| 			 return ae.readyState > 2; | ||||
| 		 } | ||||
|  | ||||
|          // Main video must be fully loaded | ||||
|          function update_audio_metadata(ae) { | ||||
|              if (!audio_ready(ae)) { | ||||
|                  ae.onloadedmetadata = function () { update_video_metadata(ae) }; | ||||
|              } else { | ||||
|                  date_prop_name.textContent = "Date Recorded: "; | ||||
|                  time_prop_name.textContent = "Time Recorded: "; | ||||
|                  type_prop_name.textContent = 'Audio Type: '; | ||||
|                  res_prop_wrapper.style.display = 'none'; | ||||
|                  res_prop_name.textContent = 'Audio Duration: '; | ||||
|                  image_description.textContent = ae.parentElement.getAttribute('data-desc'); | ||||
|                  var date = ae.parentElement.getAttribute('data-date'); | ||||
|                  if (date === '') { | ||||
|                      date = 'Unknown'; | ||||
|                  } | ||||
|                  var time = ae.parentElement.getAttribute('data-time'); | ||||
|                  if (time === '') { | ||||
|                      time = 'Unknown'; | ||||
|                  } | ||||
|                  date_prop.textContent = date; | ||||
|                  time_prop.textContent = time; | ||||
|                  var lat = ae.parentElement.getAttribute('data-lat'); | ||||
|                  var lon = ae.parentElement.getAttribute('data-lon'); | ||||
|                  if (lat === '' || lon === '') { | ||||
|                      map_wrapper.style.display = "none"; | ||||
|                  } else { | ||||
|                      img_lat = lat; | ||||
|                      img_lon = lon; | ||||
|                      set_map_location(img_lat, img_lon); | ||||
|                      map_wrapper.style.display = "initial"; | ||||
|                  } | ||||
|                  file_name_prop.textContent = ae.parentElement.getAttribute('data-name'); | ||||
|                  file_size_prop.textContent = ae.parentElement.getAttribute('data-size') + " B"; | ||||
|                  image_type_prop.textContent = ae.parentElement.getAttribute('data-type'); | ||||
|              } | ||||
|          } | ||||
|  | ||||
|          function get_time_string(dur) { | ||||
|              var h = Math.floor(dur / 3600); | ||||
|              dur -= h * 3600; | ||||
|              var m = Math.floor(dur / 60); | ||||
|              dur -= m * 60; | ||||
|              var s = Math.round(dur); | ||||
|              if (!(h <= 0)) { | ||||
|                  return h + "h " + m + "m " + s + "s"; | ||||
|              } else if (!(m <= 0)) { | ||||
|                  return m + "m " + s + "s"; | ||||
|              } else { | ||||
|                  return s + "s"; | ||||
|              } | ||||
|          } | ||||
|  | ||||
|          // Handle resize stuff | ||||
|          function windowResizeHandler() { | ||||
|              if (!isIpadOS() && window.innerWidth < 1000) { | ||||
|                  document.querySelectorAll('.sidebar-image-wrapper').forEach((e) => { | ||||
|                      e.style.width = "30vw"; | ||||
|                      e.style.height = "30vw"; | ||||
|                  }); | ||||
|                  sidebar_wrapper.style.width = "100%"; | ||||
|                  main_image_wrapper.style.height = 'initial'; | ||||
|                  main_image_wrapper.style.marginTop = '2em'; | ||||
|                  metadata_window.style.width = "80vw"; | ||||
|                  map_elem.style.height = '60vh'; | ||||
|                  album_title.style.marginTop = '2em'; | ||||
| 				 enable_touch = true; | ||||
|              } else { | ||||
|                  document.querySelectorAll('.sidebar-image-wrapper').forEach((e) => { | ||||
|                      e.style.width = "10vw"; | ||||
|                      e.style.height = "10vw"; | ||||
|                  }); | ||||
|                  sidebar_wrapper.style.width = "initial"; | ||||
|                  main_image_wrapper.style.height = '76vh'; | ||||
|                  main_image_wrapper.style.marginTop = '0'; | ||||
|                  metadata_window.style.width = "40vw"; | ||||
|                  album_title.style.marginTop = '0'; | ||||
|                  map_elem.style.height = '40vh'; | ||||
| 				 enable_touch = false; | ||||
|              } | ||||
|              info_button.style.width = info_button.offsetHeight + 'px'; | ||||
|              download_button_image.style.width = info_button.offsetHeight + 'px'; | ||||
|              download_button_image.style.height = info_button.offsetHeight + 'px'; | ||||
|              download_button.style.display = "initial"; | ||||
|              expand_button.style.width = info_button.offsetHeight + 'px'; | ||||
|              expand_button.style.height = info_button.offsetHeight + 'px'; | ||||
|              expand_button.style.display = "initial"; | ||||
|              size_button.style.width = info_button.offsetHeight + 'px'; | ||||
|              size_button.style.height = info_button.offsetHeight + 'px'; | ||||
|              size_button.style.display = "initial"; | ||||
|              info_button_wrapper.style.visibility = "initial"; | ||||
|          } | ||||
|  | ||||
|          window.addEventListener("resize", windowResizeHandler, true); | ||||
|          window.addEventListener("load", windowResizeHandler, true); | ||||
|  | ||||
| 		 function media_load_handler() { | ||||
| 			 if (jump_count != 0) { | ||||
| 				 set_main_image(current_image + jump_count); | ||||
| 				 jump_count = 0; | ||||
| 			 } else { | ||||
| 				 loading_overlay.style.display = "none"; | ||||
| 			 } | ||||
| 		 } | ||||
|  | ||||
| 		 function change_image(ammount) { | ||||
| 			 if (!video_ready(main_video) && !audio_ready(main_audio) && !image_ready(main_image)) { | ||||
| 				 jump_count += ammount; | ||||
| 			 } else { | ||||
| 				 set_main_image(current_image + ammount); | ||||
| 			 } | ||||
| 		 } | ||||
|  | ||||
|          // Loading images | ||||
|          function set_main_image(image, do_scroll = false, do_metadata = true) { | ||||
| 			 loading_overlay.style.display = "revert"; | ||||
|              if (image < 0 || image >= MAX_IMAGES) { | ||||
|                  return; | ||||
|              } | ||||
|              current_image = image; | ||||
|              var ie = sidebar.children[image].firstChild; | ||||
|              if (main_video.innerHTML !== "") { | ||||
|                  main_video.pause(); | ||||
|              } else if (main_audio.innerHTML !== "") { | ||||
|                  main_audio.pause(); | ||||
|              } | ||||
|              main_video.innerHTML = ""; | ||||
|              main_audio.innerHTML = ""; | ||||
|              main_image.style.display = "none"; | ||||
|              main_video.style.display = "none"; | ||||
|              main_audio.style.display = "none"; | ||||
|              main_audio_image.style.display = "none"; | ||||
|              if (ie.getAttribute('data-audiotype') !== null) { | ||||
|                  var type = ie.getAttribute('data-audiotype'); | ||||
|                  var path = ie.getAttribute('data-audiopath'); | ||||
|                  main_audio.innerHTML = '<source src="' + path + '" type="' + type + '" />'; | ||||
|                  if (do_metadata) { | ||||
|                      update_audio_metadata(ie); | ||||
|                  } | ||||
|                  download_button.href = path; | ||||
|                  main_audio.load(); | ||||
|                  main_audio.style.display = "initial"; | ||||
|                  main_audio_image.style.display = "initial"; | ||||
|              } else if (ie.getAttribute('data-videopath') !== null) { | ||||
|                  var ds = ie.getAttribute('data-videopath'); | ||||
|                  var dt = ie.getAttribute('data-type'); | ||||
|                  main_video.innerHTML = '<source src="' + ds + '" type="' + dt + '" />'; | ||||
|                  if (do_metadata) { | ||||
|                      update_video_metadata(ie); | ||||
|                  } | ||||
|                  download_button.href = ds; | ||||
|                  main_video.load(); | ||||
|                  main_video.style.display = "initial"; | ||||
|              } else { | ||||
|                  var ds; | ||||
|                  if (full_size) { | ||||
|                      ds = 'albums/' + ie.getAttribute('data-src'); | ||||
|                  } else { | ||||
|                      ds = 'thumbnails/large/' + ie.getAttribute('data-src'); | ||||
|                  } | ||||
|                  main_image.src = ds; | ||||
|                  if (do_metadata) { | ||||
|                      update_image_metadata(ie); | ||||
|                  } | ||||
|                  download_button.href = ds; | ||||
|                  main_image.style.display = "initial"; | ||||
|              } | ||||
|              current_image_text.textContent = (image + 1) + ' / ' + MAX_IMAGES; | ||||
|              if (image == 0) { | ||||
|                  prev_image_button.style.visibility = "hidden"; | ||||
|              } else { | ||||
|                  prev_image_button.style.visibility = "visible"; | ||||
|              } | ||||
|              if (image == MAX_IMAGES - 1) { | ||||
|                  next_image_button.style.visibility = "hidden"; | ||||
|              } else { | ||||
|                  next_image_button.style.visibility = "visible"; | ||||
|              } | ||||
|              window.history.replaceState('', '', '?album=<?php echo htmlspecialchars($_GET['album'], | ||||
|                                                                         ENT_QUOTES | ENT_HTML5); ?>&image=' + image); | ||||
|              if (do_scroll) { | ||||
|                  var w = sidebar.children[image]; | ||||
|                  w.scrollIntoView(true); | ||||
|              } | ||||
|          } | ||||
|  | ||||
|          function load_entry_media(elem) { | ||||
|              if (elem.getAttribute('data-visible') !== null) { | ||||
|                  me = elem.firstChild; | ||||
|                  if (me.getAttribute('data-audiotype') !== null) { | ||||
|                      // Audio does not require work | ||||
|                  } else if (me instanceof HTMLImageElement) { | ||||
|                      // Image | ||||
|                      me.src = 'thumbnails/small/' + me.getAttribute('data-src'); | ||||
|                  } | ||||
|                  me.style.display = "initial"; | ||||
|              } | ||||
|          } | ||||
|  | ||||
|          function image_scroll_callback(entries, observer) { | ||||
|              entries.forEach((entry) => { | ||||
|                  var elem = entry.target; | ||||
|                  if (entry.isIntersecting) { | ||||
|                      elem.setAttribute('data-visible', ''); | ||||
|                      setTimeout(load_entry_media, 250, elem); | ||||
|                      observer.unobserve(elem); | ||||
|                  } else { | ||||
|                      elem.removeAttribute('data-visible'); | ||||
|                  } | ||||
|              }); | ||||
|          } | ||||
|  | ||||
|          var io = new IntersectionObserver(image_scroll_callback, { root: sidebar }); | ||||
|          for (i = 0; i < sidebar.children.length; ++i) { | ||||
|              io.observe(sidebar.children[i]); | ||||
|          } | ||||
|          set_main_image(current_image, true); | ||||
|  | ||||
|          // Key press and swipe | ||||
|          window.onkeydown = function (gfg) { | ||||
|              if (gfg.keyCode == 37 /* left */) { | ||||
|                  set_main_image(current_image - 1, true); | ||||
|              } else if (gfg.keyCode == 39 /* right */) { | ||||
|                  set_main_image(current_image + 1, true); | ||||
|              } | ||||
|          } | ||||
|  | ||||
|          var touchstartX = 0; | ||||
|          var touchendX = 0; | ||||
|          function handleSwipes() { | ||||
| 			 if (enable_touch) { | ||||
| 				 if (touchendX < touchstartX) { | ||||
| 					 set_main_image(current_image + 1, true); | ||||
| 				 } else if (touchendX > touchstartX) { | ||||
| 					 set_main_image(current_image - 1, true); | ||||
| 				 } | ||||
| 				 main_image_wrapper.scrollIntoView(true); | ||||
| 			 } | ||||
|          } | ||||
|          main_image_wrapper.addEventListener('touchstart', e => { | ||||
|              touchstartX = e.changedTouches[0].screenX; | ||||
|          }); | ||||
|          main_image_wrapper.addEventListener('touchend', e => { | ||||
|              touchendX = e.changedTouches[0].screenX; | ||||
|              handleSwipes(); | ||||
|          }); | ||||
|  | ||||
|          function toggle_expand() { | ||||
|              if (expanded) { | ||||
|                  sidebar_wrapper.style.display = 'flex'; | ||||
|                  image_and_info_wrapper.style.width = '70vw'; | ||||
|                  image_and_info_wrapper.style.height = 'max-content'; | ||||
|                  image_and_info_wrapper.style.marginLeft = '2vw'; | ||||
|                  expand_button.src = 'expand-button.svg'; | ||||
|              } else { | ||||
|                  sidebar_wrapper.style.display = 'none'; | ||||
|                  image_and_info_wrapper.style.width = '100%'; | ||||
|                  image_and_info_wrapper.style.height = '100%'; | ||||
|                  image_and_info_wrapper.style.marginLeft = '0'; | ||||
|                  expand_button.src = 'contract-button.svg'; | ||||
|              } | ||||
|              expanded = !expanded; | ||||
|          } | ||||
|  | ||||
|          function toggle_size() { | ||||
|              full_size = !full_size; | ||||
|              if (full_size) { | ||||
| 				 size_button.src = "hd.svg" | ||||
|              } else { | ||||
| 				 size_button.src = "sd.svg" | ||||
|              } | ||||
|              set_main_image(current_image, false, false); | ||||
|          } | ||||
|         </script> | ||||
|     </body> | ||||
| </html> | ||||