WebOrbiton
v1.0.0.0

StocketBase

240 lines · 8.8 KB
  1. (function () {
  2. var MAX_BYTES = 25 * 1024 * 1024;
  3. var ACCEPT = {
  4. image: ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],
  5. video: ['video/mp4', 'video/webm']
  6. };
  7. ​
  8. function csrfToken() {
  9. var field = document.querySelector('input[name="csrf_token"]');
  10. ​
  11. return field ? field.value : '';
  12. }
  13. ​
  14. function element(tag, className, text) {
  15. var node = document.createElement(tag);
  16. if (className) {
  17. node.className = className;
  18. }
  19. if (text !== undefined) {
  20. node.textContent = text;
  21. }
  22. ​
  23. return node;
  24. }
  25. ​
  26. function open(options) {
  27. var type = options && options.type === 'video' ? 'video' : 'image';
  28. var onSelect = options && typeof options.onSelect === 'function' ? options.onSelect : function () {};
  29. var previousFocus = document.activeElement;
  30. ​
  31. var overlay = element('div', 'media-modal-overlay');
  32. var dialog = element('div', 'media-modal');
  33. dialog.setAttribute('role', 'dialog');
  34. dialog.setAttribute('aria-modal', 'true');
  35. dialog.setAttribute('aria-label', type === 'video' ? 'Video' : 'Image');
  36. ​
  37. var header = element('div', 'media-modal-header');
  38. var tabs = element('div', 'media-modal-tabs');
  39. var tabUpload = element('button', 'media-modal-tab is-active', 'Upload');
  40. var tabLibrary = element('button', 'media-modal-tab', 'Library');
  41. tabUpload.type = 'button';
  42. tabLibrary.type = 'button';
  43. tabs.appendChild(tabUpload);
  44. tabs.appendChild(tabLibrary);
  45. ​
  46. var close = element('button', 'media-modal-close');
  47. close.type = 'button';
  48. close.setAttribute('aria-label', 'Close');
  49. close.innerHTML = '×';
  50. ​
  51. header.appendChild(tabs);
  52. header.appendChild(close);
  53. ​
  54. var body = element('div', 'media-modal-body');
  55. var uploadPane = element('div', 'media-modal-pane');
  56. var libraryPane = element('div', 'media-modal-pane');
  57. libraryPane.hidden = true;
  58. ​
  59. var drop = element('label', 'media-drop');
  60. var dropTitle = element('div', 'media-drop-title', 'Drop a file here or click to choose');
  61. var dropHint = element('div', 'media-drop-hint', type === 'video' ? 'MP4 or WebM, up to 25 MB' : 'JPG, PNG, GIF or WebP, up to 25 MB');
  62. var fileInput = document.createElement('input');
  63. fileInput.type = 'file';
  64. fileInput.accept = ACCEPT[type].join(',');
  65. fileInput.hidden = true;
  66. drop.appendChild(dropTitle);
  67. drop.appendChild(dropHint);
  68. drop.appendChild(fileInput);
  69. ​
  70. var status = element('div', 'media-status');
  71. uploadPane.appendChild(drop);
  72. uploadPane.appendChild(status);
  73. ​
  74. var grid = element('div', 'media-grid');
  75. var libraryStatus = element('div', 'media-status');
  76. libraryPane.appendChild(libraryStatus);
  77. libraryPane.appendChild(grid);
  78. ​
  79. body.appendChild(uploadPane);
  80. body.appendChild(libraryPane);
  81. dialog.appendChild(header);
  82. dialog.appendChild(body);
  83. overlay.appendChild(dialog);
  84. document.body.appendChild(overlay);
  85. document.body.classList.add('media-modal-open');
  86. ​
  87. var libraryLoaded = false;
  88. ​
  89. function shut() {
  90. document.removeEventListener('keydown', onKey);
  91. overlay.remove();
  92. document.body.classList.remove('media-modal-open');
  93. if (previousFocus && typeof previousFocus.focus === 'function') {
  94. previousFocus.focus();
  95. }
  96. }
  97. ​
  98. function finish(url) {
  99. shut();
  100. onSelect(url);
  101. }
  102. ​
  103. function onKey(event) {
  104. if (event.key === 'Escape') {
  105. shut();
  106. }
  107. }
  108. ​
  109. function showTab(name) {
  110. var library = name === 'library';
  111. tabUpload.classList.toggle('is-active', !library);
  112. tabLibrary.classList.toggle('is-active', library);
  113. uploadPane.hidden = library;
  114. libraryPane.hidden = !library;
  115. if (library && !libraryLoaded) {
  116. loadLibrary();
  117. }
  118. }
  119. ​
  120. function loadLibrary() {
  121. libraryLoaded = true;
  122. libraryStatus.textContent = 'Loading…';
  123. ​
  124. fetch('media-library.php?type=' + type, { credentials: 'same-origin' })
  125. .then(function (response) { return response.json(); })
  126. .then(function (result) {
  127. if (!result.success) {
  128. throw new Error(result.error || 'Could not load the library.');
  129. }
  130. ​
  131. grid.innerHTML = '';
  132. libraryStatus.textContent = result.items.length === 0 ? 'Nothing uploaded yet.' : '';
  133. ​
  134. result.items.forEach(function (item) {
  135. var tile = element('button', 'media-tile');
  136. tile.type = 'button';
  137. tile.setAttribute('title', item.name);
  138. ​
  139. if (type === 'video') {
  140. var video = document.createElement('video');
  141. video.src = item.url + '#t=0.1';
  142. video.preload = 'metadata';
  143. video.muted = true;
  144. tile.appendChild(video);
  145. } else {
  146. var image = document.createElement('img');
  147. image.src = item.url;
  148. image.alt = '';
  149. image.loading = 'lazy';
  150. tile.appendChild(image);
  151. }
  152. ​
  153. tile.addEventListener('click', function () {
  154. finish(item.url);
  155. });
  156. grid.appendChild(tile);
  157. });
  158. ​
  159. if (result.total > result.items.length) {
  160. libraryStatus.textContent = 'Showing the newest ' + result.items.length + ' of ' + result.total + '.';
  161. }
  162. })
  163. .catch(function (error) {
  164. libraryLoaded = false;
  165. libraryStatus.textContent = error.message;
  166. });
  167. }
  168. ​
  169. function upload(file) {
  170. if (!file) {
  171. return;
  172. }
  173. if (ACCEPT[type].indexOf(file.type) === -1) {
  174. status.textContent = type === 'video' ? 'Use an MP4 or WebM video.' : 'Use a JPG, PNG, GIF or WebP image.';
  175. return;
  176. }
  177. if (file.size > MAX_BYTES) {
  178. status.textContent = 'The file is too large (max 25 MB).';
  179. return;
  180. }
  181. ​
  182. var data = new FormData();
  183. data.append('file', file);
  184. data.append('csrf_token', csrfToken());
  185. status.textContent = 'Uploading…';
  186. drop.classList.add('is-busy');
  187. ​
  188. fetch('media-upload.php', { method: 'POST', body: data, credentials: 'same-origin' })
  189. .then(function (response) { return response.json(); })
  190. .then(function (result) {
  191. if (!result.success) {
  192. throw new Error(result.error || 'Upload failed.');
  193. }
  194. finish(result.url);
  195. })
  196. .catch(function (error) {
  197. status.textContent = error.message;
  198. drop.classList.remove('is-busy');
  199. });
  200. }
  201. ​
  202. tabUpload.addEventListener('click', function () { showTab('upload'); });
  203. tabLibrary.addEventListener('click', function () { showTab('library'); });
  204. close.addEventListener('click', shut);
  205. overlay.addEventListener('mousedown', function (event) {
  206. if (event.target === overlay) {
  207. shut();
  208. }
  209. });
  210. document.addEventListener('keydown', onKey);
  211. ​
  212. fileInput.addEventListener('change', function () {
  213. upload(fileInput.files[0]);
  214. fileInput.value = '';
  215. });
  216. ​
  217. ['dragenter', 'dragover'].forEach(function (name) {
  218. drop.addEventListener(name, function (event) {
  219. event.preventDefault();
  220. drop.classList.add('is-over');
  221. });
  222. });
  223. ['dragleave', 'drop'].forEach(function (name) {
  224. drop.addEventListener(name, function (event) {
  225. event.preventDefault();
  226. drop.classList.remove('is-over');
  227. });
  228. });
  229. drop.addEventListener('drop', function (event) {
  230. if (event.dataTransfer && event.dataTransfer.files.length) {
  231. upload(event.dataTransfer.files[0]);
  232. }
  233. });
  234. ​
  235. tabUpload.focus();
  236. }
  237. ​
  238. window.StocketBaseMedia = { open: open };
  239. })();
  240. ​