embedded.html 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796
  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="../script/jquery.min.js"></script>
  10. <script src="../script/ao_module.js"></script>
  11. <script src="constants.js"></script>
  12. <link rel="manifest" href="manifest.json">
  13. <style>
  14. body{
  15. margin: 0px !important;
  16. background:#1a1a1a;
  17. overflow: hidden;
  18. display: flex;
  19. }
  20. #imageContainer{
  21. flex: 1;
  22. position: relative;
  23. display: flex;
  24. align-items: center;
  25. justify-content: center;
  26. transition: margin-right 0.3s ease;
  27. }
  28. #imageContainer.editing{
  29. margin-right: 320px;
  30. }
  31. .arrow{
  32. width: 2em;
  33. opacity: 0.5;
  34. position: fixed;
  35. top: calc(50% - 1em);
  36. cursor: pointer;
  37. }
  38. .left.arrow{
  39. left: 2em;
  40. }
  41. .right.arrow{
  42. right: 2em;
  43. }
  44. #img{
  45. transition: transform 0.5s;
  46. }
  47. .edit{
  48. width: 2em;
  49. opacity: 0.5;
  50. position: fixed;
  51. bottom: 1em;
  52. cursor: pointer;
  53. left: 1em;
  54. }
  55. .edit:hover{
  56. opacity: 1;
  57. }
  58. #editPanel{
  59. position: fixed;
  60. right: 0;
  61. top: 0;
  62. width: 320px;
  63. height: 100vh;
  64. background: rgba(26, 26, 26, 0.95);
  65. backdrop-filter: blur(10px);
  66. color: white;
  67. padding: 20px;
  68. box-sizing: border-box;
  69. transform: translateX(100%);
  70. transition: transform 0.3s ease;
  71. z-index: 1000;
  72. overflow-y: auto;
  73. flex-shrink: 0;
  74. }
  75. #editPanel.active{
  76. transform: translateX(0);
  77. }
  78. .edit-section{
  79. margin-bottom: 25px;
  80. padding-bottom: 20px;
  81. border-bottom: 1px solid rgba(255,255,255,0.1);
  82. }
  83. .edit-section:last-child{
  84. border-bottom: none;
  85. }
  86. .edit-section h3{
  87. margin: 0 0 15px 0;
  88. font-size: 14px;
  89. font-weight: 600;
  90. text-transform: uppercase;
  91. letter-spacing: 0.5px;
  92. color: #4b75ff;
  93. }
  94. .button-group{
  95. display: flex;
  96. gap: 10px;
  97. margin-top: 10px;
  98. }
  99. .edit-button{
  100. flex: 1;
  101. padding: 10px;
  102. background: #4b75ff;
  103. color: white;
  104. border: none;
  105. border-radius: 5px;
  106. cursor: pointer;
  107. font-size: 13px;
  108. transition: background 0.2s;
  109. }
  110. .edit-button:hover{
  111. background: #3d5fd8;
  112. }
  113. .edit-button.secondary{
  114. background: rgba(255,255,255,0.1);
  115. }
  116. .edit-button.secondary:hover{
  117. background: rgba(255,255,255,0.2);
  118. }
  119. #cropOverlay{
  120. position: fixed;
  121. top: 0;
  122. left: 0;
  123. width: 100%;
  124. height: 100%;
  125. display: none;
  126. z-index: 999;
  127. }
  128. #cropOverlay.active{
  129. display: block;
  130. }
  131. #cropBox{
  132. position: absolute;
  133. border: 2px solid #4b75ff;
  134. box-shadow: 0 0 0 9999px rgba(0,0,0,0.5);
  135. cursor: move;
  136. }
  137. .crop-handle{
  138. position: absolute;
  139. width: 20px;
  140. height: 20px;
  141. background: white;
  142. border: 2px solid #4b75ff;
  143. border-radius: 50%;
  144. cursor: move;
  145. }
  146. .crop-handle.top{
  147. top: -10px;
  148. left: 50%;
  149. transform: translateX(-50%);
  150. cursor: ns-resize;
  151. }
  152. .crop-handle.bottom{
  153. bottom: -10px;
  154. left: 50%;
  155. transform: translateX(-50%);
  156. cursor: ns-resize;
  157. }
  158. .crop-handle.left{
  159. left: -10px;
  160. top: 50%;
  161. transform: translateY(-50%);
  162. cursor: ew-resize;
  163. }
  164. .crop-handle.right{
  165. right: -10px;
  166. top: 50%;
  167. transform: translateY(-50%);
  168. cursor: ew-resize;
  169. }
  170. #closeEditPanel{
  171. position: absolute;
  172. top: 15px;
  173. right: 15px;
  174. width: 30px;
  175. height: 30px;
  176. background: rgba(255,255,255,0.1);
  177. border: none;
  178. border-radius: 50%;
  179. color: white;
  180. font-size: 20px;
  181. cursor: pointer;
  182. display: flex;
  183. align-items: center;
  184. justify-content: center;
  185. transition: background 0.2s;
  186. }
  187. #closeEditPanel:hover{
  188. background: rgba(255,255,255,0.2);
  189. }
  190. </style>
  191. </head>
  192. <body>
  193. <div id="imageContainer">
  194. <img id="img" style="max-height: 100vh;max-width: 100%;">
  195. <img class="left arrow" style="display:none;" onclick="previousImage();" src="embedded/arrow-left.svg">
  196. <img class="right arrow" style="display:none;" onclick="nextImage();" src="embedded/arrow-right.svg">
  197. <img class="edit" onclick="toggleEditPanel();" src="embedded/edit.svg" title="Edit Image">
  198. </div>
  199. <!-- Edit Panel -->
  200. <div id="editPanel">
  201. <button id="closeEditPanel" onclick="toggleEditPanel();">×</button>
  202. <h2 style="margin-top: 0; font-size: 18px;">Edit Image</h2>
  203. <!-- Crop Section -->
  204. <div class="edit-section">
  205. <h3>Crop Image</h3>
  206. <div class="button-group">
  207. <button class="edit-button" onclick="startCrop();">Start Crop</button>
  208. <button class="edit-button secondary" onclick="cancelCrop();">Cancel</button>
  209. </div>
  210. <button class="edit-button" style="margin-top: 10px; width: 100%;" onclick="applyCrop();">Apply Crop</button>
  211. </div>
  212. <!-- Rotation Section -->
  213. <div class="edit-section">
  214. <h3>Rotate</h3>
  215. <div class="button-group">
  216. <button class="edit-button" onclick="rotateImage(-90);">↺ Left</button>
  217. <button class="edit-button" onclick="rotateImage(90);">↻ Right</button>
  218. </div>
  219. </div>
  220. <!-- Save Section -->
  221. <div class="edit-section">
  222. <h3>Save</h3>
  223. <button class="edit-button" style="width: 100%; margin-bottom: 10px;" onclick="saveToArozOS();">Save to ArozOS</button>
  224. <button class="edit-button secondary" style="width: 100%;" onclick="downloadImage();">Download to Device</button>
  225. </div>
  226. <!-- Reset Section -->
  227. <div class="edit-section">
  228. <button class="edit-button secondary" style="width: 100%;" onclick="resetEdits();">Reset All Changes</button>
  229. </div>
  230. </div>
  231. <!-- Crop Overlay -->
  232. <div id="cropOverlay">
  233. <div id="cropBox">
  234. <div class="crop-handle top"></div>
  235. <div class="crop-handle bottom"></div>
  236. <div class="crop-handle left"></div>
  237. <div class="crop-handle right"></div>
  238. </div>
  239. </div>
  240. <script>
  241. //Get file playback info from hash
  242. var playbackFile = ao_module_loadInputFiles();
  243. var nearbyFileList = [];
  244. var currentImageURL = "";
  245. var currentImageFilename = "";
  246. var currentViewingIndex = 0;
  247. var initMargin = [];
  248. var currentMargin = [];
  249. // Edit functionality variables
  250. var rotation = 0;
  251. var isCropping = false;
  252. var isEditMode = false;
  253. var cropData = null;
  254. var canvas = document.createElement('canvas');
  255. var ctx = canvas.getContext('2d');
  256. //Only handle one file
  257. playbackFile = playbackFile[0];
  258. loadImage(playbackFile.filename, playbackFile.filepath);
  259. $(window).on("resize ", function() {
  260. updateImgSize();
  261. });
  262. /*
  263. Edit Panel Functions
  264. */
  265. let interval;
  266. function toggleEditPanel(){
  267. var panel = document.getElementById('editPanel');
  268. var container = document.getElementById('imageContainer');
  269. panel.classList.toggle('active');
  270. container.classList.toggle('editing');
  271. isEditMode = panel.classList.contains('active');
  272. if (interval){
  273. clearInterval(interval);
  274. }
  275. interval = setInterval(function(){
  276. updateImgSize();
  277. }, 1);
  278. setTimeout(function(){
  279. clearInterval(interval);
  280. }, 300);
  281. }
  282. function applyAllFilters(){
  283. var transformString = `rotate(${rotation}deg)`;
  284. var img = document.getElementById('img');
  285. img.style.transform = transformString;
  286. }
  287. function rotateImage(degrees){
  288. rotation += degrees;
  289. applyAllFilters();
  290. }
  291. function resetEdits(){
  292. rotation = 0;
  293. cancelCrop();
  294. document.getElementById('img').src = currentImageURL;
  295. cropData = null;
  296. applyAllFilters();
  297. updateImgSize();
  298. }
  299. /*
  300. Crop Functions
  301. */
  302. function startCrop(){
  303. if (isCropping) return;
  304. isCropping = true;
  305. var overlay = document.getElementById('cropOverlay');
  306. var cropBox = document.getElementById('cropBox');
  307. var img = document.getElementById('img');
  308. // Get image position and size
  309. var rect = img.getBoundingClientRect();
  310. // Initialize crop box to center of image (50% size)
  311. var cropWidth = rect.width * 0.6;
  312. var cropHeight = rect.height * 0.6;
  313. var cropLeft = rect.left + (rect.width - cropWidth) / 2;
  314. var cropTop = rect.top + (rect.height - cropHeight) / 2;
  315. cropBox.style.left = cropLeft + 'px';
  316. cropBox.style.top = cropTop + 'px';
  317. cropBox.style.width = cropWidth + 'px';
  318. cropBox.style.height = cropHeight + 'px';
  319. overlay.classList.add('active');
  320. // Add drag functionality to handles
  321. setupCropHandles();
  322. }
  323. function setupCropHandles(){
  324. var cropBox = document.getElementById('cropBox');
  325. var handles = cropBox.querySelectorAll('.crop-handle');
  326. // Remove existing listeners by cloning
  327. var newCropBox = cropBox.cloneNode(true);
  328. cropBox.parentNode.replaceChild(newCropBox, cropBox);
  329. cropBox = newCropBox;
  330. handles = cropBox.querySelectorAll('.crop-handle');
  331. // Add drag functionality to the crop box itself
  332. cropBox.addEventListener('mousedown', function(e){
  333. // Check if clicking on the box itself, not a handle
  334. if (e.target === cropBox){
  335. e.preventDefault();
  336. var startX = e.clientX;
  337. var startY = e.clientY;
  338. var startLeft = parseInt(cropBox.style.left);
  339. var startTop = parseInt(cropBox.style.top);
  340. var img = document.getElementById('img');
  341. var imgRect = img.getBoundingClientRect();
  342. var boxWidth = parseInt(cropBox.style.width);
  343. var boxHeight = parseInt(cropBox.style.height);
  344. function onMouseMove(e){
  345. var dx = e.clientX - startX;
  346. var dy = e.clientY - startY;
  347. var newLeft = startLeft + dx;
  348. var newTop = startTop + dy;
  349. // Constrain to image boundaries
  350. if (newLeft < imgRect.left) newLeft = imgRect.left;
  351. if (newTop < imgRect.top) newTop = imgRect.top;
  352. if (newLeft + boxWidth > imgRect.right) newLeft = imgRect.right - boxWidth;
  353. if (newTop + boxHeight > imgRect.bottom) newTop = imgRect.bottom - boxHeight;
  354. cropBox.style.left = newLeft + 'px';
  355. cropBox.style.top = newTop + 'px';
  356. }
  357. function onMouseUp(){
  358. document.removeEventListener('mousemove', onMouseMove);
  359. document.removeEventListener('mouseup', onMouseUp);
  360. }
  361. document.addEventListener('mousemove', onMouseMove);
  362. document.addEventListener('mouseup', onMouseUp);
  363. }
  364. });
  365. // Add handle resize functionality
  366. handles.forEach(function(handle){
  367. handle.addEventListener('mousedown', function(e){
  368. e.preventDefault();
  369. e.stopPropagation(); // Prevent triggering box drag
  370. var startX = e.clientX;
  371. var startY = e.clientY;
  372. var startLeft = parseInt(cropBox.style.left);
  373. var startTop = parseInt(cropBox.style.top);
  374. var startWidth = parseInt(cropBox.style.width);
  375. var startHeight = parseInt(cropBox.style.height);
  376. var handleClass = handle.className.split(' ')[1];
  377. var img = document.getElementById('img');
  378. var imgRect = img.getBoundingClientRect();
  379. var minSize = 20; // Minimum crop box size
  380. function onMouseMove(e){
  381. var dx = e.clientX - startX;
  382. var dy = e.clientY - startY;
  383. if (handleClass === 'top'){
  384. var newTop = startTop + dy;
  385. var newHeight = startHeight - dy;
  386. // Constrain to image boundaries and minimum size
  387. if (newTop < imgRect.top) {
  388. newTop = imgRect.top;
  389. newHeight = startTop + startHeight - imgRect.top;
  390. }
  391. if (newHeight >= minSize){
  392. cropBox.style.top = newTop + 'px';
  393. cropBox.style.height = newHeight + 'px';
  394. }
  395. } else if (handleClass === 'bottom'){
  396. var newHeight = startHeight + dy;
  397. var maxHeight = imgRect.bottom - startTop;
  398. if (newHeight > maxHeight) newHeight = maxHeight;
  399. if (newHeight >= minSize){
  400. cropBox.style.height = newHeight + 'px';
  401. }
  402. } else if (handleClass === 'left'){
  403. var newLeft = startLeft + dx;
  404. var newWidth = startWidth - dx;
  405. // Constrain to image boundaries and minimum size
  406. if (newLeft < imgRect.left) {
  407. newLeft = imgRect.left;
  408. newWidth = startLeft + startWidth - imgRect.left;
  409. }
  410. if (newWidth >= minSize){
  411. cropBox.style.left = newLeft + 'px';
  412. cropBox.style.width = newWidth + 'px';
  413. }
  414. } else if (handleClass === 'right'){
  415. var newWidth = startWidth + dx;
  416. var maxWidth = imgRect.right - startLeft;
  417. if (newWidth > maxWidth) newWidth = maxWidth;
  418. if (newWidth >= minSize){
  419. cropBox.style.width = newWidth + 'px';
  420. }
  421. }
  422. }
  423. function onMouseUp(){
  424. document.removeEventListener('mousemove', onMouseMove);
  425. document.removeEventListener('mouseup', onMouseUp);
  426. }
  427. document.addEventListener('mousemove', onMouseMove);
  428. document.addEventListener('mouseup', onMouseUp);
  429. });
  430. });
  431. }
  432. function cancelCrop(){
  433. isCropping = false;
  434. document.getElementById('cropOverlay').classList.remove('active');
  435. }
  436. function applyCrop(){
  437. if (!isCropping) return;
  438. var cropBox = document.getElementById('cropBox');
  439. var img = document.getElementById('img');
  440. var imgRect = img.getBoundingClientRect();
  441. var cropRect = cropBox.getBoundingClientRect();
  442. // Calculate crop coordinates relative to the image
  443. var scaleX = img.naturalWidth / imgRect.width;
  444. var scaleY = img.naturalHeight / imgRect.height;
  445. cropData = {
  446. x: (cropRect.left - imgRect.left) * scaleX,
  447. y: (cropRect.top - imgRect.top) * scaleY,
  448. width: cropRect.width * scaleX,
  449. height: cropRect.height * scaleY
  450. };
  451. // Apply crop visually
  452. canvas.width = cropData.width;
  453. canvas.height = cropData.height;
  454. var tempImg = new Image();
  455. tempImg.crossOrigin = 'anonymous';
  456. tempImg.onload = function(){
  457. ctx.drawImage(tempImg, cropData.x, cropData.y, cropData.width, cropData.height, 0, 0, cropData.width, cropData.height);
  458. img.src = canvas.toDataURL('image/png');
  459. cancelCrop();
  460. };
  461. tempImg.src = currentImageURL;
  462. }
  463. /*
  464. Save Functions
  465. */
  466. function getEditedImageBlob(callback){
  467. var img = document.getElementById('img');
  468. // Create a new canvas with all edits applied
  469. var editCanvas = document.createElement('canvas');
  470. var editCtx = editCanvas.getContext('2d');
  471. var tempImg = new Image();
  472. tempImg.crossOrigin = 'anonymous';
  473. tempImg.onload = function(){
  474. // Handle rotation
  475. if (rotation % 180 !== 0){
  476. editCanvas.width = tempImg.height;
  477. editCanvas.height = tempImg.width;
  478. } else {
  479. editCanvas.width = tempImg.width;
  480. editCanvas.height = tempImg.height;
  481. }
  482. editCtx.translate(editCanvas.width / 2, editCanvas.height / 2);
  483. editCtx.rotate(rotation * Math.PI / 180);
  484. editCtx.drawImage(tempImg, -tempImg.width / 2, -tempImg.height / 2);
  485. editCanvas.toBlob(function(blob){
  486. callback(blob);
  487. }, 'image/png');
  488. };
  489. tempImg.src = img.src;
  490. }
  491. let savePendingblob = null;
  492. function saveToArozOS(){
  493. getEditedImageBlob(function(blob){
  494. // Get current file's directory
  495. var currentDir = playbackFile.filepath.substring(0, playbackFile.filepath.lastIndexOf('/'));
  496. savePendingblob = blob;
  497. ao_module_openFileSelector(fileSelected, currentDir, 'new', false, {
  498. defaultName: 'edited_' + currentImageFilename
  499. });
  500. });
  501. }
  502. function fileSelected(selectedFiles){
  503. if (selectedFiles.length === 0){
  504. console.log('Save cancelled');
  505. return;
  506. }
  507. var savePath = selectedFiles[0].filepath;
  508. savePath = savePath.split("/");
  509. savePath.pop();
  510. savePath = savePath.join("/") + "/";
  511. var saveFilename = selectedFiles[0].filename;
  512. // Upload the edited image using ao_module_uploadFile
  513. var file = new File([savePendingblob], currentImageFilename, { type: 'image/png' });
  514. savePendingblob = null;
  515. ao_module_uploadFile(
  516. file,
  517. savePath,
  518. function(response){
  519. alert('Image saved successfully!');
  520. },
  521. undefined,
  522. function(status){
  523. alert('Failed to save image. Error code: ' + status);
  524. }
  525. );
  526. }
  527. function downloadImage(){
  528. getEditedImageBlob(function(blob){
  529. var url = URL.createObjectURL(blob);
  530. var a = document.createElement('a');
  531. a.href = url;
  532. a.download = 'edited_' + currentImageFilename;
  533. document.body.appendChild(a);
  534. a.click();
  535. document.body.removeChild(a);
  536. URL.revokeObjectURL(url);
  537. });
  538. }
  539. //Load the nearby image files and allow swapping using <- and -> key
  540. function loadNearbyFiles(filepath){
  541. ao_module_agirun("Photo/embedded/listNearbyImage.js", {
  542. path: filepath
  543. }, function(data){
  544. if (data.error != undefined){
  545. alert(data.error);
  546. }else{
  547. nearbyFileList = data;
  548. $(".arrow").css("display", "");
  549. //Track which index currently the user is viewing
  550. for (var i = 0; i < nearbyFileList.length; i++){
  551. var thisPath = nearbyFileList[i];
  552. if (thisPath == filepath.split("\\").join("/")){
  553. currentViewingIndex = i;
  554. }
  555. }
  556. }
  557. })
  558. }
  559. function nextImage(){
  560. // Don't allow navigation when in edit mode
  561. if (isEditMode) return;
  562. nextPhoto = currentViewingIndex + 1;
  563. if (nextPhoto > nearbyFileList.length - 1){
  564. nextPhoto = nearbyFileList.length - 1;
  565. }
  566. var filepath = nearbyFileList[nextPhoto];
  567. var filename = filepath.split('/').pop();
  568. if (nextPhoto != currentViewingIndex){
  569. //Change in photo index
  570. loadImage(filename, filepath);
  571. currentViewingIndex = nextPhoto;
  572. }
  573. }
  574. function previousImage(){
  575. // Don't allow navigation when in edit mode
  576. if (isEditMode) return;
  577. nextPhoto = currentViewingIndex - 1;
  578. if (nextPhoto < 0){
  579. nextPhoto = 0;
  580. }
  581. var filepath = nearbyFileList[nextPhoto];
  582. var filename = filepath.split('/').pop();
  583. if (nextPhoto != currentViewingIndex){
  584. //Change in photo index
  585. loadImage(filename, filepath);
  586. currentViewingIndex = nextPhoto;
  587. }
  588. }
  589. //Bind arrow key events
  590. $("body").on("keydown", function(e){
  591. // Don't allow navigation when in edit mode
  592. if (isEditMode) return;
  593. var nextPhoto = currentViewingIndex;
  594. if (e.keyCode == 37){
  595. //<-
  596. if (nearbyFileList.length > 0){
  597. previousImage();
  598. }
  599. }else if (e.keyCode == 39){
  600. //->
  601. if (nearbyFileList.length > 0){
  602. nextImage();
  603. }
  604. }else{
  605. //Invalid keycode to operate
  606. return;
  607. }
  608. })
  609. loadNearbyFiles(playbackFile.filepath);
  610. async function loadImage(filename, filepath){
  611. $("#img").hide();
  612. ao_module_setWindowTitle("Photo - " + filename);
  613. // Backend handles RAW files automatically
  614. $("#img").attr("src", '../media?file=' + encodeURIComponent(filepath));
  615. currentImageURL = '../media?file=' + encodeURIComponent(filepath);
  616. currentImageFilename = filename;
  617. //realigin to center
  618. $('#img').on('load', function() {
  619. // Reset all edits when loading new image
  620. rotation = 0;
  621. applyAllFilters();
  622. updateImgSize();
  623. $("#img").show();
  624. });
  625. }
  626. function updateImgSize() {
  627. $('#img').css("margin-top", (window.innerHeight - $("#img").height()) / 2);
  628. initMargin = [(window.innerWidth - $("#img").width()) / 2, (window.innerHeight - $("#img").height()) / 2];
  629. currentMargin = initMargin;
  630. }
  631. //Touch gesture detections
  632. document.addEventListener('touchstart', handleTouchStart, false);
  633. document.addEventListener('touchmove', handleTouchMove, false);
  634. var xDown = null;
  635. var yDown = null;
  636. function getTouches(evt) {
  637. return evt.touches || // browser API
  638. evt.originalEvent.touches; // jQuery
  639. }
  640. function handleTouchStart(evt) {
  641. const firstTouch = getTouches(evt)[0];
  642. xDown = firstTouch.clientX;
  643. yDown = firstTouch.clientY;
  644. };
  645. function handleTouchMove(evt) {
  646. if ( ! xDown || ! yDown ) {
  647. return;
  648. }
  649. var xUp = evt.touches[0].clientX;
  650. var yUp = evt.touches[0].clientY;
  651. var xDiff = xDown - xUp;
  652. var yDiff = yDown - yUp;
  653. if ( Math.abs( xDiff ) > Math.abs( yDiff ) ) {/*most significant*/
  654. if ( xDiff > 0 ) {
  655. /* right swipe */
  656. nextImage();
  657. } else {
  658. /* left swipe */
  659. previousImage();
  660. }
  661. } else {
  662. if ( yDiff > 0 ) {
  663. /* down swipe */
  664. } else {
  665. /* up swipe */
  666. }
  667. }
  668. /* reset values */
  669. xDown = null;
  670. yDown = null;
  671. };
  672. function isZoomed(){
  673. return window.matchMedia('(max--moz-device-pixel-ratio:0.99), (min--moz-device-pixel-ratio:1.01)').matches;
  674. }
  675. </script>
  676. </body>
  677. </html>