photo.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659
  1. /**
  2. * @description Takes care of every action a photo can handle and execute.
  3. * @copyright 2015 by Tobias Reich
  4. */
  5. photo = {
  6. json : null,
  7. cache : null
  8. }
  9. photo.getID = function() {
  10. let id = null
  11. if (photo.json) id = photo.json.id
  12. else id = $('.photo:hover, .photo.active').attr('data-id')
  13. if ($.isNumeric(id)===true) return id
  14. else return false
  15. }
  16. photo.load = function(photoID, albumID) {
  17. let params = {
  18. photoID,
  19. albumID,
  20. password: password.value
  21. }
  22. api.post('Photo::get', params, function(data) {
  23. const checkPasswd = function() {
  24. if (password.value!=='') photo.load(photoID, albumID)
  25. else setTimeout(checkPasswd, 250)
  26. }
  27. if (data==='Warning: Photo private!') {
  28. lychee.content.show()
  29. lychee.goto('')
  30. return false
  31. }
  32. if (data==='Warning: Wrong password!') {
  33. checkPasswd()
  34. return false
  35. }
  36. photo.json = data
  37. if (!visible.photo()) view.photo.show()
  38. view.photo.init()
  39. lychee.imageview.show()
  40. setTimeout(() => {
  41. lychee.content.show()
  42. photo.preloadNext(photoID)
  43. }, 300)
  44. })
  45. }
  46. // Preload the next photo for better response time
  47. photo.preloadNext = function(photoID) {
  48. if (album.json &&
  49. album.json.content &&
  50. album.json.content[photoID] &&
  51. album.json.content[photoID].nextPhoto!='') {
  52. let nextPhoto = album.json.content[photoID].nextPhoto
  53. let url = album.json.content[nextPhoto].url
  54. $('head [data-prefetch]').remove()
  55. $('head').append(`<link data-prefetch rel="prefetch" href="${ url }">`)
  56. }
  57. }
  58. photo.parse = function() {
  59. if (!photo.json.title) photo.json.title = 'Untitled'
  60. }
  61. photo.previous = function(animate) {
  62. if (photo.getID()!==false &&
  63. album.json &&
  64. album.json.content[photo.getID()] &&
  65. album.json.content[photo.getID()].previousPhoto!=='') {
  66. let delay = 0
  67. if (animate===true) {
  68. delay = 200
  69. $('#imageview #image').css({
  70. WebkitTransform : 'translateX(100%)',
  71. MozTransform : 'translateX(100%)',
  72. transform : 'translateX(100%)',
  73. opacity : 0
  74. })
  75. }
  76. setTimeout(() => {
  77. if (photo.getID()===false) return false
  78. lychee.goto(album.getID() + '/' + album.json.content[photo.getID()].previousPhoto)
  79. }, delay)
  80. }
  81. }
  82. photo.next = function(animate) {
  83. if (photo.getID()!==false &&
  84. album.json &&
  85. album.json.content[photo.getID()] &&
  86. album.json.content[photo.getID()].nextPhoto!=='') {
  87. let delay = 0
  88. if (animate===true) {
  89. delay = 200
  90. $('#imageview #image').css({
  91. WebkitTransform : 'translateX(-100%)',
  92. MozTransform : 'translateX(-100%)',
  93. transform : 'translateX(-100%)',
  94. opacity : 0
  95. })
  96. }
  97. setTimeout(() => {
  98. if (photo.getID()===false) return false
  99. lychee.goto(album.getID() + '/' + album.json.content[photo.getID()].nextPhoto)
  100. }, delay)
  101. }
  102. }
  103. photo.duplicate = function(photoIDs) {
  104. if (!photoIDs) return false
  105. if (photoIDs instanceof Array===false) photoIDs = [ photoIDs ]
  106. albums.refresh()
  107. let params = {
  108. photoIDs: photoIDs.join()
  109. }
  110. api.post('Photo::duplicate', params, function(data) {
  111. if (data!==true) lychee.error(null, params, data)
  112. else album.load(album.getID())
  113. })
  114. }
  115. photo.delete = function(photoIDs) {
  116. let action = {}
  117. let cancel = {}
  118. let msg = ''
  119. let photoTitle = ''
  120. if (!photoIDs) return false
  121. if (photoIDs instanceof Array===false) photoIDs = [ photoIDs ]
  122. if (photoIDs.length===1) {
  123. // Get title if only one photo is selected
  124. if (visible.photo()) photoTitle = photo.json.title
  125. else photoTitle = album.json.content[photoIDs].title
  126. // Fallback for photos without a title
  127. if (photoTitle==='') photoTitle = 'Untitled'
  128. }
  129. action.fn = function() {
  130. let nextPhoto
  131. let previousPhoto
  132. basicModal.close()
  133. photoIDs.forEach(function(id, index, array) {
  134. // Change reference for the next and previous photo
  135. if (album.json.content[id].nextPhoto!=='' || album.json.content[id].previousPhoto!=='') {
  136. nextPhoto = album.json.content[id].nextPhoto
  137. previousPhoto = album.json.content[id].previousPhoto
  138. album.json.content[previousPhoto].nextPhoto = nextPhoto
  139. album.json.content[nextPhoto].previousPhoto = previousPhoto
  140. }
  141. delete album.json.content[id]
  142. view.album.content.delete(id)
  143. })
  144. albums.refresh()
  145. // Go to next photo if there is a next photo and
  146. // next photo is not the current one. Show album otherwise.
  147. if (visible.photo() && nextPhoto!=='' && nextPhoto!==photo.getID()) lychee.goto(album.getID() + '/' + nextPhoto)
  148. else if (!visible.albums()) lychee.goto(album.getID())
  149. let params = {
  150. photoIDs: photoIDs.join()
  151. }
  152. api.post('Photo::delete', params, function(data) {
  153. if (data!==true) lychee.error(null, params, data)
  154. })
  155. }
  156. if (photoIDs.length===1) {
  157. action.title = 'Delete Photo'
  158. cancel.title = 'Keep Photo'
  159. msg = lychee.html`<p>Are you sure you want to delete the photo '$${ photoTitle }'? This action can't be undone!</p>`
  160. } else {
  161. action.title = 'Delete Photo'
  162. cancel.title = 'Keep Photo'
  163. msg = lychee.html`<p>Are you sure you want to delete all $${ photoIDs.length } selected photo? This action can't be undone!</p>`
  164. }
  165. basicModal.show({
  166. body: msg,
  167. buttons: {
  168. action: {
  169. title: action.title,
  170. fn: action.fn,
  171. class: 'red'
  172. },
  173. cancel: {
  174. title: cancel.title,
  175. fn: basicModal.close
  176. }
  177. }
  178. })
  179. }
  180. photo.setTitle = function(photoIDs) {
  181. let oldTitle = ''
  182. let msg = ''
  183. if (!photoIDs) return false
  184. if (photoIDs instanceof Array===false) photoIDs = [ photoIDs ]
  185. if (photoIDs.length===1) {
  186. // Get old title if only one photo is selected
  187. if (photo.json) oldTitle = photo.json.title
  188. else if (album.json) oldTitle = album.json.content[photoIDs].title
  189. }
  190. const action = function(data) {
  191. basicModal.close()
  192. let newTitle = data.title
  193. if (visible.photo()) {
  194. photo.json.title = (newTitle==='' ? 'Untitled' : newTitle)
  195. view.photo.title()
  196. }
  197. photoIDs.forEach(function(id, index, array) {
  198. album.json.content[id].title = newTitle
  199. view.album.content.title(id)
  200. })
  201. let params = {
  202. photoIDs : photoIDs.join(),
  203. title : newTitle
  204. }
  205. api.post('Photo::setTitle', params, function(data) {
  206. if (data!==true) lychee.error(null, params, data)
  207. })
  208. }
  209. let input = lychee.html`<input class='text' name='title' type='text' maxlength='50' placeholder='Title' value='$${ oldTitle }'>`
  210. if (photoIDs.length===1) msg = lychee.html`<p>Enter a new title for this photo: ${ input }</p>`
  211. else msg = lychee.html`<p>Enter a title for all $${ photoIDs.length } selected photos: ${ input }</p>`
  212. basicModal.show({
  213. body: msg,
  214. buttons: {
  215. action: {
  216. title: 'Set title',
  217. fn: action
  218. },
  219. cancel: {
  220. title: 'Cancel',
  221. fn: basicModal.close
  222. }
  223. }
  224. })
  225. }
  226. photo.setAlbum = function(photoIDs, albumID) {
  227. let nextPhoto
  228. let previousPhoto
  229. if (!photoIDs) return false
  230. if (photoIDs instanceof Array===false) photoIDs = [ photoIDs ]
  231. if (visible.photo) lychee.goto(album.getID())
  232. photoIDs.forEach(function(id, index, array) {
  233. // Change reference for the next and previous photo
  234. if (album.json.content[id].nextPhoto!==''||album.json.content[id].previousPhoto!=='') {
  235. nextPhoto = album.json.content[id].nextPhoto
  236. previousPhoto = album.json.content[id].previousPhoto
  237. album.json.content[previousPhoto].nextPhoto = nextPhoto
  238. album.json.content[nextPhoto].previousPhoto = previousPhoto
  239. }
  240. delete album.json.content[id]
  241. view.album.content.delete(id)
  242. })
  243. albums.refresh()
  244. let params = {
  245. photoIDs: photoIDs.join(),
  246. albumID
  247. }
  248. api.post('Photo::setAlbum', params, function(data) {
  249. if (data!==true) lychee.error(null, params, data)
  250. })
  251. }
  252. photo.setStar = function(photoIDs) {
  253. if (!photoIDs) return false
  254. if (visible.photo()) {
  255. photo.json.star = (photo.json.star==='0' ? '1' : '0')
  256. view.photo.star()
  257. }
  258. photoIDs.forEach(function(id, index, array) {
  259. album.json.content[id].star = (album.json.content[id].star==='0' ? '1' : '0')
  260. view.album.content.star(id)
  261. })
  262. albums.refresh()
  263. let params = {
  264. photoIDs: photoIDs.join()
  265. }
  266. api.post('Photo::setStar', params, function(data) {
  267. if (data!==true) lychee.error(null, params, data)
  268. })
  269. }
  270. photo.setPublic = function(photoID, e) {
  271. if (photo.json.public==='2') {
  272. const action = function() {
  273. basicModal.close()
  274. lychee.goto(photo.json.original_album)
  275. }
  276. basicModal.show({
  277. body: '<p>This photo is located in a public album. To make this photo private or public, edit the visibility of the associated album.</p>',
  278. buttons: {
  279. action: {
  280. title: 'Show Album',
  281. fn: action
  282. },
  283. cancel: {
  284. title: 'Cancel',
  285. fn: basicModal.close
  286. }
  287. }
  288. })
  289. return false
  290. }
  291. if (visible.photo()) {
  292. photo.json.public = (photo.json.public==='0' ? '1' : '0')
  293. view.photo.public()
  294. if (photo.json.public==='1') contextMenu.sharePhoto(photoID, e)
  295. }
  296. album.json.content[photoID].public = (album.json.content[photoID].public==='0' ? '1' : '0')
  297. view.album.content.public(photoID)
  298. albums.refresh()
  299. api.post('Photo::setPublic', { photoID }, function(data) {
  300. if (data!==true) lychee.error(null, params, data)
  301. })
  302. }
  303. photo.setDescription = function(photoID) {
  304. let oldDescription = photo.json.description
  305. const action = function(data) {
  306. basicModal.close()
  307. let description = data.description
  308. if (visible.photo()) {
  309. photo.json.description = description
  310. view.photo.description()
  311. }
  312. let params = {
  313. photoID,
  314. description
  315. }
  316. api.post('Photo::setDescription', params, function(data) {
  317. if (data!==true) lychee.error(null, params, data)
  318. })
  319. }
  320. basicModal.show({
  321. body: lychee.html`<p>Enter a description for this photo: <input class='text' name='description' type='text' maxlength='800' placeholder='Description' value='$${ oldDescription }'></p>`,
  322. buttons: {
  323. action: {
  324. title: 'Set Description',
  325. fn: action
  326. },
  327. cancel: {
  328. title: 'Cancel',
  329. fn: basicModal.close
  330. }
  331. }
  332. })
  333. }
  334. photo.editTags = function(photoIDs) {
  335. let oldTags = ''
  336. let msg = ''
  337. if (!photoIDs) return false
  338. if (photoIDs instanceof Array===false) photoIDs = [ photoIDs ]
  339. // Get tags
  340. if (visible.photo()) oldTags = photo.json.tags
  341. else if (visible.album() && photoIDs.length===1) oldTags = album.json.content[photoIDs].tags
  342. else if (visible.search() && photoIDs.length===1) oldTags = album.json.content[photoIDs].tags
  343. else if (visible.album() && photoIDs.length>1) {
  344. let same = true
  345. photoIDs.forEach(function(id, index, array) {
  346. if (album.json.content[id].tags===album.json.content[photoIDs[0]].tags && same===true) same = true
  347. else same = false
  348. })
  349. if (same===true) oldTags = album.json.content[photoIDs[0]].tags
  350. }
  351. // Improve tags
  352. oldTags = oldTags.replace(/,/g, ', ')
  353. const action = function(data) {
  354. basicModal.close()
  355. photo.setTags(photoIDs, data.tags)
  356. }
  357. let input = lychee.html`<input class='text' name='tags' type='text' maxlength='800' placeholder='Tags' value='$${ oldTags }'>`
  358. if (photoIDs.length===1) msg = lychee.html`<p>Enter your tags for this photo. You can add multiple tags by separating them with a comma: ${ input }</p>`
  359. else msg = lychee.html`<p>Enter your tags for all $${ photoIDs.length } selected photos. Existing tags will be overwritten. You can add multiple tags by separating them with a comma: ${ input }</p>`
  360. basicModal.show({
  361. body: msg,
  362. buttons: {
  363. action: {
  364. title: 'Set Tags',
  365. fn: action
  366. },
  367. cancel: {
  368. title: 'Cancel',
  369. fn: basicModal.close
  370. }
  371. }
  372. })
  373. }
  374. photo.setTags = function(photoIDs, tags) {
  375. if (!photoIDs) return false
  376. if (photoIDs instanceof Array===false) photoIDs = [photoIDs]
  377. // Parse tags
  378. tags = tags.replace(/(\ ,\ )|(\ ,)|(,\ )|(,{1,}\ {0,})|(,$|^,)/g, ',')
  379. tags = tags.replace(/,$|^,|(\ ){0,}$/g, '')
  380. if (visible.photo()) {
  381. photo.json.tags = tags
  382. view.photo.tags()
  383. }
  384. photoIDs.forEach(function(id, index, array) {
  385. album.json.content[id].tags = tags
  386. })
  387. let params = {
  388. photoIDs: photoIDs.join(),
  389. tags
  390. }
  391. api.post('Photo::setTags', params, function(data) {
  392. if (data!==true) lychee.error(null, params, data)
  393. })
  394. }
  395. photo.deleteTag = function(photoID, index) {
  396. let tags
  397. // Remove
  398. tags = photo.json.tags.split(',')
  399. tags.splice(index, 1)
  400. // Save
  401. photo.json.tags = tags.toString()
  402. photo.setTags([ photoID ], photo.json.tags)
  403. }
  404. photo.share = function(photoID, service) {
  405. let link = ''
  406. let url = photo.getViewLink(photoID)
  407. switch (service) {
  408. case 'twitter':
  409. link = `https://twitter.com/share?url=${ encodeURI(url) }`
  410. break
  411. case 'facebook':
  412. link = `http://www.facebook.com/sharer.php?u=${ encodeURI(url) }&t=${ encodeURI(photo.json.title) }`
  413. break
  414. case 'mail':
  415. link = `mailto:?subject=${ encodeURI(photo.json.title) }&body=${ encodeURI(url) }`
  416. break
  417. case 'dropbox':
  418. lychee.loadDropbox(function() {
  419. let filename = photo.json.title + '.' + photo.getDirectLink().split('.').pop()
  420. Dropbox.save(photo.getDirectLink(), filename)
  421. })
  422. break
  423. default:
  424. link = ''
  425. break
  426. }
  427. if (link.length!=='') location.href = link
  428. }
  429. photo.getArchive = function(photoID) {
  430. let link
  431. let url = `${ api.path }?function=Photo::getArchive&photoID=${ photoID }`
  432. if (location.href.indexOf('index.html')>0) link = location.href.replace(location.hash, '').replace('index.html', url)
  433. else link = location.href.replace(location.hash, '') + url
  434. if (lychee.publicMode===true) link += `&password=${ encodeURIComponent(password.value) }`
  435. location.href = link
  436. }
  437. photo.getDirectLink = function() {
  438. let url = ''
  439. if (photo.json && photo.json.url && photo.json.url!=='') url = photo.json.url
  440. return url
  441. }
  442. photo.getViewLink = function(photoID) {
  443. let url = 'view.php?p=' + photoID
  444. if (location.href.indexOf('index.html')>0) return location.href.replace('index.html' + location.hash, url)
  445. else return location.href.replace(location.hash, url)
  446. }