Baha imgur upload

add upload to imgur in bahamut

  1. // ==UserScript==
  2. // @name Baha imgur upload
  3. // @namespace https://blog.maple3142.net/
  4. // @version 0.7.4
  5. // @description add upload to imgur in bahamut
  6. // @author maple3142
  7. // @match https://*.gamer.com.tw/*
  8. // @match https://blog.maple3142.net/bahamut-imgur-upload.html
  9. // @require https://code.jquery.com/jquery-3.2.1.min.js
  10. // @grant GM_getValue
  11. // @grant GM_setValue
  12. // ==/UserScript==
  13.  
  14. ;(function($) {
  15. 'use strict'
  16. /*
  17. * ALBUM_TO_UPLOAD 是你想要上傳的目標相簿 id
  18. * 例如相簿 https://imgur.com/a/C8763 的 id 是 C8763
  19. * 請把他貼到 GM_getValue('ALBUM_TO_UPLOAD','') 後面的引號中,變成 GM_getValue('ALBUM_TO_UPLOAD','C8763')
  20. * 這樣可以確保 id 不會在腳本更新後被清除,不過如果要修改的需要自己去腳本管理器的儲存空間修改
  21. * Tampermonkey 直接在編輯頁面上面的 Storage 頁面修改就好,其他我就不知道了
  22. */
  23. const ALBUM_TO_UPLOAD = GM_getValue('ALBUM_TO_UPLOAD', '')
  24. if (ALBUM_TO_UPLOAD) GM_setValue('ALBUM_TO_UPLOAD', ALBUM_TO_UPLOAD)
  25.  
  26. const debounce = delay => fn => {
  27. let de = false
  28. return (...args) => {
  29. if (de) return
  30. de = true
  31. fn(...args)
  32. setTimeout(() => (de = false), delay)
  33. }
  34. }
  35. const qs = o =>
  36. Object.keys(o)
  37. .map(k => k + '=' + encodeURIComponent(o[k]))
  38. .join('&')
  39. const insertToRte = c => {
  40. // copy from utility_fx.js
  41. let a
  42. a = bahaRte.win.getSelection()
  43. a.getRangeAt &&
  44. a.rangeCount &&
  45. ((a = a.getRangeAt(0)), a.deleteContents(), (c = a.createContextualFragment(c)), a.insertNode(c))
  46. }
  47. const insertUrlToField = url => {
  48. if (unsafeWindow.bahaRte != null) {
  49. // full rte editor
  50. const ht = $('<div>')
  51. .append($('<img>').attr('src', url))
  52. .html()
  53. insertToRte(ht)
  54. } else if ($('#balaTextId').length) {
  55. // guild/bala reply
  56. const id = $('#balaTextId')
  57. .html()
  58. .trim()
  59. const $tx = $('#' + id)
  60. $tx.val($tx.val() + url)
  61. } else if ($('#msgtalk').length) {
  62. // guild/bala new
  63. const $msgtalk = $('#msgtalk')
  64. $msgtalk.val($msgtalk.val() + url)
  65. } else if (
  66. typeof Forum !== 'undefined' &&
  67. typeof Forum.C !== 'undefined' &&
  68. typeof Forum.C.quills !== 'undefined'
  69. ) {
  70. // quick reply
  71. const q = Forum.C.quills[0]
  72. const { index } = q.getSelection() || {}
  73. q.insertEmbed(index || 0, 'image', url)
  74. } else {
  75. //others
  76. prompt('暫時還不支援這種編輯器,不過可以複製下方的網址來貼上', url)
  77. }
  78. }
  79. unsafeWindow.balaInsertImage = () => (insertUrlToField($('#bhImgImageUrl').val()), egg.lightbox.close()) // polyfill original buggy image insert
  80. const isOldImgBoxChecked = i => $(`input[name=bhImgMode][value=${i}]`).prop('checked')
  81. if (location.hostname === 'blog.maple3142.net') {
  82. const access_token = /access_token=(.*?)&/.exec(location.hash)[1]
  83. if (access_token) {
  84. GM_setValue('access_token', access_token)
  85. }
  86. } else {
  87. if (typeof Dropzone !== 'undefined') {
  88. // hook dropzone instances
  89. Dropzone.instances = []
  90. const _Dropzone = Dropzone
  91. const Dropzone$ = function(...o) {
  92. const i = new _Dropzone(...o)
  93. _Dropzone.instances.push(i)
  94. return i
  95. }
  96. unsafeWindow.Dropzone = Object.assign(Dropzone$, _Dropzone)
  97. }
  98. const observer = new MutationObserver(
  99. debounce(10)(_ => {
  100. // new image box
  101. if ($('.tab-menu__item1.active').css('display') === 'block') {
  102. // 上傳圖片 tab1 打開了
  103. if ($('#imgur_uplbtn').length) return // ignore it if exists
  104. const $uplbtn = $('<button>')
  105. .addClass('btn')
  106. .addClass('btn-insert')
  107. .addClass('btn-primary')
  108. .addClass('unchecked')
  109. .attr('id', 'imgur_uplbtn')
  110. .text('imgur 模式: 停用')
  111. const $cancelbtn = $('.dialogify .btn:contains(取消)')
  112. $('.dialogify .btn.btn-insert.btn-primary').before($uplbtn)
  113. $uplbtn.on('click', e => {
  114. e.preventDefault()
  115. e.stopPropagation()
  116. if (!chk_isAuthorized()) {
  117. login()
  118. return
  119. }
  120. imgurEnable = !imgurEnable
  121. if (imgurEnable) $uplbtn.removeClass('unchecked').text('imgur 模式: 啟用')
  122. else $uplbtn.addClass('unchecked').text('imgur 模式: 停用')
  123. })
  124. // Dropzone handling
  125. let imgurEnable = false
  126. const dz = Dropzone.instances[Dropzone.instances.length - 1]
  127. if (dz.hooked) return
  128. dz.hooked = true
  129. dz.on('sending', (e, xhr, fd) => {
  130. if (imgurEnable) dzupload(xhr)
  131. })
  132. const originalcb = dz._callbacks.success[1]
  133. dz._callbacks.success[1] = (file, r) => {
  134. console.log('dz success', r, originalcb)
  135. if (r.token) {
  136. // normal baha file upload
  137. originalcb.apply(dz, [file, r])
  138. } else {
  139. $cancelbtn.click()
  140. insertUrlToField(r.data.link)
  141. }
  142. }
  143.  
  144. document.onpaste = e => {
  145. const { items } = e.clipboardData
  146. for (let i = 0; i < items.length; i++) {
  147. // It doesn't have iterator protocol...
  148. const item = items[i]
  149. if (item.kind === 'file') {
  150. dz.addFile(item.getAsFile())
  151. }
  152. }
  153. }
  154. } else {
  155. $('#imgur_uplbtn').remove()
  156. }
  157.  
  158. if ($('.tab-menu__item3.active').css('display') === 'block') {
  159. if ($('#imgur_urlcvt').length) return
  160. const $urlinput = $('#insertImageUrl')
  161. const $cvtbutton = $('<button>')
  162. .attr('id', 'imgur_urlcvt')
  163. .addClass('btn')
  164. .addClass('btn-primary')
  165. .text('轉換為 imgur 網址')
  166. $urlinput.after($cvtbutton)
  167. $cvtbutton.on('click', e => {
  168. e.preventDefault()
  169. if (!chk_isAuthorized()) {
  170. login()
  171. return
  172. }
  173. const url = $urlinput.val()
  174. if (!url) {
  175. alert('請輸入網址')
  176. return
  177. }
  178. $cvtbutton.text('圖片上傳中, 請稍候...').show()
  179. upload(url)
  180. .then(r => {
  181. $urlinput.val(r.data.link)
  182. $cvtbutton.text('轉換為 imgur 網址')
  183. })
  184. .catch(e => {
  185. console.error(e)
  186. alert('上傳失敗')
  187. $cvtbutton.text('轉換為 imgur 網址')
  188. })
  189. })
  190. } else {
  191. $('#bahaimgur_cvt').remove()
  192. }
  193.  
  194. // old image box
  195. if (isOldImgBoxChecked(1) && !$('#imgurold_upl').length) {
  196. const $uplbtn = $('<button>')
  197. .text('上傳 imgur')
  198. .css('margin-left', '3px')
  199. const $uplfile = $('<input>')
  200. .attr('type', 'file')
  201. .width(220)
  202. const $wrap = $('<div>').attr('id', 'imgurold_upl')
  203. $('#bhImgModeUpload').append($wrap.append($uplfile).append($uplbtn))
  204. $uplbtn.on('click', e => {
  205. e.preventDefault()
  206. e.stopPropagation()
  207. if (!chk_isAuthorized()) {
  208. login()
  209. return
  210. }
  211. const file = $uplfile[0].files[0]
  212. if (!file) return //no file
  213. $uplbtn.text('上傳中...')
  214. upload(file)
  215. .then(r => {
  216. insertUrlToField(r.data.link)
  217. egg.lightbox.close()
  218. })
  219. .catch(e => {
  220. console.error(e)
  221. alert('上傳失敗')
  222. egg.lightbox.close()
  223. })
  224. })
  225. } else if (isOldImgBoxChecked(3) && !$('#imgurold_cvt').length) {
  226. const $urlinput = $('#bhImgImageUrl')
  227. const $cvtbutton = $('<button>')
  228. .text('轉換為 imgur 網址')
  229. .css('display', 'block')
  230. .attr('id', 'imgurold_cvt')
  231. $urlinput
  232. .after($cvtbutton)
  233. .parent()
  234. .css('display', 'flex')
  235. .css('flex-direction', 'column')
  236. .css('align-items', 'center')
  237.  
  238. $cvtbutton.on('click', e => {
  239. e.preventDefault()
  240. if (!chk_isAuthorized()) {
  241. login()
  242. return
  243. }
  244. const url = $urlinput.val()
  245. if (!url) {
  246. alert('請輸入網址')
  247. return
  248. }
  249. $cvtbutton.text('圖片上傳中, 請稍候...').show()
  250. upload(url)
  251. .then(r => {
  252. $urlinput.val(r.data.link)
  253. $cvtbutton.text('轉換為 imgur 網址')
  254. })
  255. .catch(e => {
  256. console.error(e)
  257. alert('上傳失敗')
  258. $cvtbutton.text('轉換為 imgur 網址')
  259. })
  260. })
  261. }
  262. })
  263. )
  264. observer.observe(document.body, { attributes: true, childList: true, characterData: true, subtree: true })
  265. }
  266. function getInitialUploadData() {
  267. const data = new FormData()
  268. if (ALBUM_TO_UPLOAD) {
  269. data.append('album', ALBUM_TO_UPLOAD)
  270. }
  271. return data
  272. }
  273. function upload(image) {
  274. const data = getInitialUploadData()
  275. data.append('image', image)
  276. return fetch('https://api.imgur.com/3/image', {
  277. method: 'POST',
  278. credentials: 'omit',
  279. body: data,
  280. headers: {
  281. Authorization: `Bearer ${GM_getValue('access_token')}`
  282. }
  283. })
  284. .then(r => r.json())
  285. .then(r => {
  286. if (!r.success) throw new Error(r)
  287. return r
  288. })
  289. }
  290. function dzupload(xhr) {
  291. const data = getInitialUploadData()
  292. const fd$ = new Promise(res => {
  293. xhr._send = xhr.send
  294. xhr.send = res
  295. })
  296. return fd$.then(fd => {
  297. xhr.withCredentials = false
  298. xhr.open('POST', 'https://api.imgur.com/3/image')
  299. xhr.setRequestHeader('Authorization', `Bearer ${GM_getValue('access_token')}`)
  300. data.append('image', fd.get('dzfile'))
  301. xhr._send(data)
  302. })
  303. }
  304. function chk_isAuthorized() {
  305. return GM_getValue('access_token', null) !== null
  306. }
  307. function login() {
  308. window.open(
  309. 'https://api.imgur.com/oauth2/authorize?client_id=41e93183c27ec0e&response_type=token',
  310. 'oauth',
  311. 'height=700,width=700'
  312. )
  313. }
  314. const css = document.createElement('style')
  315. css.textContent = `.btn.unchecked{box-shadow: inset 0 1px 1px rgba(0,0,0,0.2);opacity:0.5;}`
  316. document.body.appendChild(css)
  317. })(jQuery)