1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00
joplin/packages/app-desktop/gui/note-viewer/lib.js

239 lines
6.5 KiB
JavaScript

function pregQuote(str, delimiter = '') {
return (`${str}`).replace(new RegExp(`[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\${delimiter || ''}-]`, 'g'), '\\$&');
}
function replaceRegexDiacritics(regexString) {
if (!regexString) return '';
const diacriticReplacements = {
a: '[aàáâãäåāą]',
A: '[AÀÁÂÃÄÅĀĄ]',
c: '[cçćč]',
C: '[CÇĆČ]',
d: '[dđď]',
D: '[DĐĎ]',
e: '[eèéêëěēę]',
E: '[EÈÉÊËĚĒĘ]',
i: '[iìíîïī]',
I: '[IÌÍÎÏĪ]',
l: '[lł]',
L: '[LŁ]',
n: '[nñňń]',
N: '[NÑŇŃ]',
o: '[oòóôõöøō]',
O: '[OÒÓÔÕÖØŌ]',
r: '[rř]',
R: '[RŘ]',
s: '[sšś]',
S: '[SŠŚ]',
t: '[tť]',
T: '[TŤ]',
u: '[uùúûüůū]',
U: '[UÙÚÛÜŮŪ]',
y: '[yÿý]',
Y: '[YŸÝ]',
z: '[zžżź]',
Z: '[ZŽŻŹ]',
};
let output = '';
for (let i = 0; i < regexString.length; i++) {
const c = regexString[i];
const r = diacriticReplacements[c];
if (r) {
output += r;
} else {
output += c;
}
}
return output;
}
if (typeof module !== 'undefined') {
module.exports = { pregQuote, replaceRegexDiacritics };
}
const markJsUtils = {};
const isInsideContainer = (node, tagName) => {
if (!node) return false;
tagName = tagName.toLowerCase();
while (node) {
if (node.tagName && node.tagName.toLowerCase() === tagName) return true;
node = node.parentNode;
}
return false;
};
markJsUtils.markKeyword = (mark, keyword, stringUtils, extraOptions = null) => {
if (typeof keyword === 'string') {
keyword = {
type: 'text',
value: keyword,
};
}
const isBasicSearch = ['ja', 'zh', 'ko'].indexOf(keyword.scriptType) >= 0;
let value = keyword.value;
let accuracy = keyword.accuracy ? keyword.accuracy : { value: 'exactly', limiters: ':;.,-–—‒_(){}[]!\'"+='.split('') };
if (isBasicSearch) accuracy = 'partially';
if (keyword.type === 'regex') {
accuracy = 'complementary';
// Remove the trailing wildcard and "accuracy = complementary" will take
// care of highlighting the relevant keywords.
// Known bug: it will also highlight word that contain the term as a
// suffix for example for "ent*", it will highlight "present" which is
// incorrect (it should only highlight what starts with "ent") but for
// now will do. Mark.js doesn't have an option to tweak this behaviour.
value = keyword.value.substr(0, keyword.value.length - 1);
}
mark.mark(
[value],
{
accuracy: accuracy,
filter: (node, _term, _totalCounter, _counter) => {
// We exclude SVG because it creates a "<mark>" tag inside
// the document, which is not a valid SVG tag. As a result
// the content within that tag disappears.
//
// mark.js has an "exclude" parameter, but it doesn't work
// so we use "filter" instead.
//
// https://github.com/joplin/plugin-abc-sheet-music
if (isInsideContainer(node, 'SVG')) return false;
return true;
},
...extraOptions,
},
);
};
if (typeof module !== 'undefined') {
module.exports = markJsUtils;
}
const webviewLib = {};
let manualDownloadResourceElements = [];
webviewLib.onUnloadedResourceClick = function(event) {
const resourceId = event.currentTarget.getAttribute('data-resource-id');
webviewLib.options_.postMessage(`markForDownload:${resourceId}`);
};
webviewLib.setupResourceManualDownload = function() {
for (const element of manualDownloadResourceElements) {
element.style.cursor = 'default';
element.removeEventListener('click', webviewLib.onUnloadedResourceClick);
}
manualDownloadResourceElements = [];
const elements = document.getElementsByClassName('resource-status-notDownloaded');
for (const element of elements) {
element.style.cursor = 'pointer';
element.addEventListener('click', webviewLib.onUnloadedResourceClick);
manualDownloadResourceElements.push(element);
}
};
webviewLib.handleInternalLink = function(event, anchorNode) {
const href = anchorNode.getAttribute('href');
if (!href) return false;
if (href.indexOf('#') === 0) {
event.preventDefault();
// let old_hash = location.hash;
location.hash = href;
// HACK
// For some reason anchors at the bottom cause the webview to move itself
// so that the content is aligned with the top of the screen
// This basically refreshes the scroll view so that is returns to a normal
// position, the scroll positions stays correct though
// Additionally an anchor could not be clicked twice because the location
// would not change, this fixes that also
//
// Commented out to fix https://github.com/laurent22/joplin/issues/2141
// We might need to fix a better fix to the previous bug.
//
// setTimeout(function() {
// location.hash = old_hash;
// }, 10);
return true;
}
return false;
};
webviewLib.getParentAnchorElement = function(element) {
let counter = 0;
while (true) {
if (counter++ >= 10000) {
console.warn('been looping for too long - exiting');
return null;
}
if (!element) return null;
if (element.nodeName.toUpperCase() === 'A') return element;
element = element.parentElement;
}
};
webviewLib.cloneError = function(error) {
return {
message: error.message,
stack: error.stack,
};
};
webviewLib.logEnabledEventHandler = function(fn) {
return function(event) {
try {
return fn(event);
} catch (error) {
webviewLib.options_.postMessage(`error:${JSON.stringify(webviewLib.cloneError(error))}`);
throw error;
}
};
};
webviewLib.initialize = function(options) {
webviewLib.options_ = options;
};
document.addEventListener('click', (event) => {
const anchor = webviewLib.getParentAnchorElement(event.target);
if (!anchor) return;
// Prevent URLs added via <a> tags from being opened within the application itself
// otherwise it would open the whole website within the WebView.
// Note that we already handle some links in html_inline.js, however not all of them
// go through this plugin, in particular links coming from third-party packages such
// as Katex or Mermaid.
if (!anchor.hasAttribute('data-from-md')) {
if (webviewLib.handleInternalLink(event, anchor)) return;
event.preventDefault();
if (anchor.getAttribute('href')) webviewLib.options_.postMessage(anchor.getAttribute('href'));
// Depending on the chart type, the generated SVG contains an anchor element with xlink:href attribute.
if (anchor.getAttribute('xlink:href')) webviewLib.options_.postMessage(anchor.getAttribute('xlink:href'));
return;
}
// If this is an internal link, jump to the anchor directly
if (anchor.hasAttribute('data-from-md')) {
if (webviewLib.handleInternalLink(event, anchor)) return;
}
});