mirror of https://github.com/status-im/codimd.git
refactor
This commit is contained in:
parent
e4855cdb3c
commit
f83b4b77fd
|
@ -3973,6 +3973,21 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.1.tgz",
|
||||
"integrity": "sha512-PGAK759pxyfXE78NbKxyfRcWYA/KwW17X290cNev/qAsn9eQIxkH4shoNBafH37wewhDG/0p1cHPbK6+SzZjWQ=="
|
||||
},
|
||||
"@types/jquery": {
|
||||
"version": "3.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.6.tgz",
|
||||
"integrity": "sha512-SmgCQRzGPId4MZQKDj9Hqc6kSXFNWZFHpELkyK8AQhf8Zr6HKfCzFv9ZC1Fv3FyQttJZOlap3qYb12h61iZAIg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/sizzle": "*"
|
||||
}
|
||||
},
|
||||
"@types/js-cookie": {
|
||||
"version": "2.2.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.7.tgz",
|
||||
"integrity": "sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/json-schema": {
|
||||
"version": "7.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz",
|
||||
|
@ -4014,6 +4029,12 @@
|
|||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/list.js": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/list.js/-/list.js-2.3.1.tgz",
|
||||
"integrity": "sha512-yKwk95g7K5mKl9i5/rrkrbwoXvRtiC/gd4b1qU8mbfxanSkPsCSqAvmgaXS+tdj66+l5BMvdargh57pdOH+yBg==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/lodash": {
|
||||
"version": "4.14.170",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.170.tgz",
|
||||
|
@ -4118,6 +4139,15 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz",
|
||||
"integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA=="
|
||||
},
|
||||
"@types/select2": {
|
||||
"version": "4.0.54",
|
||||
"resolved": "https://registry.npmjs.org/@types/select2/-/select2-4.0.54.tgz",
|
||||
"integrity": "sha512-xq8c3zsKktym2b9XbDLUWBDyX0WyWjKzeFsDQHkM9p6NfxJza4g2Ud1bheTKNXpHNyVPUrfGwDEMN7JBe3gQbw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/jquery": "*"
|
||||
}
|
||||
},
|
||||
"@types/serve-static": {
|
||||
"version": "1.13.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.9.tgz",
|
||||
|
@ -4127,6 +4157,12 @@
|
|||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/sizzle": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz",
|
||||
"integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/socket.io": {
|
||||
"version": "2.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-2.1.13.tgz",
|
||||
|
@ -4147,6 +4183,12 @@
|
|||
"socket.io-parser": "*"
|
||||
}
|
||||
},
|
||||
"@types/store": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/store/-/store-2.0.2.tgz",
|
||||
"integrity": "sha512-ZPHnXkzmGMfk+pHqAGzTSpA9CbsHmJLgkvOl5w52LZ0XTxB1ZIHWZzQ7lEtjTNWScBbsQekg8TjApMXkMe4nkw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/validator": {
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.1.4.tgz",
|
||||
|
@ -19059,13 +19101,21 @@
|
|||
"integrity": "sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM="
|
||||
},
|
||||
"xss": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/xss/-/xss-1.0.6.tgz",
|
||||
"integrity": "sha512-6Q9TPBeNyoTRxgZFk5Ggaepk/4vUOYdOsIUYvLehcsIZTFjaavbVnsuAkLA5lIFuug5hw8zxcB9tm01gsjph2A==",
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/xss/-/xss-1.0.9.tgz",
|
||||
"integrity": "sha512-2t7FahYnGJys6DpHLhajusId7R0Pm2yTmuL0GV9+mV0ZlaLSnb2toBmppATfg5sWIhZQGlsTLoecSzya+l4EAQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"commander": "^2.9.0",
|
||||
"commander": "^2.20.3",
|
||||
"cssfilter": "0.0.10"
|
||||
},
|
||||
"dependencies": {
|
||||
"commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"xtend": {
|
||||
|
|
|
@ -119,6 +119,9 @@
|
|||
"@types/express": "4.17.9",
|
||||
"@types/express-flash": "0.0.2",
|
||||
"@types/express-session": "^1.17.3",
|
||||
"@types/jquery": "^3.5.6",
|
||||
"@types/js-cookie": "^2.2.7",
|
||||
"@types/list.js": "^2.3.1",
|
||||
"@types/lodash": "^4.14.170",
|
||||
"@types/markdown-pdf": "^9.0.0",
|
||||
"@types/mime-types": "^2.1.0",
|
||||
|
@ -128,7 +131,9 @@
|
|||
"@types/passport.socketio": "^3.7.5",
|
||||
"@types/qs": "^6.9.6",
|
||||
"@types/randomcolor": "^0.5.5",
|
||||
"@types/select2": "^4.0.54",
|
||||
"@types/socket.io": "^2.1.13",
|
||||
"@types/store": "^2.0.2",
|
||||
"@types/validator": "^13.1.4",
|
||||
"@typescript-eslint/eslint-plugin": "^4.26.1",
|
||||
"@typescript-eslint/parser": "^4.26.1",
|
||||
|
@ -222,7 +227,7 @@
|
|||
"webpack-cli": "~4.7.2",
|
||||
"webpack-merge": "~5.8.0",
|
||||
"wurl": "~2.5.3",
|
||||
"xss": "~1.0.6"
|
||||
"xss": "~1.0.9"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"bufferutil": "~4.0.0",
|
||||
|
|
|
@ -1,38 +1,115 @@
|
|||
/* eslint-env browser, jquery */
|
||||
/* global moment, serverurl */
|
||||
import {saveAs} from 'file-saver'
|
||||
import $ from 'jquery'
|
||||
import List from 'list.js'
|
||||
import unescapeHTML from 'lodash/unescape'
|
||||
import moment from 'moment'
|
||||
|
||||
import {
|
||||
checkIfAuth,
|
||||
clearLoginState,
|
||||
getLoginState,
|
||||
resetCheckAuth,
|
||||
setloginStateChangeEvent
|
||||
} from './lib/common/login'
|
||||
import '../css/cover.css'
|
||||
import '../css/site.css'
|
||||
|
||||
import {
|
||||
clearDuplicatedHistory,
|
||||
deleteServerHistory,
|
||||
clearServerHistoryAsync,
|
||||
deleteServerHistoryAsync,
|
||||
getHistory,
|
||||
getHistoryAsync,
|
||||
getStorageHistory,
|
||||
NoteHistory,
|
||||
parseHistory,
|
||||
parseServerToHistory,
|
||||
parseStorageToHistory,
|
||||
postHistoryToServer,
|
||||
postHistoryToServerAsync,
|
||||
removeHistory,
|
||||
saveHistory,
|
||||
saveStorageHistoryToServer
|
||||
} from './history'
|
||||
|
||||
import { saveAs } from 'file-saver'
|
||||
import List from 'list.js'
|
||||
import unescapeHTML from 'lodash/unescape'
|
||||
import {
|
||||
checkIfAuth,
|
||||
clearLoginState,
|
||||
getLoginState,
|
||||
getLoginUserProfile, isLogin,
|
||||
resetCheckAuth,
|
||||
setLoginStateChangeEvent
|
||||
} from './lib/common/login'
|
||||
import {serverurl} from "./lib/config";
|
||||
|
||||
require('./locale')
|
||||
import {initializeLocaleDropdown} from "./locale";
|
||||
|
||||
require('../css/cover.css')
|
||||
require('../css/site.css')
|
||||
initializeLocaleDropdown()
|
||||
|
||||
const options = {
|
||||
setLoginStateChangeEvent(pageInit)
|
||||
|
||||
void pageInit()
|
||||
|
||||
async function pageInit() {
|
||||
try {
|
||||
const profile = await getLoginUserProfile()
|
||||
$('.ui-signin').hide()
|
||||
$('.ui-or').hide()
|
||||
$('.ui-welcome').show()
|
||||
if (profile.photo) $('.ui-avatar').prop('src', profile.photo).show()
|
||||
else $('.ui-avatar').prop('src', '').hide()
|
||||
$('.ui-name').html(profile.name)
|
||||
$('.ui-signout').show()
|
||||
navSection.historyPageBtn.trigger('click')
|
||||
parseServerToHistory(historyList, parseHistoryCallback)
|
||||
} catch (err) {
|
||||
$('.ui-signin').show()
|
||||
$('.ui-or').show()
|
||||
$('.ui-welcome').hide()
|
||||
$('.ui-avatar').prop('src', '').hide()
|
||||
$('.ui-name').html('')
|
||||
$('.ui-signout').hide()
|
||||
parseStorageToHistory(historyList, parseHistoryCallback)
|
||||
}
|
||||
}
|
||||
|
||||
// prevent empty link change hash
|
||||
$('a[href="#"]').on('click', function (e) {
|
||||
e.preventDefault()
|
||||
})
|
||||
|
||||
/**
|
||||
* masthead nav section
|
||||
*/
|
||||
const navSection = {
|
||||
introPageBtn: $('.ui-home'),
|
||||
historyPageBtn: $('.ui-history'),
|
||||
deleteUserModalCancel: $('.ui-delete-user-modal-cancel'),
|
||||
logoutBtn: $('.ui-logout')
|
||||
}
|
||||
const introSection = $('#home')
|
||||
const historySection = $('#history')
|
||||
|
||||
navSection.introPageBtn.on('click', function (e) {
|
||||
if (!introSection.is(':visible')) {
|
||||
$('.section:visible').hide()
|
||||
introSection.fadeIn()
|
||||
}
|
||||
})
|
||||
|
||||
navSection.historyPageBtn.on('click', () => {
|
||||
if (!historySection.is(':visible')) {
|
||||
$('.section:visible').hide()
|
||||
historySection.fadeIn()
|
||||
}
|
||||
})
|
||||
|
||||
navSection.deleteUserModalCancel.on('click', () => {
|
||||
$('.ui-delete-user').parent().removeClass('active')
|
||||
})
|
||||
|
||||
navSection.logoutBtn.on('click', () => {
|
||||
clearLoginState()
|
||||
location.href = `${serverurl}/logout`
|
||||
})
|
||||
|
||||
/**
|
||||
* History Section
|
||||
*/
|
||||
|
||||
const options: List.ListOptions = {
|
||||
valueNames: ['id', 'text', 'timestamp', 'fromNow', 'time', 'tags', 'pinned'],
|
||||
item: `<li class="col-xs-12 col-sm-6 col-md-6 col-lg-4">
|
||||
<span class="id" style="display:none;"></span>
|
||||
|
@ -54,84 +131,166 @@ const options = {
|
|||
</a>
|
||||
</li>`,
|
||||
page: 18,
|
||||
pagination: [{
|
||||
pagination: {
|
||||
outerWindow: 1
|
||||
}]
|
||||
}
|
||||
}
|
||||
const historyList = new List('history', options)
|
||||
|
||||
window.migrateHistoryFromTempCallback = pageInit
|
||||
setloginStateChangeEvent(pageInit)
|
||||
/**
|
||||
* History Tool Bar
|
||||
*/
|
||||
const historyListContainer = $('#history-list')
|
||||
const historyPagination = $('.pagination')
|
||||
const historyToolbar = {
|
||||
importFromBrowserBtn: $('.ui-import-from-browser'),
|
||||
clearHistoryBtn: $('.ui-clear-history'),
|
||||
saveHistoryBtn: $('.ui-save-history'),
|
||||
openHistoryFile: $('.ui-open-history'),
|
||||
tagFilter: $('.ui-use-tags')
|
||||
}
|
||||
|
||||
pageInit()
|
||||
/**
|
||||
* clear all history
|
||||
*/
|
||||
historyToolbar.clearHistoryBtn.on('click', () => {
|
||||
$('.ui-delete-history-modal-msg').text('Do you really want to clear all history?')
|
||||
$('.ui-delete-history-modal-item').html('There is no turning back.')
|
||||
isClearAllHistory = true
|
||||
deleteHistoryModalTargetId = null
|
||||
})
|
||||
|
||||
function pageInit () {
|
||||
checkIfAuth(
|
||||
data => {
|
||||
$('.ui-signin').hide()
|
||||
$('.ui-or').hide()
|
||||
$('.ui-welcome').show()
|
||||
if (data.photo) $('.ui-avatar').prop('src', data.photo).show()
|
||||
else $('.ui-avatar').prop('src', '').hide()
|
||||
$('.ui-name').html(data.name)
|
||||
$('.ui-signout').show()
|
||||
$('.ui-history').click()
|
||||
parseServerToHistory(historyList, parseHistoryCallback)
|
||||
},
|
||||
() => {
|
||||
$('.ui-signin').show()
|
||||
$('.ui-or').show()
|
||||
$('.ui-welcome').hide()
|
||||
$('.ui-avatar').prop('src', '').hide()
|
||||
$('.ui-name').html('')
|
||||
$('.ui-signout').hide()
|
||||
historyToolbar.importFromBrowserBtn.on('click', () => {
|
||||
saveStorageHistoryToServer(() => {
|
||||
parseStorageToHistory(historyList, parseHistoryCallback)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
$('.masthead-nav li').click(function () {
|
||||
$(this).siblings().removeClass('active')
|
||||
$(this).addClass('active')
|
||||
})
|
||||
})
|
||||
|
||||
// prevent empty link change hash
|
||||
$('a[href="#"]').click(function (e) {
|
||||
e.preventDefault()
|
||||
historyToolbar.saveHistoryBtn.on('click', async function () {
|
||||
const history = JSON.stringify(await getHistoryAsync())
|
||||
const blob = new Blob([history], {
|
||||
type: 'application/json;charset=utf-8'
|
||||
})
|
||||
saveAs(blob, `codimd_history_${moment().format('YYYYMMDDHHmmss')}`, true)
|
||||
})
|
||||
|
||||
$('.ui-home').click(function (e) {
|
||||
if (!$('#home').is(':visible')) {
|
||||
$('.section:visible').hide()
|
||||
$('#home').fadeIn()
|
||||
historyToolbar.openHistoryFile.on('change', function (e) {
|
||||
const files = e.target.files || e.dataTransfer.files
|
||||
const file = files[0]
|
||||
const reader = new FileReader()
|
||||
reader.onload = () => {
|
||||
const notehistory = JSON.parse(reader.result)
|
||||
// console.log(notehistory);
|
||||
if (!reader.result) return
|
||||
getHistory(data => {
|
||||
let mergedata = data.concat(notehistory)
|
||||
mergedata = clearDuplicatedHistory(mergedata)
|
||||
saveHistory(mergedata)
|
||||
parseHistory(historyList, parseHistoryCallback)
|
||||
})
|
||||
historyToolbar.openHistoryFile.replaceWith(historyToolbar.openHistoryFile.val('').clone(true))
|
||||
}
|
||||
reader.readAsText(file)
|
||||
})
|
||||
|
||||
$('.ui-refresh-history').on('click', () => {
|
||||
const lastTags = historyToolbar.tagFilter.select2('val')
|
||||
historyToolbar.tagFilter.select2('val', '')
|
||||
historyList.filter()
|
||||
const lastKeyword = $('.search').val()
|
||||
$('.search').val('')
|
||||
historyList.search()
|
||||
historyListContainer.slideUp('fast')
|
||||
historyPagination.hide()
|
||||
|
||||
resetCheckAuth()
|
||||
historyList.clear()
|
||||
parseHistory(historyList, (list, notehistory) => {
|
||||
parseHistoryCallback(list, notehistory)
|
||||
historyToolbar.tagFilter.select2('val', lastTags)
|
||||
historyToolbar.tagFilter.trigger('change')
|
||||
historyList.search(lastKeyword)
|
||||
$('.search').val(lastKeyword)
|
||||
refreshHistoryUIElementVisibility()
|
||||
historyListContainer.slideDown('fast')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
let filtertags = []
|
||||
historyToolbar.tagFilter.select2({
|
||||
placeholder: historyToolbar.tagFilter.attr('placeholder'),
|
||||
multiple: true,
|
||||
data() {
|
||||
return {
|
||||
results: filtertags
|
||||
}
|
||||
}
|
||||
})
|
||||
$('.select2-input').css('width', 'inherit')
|
||||
buildTagsFilter([])
|
||||
|
||||
$('.ui-history').click(() => {
|
||||
if (!$('#history').is(':visible')) {
|
||||
$('.section:visible').hide()
|
||||
$('#history').fadeIn()
|
||||
function buildTagsFilter(tags) {
|
||||
for (let i = 0; i < tags.length; i++) {
|
||||
tags[i] = {
|
||||
id: i,
|
||||
text: unescapeHTML(tags[i])
|
||||
}
|
||||
}
|
||||
filtertags = tags
|
||||
}
|
||||
|
||||
|
||||
historyToolbar.tagFilter.on('change', function () {
|
||||
const tags = []
|
||||
const data = $(this).select2('data')
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
tags.push(data[i].text)
|
||||
}
|
||||
if (tags.length > 0) {
|
||||
historyList.filter(item => {
|
||||
const values = item.values()
|
||||
if (!values.tags) return false
|
||||
let found = false
|
||||
for (let i = 0; i < tags.length; i++) {
|
||||
if (values.tags.includes(tags[i])) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
return found
|
||||
})
|
||||
} else {
|
||||
historyList.filter()
|
||||
}
|
||||
refreshHistoryUIElementVisibility()
|
||||
})
|
||||
|
||||
function checkHistoryList () {
|
||||
if ($('#history-list').children().length > 0) {
|
||||
$('.pagination').show()
|
||||
$('.search').on('keyup', () => {
|
||||
refreshHistoryUIElementVisibility()
|
||||
})
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------------------------------------------
|
||||
|
||||
function refreshHistoryUIElementVisibility() {
|
||||
if (historyListContainer.children().length > 0) {
|
||||
historyPagination.show()
|
||||
$('.ui-nohistory').hide()
|
||||
$('.ui-import-from-browser').hide()
|
||||
} else if ($('#history-list').children().length === 0) {
|
||||
$('.pagination').hide()
|
||||
historyToolbar.importFromBrowserBtn.hide()
|
||||
return
|
||||
}
|
||||
historyPagination.hide()
|
||||
$('.ui-nohistory').slideDown()
|
||||
getStorageHistory(data => {
|
||||
if (data && data.length > 0 && getLoginState() && historyList.items.length === 0) {
|
||||
$('.ui-import-from-browser').slideDown()
|
||||
historyToolbar.importFromBrowserBtn.slideDown()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function parseHistoryCallback(list, notehistory) {
|
||||
checkHistoryList()
|
||||
refreshHistoryUIElementVisibility()
|
||||
// sort by pinned then timestamp
|
||||
list.sort('', {
|
||||
sortFunction(a, b) {
|
||||
|
@ -160,8 +319,12 @@ function parseHistoryCallback (list, notehistory) {
|
|||
for (let j = 0; j < tags.length; j++) {
|
||||
// push info filtertags if not found
|
||||
let found = false
|
||||
if (filtertags.includes(tags[j])) { found = true }
|
||||
if (!found) { filtertags.push(tags[j]) }
|
||||
if (filtertags.includes(tags[j])) {
|
||||
found = true
|
||||
}
|
||||
if (!found) {
|
||||
filtertags.push(tags[j])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -169,7 +332,7 @@ function parseHistoryCallback (list, notehistory) {
|
|||
}
|
||||
|
||||
// update items whenever list updated
|
||||
historyList.on('updated', e => {
|
||||
historyList.on('updated', function (e) {
|
||||
for (let i = 0, l = e.items.length; i < l; i++) {
|
||||
const item = e.items[i]
|
||||
if (item.visible()) {
|
||||
|
@ -189,7 +352,7 @@ historyList.on('updated', e => {
|
|||
// parse tags
|
||||
const tags = values.tags
|
||||
if (tags && tags.length > 0 && tagsEl.children().length <= 0) {
|
||||
const labels = []
|
||||
const labels: string[] = []
|
||||
for (let j = 0; j < tags.length; j++) {
|
||||
// push into the item label
|
||||
labels.push(`<span class='label label-default'>${tags[j]}</span>`)
|
||||
|
@ -199,21 +362,65 @@ historyList.on('updated', e => {
|
|||
}
|
||||
}
|
||||
$('.ui-history-close').off('click')
|
||||
$('.ui-history-close').on('click', historyCloseClick)
|
||||
$('.ui-history-close').on('click', onHistoryCloseClick)
|
||||
$('.ui-history-pin').off('click')
|
||||
$('.ui-history-pin').on('click', historyPinClick)
|
||||
})
|
||||
|
||||
function historyCloseClick (e) {
|
||||
/**
|
||||
* Delete History Modal
|
||||
*/
|
||||
let isClearAllHistory: boolean = false
|
||||
let deleteHistoryModalTargetId: string | null = null
|
||||
|
||||
function onHistoryCloseClick(this: HTMLElement, e) {
|
||||
e.preventDefault()
|
||||
const id = $(this).closest('a').siblings('span').html()
|
||||
const value = historyList.get('id', id)[0]._values
|
||||
const id: string = $(this).closest('a').siblings('span').html()
|
||||
const value: NoteHistory = historyList.get('id', id)[0].values()
|
||||
$('.ui-delete-history-modal-msg').text('Do you really want to delete below history?')
|
||||
$('.ui-delete-history-modal-item').html(`<i class="fa fa-file-text"></i> ${value.text}<br><i class="fa fa-clock-o"></i> ${value.time}`)
|
||||
clearHistory = false
|
||||
deleteId = id
|
||||
isClearAllHistory = false
|
||||
deleteHistoryModalTargetId = id
|
||||
}
|
||||
|
||||
$('.ui-delete-history-modal-confirm').on('click', async function () {
|
||||
if (await isLogin()) {
|
||||
if (isClearAllHistory) {
|
||||
await clearServerHistoryAsync()
|
||||
} else {
|
||||
if (deleteHistoryModalTargetId) {
|
||||
await deleteServerHistoryAsync(deleteHistoryModalTargetId)
|
||||
}
|
||||
}
|
||||
refreshHistoryUIElementVisibility()
|
||||
$('.delete-history-modal').modal('hide')
|
||||
deleteHistoryModalTargetId = null
|
||||
isClearAllHistory = false
|
||||
} else {
|
||||
if (isClearAllHistory) {
|
||||
saveHistory([])
|
||||
historyList.clear()
|
||||
refreshHistoryUIElementVisibility()
|
||||
deleteHistoryModalTargetId = null
|
||||
} else {
|
||||
if (!deleteHistoryModalTargetId) return
|
||||
getHistory(notehistory => {
|
||||
const newnotehistory = removeHistory(deleteHistoryModalTargetId, notehistory)
|
||||
saveHistory(newnotehistory)
|
||||
historyList.remove('id', deleteHistoryModalTargetId)
|
||||
refreshHistoryUIElementVisibility()
|
||||
deleteHistoryModalTargetId = null
|
||||
})
|
||||
}
|
||||
$('.delete-history-modal').modal('hide')
|
||||
isClearAllHistory = false
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* Pin History
|
||||
*/
|
||||
|
||||
function historyPinClick(e) {
|
||||
e.preventDefault()
|
||||
const $this = $(this)
|
||||
|
@ -229,13 +436,17 @@ function historyPinClick (e) {
|
|||
item._values.pinned = false
|
||||
}
|
||||
checkIfAuth(() => {
|
||||
postHistoryToServer(id, {
|
||||
pinned
|
||||
}, (err, result) => {
|
||||
if (!err) {
|
||||
if (pinned) { $this.addClass('active') } else { $this.removeClass('active') }
|
||||
postHistoryToServerAsync(id, {pinned})
|
||||
.then(() => {
|
||||
if (pinned) {
|
||||
$this.addClass('active')
|
||||
} else {
|
||||
$this.removeClass('active')
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
})
|
||||
}, () => {
|
||||
getHistory(notehistory => {
|
||||
for (let i = 0; i < notehistory.length; i++) {
|
||||
|
@ -245,7 +456,11 @@ function historyPinClick (e) {
|
|||
}
|
||||
}
|
||||
saveHistory(notehistory)
|
||||
if (pinned) { $this.addClass('active') } else { $this.removeClass('active') }
|
||||
if (pinned) {
|
||||
$this.addClass('active')
|
||||
} else {
|
||||
$this.removeClass('active')
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -261,170 +476,3 @@ function updateItemFromNow () {
|
|||
item.find('.fromNow').text(moment(timestamp).fromNow())
|
||||
}
|
||||
}
|
||||
|
||||
var clearHistory = false
|
||||
var deleteId = null
|
||||
|
||||
function deleteHistory () {
|
||||
checkIfAuth(() => {
|
||||
deleteServerHistory(deleteId, (err, result) => {
|
||||
if (!err) {
|
||||
if (clearHistory) {
|
||||
historyList.clear()
|
||||
checkHistoryList()
|
||||
} else {
|
||||
historyList.remove('id', deleteId)
|
||||
checkHistoryList()
|
||||
}
|
||||
}
|
||||
$('.delete-history-modal').modal('hide')
|
||||
deleteId = null
|
||||
clearHistory = false
|
||||
})
|
||||
}, () => {
|
||||
if (clearHistory) {
|
||||
saveHistory([])
|
||||
historyList.clear()
|
||||
checkHistoryList()
|
||||
deleteId = null
|
||||
} else {
|
||||
if (!deleteId) return
|
||||
getHistory(notehistory => {
|
||||
const newnotehistory = removeHistory(deleteId, notehistory)
|
||||
saveHistory(newnotehistory)
|
||||
historyList.remove('id', deleteId)
|
||||
checkHistoryList()
|
||||
deleteId = null
|
||||
})
|
||||
}
|
||||
$('.delete-history-modal').modal('hide')
|
||||
clearHistory = false
|
||||
})
|
||||
}
|
||||
|
||||
$('.ui-delete-history-modal-confirm').click(() => {
|
||||
deleteHistory()
|
||||
})
|
||||
|
||||
$('.ui-import-from-browser').click(() => {
|
||||
saveStorageHistoryToServer(() => {
|
||||
parseStorageToHistory(historyList, parseHistoryCallback)
|
||||
})
|
||||
})
|
||||
|
||||
$('.ui-save-history').click(() => {
|
||||
getHistory(data => {
|
||||
const history = JSON.stringify(data)
|
||||
const blob = new Blob([history], {
|
||||
type: 'application/json;charset=utf-8'
|
||||
})
|
||||
saveAs(blob, `codimd_history_${moment().format('YYYYMMDDHHmmss')}`, true)
|
||||
})
|
||||
})
|
||||
|
||||
$('.ui-open-history').bind('change', e => {
|
||||
const files = e.target.files || e.dataTransfer.files
|
||||
const file = files[0]
|
||||
const reader = new FileReader()
|
||||
reader.onload = () => {
|
||||
const notehistory = JSON.parse(reader.result)
|
||||
// console.log(notehistory);
|
||||
if (!reader.result) return
|
||||
getHistory(data => {
|
||||
let mergedata = data.concat(notehistory)
|
||||
mergedata = clearDuplicatedHistory(mergedata)
|
||||
saveHistory(mergedata)
|
||||
parseHistory(historyList, parseHistoryCallback)
|
||||
})
|
||||
$('.ui-open-history').replaceWith($('.ui-open-history').val('').clone(true))
|
||||
}
|
||||
reader.readAsText(file)
|
||||
})
|
||||
|
||||
$('.ui-clear-history').click(() => {
|
||||
$('.ui-delete-history-modal-msg').text('Do you really want to clear all history?')
|
||||
$('.ui-delete-history-modal-item').html('There is no turning back.')
|
||||
clearHistory = true
|
||||
deleteId = null
|
||||
})
|
||||
|
||||
$('.ui-refresh-history').click(() => {
|
||||
const lastTags = $('.ui-use-tags').select2('val')
|
||||
$('.ui-use-tags').select2('val', '')
|
||||
historyList.filter()
|
||||
const lastKeyword = $('.search').val()
|
||||
$('.search').val('')
|
||||
historyList.search()
|
||||
$('#history-list').slideUp('fast')
|
||||
$('.pagination').hide()
|
||||
|
||||
resetCheckAuth()
|
||||
historyList.clear()
|
||||
parseHistory(historyList, (list, notehistory) => {
|
||||
parseHistoryCallback(list, notehistory)
|
||||
$('.ui-use-tags').select2('val', lastTags)
|
||||
$('.ui-use-tags').trigger('change')
|
||||
historyList.search(lastKeyword)
|
||||
$('.search').val(lastKeyword)
|
||||
checkHistoryList()
|
||||
$('#history-list').slideDown('fast')
|
||||
})
|
||||
})
|
||||
|
||||
$('.ui-delete-user-modal-cancel').click(() => {
|
||||
$('.ui-delete-user').parent().removeClass('active')
|
||||
})
|
||||
|
||||
$('.ui-logout').click(() => {
|
||||
clearLoginState()
|
||||
location.href = `${serverurl}/logout`
|
||||
})
|
||||
|
||||
let filtertags = []
|
||||
$('.ui-use-tags').select2({
|
||||
placeholder: $('.ui-use-tags').attr('placeholder'),
|
||||
multiple: true,
|
||||
data () {
|
||||
return {
|
||||
results: filtertags
|
||||
}
|
||||
}
|
||||
})
|
||||
$('.select2-input').css('width', 'inherit')
|
||||
buildTagsFilter([])
|
||||
|
||||
function buildTagsFilter (tags) {
|
||||
for (let i = 0; i < tags.length; i++) {
|
||||
tags[i] = {
|
||||
id: i,
|
||||
text: unescapeHTML(tags[i])
|
||||
}
|
||||
}
|
||||
filtertags = tags
|
||||
}
|
||||
$('.ui-use-tags').on('change', function () {
|
||||
const tags = []
|
||||
const data = $(this).select2('data')
|
||||
for (let i = 0; i < data.length; i++) { tags.push(data[i].text) }
|
||||
if (tags.length > 0) {
|
||||
historyList.filter(item => {
|
||||
const values = item.values()
|
||||
if (!values.tags) return false
|
||||
let found = false
|
||||
for (let i = 0; i < tags.length; i++) {
|
||||
if (values.tags.includes(tags[i])) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
return found
|
||||
})
|
||||
} else {
|
||||
historyList.filter()
|
||||
}
|
||||
checkHistoryList()
|
||||
})
|
||||
|
||||
$('.search').keyup(() => {
|
||||
checkHistoryList()
|
||||
})
|
||||
|
|
|
@ -44,6 +44,8 @@ require('prismjs/components/prism-gherkin')
|
|||
|
||||
require('./lib/common/login')
|
||||
require('../vendor/md-toc')
|
||||
|
||||
|
||||
let viz = new window.Viz()
|
||||
const plantumlEncoder = require('plantuml-encoder')
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
/* eslint-env browser, jquery */
|
||||
/* global serverurl, moment */
|
||||
|
||||
import $ from 'jquery'
|
||||
import store from 'store'
|
||||
import LZString from '@hackmd/lz-string'
|
||||
|
||||
import {serverurl} from "./lib/config";
|
||||
import escapeHTML from 'lodash/escape'
|
||||
|
||||
import {
|
||||
|
@ -11,12 +12,10 @@ import {
|
|||
encodeNoteId
|
||||
} from './utils'
|
||||
|
||||
import { checkIfAuth } from './lib/common/login'
|
||||
import {checkIfAuth, isLogin} from './lib/common/login'
|
||||
|
||||
import {urlpath} from './lib/config'
|
||||
|
||||
window.migrateHistoryFromTempCallback = null
|
||||
|
||||
export function saveHistory(notehistory) {
|
||||
checkIfAuth(
|
||||
() => {
|
||||
|
@ -67,7 +66,9 @@ export function clearDuplicatedHistory (notehistory) {
|
|||
break
|
||||
}
|
||||
}
|
||||
if (!found) { newnotehistory.push(notehistory[i]) }
|
||||
if (!found) {
|
||||
newnotehistory.push(notehistory[i])
|
||||
}
|
||||
}
|
||||
return newnotehistory
|
||||
}
|
||||
|
@ -154,22 +155,42 @@ function generateHistory (title, tags, notehistory) {
|
|||
}
|
||||
|
||||
// used for outer
|
||||
export function getHistory (callback) {
|
||||
checkIfAuth(
|
||||
() => {
|
||||
getServerHistory(callback)
|
||||
},
|
||||
() => {
|
||||
getStorageHistory(callback)
|
||||
|
||||
export async function getHistoryAsync(): Promise<NoteHistory[]> {
|
||||
if (await isLogin()) {
|
||||
return await getServerHistoryAsync()
|
||||
}
|
||||
)
|
||||
return getStorageHistoryAsync();
|
||||
}
|
||||
|
||||
export function getHistory(callback: (_: NoteHistory[]) => void) {
|
||||
getHistoryAsync()
|
||||
.then((data) => {
|
||||
callback(data)
|
||||
})
|
||||
}
|
||||
|
||||
export interface NoteHistory {
|
||||
id?: string
|
||||
text?: string
|
||||
time?: Date | number
|
||||
tags?: string[]
|
||||
pinned?: boolean
|
||||
}
|
||||
|
||||
function getServerHistoryAsync(): Promise<NoteHistory[]> {
|
||||
return new Promise((resolve) => {
|
||||
getServerHistory(function (data: NoteHistory[]) {
|
||||
resolve(data)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function getServerHistory(callback) {
|
||||
$.get(`${serverurl}/history`)
|
||||
.done(data => {
|
||||
if (data.history) {
|
||||
callback(data.history)
|
||||
callback(data.history as NoteHistory[])
|
||||
}
|
||||
})
|
||||
.fail((xhr, status, error) => {
|
||||
|
@ -177,10 +198,23 @@ function getServerHistory (callback) {
|
|||
})
|
||||
}
|
||||
|
||||
function getStorageHistoryAsync(): NoteHistory[] {
|
||||
let data = store.get('notehistory')
|
||||
if (data) {
|
||||
if (typeof data === 'string') {
|
||||
data = JSON.parse(data)
|
||||
}
|
||||
return data
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
export function getStorageHistory(callback) {
|
||||
let data = store.get('notehistory')
|
||||
if (data) {
|
||||
if (typeof data === 'string') { data = JSON.parse(data) }
|
||||
if (typeof data === 'string') {
|
||||
data = JSON.parse(data)
|
||||
}
|
||||
callback(data)
|
||||
}
|
||||
// eslint-disable-next-line standard/no-callback-literal
|
||||
|
@ -213,7 +247,9 @@ export function parseServerToHistory (list, callback) {
|
|||
export function parseStorageToHistory(list, callback) {
|
||||
let data = store.get('notehistory')
|
||||
if (data) {
|
||||
if (typeof data === 'string') { data = JSON.parse(data) }
|
||||
if (typeof data === 'string') {
|
||||
data = JSON.parse(data)
|
||||
}
|
||||
parseToHistory(list, data, callback)
|
||||
}
|
||||
parseToHistory(list, [], callback)
|
||||
|
@ -242,29 +278,53 @@ function parseToHistory (list, notehistory, callback) {
|
|||
notehistory[i].text = escapeHTML(notehistory[i].text)
|
||||
notehistory[i].tags = (notehistory[i].tags && notehistory[i].tags.length > 0) ? escapeHTML(notehistory[i].tags).split(',') : []
|
||||
// add to list
|
||||
if (notehistory[i].id && list.get('id', notehistory[i].id).length === 0) { list.add(notehistory[i]) }
|
||||
if (notehistory[i].id && list.get('id', notehistory[i].id).length === 0) {
|
||||
list.add(notehistory[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
callback(list, notehistory)
|
||||
}
|
||||
|
||||
export function postHistoryToServer (noteId, data, callback) {
|
||||
$.post(`${serverurl}/history/${noteId}`, data)
|
||||
.done(result => callback(null, result))
|
||||
interface HistoryUpdatePayload {
|
||||
pinned: boolean
|
||||
}
|
||||
|
||||
export function postHistoryToServerAsync(noteId: string, data ?: HistoryUpdatePayload) {
|
||||
return new Promise((resolve, reject) => {
|
||||
$.post(`${serverurl}/history/${noteId}`, data as any)
|
||||
.done(result => resolve(result))
|
||||
.fail((xhr, status, error) => {
|
||||
console.error(xhr.responseText)
|
||||
return callback(error, null)
|
||||
return reject(error)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteServerHistory (noteId, callback) {
|
||||
export function deleteServerHistoryAsync(noteId: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
$.ajax({
|
||||
url: `${serverurl}/history${noteId ? '/' + noteId : ''}`,
|
||||
url: `${serverurl}/history/${noteId}`,
|
||||
type: 'DELETE'
|
||||
})
|
||||
.done(result => callback(null, result))
|
||||
.done(result => resolve(result))
|
||||
.fail((xhr, status, error) => {
|
||||
console.error(xhr.responseText)
|
||||
return callback(error, null)
|
||||
return reject(error)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function clearServerHistoryAsync() {
|
||||
return new Promise((resolve, reject) => {
|
||||
$.ajax({
|
||||
url: `${serverurl}/history`,
|
||||
type: 'DELETE'
|
||||
})
|
||||
.done(result => resolve(result))
|
||||
.fail((xhr, status, error) => {
|
||||
console.error(xhr.responseText)
|
||||
return reject(error)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -23,14 +23,14 @@ import { Spinner } from 'spin.js'
|
|||
|
||||
import {
|
||||
checkLoginStateChanged,
|
||||
setloginStateChangeEvent
|
||||
setLoginStateChangeEvent
|
||||
} from './lib/common/login'
|
||||
|
||||
import {
|
||||
debug,
|
||||
DROPBOX_APP_KEY,
|
||||
noteid,
|
||||
noteurl,
|
||||
noteurl, serverurl,
|
||||
urlpath,
|
||||
version
|
||||
} from './lib/config'
|
||||
|
@ -66,10 +66,9 @@ import {
|
|||
|
||||
import {
|
||||
writeHistory,
|
||||
deleteServerHistory,
|
||||
getHistory,
|
||||
saveHistory,
|
||||
removeHistory
|
||||
removeHistory, deleteServerHistoryAsync
|
||||
} from './history'
|
||||
|
||||
import { preventXSS } from './render'
|
||||
|
@ -396,7 +395,7 @@ function setNeedRefresh () {
|
|||
showStatus(statusType.offline)
|
||||
}
|
||||
|
||||
setloginStateChangeEvent(function () {
|
||||
setLoginStateChangeEvent(function () {
|
||||
setRefreshModal('user-state-changed')
|
||||
setNeedRefresh()
|
||||
})
|
||||
|
@ -1752,11 +1751,14 @@ socket.on('error', function (data) {
|
|||
console.error(data)
|
||||
if (data.message && data.message.indexOf('AUTH failed') === 0) { location.href = serverurl + '/403' }
|
||||
})
|
||||
socket.on('delete', function () {
|
||||
socket.on('delete', async function () {
|
||||
if (personalInfo.login) {
|
||||
deleteServerHistory(noteid, function (err, data) {
|
||||
if (!err) location.href = serverurl
|
||||
})
|
||||
try {
|
||||
await deleteServerHistoryAsync(noteid)
|
||||
location.href = serverurl
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
} else {
|
||||
getHistory(function (notehistory) {
|
||||
var newnotehistory = removeHistory(noteid, notehistory)
|
||||
|
|
|
@ -1,24 +1,28 @@
|
|||
/* eslint-env browser, jquery */
|
||||
/* global Cookies */
|
||||
|
||||
import Cookies from 'js-cookie'
|
||||
|
||||
import {serverurl} from '../config'
|
||||
|
||||
let checkAuth = false
|
||||
type PureFunction = () => void
|
||||
|
||||
let checkAuth: boolean = false
|
||||
let profile = null
|
||||
let lastLoginState = getLoginState()
|
||||
let lastUserId = getUserId()
|
||||
let loginStateChangeEvent = null
|
||||
let lastUserId: string | null = getUserId()
|
||||
let loginStateChangeEvent: PureFunction | null = null
|
||||
|
||||
export function setloginStateChangeEvent (func) {
|
||||
export function setLoginStateChangeEvent(func: PureFunction): void {
|
||||
loginStateChangeEvent = func
|
||||
}
|
||||
|
||||
export function resetCheckAuth () {
|
||||
export function resetCheckAuth(): void {
|
||||
checkAuth = false
|
||||
}
|
||||
|
||||
export function setLoginState (bool, id) {
|
||||
Cookies.set('loginstate', bool, {
|
||||
function setLoginState(bool: boolean, id?: string) {
|
||||
Cookies.set('loginstate', String(bool), {
|
||||
expires: 365
|
||||
})
|
||||
if (id) {
|
||||
|
@ -29,33 +33,40 @@ export function setLoginState (bool, id) {
|
|||
Cookies.remove('userid')
|
||||
}
|
||||
lastLoginState = bool
|
||||
lastUserId = id
|
||||
lastUserId = id || null
|
||||
checkLoginStateChanged()
|
||||
}
|
||||
|
||||
export function checkLoginStateChanged () {
|
||||
export function checkLoginStateChanged(): boolean {
|
||||
if (getLoginState() !== lastLoginState || getUserId() !== lastUserId) {
|
||||
if (loginStateChangeEvent) setTimeout(loginStateChangeEvent, 100)
|
||||
return true
|
||||
} else {
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export function getLoginState () {
|
||||
export function getLoginState(): boolean {
|
||||
const state = Cookies.get('loginstate')
|
||||
return state === 'true' || state === true
|
||||
return state === 'true'
|
||||
}
|
||||
|
||||
export function getUserId () {
|
||||
return Cookies.get('userid')
|
||||
function getUserId(): string {
|
||||
return Cookies.get('userid') || ""
|
||||
}
|
||||
|
||||
export function clearLoginState () {
|
||||
export function clearLoginState(): void {
|
||||
Cookies.remove('loginstate')
|
||||
}
|
||||
|
||||
export function checkIfAuth (yesCallback, noCallback) {
|
||||
type YesCallbackFunc = (profile: any) => void
|
||||
|
||||
/**
|
||||
* getLoginUserProfile
|
||||
* @throws Error when user not login
|
||||
* @return profile user profile
|
||||
*/
|
||||
export async function getLoginUserProfile(): Promise<any> {
|
||||
return new Promise(function (resolve, reject) {
|
||||
const cookieLoginState = getLoginState()
|
||||
if (checkLoginStateChanged()) checkAuth = false
|
||||
if (!checkAuth || typeof cookieLoginState === 'undefined') {
|
||||
|
@ -63,30 +74,40 @@ export function checkIfAuth (yesCallback, noCallback) {
|
|||
.done(data => {
|
||||
if (data && data.status === 'ok') {
|
||||
profile = data
|
||||
yesCallback(profile)
|
||||
setLoginState(true, data.id)
|
||||
return resolve(profile)
|
||||
} else {
|
||||
noCallback()
|
||||
setLoginState(false)
|
||||
return reject(new Error('user not login'))
|
||||
}
|
||||
})
|
||||
.fail(() => {
|
||||
noCallback()
|
||||
return reject(new Error('user not login'))
|
||||
})
|
||||
.always(() => {
|
||||
checkAuth = true
|
||||
})
|
||||
} else if (cookieLoginState) {
|
||||
yesCallback(profile)
|
||||
return resolve(profile)
|
||||
} else {
|
||||
noCallback()
|
||||
return reject(new Error('user not login'))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export async function isLogin(): Promise<boolean> {
|
||||
try {
|
||||
await getLoginUserProfile()
|
||||
return true
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
checkAuth,
|
||||
profile,
|
||||
lastLoginState,
|
||||
lastUserId,
|
||||
loginStateChangeEvent
|
||||
export function checkIfAuth(yesCallback: YesCallbackFunc, noCallback: PureFunction) {
|
||||
getLoginUserProfile().then((profile) => {
|
||||
yesCallback(profile)
|
||||
}).catch(() => {
|
||||
noCallback()
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,12 +1,3 @@
|
|||
declare interface Window {
|
||||
DROPBOX_APP_KEY: string,
|
||||
domain: string,
|
||||
urlpath: string,
|
||||
debug: boolean,
|
||||
serverurl: string,
|
||||
version: string
|
||||
USE_CDN: boolean
|
||||
}
|
||||
|
||||
export const DROPBOX_APP_KEY = window.DROPBOX_APP_KEY || ''
|
||||
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
/* eslint-env browser, jquery */
|
||||
/* global Cookies */
|
||||
|
||||
var lang = 'en'
|
||||
var userLang = navigator.language || navigator.userLanguage
|
||||
var userLangCode = userLang.split('-')[0]
|
||||
var locale = $('.ui-locale')
|
||||
var supportLangs = []
|
||||
$('.ui-locale option').each(function () {
|
||||
supportLangs.push($(this).val())
|
||||
})
|
||||
import $ from 'jquery'
|
||||
import Cookies from 'js-cookie'
|
||||
|
||||
const userLang = navigator.language || navigator.userLanguage
|
||||
const userLangCode = userLang.split('-')[0]
|
||||
|
||||
let lang = 'en'
|
||||
let supportLangs: string[] = []
|
||||
|
||||
function reloadLanguageFromCookie () {
|
||||
if (Cookies.get('locale')) {
|
||||
lang = Cookies.get('locale')
|
||||
lang = Cookies.get('locale') as string
|
||||
if (lang === 'zh') {
|
||||
lang = 'zh-TW'
|
||||
}
|
||||
|
@ -19,13 +21,28 @@ if (Cookies.get('locale')) {
|
|||
} else if (supportLangs.indexOf(userLangCode) !== -1) {
|
||||
lang = supportLangs[supportLangs.indexOf(userLangCode)]
|
||||
}
|
||||
}
|
||||
|
||||
locale.val(lang)
|
||||
function fillSupportLanguageArrayFromDropdownOptions () {
|
||||
const $langOptions = $('.ui-locale option');
|
||||
$langOptions.each(function () {
|
||||
supportLangs.push($(this).val() as string)
|
||||
})
|
||||
}
|
||||
|
||||
export function initializeLocaleDropdown() {
|
||||
const $locale = $('.ui-locale')
|
||||
|
||||
fillSupportLanguageArrayFromDropdownOptions()
|
||||
reloadLanguageFromCookie()
|
||||
|
||||
$locale.val(lang)
|
||||
$('select.ui-locale option[value="' + lang + '"]').attr('selected', 'selected')
|
||||
|
||||
locale.change(function () {
|
||||
Cookies.set('locale', $(this).val(), {
|
||||
$locale.on('change', function () {
|
||||
Cookies.set('locale', $(this).val() as string, {
|
||||
expires: 365
|
||||
})
|
||||
window.location.reload()
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
/* eslint-env browser, jquery */
|
||||
// allow some attributes
|
||||
|
||||
var filterXSS = require('xss')
|
||||
import {filterXSS, escapeAttrValue, whiteList} from 'xss'
|
||||
|
||||
var whiteListAttr = ['id', 'class', 'style']
|
||||
const whiteListAttr = ['id', 'class', 'style']
|
||||
window.whiteListAttr = whiteListAttr
|
||||
// allow link starts with '.', '/' and custom protocol with '://', exclude link starts with javascript://
|
||||
var linkRegex = /^(?!javascript:\/\/)([\w|-]+:\/\/)|^([.|/])+/i
|
||||
const linkRegex = /^(?!javascript:\/\/)([\w|-]+:\/\/)|^([.|/])+/i
|
||||
// allow data uri, from https://gist.github.com/bgrins/6194623
|
||||
var dataUriRegex = /^\s*data:([a-z]+\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)?)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@/?%\s]*)\s*$/i
|
||||
const dataUriRegex = /^\s*data:([a-z]+\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)?)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@/?%\s]*)\s*$/i
|
||||
// custom white list
|
||||
var whiteList = filterXSS.whiteList
|
||||
// allow ol specify start number
|
||||
whiteList.ol = ['start']
|
||||
// allow li specify value number
|
||||
|
@ -34,7 +33,7 @@ whiteList.figure = []
|
|||
// allow figcaption tag
|
||||
whiteList.figcaption = []
|
||||
|
||||
var filterXSSOptions = {
|
||||
const filterXSSOptions = {
|
||||
allowCommentTag: true,
|
||||
whiteList: whiteList,
|
||||
escapeHtml: function (html) {
|
||||
|
@ -51,28 +50,25 @@ var filterXSSOptions = {
|
|||
onTagAttr: function (tag, name, value, isWhiteAttr) {
|
||||
// allow href and src that match linkRegex
|
||||
if (isWhiteAttr && (name === 'href' || name === 'src') && linkRegex.test(value)) {
|
||||
return name + '="' + filterXSS.escapeAttrValue(value) + '"'
|
||||
return name + '="' + escapeAttrValue(value) + '"'
|
||||
}
|
||||
// allow data uri in img src
|
||||
if (isWhiteAttr && (tag === 'img' && name === 'src') && dataUriRegex.test(value)) {
|
||||
return name + '="' + filterXSS.escapeAttrValue(value) + '"'
|
||||
return name + '="' + escapeAttrValue(value) + '"'
|
||||
}
|
||||
},
|
||||
onIgnoreTagAttr: function (tag, name, value, isWhiteAttr) {
|
||||
// allow attr start with 'data-' or in the whiteListAttr
|
||||
if (name.substr(0, 5) === 'data-' || window.whiteListAttr.indexOf(name) !== -1) {
|
||||
// escape its value using built-in escapeAttrValue function
|
||||
return name + '="' + filterXSS.escapeAttrValue(value) + '"'
|
||||
return name + '="' + escapeAttrValue(value) + '"'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function preventXSS (html) {
|
||||
export function preventXSS (html) {
|
||||
return filterXSS(html, filterXSSOptions)
|
||||
}
|
||||
window.preventXSS = preventXSS
|
||||
|
||||
module.exports = {
|
||||
preventXSS: preventXSS,
|
||||
escapeAttrValue: filterXSS.escapeAttrValue
|
||||
}
|
||||
export {escapeAttrValue} from 'xss'
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"lib": ["DOM", "ESNext"],
|
||||
"sourceMap": true,
|
||||
"baseUrl": ".",
|
||||
"allowJs": true,
|
||||
"strict": false,
|
||||
"strictNullChecks": true,
|
||||
"strictBindCallApply": true,
|
||||
"noImplicitThis": true,
|
||||
"alwaysStrict": true,
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
|
||||
"typeRoots": ["../../node_modules/@types",
|
||||
"./typings"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"./"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
declare global {
|
||||
interface Window {
|
||||
Viz: any
|
||||
createtime: any
|
||||
lastchangetime: any
|
||||
lastchangeui: any
|
||||
owner: any
|
||||
ownerprofile: any
|
||||
whiteListAttr: any
|
||||
preventXSS: any
|
||||
}
|
||||
}
|
|
@ -2,23 +2,23 @@ import base64url from 'base64url'
|
|||
|
||||
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
|
||||
|
||||
export function checkNoteIdValid (id) {
|
||||
export function checkNoteIdValid (id: string) {
|
||||
const result = id.match(uuidRegex)
|
||||
return !!(result && result.length === 1)
|
||||
}
|
||||
|
||||
export function encodeNoteId (id) {
|
||||
export function encodeNoteId (id: string) {
|
||||
// remove dashes in UUID and encode in url-safe base64
|
||||
const str = id.replace(/-/g, '')
|
||||
const hexStr = Buffer.from(str, 'hex')
|
||||
return base64url.encode(hexStr)
|
||||
}
|
||||
|
||||
export function decodeNoteId (encodedId) {
|
||||
export function decodeNoteId (encodedId: string) {
|
||||
// decode from url-safe base64
|
||||
const id = base64url.toBuffer(encodedId).toString('hex')
|
||||
// add dashes between the UUID string parts
|
||||
const idParts = []
|
||||
const idParts: string[] = []
|
||||
idParts.push(id.substr(0, 8))
|
||||
idParts.push(id.substr(8, 4))
|
||||
idParts.push(id.substr(12, 4))
|
||||
|
|
Loading…
Reference in New Issue