photo.html 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. <!DOCTYPE html>
  2. <meta name="apple-mobile-web-app-capable" content="yes" />
  3. <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1" />
  4. <html>
  5. <head>
  6. <meta charset="UTF-8">
  7. <meta name="theme-color" content="#4b75ff">
  8. <title>Photo Viewer</title>
  9. <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
  10. <link rel="manifest" href="manifest.json">
  11. <style>
  12. body{
  13. margin: 0px !important;
  14. background:rgba(34,34,34,1);
  15. overflow: hidden;
  16. }
  17. .arrow{
  18. width: 2em;
  19. opacity: 0.5;
  20. position: fixed;
  21. top: calc(50% - 1em);
  22. cursor: pointer;
  23. }
  24. .left.arrow{
  25. left: 2em;
  26. }
  27. .right.arrow{
  28. right: 2em;
  29. }
  30. .zoom{
  31. width: 2em;
  32. opacity: 0.5;
  33. position: fixed;
  34. bottom: 1em;
  35. cursor: pointer;
  36. }
  37. .zoom.out{
  38. right: 1em;
  39. }
  40. .zoom.in{
  41. right: 3.2em;
  42. }
  43. #img{
  44. transition: transform 0.5s;
  45. }
  46. </style>
  47. </head>
  48. <body>
  49. <img id="img" style="max-height: 100vh;max-width: 100%;">
  50. <img class="left arrow" style="display:none;" onclick="previousImage();" src="img/arrow-left.svg">
  51. <img class="right arrow" style="display:none;" onclick="nextImage();" src="img/arrow-right.svg">
  52. <img class="zoom in" onclick="zoomIn();" src="img/zoom-in.svg">
  53. <img class="zoom out" onclick="zoomOut();" src="img/zoom-out.svg">
  54. <script>
  55. function loadInputFiles(){
  56. try{
  57. if (window.location.hash.length == 0){
  58. return null;
  59. }
  60. var inputFileInfo = window.location.hash.substring(1,window.location.hash.length);
  61. inputFileInfo = JSON.parse(decodeURIComponent(inputFileInfo));
  62. if (inputFileInfo.length == 0){
  63. return null;
  64. }
  65. return inputFileInfo
  66. }catch{
  67. return null;
  68. }
  69. }
  70. //Get file playback info from hash
  71. var playbackFile = {};
  72. var nearbyFileList = [];
  73. var currentImageURL = "";
  74. var currentImageFilename = "";
  75. var currentViewingIndex = 0;
  76. var zoomLevel = 1;
  77. var initMargin = [];
  78. var currentMargin = [];
  79. $(document).ready(function(){
  80. playbackFile = loadInputFiles();
  81. playbackFile = playbackFile;
  82. loadImage(playbackFile.filename, playbackFile.filepath);
  83. loadNearbyFiles(playbackFile.filepath);
  84. });
  85. $(window).on("resize ", function() {
  86. updateImgSize();
  87. });
  88. /*
  89. Zooming related functions
  90. */
  91. function zoomIn(){
  92. zoomLevel+= 0.5;
  93. if (zoomLevel >=3){
  94. zoomLevel = 3;
  95. }
  96. applyZoom();
  97. }
  98. function zoomOut(){
  99. zoomLevel-= 0.5;
  100. applyZoom();
  101. }
  102. $(window).bind('mousewheel DOMMouseScroll', function(event){
  103. //Get the percentage of offsets from the cursor position to the photo edge
  104. if (event.originalEvent.wheelDelta > 0 || event.originalEvent.detail < 0) {
  105. // scroll up
  106. zoomIn();
  107. }
  108. else {
  109. // scroll down
  110. zoomOut();
  111. }
  112. });
  113. function applyZoom(){
  114. if (zoomLevel < 1){
  115. zoomLevel = 1;
  116. }
  117. if (zoomLevel == 1){
  118. //Reset offsets
  119. updateImgSize();
  120. }
  121. $("#img").css("transform", `scale(${zoomLevel})`);
  122. }
  123. //Event binding for photo draging
  124. var isDragging = false;
  125. var initPositions = [];
  126. $(window).mousedown(function(evt) {
  127. evt.preventDefault();
  128. handleZoomMousedown(evt.clientX, evt.clientY);
  129. });
  130. $(window).mousemove(function(evt) {
  131. handleZoomMouseMove(evt.clientX, evt.clientY);
  132. });
  133. $(window).mouseup(function() {
  134. handleZoomMouseUp();
  135. });
  136. function getCurrentImageMargins(){
  137. var accLeft = $("#img").css("margin-left").replace("px","");
  138. var accTop = $("#img").css("margin-top").replace("px","");
  139. return [parseFloat(accLeft), parseFloat(accTop)];
  140. }
  141. function handleZoomMousedown(x,y){
  142. if (zoomLevel > 1){
  143. //Only allow dragging when zoomlv > 1
  144. isDragging = true;
  145. var accLeft = $("#img").css("margin-left").replace("px","");
  146. var accTop = $("#img").css("margin-top").replace("px","");
  147. initPositions = [JSON.parse(JSON.stringify(x - accLeft)), JSON.parse(JSON.stringify(y - accTop))];
  148. }
  149. }
  150. function handleZoomMouseMove(x,y){
  151. if (isDragging){
  152. console.log("dragging");
  153. var offsetsToStartPoint = [initPositions[0] - x, initPositions[1] - y];
  154. MoveImage(-offsetsToStartPoint[0], -offsetsToStartPoint[1]);
  155. }
  156. }
  157. function MoveImage(x,y){
  158. $("#img").css("margin-left", x + "px");
  159. $("#img").css("margin-top", y + "px");
  160. }
  161. function handleZoomMouseUp(){
  162. isDragging = false;
  163. }
  164. //Load the nearby image files and allow swapping using <- and -> key
  165. function loadNearbyFiles(filepath){
  166. let parentDir = filepath.split("/");
  167. parentDir.pop();
  168. parentDir = parentDir.join("/");
  169. console.log(filepath, parentDir);
  170. $.get("/api/fs/list?dir=" + parentDir, function(data){
  171. if (data.error != undefined){
  172. alert(data.error);
  173. }else{
  174. nearbyFileList = [];
  175. data.forEach(fileEntry => {
  176. if (fileEntry.IsDir){
  177. return;
  178. }
  179. let ext = fileEntry.Filename.split(".").pop();
  180. if (ext == "jpg" || ext == "jpeg" || ext == "webp" || ext == "png" || ext == "gif"){
  181. //Can render on web
  182. nearbyFileList.push(parentDir + "/" + fileEntry.Filename);
  183. }
  184. });
  185. if (nearbyFileList.length > 0){
  186. $(".arrow").css("display", "");
  187. }
  188. //Track which index currently the user is viewing
  189. for (var i = 0; i < nearbyFileList.length; i++){
  190. var thisPath = nearbyFileList[i];
  191. if (thisPath == filepath.split("\\").join("/")){
  192. currentViewingIndex = i;
  193. }
  194. }
  195. }
  196. });
  197. /*
  198. ao_module_agirun("Photo/embedded/listNearbyImage.js", {
  199. path: filepath
  200. }, function(data){
  201. if (data.error != undefined){
  202. alert(data.error);
  203. }else{
  204. nearbyFileList = data;
  205. $(".arrow").css("display", "");
  206. //Track which index currently the user is viewing
  207. for (var i = 0; i < nearbyFileList.length; i++){
  208. var thisPath = nearbyFileList[i];
  209. if (thisPath == filepath.split("\\").join("/")){
  210. currentViewingIndex = i;
  211. }
  212. }
  213. }
  214. })
  215. */
  216. }
  217. function nextImage(){
  218. nextPhoto = currentViewingIndex + 1;
  219. if (nextPhoto > nearbyFileList.length - 1){
  220. nextPhoto = nearbyFileList.length - 1;
  221. }
  222. var filepath = nearbyFileList[nextPhoto];
  223. var filename = filepath.split('/').pop();
  224. if (nextPhoto != currentViewingIndex){
  225. //Change in photo index
  226. loadImage(filename, filepath);
  227. currentViewingIndex = nextPhoto;
  228. }
  229. }
  230. function previousImage(){
  231. nextPhoto = currentViewingIndex - 1;
  232. if (nextPhoto < 0){
  233. nextPhoto = 0;
  234. }
  235. var filepath = nearbyFileList[nextPhoto];
  236. var filename = filepath.split('/').pop();
  237. if (nextPhoto != currentViewingIndex){
  238. //Change in photo index
  239. loadImage(filename, filepath);
  240. currentViewingIndex = nextPhoto;
  241. }
  242. }
  243. //Bind arrow key events
  244. $("body").on("keydown", function(e){
  245. var nextPhoto = currentViewingIndex;
  246. if (e.keyCode == 37){
  247. //<-
  248. if (nearbyFileList.length > 0){
  249. previousImage();
  250. }
  251. }else if (e.keyCode == 39){
  252. //->
  253. if (nearbyFileList.length > 0){
  254. nextImage();
  255. }
  256. }else{
  257. //Invalid keycode to operate
  258. return;
  259. }
  260. })
  261. function loadImage(filename, filepath){
  262. $("#img").hide();
  263. document.title = ("Photo - " + filename);
  264. $("#img").attr("src", '/api/fs/download?preview=true&file=' + encodeURIComponent(filepath))
  265. currentImageURL = '/api/fs/download?preview=true&file=' + encodeURIComponent(filepath);
  266. currentImageFilename = filename;
  267. //realigin to center
  268. $('#img').on('load', function() {
  269. zoomLevel = 1;
  270. applyZoom();
  271. updateImgSize();
  272. $("#img").show();
  273. });
  274. }
  275. function updateImgSize() {
  276. $('#img').css("margin-top", (window.innerHeight - $("#img").height()) / 2);
  277. $('#img').css("margin-left", (window.innerWidth - $("#img").width()) / 2);
  278. initMargin = [(window.innerWidth - $("#img").width()) / 2, (window.innerHeight - $("#img").height()) / 2];
  279. currentMargin = initMargin;
  280. }
  281. //Touch gesture detections
  282. document.addEventListener('touchstart', handleTouchStart, false);
  283. document.addEventListener('touchmove', handleTouchMove, false);
  284. var xDown = null;
  285. var yDown = null;
  286. function getTouches(evt) {
  287. return evt.touches || // browser API
  288. evt.originalEvent.touches; // jQuery
  289. }
  290. function handleTouchStart(evt) {
  291. const firstTouch = getTouches(evt)[0];
  292. xDown = firstTouch.clientX;
  293. yDown = firstTouch.clientY;
  294. };
  295. function handleTouchMove(evt) {
  296. if ( ! xDown || ! yDown ) {
  297. return;
  298. }
  299. var xUp = evt.touches[0].clientX;
  300. var yUp = evt.touches[0].clientY;
  301. var imgmg = getCurrentImageMargins();
  302. var xDiff = xDown - xUp;
  303. var xDiffAcc = xDiff - imgmg[0];
  304. var yDiff = yDown - yUp;
  305. var yDiffAcc = yDiff - imgmg[1];
  306. if (zoomLevel == 1){
  307. if ( Math.abs( xDiff ) > Math.abs( yDiff ) ) {/*most significant*/
  308. if ( xDiff > 0 ) {
  309. /* right swipe */
  310. nextImage();
  311. } else {
  312. /* left swipe */
  313. previousImage();
  314. }
  315. } else {
  316. if ( yDiff > 0 ) {
  317. /* down swipe */
  318. } else {
  319. /* up swipe */
  320. }
  321. }
  322. }else{
  323. MoveImage(-xDiffAcc, -yDiffAcc);
  324. }
  325. /* reset values */
  326. if (zoomLevel == 1){
  327. xDown = null;
  328. yDown = null;
  329. }else{
  330. xDown = xUp;
  331. yDown = yUp;
  332. }
  333. };
  334. function isZoomed(){
  335. return window.matchMedia('(max--moz-device-pixel-ratio:0.99), (min--moz-device-pixel-ratio:1.01)').matches;
  336. }
  337. </script>
  338. </body>
  339. </html>