mirror of
https://github.com/Jonnyan404/memos-bber.git
synced 2026-04-25 12:08:37 +09:00
d307741f1f
Add a GitHub Actions workflow (package-extensions.yml) to build and upload Chrome/Firefox packages (store and offline artifacts) on manual trigger or when pushing v* tags. Update README with packaging instructions. Reorganize extension sources into chrome/ and firefox/ directories, add Firefox-specific files (manifest, locales, assets, CSS, LICENSE), and bump Chrome manifest version to 2026.04.23. Also modify js/oper.js (moved to chrome/js) to improve proportional editor resizing: add drag-to-resize, scale clamping/persistence (localStorage + chrome.storage.sync), pointer event handlers, and max-scale computation.
287 lines
8.1 KiB
JavaScript
287 lines
8.1 KiB
JavaScript
(function (global) {
|
|
'use strict'
|
|
|
|
function isNotFoundLikeXhr(jqXhr) {
|
|
const status = jqXhr && jqXhr.status
|
|
return status === 404 || status === 405
|
|
}
|
|
|
|
function extractMemoListFromResponse(data) {
|
|
if (!data) return []
|
|
if (Array.isArray(data)) return data
|
|
if (Array.isArray(data.memos)) return data.memos
|
|
if (data.data && Array.isArray(data.data.memos)) return data.data.memos
|
|
if (Array.isArray(data.list)) return data.list
|
|
return []
|
|
}
|
|
|
|
function extractMemoEntityFromResponse(data) {
|
|
if (!data) return data
|
|
if (data.memo) return data.memo
|
|
if (data.data && data.data.memo) return data.data.memo
|
|
if (data.data && (data.data.id != null || data.data.name || data.data.content)) return data.data
|
|
return data
|
|
}
|
|
|
|
function extractResourceEntityFromResponse(data) {
|
|
if (!data) return data
|
|
if (data.resource) return data.resource
|
|
if (data.data && data.data.resource) return data.data.resource
|
|
if (data.data && (data.data.id != null || data.data.name || data.data.filename)) return data.data
|
|
return data
|
|
}
|
|
|
|
function requestGet(url, headers, success, fail) {
|
|
global.$
|
|
.ajax({
|
|
url: url,
|
|
type: 'GET',
|
|
contentType: 'application/json',
|
|
dataType: 'json',
|
|
headers: headers
|
|
})
|
|
.done(function (data) {
|
|
if (success) success(data)
|
|
})
|
|
.fail(function (xhr) {
|
|
if (fail) fail(xhr)
|
|
})
|
|
}
|
|
|
|
function requestPostJson(url, headers, body, success, fail) {
|
|
global.$
|
|
.ajax({
|
|
url: url,
|
|
type: 'POST',
|
|
contentType: 'application/json',
|
|
dataType: 'json',
|
|
data: body != null ? JSON.stringify(body) : null,
|
|
headers: headers
|
|
})
|
|
.done(function (data) {
|
|
if (success) success(data)
|
|
})
|
|
.fail(function (xhr) {
|
|
if (fail) fail(xhr)
|
|
})
|
|
}
|
|
|
|
function requestPatchJson(url, headers, body, success, fail) {
|
|
global.$
|
|
.ajax({
|
|
url: url,
|
|
type: 'PATCH',
|
|
contentType: 'application/json',
|
|
dataType: 'json',
|
|
data: body != null ? JSON.stringify(body) : null,
|
|
headers: headers
|
|
})
|
|
.done(function (data) {
|
|
if (success) success(data)
|
|
})
|
|
.fail(function (xhr) {
|
|
if (fail) fail(xhr)
|
|
})
|
|
}
|
|
|
|
// v1 memo list: GET /api/v1/memo
|
|
// Query params (v0.20/v0.21): limit/offset/rowStatus/content/tag (best-effort)
|
|
function listMemos(info, options, success, fail) {
|
|
const opt = options || {}
|
|
const headers = { Authorization: 'Bearer ' + info.apiTokens }
|
|
|
|
const limit = opt.limit && Number.isFinite(opt.limit) ? Math.max(1, Math.floor(opt.limit)) : 1000
|
|
const offset = opt.offset && Number.isFinite(opt.offset) ? Math.max(0, Math.floor(opt.offset)) : null
|
|
const rowStatus = typeof opt.rowStatus === 'string' && opt.rowStatus ? opt.rowStatus : 'NORMAL'
|
|
|
|
const content = typeof opt.contentSearch === 'string' ? opt.contentSearch : ''
|
|
const tag = typeof opt.tagSearch === 'string' ? opt.tagSearch : ''
|
|
|
|
let qs = '?limit=' + encodeURIComponent(String(limit))
|
|
if (offset != null) qs += '&offset=' + encodeURIComponent(String(offset))
|
|
if (rowStatus) qs += '&rowStatus=' + encodeURIComponent(String(rowStatus))
|
|
if (content) qs += '&content=' + encodeURIComponent(String(content))
|
|
if (tag) qs += '&tag=' + encodeURIComponent(String(tag).replace(/^#/, ''))
|
|
|
|
requestGet(
|
|
info.apiUrl + 'api/v1/memo' + qs,
|
|
headers,
|
|
function (data) {
|
|
if (success) success({ memos: extractMemoListFromResponse(data) })
|
|
},
|
|
function (xhr) {
|
|
// Some builds might expose plural `/api/v1/memos`; try as a last resort (still v1).
|
|
if (isNotFoundLikeXhr(xhr)) {
|
|
requestGet(
|
|
info.apiUrl + 'api/v1/memos' + qs,
|
|
headers,
|
|
function (data2) {
|
|
if (success) success({ memos: extractMemoListFromResponse(data2) })
|
|
},
|
|
fail
|
|
)
|
|
return
|
|
}
|
|
if (fail) fail(xhr)
|
|
}
|
|
)
|
|
}
|
|
|
|
function createMemo(info, body, success, fail) {
|
|
const headers = { Authorization: 'Bearer ' + info.apiTokens }
|
|
requestPostJson(
|
|
info.apiUrl + 'api/v1/memo',
|
|
headers,
|
|
body,
|
|
function (data) {
|
|
if (success) success(extractMemoEntityFromResponse(data))
|
|
},
|
|
function (xhr) {
|
|
// Last resort: plural route.
|
|
if (isNotFoundLikeXhr(xhr)) {
|
|
requestPostJson(
|
|
info.apiUrl + 'api/v1/memos',
|
|
headers,
|
|
body,
|
|
function (data2) {
|
|
if (success) success(extractMemoEntityFromResponse(data2))
|
|
},
|
|
fail
|
|
)
|
|
return
|
|
}
|
|
if (fail) fail(xhr)
|
|
}
|
|
)
|
|
}
|
|
|
|
function patchMemo(info, memoId, patch, success, fail) {
|
|
const headers = { Authorization: 'Bearer ' + info.apiTokens }
|
|
const id = memoId != null ? String(memoId) : ''
|
|
if (!id) {
|
|
if (fail) fail({ status: 400 })
|
|
return
|
|
}
|
|
|
|
requestPatchJson(
|
|
info.apiUrl + 'api/v1/memo/' + encodeURIComponent(id),
|
|
headers,
|
|
patch,
|
|
function (data) {
|
|
if (success) success(extractMemoEntityFromResponse(data))
|
|
},
|
|
fail
|
|
)
|
|
}
|
|
|
|
function getTagList(info, success, fail) {
|
|
const headers = { Authorization: 'Bearer ' + info.apiTokens }
|
|
requestGet(
|
|
info.apiUrl + 'api/v1/tag',
|
|
headers,
|
|
function (data) {
|
|
const list = Array.isArray(data) ? data : Array.isArray(data.tags) ? data.tags : []
|
|
const out = list
|
|
.map(function (t) {
|
|
if (!t) return ''
|
|
if (typeof t === 'string') return t
|
|
if (typeof t.name === 'string') return t.name
|
|
if (typeof t.tag === 'string') return t.tag
|
|
return ''
|
|
})
|
|
.map(function (s) {
|
|
return String(s).replace(/^#/, '').trim()
|
|
})
|
|
.filter(Boolean)
|
|
if (success) success(out)
|
|
},
|
|
fail
|
|
)
|
|
}
|
|
|
|
function getTagSuggestion(info, success, fail) {
|
|
const headers = { Authorization: 'Bearer ' + info.apiTokens }
|
|
requestGet(
|
|
info.apiUrl + 'api/v1/tag/suggestion',
|
|
headers,
|
|
function (data) {
|
|
const list = Array.isArray(data) ? data : []
|
|
const out = list
|
|
.map(function (s) {
|
|
return String(s).replace(/^#/, '').trim()
|
|
})
|
|
.filter(Boolean)
|
|
if (success) success(out)
|
|
},
|
|
function (xhr) {
|
|
// Some forks might only expose list.
|
|
if (isNotFoundLikeXhr(xhr)) {
|
|
getTagList(info, success, fail)
|
|
return
|
|
}
|
|
if (fail) fail(xhr)
|
|
}
|
|
)
|
|
}
|
|
|
|
function uploadResourceBlob(info, file, meta, success, fail) {
|
|
const headers = { Authorization: 'Bearer ' + info.apiTokens }
|
|
const url = info.apiUrl + 'api/v1/resource/blob'
|
|
|
|
const m = meta || {}
|
|
const filename = String(m.filename || (file && file.name) || 'upload')
|
|
|
|
const form = new FormData()
|
|
if (file) form.append('file', file, filename)
|
|
|
|
global.$
|
|
.ajax({
|
|
url: url,
|
|
type: 'POST',
|
|
data: form,
|
|
processData: false,
|
|
contentType: false,
|
|
dataType: 'json',
|
|
headers: headers
|
|
})
|
|
.done(function (data) {
|
|
if (success) success(extractResourceEntityFromResponse(data))
|
|
})
|
|
.fail(function (xhr) {
|
|
if (fail) fail(xhr)
|
|
})
|
|
}
|
|
|
|
function deleteResource(info, resourceId, success, fail) {
|
|
const headers = { Authorization: 'Bearer ' + info.apiTokens }
|
|
const id = resourceId != null ? String(resourceId) : ''
|
|
if (!id) {
|
|
if (fail) fail({ status: 400 })
|
|
return
|
|
}
|
|
|
|
global.$
|
|
.ajax({
|
|
url: info.apiUrl + 'api/v1/resource/' + encodeURIComponent(id),
|
|
type: 'DELETE',
|
|
headers: headers
|
|
})
|
|
.done(function (data) {
|
|
if (success) success(data)
|
|
})
|
|
.fail(function (xhr) {
|
|
if (fail) fail(xhr)
|
|
})
|
|
}
|
|
|
|
global.MemosApiV020V021 = {
|
|
listMemos: listMemos,
|
|
createMemo: createMemo,
|
|
patchMemo: patchMemo,
|
|
getTagList: getTagList,
|
|
getTagSuggestion: getTagSuggestion,
|
|
uploadResourceBlob: uploadResourceBlob,
|
|
deleteResource: deleteResource
|
|
}
|
|
})(window)
|