generated from dellevin/template
原生epub阅读
This commit is contained in:
@@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
"presets": [
|
|
||||||
["@babel/preset-env", {
|
|
||||||
"targets": {
|
|
||||||
"android": "7",
|
|
||||||
"chrome": "70",
|
|
||||||
"safari": "12",
|
|
||||||
"ios": "12"
|
|
||||||
},
|
|
||||||
"useBuiltIns": "entry",
|
|
||||||
"corejs": 3,
|
|
||||||
"modules": "auto"
|
|
||||||
}]
|
|
||||||
],
|
|
||||||
"plugins": [
|
|
||||||
["@babel/plugin-transform-runtime", {
|
|
||||||
"regenerator": true,
|
|
||||||
"corejs": false,
|
|
||||||
"helpers": true,
|
|
||||||
"useESModules": false
|
|
||||||
}],
|
|
||||||
"@babel/plugin-transform-class-properties",
|
|
||||||
"@babel/plugin-transform-private-methods",
|
|
||||||
"@babel/plugin-transform-nullish-coalescing-operator",
|
|
||||||
"@babel/plugin-transform-optional-chaining"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
1
assets/foliate-js/.gitattributes
vendored
1
assets/foliate-js/.gitattributes
vendored
@@ -1 +0,0 @@
|
|||||||
vendor/* linguist-vendored=true
|
|
||||||
1
assets/foliate-js/.gitignore
vendored
1
assets/foliate-js/.gitignore
vendored
@@ -1 +0,0 @@
|
|||||||
node_modules/
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
This folder forked from [Foliate-js](https://github.com/johnfactotum/foliate-js) which is MIT licensed.
|
|
||||||
Some changes also come from [readest/foliate-js](https://github.com/readest/foliate-js) which is MIT licensed.
|
|
||||||
@@ -1,233 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
|
||||||
<title>Anx Reader Debug</title>
|
|
||||||
<style>
|
|
||||||
html {
|
|
||||||
height: 100vh;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
user-select: none;
|
|
||||||
margin: 0 !important;
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
#footnote-dialog {
|
|
||||||
position: fixed;
|
|
||||||
width: 80vw;
|
|
||||||
height: 80vh;
|
|
||||||
max-width: 400px;
|
|
||||||
max-height: 200px;
|
|
||||||
min-width: 300px;
|
|
||||||
min-height: 200px;
|
|
||||||
border-radius: 15px;
|
|
||||||
border: 1px solid grey;
|
|
||||||
user-select: none;
|
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
-webkit-backdrop-filter: blur(10px);
|
|
||||||
outline: none;
|
|
||||||
z-index: 1000;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
#footnote-dialog main {
|
|
||||||
overflow: auto;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#debug-controls {
|
|
||||||
position: fixed;
|
|
||||||
top: 10px;
|
|
||||||
right: 10px;
|
|
||||||
background: rgba(0, 0, 0, 0.8);
|
|
||||||
color: white;
|
|
||||||
padding: 10px;
|
|
||||||
border-radius: 5px;
|
|
||||||
z-index: 2000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.translated-text {
|
|
||||||
display: block !important;
|
|
||||||
font-style: italic;
|
|
||||||
color: #666;
|
|
||||||
font-size: 0.9em;
|
|
||||||
margin-top: 0.2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.translated-text.hidden {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div id="debug-controls">
|
|
||||||
<div>
|
|
||||||
<button onclick="window.prevPage()">Prev</button>
|
|
||||||
<button onclick="window.nextPage()">Next</button>
|
|
||||||
<button onclick="window.goToPercent(0.5)">50%</button>
|
|
||||||
</div>
|
|
||||||
<!-- <div style="margin-top: 10px;">
|
|
||||||
<label>Translation:</label><br>
|
|
||||||
<button onclick="setTranslationMode('off')">Off</button>
|
|
||||||
<button onclick="setTranslationMode('bilingual')">Bilingual</button>
|
|
||||||
<button onclick="setTranslationMode('translation-only')">Trans Only</button>
|
|
||||||
<button onclick="setTranslationMode('original-only')">Orig Only</button>
|
|
||||||
</div>
|
|
||||||
<div style="margin-top: 10px;">
|
|
||||||
<button onclick="testCFIPreservation()">Test CFI</button>
|
|
||||||
<div id="cfi-result" style="font-size: 0.8em; margin-top: 5px;"></div>
|
|
||||||
</div> -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="footnote-dialog">
|
|
||||||
<main></main>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script type="importmap">
|
|
||||||
{
|
|
||||||
"imports": {
|
|
||||||
"core-js/stable": "https://esm.sh/core-js@3/stable"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
console.log("AnxUA", navigator.userAgent);
|
|
||||||
|
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
|
||||||
if (!urlParams.has('url')) {
|
|
||||||
const mockParams = {
|
|
||||||
importing: false,
|
|
||||||
url: '../local/xyz.epub',
|
|
||||||
initialCfi: "epubcfi(/6/14!/4,/2[CHP4]/1:1,/16/1:43)",
|
|
||||||
style: {
|
|
||||||
fontSize: 1.4,
|
|
||||||
fontName: "book",
|
|
||||||
fontPath: "",
|
|
||||||
fontWeight: 400.0,
|
|
||||||
letterSpacing: 0.0,
|
|
||||||
spacing: 1.5,
|
|
||||||
paragraphSpacing: 1.0,
|
|
||||||
textIndent: 2.0,
|
|
||||||
fontColor: "#111111ff",
|
|
||||||
backgroundColor: "#fefefeff",
|
|
||||||
topMargin: 20.0,
|
|
||||||
bottomMargin: 50.0,
|
|
||||||
sideMargin: 15.0,
|
|
||||||
justify: true,
|
|
||||||
hyphenate: true,
|
|
||||||
// 'slide' 'scroll' 'noAnimation'
|
|
||||||
pageTurnStyle: "slide",
|
|
||||||
// pageTurnStyle: "slide",
|
|
||||||
maxColumnCount: 1,
|
|
||||||
// auto 'horizontal-tb' 'vertical-rl'
|
|
||||||
// writingMode: "vertical-rl",
|
|
||||||
writingMode: "auto",
|
|
||||||
backgroundImage: "none",
|
|
||||||
|
|
||||||
// true, false
|
|
||||||
allowScript: true,
|
|
||||||
},
|
|
||||||
readingRules: {
|
|
||||||
convertChineseMode: "none",
|
|
||||||
bionicReadingMode: false
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const params = new URLSearchParams();
|
|
||||||
params.set('importing', JSON.stringify(mockParams.importing));
|
|
||||||
params.set('url', JSON.stringify(mockParams.url));
|
|
||||||
params.set('initialCfi', JSON.stringify(mockParams.initialCfi));
|
|
||||||
params.set('style', JSON.stringify(mockParams.style));
|
|
||||||
params.set('readingRules', JSON.stringify(mockParams.readingRules));
|
|
||||||
|
|
||||||
window.location.href = window.location.pathname + '?' + params.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
window.flutter_inappwebview = {
|
|
||||||
callHandler: (name, data) => {
|
|
||||||
console.log(`Flutter Handler Called: ${name}`, data);
|
|
||||||
// Mock translation response for testing
|
|
||||||
if (name === 'translateText') {
|
|
||||||
return Promise.resolve(`TRANS: ${data}`);
|
|
||||||
}
|
|
||||||
return Promise.resolve(null);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// available types: highlight, underline, bookmark
|
|
||||||
const anno = [
|
|
||||||
{ id: 1, type: 'highlight', value: "epubcfi(/6/14!/4,/10/1:0,/16/1:12)", color: 'blue', note: 'this is' },
|
|
||||||
// { id: 2, type: 'ubderline', value: "epubcfi(/6/6!/4/2[pgepubid00003]/18,/3:300,/5:102)", color: '#66ccff', note: 'this is' },
|
|
||||||
// { id: 3, type: 'underline', value: "epubcfi(/6/6!/4/2[pgepubid00003]/18/6,/1:1,/1:8)", color: 'red', note: 'this is' },
|
|
||||||
]
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
reader.renderAnnotation(anno)
|
|
||||||
}, 1000)
|
|
||||||
|
|
||||||
// Translation testing functions
|
|
||||||
function setTranslationMode(mode) {
|
|
||||||
if (reader.view && reader.view.setTranslationMode) {
|
|
||||||
reader.view.setTranslationMode(mode);
|
|
||||||
console.log('Translation mode set to:', mode);
|
|
||||||
} else {
|
|
||||||
console.warn('Translation mode not available');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function testCFIPreservation() {
|
|
||||||
if (!reader.view) {
|
|
||||||
document.getElementById('cfi-result').textContent = 'View not ready';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const testCFI = "epubcfi(/6/6!/4/2[pgepubid00003],/16/1:223,/20/7:170)";
|
|
||||||
const resultDiv = document.getElementById('cfi-result');
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Test 1: Get current location before translation
|
|
||||||
const beforeLocation = reader.view.getCFI();
|
|
||||||
resultDiv.innerHTML = `Before: ${beforeLocation}<br>`;
|
|
||||||
|
|
||||||
// Test 2: Enable translation
|
|
||||||
setTranslationMode('bilingual');
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
// Test 3: Get location after translation
|
|
||||||
const afterLocation = reader.view.getCFI();
|
|
||||||
resultDiv.innerHTML += `After: ${afterLocation}<br>`;
|
|
||||||
|
|
||||||
// Test 4: Navigate to specific CFI
|
|
||||||
reader.view.goTo(testCFI);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
const finalLocation = reader.view.getCFI();
|
|
||||||
resultDiv.innerHTML += `Final: ${finalLocation}<br>`;
|
|
||||||
|
|
||||||
if (beforeLocation && afterLocation && finalLocation) {
|
|
||||||
resultDiv.innerHTML += `<span style="color: ${beforeLocation === afterLocation ? 'green' : 'orange'};">CFI Stable: ${beforeLocation === afterLocation}</span>`;
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
}, 2000);
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
resultDiv.textContent = `Error: ${error.message}`;
|
|
||||||
console.error('CFI test error:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make functions globally available
|
|
||||||
window.setTranslationMode = setTranslationMode;
|
|
||||||
window.testCFIPreservation = testCFIPreservation;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script src="./src/book.js" type="module"></script>
|
|
||||||
<script src="./src/vendor/pdfjs/pdf.js"></script>
|
|
||||||
</html>
|
|
||||||
2
assets/foliate-js/dist/bundle.js
vendored
2
assets/foliate-js/dist/bundle.js
vendored
File diff suppressed because one or more lines are too long
1
assets/foliate-js/dist/bundle.js.LICENSE.txt
vendored
1
assets/foliate-js/dist/bundle.js.LICENSE.txt
vendored
@@ -1 +0,0 @@
|
|||||||
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */
|
|
||||||
2
assets/foliate-js/dist/pdf-legacy.js
vendored
2
assets/foliate-js/dist/pdf-legacy.js
vendored
File diff suppressed because one or more lines are too long
21
assets/foliate-js/dist/pdf-legacy.js.LICENSE.txt
vendored
21
assets/foliate-js/dist/pdf-legacy.js.LICENSE.txt
vendored
@@ -1,21 +0,0 @@
|
|||||||
/**
|
|
||||||
* @licstart The following is the entire license notice for the
|
|
||||||
* JavaScript code in this page
|
|
||||||
*
|
|
||||||
* Copyright 2023 Mozilla Foundation
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*
|
|
||||||
* @licend The above is the entire license notice for the
|
|
||||||
* JavaScript code in this page
|
|
||||||
*/
|
|
||||||
2
assets/foliate-js/dist/pdf-legacy.worker.js
vendored
2
assets/foliate-js/dist/pdf-legacy.worker.js
vendored
File diff suppressed because one or more lines are too long
@@ -1,21 +0,0 @@
|
|||||||
/**
|
|
||||||
* @licstart The following is the entire license notice for the
|
|
||||||
* JavaScript code in this page
|
|
||||||
*
|
|
||||||
* Copyright 2023 Mozilla Foundation
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*
|
|
||||||
* @licend The above is the entire license notice for the
|
|
||||||
* JavaScript code in this page
|
|
||||||
*/
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import js from '@eslint/js'
|
|
||||||
import globals from 'globals'
|
|
||||||
|
|
||||||
export default [js.configs.recommended, { ignores: ['vendor'] }, {
|
|
||||||
languageOptions: {
|
|
||||||
globals: globals.browser,
|
|
||||||
},
|
|
||||||
linterOptions: {
|
|
||||||
reportUnusedDisableDirectives: true,
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
semi: ['error', 'never'],
|
|
||||||
indent: ['warn', 4, { flatTernaryExpressions: true, SwitchCase: 1 }],
|
|
||||||
quotes: ['warn', 'single', { avoidEscape: true }],
|
|
||||||
'comma-dangle': ['warn', 'always-multiline'],
|
|
||||||
'no-trailing-spaces': 'warn',
|
|
||||||
'no-unused-vars': 'warn',
|
|
||||||
'no-console': ['warn', { allow: ['debug', 'warn', 'error', 'assert'] }],
|
|
||||||
'no-constant-condition': ['error', { checkLoops: false }],
|
|
||||||
'no-empty': ['error', { allowEmptyCatch: true }],
|
|
||||||
},
|
|
||||||
}]
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
|
||||||
<title>Anx Reader</title>
|
|
||||||
<style>
|
|
||||||
html {
|
|
||||||
height: 100vh;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
user-select: none;
|
|
||||||
margin: 0 !important;
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#footnote-dialog {
|
|
||||||
/*
|
|
||||||
padding: var(safe-area-inset-top) var(safe-area-inset-right) var(safe-area-inset-bottom) var(safe-area-inset-left);
|
|
||||||
*/
|
|
||||||
position: fixed;
|
|
||||||
width: 80vw;
|
|
||||||
height: 80vh;
|
|
||||||
max-width: 400px;
|
|
||||||
max-height: 200px;
|
|
||||||
min-width: 300px;
|
|
||||||
min-height: 200px;
|
|
||||||
border-radius: 15px;
|
|
||||||
border: 1px solid grey;
|
|
||||||
user-select: none;
|
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
-webkit-backdrop-filter: blur(10px);
|
|
||||||
outline: none;
|
|
||||||
z-index: 1000;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
#footnote-dialog main {
|
|
||||||
overflow: auto;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<div id="footnote-dialog">
|
|
||||||
<main></main>
|
|
||||||
</div>
|
|
||||||
<script>
|
|
||||||
// Prevent the default WebView2 right-click context menu on Windows.
|
|
||||||
document.addEventListener('contextmenu', e => { e.preventDefault(); });
|
|
||||||
</script>
|
|
||||||
<script>
|
|
||||||
(function () {
|
|
||||||
const ua = navigator.userAgent || '';
|
|
||||||
const platform = navigator.platform || '';
|
|
||||||
console.log('AnxUA', ua);
|
|
||||||
|
|
||||||
const isAppleDevice = /iPad|iPhone|iPod/.test(ua)
|
|
||||||
|| (/Mac/.test(platform) && navigator.maxTouchPoints > 1)
|
|
||||||
|| /Mac OS X/.test(ua);
|
|
||||||
|
|
||||||
const chromeMatch = ua.match(/Chrome\/(\d+)/);
|
|
||||||
const chromeVersion = chromeMatch ? parseInt(chromeMatch[1], 10) : NaN;
|
|
||||||
const shouldUseModernBundle = isAppleDevice || (!Number.isNaN(chromeVersion) && chromeVersion >= 100);
|
|
||||||
|
|
||||||
const loadScript = (src, options = {}) => new Promise((resolve, reject) => {
|
|
||||||
const script = document.createElement('script');
|
|
||||||
script.src = src;
|
|
||||||
if (options.type) script.type = options.type;
|
|
||||||
if (options.async !== undefined) script.async = options.async;
|
|
||||||
if (options.defer !== undefined) script.defer = options.defer;
|
|
||||||
script.onload = () => resolve();
|
|
||||||
script.onerror = (err) => {
|
|
||||||
console.error('Failed to load script', src, err);
|
|
||||||
reject(err);
|
|
||||||
};
|
|
||||||
document.head.appendChild(script);
|
|
||||||
});
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
if (shouldUseModernBundle) {
|
|
||||||
await loadScript('./src/vendor/pdfjs/pdf.js', { async: false, defer: false });
|
|
||||||
await loadScript('./src/book.js', { type: 'module' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await loadScript('./dist/bundle.js', { type: 'module' });
|
|
||||||
await loadScript('./dist/pdf-legacy.js', { async: false, defer: false });
|
|
||||||
})();
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
3904
assets/foliate-js/package-lock.json
generated
3904
assets/foliate-js/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,33 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "foliate",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "This folder forked from [Foliate-js](https://github.com/johnfactotum/foliate-js) which is MIT licensed.",
|
|
||||||
"main": "book.js",
|
|
||||||
"scripts": {
|
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
|
||||||
"build": "rm -rf dist && webpack",
|
|
||||||
"debug": "http-server . -p 3000 -c-1 --cors -o debug.html",
|
|
||||||
"dev": "http-server . -p 3000 -c-1 --cors"
|
|
||||||
},
|
|
||||||
"keywords": [],
|
|
||||||
"author": "",
|
|
||||||
"license": "ISC",
|
|
||||||
"devDependencies": {
|
|
||||||
"@babel/core": "^7.23.0",
|
|
||||||
"@babel/plugin-transform-class-properties": "^7.27.1",
|
|
||||||
"@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1",
|
|
||||||
"@babel/plugin-transform-optional-chaining": "^7.27.1",
|
|
||||||
"@babel/plugin-transform-private-methods": "^7.27.1",
|
|
||||||
"@babel/plugin-transform-runtime": "^7.23.0",
|
|
||||||
"@babel/preset-env": "^7.23.0",
|
|
||||||
"babel-loader": "^9.1.3",
|
|
||||||
"core-js": "^3.33.0",
|
|
||||||
"http-server": "^14.1.1",
|
|
||||||
"webpack": "^5.99.7",
|
|
||||||
"webpack-cli": "^6.0.1"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/runtime": "^7.23.0",
|
|
||||||
"regenerator-runtime": "^0.14.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,45 +0,0 @@
|
|||||||
export const makeComicBook = ({ entries, loadBlob, getSize }, file) => {
|
|
||||||
const cache = new Map()
|
|
||||||
const urls = new Map()
|
|
||||||
const load = async name => {
|
|
||||||
if (cache.has(name)) return cache.get(name)
|
|
||||||
const src = URL.createObjectURL(await loadBlob(name))
|
|
||||||
const page = URL.createObjectURL(
|
|
||||||
new Blob([`<img src="${src}">`], { type: 'text/html' }))
|
|
||||||
urls.set(name, [src, page])
|
|
||||||
cache.set(name, page)
|
|
||||||
return page
|
|
||||||
}
|
|
||||||
const unload = name => {
|
|
||||||
urls.get(name)?.forEach?.(url => URL.revokeObjectURL(url))
|
|
||||||
urls.delete(name)
|
|
||||||
cache.delete(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
const exts = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.svg', '.jxl', '.avif']
|
|
||||||
const files = entries
|
|
||||||
.map(entry => entry.filename)
|
|
||||||
.filter(name => exts.some(ext => name.endsWith(ext)))
|
|
||||||
.sort()
|
|
||||||
if (!files.length) throw new Error('No supported image files in archive')
|
|
||||||
|
|
||||||
const book = {}
|
|
||||||
book.getCover = () => loadBlob(files[0])
|
|
||||||
book.metadata = { title: file.name }
|
|
||||||
book.sections = files.map(name => ({
|
|
||||||
id: name,
|
|
||||||
load: () => load(name),
|
|
||||||
unload: () => unload(name),
|
|
||||||
size: getSize(name),
|
|
||||||
}))
|
|
||||||
book.toc = files.map(name => ({ label: name, href: name }))
|
|
||||||
book.rendition = { layout: 'pre-paginated' }
|
|
||||||
book.resolveHref = href => ({ index: book.sections.findIndex(s => s.id === href) })
|
|
||||||
book.splitTOCHref = href => [href, null]
|
|
||||||
book.getTOCFragment = doc => doc.documentElement
|
|
||||||
book.destroy = () => {
|
|
||||||
for (const arr of urls.values())
|
|
||||||
for (const url of arr) URL.revokeObjectURL(url)
|
|
||||||
}
|
|
||||||
return book
|
|
||||||
}
|
|
||||||
@@ -1,241 +0,0 @@
|
|||||||
const decoder = new TextDecoder()
|
|
||||||
const decode = decoder.decode.bind(decoder)
|
|
||||||
|
|
||||||
const concatTypedArray = (a, b) => {
|
|
||||||
const result = new a.constructor(a.length + b.length)
|
|
||||||
result.set(a)
|
|
||||||
result.set(b, a.length)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
const strcmp = (a, b) => {
|
|
||||||
a = a.toLowerCase(), b = b.toLowerCase()
|
|
||||||
return a < b ? -1 : a > b ? 1 : 0
|
|
||||||
}
|
|
||||||
|
|
||||||
class DictZip {
|
|
||||||
#chlen
|
|
||||||
#chunks
|
|
||||||
#compressed
|
|
||||||
inflate
|
|
||||||
async load(file) {
|
|
||||||
const header = new DataView(await file.slice(0, 12).arrayBuffer())
|
|
||||||
if (header.getUint8(0) !== 31 || header.getUint8(1) !== 139
|
|
||||||
|| header.getUint8(2) !== 8) throw new Error('Not a DictZip file')
|
|
||||||
const flg = header.getUint8(3)
|
|
||||||
if (!flg & 0b100) throw new Error('Missing FEXTRA flag')
|
|
||||||
|
|
||||||
const xlen = header.getUint16(10, true)
|
|
||||||
const extra = new DataView(await file.slice(12, 12 + xlen).arrayBuffer())
|
|
||||||
if (extra.getUint8(0) !== 82 || extra.getUint8(1) !== 65)
|
|
||||||
throw new Error('Subfield ID should be RA')
|
|
||||||
if (extra.getUint16(4, true) !== 1) throw new Error('Unsupported version')
|
|
||||||
|
|
||||||
this.#chlen = extra.getUint16(6, true)
|
|
||||||
const chcnt = extra.getUint16(8, true)
|
|
||||||
this.#chunks = []
|
|
||||||
for (let i = 0, chunkOffset = 0; i < chcnt; i++) {
|
|
||||||
const chunkSize = extra.getUint16(10 + 2 * i, true)
|
|
||||||
this.#chunks.push([chunkOffset, chunkSize])
|
|
||||||
chunkOffset = chunkOffset + chunkSize
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip to compressed data
|
|
||||||
let offset = 12 + xlen
|
|
||||||
const max = Math.min(offset + 512, file.size)
|
|
||||||
const strArr = new Uint8Array(await file.slice(0, max).arrayBuffer())
|
|
||||||
if (flg & 0b1000) { // fname
|
|
||||||
const i = strArr.indexOf(0, offset)
|
|
||||||
if (i < 0) throw new Error('Header too long')
|
|
||||||
offset = i + 1
|
|
||||||
}
|
|
||||||
if (flg & 0b10000) { // fcomment
|
|
||||||
const i = strArr.indexOf(0, offset)
|
|
||||||
if (i < 0) throw new Error('Header too long')
|
|
||||||
offset = i + 1
|
|
||||||
}
|
|
||||||
if (flg & 0b10) offset += 2 // fhcrc
|
|
||||||
this.#compressed = file.slice(offset)
|
|
||||||
}
|
|
||||||
async read(offset, size) {
|
|
||||||
const chunks = this.#chunks
|
|
||||||
const startIndex = Math.trunc(offset / this.#chlen)
|
|
||||||
const endIndex = Math.trunc((offset + size) / this.#chlen)
|
|
||||||
const buf = await this.#compressed.slice(chunks[startIndex][0],
|
|
||||||
chunks[endIndex][0] + chunks[endIndex][1]).arrayBuffer()
|
|
||||||
let arr = new Uint8Array()
|
|
||||||
for (let pos = 0, i = startIndex; i <= endIndex; i++) {
|
|
||||||
const data = new Uint8Array(buf, pos, chunks[i][1])
|
|
||||||
arr = concatTypedArray(arr, await this.inflate(data))
|
|
||||||
pos += chunks[i][1]
|
|
||||||
}
|
|
||||||
const startOffset = offset - startIndex * this.#chlen
|
|
||||||
return arr.subarray(startOffset, startOffset + size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Index {
|
|
||||||
strcmp = strcmp
|
|
||||||
// binary search
|
|
||||||
bisect(query, start = 0, end = this.words.length - 1) {
|
|
||||||
if (end - start === 1) {
|
|
||||||
if (!this.strcmp(query, this.getWord(start))) return start
|
|
||||||
if (!this.strcmp(query, this.getWord(end))) return end
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
const mid = Math.floor(start + (end - start) / 2)
|
|
||||||
const cmp = this.strcmp(query, this.getWord(mid))
|
|
||||||
if (cmp < 0) return this.bisect(query, start, mid)
|
|
||||||
if (cmp > 0) return this.bisect(query, mid, end)
|
|
||||||
return mid
|
|
||||||
}
|
|
||||||
// check for multiple definitions
|
|
||||||
checkAdjacent(query, i) {
|
|
||||||
if (i == null) return []
|
|
||||||
let j = i
|
|
||||||
const equals = i => {
|
|
||||||
const word = this.getWord(i)
|
|
||||||
return word ? this.strcmp(query, word) === 0 : false
|
|
||||||
}
|
|
||||||
while (equals(j - 1)) j--
|
|
||||||
let k = i
|
|
||||||
while (equals(k + 1)) k++
|
|
||||||
return j === k ? [i] : Array.from({ length: k + 1 - j }, (_, i) => j + i)
|
|
||||||
}
|
|
||||||
lookup(query) {
|
|
||||||
return this.checkAdjacent(query, this.bisect(query))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const decodeBase64Number = str => {
|
|
||||||
const { length } = str
|
|
||||||
let n = 0
|
|
||||||
for (let i = 0; i < length; i++) {
|
|
||||||
const c = str.charCodeAt(i)
|
|
||||||
n += (c === 43 ? 62 // "+"
|
|
||||||
: c === 47 ? 63 // "/"
|
|
||||||
: c < 58 ? c + 4 // 0-9 -> 52-61
|
|
||||||
: c < 91 ? c - 65 // A-Z -> 0-25
|
|
||||||
: c - 71 // a-z -> 26-51
|
|
||||||
) * 64 ** (length - 1 - i)
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
class DictdIndex extends Index {
|
|
||||||
getWord(i) {
|
|
||||||
return this.words[i]
|
|
||||||
}
|
|
||||||
async load(file) {
|
|
||||||
const words = []
|
|
||||||
const offsets = []
|
|
||||||
const sizes = []
|
|
||||||
for (const line of decode(await file.arrayBuffer()).split('\n')) {
|
|
||||||
const a = line.split('\t')
|
|
||||||
words.push(a[0])
|
|
||||||
offsets.push(decodeBase64Number(a[1]))
|
|
||||||
sizes.push(decodeBase64Number(a[2]))
|
|
||||||
}
|
|
||||||
this.words = words
|
|
||||||
this.offsets = offsets
|
|
||||||
this.sizes = sizes
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class DictdDict {
|
|
||||||
#dict = new DictZip()
|
|
||||||
#idx = new DictdIndex()
|
|
||||||
loadDict(file, inflate) {
|
|
||||||
this.#dict.inflate = inflate
|
|
||||||
return this.#dict.load(file)
|
|
||||||
}
|
|
||||||
async #readWord(i) {
|
|
||||||
const word = this.#idx.getWord(i)
|
|
||||||
const offset = this.#idx.offsets[i]
|
|
||||||
const size = this.#idx.sizes[i]
|
|
||||||
return { word, data: ['m', this.#dict.read(offset, size)] }
|
|
||||||
}
|
|
||||||
#readWords(arr) {
|
|
||||||
return Promise.all(arr.map(this.#readWord.bind(this)))
|
|
||||||
}
|
|
||||||
lookup(query) {
|
|
||||||
return this.#readWords(this.#idx.lookup(query))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class StarDictIndex extends Index {
|
|
||||||
isSyn
|
|
||||||
#arr
|
|
||||||
getWord(i) {
|
|
||||||
const word = this.words[i]
|
|
||||||
if (!word) return
|
|
||||||
return decode(this.#arr.subarray(word[0], word[1]))
|
|
||||||
}
|
|
||||||
async load(file) {
|
|
||||||
const { isSyn } = this
|
|
||||||
const buf = await file.arrayBuffer()
|
|
||||||
const arr = new Uint8Array(buf)
|
|
||||||
this.#arr = arr
|
|
||||||
const view = new DataView(buf)
|
|
||||||
const words = []
|
|
||||||
const offsets = []
|
|
||||||
const sizes = []
|
|
||||||
for (let i = 0; i < arr.length;) {
|
|
||||||
const newI = arr.subarray(0, i + 256).indexOf(0, i)
|
|
||||||
if (newI < 0) throw new Error('Word too big')
|
|
||||||
words.push([i, newI])
|
|
||||||
offsets.push(view.getUint32(newI + 1))
|
|
||||||
if (isSyn) i = newI + 5
|
|
||||||
else {
|
|
||||||
sizes.push(view.getUint32(newI + 5))
|
|
||||||
i = newI + 9
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.words = words
|
|
||||||
this.offsets = offsets
|
|
||||||
this.sizes = sizes
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class StarDict {
|
|
||||||
#dict = new DictZip()
|
|
||||||
#idx = new StarDictIndex()
|
|
||||||
#syn = Object.assign(new StarDictIndex(), { isSyn: true })
|
|
||||||
async loadIfo(file) {
|
|
||||||
const str = decode(await file.arrayBuffer())
|
|
||||||
this.ifo = Object.fromEntries(str.split('\n').map(line => {
|
|
||||||
const sep = line.indexOf('=')
|
|
||||||
if (sep < 0) return
|
|
||||||
return [line.slice(0, sep), line.slice(sep + 1)]
|
|
||||||
}).filter(x => x))
|
|
||||||
}
|
|
||||||
loadDict(file, inflate) {
|
|
||||||
this.#dict.inflate = inflate
|
|
||||||
return this.#dict.load(file)
|
|
||||||
}
|
|
||||||
loadIdx(file) {
|
|
||||||
return this.#idx.load(file)
|
|
||||||
}
|
|
||||||
loadSyn(file) {
|
|
||||||
if (file) return this.#syn.load(file)
|
|
||||||
}
|
|
||||||
async #readWord(i) {
|
|
||||||
const word = this.#idx.getWord(i)
|
|
||||||
const offset = this.#idx.offsets[i]
|
|
||||||
const size = this.#idx.sizes[i]
|
|
||||||
const data = await this.#dict.read(offset, size)
|
|
||||||
const seq = this.ifo.sametypesequence
|
|
||||||
if (!seq) throw new Error('TODO')
|
|
||||||
if (seq.length === 1) return { word, data: [[seq[0], data]] }
|
|
||||||
throw new Error('TODO')
|
|
||||||
}
|
|
||||||
#readWords(arr) {
|
|
||||||
return Promise.all(arr.map(this.#readWord.bind(this)))
|
|
||||||
}
|
|
||||||
lookup(query) {
|
|
||||||
return this.#readWords(this.#idx.lookup(query))
|
|
||||||
}
|
|
||||||
synonyms(query) {
|
|
||||||
return this.#readWords(this.#syn.lookup(query).map(i => this.#syn.offsets[i]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,345 +0,0 @@
|
|||||||
const findIndices = (arr, f) => arr
|
|
||||||
.map((x, i, a) => f(x, i, a) ? i : null).filter(x => x != null)
|
|
||||||
const splitAt = (arr, is) => [-1, ...is, arr.length].reduce(({ xs, a }, b) =>
|
|
||||||
({ xs: xs?.concat([arr.slice(a + 1, b)]) ?? [], a: b }), {}).xs
|
|
||||||
const concatArrays = (a, b) =>
|
|
||||||
a.slice(0, -1).concat([a[a.length - 1].concat(b[0])]).concat(b.slice(1))
|
|
||||||
|
|
||||||
const isNumber = /\d/
|
|
||||||
export const isCFI = /^epubcfi\((.*)\)$/
|
|
||||||
const escapeCFI = str => str.replace(/[\^[\](),;=]/g, '^$&')
|
|
||||||
|
|
||||||
const wrap = x => isCFI.test(x) ? x : `epubcfi(${x})`
|
|
||||||
const unwrap = x => x.match(isCFI)?.[1] ?? x
|
|
||||||
const lift = f => (...xs) =>
|
|
||||||
`epubcfi(${f(...xs.map(x => x.match(isCFI)?.[1] ?? x))})`
|
|
||||||
export const joinIndir = lift((...xs) => xs.join('!'))
|
|
||||||
|
|
||||||
const tokenizer = str => {
|
|
||||||
const tokens = []
|
|
||||||
let state, escape, value = ''
|
|
||||||
const push = x => (tokens.push(x), state = null, value = '')
|
|
||||||
const cat = x => (value += x, escape = false)
|
|
||||||
for (const char of Array.from(str.trim()).concat('')) {
|
|
||||||
if (char === '^' && !escape) {
|
|
||||||
escape = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (state === '!') push(['!'])
|
|
||||||
else if (state === ',') push([','])
|
|
||||||
else if (state === '/' || state === ':') {
|
|
||||||
if (isNumber.test(char)) {
|
|
||||||
cat(char)
|
|
||||||
continue
|
|
||||||
} else push([state, parseInt(value)])
|
|
||||||
} else if (state === '~') {
|
|
||||||
if (isNumber.test(char) || char === '.') {
|
|
||||||
cat(char)
|
|
||||||
continue
|
|
||||||
} else push(['~', parseFloat(value)])
|
|
||||||
} else if (state === '@') {
|
|
||||||
if (char === ':') {
|
|
||||||
push(['@', parseFloat(value)])
|
|
||||||
state = '@'
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (isNumber.test(char) || char === '.') {
|
|
||||||
cat(char)
|
|
||||||
continue
|
|
||||||
} else push(['@', parseFloat(value)])
|
|
||||||
} else if (state === '[') {
|
|
||||||
if (char === ';' && !escape) {
|
|
||||||
push(['[', value])
|
|
||||||
state = ';'
|
|
||||||
} else if (char === ',' && !escape) {
|
|
||||||
push(['[', value])
|
|
||||||
state = '['
|
|
||||||
} else if (char === ']' && !escape) push(['[', value])
|
|
||||||
else cat(char)
|
|
||||||
continue
|
|
||||||
} else if (state?.startsWith(';')) {
|
|
||||||
if (char === '=' && !escape) {
|
|
||||||
state = `;${value}`
|
|
||||||
value = ''
|
|
||||||
} else if (char === ';' && !escape) {
|
|
||||||
push([state, value])
|
|
||||||
state = ';'
|
|
||||||
} else if (char === ']' && !escape) push([state, value])
|
|
||||||
else cat(char)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (char === '/' || char === ':' || char === '~' || char === '@'
|
|
||||||
|| char === '[' || char === '!' || char === ',') state = char
|
|
||||||
}
|
|
||||||
return tokens
|
|
||||||
}
|
|
||||||
|
|
||||||
const findTokens = (tokens, x) => findIndices(tokens, ([t]) => t === x)
|
|
||||||
|
|
||||||
const parser = tokens => {
|
|
||||||
const parts = []
|
|
||||||
let state
|
|
||||||
for (const [type, val] of tokens) {
|
|
||||||
if (type === '/') parts.push({ index: val })
|
|
||||||
else {
|
|
||||||
const last = parts[parts.length - 1]
|
|
||||||
if (type === ':') last.offset = val
|
|
||||||
else if (type === '~') last.temporal = val
|
|
||||||
else if (type === '@') last.spatial = (last.spatial ?? []).concat(val)
|
|
||||||
else if (type === ';s') last.side = val
|
|
||||||
else if (type === '[') {
|
|
||||||
if (state === '/' && val) last.id = val
|
|
||||||
else {
|
|
||||||
last.text = (last.text ?? []).concat(val)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state = type
|
|
||||||
}
|
|
||||||
return parts
|
|
||||||
}
|
|
||||||
|
|
||||||
// split at step indirections, then parse each part
|
|
||||||
const parserIndir = tokens =>
|
|
||||||
splitAt(tokens, findTokens(tokens, '!')).map(parser)
|
|
||||||
|
|
||||||
export const parse = cfi => {
|
|
||||||
const tokens = tokenizer(unwrap(cfi))
|
|
||||||
const commas = findTokens(tokens, ',')
|
|
||||||
if (!commas.length) return parserIndir(tokens)
|
|
||||||
const [parent, start, end] = splitAt(tokens, commas).map(parserIndir)
|
|
||||||
return { parent, start, end }
|
|
||||||
}
|
|
||||||
|
|
||||||
const partToString = ({ index, id, offset, temporal, spatial, text, side }) => {
|
|
||||||
const param = side ? `;s=${side}` : ''
|
|
||||||
return `/${index}`
|
|
||||||
+ (id ? `[${escapeCFI(id)}${param}]` : '')
|
|
||||||
// "CFI expressions [..] SHOULD include an explicit character offset"
|
|
||||||
+ (offset != null && index % 2 ? `:${offset}` : '')
|
|
||||||
+ (temporal ? `~${temporal}` : '')
|
|
||||||
+ (spatial ? `@${spatial.join(':')}` : '')
|
|
||||||
+ (text || (!id && side) ? '['
|
|
||||||
+ (text?.map(escapeCFI)?.join(',') ?? '')
|
|
||||||
+ param + ']' : '')
|
|
||||||
}
|
|
||||||
|
|
||||||
const toInnerString = parsed => parsed.parent
|
|
||||||
? [parsed.parent, parsed.start, parsed.end].map(toInnerString).join(',')
|
|
||||||
: parsed.map(parts => parts.map(partToString).join('')).join('!')
|
|
||||||
|
|
||||||
const toString = parsed => wrap(toInnerString(parsed))
|
|
||||||
|
|
||||||
export const collapse = (x, toEnd) => typeof x === 'string'
|
|
||||||
? toString(collapse(parse(x), toEnd))
|
|
||||||
: x.parent ? concatArrays(x.parent, x[toEnd ? 'end' : 'start']) : x
|
|
||||||
|
|
||||||
// create range CFI from two CFIs
|
|
||||||
const buildRange = (from, to) => {
|
|
||||||
if (typeof from === 'string') from = parse(from)
|
|
||||||
if (typeof to === 'string') to = parse(to)
|
|
||||||
from = collapse(from)
|
|
||||||
to = collapse(to, true)
|
|
||||||
// ranges across multiple documents are not allowed; handle local paths only
|
|
||||||
const localFrom = from[from.length - 1], localTo = to[to.length - 1]
|
|
||||||
const localParent = [], localStart = [], localEnd = []
|
|
||||||
let pushToParent = true
|
|
||||||
const len = Math.max(localFrom.length, localTo.length)
|
|
||||||
for (let i = 0; i < len; i++) {
|
|
||||||
const a = localFrom[i], b = localTo[i]
|
|
||||||
pushToParent &&= a?.index === b?.index && !a?.offset && !b?.offset
|
|
||||||
if (pushToParent) localParent.push(a)
|
|
||||||
else {
|
|
||||||
if (a) localStart.push(a)
|
|
||||||
if (b) localEnd.push(b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// copy non-local paths from `from`
|
|
||||||
const parent = from.slice(0, -1).concat([localParent])
|
|
||||||
return toString({ parent, start: [localStart], end: [localEnd] })
|
|
||||||
}
|
|
||||||
|
|
||||||
export const compare = (a, b) => {
|
|
||||||
if (typeof a === 'string') a = parse(a)
|
|
||||||
if (typeof b === 'string') b = parse(b)
|
|
||||||
if (a.start || b.start) return compare(collapse(a), collapse(b))
|
|
||||||
|| compare(collapse(a, true), collapse(b, true))
|
|
||||||
|
|
||||||
for (let i = 0; i < Math.max(a.length, b.length); i++) {
|
|
||||||
const p = a[i], q = b[i]
|
|
||||||
const maxIndex = Math.max(p.length, q.length) - 1
|
|
||||||
for (let i = 0; i <= maxIndex; i++) {
|
|
||||||
const x = p[i], y = q[i]
|
|
||||||
if (!x) return -1
|
|
||||||
if (!y) return 1
|
|
||||||
if (x.index > y.index) return 1
|
|
||||||
if (x.index < y.index) return -1
|
|
||||||
if (i === maxIndex) {
|
|
||||||
// TODO: compare temporal & spatial offsets
|
|
||||||
if (x.offset > y.offset) return 1
|
|
||||||
if (x.offset < y.offset) return -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
const isTextNode = ({ nodeType }) => nodeType === 3 || nodeType === 4
|
|
||||||
const isElementNode = ({ nodeType }) => nodeType === 1
|
|
||||||
|
|
||||||
const getChildNodes = (node, filter) => {
|
|
||||||
const nodes = Array.from(node.childNodes)
|
|
||||||
// "content other than element and character data is ignored"
|
|
||||||
.filter(node => isTextNode(node) || isElementNode(node))
|
|
||||||
return filter ? nodes.map(node => {
|
|
||||||
const accept = filter(node)
|
|
||||||
if (accept === NodeFilter.FILTER_REJECT) return null
|
|
||||||
else if (accept === NodeFilter.FILTER_SKIP) return getChildNodes(node, filter)
|
|
||||||
else return node
|
|
||||||
}).flat().filter(x => x) : nodes
|
|
||||||
}
|
|
||||||
|
|
||||||
// child nodes are organized such that the result is always
|
|
||||||
// [element, text, element, text, ..., element],
|
|
||||||
// regardless of the actual structure in the document;
|
|
||||||
// so multiple text nodes need to be combined, and nonexistent ones counted;
|
|
||||||
// see "Step Reference to Child Element or Character Data (/)" in EPUB CFI spec
|
|
||||||
const indexChildNodes = (node, filter) => {
|
|
||||||
const nodes = getChildNodes(node, filter)
|
|
||||||
.reduce((arr, node) => {
|
|
||||||
let last = arr[arr.length - 1]
|
|
||||||
if (!last) arr.push(node)
|
|
||||||
// "there is one chunk between each pair of child elements"
|
|
||||||
else if (isTextNode(node)) {
|
|
||||||
if (Array.isArray(last)) last.push(node)
|
|
||||||
else if (isTextNode(last)) arr[arr.length - 1] = [last, node]
|
|
||||||
else arr.push(node)
|
|
||||||
} else {
|
|
||||||
if (isElementNode(last)) arr.push(null, node)
|
|
||||||
else arr.push(node)
|
|
||||||
}
|
|
||||||
return arr
|
|
||||||
}, [])
|
|
||||||
// "the first chunk is located before the first child element"
|
|
||||||
if (isElementNode(nodes[0])) nodes.unshift('first')
|
|
||||||
// "the last chunk is located after the last child element"
|
|
||||||
if (isElementNode(nodes[nodes.length - 1])) nodes.push('last')
|
|
||||||
// "'virtual' elements"
|
|
||||||
nodes.unshift('before') // "0 is a valid index"
|
|
||||||
nodes.push('after') // "n+2 is a valid index"
|
|
||||||
return nodes
|
|
||||||
}
|
|
||||||
|
|
||||||
const partsToNode = (node, parts, filter) => {
|
|
||||||
const { id } = parts[parts.length - 1]
|
|
||||||
if (id) {
|
|
||||||
const el = node.ownerDocument.getElementById(id)
|
|
||||||
if (el) return { node: el, offset: 0 }
|
|
||||||
}
|
|
||||||
for (const { index } of parts) {
|
|
||||||
const newNode = node ? indexChildNodes(node, filter)[index] : null
|
|
||||||
// handle non-existent nodes
|
|
||||||
if (newNode === 'first') return { node: node.firstChild ?? node }
|
|
||||||
if (newNode === 'last') return { node: node.lastChild ?? node }
|
|
||||||
if (newNode === 'before') return { node, before: true }
|
|
||||||
if (newNode === 'after') return { node, after: true }
|
|
||||||
node = newNode
|
|
||||||
}
|
|
||||||
const { offset } = parts[parts.length - 1]
|
|
||||||
if (!Array.isArray(node)) return { node, offset }
|
|
||||||
// get underlying text node and offset from the chunk
|
|
||||||
let sum = 0
|
|
||||||
for (const n of node) {
|
|
||||||
const { length } = n.nodeValue
|
|
||||||
if (sum + length >= offset) return { node: n, offset: offset - sum }
|
|
||||||
sum += length
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const nodeToParts = (node, offset, filter) => {
|
|
||||||
const { parentNode, id } = node
|
|
||||||
const indexed = indexChildNodes(parentNode, filter)
|
|
||||||
const index = indexed.findIndex(x =>
|
|
||||||
Array.isArray(x) ? x.some(x => x === node) : x === node)
|
|
||||||
// adjust offset as if merging the text nodes in the chunk
|
|
||||||
const chunk = indexed[index]
|
|
||||||
if (Array.isArray(chunk)) {
|
|
||||||
let sum = 0
|
|
||||||
for (const x of chunk) {
|
|
||||||
if (x === node) {
|
|
||||||
sum += offset
|
|
||||||
break
|
|
||||||
} else sum += x.nodeValue.length
|
|
||||||
}
|
|
||||||
offset = sum
|
|
||||||
}
|
|
||||||
const part = { id, index, offset }
|
|
||||||
return (parentNode !== node.ownerDocument.documentElement
|
|
||||||
? nodeToParts(parentNode, null, filter).concat(part) : [part])
|
|
||||||
// remove ignored nodes
|
|
||||||
.filter(x => x.index !== -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const fromRange = (range, filter) => {
|
|
||||||
const { startContainer, startOffset, endContainer, endOffset } = range
|
|
||||||
const start = nodeToParts(startContainer, startOffset, filter)
|
|
||||||
if (range.collapsed) return toString([start])
|
|
||||||
const end = nodeToParts(endContainer, endOffset, filter)
|
|
||||||
return buildRange([start], [end])
|
|
||||||
}
|
|
||||||
|
|
||||||
export const toRange = (doc, parts, filter) => {
|
|
||||||
const startParts = collapse(parts)
|
|
||||||
const endParts = collapse(parts, true)
|
|
||||||
|
|
||||||
const root = doc.documentElement
|
|
||||||
const start = partsToNode(root, startParts[0], filter)
|
|
||||||
const end = partsToNode(root, endParts[0], filter)
|
|
||||||
|
|
||||||
const range = doc.createRange()
|
|
||||||
|
|
||||||
if (start.before) range.setStartBefore(start.node)
|
|
||||||
else if (start.after) range.setStartAfter(start.node)
|
|
||||||
else range.setStart(start.node, start.offset)
|
|
||||||
|
|
||||||
if (end.before) range.setEndBefore(end.node)
|
|
||||||
else if (end.after) range.setEndAfter(end.node)
|
|
||||||
else range.setEnd(end.node, end.offset)
|
|
||||||
return range
|
|
||||||
}
|
|
||||||
|
|
||||||
// faster way of getting CFIs for sorted elements in a single parent
|
|
||||||
export const fromElements = elements => {
|
|
||||||
const results = []
|
|
||||||
const { parentNode } = elements[0]
|
|
||||||
const parts = nodeToParts(parentNode)
|
|
||||||
for (const [index, node] of indexChildNodes(parentNode).entries()) {
|
|
||||||
const el = elements[results.length]
|
|
||||||
if (node === el)
|
|
||||||
results.push(toString([parts.concat({ id: el.id, index })]))
|
|
||||||
}
|
|
||||||
return results
|
|
||||||
}
|
|
||||||
|
|
||||||
export const toElement = (doc, parts) =>
|
|
||||||
partsToNode(doc.documentElement, collapse(parts)).node
|
|
||||||
|
|
||||||
// turn indices into standard CFIs when you don't have an actual package document
|
|
||||||
export const fake = {
|
|
||||||
fromIndex: index => wrap(`/6/${(index + 1) * 2}`),
|
|
||||||
toIndex: parts => parts?.at(-1).index / 2 - 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
// get CFI from Calibre bookmarks
|
|
||||||
// see https://github.com/johnfactotum/foliate/issues/849
|
|
||||||
export const fromCalibrePos = pos => {
|
|
||||||
const [parts] = parse(pos)
|
|
||||||
const item = parts.shift()
|
|
||||||
parts.shift()
|
|
||||||
return toString([[{ index: 6 }, item], parts])
|
|
||||||
}
|
|
||||||
export const fromCalibreHighlight = ({ spine_index, start_cfi, end_cfi }) => {
|
|
||||||
const pre = fake.fromIndex(spine_index) + '!'
|
|
||||||
return buildRange(pre + start_cfi.slice(2), pre + end_cfi.slice(2))
|
|
||||||
}
|
|
||||||
@@ -1,347 +0,0 @@
|
|||||||
const normalizeWhitespace = str => str ? str
|
|
||||||
.replace(/[\t\n\f\r ]+/g, ' ')
|
|
||||||
.replace(/^[\t\n\f\r ]+/, '')
|
|
||||||
.replace(/[\t\n\f\r ]+$/, '') : ''
|
|
||||||
const getElementText = el => normalizeWhitespace(el?.textContent)
|
|
||||||
|
|
||||||
const NS = {
|
|
||||||
XLINK: 'http://www.w3.org/1999/xlink',
|
|
||||||
EPUB: 'http://www.idpf.org/2007/ops',
|
|
||||||
}
|
|
||||||
|
|
||||||
const MIME = {
|
|
||||||
XML: 'application/xml',
|
|
||||||
XHTML: 'application/xhtml+xml',
|
|
||||||
}
|
|
||||||
|
|
||||||
const STYLE = {
|
|
||||||
'strong': ['strong', 'self'],
|
|
||||||
'emphasis': ['em', 'self'],
|
|
||||||
'style': ['span', 'self'],
|
|
||||||
'a': 'anchor',
|
|
||||||
'strikethrough': ['s', 'self'],
|
|
||||||
'sub': ['sub', 'self'],
|
|
||||||
'sup': ['sup', 'self'],
|
|
||||||
'code': ['code', 'self'],
|
|
||||||
'image': 'image',
|
|
||||||
}
|
|
||||||
|
|
||||||
const TABLE = {
|
|
||||||
'tr': ['tr', ['align']],
|
|
||||||
'th': ['th', ['colspan', 'rowspan', 'align', 'valign']],
|
|
||||||
'td': ['td', ['colspan', 'rowspan', 'align', 'valign']],
|
|
||||||
}
|
|
||||||
|
|
||||||
const POEM = {
|
|
||||||
'epigraph': ['blockquote'],
|
|
||||||
'subtitle': ['h2', STYLE],
|
|
||||||
'text-author': ['p', STYLE],
|
|
||||||
'date': ['p', STYLE],
|
|
||||||
'stanza': 'stanza',
|
|
||||||
}
|
|
||||||
|
|
||||||
const SECTION = {
|
|
||||||
'title': ['header', {
|
|
||||||
'p': ['h1', STYLE],
|
|
||||||
'empty-line': ['br'],
|
|
||||||
}],
|
|
||||||
'epigraph': ['blockquote', 'self'],
|
|
||||||
'image': 'image',
|
|
||||||
'annotation': ['aside'],
|
|
||||||
'section': ['section', 'self'],
|
|
||||||
'p': ['p', STYLE],
|
|
||||||
'poem': ['blockquote', POEM],
|
|
||||||
'subtitle': ['h2', STYLE],
|
|
||||||
'cite': ['blockquote', 'self'],
|
|
||||||
'empty-line': ['br'],
|
|
||||||
'table': ['table', TABLE],
|
|
||||||
'text-author': ['p', STYLE],
|
|
||||||
}
|
|
||||||
POEM['epigraph'].push(SECTION)
|
|
||||||
|
|
||||||
const BODY = {
|
|
||||||
'image': 'image',
|
|
||||||
'title': ['section', {
|
|
||||||
'p': ['h1', STYLE],
|
|
||||||
'empty-line': ['br'],
|
|
||||||
}],
|
|
||||||
'epigraph': ['section', SECTION],
|
|
||||||
'section': ['section', SECTION],
|
|
||||||
}
|
|
||||||
|
|
||||||
const getImageSrc = el => {
|
|
||||||
const href = el.getAttributeNS(NS.XLINK, 'href')
|
|
||||||
const [, id] = href.split('#')
|
|
||||||
const bin = el.getRootNode().getElementById(id)
|
|
||||||
return bin
|
|
||||||
? `data:${bin.getAttribute('content-type')};base64,${bin.textContent}`
|
|
||||||
: href
|
|
||||||
}
|
|
||||||
|
|
||||||
class FB2Converter {
|
|
||||||
constructor(fb2) {
|
|
||||||
this.fb2 = fb2
|
|
||||||
this.doc = document.implementation.createDocument(NS.XHTML, 'html')
|
|
||||||
}
|
|
||||||
image(node) {
|
|
||||||
const el = this.doc.createElement('img')
|
|
||||||
el.alt = node.getAttribute('alt')
|
|
||||||
el.title = node.getAttribute('title')
|
|
||||||
el.setAttribute('src', getImageSrc(node))
|
|
||||||
return el
|
|
||||||
}
|
|
||||||
anchor(node) {
|
|
||||||
const el = this.convert(node, { 'a': ['a', STYLE] })
|
|
||||||
el.setAttribute('href', node.getAttributeNS(NS.XLINK, 'href'))
|
|
||||||
if (node.getAttribute('type') === 'note')
|
|
||||||
el.setAttributeNS(NS.EPUB, 'epub:type', 'noteref')
|
|
||||||
return el
|
|
||||||
}
|
|
||||||
stanza(node) {
|
|
||||||
const el = this.convert(node, {
|
|
||||||
'stanza': ['p', {
|
|
||||||
'title': ['header', {
|
|
||||||
'p': ['strong', STYLE],
|
|
||||||
'empty-line': ['br'],
|
|
||||||
}],
|
|
||||||
'subtitle': ['p', STYLE],
|
|
||||||
}],
|
|
||||||
})
|
|
||||||
for (const child of node.children) if (child.nodeName === 'v') {
|
|
||||||
el.append(this.doc.createTextNode(child.textContent))
|
|
||||||
el.append(this.doc.createElement('br'))
|
|
||||||
}
|
|
||||||
return el
|
|
||||||
}
|
|
||||||
convert(node, def) {
|
|
||||||
// not an element; return text content
|
|
||||||
if (node.nodeType === 3) return this.doc.createTextNode(node.textContent)
|
|
||||||
if (node.nodeType === 4) return this.doc.createCDATASection(node.textContent)
|
|
||||||
if (node.nodeType === 8) return this.doc.createComment(node.textContent)
|
|
||||||
|
|
||||||
const d = def?.[node.nodeName]
|
|
||||||
if (!d) return null
|
|
||||||
if (typeof d === 'string') return this[d](node)
|
|
||||||
|
|
||||||
const [name, opts] = d
|
|
||||||
const el = this.doc.createElement(name)
|
|
||||||
|
|
||||||
// copy the ID, and set class name from original element name
|
|
||||||
if (node.id) el.id = node.id
|
|
||||||
el.classList.add(node.nodeName)
|
|
||||||
|
|
||||||
// copy attributes
|
|
||||||
if (Array.isArray(opts)) for (const attr of opts)
|
|
||||||
el.setAttribute(attr, node.getAttribute(attr))
|
|
||||||
|
|
||||||
// process child elements recursively
|
|
||||||
const childDef = opts === 'self' ? def : Array.isArray(opts) ? null : opts
|
|
||||||
let child = node.firstChild
|
|
||||||
while (child) {
|
|
||||||
const childEl = this.convert(child, childDef)
|
|
||||||
if (childEl) el.append(childEl)
|
|
||||||
child = child.nextSibling
|
|
||||||
}
|
|
||||||
return el
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const parseXML = async blob => {
|
|
||||||
const buffer = await blob.arrayBuffer()
|
|
||||||
const str = new TextDecoder('utf-8').decode(buffer)
|
|
||||||
const parser = new DOMParser()
|
|
||||||
const doc = parser.parseFromString(str, MIME.XML)
|
|
||||||
const encoding = doc.xmlEncoding
|
|
||||||
// `Document.xmlEncoding` is deprecated, and already removed in Firefox
|
|
||||||
// so parse the XML declaration manually
|
|
||||||
|| str.match(/^<\?xml\s+version\s*=\s*["']1.\d+"\s+encoding\s*=\s*["']([A-Za-z0-9._-]*)["']/)?.[1]
|
|
||||||
if (encoding && encoding.toLowerCase() !== 'utf-8') {
|
|
||||||
const str = new TextDecoder(encoding).decode(buffer)
|
|
||||||
return parser.parseFromString(str, MIME.XML)
|
|
||||||
}
|
|
||||||
return doc
|
|
||||||
}
|
|
||||||
|
|
||||||
const style = URL.createObjectURL(new Blob([`
|
|
||||||
@namespace epub "http://www.idpf.org/2007/ops";
|
|
||||||
body > img, section > img {
|
|
||||||
display: block;
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
.title h1 {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
body > section > .title, body.notesBodyType > .title {
|
|
||||||
margin: 3em 0;
|
|
||||||
}
|
|
||||||
body.notesBodyType > section .title h1 {
|
|
||||||
text-align: start;
|
|
||||||
}
|
|
||||||
body.notesBodyType > section .title {
|
|
||||||
margin: 1em 0;
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
text-indent: 1em;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
:not(p) + p, p:first-child {
|
|
||||||
text-indent: 0;
|
|
||||||
}
|
|
||||||
.poem p {
|
|
||||||
text-indent: 0;
|
|
||||||
margin: 1em 0;
|
|
||||||
}
|
|
||||||
.text-author, .date {
|
|
||||||
text-align: end;
|
|
||||||
}
|
|
||||||
.text-author:before {
|
|
||||||
content: "—";
|
|
||||||
}
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
td, th {
|
|
||||||
padding: .25em;
|
|
||||||
}
|
|
||||||
a[epub|type~="noteref"] {
|
|
||||||
font-size: .75em;
|
|
||||||
vertical-align: super;
|
|
||||||
}
|
|
||||||
body:not(.notesBodyType) > .title, body:not(.notesBodyType) > .epigraph {
|
|
||||||
margin: 3em 0;
|
|
||||||
}
|
|
||||||
`], { type: 'text/css' }))
|
|
||||||
|
|
||||||
const template = html => `<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
|
||||||
<head><link href="${style}" rel="stylesheet" type="text/css"/></head>
|
|
||||||
<body>${html}</body>
|
|
||||||
</html>`
|
|
||||||
|
|
||||||
// name of custom ID attribute for TOC items
|
|
||||||
const dataID = 'data-foliate-id'
|
|
||||||
|
|
||||||
export const makeFB2 = async blob => {
|
|
||||||
const book = {}
|
|
||||||
const doc = await parseXML(blob)
|
|
||||||
const converter = new FB2Converter(doc)
|
|
||||||
|
|
||||||
const $ = x => doc.querySelector(x)
|
|
||||||
const $$ = x => [...doc.querySelectorAll(x)]
|
|
||||||
const getPerson = el => {
|
|
||||||
const nick = getElementText(el.querySelector('nickname'))
|
|
||||||
if (nick) return nick
|
|
||||||
const first = getElementText(el.querySelector('first-name'))
|
|
||||||
const middle = getElementText(el.querySelector('middle-name'))
|
|
||||||
const last = getElementText(el.querySelector('last-name'))
|
|
||||||
const name = [first, middle, last].filter(x => x).join(' ')
|
|
||||||
const sortAs = last
|
|
||||||
? [last, [first, middle].filter(x => x).join(' ')].join(', ')
|
|
||||||
: null
|
|
||||||
return { name, sortAs }
|
|
||||||
}
|
|
||||||
const getDate = el => el?.getAttribute('value') ?? getElementText(el)
|
|
||||||
const annotation = $('title-info annotation')
|
|
||||||
book.metadata = {
|
|
||||||
title: getElementText($('title-info book-title')),
|
|
||||||
identifier: getElementText($('document-info id')),
|
|
||||||
language: getElementText($('title-info lang')),
|
|
||||||
author: $$('title-info author').map(getPerson),
|
|
||||||
translator: $$('title-info translator').map(getPerson),
|
|
||||||
producer: $$('document-info author').map(getPerson)
|
|
||||||
.concat($$('document-info program-used').map(getElementText)),
|
|
||||||
publisher: getElementText($('publish-info publisher')),
|
|
||||||
published: getDate($('title-info date')),
|
|
||||||
modified: getDate($('document-info date')),
|
|
||||||
description: annotation ? converter.convert(annotation,
|
|
||||||
{ annotation: ['div', SECTION] }).innerHTML : null,
|
|
||||||
subject: $$('title-info genre').map(getElementText),
|
|
||||||
}
|
|
||||||
if ($('coverpage image')) {
|
|
||||||
const src = getImageSrc($('coverpage image'))
|
|
||||||
book.getCover = () => fetch(src).then(res => res.blob())
|
|
||||||
} else book.getCover = () => null
|
|
||||||
|
|
||||||
// get convert each body
|
|
||||||
const bodyData = Array.from(doc.querySelectorAll('body'), body => {
|
|
||||||
const converted = converter.convert(body, { body: ['body', BODY] })
|
|
||||||
return [Array.from(converted.children, el => {
|
|
||||||
// get list of IDs in the section
|
|
||||||
const ids = [el, ...el.querySelectorAll('[id]')].map(el => el.id)
|
|
||||||
return { el, ids }
|
|
||||||
}), converted]
|
|
||||||
})
|
|
||||||
|
|
||||||
const urls = []
|
|
||||||
const sectionData = bodyData[0][0]
|
|
||||||
// make a separate section for each section in the first body
|
|
||||||
.map(({ el, ids }) => {
|
|
||||||
// set up titles for TOC
|
|
||||||
const titles = Array.from(
|
|
||||||
el.querySelectorAll(':scope > section > .title'),
|
|
||||||
(el, index) => {
|
|
||||||
el.setAttribute(dataID, index)
|
|
||||||
return { title: getElementText(el), index }
|
|
||||||
})
|
|
||||||
return { ids, titles, el }
|
|
||||||
})
|
|
||||||
// for additional bodies, only make one section for each body
|
|
||||||
.concat(bodyData.slice(1).map(([sections, body]) => {
|
|
||||||
const ids = sections.map(s => s.ids).flat()
|
|
||||||
body.classList.add('notesBodyType')
|
|
||||||
return { ids, el: body, linear: 'no' }
|
|
||||||
}))
|
|
||||||
.map(({ ids, titles, el, linear }) => {
|
|
||||||
const str = template(el.outerHTML)
|
|
||||||
const blob = new Blob([str], { type: MIME.XHTML })
|
|
||||||
const url = URL.createObjectURL(blob)
|
|
||||||
urls.push(url)
|
|
||||||
const title = normalizeWhitespace(
|
|
||||||
el.querySelector('.title, .subtitle, p')?.textContent
|
|
||||||
?? (el.classList.contains('title') ? el.textContent : ''))
|
|
||||||
return {
|
|
||||||
ids, title, titles, load: () => url,
|
|
||||||
createDocument: () => new DOMParser().parseFromString(str, MIME.XHTML),
|
|
||||||
// doo't count image data as it'd skew the size too much
|
|
||||||
size: blob.size - Array.from(el.querySelectorAll('[src]'),
|
|
||||||
el => el.getAttribute('src')?.length ?? 0)
|
|
||||||
.reduce((a, b) => a + b, 0),
|
|
||||||
linear,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const idMap = new Map()
|
|
||||||
book.sections = sectionData.map((section, index) => {
|
|
||||||
const { ids, load, createDocument, size, linear } = section
|
|
||||||
for (const id of ids) if (id) idMap.set(id, index)
|
|
||||||
return { id: index, load, createDocument, size, linear }
|
|
||||||
})
|
|
||||||
|
|
||||||
book.toc = sectionData.map(({ title, titles }, index) => {
|
|
||||||
const id = index.toString()
|
|
||||||
return {
|
|
||||||
label: title,
|
|
||||||
href: id,
|
|
||||||
subitems: titles?.length ? titles.map(({ title, index }) => ({
|
|
||||||
label: title,
|
|
||||||
href: `${id}#${index}`,
|
|
||||||
})) : null,
|
|
||||||
}
|
|
||||||
}).filter(item => item)
|
|
||||||
|
|
||||||
book.resolveHref = href => {
|
|
||||||
const [a, b] = href.split('#')
|
|
||||||
return a
|
|
||||||
// the link is from the TOC
|
|
||||||
? { index: Number(a), anchor: doc => doc.querySelector(`[${dataID}="${b}"]`) }
|
|
||||||
// link from within the page
|
|
||||||
: { index: idMap.get(b), anchor: doc => doc.getElementById(b) }
|
|
||||||
}
|
|
||||||
book.splitTOCHref = href => href?.split('#')?.map(x => Number(x)) ?? []
|
|
||||||
book.getTOCFragment = (doc, id) => doc.querySelector(`[${dataID}="${id}"]`)
|
|
||||||
|
|
||||||
book.destroy = () => {
|
|
||||||
for (const url of urls) URL.revokeObjectURL(url)
|
|
||||||
}
|
|
||||||
return book
|
|
||||||
}
|
|
||||||
@@ -1,297 +0,0 @@
|
|||||||
const parseViewport = str => str
|
|
||||||
?.split(/[,;\s]/) // NOTE: technically, only the comma is valid
|
|
||||||
?.filter(x => x)
|
|
||||||
?.map(x => x.split('=').map(x => x.trim()))
|
|
||||||
|
|
||||||
const getViewport = (doc, viewport) => {
|
|
||||||
// use `viewBox` for SVG
|
|
||||||
if (doc.documentElement.localName === 'svg') {
|
|
||||||
const [, , width, height] = doc.documentElement
|
|
||||||
.getAttribute('viewBox')?.split(/\s/) ?? []
|
|
||||||
return { width, height }
|
|
||||||
}
|
|
||||||
|
|
||||||
// get `viewport` `meta` element
|
|
||||||
const meta = parseViewport(doc.querySelector('meta[name="viewport"]')
|
|
||||||
?.getAttribute('content'))
|
|
||||||
if (meta) return Object.fromEntries(meta)
|
|
||||||
|
|
||||||
// fallback to book's viewport
|
|
||||||
if (typeof viewport === 'string') return parseViewport(viewport)
|
|
||||||
if (viewport) return viewport
|
|
||||||
|
|
||||||
// if no viewport (possibly with image directly in spine), get image size
|
|
||||||
const img = doc.querySelector('img')
|
|
||||||
if (img) return { width: img.naturalWidth, height: img.naturalHeight }
|
|
||||||
|
|
||||||
// just show *something*, i guess...
|
|
||||||
console.warn(new Error('Missing viewport properties'))
|
|
||||||
return { width: 1000, height: 2000 }
|
|
||||||
}
|
|
||||||
|
|
||||||
export class FixedLayout extends HTMLElement {
|
|
||||||
#root = this.attachShadow({ mode: 'closed' })
|
|
||||||
#observer = new ResizeObserver(() => this.#render())
|
|
||||||
#spreads
|
|
||||||
#index = -1
|
|
||||||
defaultViewport
|
|
||||||
spread
|
|
||||||
#portrait = false
|
|
||||||
#left
|
|
||||||
#right
|
|
||||||
#center
|
|
||||||
#side
|
|
||||||
constructor() {
|
|
||||||
super()
|
|
||||||
|
|
||||||
const sheet = new CSSStyleSheet()
|
|
||||||
this.#root.adoptedStyleSheets = [sheet]
|
|
||||||
sheet.replaceSync(`:host {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}`)
|
|
||||||
|
|
||||||
this.#observer.observe(this)
|
|
||||||
}
|
|
||||||
async #createFrame(position, { index, src }) {
|
|
||||||
const element = document.createElement('div')
|
|
||||||
const iframe = document.createElement('iframe')
|
|
||||||
element.append(iframe)
|
|
||||||
Object.assign(iframe.style, {
|
|
||||||
border: '0',
|
|
||||||
display: 'none',
|
|
||||||
overflow: 'hidden',
|
|
||||||
})
|
|
||||||
// `allow-scripts` is needed for events because of WebKit bug
|
|
||||||
// https://bugs.webkit.org/show_bug.cgi?id=218086
|
|
||||||
iframe.setAttribute('sandbox', 'allow-same-origin allow-scripts')
|
|
||||||
iframe.setAttribute('scrolling', 'no')
|
|
||||||
iframe.setAttribute('part', 'filter')
|
|
||||||
this.#root.append(element)
|
|
||||||
if (!src) return { blank: true, element, iframe }
|
|
||||||
return new Promise(resolve => {
|
|
||||||
const onload = () => {
|
|
||||||
iframe.removeEventListener('load', onload)
|
|
||||||
const doc = iframe.contentDocument
|
|
||||||
doc.position = position
|
|
||||||
this.dispatchEvent(new CustomEvent('load', { detail: { doc, index } }))
|
|
||||||
const { width, height } = getViewport(doc, this.defaultViewport)
|
|
||||||
resolve({
|
|
||||||
element, iframe,
|
|
||||||
width: parseFloat(width),
|
|
||||||
height: parseFloat(height),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
iframe.addEventListener('load', onload)
|
|
||||||
iframe.src = src
|
|
||||||
})
|
|
||||||
}
|
|
||||||
#render(side = this.#side) {
|
|
||||||
if (!side) return
|
|
||||||
const left = this.#left ?? {}
|
|
||||||
const right = this.#center ?? this.#right
|
|
||||||
const target = side === 'left' ? left : right
|
|
||||||
const { width, height } = this.getBoundingClientRect()
|
|
||||||
const portrait = this.spread !== 'both' && this.spread !== 'portrait'
|
|
||||||
&& height > width
|
|
||||||
this.#portrait = portrait
|
|
||||||
const blankWidth = left.width ?? right.width
|
|
||||||
const blankHeight = left.height ?? right.height
|
|
||||||
|
|
||||||
const scale = portrait || this.#center
|
|
||||||
? Math.min(
|
|
||||||
width / (target.width ?? blankWidth),
|
|
||||||
height / (target.height ?? blankHeight))
|
|
||||||
: Math.min(
|
|
||||||
width / ((left.width ?? blankWidth) + (right.width ?? blankWidth)),
|
|
||||||
height / Math.max(
|
|
||||||
left.height ?? blankHeight,
|
|
||||||
right.height ?? blankHeight))
|
|
||||||
|
|
||||||
const transform = frame => {
|
|
||||||
const { element, iframe, width, height, blank } = frame
|
|
||||||
iframe.contentDocument.scale = scale
|
|
||||||
Object.assign(iframe.style, {
|
|
||||||
width: `${width}px`,
|
|
||||||
height: `${height}px`,
|
|
||||||
transform: `scale(${scale})`,
|
|
||||||
transformOrigin: 'top left',
|
|
||||||
display: blank ? 'none' : 'block',
|
|
||||||
})
|
|
||||||
Object.assign(element.style, {
|
|
||||||
width: `${(width ?? blankWidth) * scale}px`,
|
|
||||||
height: `${(height ?? blankHeight) * scale}px`,
|
|
||||||
overflow: 'hidden',
|
|
||||||
display: 'block',
|
|
||||||
})
|
|
||||||
if (portrait && frame !== target) {
|
|
||||||
element.style.display = 'none'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.#center) {
|
|
||||||
transform(this.#center)
|
|
||||||
} else {
|
|
||||||
transform(left)
|
|
||||||
transform(right)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async #showSpread({ left, right, center, side }) {
|
|
||||||
this.#root.replaceChildren()
|
|
||||||
this.#left = null
|
|
||||||
this.#right = null
|
|
||||||
this.#center = null
|
|
||||||
if (center) {
|
|
||||||
this.#center = await this.#createFrame('center', center)
|
|
||||||
this.#side = 'center'
|
|
||||||
this.#render()
|
|
||||||
} else {
|
|
||||||
this.#left = await this.#createFrame('left', left)
|
|
||||||
this.#right = await this.#createFrame('right', right)
|
|
||||||
this.#side = this.#left.blank ? 'right'
|
|
||||||
: this.#right.blank ? 'left' : side
|
|
||||||
this.#render()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#goLeft() {
|
|
||||||
if (this.#center || this.#left?.blank) return
|
|
||||||
if (this.#portrait && this.#left?.element?.style?.display === 'none') {
|
|
||||||
this.#right.element.style.display = 'none'
|
|
||||||
this.#left.element.style.display = 'block'
|
|
||||||
this.#side = 'left'
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#goRight() {
|
|
||||||
if (this.#center || this.#right?.blank) return
|
|
||||||
if (this.#portrait && this.#right?.element?.style?.display === 'none') {
|
|
||||||
this.#left.element.style.display = 'none'
|
|
||||||
this.#right.element.style.display = 'block'
|
|
||||||
this.#side = 'right'
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
open(book) {
|
|
||||||
this.book = book
|
|
||||||
const { rendition } = book
|
|
||||||
this.spread = rendition?.spread
|
|
||||||
this.defaultViewport = rendition?.viewport
|
|
||||||
|
|
||||||
const rtl = book.dir === 'rtl'
|
|
||||||
const ltr = !rtl
|
|
||||||
this.rtl = rtl
|
|
||||||
|
|
||||||
if (rendition?.spread === 'none')
|
|
||||||
this.#spreads = book.sections.map(section => ({ center: section }))
|
|
||||||
else this.#spreads = book.sections.reduce((arr, section) => {
|
|
||||||
const last = arr[arr.length - 1]
|
|
||||||
const { linear, pageSpread } = section
|
|
||||||
if (linear === 'no') return arr
|
|
||||||
const newSpread = () => {
|
|
||||||
const spread = {}
|
|
||||||
arr.push(spread)
|
|
||||||
return spread
|
|
||||||
}
|
|
||||||
if (pageSpread === 'center') {
|
|
||||||
const spread = last.left || last.right ? newSpread() : last
|
|
||||||
spread.center = section
|
|
||||||
}
|
|
||||||
else if (pageSpread === 'left') {
|
|
||||||
const spread = last.center || last.left || ltr ? newSpread() : last
|
|
||||||
spread.left = section
|
|
||||||
}
|
|
||||||
else if (pageSpread === 'right') {
|
|
||||||
const spread = last.center || last.right || rtl ? newSpread() : last
|
|
||||||
spread.right = section
|
|
||||||
}
|
|
||||||
else if (ltr) {
|
|
||||||
if (last.center || last.right) newSpread().left = section
|
|
||||||
else if (last.left) last.right = section
|
|
||||||
else last.left = section
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (last.center || last.left) newSpread().right = section
|
|
||||||
else if (last.right) last.left = section
|
|
||||||
else last .right = section
|
|
||||||
}
|
|
||||||
return arr
|
|
||||||
}, [{}])
|
|
||||||
}
|
|
||||||
get index() {
|
|
||||||
const spread = this.#spreads[this.#index]
|
|
||||||
const section = spread?.center ?? (this.side === 'left'
|
|
||||||
? spread.left ?? spread.right : spread.right ?? spread.left)
|
|
||||||
return this.book.sections.indexOf(section)
|
|
||||||
}
|
|
||||||
#reportLocation(reason) {
|
|
||||||
this.dispatchEvent(new CustomEvent('relocate', { detail:
|
|
||||||
{ reason, range: null, index: this.index, fraction: 0, size: 1 } }))
|
|
||||||
}
|
|
||||||
getSpreadOf(section) {
|
|
||||||
const spreads = this.#spreads
|
|
||||||
for (let index = 0; index < spreads.length; index++) {
|
|
||||||
const { left, right, center } = spreads[index]
|
|
||||||
if (left === section) return { index, side: 'left' }
|
|
||||||
if (right === section) return { index, side: 'right' }
|
|
||||||
if (center === section) return { index, side: 'center' }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async goToSpread(index, side, reason) {
|
|
||||||
if (index < 0 || index > this.#spreads.length - 1) return
|
|
||||||
if (index === this.#index) {
|
|
||||||
this.#render(side)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.#index = index
|
|
||||||
const spread = this.#spreads[index]
|
|
||||||
if (spread.center) {
|
|
||||||
const index = this.book.sections.indexOf(spread.center)
|
|
||||||
const src = await spread.center?.load?.()
|
|
||||||
await this.#showSpread({ center: { index, src } })
|
|
||||||
} else {
|
|
||||||
const indexL = this.book.sections.indexOf(spread.left)
|
|
||||||
const indexR = this.book.sections.indexOf(spread.right)
|
|
||||||
const srcL = await spread.left?.load?.()
|
|
||||||
const srcR = await spread.right?.load?.()
|
|
||||||
const left = { index: indexL, src: srcL }
|
|
||||||
const right = { index: indexR, src: srcR }
|
|
||||||
await this.#showSpread({ left, right, side })
|
|
||||||
}
|
|
||||||
this.#reportLocation(reason)
|
|
||||||
}
|
|
||||||
async select(target) {
|
|
||||||
await this.goTo(target)
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
async goTo(target) {
|
|
||||||
const { book } = this
|
|
||||||
const resolved = await target
|
|
||||||
const section = book.sections[resolved.index]
|
|
||||||
if (!section) return
|
|
||||||
const { index, side } = this.getSpreadOf(section)
|
|
||||||
await this.goToSpread(index, side)
|
|
||||||
}
|
|
||||||
async next() {
|
|
||||||
const s = this.rtl ? this.#goLeft() : this.#goRight()
|
|
||||||
if (s) this.#reportLocation('page')
|
|
||||||
else return this.goToSpread(this.#index + 1, this.rtl ? 'right' : 'left', 'page')
|
|
||||||
}
|
|
||||||
async prev() {
|
|
||||||
const s = this.rtl ? this.#goRight() : this.#goLeft()
|
|
||||||
if (s) this.#reportLocation('page')
|
|
||||||
else return this.goToSpread(this.#index - 1, this.rtl ? 'left' : 'right', 'page')
|
|
||||||
}
|
|
||||||
getContents() {
|
|
||||||
return Array.from(this.#root.querySelectorAll('iframe'), frame => ({
|
|
||||||
doc: frame.contentDocument,
|
|
||||||
// TODO: index, overlayer
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
destroy() {
|
|
||||||
this.#observer.unobserve(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define('foliate-fxl', FixedLayout)
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
const getTypes = el => new Set(el?.getAttributeNS?.('http://www.idpf.org/2007/ops', 'type')?.split(' '))
|
|
||||||
const getRoles = el => new Set(el?.getAttribute?.('role')?.split(' '))
|
|
||||||
|
|
||||||
const isSuper = el => {
|
|
||||||
const { verticalAlign } = getComputedStyle(el)
|
|
||||||
return verticalAlign === 'super' || /^\d/.test(verticalAlign)
|
|
||||||
}
|
|
||||||
|
|
||||||
const refTypes = ['biblioref', 'glossref', 'noteref']
|
|
||||||
const refRoles = ['doc-biblioref', 'doc-glossref', 'doc-noteref']
|
|
||||||
const isFootnoteReference = a => {
|
|
||||||
const types = getTypes(a)
|
|
||||||
const roles = getRoles(a)
|
|
||||||
return {
|
|
||||||
yes: refRoles.some(r => roles.has(r)) || refTypes.some(t => types.has(t)),
|
|
||||||
maybe: () => !types.has('backlink') && !roles.has('doc-backlink')
|
|
||||||
&& (isSuper(a) || a.children.length === 1 && isSuper(a.children[0])
|
|
||||||
|| isSuper(a.parentElement)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const getReferencedType = el => {
|
|
||||||
const types = getTypes(el)
|
|
||||||
const roles = getRoles(el)
|
|
||||||
return roles.has('doc-biblioentry') || types.has('biblioentry') ? 'biblioentry'
|
|
||||||
: roles.has('definition') || types.has('glossdef') ? 'definition'
|
|
||||||
: roles.has('doc-endnote') || types.has('endnote') || types.has('rearnote') ? 'endnote'
|
|
||||||
: roles.has('doc-footnote') || types.has('footnote') ? 'footnote'
|
|
||||||
: roles.has('note') || types.has('note') ? 'note' : null
|
|
||||||
}
|
|
||||||
|
|
||||||
const isInline = 'a, span, sup, sub, em, strong, i, b, small, big'
|
|
||||||
const extractFootnote = (doc, anchor) => {
|
|
||||||
let el = anchor(doc)
|
|
||||||
const target = el
|
|
||||||
while (el.matches(isInline)) {
|
|
||||||
const parent = el.parentElement
|
|
||||||
if (!parent) break
|
|
||||||
el = parent
|
|
||||||
}
|
|
||||||
if (el === doc.body) {
|
|
||||||
const sibling = target.nextElementSibling
|
|
||||||
if (sibling && !sibling.matches(isInline)) return sibling
|
|
||||||
throw new Error('Failed to extract footnote')
|
|
||||||
}
|
|
||||||
return el
|
|
||||||
}
|
|
||||||
|
|
||||||
export class FootnoteHandler extends EventTarget {
|
|
||||||
detectFootnotes = true
|
|
||||||
#showFragment(book, { index, anchor }, href) {
|
|
||||||
const view = document.createElement('foliate-view')
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
view.addEventListener('load', e => {
|
|
||||||
try {
|
|
||||||
const { doc } = e.detail
|
|
||||||
const el = anchor(doc)
|
|
||||||
const type = getReferencedType(el)
|
|
||||||
const hidden = el?.matches?.('aside') && type === 'footnote'
|
|
||||||
if (el) {
|
|
||||||
const range = el.startContainer ? el : doc.createRange()
|
|
||||||
if (!el.startContainer) {
|
|
||||||
if (el.matches('li, aside')) range.selectNodeContents(el)
|
|
||||||
else range.selectNode(el)
|
|
||||||
}
|
|
||||||
const frag = range.extractContents()
|
|
||||||
doc.body.replaceChildren()
|
|
||||||
doc.body.appendChild(frag)
|
|
||||||
}
|
|
||||||
const detail = { view, href, type, hidden, target: el }
|
|
||||||
this.dispatchEvent(new CustomEvent('render', { detail }))
|
|
||||||
resolve()
|
|
||||||
} catch (e) {
|
|
||||||
reject(e)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
view.open(book)
|
|
||||||
.then(() => this.dispatchEvent(new CustomEvent('before-render', { detail: { view } })))
|
|
||||||
.then(() => view.goTo(index))
|
|
||||||
.catch(reject)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
handle(book, e) {
|
|
||||||
const { a, href } = e.detail
|
|
||||||
const { yes, maybe } = isFootnoteReference(a)
|
|
||||||
if (yes) {
|
|
||||||
e.preventDefault()
|
|
||||||
return Promise.resolve(book.resolveHref(href)).then(target =>
|
|
||||||
this.#showFragment(book, target, href))
|
|
||||||
}
|
|
||||||
else if (this.detectFootnotes && maybe()) {
|
|
||||||
e.preventDefault()
|
|
||||||
return Promise.resolve(book.resolveHref(href)).then(({ index, anchor }) => {
|
|
||||||
const target = { index, anchor: doc => extractFootnote(doc, anchor) }
|
|
||||||
return this.#showFragment(book, target, href)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,282 +0,0 @@
|
|||||||
const NS = {
|
|
||||||
ATOM: 'http://www.w3.org/2005/Atom',
|
|
||||||
OPDS: 'http://opds-spec.org/2010/catalog',
|
|
||||||
THR: 'http://purl.org/syndication/thread/1.0',
|
|
||||||
DC: 'http://purl.org/dc/elements/1.1/',
|
|
||||||
DCTERMS: 'http://purl.org/dc/terms/',
|
|
||||||
}
|
|
||||||
|
|
||||||
const MIME = {
|
|
||||||
ATOM: 'application/atom+xml',
|
|
||||||
OPDS2: 'application/opds+json',
|
|
||||||
}
|
|
||||||
|
|
||||||
export const REL = {
|
|
||||||
ACQ: 'http://opds-spec.org/acquisition',
|
|
||||||
FACET: 'http://opds-spec.org/facet',
|
|
||||||
GROUP: 'http://opds-spec.org/group',
|
|
||||||
COVER: [
|
|
||||||
'http://opds-spec.org/image',
|
|
||||||
'http://opds-spec.org/cover',
|
|
||||||
],
|
|
||||||
THUMBNAIL: [
|
|
||||||
'http://opds-spec.org/image/thumbnail',
|
|
||||||
'http://opds-spec.org/thumbnail',
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SYMBOL = {
|
|
||||||
SUMMARY: Symbol('summary'),
|
|
||||||
CONTENT: Symbol('content'),
|
|
||||||
}
|
|
||||||
|
|
||||||
const FACET_GROUP = Symbol('facetGroup')
|
|
||||||
|
|
||||||
const groupByArray = (arr, f) => {
|
|
||||||
const map = new Map()
|
|
||||||
if (arr) for (const el of arr) {
|
|
||||||
const keys = f(el)
|
|
||||||
for (const key of [keys].flat()) {
|
|
||||||
const group = map.get(key)
|
|
||||||
if (group) group.push(el)
|
|
||||||
else map.set(key, [el])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://www.rfc-editor.org/rfc/rfc7231#section-3.1.1
|
|
||||||
const parseMediaType = str => {
|
|
||||||
if (!str) return null
|
|
||||||
const [mediaType, ...ps] = str.split(/ *; */)
|
|
||||||
return {
|
|
||||||
mediaType: mediaType.toLowerCase(),
|
|
||||||
parameters: Object.fromEntries(ps.map(p => {
|
|
||||||
const [name, val] = p.split('=')
|
|
||||||
return [name.toLowerCase(), val?.replace(/(^"|"$)/g, '')]
|
|
||||||
})),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const isOPDSCatalog = str => {
|
|
||||||
const parsed = parseMediaType(str)
|
|
||||||
if (!parsed) return false
|
|
||||||
const { mediaType, parameters } = parsed
|
|
||||||
if (mediaType === MIME.OPDS2) return true
|
|
||||||
return mediaType === MIME.ATOM && parameters.profile?.toLowerCase() === 'opds-catalog'
|
|
||||||
}
|
|
||||||
|
|
||||||
// ignore the namespace if it doesn't appear in document at all
|
|
||||||
const useNS = (doc, ns) =>
|
|
||||||
doc.lookupNamespaceURI(null) === ns || doc.lookupPrefix(ns) ? ns : null
|
|
||||||
|
|
||||||
const filterNS = ns => ns
|
|
||||||
? name => el => el.namespaceURI === ns && el.localName === name
|
|
||||||
: name => el => el.localName === name
|
|
||||||
|
|
||||||
const getContent = el => {
|
|
||||||
if (!el) return
|
|
||||||
const type = el.getAttribute('type') ?? 'text'
|
|
||||||
const value = type === 'xhtml' ? el.innerHTML
|
|
||||||
: type === 'html' ? el.textContent
|
|
||||||
.replaceAll('<', '<')
|
|
||||||
.replaceAll('>', '>')
|
|
||||||
.replaceAll('&', '&')
|
|
||||||
: el.textContent
|
|
||||||
return { value, type }
|
|
||||||
}
|
|
||||||
|
|
||||||
const getTextContent = el => {
|
|
||||||
const content = getContent(el)
|
|
||||||
if (content?.type === 'text') return content?.value
|
|
||||||
}
|
|
||||||
|
|
||||||
const getSummary = (a, b) => getTextContent(a) ?? getTextContent(b)
|
|
||||||
|
|
||||||
const getPrice = link => {
|
|
||||||
const price = link.getElementsByTagNameNS(NS.OPDS, 'price')[0]
|
|
||||||
return price ? {
|
|
||||||
currency: price.getAttribute('currencycode'),
|
|
||||||
value: price.textContent,
|
|
||||||
} : null
|
|
||||||
}
|
|
||||||
|
|
||||||
const getIndirectAcquisition = el => {
|
|
||||||
const ia = el.getElementsByTagNameNS(NS.OPDS, 'indirectAcquisition')[0]
|
|
||||||
if (!ia) return []
|
|
||||||
return [{ type: ia.getAttribute('type') }, ...getIndirectAcquisition(ia)]
|
|
||||||
}
|
|
||||||
|
|
||||||
const getLink = link => {
|
|
||||||
const obj = {
|
|
||||||
rel: link.getAttribute('rel')?.split(/ +/),
|
|
||||||
href: link.getAttribute('href'),
|
|
||||||
type: link.getAttribute('type'),
|
|
||||||
title: link.getAttribute('title'),
|
|
||||||
properties: {
|
|
||||||
price: getPrice(link),
|
|
||||||
indirectAcquisition: getIndirectAcquisition(link),
|
|
||||||
numberOfItems: link.getAttributeNS(NS.THR, 'count'),
|
|
||||||
},
|
|
||||||
[FACET_GROUP]: link.getAttributeNS(NS.OPDS, 'facetGroup'),
|
|
||||||
}
|
|
||||||
if (link.getAttributeNS(NS.OPDS, 'activeFacet') === 'true')
|
|
||||||
obj.rel = [obj.rel ?? []].flat().concat('self')
|
|
||||||
return obj
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getPublication = entry => {
|
|
||||||
const filter = filterNS(useNS(entry.ownerDocument, NS.ATOM))
|
|
||||||
const children = Array.from(entry.children)
|
|
||||||
const filterDCEL = filterNS(NS.DC)
|
|
||||||
const filterDCTERMS = filterNS(NS.DCTERMS)
|
|
||||||
const filterDC = x => {
|
|
||||||
const a = filterDCEL(x), b = filterDCTERMS(x)
|
|
||||||
return y => a(y) || b(y)
|
|
||||||
}
|
|
||||||
const links = children.filter(filter('link')).map(getLink)
|
|
||||||
const linksByRel = groupByArray(links, link => link.rel)
|
|
||||||
return {
|
|
||||||
metadata: {
|
|
||||||
title: children.find(filter('title'))?.textContent ?? '',
|
|
||||||
author: children.filter(filter('author')).map(person => {
|
|
||||||
const NS = person.namespaceURI
|
|
||||||
const uri = person.getElementsByTagNameNS(NS, 'uri')[0]?.textContent
|
|
||||||
return {
|
|
||||||
name: person.getElementsByTagNameNS(NS, 'name')[0]?.textContent ?? '',
|
|
||||||
links: uri ? [{ href: uri }] : [],
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
publisher: children.find(filterDC('publisher'))?.textContent,
|
|
||||||
published: (children.find(filterDCTERMS('issued'))
|
|
||||||
?? children.find(filterDC('date')))?.textContent,
|
|
||||||
language: children.find(filterDC('language'))?.textContent,
|
|
||||||
identifier: children.find(filterDC('identifier'))?.textContent,
|
|
||||||
subject: children.filter(filter('category')).map(category => ({
|
|
||||||
name: category.getAttribute('label'),
|
|
||||||
code: category.getAttribute('term'),
|
|
||||||
})),
|
|
||||||
[SYMBOL.CONTENT]: getContent(children.find(filter('content'))
|
|
||||||
?? children.find(filter('summary'))),
|
|
||||||
},
|
|
||||||
links,
|
|
||||||
images: REL.COVER.concat(REL.THUMBNAIL)
|
|
||||||
.map(R => linksByRel.get(R)?.[0]).filter(x => x),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getFeed = doc => {
|
|
||||||
const ns = useNS(doc, NS.ATOM)
|
|
||||||
const filter = filterNS(ns)
|
|
||||||
const children = Array.from(doc.documentElement.children)
|
|
||||||
const entries = children.filter(filter('entry'))
|
|
||||||
const links = children.filter(filter('link')).map(getLink)
|
|
||||||
const linksByRel = groupByArray(links, link => link.rel)
|
|
||||||
|
|
||||||
const groupedItems = new Map([[null, []]])
|
|
||||||
const groupLinkMap = new Map()
|
|
||||||
for (const entry of entries) {
|
|
||||||
const children = Array.from(entry.children)
|
|
||||||
const links = children.filter(filter('link')).map(getLink)
|
|
||||||
const linksByRel = groupByArray(links, link => link.rel)
|
|
||||||
const isPub = [...linksByRel.keys()]
|
|
||||||
.some(rel => rel?.startsWith(REL.ACQ) || rel === 'preview')
|
|
||||||
|
|
||||||
const groupLinks = linksByRel.get(REL.GROUP) ?? linksByRel.get('collection')
|
|
||||||
const groupLink = groupLinks?.length
|
|
||||||
? groupLinks.find(link => groupedItems.has(link.href)) ?? groupLinks[0] : null
|
|
||||||
if (groupLink && !groupLinkMap.has(groupLink.href))
|
|
||||||
groupLinkMap.set(groupLink.href, groupLink)
|
|
||||||
|
|
||||||
const item = isPub
|
|
||||||
? getPublication(entry)
|
|
||||||
: Object.assign(links.find(link => isOPDSCatalog(link.type)) ?? links[0] ?? {}, {
|
|
||||||
title: children.find(filter('title'))?.textContent,
|
|
||||||
[SYMBOL.SUMMARY]: getSummary(children.find(filter('summary')),
|
|
||||||
children.find(filter('content'))),
|
|
||||||
})
|
|
||||||
|
|
||||||
const arr = groupedItems.get(groupLink?.href ?? null)
|
|
||||||
if (arr) arr.push(item)
|
|
||||||
else groupedItems.set(groupLink.href, [item])
|
|
||||||
}
|
|
||||||
const [items, ...groups] = Array.from(groupedItems, ([key, items]) => {
|
|
||||||
const itemsKey = items[0]?.metadata ? 'publications' : 'navigation'
|
|
||||||
if (key == null) return { [itemsKey]: items }
|
|
||||||
const link = groupLinkMap.get(key)
|
|
||||||
return {
|
|
||||||
metadata: {
|
|
||||||
title: link.title,
|
|
||||||
numberOfItems: link.properties.numberOfItems,
|
|
||||||
},
|
|
||||||
links: [{ rel: 'self', href: link.href, type: link.type }],
|
|
||||||
[itemsKey]: items,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return {
|
|
||||||
metadata: {
|
|
||||||
title: children.find(filter('title'))?.textContent,
|
|
||||||
subtitle: children.find(filter('subtitle'))?.textContent,
|
|
||||||
},
|
|
||||||
links,
|
|
||||||
...items,
|
|
||||||
groups,
|
|
||||||
facets: Array.from(
|
|
||||||
groupByArray(linksByRel.get(REL.FACET) ?? [], link => link[FACET_GROUP]),
|
|
||||||
([facet, links]) => ({ metadata: { title: facet }, links })),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getSearch = async link => {
|
|
||||||
const { replace, getVariables } = await import('./uri-template.js')
|
|
||||||
return {
|
|
||||||
metadata: {
|
|
||||||
title: link.title,
|
|
||||||
},
|
|
||||||
search: map => replace(link.href, map.get(null)),
|
|
||||||
params: Array.from(getVariables(link.href), name => ({ name })),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getOpenSearch = doc => {
|
|
||||||
const defaultNS = doc.documentElement.namespaceURI
|
|
||||||
const filter = filterNS(defaultNS)
|
|
||||||
const children = Array.from(doc.documentElement.children)
|
|
||||||
|
|
||||||
const $$urls = children.filter(filter('Url'))
|
|
||||||
const $url = $$urls.find(url => isOPDSCatalog(url.getAttribute('type'))) ?? $$urls[0]
|
|
||||||
if (!$url) throw new Error('document must contain at least one Url element')
|
|
||||||
|
|
||||||
const regex = /{(?:([^}]+?):)?(.+?)(\?)?}/g
|
|
||||||
const defaultMap = new Map([
|
|
||||||
['count', '100'],
|
|
||||||
['startIndex', $url.getAttribute('indexOffset') ?? '0'],
|
|
||||||
['startPage', $url.getAttribute('pageOffset') ?? '0'],
|
|
||||||
['language', '*'],
|
|
||||||
['inputEncoding', 'UTF-8'],
|
|
||||||
['outputEncoding', 'UTF-8'],
|
|
||||||
])
|
|
||||||
|
|
||||||
const template = $url.getAttribute('template')
|
|
||||||
return {
|
|
||||||
metadata: {
|
|
||||||
title: (children.find(filter('LongName')) ?? children.find(filter('ShortName')))?.textContent,
|
|
||||||
description: children.find(filter('Description'))?.textContent,
|
|
||||||
},
|
|
||||||
search: map => template.replace(regex, (_, prefix, param) => {
|
|
||||||
const namespace = prefix ? $url.lookupNamespaceURI(prefix) : null
|
|
||||||
const ns = namespace === defaultNS ? null : namespace
|
|
||||||
const val = map.get(ns)?.get(param)
|
|
||||||
return encodeURIComponent(val ? val : (!ns ? defaultMap.get(param) ?? '' : ''))
|
|
||||||
}),
|
|
||||||
params: Array.from(template.matchAll(regex), ([, prefix, param, optional]) => {
|
|
||||||
const namespace = prefix ? $url.lookupNamespaceURI(prefix) : null
|
|
||||||
const ns = namespace === defaultNS ? null : namespace
|
|
||||||
return {
|
|
||||||
ns, name: param,
|
|
||||||
required: !optional,
|
|
||||||
value: ns && ns !== defaultNS ? '' : defaultMap.get(param) ?? '',
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,227 +0,0 @@
|
|||||||
const createSVGElement = tag =>
|
|
||||||
document.createElementNS('http://www.w3.org/2000/svg', tag)
|
|
||||||
|
|
||||||
export class Overlayer {
|
|
||||||
#svg = createSVGElement('svg')
|
|
||||||
#map = new Map()
|
|
||||||
#doc = null
|
|
||||||
constructor(doc) {
|
|
||||||
this.#doc = doc
|
|
||||||
Object.assign(this.#svg.style, {
|
|
||||||
position: 'absolute', top: '0', left: '0',
|
|
||||||
width: '100%', height: '100%',
|
|
||||||
pointerEvents: 'none',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
get element() {
|
|
||||||
return this.#svg
|
|
||||||
}
|
|
||||||
get #zoom() {
|
|
||||||
// Safari does not zoom the client rects, while Chrome, Edge and Firefox does
|
|
||||||
if (/^((?!chrome|android).)*AppleWebKit/i.test(navigator.userAgent) && !window.chrome) {
|
|
||||||
return window.getComputedStyle(this.#doc.body).zoom || 1.0
|
|
||||||
}
|
|
||||||
return 1.0
|
|
||||||
}
|
|
||||||
#splitRangeByParagraph(range) {
|
|
||||||
const ancestor = range.commonAncestorContainer
|
|
||||||
const paragraphs = Array.from(ancestor.querySelectorAll?.('p, h1, h2, h3, h4') || [])
|
|
||||||
|
|
||||||
const splitRanges = []
|
|
||||||
paragraphs.forEach((p) => {
|
|
||||||
const pRange = document.createRange()
|
|
||||||
if (range.intersectsNode(p)) {
|
|
||||||
pRange.selectNodeContents(p)
|
|
||||||
if (pRange.compareBoundaryPoints(Range.START_TO_START, range) < 0) {
|
|
||||||
pRange.setStart(range.startContainer, range.startOffset)
|
|
||||||
}
|
|
||||||
if (pRange.compareBoundaryPoints(Range.END_TO_END, range) > 0) {
|
|
||||||
pRange.setEnd(range.endContainer, range.endOffset)
|
|
||||||
}
|
|
||||||
splitRanges.push(pRange)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return splitRanges.length === 0 ? [range] : splitRanges
|
|
||||||
}
|
|
||||||
add(key, range, draw, options) {
|
|
||||||
if (this.#map.has(key)) this.remove(key)
|
|
||||||
if (typeof range === 'function') range = range(this.#svg.getRootNode())
|
|
||||||
const zoom = this.#zoom
|
|
||||||
let rects = []
|
|
||||||
this.#splitRangeByParagraph(range).forEach((pRange) => {
|
|
||||||
const pRects = Array.from(pRange.getClientRects()).map(rect => ({
|
|
||||||
left: rect.left * zoom,
|
|
||||||
top: rect.top * zoom,
|
|
||||||
right: rect.right * zoom,
|
|
||||||
bottom: rect.bottom * zoom,
|
|
||||||
width: rect.width * zoom,
|
|
||||||
height: rect.height * zoom,
|
|
||||||
}))
|
|
||||||
rects = rects.concat(pRects)
|
|
||||||
})
|
|
||||||
const element = draw(rects, options)
|
|
||||||
this.#svg.append(element)
|
|
||||||
this.#map.set(key, { range, draw, options, element, rects })
|
|
||||||
}
|
|
||||||
remove(key) {
|
|
||||||
if (!this.#map.has(key)) return
|
|
||||||
this.#svg.removeChild(this.#map.get(key).element)
|
|
||||||
this.#map.delete(key)
|
|
||||||
}
|
|
||||||
redraw() {
|
|
||||||
for (const obj of this.#map.values()) {
|
|
||||||
const { range, draw, options, element } = obj
|
|
||||||
this.#svg.removeChild(element)
|
|
||||||
const zoom = this.#zoom
|
|
||||||
let rects = []
|
|
||||||
this.#splitRangeByParagraph(range).forEach((pRange) => {
|
|
||||||
const pRects = Array.from(pRange.getClientRects()).map(rect => ({
|
|
||||||
left: rect.left * zoom,
|
|
||||||
top: rect.top * zoom,
|
|
||||||
right: rect.right * zoom,
|
|
||||||
bottom: rect.bottom * zoom,
|
|
||||||
width: rect.width * zoom,
|
|
||||||
height: rect.height * zoom,
|
|
||||||
}))
|
|
||||||
rects = rects.concat(pRects)
|
|
||||||
})
|
|
||||||
const el = draw(rects, options)
|
|
||||||
this.#svg.append(el)
|
|
||||||
obj.element = el
|
|
||||||
obj.rects = rects
|
|
||||||
}
|
|
||||||
}
|
|
||||||
hitTest({ x, y }) {
|
|
||||||
const arr = Array.from(this.#map.entries())
|
|
||||||
// loop in reverse to hit more recently added items first
|
|
||||||
for (let i = arr.length - 1; i >= 0; i--) {
|
|
||||||
const [key, obj] = arr[i]
|
|
||||||
for (const { left, top, right, bottom } of obj.rects)
|
|
||||||
if (top <= y && left <= x && bottom > y && right > x)
|
|
||||||
return [key, obj.range]
|
|
||||||
}
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
static underline(rects, options = {}) {
|
|
||||||
const { color = 'red', width: strokeWidth = 2, padding = 0, writingMode } = options
|
|
||||||
const g = createSVGElement('g')
|
|
||||||
g.setAttribute('fill', color)
|
|
||||||
if (writingMode === 'vertical-rl' || writingMode === 'vertical-lr')
|
|
||||||
for (const { right, top, height } of rects) {
|
|
||||||
const el = createSVGElement('rect')
|
|
||||||
el.setAttribute('x', right - strokeWidth / 2 + padding)
|
|
||||||
el.setAttribute('y', top)
|
|
||||||
el.setAttribute('height', height)
|
|
||||||
el.setAttribute('width', strokeWidth)
|
|
||||||
g.append(el)
|
|
||||||
}
|
|
||||||
else for (const { left, bottom, width } of rects) {
|
|
||||||
const el = createSVGElement('rect')
|
|
||||||
el.setAttribute('x', left)
|
|
||||||
el.setAttribute('y', bottom - strokeWidth / 2 + padding)
|
|
||||||
el.setAttribute('height', strokeWidth)
|
|
||||||
el.setAttribute('width', width)
|
|
||||||
g.append(el)
|
|
||||||
}
|
|
||||||
return g
|
|
||||||
}
|
|
||||||
static strikethrough(rects, options = {}) {
|
|
||||||
const { color = 'red', width: strokeWidth = 2, writingMode } = options
|
|
||||||
const g = createSVGElement('g')
|
|
||||||
g.setAttribute('fill', color)
|
|
||||||
if (writingMode === 'vertical-rl' || writingMode === 'vertical-lr')
|
|
||||||
for (const { right, left, top, height } of rects) {
|
|
||||||
const el = createSVGElement('rect')
|
|
||||||
el.setAttribute('x', (right + left) / 2)
|
|
||||||
el.setAttribute('y', top)
|
|
||||||
el.setAttribute('height', height)
|
|
||||||
el.setAttribute('width', strokeWidth)
|
|
||||||
g.append(el)
|
|
||||||
}
|
|
||||||
else for (const { left, top, bottom, width } of rects) {
|
|
||||||
const el = createSVGElement('rect')
|
|
||||||
el.setAttribute('x', left)
|
|
||||||
el.setAttribute('y', (top + bottom) / 2)
|
|
||||||
el.setAttribute('height', strokeWidth)
|
|
||||||
el.setAttribute('width', width)
|
|
||||||
g.append(el)
|
|
||||||
}
|
|
||||||
return g
|
|
||||||
}
|
|
||||||
static squiggly(rects, options = {}) {
|
|
||||||
const { color = 'red', width: strokeWidth = 2, padding = 0, writingMode } = options
|
|
||||||
const g = createSVGElement('g')
|
|
||||||
g.setAttribute('fill', 'none')
|
|
||||||
g.setAttribute('stroke', color)
|
|
||||||
g.setAttribute('stroke-width', strokeWidth)
|
|
||||||
const block = strokeWidth * 1.5
|
|
||||||
if (writingMode === 'vertical-rl' || writingMode === 'vertical-lr')
|
|
||||||
for (const { right, top, height } of rects) {
|
|
||||||
const el = createSVGElement('path')
|
|
||||||
const n = Math.round(height / block / 1.5)
|
|
||||||
const inline = height / n
|
|
||||||
const ls = Array.from({ length: n },
|
|
||||||
(_, i) => `l${i % 2 ? -block : block} ${inline}`).join('')
|
|
||||||
el.setAttribute('d', `M${right - strokeWidth / 2 + padding} ${top}${ls}`)
|
|
||||||
g.append(el)
|
|
||||||
}
|
|
||||||
else for (const { left, bottom, width } of rects) {
|
|
||||||
const el = createSVGElement('path')
|
|
||||||
const n = Math.round(width / block / 1.5)
|
|
||||||
const inline = width / n
|
|
||||||
const ls = Array.from({ length: n },
|
|
||||||
(_, i) => `l${inline} ${i % 2 ? block : -block}`).join('')
|
|
||||||
el.setAttribute('d', `M${left} ${bottom + strokeWidth / 2 + padding}${ls}`)
|
|
||||||
g.append(el)
|
|
||||||
}
|
|
||||||
return g
|
|
||||||
}
|
|
||||||
static highlight(rects, options = {}) {
|
|
||||||
const { color = 'red', padding = 0 } = options
|
|
||||||
const g = createSVGElement('g')
|
|
||||||
g.setAttribute('fill', color)
|
|
||||||
g.style.opacity = 'var(--overlayer-highlight-opacity, .3)'
|
|
||||||
g.style.mixBlendMode = 'var(--overlayer-highlight-blend-mode, normal)'
|
|
||||||
for (const { left, top, height, width } of rects) {
|
|
||||||
const el = createSVGElement('rect')
|
|
||||||
el.setAttribute('x', left - padding)
|
|
||||||
el.setAttribute('y', top - padding)
|
|
||||||
el.setAttribute('height', height + padding * 2)
|
|
||||||
el.setAttribute('width', width + padding * 2)
|
|
||||||
g.append(el)
|
|
||||||
}
|
|
||||||
return g
|
|
||||||
}
|
|
||||||
static outline(rects, options = {}) {
|
|
||||||
const { color = 'red', width: strokeWidth = 3, padding = 0, radius = 3 } = options
|
|
||||||
const g = createSVGElement('g')
|
|
||||||
g.setAttribute('fill', 'none')
|
|
||||||
g.setAttribute('stroke', color)
|
|
||||||
g.setAttribute('stroke-width', strokeWidth)
|
|
||||||
for (const { left, top, height, width } of rects) {
|
|
||||||
const el = createSVGElement('rect')
|
|
||||||
el.setAttribute('x', left - padding)
|
|
||||||
el.setAttribute('y', top - padding)
|
|
||||||
el.setAttribute('height', height + padding * 2)
|
|
||||||
el.setAttribute('width', width + padding * 2)
|
|
||||||
el.setAttribute('rx', radius)
|
|
||||||
g.append(el)
|
|
||||||
}
|
|
||||||
return g
|
|
||||||
}
|
|
||||||
// make an exact copy of an image in the overlay
|
|
||||||
// one can then apply filters to the entire element, without affecting them;
|
|
||||||
// it's a bit silly and probably better to just invert images twice
|
|
||||||
// (though the color will be off in that case if you do heu-rotate)
|
|
||||||
static copyImage([rect], options = {}) {
|
|
||||||
const { src } = options
|
|
||||||
const image = createSVGElement('image')
|
|
||||||
const { left, top, height, width } = rect
|
|
||||||
image.setAttribute('href', src)
|
|
||||||
image.setAttribute('x', left)
|
|
||||||
image.setAttribute('y', top)
|
|
||||||
image.setAttribute('height', height)
|
|
||||||
image.setAttribute('width', width)
|
|
||||||
return image
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,616 +0,0 @@
|
|||||||
/* global pdfjsLib */
|
|
||||||
|
|
||||||
// https://github.com/mozilla/pdf.js/blob/f04967017f22e46d70d11468dd928b4cdc2f6ea1/web/text_layer_builder.css
|
|
||||||
const textLayerBuilderCSS = `
|
|
||||||
/* Copyright 2014 Mozilla Foundation
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--highlight-bg-color: rgb(180 0 170);
|
|
||||||
--highlight-selected-bg-color: rgb(0 100 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (forced-colors: active) {
|
|
||||||
:root {
|
|
||||||
--highlight-bg-color: Highlight;
|
|
||||||
--highlight-selected-bg-color: ButtonText;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.textLayer {
|
|
||||||
position: absolute;
|
|
||||||
text-align: initial;
|
|
||||||
inset: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
opacity: 0.25;
|
|
||||||
line-height: 1;
|
|
||||||
text-size-adjust: none;
|
|
||||||
forced-color-adjust: none;
|
|
||||||
transform-origin: 0 0;
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.textLayer :is(span, br) {
|
|
||||||
color: transparent;
|
|
||||||
position: absolute;
|
|
||||||
white-space: pre;
|
|
||||||
cursor: text;
|
|
||||||
transform-origin: 0% 0%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Only necessary in Google Chrome, see issue 14205, and most unfortunately
|
|
||||||
* the problem doesn't show up in "text" reference tests. */
|
|
||||||
/*#if !MOZCENTRAL*/
|
|
||||||
.textLayer span.markedContent {
|
|
||||||
top: 0;
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
/*#endif*/
|
|
||||||
|
|
||||||
.textLayer .highlight {
|
|
||||||
margin: -1px;
|
|
||||||
padding: 1px;
|
|
||||||
background-color: var(--highlight-bg-color);
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.textLayer .highlight.appended {
|
|
||||||
position: initial;
|
|
||||||
}
|
|
||||||
|
|
||||||
.textLayer .highlight.begin {
|
|
||||||
border-radius: 4px 0 0 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.textLayer .highlight.end {
|
|
||||||
border-radius: 0 4px 4px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.textLayer .highlight.middle {
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.textLayer .highlight.selected {
|
|
||||||
background-color: var(--highlight-selected-bg-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.textLayer ::selection {
|
|
||||||
/*#if !MOZCENTRAL*/
|
|
||||||
background: blue;
|
|
||||||
/*#endif*/
|
|
||||||
background: AccentColor; /* stylelint-disable-line declaration-block-no-duplicate-properties */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Avoids https://github.com/mozilla/pdf.js/issues/13840 in Chrome */
|
|
||||||
/*#if !MOZCENTRAL*/
|
|
||||||
.textLayer br::selection {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
/*#endif*/
|
|
||||||
|
|
||||||
.textLayer .endOfContent {
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
inset: 100% 0 0;
|
|
||||||
z-index: -1;
|
|
||||||
cursor: default;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.textLayer .endOfContent.active {
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
//https://github.com/mozilla/pdf.js/blob/d64f223d034ad74fb62571c3acff566d25eca413/web/annotation_layer_builder.css
|
|
||||||
const annotationLayerBuilderCSS = `
|
|
||||||
/* Copyright 2014 Mozilla Foundation
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--annotation-unfocused-field-background: url("data:image/svg+xml;charset=UTF-8,<svg width='1px' height='1px' xmlns='http://www.w3.org/2000/svg'><rect width='100%' height='100%' style='fill:rgba(0, 54, 255, 0.13);'/></svg>");
|
|
||||||
--input-focus-border-color: Highlight;
|
|
||||||
--input-focus-outline: 1px solid Canvas;
|
|
||||||
--input-unfocused-border-color: transparent;
|
|
||||||
--input-disabled-border-color: transparent;
|
|
||||||
--input-hover-border-color: black;
|
|
||||||
--link-outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (forced-colors: active) {
|
|
||||||
:root {
|
|
||||||
--input-focus-border-color: CanvasText;
|
|
||||||
--input-unfocused-border-color: ActiveText;
|
|
||||||
--input-disabled-border-color: GrayText;
|
|
||||||
--input-hover-border-color: Highlight;
|
|
||||||
--link-outline: 1.5px solid LinkText;
|
|
||||||
--hcm-highligh-filter: invert(100%);
|
|
||||||
}
|
|
||||||
.annotationLayer .textWidgetAnnotation :is(input, textarea):required,
|
|
||||||
.annotationLayer .choiceWidgetAnnotation select:required,
|
|
||||||
.annotationLayer
|
|
||||||
.buttonWidgetAnnotation:is(.checkBox, .radioButton)
|
|
||||||
input:required {
|
|
||||||
outline: 1.5px solid selectedItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .linkAnnotation:hover {
|
|
||||||
backdrop-filter: var(--hcm-highligh-filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .linkAnnotation > a:hover {
|
|
||||||
opacity: 0 !important;
|
|
||||||
background: none !important;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .popupAnnotation .popup {
|
|
||||||
outline: calc(1.5px * var(--scale-factor)) solid CanvasText !important;
|
|
||||||
background-color: ButtonFace !important;
|
|
||||||
color: ButtonText !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .highlightArea:hover::after {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
backdrop-filter: var(--hcm-highligh-filter);
|
|
||||||
content: "";
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .popupAnnotation.focused .popup {
|
|
||||||
outline: calc(3px * var(--scale-factor)) solid Highlight !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
transform-origin: 0 0;
|
|
||||||
z-index: 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer[data-main-rotation="90"] .norotate {
|
|
||||||
transform: rotate(270deg) translateX(-100%);
|
|
||||||
}
|
|
||||||
.annotationLayer[data-main-rotation="180"] .norotate {
|
|
||||||
transform: rotate(180deg) translate(-100%, -100%);
|
|
||||||
}
|
|
||||||
.annotationLayer[data-main-rotation="270"] .norotate {
|
|
||||||
transform: rotate(90deg) translateY(-100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer canvas {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer section {
|
|
||||||
position: absolute;
|
|
||||||
text-align: initial;
|
|
||||||
pointer-events: auto;
|
|
||||||
box-sizing: border-box;
|
|
||||||
transform-origin: 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .linkAnnotation {
|
|
||||||
outline: var(--link-outline);
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer :is(.linkAnnotation, .buttonWidgetAnnotation.pushButton) > a {
|
|
||||||
position: absolute;
|
|
||||||
font-size: 1em;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer
|
|
||||||
:is(.linkAnnotation, .buttonWidgetAnnotation.pushButton):not(.hasBorder)
|
|
||||||
> a:hover {
|
|
||||||
opacity: 0.2;
|
|
||||||
background-color: rgb(255 255 0);
|
|
||||||
box-shadow: 0 2px 10px rgb(255 255 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .linkAnnotation.hasBorder:hover {
|
|
||||||
background-color: rgb(255 255 0 / 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .hasBorder {
|
|
||||||
background-size: 100% 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .textAnnotation img {
|
|
||||||
position: absolute;
|
|
||||||
cursor: pointer;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .textWidgetAnnotation :is(input, textarea),
|
|
||||||
.annotationLayer .choiceWidgetAnnotation select,
|
|
||||||
.annotationLayer .buttonWidgetAnnotation:is(.checkBox, .radioButton) input {
|
|
||||||
background-image: var(--annotation-unfocused-field-background);
|
|
||||||
border: 2px solid var(--input-unfocused-border-color);
|
|
||||||
box-sizing: border-box;
|
|
||||||
font: calc(9px * var(--scale-factor)) sans-serif;
|
|
||||||
height: 100%;
|
|
||||||
margin: 0;
|
|
||||||
vertical-align: top;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .textWidgetAnnotation :is(input, textarea):required,
|
|
||||||
.annotationLayer .choiceWidgetAnnotation select:required,
|
|
||||||
.annotationLayer
|
|
||||||
.buttonWidgetAnnotation:is(.checkBox, .radioButton)
|
|
||||||
input:required {
|
|
||||||
outline: 1.5px solid red;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .choiceWidgetAnnotation select option {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .buttonWidgetAnnotation.radioButton input {
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .textWidgetAnnotation textarea {
|
|
||||||
resize: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .textWidgetAnnotation :is(input, textarea)[disabled],
|
|
||||||
.annotationLayer .choiceWidgetAnnotation select[disabled],
|
|
||||||
.annotationLayer
|
|
||||||
.buttonWidgetAnnotation:is(.checkBox, .radioButton)
|
|
||||||
input[disabled] {
|
|
||||||
background: none;
|
|
||||||
border: 2px solid var(--input-disabled-border-color);
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .textWidgetAnnotation :is(input, textarea):hover,
|
|
||||||
.annotationLayer .choiceWidgetAnnotation select:hover,
|
|
||||||
.annotationLayer
|
|
||||||
.buttonWidgetAnnotation:is(.checkBox, .radioButton)
|
|
||||||
input:hover {
|
|
||||||
border: 2px solid var(--input-hover-border-color);
|
|
||||||
}
|
|
||||||
.annotationLayer .textWidgetAnnotation :is(input, textarea):hover,
|
|
||||||
.annotationLayer .choiceWidgetAnnotation select:hover,
|
|
||||||
.annotationLayer .buttonWidgetAnnotation.checkBox input:hover {
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .textWidgetAnnotation :is(input, textarea):focus,
|
|
||||||
.annotationLayer .choiceWidgetAnnotation select:focus {
|
|
||||||
background: none;
|
|
||||||
border: 2px solid var(--input-focus-border-color);
|
|
||||||
border-radius: 2px;
|
|
||||||
outline: var(--input-focus-outline);
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .buttonWidgetAnnotation:is(.checkBox, .radioButton) :focus {
|
|
||||||
background-image: none;
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .buttonWidgetAnnotation.checkBox :focus {
|
|
||||||
border: 2px solid var(--input-focus-border-color);
|
|
||||||
border-radius: 2px;
|
|
||||||
outline: var(--input-focus-outline);
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .buttonWidgetAnnotation.radioButton :focus {
|
|
||||||
border: 2px solid var(--input-focus-border-color);
|
|
||||||
outline: var(--input-focus-outline);
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::before,
|
|
||||||
.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::after,
|
|
||||||
.annotationLayer .buttonWidgetAnnotation.radioButton input:checked::before {
|
|
||||||
background-color: CanvasText;
|
|
||||||
content: "";
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::before,
|
|
||||||
.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::after {
|
|
||||||
height: 80%;
|
|
||||||
left: 45%;
|
|
||||||
width: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::before {
|
|
||||||
transform: rotate(45deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::after {
|
|
||||||
transform: rotate(-45deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .buttonWidgetAnnotation.radioButton input:checked::before {
|
|
||||||
border-radius: 50%;
|
|
||||||
height: 50%;
|
|
||||||
left: 30%;
|
|
||||||
top: 20%;
|
|
||||||
width: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .textWidgetAnnotation input.comb {
|
|
||||||
font-family: monospace;
|
|
||||||
padding-left: 2px;
|
|
||||||
padding-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .textWidgetAnnotation input.comb:focus {
|
|
||||||
/*
|
|
||||||
* Letter spacing is placed on the right side of each character. Hence, the
|
|
||||||
* letter spacing of the last character may be placed outside the visible
|
|
||||||
* area, causing horizontal scrolling. We avoid this by extending the width
|
|
||||||
* when the element has focus and revert this when it loses focus.
|
|
||||||
*/
|
|
||||||
width: 103%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .buttonWidgetAnnotation:is(.checkBox, .radioButton) input {
|
|
||||||
appearance: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .fileAttachmentAnnotation .popupTriggerArea {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .popupAnnotation {
|
|
||||||
position: absolute;
|
|
||||||
font-size: calc(9px * var(--scale-factor));
|
|
||||||
pointer-events: none;
|
|
||||||
width: max-content;
|
|
||||||
max-width: 45%;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .popup {
|
|
||||||
background-color: rgb(255 255 153);
|
|
||||||
box-shadow: 0 calc(2px * var(--scale-factor)) calc(5px * var(--scale-factor))
|
|
||||||
rgb(136 136 136);
|
|
||||||
border-radius: calc(2px * var(--scale-factor));
|
|
||||||
outline: 1.5px solid rgb(255 255 74);
|
|
||||||
padding: calc(6px * var(--scale-factor));
|
|
||||||
cursor: pointer;
|
|
||||||
font: message-box;
|
|
||||||
white-space: normal;
|
|
||||||
word-wrap: break-word;
|
|
||||||
pointer-events: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .popupAnnotation.focused .popup {
|
|
||||||
outline-width: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .popup * {
|
|
||||||
font-size: calc(9px * var(--scale-factor));
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .popup > .header {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .popup > .header h1 {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .popup > .header .popupDate {
|
|
||||||
display: inline-block;
|
|
||||||
margin-left: calc(5px * var(--scale-factor));
|
|
||||||
width: fit-content;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .popupContent {
|
|
||||||
border-top: 1px solid rgb(51 51 51);
|
|
||||||
margin-top: calc(2px * var(--scale-factor));
|
|
||||||
padding-top: calc(2px * var(--scale-factor));
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .richText > * {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
font-size: calc(9px * var(--scale-factor));
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .popupTriggerArea {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer section svg {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .annotationTextContent {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
opacity: 0;
|
|
||||||
color: transparent;
|
|
||||||
user-select: none;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer .annotationTextContent span {
|
|
||||||
width: 100%;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotationLayer svg.quadrilateralsContainer {
|
|
||||||
contain: strict;
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const renderPage = async (page, getImageBlob) => {
|
|
||||||
|
|
||||||
const naturalPdfSize = page.getViewport({ scale: 1 })
|
|
||||||
const naturalPdfRatio = naturalPdfSize.width / naturalPdfSize.height
|
|
||||||
const appRatio = innerWidth / innerHeight
|
|
||||||
const pdfToAppResolutionRatio = appRatio / naturalPdfRatio
|
|
||||||
|
|
||||||
const scale = devicePixelRatio * pdfToAppResolutionRatio
|
|
||||||
const viewport = page.getViewport({ scale })
|
|
||||||
|
|
||||||
const canvas = document.createElement('canvas')
|
|
||||||
canvas.height = viewport.height
|
|
||||||
canvas.width = viewport.width
|
|
||||||
const canvasContext = canvas.getContext('2d')
|
|
||||||
await page.render({ canvasContext, viewport }).promise
|
|
||||||
const blob = await new Promise(resolve => canvas.toBlob(resolve))
|
|
||||||
if (getImageBlob) return blob
|
|
||||||
|
|
||||||
/*
|
|
||||||
// with the SVG backend
|
|
||||||
const operatorList = await page.getOperatorList()
|
|
||||||
const svgGraphics = new pdfjsLib.SVGGraphics(page.commonObjs, page.objs)
|
|
||||||
const svg = await svgGraphics.getSVG(operatorList, viewport)
|
|
||||||
const str = new XMLSerializer().serializeToString(svg)
|
|
||||||
const blob = new Blob([str], { type: 'image/svg+xml' })
|
|
||||||
*/
|
|
||||||
|
|
||||||
const container = document.createElement('div')
|
|
||||||
container.classList.add('textLayer')
|
|
||||||
await pdfjsLib.renderTextLayer({
|
|
||||||
textContentSource: await page.getTextContent(),
|
|
||||||
container, viewport,
|
|
||||||
}).promise
|
|
||||||
|
|
||||||
const div = document.createElement('div')
|
|
||||||
div.classList.add('annotationLayer')
|
|
||||||
await new pdfjsLib.AnnotationLayer({ page, viewport, div }).render({
|
|
||||||
annotations: await page.getAnnotations(),
|
|
||||||
linkService: {
|
|
||||||
getDestinationHash: dest => JSON.stringify(dest),
|
|
||||||
addLinkAttributes: (link, url) => link.href = url,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const src = URL.createObjectURL(blob)
|
|
||||||
const url = URL.createObjectURL(new Blob([`
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<style>
|
|
||||||
:root {
|
|
||||||
--scale-factor: ${scale};
|
|
||||||
}
|
|
||||||
html, body {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
${textLayerBuilderCSS}
|
|
||||||
${annotationLayerBuilderCSS}
|
|
||||||
</style>
|
|
||||||
<img src="${src}">
|
|
||||||
${container.outerHTML}
|
|
||||||
${div.outerHTML}
|
|
||||||
`], { type: 'text/html' }))
|
|
||||||
return url
|
|
||||||
}
|
|
||||||
|
|
||||||
const makeTOCItem = item => ({
|
|
||||||
label: item.title,
|
|
||||||
href: JSON.stringify(item.dest),
|
|
||||||
subitems: item.items.length ? item.items.map(makeTOCItem) : null,
|
|
||||||
})
|
|
||||||
|
|
||||||
export const makePDF = async file => {
|
|
||||||
const data = new Uint8Array(await file.arrayBuffer())
|
|
||||||
const pdf = await pdfjsLib.getDocument({ data }).promise
|
|
||||||
|
|
||||||
const book = { rendition: { layout: 'pre-paginated' } }
|
|
||||||
|
|
||||||
const info = (await pdf.getMetadata())?.info
|
|
||||||
book.metadata = {
|
|
||||||
title: info?.Title,
|
|
||||||
author: info?.Author,
|
|
||||||
}
|
|
||||||
|
|
||||||
const outline = await pdf.getOutline()
|
|
||||||
book.toc = outline?.map(makeTOCItem)
|
|
||||||
|
|
||||||
const cache = new Map()
|
|
||||||
book.sections = Array.from({ length: pdf.numPages }).map((_, i) => ({
|
|
||||||
id: i,
|
|
||||||
load: async () => {
|
|
||||||
const cached = cache.get(i)
|
|
||||||
if (cached) return cached
|
|
||||||
const url = await renderPage(await pdf.getPage(i + 1))
|
|
||||||
cache.set(i, url)
|
|
||||||
return url
|
|
||||||
},
|
|
||||||
size: 1000,
|
|
||||||
}))
|
|
||||||
book.sections[0].pageSpread = 'right'
|
|
||||||
book.isExternal = uri => /^\w+:/i.test(uri)
|
|
||||||
book.resolveHref = async href => {
|
|
||||||
const parsed = JSON.parse(href)
|
|
||||||
const dest = typeof parsed === 'string'
|
|
||||||
? await pdf.getDestination(parsed) : parsed
|
|
||||||
if (!dest || !dest[0]) return { index: 0 }
|
|
||||||
const index = await pdf.getPageIndex(dest[0])
|
|
||||||
return { index }
|
|
||||||
}
|
|
||||||
book.splitTOCHref = async href => {
|
|
||||||
const parsed = JSON.parse(href)
|
|
||||||
const dest = typeof parsed === 'string'
|
|
||||||
? await pdf.getDestination(parsed) : parsed
|
|
||||||
if (!dest || !dest[0]) return [0, null]
|
|
||||||
const index = await pdf.getPageIndex(dest[0])
|
|
||||||
return [index, null]
|
|
||||||
}
|
|
||||||
book.getTOCFragment = doc => doc.documentElement
|
|
||||||
book.getCover = async () => renderPage(await pdf.getPage(1), true)
|
|
||||||
return book
|
|
||||||
}
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
// assign a unique ID for each TOC item
|
|
||||||
const assignIDs = toc => {
|
|
||||||
let id = 0
|
|
||||||
const assignID = item => {
|
|
||||||
item.id = id++
|
|
||||||
if (item.subitems) for (const subitem of item.subitems) assignID(subitem)
|
|
||||||
}
|
|
||||||
for (const item of toc) assignID(item)
|
|
||||||
return toc
|
|
||||||
}
|
|
||||||
|
|
||||||
const flatten = items => items
|
|
||||||
.map(item => item.subitems?.length
|
|
||||||
? [item, flatten(item.subitems)].flat()
|
|
||||||
: item)
|
|
||||||
.flat()
|
|
||||||
|
|
||||||
export class TOCProgress {
|
|
||||||
async init({ toc, ids, splitHref, getFragment }) {
|
|
||||||
assignIDs(toc)
|
|
||||||
const items = flatten(toc)
|
|
||||||
const grouped = new Map()
|
|
||||||
for (const [i, item] of items.entries()) {
|
|
||||||
const [id, fragment] = await splitHref(item?.href) ?? []
|
|
||||||
const value = { fragment, item }
|
|
||||||
if (grouped.has(id)) grouped.get(id).items.push(value)
|
|
||||||
else grouped.set(id, { prev: items[i - 1], items: [value] })
|
|
||||||
}
|
|
||||||
const map = new Map()
|
|
||||||
for (const [i, id] of ids.entries()) {
|
|
||||||
if (grouped.has(id)) map.set(id, grouped.get(id))
|
|
||||||
else map.set(id, map.get(ids[i - 1]))
|
|
||||||
}
|
|
||||||
this.ids = ids
|
|
||||||
this.map = map
|
|
||||||
this.getFragment = getFragment
|
|
||||||
}
|
|
||||||
getProgress(index, range) {
|
|
||||||
if (!this.ids) return
|
|
||||||
const id = this.ids[index]
|
|
||||||
const obj = this.map.get(id)
|
|
||||||
if (!obj) return null
|
|
||||||
const { prev, items } = obj
|
|
||||||
if (!items) return prev
|
|
||||||
if (!range || items.length === 1 && !items[0].fragment) return items[0].item
|
|
||||||
|
|
||||||
const doc = range.startContainer.getRootNode()
|
|
||||||
for (const [i, { fragment }] of items.entries()) {
|
|
||||||
const el = this.getFragment(doc, fragment)
|
|
||||||
if (!el) continue
|
|
||||||
if (range.comparePoint(el, 0) > 0)
|
|
||||||
return (items[i - 1]?.item ?? prev)
|
|
||||||
}
|
|
||||||
return items[items.length - 1].item
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SectionProgress {
|
|
||||||
constructor(sections, sizePerLoc, sizePerTimeUnit) {
|
|
||||||
this.sizes = sections.map(s => s.linear != 'no' && s.size > 0 ? s.size : 0)
|
|
||||||
this.sizePerLoc = sizePerLoc
|
|
||||||
this.sizePerTimeUnit = sizePerTimeUnit
|
|
||||||
this.sizeTotal = this.sizes.reduce((a, b) => a + b, 0)
|
|
||||||
this.sectionFractions = this.#getSectionFractions()
|
|
||||||
}
|
|
||||||
#getSectionFractions() {
|
|
||||||
const { sizeTotal } = this
|
|
||||||
const results = [0]
|
|
||||||
let sum = 0
|
|
||||||
for (const size of this.sizes) results.push((sum += size) / sizeTotal)
|
|
||||||
return results
|
|
||||||
}
|
|
||||||
// get progress given index of and fractions within a section
|
|
||||||
getProgress(index, fractionInSection, pageFraction = 0) {
|
|
||||||
const { sizes, sizePerLoc, sizePerTimeUnit, sizeTotal } = this
|
|
||||||
const sizeInSection = sizes[index] ?? 0
|
|
||||||
const sizeBefore = sizes.slice(0, index).reduce((a, b) => a + b, 0)
|
|
||||||
const size = sizeBefore + fractionInSection * sizeInSection
|
|
||||||
const nextSize = size + pageFraction * sizeInSection
|
|
||||||
const remainingTotal = sizeTotal - size
|
|
||||||
const remainingSection = (1 - fractionInSection) * sizeInSection
|
|
||||||
return {
|
|
||||||
fraction: nextSize / sizeTotal,
|
|
||||||
section: {
|
|
||||||
current: index,
|
|
||||||
total: sizes.length,
|
|
||||||
},
|
|
||||||
location: {
|
|
||||||
current: Math.floor(size / sizePerLoc),
|
|
||||||
next: Math.floor(nextSize / sizePerLoc),
|
|
||||||
total: Math.ceil(sizeTotal / sizePerLoc),
|
|
||||||
},
|
|
||||||
time: {
|
|
||||||
section: remainingSection / sizePerTimeUnit,
|
|
||||||
total: remainingTotal / sizePerTimeUnit,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// the inverse of `getProgress`
|
|
||||||
// get index of and fraction in section based on total fraction
|
|
||||||
getSection(fraction) {
|
|
||||||
if (fraction <= 0) return [0, 0]
|
|
||||||
if (fraction >= 1) return [this.sizes.length - 1, 1]
|
|
||||||
fraction = fraction + Number.EPSILON
|
|
||||||
const { sizeTotal } = this
|
|
||||||
let index = this.sectionFractions.findIndex(x => x > fraction) - 1
|
|
||||||
if (index < 0) return [0, 0]
|
|
||||||
while (!this.sizes[index]) index++
|
|
||||||
const fractionInSection = (fraction - this.sectionFractions[index])
|
|
||||||
/ (this.sizes[index] / sizeTotal)
|
|
||||||
return [index, fractionInSection]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,130 +0,0 @@
|
|||||||
// length for context in excerpts
|
|
||||||
const CONTEXT_LENGTH = 50
|
|
||||||
|
|
||||||
const normalizeWhitespace = str => str.replace(/\s+/g, ' ')
|
|
||||||
|
|
||||||
const makeExcerpt = (strs, { startIndex, startOffset, endIndex, endOffset }) => {
|
|
||||||
const start = strs[startIndex]
|
|
||||||
const end = strs[endIndex]
|
|
||||||
const match = start === end
|
|
||||||
? start.slice(startOffset, endOffset)
|
|
||||||
: start.slice(startOffset)
|
|
||||||
+ strs.slice(start + 1, end).join('')
|
|
||||||
+ end.slice(0, endOffset)
|
|
||||||
const trimmedStart = normalizeWhitespace(start.slice(0, startOffset)).trimStart()
|
|
||||||
const trimmedEnd = normalizeWhitespace(end.slice(endOffset)).trimEnd()
|
|
||||||
const ellipsisPre = trimmedStart.length < CONTEXT_LENGTH ? '' : '…'
|
|
||||||
const ellipsisPost = trimmedEnd.length < CONTEXT_LENGTH ? '' : '…'
|
|
||||||
const pre = `${ellipsisPre}${trimmedStart.slice(-CONTEXT_LENGTH)}`
|
|
||||||
const post = `${trimmedEnd.slice(0, CONTEXT_LENGTH)}${ellipsisPost}`
|
|
||||||
return { pre, match, post }
|
|
||||||
}
|
|
||||||
|
|
||||||
const simpleSearch = function* (strs, query, options = {}) {
|
|
||||||
const { locales = 'en', sensitivity } = options
|
|
||||||
const matchCase = sensitivity === 'variant'
|
|
||||||
const haystack = strs.join('')
|
|
||||||
const lowerHaystack = matchCase ? haystack : haystack.toLocaleLowerCase(locales)
|
|
||||||
const needle = matchCase ? query : query.toLocaleLowerCase(locales)
|
|
||||||
const needleLength = needle.length
|
|
||||||
let index = -1
|
|
||||||
let strIndex = -1
|
|
||||||
let sum = 0
|
|
||||||
do {
|
|
||||||
index = lowerHaystack.indexOf(needle, index + 1)
|
|
||||||
if (index > -1) {
|
|
||||||
while (sum <= index) sum += strs[++strIndex].length
|
|
||||||
const startIndex = strIndex
|
|
||||||
const startOffset = index - (sum - strs[strIndex].length)
|
|
||||||
const end = index + needleLength
|
|
||||||
while (sum <= end) sum += strs[++strIndex].length
|
|
||||||
const endIndex = strIndex
|
|
||||||
const endOffset = end - (sum - strs[strIndex].length)
|
|
||||||
const range = { startIndex, startOffset, endIndex, endOffset }
|
|
||||||
yield { range, excerpt: makeExcerpt(strs, range) }
|
|
||||||
}
|
|
||||||
} while (index > -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
const segmenterSearch = function* (strs, query, options = {}) {
|
|
||||||
const { locales = 'en', granularity = 'word', sensitivity = 'base' } = options
|
|
||||||
let segmenter, collator
|
|
||||||
try {
|
|
||||||
segmenter = new Intl.Segmenter(locales, { usage: 'search', granularity })
|
|
||||||
collator = new Intl.Collator(locales, { sensitivity })
|
|
||||||
} catch (e) {
|
|
||||||
console.warn(e)
|
|
||||||
segmenter = new Intl.Segmenter('en', { usage: 'search', granularity })
|
|
||||||
collator = new Intl.Collator('en', { sensitivity })
|
|
||||||
}
|
|
||||||
const queryLength = Array.from(segmenter.segment(query)).length
|
|
||||||
|
|
||||||
const substrArr = []
|
|
||||||
let strIndex = 0
|
|
||||||
let segments = segmenter.segment(strs[strIndex])[Symbol.iterator]()
|
|
||||||
main: while (strIndex < strs.length) {
|
|
||||||
while (substrArr.length < queryLength) {
|
|
||||||
const { done, value } = segments.next()
|
|
||||||
if (done) {
|
|
||||||
// the current string is exhausted
|
|
||||||
// move on to the next string
|
|
||||||
strIndex++
|
|
||||||
if (strIndex < strs.length) {
|
|
||||||
segments = segmenter.segment(strs[strIndex])[Symbol.iterator]()
|
|
||||||
continue
|
|
||||||
} else break main
|
|
||||||
}
|
|
||||||
const { index, segment } = value
|
|
||||||
// ignore formatting characters
|
|
||||||
if (!/[^\p{Format}]/u.test(segment)) continue
|
|
||||||
// normalize whitespace
|
|
||||||
if (/\s/u.test(segment)) {
|
|
||||||
if (!/\s/u.test(substrArr[substrArr.length - 1]?.segment))
|
|
||||||
substrArr.push({ strIndex, index, segment: ' ' })
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
value.strIndex = strIndex
|
|
||||||
substrArr.push(value)
|
|
||||||
}
|
|
||||||
const substr = substrArr.map(x => x.segment).join('')
|
|
||||||
if (collator.compare(query, substr) === 0) {
|
|
||||||
const endIndex = strIndex
|
|
||||||
const lastSeg = substrArr[substrArr.length - 1]
|
|
||||||
const endOffset = lastSeg.index + lastSeg.segment.length
|
|
||||||
const startIndex = substrArr[0].strIndex
|
|
||||||
const startOffset = substrArr[0].index
|
|
||||||
const range = { startIndex, startOffset, endIndex, endOffset }
|
|
||||||
yield { range, excerpt: makeExcerpt(strs, range) }
|
|
||||||
}
|
|
||||||
substrArr.shift()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const search = (strs, query, options) => {
|
|
||||||
const { granularity = 'grapheme', sensitivity = 'base' } = options
|
|
||||||
if (!Intl?.Segmenter || granularity === 'grapheme'
|
|
||||||
&& (sensitivity === 'variant' || sensitivity === 'accent'))
|
|
||||||
return simpleSearch(strs, query, options)
|
|
||||||
return segmenterSearch(strs, query, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const searchMatcher = (textWalker, opts) => {
|
|
||||||
const { defalutLocale, matchCase, matchDiacritics, matchWholeWords } = opts
|
|
||||||
return function* (doc, query) {
|
|
||||||
const iter = textWalker(doc, function* (strs, makeRange) {
|
|
||||||
for (const result of search(strs, query, {
|
|
||||||
locales: doc.body.lang || doc.documentElement.lang || defalutLocale || 'en',
|
|
||||||
granularity: matchWholeWords ? 'word' : 'grapheme',
|
|
||||||
sensitivity: matchDiacritics && matchCase ? 'variant'
|
|
||||||
: matchDiacritics && !matchCase ? 'accent'
|
|
||||||
: !matchDiacritics && matchCase ? 'case'
|
|
||||||
: 'base',
|
|
||||||
})) {
|
|
||||||
const { startIndex, startOffset, endIndex, endOffset } = result.range
|
|
||||||
result.range = makeRange(startIndex, startOffset, endIndex, endOffset)
|
|
||||||
yield result
|
|
||||||
}
|
|
||||||
})
|
|
||||||
for (const result of iter) yield result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
const walkRange = (range, walker) => {
|
|
||||||
const nodes = []
|
|
||||||
for (let node = walker.currentNode; node; node = walker.nextNode()) {
|
|
||||||
const compare = range.comparePoint(node, 0)
|
|
||||||
if (compare === 0) nodes.push(node)
|
|
||||||
else if (compare > 0) break
|
|
||||||
}
|
|
||||||
return nodes
|
|
||||||
}
|
|
||||||
|
|
||||||
const walkDocument = (_, walker) => {
|
|
||||||
const nodes = []
|
|
||||||
for (let node = walker.nextNode(); node; node = walker.nextNode())
|
|
||||||
nodes.push(node)
|
|
||||||
return nodes
|
|
||||||
}
|
|
||||||
|
|
||||||
const filter = NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT
|
|
||||||
| NodeFilter.SHOW_CDATA_SECTION
|
|
||||||
|
|
||||||
const acceptNode = node => {
|
|
||||||
if (node.nodeType === 1) {
|
|
||||||
const name = node.tagName.toLowerCase()
|
|
||||||
if (name === 'script' || name === 'style') return NodeFilter.FILTER_REJECT
|
|
||||||
|
|
||||||
// Skip translation elements to preserve CFI calculations
|
|
||||||
if (node.classList && node.classList.contains('translated-text')) {
|
|
||||||
return NodeFilter.FILTER_REJECT
|
|
||||||
}
|
|
||||||
|
|
||||||
return NodeFilter.FILTER_SKIP
|
|
||||||
}
|
|
||||||
return NodeFilter.FILTER_ACCEPT
|
|
||||||
}
|
|
||||||
|
|
||||||
export const textWalker = function* (x, func) {
|
|
||||||
const root = x.commonAncestorContainer ?? x.body ?? x
|
|
||||||
const walker = document.createTreeWalker(root, filter, { acceptNode })
|
|
||||||
const walk = x.commonAncestorContainer ? walkRange : walkDocument
|
|
||||||
const nodes = walk(x, walker)
|
|
||||||
const strs = nodes.map(node => node.nodeValue)
|
|
||||||
const makeRange = (startIndex, startOffset, endIndex, endOffset) => {
|
|
||||||
const range = document.createRange()
|
|
||||||
range.setStart(nodes[startIndex], startOffset)
|
|
||||||
range.setEnd(nodes[endIndex], endOffset)
|
|
||||||
return range
|
|
||||||
}
|
|
||||||
for (const match of func(strs, makeRange)) yield match
|
|
||||||
}
|
|
||||||
@@ -1,359 +0,0 @@
|
|||||||
// Translation modes
|
|
||||||
export const TranslationMode = {
|
|
||||||
OFF: 'off',
|
|
||||||
TRANSLATION_ONLY: 'translation-only',
|
|
||||||
ORIGINAL_ONLY: 'original-only',
|
|
||||||
BILINGUAL: 'bilingual'
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make TranslationMode globally available for debugging
|
|
||||||
if (typeof window !== 'undefined') {
|
|
||||||
window.TranslationMode = TranslationMode
|
|
||||||
}
|
|
||||||
|
|
||||||
// Translation function that calls Flutter's translation service
|
|
||||||
const translate = async (text) => {
|
|
||||||
try {
|
|
||||||
// Call Flutter's translation handler
|
|
||||||
const result = await window.flutter_inappwebview.callHandler('translateText', text)
|
|
||||||
return result || `Translation failed: ${text}`
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Translation failed:', error)
|
|
||||||
return `Translation error: ${text}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Translator {
|
|
||||||
#translationMode = TranslationMode.OFF
|
|
||||||
observedElements = new Set()
|
|
||||||
#translatedElements = new WeakMap()
|
|
||||||
#observer = null
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.#initializeObserver()
|
|
||||||
}
|
|
||||||
|
|
||||||
#initializeObserver() {
|
|
||||||
this.#observer = new IntersectionObserver(
|
|
||||||
(entries) => {
|
|
||||||
// console.log(`IntersectionObserver triggered with ${entries.length} entries`)
|
|
||||||
entries.forEach(entry => {
|
|
||||||
if (entry.isIntersecting) {
|
|
||||||
// console.log('Element intersecting, translating:', entry.target.tagName, entry.target.textContent?.substring(0, 30))
|
|
||||||
this.#translateElement(entry.target).catch(error =>
|
|
||||||
console.warn('Translation failed in observer:', error)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{
|
|
||||||
rootMargin: '1280px',
|
|
||||||
threshold: 0
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async setTranslationMode(mode) {
|
|
||||||
if (!Object.values(TranslationMode).includes(mode)) {
|
|
||||||
console.warn(`Invalid translation mode: ${mode}`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const oldMode = this.#translationMode
|
|
||||||
this.#translationMode = mode
|
|
||||||
|
|
||||||
if (oldMode !== mode) {
|
|
||||||
// console.log(`Translation mode changed from ${oldMode} to ${mode}`)
|
|
||||||
|
|
||||||
if (mode === TranslationMode.OFF) {
|
|
||||||
// Turn off translation
|
|
||||||
this.#updateTranslationDisplay()
|
|
||||||
} else if (oldMode === TranslationMode.OFF) {
|
|
||||||
// Turn on translation - force translate visible elements and wait for completion
|
|
||||||
await this.#forceTranslateVisibleElements()
|
|
||||||
} else {
|
|
||||||
// Just update display mode
|
|
||||||
this.#updateTranslationDisplay()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Re-render annotations after translation mode change (and after translation completion)
|
|
||||||
if (window.reader && window.reader.annotationsByValue) {
|
|
||||||
const existingAnnotations = Array.from(window.reader.annotationsByValue.values())
|
|
||||||
if (existingAnnotations.length > 0) {
|
|
||||||
// console.log('Re-rendering annotations after translation mode change:', existingAnnotations.length)
|
|
||||||
window.renderAnnotations(existingAnnotations)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getTranslationMode() {
|
|
||||||
return this.#translationMode
|
|
||||||
}
|
|
||||||
|
|
||||||
observeDocument(doc) {
|
|
||||||
// console.log('Observing document for translation, doc:', doc)
|
|
||||||
if (!doc) {
|
|
||||||
console.warn('No document provided to observeDocument')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const textElements = this.#walkTextNodes(doc.body || doc.documentElement)
|
|
||||||
// console.log(`Found ${textElements.length} text elements to observe`)
|
|
||||||
|
|
||||||
textElements.forEach(element => {
|
|
||||||
if (!this.observedElements.has(element)) {
|
|
||||||
this.#observer.observe(element)
|
|
||||||
this.observedElements.add(element)
|
|
||||||
// console.log('Added element to observer:', element.tagName, element.textContent?.substring(0, 50))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// console.log(`Total observed elements: ${this.observedElements.size}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
clearTranslations() {
|
|
||||||
// Remove all translation elements and restore original content
|
|
||||||
this.observedElements.forEach(element => {
|
|
||||||
const translationElements = element.querySelectorAll('.translated-text')
|
|
||||||
translationElements.forEach(trans => trans.remove())
|
|
||||||
|
|
||||||
// Restore original text if hidden
|
|
||||||
this.#restoreOriginalText(element)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Clear observer
|
|
||||||
this.#observer.disconnect()
|
|
||||||
this.observedElements.clear()
|
|
||||||
this.#translatedElements = new WeakMap()
|
|
||||||
|
|
||||||
// Reinitialize observer
|
|
||||||
this.#initializeObserver()
|
|
||||||
}
|
|
||||||
|
|
||||||
#walkTextNodes(root, rejectTags = ['pre', 'code', 'math', 'style', 'script']) {
|
|
||||||
const elements = []
|
|
||||||
|
|
||||||
const walk = (node, depth = 0) => {
|
|
||||||
if (depth > 15) return
|
|
||||||
|
|
||||||
const children = Array.from(node.children || [])
|
|
||||||
for (const child of children) {
|
|
||||||
if (rejectTags.includes(child.tagName.toLowerCase())) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip translation elements
|
|
||||||
if (child.classList.contains('translated-text')) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
const hasDirectText = Array.from(child.childNodes).some(node => {
|
|
||||||
if (node.nodeType === Node.TEXT_NODE && node.textContent?.trim()) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if (node.nodeType === Node.ELEMENT_NODE && node.tagName === 'SPAN') {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
|
|
||||||
if (child.children.length === 0 && child.textContent?.trim()) {
|
|
||||||
elements.push(child)
|
|
||||||
} else if (hasDirectText) {
|
|
||||||
elements.push(child)
|
|
||||||
} else if (child.children.length > 0) {
|
|
||||||
walk(child, depth + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
walk(root)
|
|
||||||
return elements
|
|
||||||
}
|
|
||||||
|
|
||||||
async #translateElement(element) {
|
|
||||||
if (this.#translationMode === TranslationMode.OFF) return
|
|
||||||
if (this.#translatedElements.has(element)) return
|
|
||||||
|
|
||||||
const text = element.innerText?.trim()
|
|
||||||
if (!text) return
|
|
||||||
|
|
||||||
try {
|
|
||||||
const translatedText = await translate(text)
|
|
||||||
|
|
||||||
// Mark as translated to prevent re-processing
|
|
||||||
this.#translatedElements.set(element, {
|
|
||||||
originalText: text,
|
|
||||||
translatedText: translatedText
|
|
||||||
})
|
|
||||||
|
|
||||||
this.#applyTranslation(element, translatedText)
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('Translation failed:', error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#applyTranslation(element, translatedText) {
|
|
||||||
// Remove existing translation if any
|
|
||||||
const existingTranslation = element.querySelector('.translated-text')
|
|
||||||
if (existingTranslation) {
|
|
||||||
existingTranslation.remove()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create translation wrapper
|
|
||||||
const wrapper = document.createElement('span')
|
|
||||||
wrapper.className = 'translated-text'
|
|
||||||
wrapper.setAttribute('data-translation-mark', '1')
|
|
||||||
wrapper.style.display = 'block'
|
|
||||||
// wrapper.style.fontSize = '0.9em'
|
|
||||||
// wrapper.style.color = '#666'
|
|
||||||
// wrapper.style.fontStyle = 'italic'
|
|
||||||
wrapper.style.marginTop = '0.2em'
|
|
||||||
wrapper.textContent = translatedText
|
|
||||||
|
|
||||||
// Apply based on current mode
|
|
||||||
this.#updateElementDisplay(element, wrapper)
|
|
||||||
|
|
||||||
element.appendChild(wrapper)
|
|
||||||
}
|
|
||||||
|
|
||||||
#updateElementDisplay(element, translationWrapper) {
|
|
||||||
const data = this.#translatedElements.get(element)
|
|
||||||
if (!data) return
|
|
||||||
|
|
||||||
switch (this.#translationMode) {
|
|
||||||
case TranslationMode.TRANSLATION_ONLY:
|
|
||||||
this.#hideOriginalText(element)
|
|
||||||
translationWrapper.style.display = 'block'
|
|
||||||
break
|
|
||||||
|
|
||||||
case TranslationMode.ORIGINAL_ONLY:
|
|
||||||
this.#restoreOriginalText(element)
|
|
||||||
translationWrapper.style.display = 'none'
|
|
||||||
break
|
|
||||||
|
|
||||||
case TranslationMode.BILINGUAL:
|
|
||||||
this.#restoreOriginalText(element)
|
|
||||||
translationWrapper.style.display = 'block'
|
|
||||||
break
|
|
||||||
|
|
||||||
case TranslationMode.OFF:
|
|
||||||
default:
|
|
||||||
this.#restoreOriginalText(element)
|
|
||||||
translationWrapper.style.display = 'none'
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#hideOriginalText(element) {
|
|
||||||
// Use CSS to hide original content instead of removing DOM nodes
|
|
||||||
if (!element.hasAttribute('data-original-visibility')) {
|
|
||||||
element.setAttribute('data-original-visibility', 'hidden')
|
|
||||||
|
|
||||||
// Hide all child nodes except translation elements using CSS
|
|
||||||
Array.from(element.childNodes).forEach(node => {
|
|
||||||
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
||||||
const el = node
|
|
||||||
if (!el.classList || !el.classList.contains('translated-text')) {
|
|
||||||
// Store and hide using CSS
|
|
||||||
if (!el.hasAttribute('data-original-display')) {
|
|
||||||
el.setAttribute('data-original-display', el.style.display || 'initial')
|
|
||||||
el.style.display = 'none'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (node.nodeType === Node.TEXT_NODE) {
|
|
||||||
// For text nodes, store content and make invisible
|
|
||||||
if (!node.__originalContent) {
|
|
||||||
node.__originalContent = node.textContent
|
|
||||||
node.textContent = ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark element as having hidden text
|
|
||||||
element.classList.add('translation-source-hidden')
|
|
||||||
}
|
|
||||||
|
|
||||||
#restoreOriginalText(element) {
|
|
||||||
// Restore visibility by reversing the hide operations
|
|
||||||
if (element.hasAttribute('data-original-visibility')) {
|
|
||||||
// Restore all child nodes
|
|
||||||
Array.from(element.childNodes).forEach(node => {
|
|
||||||
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
||||||
const el = node
|
|
||||||
if (!el.classList || !el.classList.contains('translated-text')) {
|
|
||||||
// Restore original display
|
|
||||||
if (el.hasAttribute('data-original-display')) {
|
|
||||||
const originalDisplay = el.getAttribute('data-original-display')
|
|
||||||
el.style.display = originalDisplay === 'initial' ? '' : originalDisplay
|
|
||||||
el.removeAttribute('data-original-display')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (node.nodeType === Node.TEXT_NODE) {
|
|
||||||
// Restore text content
|
|
||||||
if (node.__originalContent !== undefined) {
|
|
||||||
node.textContent = node.__originalContent
|
|
||||||
delete node.__originalContent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
element.removeAttribute('data-original-visibility')
|
|
||||||
}
|
|
||||||
|
|
||||||
element.classList.remove('translation-source-hidden')
|
|
||||||
}
|
|
||||||
|
|
||||||
async #forceTranslateVisibleElements() {
|
|
||||||
// console.log('Force translating visible elements')
|
|
||||||
|
|
||||||
const translationPromises = []
|
|
||||||
|
|
||||||
// Find elements in viewport and translate them immediately
|
|
||||||
this.observedElements.forEach(element => {
|
|
||||||
const rect = element.getBoundingClientRect()
|
|
||||||
const isVisible = rect.top < window.innerHeight && rect.bottom > 0
|
|
||||||
|
|
||||||
if (isVisible && !this.#translatedElements.has(element)) {
|
|
||||||
// console.log('Force translating visible element:', element)
|
|
||||||
const translationPromise = this.#translateElement(element).catch(error => {
|
|
||||||
console.warn('Force translation failed:', error)
|
|
||||||
})
|
|
||||||
translationPromises.push(translationPromise)
|
|
||||||
} else if (isVisible && this.#translatedElements.has(element)) {
|
|
||||||
// Element already translated, just update display
|
|
||||||
const translationWrapper = element.querySelector('.translated-text')
|
|
||||||
if (translationWrapper) {
|
|
||||||
this.#updateElementDisplay(element, translationWrapper)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Wait for all visible translations to complete
|
|
||||||
if (translationPromises.length > 0) {
|
|
||||||
// console.log(`Waiting for ${translationPromises.length} translations to complete`)
|
|
||||||
await Promise.allSettled(translationPromises)
|
|
||||||
// console.log('All visible translations completed')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#updateTranslationDisplay() {
|
|
||||||
// console.log('Updating translation display for mode:', this.#translationMode, 'Elements:', this.observedElements.size)
|
|
||||||
this.observedElements.forEach(element => {
|
|
||||||
const translationWrapper = element.querySelector('.translated-text')
|
|
||||||
if (translationWrapper) {
|
|
||||||
// console.log('Updating display for element with translation:', element)
|
|
||||||
this.#updateElementDisplay(element, translationWrapper)
|
|
||||||
} else {
|
|
||||||
// console.log('No translation wrapper found for element:', element)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy() {
|
|
||||||
this.clearTranslations()
|
|
||||||
this.#observer = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,368 +0,0 @@
|
|||||||
const blockTags = new Set([
|
|
||||||
'article', 'aside', 'audio', 'blockquote', 'caption',
|
|
||||||
'details', 'dialog', 'div', 'dl', 'dt', 'dd',
|
|
||||||
'figure', 'footer', 'form', 'figcaption',
|
|
||||||
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hgroup', 'hr', 'li',
|
|
||||||
'main', 'math', 'nav', 'ol', 'p', 'pre', 'section', 'tr',
|
|
||||||
])
|
|
||||||
|
|
||||||
function rangeIsEmpty(range) {
|
|
||||||
return range.collapsed || range.toString().trim() === ''
|
|
||||||
}
|
|
||||||
|
|
||||||
const quoteChars = new Set(['"', "'", '“', '”', '‘', '’'])
|
|
||||||
|
|
||||||
const isLocalLink = href => {
|
|
||||||
if (!href) return false
|
|
||||||
const trimmed = href.trim()
|
|
||||||
if (!trimmed) return false
|
|
||||||
if (trimmed.startsWith('#')) return true
|
|
||||||
return !/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(trimmed)
|
|
||||||
}
|
|
||||||
|
|
||||||
const shouldSkipTextNode = node => {
|
|
||||||
const parent = node.parentElement
|
|
||||||
if (!parent) return false
|
|
||||||
const anchor = parent.closest('a')
|
|
||||||
if (!anchor) return false
|
|
||||||
return isLocalLink(anchor.getAttribute('href'))
|
|
||||||
}
|
|
||||||
|
|
||||||
const getRangeText = range => {
|
|
||||||
const fragment = range.cloneContents()
|
|
||||||
const walker = document.createTreeWalker(fragment, NodeFilter.SHOW_TEXT)
|
|
||||||
let text = ''
|
|
||||||
for (let node = walker.nextNode(); node; node = walker.nextNode()) {
|
|
||||||
if (shouldSkipTextNode(node)) continue
|
|
||||||
text += node.textContent ?? ''
|
|
||||||
}
|
|
||||||
return text
|
|
||||||
}
|
|
||||||
|
|
||||||
const findBlockAncestor = node => {
|
|
||||||
let el = node.parentElement
|
|
||||||
while (el && !blockTags.has(el.tagName?.toLowerCase?.())) {
|
|
||||||
el = el.parentElement
|
|
||||||
}
|
|
||||||
return el ?? node.ownerDocument?.body ?? null
|
|
||||||
}
|
|
||||||
|
|
||||||
const isSentenceTerminator = (char, nextChar) => {
|
|
||||||
if (char === '.') {
|
|
||||||
if (!nextChar) return true
|
|
||||||
if (quoteChars.has(nextChar)) return true
|
|
||||||
if (/\s/.test(nextChar)) return true
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return char === '!' || char === '?' || char === '。' || char === '!' || char === '?'
|
|
||||||
}
|
|
||||||
|
|
||||||
const advancePastQuotes = (text, index) => {
|
|
||||||
let end = index
|
|
||||||
while (end < text.length && quoteChars.has(text[end])) end++
|
|
||||||
return end
|
|
||||||
}
|
|
||||||
|
|
||||||
function* getBlocks(doc) {
|
|
||||||
const walker = doc.createTreeWalker(doc.body, NodeFilter.SHOW_TEXT)
|
|
||||||
let startNode = null
|
|
||||||
let startOffset = 0
|
|
||||||
let currentBlock = null
|
|
||||||
let lastNode = null
|
|
||||||
let lastOffset = 0
|
|
||||||
|
|
||||||
const flushRange = () => {
|
|
||||||
if (!startNode || !lastNode) return null
|
|
||||||
const range = doc.createRange()
|
|
||||||
range.setStart(startNode, startOffset)
|
|
||||||
range.setEnd(lastNode, lastOffset)
|
|
||||||
startNode = null
|
|
||||||
startOffset = 0
|
|
||||||
currentBlock = null
|
|
||||||
lastNode = null
|
|
||||||
lastOffset = 0
|
|
||||||
if (rangeIsEmpty(range)) return null
|
|
||||||
return range
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let node = walker.nextNode(); node; node = walker.nextNode()) {
|
|
||||||
if (!node.textContent) continue
|
|
||||||
if (shouldSkipTextNode(node)) continue
|
|
||||||
|
|
||||||
const block = findBlockAncestor(node)
|
|
||||||
|
|
||||||
if (!startNode) {
|
|
||||||
startNode = node
|
|
||||||
startOffset = 0
|
|
||||||
currentBlock = block
|
|
||||||
} else if (block !== currentBlock) {
|
|
||||||
const range = flushRange()
|
|
||||||
if (range) yield range
|
|
||||||
startNode = node
|
|
||||||
startOffset = 0
|
|
||||||
currentBlock = block
|
|
||||||
}
|
|
||||||
|
|
||||||
const text = node.textContent
|
|
||||||
let index = 0
|
|
||||||
while (index < text.length) {
|
|
||||||
const char = text[index]
|
|
||||||
const nextChar = text[index + 1]
|
|
||||||
if (isSentenceTerminator(char, nextChar)) {
|
|
||||||
const endOffset = advancePastQuotes(text, index + 1)
|
|
||||||
const range = doc.createRange()
|
|
||||||
range.setStart(startNode, startOffset)
|
|
||||||
range.setEnd(node, endOffset)
|
|
||||||
if (!rangeIsEmpty(range)) yield range
|
|
||||||
startNode = node
|
|
||||||
startOffset = endOffset
|
|
||||||
lastNode = node
|
|
||||||
lastOffset = endOffset
|
|
||||||
index = endOffset
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
index += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
lastNode = node
|
|
||||||
lastOffset = text.length
|
|
||||||
|
|
||||||
if (startNode === node && startOffset === text.length) {
|
|
||||||
startNode = null
|
|
||||||
startOffset = 0
|
|
||||||
currentBlock = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const remaining = flushRange()
|
|
||||||
if (remaining) yield remaining
|
|
||||||
}
|
|
||||||
|
|
||||||
class ListIterator {
|
|
||||||
#arr = []
|
|
||||||
#iter
|
|
||||||
#index = -1
|
|
||||||
#f
|
|
||||||
constructor(iter, f = x => x) {
|
|
||||||
this.#iter = iter
|
|
||||||
this.#f = f
|
|
||||||
}
|
|
||||||
current() {
|
|
||||||
if (this.#arr[this.#index]) return this.#f(this.#arr[this.#index])
|
|
||||||
}
|
|
||||||
first() {
|
|
||||||
const newIndex = 0
|
|
||||||
if (this.#arr[newIndex]) {
|
|
||||||
this.#index = newIndex
|
|
||||||
return this.#f(this.#arr[newIndex])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
last() {
|
|
||||||
for (const value of this.#iter) this.#arr.push(value)
|
|
||||||
const newIndex = this.#arr.length - 1
|
|
||||||
if (this.#arr[newIndex]) {
|
|
||||||
this.#index = newIndex
|
|
||||||
return this.#f(this.#arr[newIndex])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
prev() {
|
|
||||||
const newIndex = this.#index - 1
|
|
||||||
if (this.#arr[newIndex]) {
|
|
||||||
this.#index = newIndex
|
|
||||||
return this.#f(this.#arr[newIndex])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
next() {
|
|
||||||
const newIndex = this.#index + 1
|
|
||||||
if (this.#arr[newIndex]) {
|
|
||||||
this.#index = newIndex
|
|
||||||
return this.#f(this.#arr[newIndex])
|
|
||||||
}
|
|
||||||
while (true) {
|
|
||||||
const { done, value } = this.#iter.next()
|
|
||||||
if (done) break
|
|
||||||
this.#arr.push(value)
|
|
||||||
if (this.#arr[newIndex]) {
|
|
||||||
this.#index = newIndex
|
|
||||||
return this.#f(this.#arr[newIndex])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#ensure(index) {
|
|
||||||
while (this.#arr[index] == null) {
|
|
||||||
const { done, value } = this.#iter.next()
|
|
||||||
if (done) break
|
|
||||||
this.#arr.push(value)
|
|
||||||
if (this.#arr.length - 1 >= index) break
|
|
||||||
}
|
|
||||||
return this.#arr[index]
|
|
||||||
}
|
|
||||||
prepare() {
|
|
||||||
const newIndex = this.#index + 1
|
|
||||||
if (this.#arr[newIndex]) return this.#f(this.#arr[newIndex])
|
|
||||||
while (true) {
|
|
||||||
const { done, value } = this.#iter.next()
|
|
||||||
if (done) break
|
|
||||||
this.#arr.push(value)
|
|
||||||
if (this.#arr[newIndex]) return this.#f(this.#arr[newIndex])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
peek(count = 1, offset = 1) {
|
|
||||||
if (count <= 0) return []
|
|
||||||
const startIndex = Math.max(this.#index + offset, 0)
|
|
||||||
const results = []
|
|
||||||
const endIndex = startIndex + count
|
|
||||||
for (let idx = startIndex; idx < endIndex; idx++) {
|
|
||||||
const value = this.#arr[idx] ?? this.#ensure(idx)
|
|
||||||
if (!value) break
|
|
||||||
results.push(this.#f(value))
|
|
||||||
}
|
|
||||||
return results
|
|
||||||
}
|
|
||||||
find(f) {
|
|
||||||
const index = this.#arr.findIndex(x => f(x))
|
|
||||||
if (index > -1) {
|
|
||||||
this.#index = index
|
|
||||||
return this.#f(this.#arr[index])
|
|
||||||
}
|
|
||||||
while (true) {
|
|
||||||
const { done, value } = this.#iter.next()
|
|
||||||
if (done) break
|
|
||||||
this.#arr.push(value)
|
|
||||||
if (f(value)) {
|
|
||||||
this.#index = this.#arr.length - 1
|
|
||||||
return this.#f(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class TTS {
|
|
||||||
#list
|
|
||||||
#lastMark
|
|
||||||
#getCfi
|
|
||||||
constructor(doc, textWalker, highlight, getCfi) {
|
|
||||||
this.doc = doc
|
|
||||||
this.highlight = highlight
|
|
||||||
this.#getCfi = getCfi
|
|
||||||
this.#list = new ListIterator(getBlocks(doc), range => {
|
|
||||||
return [getRangeText(range), range]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#getText(text, getNode) {
|
|
||||||
if (!text) return ''
|
|
||||||
if (!getNode) return text
|
|
||||||
const tempElement = document.createElement('div')
|
|
||||||
tempElement.innerHTML = text
|
|
||||||
let node = getNode(tempElement)?.previousSibling
|
|
||||||
while (node) {
|
|
||||||
const next = node.previousSibling ?? node.parentNode?.previousSibling
|
|
||||||
node.parentNode.removeChild(node)
|
|
||||||
node = next
|
|
||||||
}
|
|
||||||
return tempElement.textContent
|
|
||||||
}
|
|
||||||
|
|
||||||
#ensureCurrentEntry() {
|
|
||||||
const current = this.#list.current()
|
|
||||||
if (current) return current
|
|
||||||
return this.#list.first() ?? this.#list.next()
|
|
||||||
}
|
|
||||||
|
|
||||||
#resultFrom(entry, { highlight = false } = {}) {
|
|
||||||
if (!entry) return null
|
|
||||||
const [text, range] = entry
|
|
||||||
if (!text || !range) return null
|
|
||||||
const plainText = this.#getText(text)
|
|
||||||
let cfi = null
|
|
||||||
if (highlight && this.highlight && range.cloneRange) {
|
|
||||||
cfi = this.highlight(range.cloneRange()) ?? null
|
|
||||||
}
|
|
||||||
if (!cfi && this.#getCfi && range.cloneRange) {
|
|
||||||
cfi = this.#getCfi(range.cloneRange())
|
|
||||||
}
|
|
||||||
return { text: plainText, cfi }
|
|
||||||
}
|
|
||||||
|
|
||||||
start() {
|
|
||||||
this.#lastMark = null
|
|
||||||
const entry = this.#list.first()
|
|
||||||
if (!entry) return this.next()
|
|
||||||
return this.#resultFrom(entry, { highlight: true })?.text
|
|
||||||
}
|
|
||||||
|
|
||||||
end() {
|
|
||||||
this.#lastMark = null
|
|
||||||
const entry = this.#list.last()
|
|
||||||
if (!entry) return this.next()
|
|
||||||
return this.#resultFrom(entry, { highlight: true })?.text
|
|
||||||
}
|
|
||||||
|
|
||||||
resume() {
|
|
||||||
const entry = this.#list.current()
|
|
||||||
if (!entry) return this.next()
|
|
||||||
return this.#resultFrom(entry)?.text
|
|
||||||
}
|
|
||||||
|
|
||||||
prev(paused) {
|
|
||||||
this.#lastMark = null
|
|
||||||
const entry = this.#list.prev()
|
|
||||||
if (paused && entry?.[1]) this.highlight(entry[1].cloneRange())
|
|
||||||
return this.#resultFrom(entry)?.text
|
|
||||||
}
|
|
||||||
|
|
||||||
next(paused) {
|
|
||||||
this.#lastMark = null
|
|
||||||
const entry = this.#list.next()
|
|
||||||
if (paused && entry?.[1]) this.highlight(entry[1].cloneRange())
|
|
||||||
return this.#resultFrom(entry)?.text
|
|
||||||
}
|
|
||||||
|
|
||||||
// get next text without moving the iterator
|
|
||||||
prepare() {
|
|
||||||
const entry = this.#list.prepare()
|
|
||||||
return this.#resultFrom(entry)?.text
|
|
||||||
}
|
|
||||||
|
|
||||||
from(range) {
|
|
||||||
this.#lastMark = null
|
|
||||||
const entry = this.#list.find(range_ =>
|
|
||||||
range.compareBoundaryPoints(Range.END_TO_START, range_) <= 0)
|
|
||||||
if (entry?.[1]) this.highlight(entry[1].cloneRange())
|
|
||||||
return this.#resultFrom(entry)?.text
|
|
||||||
}
|
|
||||||
|
|
||||||
currentDetail() {
|
|
||||||
const entry = this.#ensureCurrentEntry()
|
|
||||||
return this.#resultFrom(entry)
|
|
||||||
}
|
|
||||||
|
|
||||||
collectDetails(count = 1, { includeCurrent = false, offset = 1 } = {}) {
|
|
||||||
if (!Number.isFinite(count) || count <= 0) return []
|
|
||||||
const details = []
|
|
||||||
if (includeCurrent) {
|
|
||||||
const entry = this.#ensureCurrentEntry()
|
|
||||||
const detail = this.#resultFrom(entry)
|
|
||||||
if (detail) details.push(detail)
|
|
||||||
}
|
|
||||||
const needed = count - details.length
|
|
||||||
if (needed <= 0) return details
|
|
||||||
const entries = this.#list.peek(needed, offset)
|
|
||||||
for (const entry of entries) {
|
|
||||||
const detail = this.#resultFrom(entry)
|
|
||||||
if (detail) details.push(detail)
|
|
||||||
}
|
|
||||||
return details
|
|
||||||
}
|
|
||||||
|
|
||||||
highlightCfi(cfi) {
|
|
||||||
if (!cfi) return null
|
|
||||||
const entry = this.#list.find(range => {
|
|
||||||
const candidate = this.#getCfi?.(range.cloneRange?.())
|
|
||||||
return candidate === cfi
|
|
||||||
})
|
|
||||||
if (!entry) return null
|
|
||||||
return this.#resultFrom(entry, { highlight: true })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
// URI Template: https://datatracker.ietf.org/doc/html/rfc6570
|
|
||||||
|
|
||||||
const regex = /{([+#./;?&])?([^}]+?)}/g
|
|
||||||
const varspecRegex = /(.+?)(\*|:[1-9]\d{0,3})?$/
|
|
||||||
|
|
||||||
const table = {
|
|
||||||
undefined: { first: '', sep: ',' },
|
|
||||||
'+': { first: '', sep: ',', allowReserved: true },
|
|
||||||
'.': { first: '.', sep: '.' },
|
|
||||||
'/': { first: '/', sep: '/' },
|
|
||||||
';': { first: ';', sep: ';', named: true, ifemp: '' },
|
|
||||||
'?': { first: '?', sep: '&', named: true, ifemp: '=' },
|
|
||||||
'&': { first: '&', sep: '&', named: true, ifemp: '=' },
|
|
||||||
'#': { first: '&', sep: '&', allowReserved: true },
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2.4.1 Prefix Values, "Note that this numbering is in characters, not octets"
|
|
||||||
const prefix = (maxLength, str) => {
|
|
||||||
let result = ''
|
|
||||||
for (const char of str) {
|
|
||||||
const newResult = char
|
|
||||||
if (newResult.length > maxLength) return result
|
|
||||||
else result = newResult
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
export const replace = (str, map) => str.replace(regex, (_, operator, variableList) => {
|
|
||||||
const { first, sep, named, ifemp, allowReserved } = table[operator]
|
|
||||||
// TODO: this isn't spec compliant
|
|
||||||
const encode = allowReserved ? encodeURI : encodeURIComponent
|
|
||||||
const values = variableList.split(',').map(varspec => {
|
|
||||||
const match = varspec.match(varspecRegex)
|
|
||||||
if (!match) return
|
|
||||||
const [, name, modifier] = match
|
|
||||||
let value = map.get(name)
|
|
||||||
if (modifier?.startsWith(':')) {
|
|
||||||
const maxLength = parseInt(modifier.slice(1))
|
|
||||||
value = prefix(maxLength, value)
|
|
||||||
}
|
|
||||||
return [name, value ? encode(value) : null]
|
|
||||||
})
|
|
||||||
if (!values.filter(([, value]) => value).length) return ''
|
|
||||||
return first + values
|
|
||||||
.map(([name, value]) => value
|
|
||||||
? (named ? name + (value ? '=' + value : ifemp) : value) : '')
|
|
||||||
.filter(x => x).join(sep)
|
|
||||||
})
|
|
||||||
|
|
||||||
export const getVariables = str => new Set(Array.from(str.matchAll(regex),
|
|
||||||
([,, variableList]) => variableList.split(',')
|
|
||||||
.map(varspec => varspec.match(varspecRegex)?.[1])).flat())
|
|
||||||
1
assets/foliate-js/src/vendor/fflate.js
vendored
1
assets/foliate-js/src/vendor/fflate.js
vendored
@@ -1 +0,0 @@
|
|||||||
var r=Uint8Array,e=Uint16Array,a=Uint32Array,n=new r([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0]),t=new r([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0]),i=new r([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),f=function(r,n){for(var t=new e(31),i=0;i<31;++i)t[i]=n+=1<<r[i-1];var f=new a(t[30]);for(i=1;i<30;++i)for(var o=t[i];o<t[i+1];++o)f[o]=o-t[i]<<5|i;return[t,f]},o=f(n,2),v=o[0],l=o[1];v[28]=258,l[258]=28;for(var u=f(t,0)[0],c=new e(32768),d=0;d<32768;++d){var s=(43690&d)>>>1|(21845&d)<<1;s=(61680&(s=(52428&s)>>>2|(13107&s)<<2))>>>4|(3855&s)<<4,c[d]=((65280&s)>>>8|(255&s)<<8)>>>1}var w=function(r,a,n){for(var t=r.length,i=0,f=new e(a);i<t;++i)r[i]&&++f[r[i]-1];var o,v=new e(a);for(i=0;i<a;++i)v[i]=v[i-1]+f[i-1]<<1;if(n){o=new e(1<<a);var l=15-a;for(i=0;i<t;++i)if(r[i])for(var u=i<<4|r[i],d=a-r[i],s=v[r[i]-1]++<<d,w=s|(1<<d)-1;s<=w;++s)o[c[s]>>>l]=u}else for(o=new e(t),i=0;i<t;++i)r[i]&&(o[i]=c[v[r[i]-1]++]>>>15-r[i]);return o},b=new r(288);for(d=0;d<144;++d)b[d]=8;for(d=144;d<256;++d)b[d]=9;for(d=256;d<280;++d)b[d]=7;for(d=280;d<288;++d)b[d]=8;var h=new r(32);for(d=0;d<32;++d)h[d]=5;var E=w(b,9,1),p=w(h,5,1),g=function(r){for(var e=r[0],a=1;a<r.length;++a)r[a]>e&&(e=r[a]);return e},y=function(r,e,a){var n=e/8|0;return(r[n]|r[n+1]<<8)>>(7&e)&a},k=function(r,e){var a=e/8|0;return(r[a]|r[a+1]<<8|r[a+2]<<16)>>(7&e)},T=["unexpected EOF","invalid block type","invalid length/literal","invalid distance","stream finished","no stream handler",,"no callback","invalid UTF-8 data","extra field too long","date not in range 1980-2099","filename too long","stream finishing","invalid zip data"],m=function(r,e,a){var n=new Error(e||T[r]);if(n.code=r,Error.captureStackTrace&&Error.captureStackTrace(n,m),!a)throw n;return n},x=function(f,o,l){var c=f.length;if(!c||l&&l.f&&!l.l)return o||new r(0);var d=!o||l,s=!l||l.i;l||(l={}),o||(o=new r(3*c));var b=function(e){var a=o.length;if(e>a){var n=new r(Math.max(2*a,e));n.set(o),o=n}},h=l.f||0,T=l.p||0,x=l.b||0,S=l.l,U=l.d,_=l.m,z=l.n,A=8*c;do{if(!S){h=y(f,T,1);var M=y(f,T+1,3);if(T+=3,!M){var B=f[(C=4+((T+7)/8|0))-4]|f[C-3]<<8,D=C+B;if(D>c){s&&m(0);break}d&&b(x+B),o.set(f.subarray(C,D),x),l.b=x+=B,l.p=T=8*D,l.f=h;continue}if(1==M)S=E,U=p,_=9,z=5;else if(2==M){var F=y(f,T,31)+257,L=y(f,T+10,15)+4,N=F+y(f,T+5,31)+1;T+=14;for(var P=new r(N),R=new r(19),Y=0;Y<L;++Y)R[i[Y]]=y(f,T+3*Y,7);T+=3*L;var O=g(R),j=(1<<O)-1,q=w(R,O,1);for(Y=0;Y<N;){var C,G=q[y(f,T,j)];if(T+=15&G,(C=G>>>4)<16)P[Y++]=C;else{var H=0,I=0;for(16==C?(I=3+y(f,T,3),T+=2,H=P[Y-1]):17==C?(I=3+y(f,T,7),T+=3):18==C&&(I=11+y(f,T,127),T+=7);I--;)P[Y++]=H}}var J=P.subarray(0,F),K=P.subarray(F);_=g(J),z=g(K),S=w(J,_,1),U=w(K,z,1)}else m(1);if(T>A){s&&m(0);break}}d&&b(x+131072);for(var Q=(1<<_)-1,V=(1<<z)-1,W=T;;W=T){var X=(H=S[k(f,T)&Q])>>>4;if((T+=15&H)>A){s&&m(0);break}if(H||m(2),X<256)o[x++]=X;else{if(256==X){W=T,S=null;break}var Z=X-254;if(X>264){var $=n[Y=X-257];Z=y(f,T,(1<<$)-1)+v[Y],T+=$}var rr=U[k(f,T)&V],er=rr>>>4;rr||m(3),T+=15&rr;K=u[er];if(er>3){$=t[er];K+=k(f,T)&(1<<$)-1,T+=$}if(T>A){s&&m(0);break}d&&b(x+131072);for(var ar=x+Z;x<ar;x+=4)o[x]=o[x-K],o[x+1]=o[x+1-K],o[x+2]=o[x+2-K],o[x+3]=o[x+3-K];x=ar}}l.l=S,l.p=W,l.b=x,l.f=h,S&&(h=1,l.m=_,l.d=U,l.n=z)}while(!h);return x==o.length?o:function(n,t,i){(null==t||t<0)&&(t=0),(null==i||i>n.length)&&(i=n.length);var f=new(2==n.BYTES_PER_ELEMENT?e:4==n.BYTES_PER_ELEMENT?a:r)(i-t);return f.set(n.subarray(t,i)),f}(o,0,x)},S=new r(0);function U(r,e){return x(((8!=(15&(a=r)[0])||a[0]>>>4>7||(a[0]<<8|a[1])%31)&&m(6,"invalid zlib data"),32&a[1]&&m(6,"invalid zlib data: preset dictionaries not supported"),r.subarray(2,-4)),e);var a}var _="undefined"!=typeof TextDecoder&&new TextDecoder;try{_.decode(S,{stream:!0}),1}catch(r){}export{U as unzlibSync};
|
|
||||||
18146
assets/foliate-js/src/vendor/pdfjs/pdf.js
vendored
18146
assets/foliate-js/src/vendor/pdfjs/pdf.js
vendored
File diff suppressed because it is too large
Load Diff
58353
assets/foliate-js/src/vendor/pdfjs/pdf.worker.js
vendored
58353
assets/foliate-js/src/vendor/pdfjs/pdf.worker.js
vendored
File diff suppressed because one or more lines are too long
116
assets/foliate-js/src/vendor/prism/README.md
vendored
116
assets/foliate-js/src/vendor/prism/README.md
vendored
@@ -1,116 +0,0 @@
|
|||||||
# Prism.js Code Syntax Highlighting
|
|
||||||
|
|
||||||
This directory contains the Prism.js library for code syntax highlighting in Anx Reader.
|
|
||||||
|
|
||||||
## Directory Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
prism/
|
|
||||||
├── prism-core.min.js # Core Prism.js library
|
|
||||||
├── prism-autoloader.min.js # Automatic language loading plugin
|
|
||||||
├── components/ # Language definition files (62 languages)
|
|
||||||
│ ├── prism-javascript.min.js
|
|
||||||
│ ├── prism-python.min.js
|
|
||||||
│ ├── prism-java.min.js
|
|
||||||
│ └── ... (62 files total)
|
|
||||||
├── themes/ # CSS themes (12 themes)
|
|
||||||
│ ├── prism-default.min.css
|
|
||||||
│ ├── prism-vs-dark.min.css
|
|
||||||
│ ├── prism-one-dark.min.css
|
|
||||||
│ └── ... (12 files total)
|
|
||||||
└── download_components.sh # Script to download additional languages
|
|
||||||
```
|
|
||||||
|
|
||||||
## Supported Languages (62)
|
|
||||||
|
|
||||||
The following languages are included for offline use:
|
|
||||||
|
|
||||||
### Web Development
|
|
||||||
- JavaScript, TypeScript, JSX, TSX
|
|
||||||
- HTML/XML (markup), CSS, Sass, SCSS, Less
|
|
||||||
- PHP, PHP-Extras
|
|
||||||
- JSON, YAML, TOML
|
|
||||||
- Markdown, LaTeX
|
|
||||||
|
|
||||||
### System & Scripting
|
|
||||||
- Bash, Shell Session, PowerShell, Batch
|
|
||||||
- Python, Ruby, Perl, Lua, R
|
|
||||||
|
|
||||||
### Compiled Languages
|
|
||||||
- C, C++, C#, Objective-C
|
|
||||||
- Java, Kotlin, Scala, Groovy
|
|
||||||
- Go, Rust, Swift, Dart
|
|
||||||
|
|
||||||
### Functional Languages
|
|
||||||
- Haskell, Elixir, Erlang, Julia
|
|
||||||
|
|
||||||
### Database
|
|
||||||
- SQL, PL/SQL, MongoDB
|
|
||||||
|
|
||||||
### Configuration & DevOps
|
|
||||||
- Docker, Git, Diff
|
|
||||||
- Nginx, Makefile
|
|
||||||
- INI, Properties
|
|
||||||
|
|
||||||
### Other
|
|
||||||
- GraphQL, Protobuf
|
|
||||||
- Regex, HTTP
|
|
||||||
- Visual Basic, VB.NET
|
|
||||||
|
|
||||||
## Themes (12)
|
|
||||||
|
|
||||||
### Light Themes (4)
|
|
||||||
1. **Default** - Classic Prism theme
|
|
||||||
2. **GitHub** - GitHub-style highlighting
|
|
||||||
3. **One Light** - Atom One Light theme
|
|
||||||
4. **Material Light** - Material Design light theme
|
|
||||||
|
|
||||||
### Dark Themes (8)
|
|
||||||
1. **VS Dark** - Visual Studio Code dark theme
|
|
||||||
2. **One Dark** - Atom One Dark theme
|
|
||||||
3. **Dracula** - Popular Dracula theme
|
|
||||||
4. **Material Dark** - Material Design dark theme
|
|
||||||
5. **Nord** - Nord color palette
|
|
||||||
6. **Night Owl** - Night Owl theme
|
|
||||||
7. **Solarized Dark** - Solarized dark variant
|
|
||||||
8. **Atom Dark** - Atom editor dark theme
|
|
||||||
|
|
||||||
## How It Works
|
|
||||||
|
|
||||||
1. **Automatic Detection**: When a code block is detected, Prism.js automatically identifies the language
|
|
||||||
2. **Local Loading**: The autoloader loads the required language file from `components/` folder
|
|
||||||
3. **Offline Support**: All 62 language files are pre-downloaded for offline use
|
|
||||||
4. **No Network Required**: Everything works without internet connection
|
|
||||||
|
|
||||||
## Adding More Languages
|
|
||||||
|
|
||||||
If you need additional languages not included in the default set:
|
|
||||||
|
|
||||||
1. Visit: https://github.com/PrismJS/prism/tree/master/components
|
|
||||||
2. Download the required `prism-{language}.min.js` file
|
|
||||||
3. Place it in the `components/` folder
|
|
||||||
|
|
||||||
Or use the included download script:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd assets/foliate-js/src/vendor/prism
|
|
||||||
./download_components.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
## Version
|
|
||||||
|
|
||||||
Current version: **Prism.js 1.29.0**
|
|
||||||
|
|
||||||
## Total Size
|
|
||||||
|
|
||||||
- Core + Autoloader: ~13 KB
|
|
||||||
- Themes (12 files): ~30 KB
|
|
||||||
- Languages (62 files): ~272 KB
|
|
||||||
- **Total: ~315 KB**
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
- Official Website: https://prismjs.com/
|
|
||||||
- GitHub: https://github.com/PrismJS/prism
|
|
||||||
- Documentation: https://prismjs.com/docs/
|
|
||||||
- Language List: https://prismjs.com/#supported-languages
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
|||||||
Prism.languages.basic={comment:{pattern:/(?:!|REM\b).+/i,inside:{keyword:/^REM/i}},string:{pattern:/"(?:""|[!#$%&'()*,\/:;<=>?^\w +\-.])*"/,greedy:!0},number:/(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:E[+-]?\d+)?/i,keyword:/\b(?:AS|BEEP|BLOAD|BSAVE|CALL(?: ABSOLUTE)?|CASE|CHAIN|CHDIR|CLEAR|CLOSE|CLS|COM|COMMON|CONST|DATA|DECLARE|DEF(?: FN| SEG|DBL|INT|LNG|SNG|STR)|DIM|DO|DOUBLE|ELSE|ELSEIF|END|ENVIRON|ERASE|ERROR|EXIT|FIELD|FILES|FOR|FUNCTION|GET|GOSUB|GOTO|IF|INPUT|INTEGER|IOCTL|KEY|KILL|LINE INPUT|LOCATE|LOCK|LONG|LOOP|LSET|MKDIR|NAME|NEXT|OFF|ON(?: COM| ERROR| KEY| TIMER)?|OPEN|OPTION BASE|OUT|POKE|PUT|READ|REDIM|REM|RESTORE|RESUME|RETURN|RMDIR|RSET|RUN|SELECT CASE|SHARED|SHELL|SINGLE|SLEEP|STATIC|STEP|STOP|STRING|SUB|SWAP|SYSTEM|THEN|TIMER|TO|TROFF|TRON|TYPE|UNLOCK|UNTIL|USING|VIEW PRINT|WAIT|WEND|WHILE|WRITE)(?:\$|\b)/i,function:/\b(?:ABS|ACCESS|ACOS|ANGLE|AREA|ARITHMETIC|ARRAY|ASIN|ASK|AT|ATN|BASE|BEGIN|BREAK|CAUSE|CEIL|CHR|CLIP|COLLATE|COLOR|CON|COS|COSH|COT|CSC|DATE|DATUM|DEBUG|DECIMAL|DEF|DEG|DEGREES|DELETE|DET|DEVICE|DISPLAY|DOT|ELAPSED|EPS|ERASABLE|EXLINE|EXP|EXTERNAL|EXTYPE|FILETYPE|FIXED|FP|GO|GRAPH|HANDLER|IDN|IMAGE|IN|INT|INTERNAL|IP|IS|KEYED|LBOUND|LCASE|LEFT|LEN|LENGTH|LET|LINE|LINES|LOG|LOG10|LOG2|LTRIM|MARGIN|MAT|MAX|MAXNUM|MID|MIN|MISSING|MOD|NATIVE|NUL|NUMERIC|OF|OPTION|ORD|ORGANIZATION|OUTIN|OUTPUT|PI|POINT|POINTER|POINTS|POS|PRINT|PROGRAM|PROMPT|RAD|RADIANS|RANDOMIZE|RECORD|RECSIZE|RECTYPE|RELATIVE|REMAINDER|REPEAT|REST|RETRY|REWRITE|RIGHT|RND|ROUND|RTRIM|SAME|SEC|SELECT|SEQUENTIAL|SET|SETTER|SGN|SIN|SINH|SIZE|SKIP|SQR|STANDARD|STATUS|STR|STREAM|STYLE|TAB|TAN|TANH|TEMPLATE|TEXT|THERE|TIME|TIMEOUT|TRACE|TRANSFORM|TRUNCATE|UBOUND|UCASE|USE|VAL|VARIABLE|VIEWPORT|WHEN|WINDOW|WITH|ZER|ZONEWIDTH)(?:\$|\b)/i,operator:/<[=>]?|>=?|[+\-*\/^=&]|\b(?:AND|EQV|IMP|NOT|OR|XOR)\b/i,punctuation:/[,;:()]/};
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
!function(e){var r=/%%?[~:\w]+%?|!\S+!/,t={pattern:/\/[a-z?]+(?=[ :]|$):?|-[a-z]\b|--[a-z-]+\b/im,alias:"attr-name",inside:{punctuation:/:/}},n=/"(?:[\\"]"|[^"])*"(?!")/,i=/(?:\b|-)\d+\b/;e.languages.batch={comment:[/^::.*/m,{pattern:/((?:^|[&(])[ \t]*)rem\b(?:[^^&)\r\n]|\^(?:\r\n|[\s\S]))*/im,lookbehind:!0}],label:{pattern:/^:.*/m,alias:"property"},command:[{pattern:/((?:^|[&(])[ \t]*)for(?: \/[a-z?](?:[ :](?:"[^"]*"|[^\s"/]\S*))?)* \S+ in \([^)]+\) do/im,lookbehind:!0,inside:{keyword:/\b(?:do|in)\b|^for\b/i,string:n,parameter:t,variable:r,number:i,punctuation:/[()',]/}},{pattern:/((?:^|[&(])[ \t]*)if(?: \/[a-z?](?:[ :](?:"[^"]*"|[^\s"/]\S*))?)* (?:not )?(?:cmdextversion \d+|defined \w+|errorlevel \d+|exist \S+|(?:"[^"]*"|(?!")(?:(?!==)\S)+)?(?:==| (?:equ|geq|gtr|leq|lss|neq) )(?:"[^"]*"|[^\s"]\S*))/im,lookbehind:!0,inside:{keyword:/\b(?:cmdextversion|defined|errorlevel|exist|not)\b|^if\b/i,string:n,parameter:t,variable:r,number:i,operator:/\^|==|\b(?:equ|geq|gtr|leq|lss|neq)\b/i}},{pattern:/((?:^|[&()])[ \t]*)else\b/im,lookbehind:!0,inside:{keyword:/^else\b/i}},{pattern:/((?:^|[&(])[ \t]*)set(?: \/[a-z](?:[ :](?:"[^"]*"|[^\s"/]\S*))?)* (?:[^^&)\r\n]|\^(?:\r\n|[\s\S]))*/im,lookbehind:!0,inside:{keyword:/^set\b/i,string:n,parameter:t,variable:[r,/\w+(?=(?:[*\/%+\-&^|]|<<|>>)?=)/],number:i,operator:/[*\/%+\-&^|]=?|<<=?|>>=?|[!~_=]/,punctuation:/[()',]/}},{pattern:/((?:^|[&(])[ \t]*@?)\w+\b(?:"(?:[\\"]"|[^"])*"(?!")|[^"^&)\r\n]|\^(?:\r\n|[\s\S]))*/m,lookbehind:!0,inside:{keyword:/^\w+\b/,string:n,parameter:t,label:{pattern:/(^\s*):\S+/m,lookbehind:!0,alias:"property"},variable:r,number:i,operator:/\^/}}],operator:/[&@]/,punctuation:/[()']/}}(Prism);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Prism.languages.c=Prism.languages.extend("clike",{comment:{pattern:/\/\/(?:[^\r\n\\]|\\(?:\r\n?|\n|(?![\r\n])))*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},string:{pattern:/"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/,greedy:!0},"class-name":{pattern:/(\b(?:enum|struct)\s+(?:__attribute__\s*\(\([\s\S]*?\)\)\s*)?)\w+|\b[a-z]\w*_t\b/,lookbehind:!0},keyword:/\b(?:_Alignas|_Alignof|_Atomic|_Bool|_Complex|_Generic|_Imaginary|_Noreturn|_Static_assert|_Thread_local|__attribute__|asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|inline|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|typeof|union|unsigned|void|volatile|while)\b/,function:/\b[a-z_]\w*(?=\s*\()/i,number:/(?:\b0x(?:[\da-f]+(?:\.[\da-f]*)?|\.[\da-f]+)(?:p[+-]?\d+)?|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?)[ful]{0,4}/i,operator:/>>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?/}),Prism.languages.insertBefore("c","string",{char:{pattern:/'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n]){0,32}'/,greedy:!0}}),Prism.languages.insertBefore("c","string",{macro:{pattern:/(^[\t ]*)#\s*[a-z](?:[^\r\n\\/]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|\\(?:\r\n|[\s\S]))*/im,lookbehind:!0,greedy:!0,alias:"property",inside:{string:[{pattern:/^(#\s*include\s*)<[^>]+>/,lookbehind:!0},Prism.languages.c.string],char:Prism.languages.c.char,comment:Prism.languages.c.comment,"macro-name":[{pattern:/(^#\s*define\s+)\w+\b(?!\()/i,lookbehind:!0},{pattern:/(^#\s*define\s+)\w+\b(?=\()/i,lookbehind:!0,alias:"function"}],directive:{pattern:/^(#\s*)[a-z]+/,lookbehind:!0,alias:"keyword"},"directive-hash":/^#/,punctuation:/##|\\(?=[\r\n])/,expression:{pattern:/\S[\s\S]*/,inside:Prism.languages.c}}}}),Prism.languages.insertBefore("c","function",{constant:/\b(?:EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|__DATE__|__FILE__|__LINE__|__TIMESTAMP__|__TIME__|__func__|stderr|stdin|stdout)\b/}),delete Prism.languages.c.boolean;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/,boolean:/\b(?:false|true)\b/,function:/\b\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/};
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
|||||||
!function(e){var t=/\b(?:alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|char8_t|class|co_await|co_return|co_yield|compl|concept|const|const_cast|consteval|constexpr|constinit|continue|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|final|float|for|friend|goto|if|import|inline|int|int16_t|int32_t|int64_t|int8_t|long|module|mutable|namespace|new|noexcept|nullptr|operator|override|private|protected|public|register|reinterpret_cast|requires|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|uint16_t|uint32_t|uint64_t|uint8_t|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/,n="\\b(?!<keyword>)\\w+(?:\\s*\\.\\s*\\w+)*\\b".replace(/<keyword>/g,(function(){return t.source}));e.languages.cpp=e.languages.extend("c",{"class-name":[{pattern:RegExp("(\\b(?:class|concept|enum|struct|typename)\\s+)(?!<keyword>)\\w+".replace(/<keyword>/g,(function(){return t.source}))),lookbehind:!0},/\b[A-Z]\w*(?=\s*::\s*\w+\s*\()/,/\b[A-Z_]\w*(?=\s*::\s*~\w+\s*\()/i,/\b\w+(?=\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>\s*::\s*\w+\s*\()/],keyword:t,number:{pattern:/(?:\b0b[01']+|\b0x(?:[\da-f']+(?:\.[\da-f']*)?|\.[\da-f']+)(?:p[+-]?[\d']+)?|(?:\b[\d']+(?:\.[\d']*)?|\B\.[\d']+)(?:e[+-]?[\d']+)?)[ful]{0,4}/i,greedy:!0},operator:/>>=?|<<=?|->|--|\+\+|&&|\|\||[?:~]|<=>|[-+*/%&|^!=<>]=?|\b(?:and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/,boolean:/\b(?:false|true)\b/}),e.languages.insertBefore("cpp","string",{module:{pattern:RegExp('(\\b(?:import|module)\\s+)(?:"(?:\\\\(?:\r\n|[^])|[^"\\\\\r\n])*"|<[^<>\r\n]*>|'+"<mod-name>(?:\\s*:\\s*<mod-name>)?|:\\s*<mod-name>".replace(/<mod-name>/g,(function(){return n}))+")"),lookbehind:!0,greedy:!0,inside:{string:/^[<"][\s\S]+/,operator:/:/,punctuation:/\./}},"raw-string":{pattern:/R"([^()\\ ]{0,16})\([\s\S]*?\)\1"/,alias:"string",greedy:!0}}),e.languages.insertBefore("cpp","keyword",{"generic-function":{pattern:/\b(?!operator\b)[a-z_]\w*\s*<(?:[^<>]|<[^<>]*>)*>(?=\s*\()/i,inside:{function:/^\w+/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:e.languages.cpp}}}}),e.languages.insertBefore("cpp","operator",{"double-colon":{pattern:/::/,alias:"punctuation"}}),e.languages.insertBefore("cpp","class-name",{"base-clause":{pattern:/(\b(?:class|struct)\s+\w+\s*:\s*)[^;{}"'\s]+(?:\s+[^;{}"'\s]+)*(?=\s*[;{])/,lookbehind:!0,greedy:!0,inside:e.languages.extend("cpp",{})}}),e.languages.insertBefore("inside","double-colon",{"class-name":/\b[a-z_]\w*\b(?!\s*::)/i},e.languages.cpp["base-clause"])}(Prism);
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
|||||||
!function(s){var e=/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/;s.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:RegExp("@[\\w-](?:[^;{\\s\"']|\\s+(?!\\s)|"+e.source+")*?(?:;|(?=\\s*\\{))"),inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+e.source+"|(?:[^\\\\\r\n()\"']|\\\\[^])*)\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+e.source+"$"),alias:"url"}}},selector:{pattern:RegExp("(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|"+e.source+")*(?=\\s*\\{)"),lookbehind:!0},string:{pattern:e,greedy:!0},property:{pattern:/(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,lookbehind:!0},important:/!important\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,lookbehind:!0},punctuation:/[(){};:,]/},s.languages.css.atrule.inside.rest=s.languages.css;var t=s.languages.markup;t&&(t.tag.addInlined("style","css"),t.tag.addAttribute("style","css"))}(Prism);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
!function(e){var a=[/\b(?:async|sync|yield)\*/,/\b(?:abstract|assert|async|await|break|case|catch|class|const|continue|covariant|default|deferred|do|dynamic|else|enum|export|extends|extension|external|factory|final|finally|for|get|hide|if|implements|import|in|interface|library|mixin|new|null|on|operator|part|rethrow|return|set|show|static|super|switch|sync|this|throw|try|typedef|var|void|while|with|yield)\b/],n="(^|[^\\w.])(?:[a-z]\\w*\\s*\\.\\s*)*(?:[A-Z]\\w*\\s*\\.\\s*)*",s={pattern:RegExp(n+"[A-Z](?:[\\d_A-Z]*[a-z]\\w*)?\\b"),lookbehind:!0,inside:{namespace:{pattern:/^[a-z]\w*(?:\s*\.\s*[a-z]\w*)*(?:\s*\.)?/,inside:{punctuation:/\./}}}};e.languages.dart=e.languages.extend("clike",{"class-name":[s,{pattern:RegExp(n+"[A-Z]\\w*(?=\\s+\\w+\\s*[;,=()])"),lookbehind:!0,inside:s.inside}],keyword:a,operator:/\bis!|\b(?:as|is)\b|\+\+|--|&&|\|\||<<=?|>>=?|~(?:\/=?)?|[+\-*\/%&^|=!<>]=?|\?/}),e.languages.insertBefore("dart","string",{"string-literal":{pattern:/r?(?:("""|''')[\s\S]*?\1|(["'])(?:\\.|(?!\2)[^\\\r\n])*\2(?!\2))/,greedy:!0,inside:{interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$(?:\w+|\{(?:[^{}]|\{[^{}]*\})*\})/,lookbehind:!0,inside:{punctuation:/^\$\{?|\}$/,expression:{pattern:/[\s\S]+/,inside:e.languages.dart}}},string:/[\s\S]+/}},string:void 0}),e.languages.insertBefore("dart","class-name",{metadata:{pattern:/@\w+/,alias:"function"}}),e.languages.insertBefore("dart","class-name",{generics:{pattern:/<(?:[\w\s,.&?]|<(?:[\w\s,.&?]|<(?:[\w\s,.&?]|<[\w\s,.&?]*>)*>)*>)*>/,inside:{"class-name":s,keyword:a,punctuation:/[<>(),.:]/,operator:/[?&|]/}}})}(Prism);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
!function(e){e.languages.diff={coord:[/^(?:\*{3}|-{3}|\+{3}).*$/m,/^@@.*@@$/m,/^\d.*$/m]};var n={"deleted-sign":"-","deleted-arrow":"<","inserted-sign":"+","inserted-arrow":">",unchanged:" ",diff:"!"};Object.keys(n).forEach((function(a){var i=n[a],r=[];/^\w+$/.test(a)||r.push(/\w+/.exec(a)[0]),"diff"===a&&r.push("bold"),e.languages.diff[a]={pattern:RegExp("^(?:["+i+"].*(?:\r\n?|\n|(?![\\s\\S])))+","m"),alias:r,inside:{line:{pattern:/(.)(?=[\s\S]).*(?:\r\n?|\n)?/,lookbehind:!0},prefix:{pattern:/[\s\S]/,alias:/\w+/.exec(a)[0]}}}})),Object.defineProperty(e.languages.diff,"PREFIXES",{value:n})}(Prism);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
!function(e){var n="(?:[ \t]+(?![ \t])(?:<SP_BS>)?|<SP_BS>)".replace(/<SP_BS>/g,(function(){return"\\\\[\r\n](?:\\s|\\\\[\r\n]|#.*(?!.))*(?![\\s#]|\\\\[\r\n])"})),r="\"(?:[^\"\\\\\r\n]|\\\\(?:\r\n|[^]))*\"|'(?:[^'\\\\\r\n]|\\\\(?:\r\n|[^]))*'",t="--[\\w-]+=(?:<STR>|(?![\"'])(?:[^\\s\\\\]|\\\\.)+)".replace(/<STR>/g,(function(){return r})),o={pattern:RegExp(r),greedy:!0},i={pattern:/(^[ \t]*)#.*/m,lookbehind:!0,greedy:!0};function a(e,r){return e=e.replace(/<OPT>/g,(function(){return t})).replace(/<SP>/g,(function(){return n})),RegExp(e,r)}e.languages.docker={instruction:{pattern:/(^[ \t]*)(?:ADD|ARG|CMD|COPY|ENTRYPOINT|ENV|EXPOSE|FROM|HEALTHCHECK|LABEL|MAINTAINER|ONBUILD|RUN|SHELL|STOPSIGNAL|USER|VOLUME|WORKDIR)(?=\s)(?:\\.|[^\r\n\\])*(?:\\$(?:\s|#.*$)*(?![\s#])(?:\\.|[^\r\n\\])*)*/im,lookbehind:!0,greedy:!0,inside:{options:{pattern:a("(^(?:ONBUILD<SP>)?\\w+<SP>)<OPT>(?:<SP><OPT>)*","i"),lookbehind:!0,greedy:!0,inside:{property:{pattern:/(^|\s)--[\w-]+/,lookbehind:!0},string:[o,{pattern:/(=)(?!["'])(?:[^\s\\]|\\.)+/,lookbehind:!0}],operator:/\\$/m,punctuation:/=/}},keyword:[{pattern:a("(^(?:ONBUILD<SP>)?HEALTHCHECK<SP>(?:<OPT><SP>)*)(?:CMD|NONE)\\b","i"),lookbehind:!0,greedy:!0},{pattern:a("(^(?:ONBUILD<SP>)?FROM<SP>(?:<OPT><SP>)*(?!--)[^ \t\\\\]+<SP>)AS","i"),lookbehind:!0,greedy:!0},{pattern:a("(^ONBUILD<SP>)\\w+","i"),lookbehind:!0,greedy:!0},{pattern:/^\w+/,greedy:!0}],comment:i,string:o,variable:/\$(?:\w+|\{[^{}"'\\]*\})/,operator:/\\$/m}},comment:i},e.languages.dockerfile=e.languages.docker}(Prism);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Prism.languages.elixir={doc:{pattern:/@(?:doc|moduledoc)\s+(?:("""|''')[\s\S]*?\1|("|')(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2)/,inside:{attribute:/^@\w+/,string:/['"][\s\S]+/}},comment:{pattern:/#.*/,greedy:!0},regex:{pattern:/~[rR](?:("""|''')(?:\\[\s\S]|(?!\1)[^\\])+\1|([\/|"'])(?:\\.|(?!\2)[^\\\r\n])+\2|\((?:\\.|[^\\)\r\n])+\)|\[(?:\\.|[^\\\]\r\n])+\]|\{(?:\\.|[^\\}\r\n])+\}|<(?:\\.|[^\\>\r\n])+>)[uismxfr]*/,greedy:!0},string:[{pattern:/~[cCsSwW](?:("""|''')(?:\\[\s\S]|(?!\1)[^\\])+\1|([\/|"'])(?:\\.|(?!\2)[^\\\r\n])+\2|\((?:\\.|[^\\)\r\n])+\)|\[(?:\\.|[^\\\]\r\n])+\]|\{(?:\\.|#\{[^}]+\}|#(?!\{)|[^#\\}\r\n])+\}|<(?:\\.|[^\\>\r\n])+>)[csa]?/,greedy:!0,inside:{}},{pattern:/("""|''')[\s\S]*?\1/,greedy:!0,inside:{}},{pattern:/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0,inside:{}}],atom:{pattern:/(^|[^:]):\w+/,lookbehind:!0,alias:"symbol"},module:{pattern:/\b[A-Z]\w*\b/,alias:"class-name"},"attr-name":/\b\w+\??:(?!:)/,argument:{pattern:/(^|[^&])&\d+/,lookbehind:!0,alias:"variable"},attribute:{pattern:/@\w+/,alias:"variable"},function:/\b[_a-zA-Z]\w*[?!]?(?:(?=\s*(?:\.\s*)?\()|(?=\/\d))/,number:/\b(?:0[box][a-f\d_]+|\d[\d_]*)(?:\.[\d_]+)?(?:e[+-]?[\d_]+)?\b/i,keyword:/\b(?:after|alias|and|case|catch|cond|def(?:callback|delegate|exception|impl|macro|module|n|np|p|protocol|struct)?|do|else|end|fn|for|if|import|not|or|quote|raise|require|rescue|try|unless|unquote|use|when)\b/,boolean:/\b(?:false|nil|true)\b/,operator:[/\bin\b|&&?|\|[|>]?|\\\\|::|\.\.\.?|\+\+?|-[->]?|<[-=>]|>=|!==?|\B!|=(?:==?|[>~])?|[*\/^]/,{pattern:/([^<])<(?!<)/,lookbehind:!0},{pattern:/([^>])>(?!>)/,lookbehind:!0}],punctuation:/<<|>>|[.,%\[\]{}()]/},Prism.languages.elixir.string.forEach((function(e){e.inside={interpolation:{pattern:/#\{[^}]+\}/,inside:{delimiter:{pattern:/^#\{|\}$/,alias:"punctuation"},rest:Prism.languages.elixir}}}}));
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Prism.languages.erlang={comment:/%.+/,string:{pattern:/"(?:\\.|[^\\"\r\n])*"/,greedy:!0},"quoted-function":{pattern:/'(?:\\.|[^\\'\r\n])+'(?=\()/,alias:"function"},"quoted-atom":{pattern:/'(?:\\.|[^\\'\r\n])+'/,alias:"atom"},boolean:/\b(?:false|true)\b/,keyword:/\b(?:after|begin|case|catch|end|fun|if|of|receive|try|when)\b/,number:[/\$\\?./,/\b\d+#[a-z0-9]+/i,/(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i],function:/\b[a-z][\w@]*(?=\()/,variable:{pattern:/(^|[^@])(?:\b|\?)[A-Z_][\w@]*/,lookbehind:!0},operator:[/[=\/<>:]=|=[:\/]=|\+\+?|--?|[=*\/!]|\b(?:and|andalso|band|bnot|bor|bsl|bsr|bxor|div|not|or|orelse|rem|xor)\b/,{pattern:/(^|[^<])<(?!<)/,lookbehind:!0},{pattern:/(^|[^>])>(?!>)/,lookbehind:!0}],atom:/\b[a-z][\w@]*/,punctuation:/[()[\]{}:;,.#|]|<<|>>/};
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Prism.languages.git={comment:/^#.*/m,deleted:/^[-–].*/m,inserted:/^\+.*/m,string:/("|')(?:\\.|(?!\1)[^\\\r\n])*\1/,command:{pattern:/^.*\$ git .*$/m,inside:{parameter:/\s--?\w+/}},coord:/^@@.*@@$/m,"commit-sha1":/^commit \w{40}$/m};
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Prism.languages.go=Prism.languages.extend("clike",{string:{pattern:/(^|[^\\])"(?:\\.|[^"\\\r\n])*"|`[^`]*`/,lookbehind:!0,greedy:!0},keyword:/\b(?:break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go(?:to)?|if|import|interface|map|package|range|return|select|struct|switch|type|var)\b/,boolean:/\b(?:_|false|iota|nil|true)\b/,number:[/\b0(?:b[01_]+|o[0-7_]+)i?\b/i,/\b0x(?:[a-f\d_]+(?:\.[a-f\d_]*)?|\.[a-f\d_]+)(?:p[+-]?\d+(?:_\d+)*)?i?(?!\w)/i,/(?:\b\d[\d_]*(?:\.[\d_]*)?|\B\.\d[\d_]*)(?:e[+-]?[\d_]+)?i?(?!\w)/i],operator:/[*\/%^!=]=?|\+[=+]?|-[=-]?|\|[=|]?|&(?:=|&|\^=?)?|>(?:>=?|=)?|<(?:<=?|=|-)?|:=|\.\.\./,builtin:/\b(?:append|bool|byte|cap|close|complex|complex(?:64|128)|copy|delete|error|float(?:32|64)|u?int(?:8|16|32|64)?|imag|len|make|new|panic|print(?:ln)?|real|recover|rune|string|uintptr)\b/}),Prism.languages.insertBefore("go","string",{char:{pattern:/'(?:\\.|[^'\\\r\n]){0,10}'/,greedy:!0}}),delete Prism.languages.go["class-name"];
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Prism.languages.graphql={comment:/#.*/,description:{pattern:/(?:"""(?:[^"]|(?!""")")*"""|"(?:\\.|[^\\"\r\n])*")(?=\s*[a-z_])/i,greedy:!0,alias:"string",inside:{"language-markdown":{pattern:/(^"(?:"")?)(?!\1)[\s\S]+(?=\1$)/,lookbehind:!0,inside:Prism.languages.markdown}}},string:{pattern:/"""(?:[^"]|(?!""")")*"""|"(?:\\.|[^\\"\r\n])*"/,greedy:!0},number:/(?:\B-|\b)\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,boolean:/\b(?:false|true)\b/,variable:/\$[a-z_]\w*/i,directive:{pattern:/@[a-z_]\w*/i,alias:"function"},"attr-name":{pattern:/\b[a-z_]\w*(?=\s*(?:\((?:[^()"]|"(?:\\.|[^\\"\r\n])*")*\))?:)/i,greedy:!0},"atom-input":{pattern:/\b[A-Z]\w*Input\b/,alias:"class-name"},scalar:/\b(?:Boolean|Float|ID|Int|String)\b/,constant:/\b[A-Z][A-Z_\d]*\b/,"class-name":{pattern:/(\b(?:enum|implements|interface|on|scalar|type|union)\s+|&\s*|:\s*|\[)[A-Z_]\w*/,lookbehind:!0},fragment:{pattern:/(\bfragment\s+|\.{3}\s*(?!on\b))[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},"definition-mutation":{pattern:/(\bmutation\s+)[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},"definition-query":{pattern:/(\bquery\s+)[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},keyword:/\b(?:directive|enum|extend|fragment|implements|input|interface|mutation|on|query|repeatable|scalar|schema|subscription|type|union)\b/,operator:/[!=|&]|\.{3}/,"property-query":/\w+(?=\s*\()/,object:/\w+(?=\s*\{)/,punctuation:/[!(){}\[\]:=,]/,property:/\w+/},Prism.hooks.add("after-tokenize",(function(n){if("graphql"===n.language)for(var t=n.tokens.filter((function(n){return"string"!=typeof n&&"comment"!==n.type&&"scalar"!==n.type})),e=0;e<t.length;){var a=t[e++];if("keyword"===a.type&&"mutation"===a.content){var r=[];if(c(["definition-mutation","punctuation"])&&"("===l(1).content){e+=2;var i=f(/^\($/,/^\)$/);if(-1===i)continue;for(;e<i;e++){var o=l(0);"variable"===o.type&&(b(o,"variable-input"),r.push(o.content))}e=i+1}if(c(["punctuation","property-query"])&&"{"===l(0).content&&(e++,b(l(0),"property-mutation"),r.length>0)){var s=f(/^\{$/,/^\}$/);if(-1===s)continue;for(var u=e;u<s;u++){var p=t[u];"variable"===p.type&&r.indexOf(p.content)>=0&&b(p,"variable-input")}}}}function l(n){return t[e+n]}function c(n,t){t=t||0;for(var e=0;e<n.length;e++){var a=l(e+t);if(!a||a.type!==n[e])return!1}return!0}function f(n,a){for(var r=1,i=e;i<t.length;i++){var o=t[i],s=o.content;if("punctuation"===o.type&&"string"==typeof s)if(n.test(s))r++;else if(a.test(s)&&0==--r)return i}return-1}function b(n,t){var e=n.alias;e?Array.isArray(e)||(n.alias=e=[e]):n.alias=e=[],e.push(t)}}));
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
!function(e){var n={pattern:/((?:^|[^\\$])(?:\\{2})*)\$(?:\w+|\{[^{}]*\})/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{?|\}$/,alias:"punctuation"},expression:{pattern:/[\s\S]+/,inside:null}}};e.languages.groovy=e.languages.extend("clike",{string:{pattern:/'''(?:[^\\]|\\[\s\S])*?'''|'(?:\\.|[^\\'\r\n])*'/,greedy:!0},keyword:/\b(?:abstract|as|assert|boolean|break|byte|case|catch|char|class|const|continue|def|default|do|double|else|enum|extends|final|finally|float|for|goto|if|implements|import|in|instanceof|int|interface|long|native|new|package|private|protected|public|return|short|static|strictfp|super|switch|synchronized|this|throw|throws|trait|transient|try|void|volatile|while)\b/,number:/\b(?:0b[01_]+|0x[\da-f_]+(?:\.[\da-f_p\-]+)?|[\d_]+(?:\.[\d_]+)?(?:e[+-]?\d+)?)[glidf]?\b/i,operator:{pattern:/(^|[^.])(?:~|==?~?|\?[.:]?|\*(?:[.=]|\*=?)?|\.[@&]|\.\.<|\.\.(?!\.)|-[-=>]?|\+[+=]?|!=?|<(?:<=?|=>?)?|>(?:>>?=?|=)?|&[&=]?|\|[|=]?|\/=?|\^=?|%=?)/,lookbehind:!0},punctuation:/\.+|[{}[\];(),:$]/}),e.languages.insertBefore("groovy","string",{shebang:{pattern:/#!.+/,alias:"comment",greedy:!0},"interpolation-string":{pattern:/"""(?:[^\\]|\\[\s\S])*?"""|(["/])(?:\\.|(?!\1)[^\\\r\n])*\1|\$\/(?:[^/$]|\$(?:[/$]|(?![/$]))|\/(?!\$))*\/\$/,greedy:!0,inside:{interpolation:n,string:/[\s\S]+/}}}),e.languages.insertBefore("groovy","punctuation",{"spock-block":/\b(?:and|cleanup|expect|given|setup|then|when|where):/}),e.languages.insertBefore("groovy","function",{annotation:{pattern:/(^|[^.])@\w+/,lookbehind:!0,alias:"punctuation"}}),n.inside.expression.inside=e.languages.groovy}(Prism);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Prism.languages.haskell={comment:{pattern:/(^|[^-!#$%*+=?&@|~.:<>^\\\/])(?:--(?:(?=.)[^-!#$%*+=?&@|~.:<>^\\\/].*|$)|\{-[\s\S]*?-\})/m,lookbehind:!0},char:{pattern:/'(?:[^\\']|\\(?:[abfnrtv\\"'&]|\^[A-Z@[\]^_]|ACK|BEL|BS|CAN|CR|DC1|DC2|DC3|DC4|DEL|DLE|EM|ENQ|EOT|ESC|ETB|ETX|FF|FS|GS|HT|LF|NAK|NUL|RS|SI|SO|SOH|SP|STX|SUB|SYN|US|VT|\d+|o[0-7]+|x[0-9a-fA-F]+))'/,alias:"string"},string:{pattern:/"(?:[^\\"]|\\(?:\S|\s+\\))*"/,greedy:!0},keyword:/\b(?:case|class|data|deriving|do|else|if|in|infixl|infixr|instance|let|module|newtype|of|primitive|then|type|where)\b/,"import-statement":{pattern:/(^[\t ]*)import\s+(?:qualified\s+)?(?:[A-Z][\w']*)(?:\.[A-Z][\w']*)*(?:\s+as\s+(?:[A-Z][\w']*)(?:\.[A-Z][\w']*)*)?(?:\s+hiding\b)?/m,lookbehind:!0,inside:{keyword:/\b(?:as|hiding|import|qualified)\b/,punctuation:/\./}},builtin:/\b(?:abs|acos|acosh|all|and|any|appendFile|approxRational|asTypeOf|asin|asinh|atan|atan2|atanh|basicIORun|break|catch|ceiling|chr|compare|concat|concatMap|const|cos|cosh|curry|cycle|decodeFloat|denominator|digitToInt|div|divMod|drop|dropWhile|either|elem|encodeFloat|enumFrom|enumFromThen|enumFromThenTo|enumFromTo|error|even|exp|exponent|fail|filter|flip|floatDigits|floatRadix|floatRange|floor|fmap|foldl|foldl1|foldr|foldr1|fromDouble|fromEnum|fromInt|fromInteger|fromIntegral|fromRational|fst|gcd|getChar|getContents|getLine|group|head|id|inRange|index|init|intToDigit|interact|ioError|isAlpha|isAlphaNum|isAscii|isControl|isDenormalized|isDigit|isHexDigit|isIEEE|isInfinite|isLower|isNaN|isNegativeZero|isOctDigit|isPrint|isSpace|isUpper|iterate|last|lcm|length|lex|lexDigits|lexLitChar|lines|log|logBase|lookup|map|mapM|mapM_|max|maxBound|maximum|maybe|min|minBound|minimum|mod|negate|not|notElem|null|numerator|odd|or|ord|otherwise|pack|pi|pred|primExitWith|print|product|properFraction|putChar|putStr|putStrLn|quot|quotRem|range|rangeSize|read|readDec|readFile|readFloat|readHex|readIO|readInt|readList|readLitChar|readLn|readOct|readParen|readSigned|reads|readsPrec|realToFrac|recip|rem|repeat|replicate|return|reverse|round|scaleFloat|scanl|scanl1|scanr|scanr1|seq|sequence|sequence_|show|showChar|showInt|showList|showLitChar|showParen|showSigned|showString|shows|showsPrec|significand|signum|sin|sinh|snd|sort|span|splitAt|sqrt|subtract|succ|sum|tail|take|takeWhile|tan|tanh|threadToIOResult|toEnum|toInt|toInteger|toLower|toRational|toUpper|truncate|uncurry|undefined|unlines|until|unwords|unzip|unzip3|userError|words|writeFile|zip|zip3|zipWith|zipWith3)\b/,number:/\b(?:\d+(?:\.\d+)?(?:e[+-]?\d+)?|0o[0-7]+|0x[0-9a-f]+)\b/i,operator:[{pattern:/`(?:[A-Z][\w']*\.)*[_a-z][\w']*`/,greedy:!0},{pattern:/(\s)\.(?=\s)/,lookbehind:!0},/[-!#$%*+=?&@|~:<>^\\\/][-!#$%*+=?&@|~.:<>^\\\/]*|\.[-!#$%*+=?&@|~.:<>^\\\/]+/],hvariable:{pattern:/\b(?:[A-Z][\w']*\.)*[_a-z][\w']*/,inside:{punctuation:/\./}},constant:{pattern:/\b(?:[A-Z][\w']*\.)*[A-Z][\w']*/,inside:{punctuation:/\./}},punctuation:/[{}[\];(),.:]/},Prism.languages.hs=Prism.languages.haskell;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
!function(t){function a(t){return RegExp("(^(?:"+t+"):[ \t]*(?![ \t]))[^]+","i")}t.languages.http={"request-line":{pattern:/^(?:CONNECT|DELETE|GET|HEAD|OPTIONS|PATCH|POST|PRI|PUT|SEARCH|TRACE)\s(?:https?:\/\/|\/)\S*\sHTTP\/[\d.]+/m,inside:{method:{pattern:/^[A-Z]+\b/,alias:"property"},"request-target":{pattern:/^(\s)(?:https?:\/\/|\/)\S*(?=\s)/,lookbehind:!0,alias:"url",inside:t.languages.uri},"http-version":{pattern:/^(\s)HTTP\/[\d.]+/,lookbehind:!0,alias:"property"}}},"response-status":{pattern:/^HTTP\/[\d.]+ \d+ .+/m,inside:{"http-version":{pattern:/^HTTP\/[\d.]+/,alias:"property"},"status-code":{pattern:/^(\s)\d+(?=\s)/,lookbehind:!0,alias:"number"},"reason-phrase":{pattern:/^(\s).+/,lookbehind:!0,alias:"string"}}},header:{pattern:/^[\w-]+:.+(?:(?:\r\n?|\n)[ \t].+)*/m,inside:{"header-value":[{pattern:a("Content-Security-Policy"),lookbehind:!0,alias:["csp","languages-csp"],inside:t.languages.csp},{pattern:a("Public-Key-Pins(?:-Report-Only)?"),lookbehind:!0,alias:["hpkp","languages-hpkp"],inside:t.languages.hpkp},{pattern:a("Strict-Transport-Security"),lookbehind:!0,alias:["hsts","languages-hsts"],inside:t.languages.hsts},{pattern:a("[^:]+"),lookbehind:!0}],"header-name":{pattern:/^[^:]+/,alias:"keyword"},punctuation:/^:/}}};var e,n=t.languages,s={"application/javascript":n.javascript,"application/json":n.json||n.javascript,"application/xml":n.xml,"text/xml":n.xml,"text/html":n.html,"text/css":n.css,"text/plain":n.plain},i={"application/json":!0,"application/xml":!0};function r(t){var a=t.replace(/^[a-z]+\//,"");return"(?:"+t+"|\\w+/(?:[\\w.-]+\\+)+"+a+"(?![+\\w.-]))"}for(var p in s)if(s[p]){e=e||{};var l=i[p]?r(p):p;e[p.replace(/\//g,"-")]={pattern:RegExp("(content-type:\\s*"+l+"(?:(?:\r\n?|\n)[\\w-].*)*(?:\r(?:\n|(?!\n))|\n))[^ \t\\w-][^]*","i"),lookbehind:!0,inside:s[p]}}e&&t.languages.insertBefore("http","header",e)}(Prism);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Prism.languages.ini={comment:{pattern:/(^[ \f\t\v]*)[#;][^\n\r]*/m,lookbehind:!0},section:{pattern:/(^[ \f\t\v]*)\[[^\n\r\]]*\]?/m,lookbehind:!0,inside:{"section-name":{pattern:/(^\[[ \f\t\v]*)[^ \f\t\v\]]+(?:[ \f\t\v]+[^ \f\t\v\]]+)*/,lookbehind:!0,alias:"selector"},punctuation:/\[|\]/}},key:{pattern:/(^[ \f\t\v]*)[^ \f\n\r\t\v=]+(?:[ \f\t\v]+[^ \f\n\r\t\v=]+)*(?=[ \f\t\v]*=)/m,lookbehind:!0,alias:"attr-name"},value:{pattern:/(=[ \f\t\v]*)[^ \f\n\r\t\v]+(?:[ \f\t\v]+[^ \f\n\r\t\v]+)*/,lookbehind:!0,alias:"attr-value",inside:{"inner-value":{pattern:/^("|').+(?=\1$)/,lookbehind:!0}}},punctuation:/=/};
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
!function(e){var n=/\b(?:abstract|assert|boolean|break|byte|case|catch|char|class|const|continue|default|do|double|else|enum|exports|extends|final|finally|float|for|goto|if|implements|import|instanceof|int|interface|long|module|native|new|non-sealed|null|open|opens|package|permits|private|protected|provides|public|record(?!\s*[(){}[\]<>=%~.:,;?+\-*/&|^])|requires|return|sealed|short|static|strictfp|super|switch|synchronized|this|throw|throws|to|transient|transitive|try|uses|var|void|volatile|while|with|yield)\b/,t="(?:[a-z]\\w*\\s*\\.\\s*)*(?:[A-Z]\\w*\\s*\\.\\s*)*",s={pattern:RegExp("(^|[^\\w.])"+t+"[A-Z](?:[\\d_A-Z]*[a-z]\\w*)?\\b"),lookbehind:!0,inside:{namespace:{pattern:/^[a-z]\w*(?:\s*\.\s*[a-z]\w*)*(?:\s*\.)?/,inside:{punctuation:/\./}},punctuation:/\./}};e.languages.java=e.languages.extend("clike",{string:{pattern:/(^|[^\\])"(?:\\.|[^"\\\r\n])*"/,lookbehind:!0,greedy:!0},"class-name":[s,{pattern:RegExp("(^|[^\\w.])"+t+"[A-Z]\\w*(?=\\s+\\w+\\s*[;,=()]|\\s*(?:\\[[\\s,]*\\]\\s*)?::\\s*new\\b)"),lookbehind:!0,inside:s.inside},{pattern:RegExp("(\\b(?:class|enum|extends|implements|instanceof|interface|new|record|throws)\\s+)"+t+"[A-Z]\\w*\\b"),lookbehind:!0,inside:s.inside}],keyword:n,function:[e.languages.clike.function,{pattern:/(::\s*)[a-z_]\w*/,lookbehind:!0}],number:/\b0b[01][01_]*L?\b|\b0x(?:\.[\da-f_p+-]+|[\da-f_]+(?:\.[\da-f_p+-]+)?)\b|(?:\b\d[\d_]*(?:\.[\d_]*)?|\B\.\d[\d_]*)(?:e[+-]?\d[\d_]*)?[dfl]?/i,operator:{pattern:/(^|[^.])(?:<<=?|>>>?=?|->|--|\+\+|&&|\|\||::|[?:~]|[-+*/%&|^!=<>]=?)/m,lookbehind:!0},constant:/\b[A-Z][A-Z_\d]+\b/}),e.languages.insertBefore("java","string",{"triple-quoted-string":{pattern:/"""[ \t]*[\r\n](?:(?:"|"")?(?:\\.|[^"\\]))*"""/,greedy:!0,alias:"string"},char:{pattern:/'(?:\\.|[^'\\\r\n]){1,6}'/,greedy:!0}}),e.languages.insertBefore("java","class-name",{annotation:{pattern:/(^|[^.])@\w+(?:\s*\.\s*\w+)*/,lookbehind:!0,alias:"punctuation"},generics:{pattern:/<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&))*>)*>)*>)*>/,inside:{"class-name":s,keyword:n,punctuation:/[<>(),.:]/,operator:/[?&|]/}},import:[{pattern:RegExp("(\\bimport\\s+)"+t+"(?:[A-Z]\\w*|\\*)(?=\\s*;)"),lookbehind:!0,inside:{namespace:s.inside.namespace,punctuation:/\./,operator:/\*/,"class-name":/\w+/}},{pattern:RegExp("(\\bimport\\s+static\\s+)"+t+"(?:\\w+|\\*)(?=\\s*;)"),lookbehind:!0,alias:"static",inside:{namespace:s.inside.namespace,static:/\b\w+$/,punctuation:/\./,operator:/\*/,"class-name":/\w+/}}],namespace:{pattern:RegExp("(\\b(?:exports|import(?:\\s+static)?|module|open|opens|package|provides|requires|to|transitive|uses|with)\\s+)(?!<keyword>)[a-z]\\w*(?:\\.[a-z]\\w*)*\\.?".replace(/<keyword>/g,(function(){return n.source}))),lookbehind:!0,inside:{punctuation:/\./}}})}(Prism);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Prism.languages.javascript=Prism.languages.extend("clike",{"class-name":[Prism.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp("(^|[^\\w$])(?:NaN|Infinity|0[bB][01]+(?:_[01]+)*n?|0[oO][0-7]+(?:_[0-7]+)*n?|0[xX][\\dA-Fa-f]+(?:_[\\dA-Fa-f]+)*n?|\\d+(?:_\\d+)*n|(?:\\d+(?:_\\d+)*(?:\\.(?:\\d+(?:_\\d+)*)?)?|\\.\\d+(?:_\\d+)*)(?:[Ee][+-]?\\d+(?:_\\d+)*)?)(?![\\w$])"),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),Prism.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:RegExp("((?:^|[^$\\w\\xA0-\\uFFFF.\"'\\])\\s]|\\b(?:return|yield))\\s*)/(?:(?:\\[(?:[^\\]\\\\\r\n]|\\\\.)*\\]|\\\\.|[^/\\\\\\[\r\n])+/[dgimyus]{0,7}|(?:\\[(?:[^[\\]\\\\\r\n]|\\\\.|\\[(?:[^[\\]\\\\\r\n]|\\\\.|\\[(?:[^[\\]\\\\\r\n]|\\\\.)*\\])*\\])*\\]|\\\\.|[^/\\\\\\[\r\n])+/[dgimyus]{0,7}v[dgimyus]{0,7})(?=(?:\\s|/\\*(?:[^*]|\\*(?!/))*\\*/)*(?:$|[\r\n,.;:})\\]]|//))"),lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:Prism.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:Prism.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),Prism.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),Prism.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),Prism.languages.markup&&(Prism.languages.markup.tag.addInlined("script","javascript"),Prism.languages.markup.tag.addAttribute("on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)","javascript")),Prism.languages.js=Prism.languages.javascript;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Prism.languages.json={property:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?=\s*:)/,lookbehind:!0,greedy:!0},string:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?!\s*:)/,lookbehind:!0,greedy:!0},comment:{pattern:/\/\/.*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},number:/-?\b\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,punctuation:/[{}[\],]/,operator:/:/,boolean:/\b(?:false|true)\b/,null:{pattern:/\bnull\b/,alias:"keyword"}},Prism.languages.webmanifest=Prism.languages.json;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
!function(t){var n=t.util.clone(t.languages.javascript),e="(?:\\{<S>*\\.{3}(?:[^{}]|<BRACES>)*\\})";function a(t,n){return t=t.replace(/<S>/g,(function(){return"(?:\\s|//.*(?!.)|/\\*(?:[^*]|\\*(?!/))\\*/)"})).replace(/<BRACES>/g,(function(){return"(?:\\{(?:\\{(?:\\{[^{}]*\\}|[^{}])*\\}|[^{}])*\\})"})).replace(/<SPREAD>/g,(function(){return e})),RegExp(t,n)}e=a(e).source,t.languages.jsx=t.languages.extend("markup",n),t.languages.jsx.tag.pattern=a("</?(?:[\\w.:-]+(?:<S>+(?:[\\w.:$-]+(?:=(?:\"(?:\\\\[^]|[^\\\\\"])*\"|'(?:\\\\[^]|[^\\\\'])*'|[^\\s{'\"/>=]+|<BRACES>))?|<SPREAD>))*<S>*/?)?>"),t.languages.jsx.tag.inside.tag.pattern=/^<\/?[^\s>\/]*/,t.languages.jsx.tag.inside["attr-value"].pattern=/=(?!\{)(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s'">]+)/,t.languages.jsx.tag.inside.tag.inside["class-name"]=/^[A-Z]\w*(?:\.[A-Z]\w*)*$/,t.languages.jsx.tag.inside.comment=n.comment,t.languages.insertBefore("inside","attr-name",{spread:{pattern:a("<SPREAD>"),inside:t.languages.jsx}},t.languages.jsx.tag),t.languages.insertBefore("inside","special-attr",{script:{pattern:a("=<BRACES>"),alias:"language-javascript",inside:{"script-punctuation":{pattern:/^=(?=\{)/,alias:"punctuation"},rest:t.languages.jsx}}},t.languages.jsx.tag);var s=function(t){return t?"string"==typeof t?t:"string"==typeof t.content?t.content:t.content.map(s).join(""):""},g=function(n){for(var e=[],a=0;a<n.length;a++){var o=n[a],i=!1;if("string"!=typeof o&&("tag"===o.type&&o.content[0]&&"tag"===o.content[0].type?"</"===o.content[0].content[0].content?e.length>0&&e[e.length-1].tagName===s(o.content[0].content[1])&&e.pop():"/>"===o.content[o.content.length-1].content||e.push({tagName:s(o.content[0].content[1]),openedBraces:0}):e.length>0&&"punctuation"===o.type&&"{"===o.content?e[e.length-1].openedBraces++:e.length>0&&e[e.length-1].openedBraces>0&&"punctuation"===o.type&&"}"===o.content?e[e.length-1].openedBraces--:i=!0),(i||"string"==typeof o)&&e.length>0&&0===e[e.length-1].openedBraces){var r=s(o);a<n.length-1&&("string"==typeof n[a+1]||"plain-text"===n[a+1].type)&&(r+=s(n[a+1]),n.splice(a+1,1)),a>0&&("string"==typeof n[a-1]||"plain-text"===n[a-1].type)&&(r=s(n[a-1])+r,n.splice(a-1,1),a--),n[a]=new t.Token("plain-text",r,null,r)}o.content&&"string"!=typeof o.content&&g(o.content)}};t.hooks.add("after-tokenize",(function(t){"jsx"!==t.language&&"tsx"!==t.language||g(t.tokens)}))}(Prism);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Prism.languages.julia={comment:{pattern:/(^|[^\\])(?:#=(?:[^#=]|=(?!#)|#(?!=)|#=(?:[^#=]|=(?!#)|#(?!=))*=#)*=#|#.*)/,lookbehind:!0},regex:{pattern:/r"(?:\\.|[^"\\\r\n])*"[imsx]{0,4}/,greedy:!0},string:{pattern:/"""[\s\S]+?"""|(?:\b\w+)?"(?:\\.|[^"\\\r\n])*"|`(?:[^\\`\r\n]|\\.)*`/,greedy:!0},char:{pattern:/(^|[^\w'])'(?:\\[^\r\n][^'\r\n]*|[^\\\r\n])'/,lookbehind:!0,greedy:!0},keyword:/\b(?:abstract|baremodule|begin|bitstype|break|catch|ccall|const|continue|do|else|elseif|end|export|finally|for|function|global|if|immutable|import|importall|in|let|local|macro|module|print|println|quote|return|struct|try|type|typealias|using|while)\b/,boolean:/\b(?:false|true)\b/,number:/(?:\b(?=\d)|\B(?=\.))(?:0[box])?(?:[\da-f]+(?:_[\da-f]+)*(?:\.(?:\d+(?:_\d+)*)?)?|\.\d+(?:_\d+)*)(?:[efp][+-]?\d+(?:_\d+)*)?j?/i,operator:/&&|\|\||[-+*^%÷⊻&$\\]=?|\/[\/=]?|!=?=?|\|[=>]?|<(?:<=?|[=:|])?|>(?:=|>>?=?)?|==?=?|[~≠≤≥'√∛]/,punctuation:/::?|[{}[\]();,.?]/,constant:/\b(?:(?:Inf|NaN)(?:16|32|64)?|im|pi)\b|[πℯ]/};
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
!function(n){n.languages.kotlin=n.languages.extend("clike",{keyword:{pattern:/(^|[^.])\b(?:abstract|actual|annotation|as|break|by|catch|class|companion|const|constructor|continue|crossinline|data|do|dynamic|else|enum|expect|external|final|finally|for|fun|get|if|import|in|infix|init|inline|inner|interface|internal|is|lateinit|noinline|null|object|open|operator|out|override|package|private|protected|public|reified|return|sealed|set|super|suspend|tailrec|this|throw|to|try|typealias|val|var|vararg|when|where|while)\b/,lookbehind:!0},function:[{pattern:/(?:`[^\r\n`]+`|\b\w+)(?=\s*\()/,greedy:!0},{pattern:/(\.)(?:`[^\r\n`]+`|\w+)(?=\s*\{)/,lookbehind:!0,greedy:!0}],number:/\b(?:0[xX][\da-fA-F]+(?:_[\da-fA-F]+)*|0[bB][01]+(?:_[01]+)*|\d+(?:_\d+)*(?:\.\d+(?:_\d+)*)?(?:[eE][+-]?\d+(?:_\d+)*)?[fFL]?)\b/,operator:/\+[+=]?|-[-=>]?|==?=?|!(?:!|==?)?|[\/*%<>]=?|[?:]:?|\.\.|&&|\|\||\b(?:and|inv|or|shl|shr|ushr|xor)\b/}),delete n.languages.kotlin["class-name"];var e={"interpolation-punctuation":{pattern:/^\$\{?|\}$/,alias:"punctuation"},expression:{pattern:/[\s\S]+/,inside:n.languages.kotlin}};n.languages.insertBefore("kotlin","string",{"string-literal":[{pattern:/"""(?:[^$]|\$(?:(?!\{)|\{[^{}]*\}))*?"""/,alias:"multiline",inside:{interpolation:{pattern:/\$(?:[a-z_]\w*|\{[^{}]*\})/i,inside:e},string:/[\s\S]+/}},{pattern:/"(?:[^"\\\r\n$]|\\.|\$(?:(?!\{)|\{[^{}]*\}))*"/,alias:"singleline",inside:{interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$(?:[a-z_]\w*|\{[^{}]*\})/i,lookbehind:!0,inside:e},string:/[\s\S]+/}}],char:{pattern:/'(?:[^'\\\r\n]|\\(?:.|u[a-fA-F0-9]{0,4}))'/,greedy:!0}}),delete n.languages.kotlin.string,n.languages.insertBefore("kotlin","keyword",{annotation:{pattern:/\B@(?:\w+:)?(?:[A-Z]\w*|\[[^\]]+\])/,alias:"builtin"}}),n.languages.insertBefore("kotlin","function",{label:{pattern:/\b\w+@|@\w+\b/,alias:"symbol"}}),n.languages.kt=n.languages.kotlin,n.languages.kts=n.languages.kotlin}(Prism);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
!function(a){var e=/\\(?:[^a-z()[\]]|[a-z*]+)/i,n={"equation-command":{pattern:e,alias:"regex"}};a.languages.latex={comment:/%.*/,cdata:{pattern:/(\\begin\{((?:lstlisting|verbatim)\*?)\})[\s\S]*?(?=\\end\{\2\})/,lookbehind:!0},equation:[{pattern:/\$\$(?:\\[\s\S]|[^\\$])+\$\$|\$(?:\\[\s\S]|[^\\$])+\$|\\\([\s\S]*?\\\)|\\\[[\s\S]*?\\\]/,inside:n,alias:"string"},{pattern:/(\\begin\{((?:align|eqnarray|equation|gather|math|multline)\*?)\})[\s\S]*?(?=\\end\{\2\})/,lookbehind:!0,inside:n,alias:"string"}],keyword:{pattern:/(\\(?:begin|cite|documentclass|end|label|ref|usepackage)(?:\[[^\]]+\])?\{)[^}]+(?=\})/,lookbehind:!0},url:{pattern:/(\\url\{)[^}]+(?=\})/,lookbehind:!0},headline:{pattern:/(\\(?:chapter|frametitle|paragraph|part|section|subparagraph|subsection|subsubparagraph|subsubsection|subsubsubparagraph)\*?(?:\[[^\]]+\])?\{)[^}]+(?=\})/,lookbehind:!0,alias:"class-name"},function:{pattern:e,alias:"selector"},punctuation:/[[\]{}&]/},a.languages.tex=a.languages.latex,a.languages.context=a.languages.latex}(Prism);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Prism.languages.less=Prism.languages.extend("css",{comment:[/\/\*[\s\S]*?\*\//,{pattern:/(^|[^\\])\/\/.*/,lookbehind:!0}],atrule:{pattern:/@[\w-](?:\((?:[^(){}]|\([^(){}]*\))*\)|[^(){};\s]|\s+(?!\s))*?(?=\s*\{)/,inside:{punctuation:/[:()]/}},selector:{pattern:/(?:@\{[\w-]+\}|[^{};\s@])(?:@\{[\w-]+\}|\((?:[^(){}]|\([^(){}]*\))*\)|[^(){};@\s]|\s+(?!\s))*?(?=\s*\{)/,inside:{variable:/@+[\w-]+/}},property:/(?:@\{[\w-]+\}|[\w-])+(?:\+_?)?(?=\s*:)/,operator:/[+\-*\/]/}),Prism.languages.insertBefore("less","property",{variable:[{pattern:/@[\w-]+\s*:/,inside:{punctuation:/:/}},/@@?[\w-]+/],"mixin-usage":{pattern:/([{;]\s*)[.#](?!\d)[\w-].*?(?=[(;])/,lookbehind:!0,alias:"function"}});
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Prism.languages.lua={comment:/^#!.+|--(?:\[(=*)\[[\s\S]*?\]\1\]|.*)/m,string:{pattern:/(["'])(?:(?!\1)[^\\\r\n]|\\z(?:\r\n|\s)|\\(?:\r\n|[^z]))*\1|\[(=*)\[[\s\S]*?\]\2\]/,greedy:!0},number:/\b0x[a-f\d]+(?:\.[a-f\d]*)?(?:p[+-]?\d+)?\b|\b\d+(?:\.\B|(?:\.\d*)?(?:e[+-]?\d+)?\b)|\B\.\d+(?:e[+-]?\d+)?\b/i,keyword:/\b(?:and|break|do|else|elseif|end|false|for|function|goto|if|in|local|nil|not|or|repeat|return|then|true|until|while)\b/,function:/(?!\d)\w+(?=\s*(?:[({]))/,operator:[/[-+*%^&|#]|\/\/?|<[<=]?|>[>=]?|[=~]=?/,{pattern:/(^|[^.])\.\.(?!\.)/,lookbehind:!0}],punctuation:/[\[\](){},;]|\.+|:+/};
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Prism.languages.makefile={comment:{pattern:/(^|[^\\])#(?:\\(?:\r\n|[\s\S])|[^\\\r\n])*/,lookbehind:!0},string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"builtin-target":{pattern:/\.[A-Z][^:#=\s]+(?=\s*:(?!=))/,alias:"builtin"},target:{pattern:/^(?:[^:=\s]|[ \t]+(?![\s:]))+(?=\s*:(?!=))/m,alias:"symbol",inside:{variable:/\$+(?:(?!\$)[^(){}:#=\s]+|(?=[({]))/}},variable:/\$+(?:(?!\$)[^(){}:#=\s]+|\([@*%<^+?][DF]\)|(?=[({]))/,keyword:/-include\b|\b(?:define|else|endef|endif|export|ifn?def|ifn?eq|include|override|private|sinclude|undefine|unexport|vpath)\b/,function:{pattern:/(\()(?:abspath|addsuffix|and|basename|call|dir|error|eval|file|filter(?:-out)?|findstring|firstword|flavor|foreach|guile|if|info|join|lastword|load|notdir|or|origin|patsubst|realpath|shell|sort|strip|subst|suffix|value|warning|wildcard|word(?:list|s)?)(?=[ \t])/,lookbehind:!0},operator:/(?:::|[?:+!])?=|[|@]/,punctuation:/[:;(){}]/};
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
|||||||
!function(e){function n(e,n){return"___"+e.toUpperCase()+n+"___"}Object.defineProperties(e.languages["markup-templating"]={},{buildPlaceholders:{value:function(t,a,r,o){if(t.language===a){var c=t.tokenStack=[];t.code=t.code.replace(r,(function(e){if("function"==typeof o&&!o(e))return e;for(var r,i=c.length;-1!==t.code.indexOf(r=n(a,i));)++i;return c[i]=e,r})),t.grammar=e.languages.markup}}},tokenizePlaceholders:{value:function(t,a){if(t.language===a&&t.tokenStack){t.grammar=e.languages[a];var r=0,o=Object.keys(t.tokenStack);!function c(i){for(var u=0;u<i.length&&!(r>=o.length);u++){var g=i[u];if("string"==typeof g||g.content&&"string"==typeof g.content){var l=o[r],s=t.tokenStack[l],f="string"==typeof g?g:g.content,p=n(a,l),k=f.indexOf(p);if(k>-1){++r;var m=f.substring(0,k),d=new e.Token(a,e.tokenize(s,t.grammar),"language-"+a,s),h=f.substring(k+p.length),v=[];m&&v.push.apply(v,c([m])),v.push(d),h&&v.push.apply(v,c([h])),"string"==typeof g?i.splice.apply(i,[u,1].concat(v)):g.content=v}}else g.content&&c(g.content)}return i}(t.tokens)}}}})}(Prism);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Prism.languages.markup={comment:{pattern:/<!--(?:(?!<!--)[\s\S])*?-->/,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/<!DOCTYPE(?:[^>"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|<!--(?:[^-]|-(?!->))*-->)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^<!|>$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern:/<!\[CDATA\[[\s\S]*?\]\]>/i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]},Prism.languages.markup.tag.inside["attr-value"].inside.entity=Prism.languages.markup.entity,Prism.languages.markup.doctype.inside["internal-subset"].inside=Prism.languages.markup,Prism.hooks.add("wrap",(function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&/,"&"))})),Object.defineProperty(Prism.languages.markup.tag,"addInlined",{value:function(a,e){var s={};s["language-"+e]={pattern:/(^<!\[CDATA\[)[\s\S]+?(?=\]\]>$)/i,lookbehind:!0,inside:Prism.languages[e]},s.cdata=/^<!\[CDATA\[|\]\]>$/i;var t={"included-cdata":{pattern:/<!\[CDATA\[[\s\S]*?\]\]>/i,inside:s}};t["language-"+e]={pattern:/[\s\S]+/,inside:Prism.languages[e]};var n={};n[a]={pattern:RegExp("(<__[^>]*>)(?:<!\\[CDATA\\[(?:[^\\]]|\\](?!\\]>))*\\]\\]>|(?!<!\\[CDATA\\[)[^])*?(?=</__>)".replace(/__/g,(function(){return a})),"i"),lookbehind:!0,greedy:!0,inside:t},Prism.languages.insertBefore("markup","cdata",n)}}),Object.defineProperty(Prism.languages.markup.tag,"addAttribute",{value:function(a,e){Prism.languages.markup.tag.inside["special-attr"].push({pattern:RegExp("(^|[\"'\\s])(?:"+a+")\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s'\">=]+(?=[\\s>]))","i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[e,"language-"+e],inside:Prism.languages[e]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup,Prism.languages.xml=Prism.languages.extend("markup",{}),Prism.languages.ssml=Prism.languages.xml,Prism.languages.atom=Prism.languages.xml,Prism.languages.rss=Prism.languages.xml;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
!function($){var e=["$eq","$gt","$gte","$in","$lt","$lte","$ne","$nin","$and","$not","$nor","$or","$exists","$type","$expr","$jsonSchema","$mod","$regex","$text","$where","$geoIntersects","$geoWithin","$near","$nearSphere","$all","$elemMatch","$size","$bitsAllClear","$bitsAllSet","$bitsAnyClear","$bitsAnySet","$comment","$elemMatch","$meta","$slice","$currentDate","$inc","$min","$max","$mul","$rename","$set","$setOnInsert","$unset","$addToSet","$pop","$pull","$push","$pullAll","$each","$position","$slice","$sort","$bit","$addFields","$bucket","$bucketAuto","$collStats","$count","$currentOp","$facet","$geoNear","$graphLookup","$group","$indexStats","$limit","$listLocalSessions","$listSessions","$lookup","$match","$merge","$out","$planCacheStats","$project","$redact","$replaceRoot","$replaceWith","$sample","$set","$skip","$sort","$sortByCount","$unionWith","$unset","$unwind","$setWindowFields","$abs","$accumulator","$acos","$acosh","$add","$addToSet","$allElementsTrue","$and","$anyElementTrue","$arrayElemAt","$arrayToObject","$asin","$asinh","$atan","$atan2","$atanh","$avg","$binarySize","$bsonSize","$ceil","$cmp","$concat","$concatArrays","$cond","$convert","$cos","$dateFromParts","$dateToParts","$dateFromString","$dateToString","$dayOfMonth","$dayOfWeek","$dayOfYear","$degreesToRadians","$divide","$eq","$exp","$filter","$first","$floor","$function","$gt","$gte","$hour","$ifNull","$in","$indexOfArray","$indexOfBytes","$indexOfCP","$isArray","$isNumber","$isoDayOfWeek","$isoWeek","$isoWeekYear","$last","$last","$let","$literal","$ln","$log","$log10","$lt","$lte","$ltrim","$map","$max","$mergeObjects","$meta","$min","$millisecond","$minute","$mod","$month","$multiply","$ne","$not","$objectToArray","$or","$pow","$push","$radiansToDegrees","$range","$reduce","$regexFind","$regexFindAll","$regexMatch","$replaceOne","$replaceAll","$reverseArray","$round","$rtrim","$second","$setDifference","$setEquals","$setIntersection","$setIsSubset","$setUnion","$size","$sin","$slice","$split","$sqrt","$stdDevPop","$stdDevSamp","$strcasecmp","$strLenBytes","$strLenCP","$substr","$substrBytes","$substrCP","$subtract","$sum","$switch","$tan","$toBool","$toDate","$toDecimal","$toDouble","$toInt","$toLong","$toObjectId","$toString","$toLower","$toUpper","$trim","$trunc","$type","$week","$year","$zip","$count","$dateAdd","$dateDiff","$dateSubtract","$dateTrunc","$getField","$rand","$sampleRate","$setField","$unsetField","$comment","$explain","$hint","$max","$maxTimeMS","$min","$orderby","$query","$returnKey","$showDiskLoc","$natural"],t="(?:"+(e=e.map((function($){return $.replace("$","\\$")}))).join("|")+")\\b";$.languages.mongodb=$.languages.extend("javascript",{}),$.languages.insertBefore("mongodb","string",{property:{pattern:/(?:(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)(?=\s*:)/,greedy:!0,inside:{keyword:RegExp("^(['\"])?"+t+"(?:\\1)?$")}}}),$.languages.mongodb.string.inside={url:{pattern:/https?:\/\/[-\w@:%.+~#=]{1,256}\.[a-z0-9()]{1,6}\b[-\w()@:%+.~#?&/=]*/i,greedy:!0},entity:{pattern:/\b(?:(?:[01]?\d\d?|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d\d?|2[0-4]\d|25[0-5])\b/,greedy:!0}},$.languages.insertBefore("mongodb","constant",{builtin:{pattern:RegExp("\\b(?:"+["ObjectId","Code","BinData","DBRef","Timestamp","NumberLong","NumberDecimal","MaxKey","MinKey","RegExp","ISODate","UUID"].join("|")+")\\b"),alias:"keyword"}})}(Prism);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
!function(e){var n=/\$(?:\w[a-z\d]*(?:_[^\x00-\x1F\s"'\\()$]*)?|\{[^}\s"'\\]+\})/i;e.languages.nginx={comment:{pattern:/(^|[\s{};])#.*/,lookbehind:!0,greedy:!0},directive:{pattern:/(^|\s)\w(?:[^;{}"'\\\s]|\\.|"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|\s+(?:#.*(?!.)|(?![#\s])))*?(?=\s*[;{])/,lookbehind:!0,greedy:!0,inside:{string:{pattern:/((?:^|[^\\])(?:\\\\)*)(?:"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')/,lookbehind:!0,greedy:!0,inside:{escape:{pattern:/\\["'\\nrt]/,alias:"entity"},variable:n}},comment:{pattern:/(\s)#.*/,lookbehind:!0,greedy:!0},keyword:{pattern:/^\S+/,greedy:!0},boolean:{pattern:/(\s)(?:off|on)(?!\S)/,lookbehind:!0},number:{pattern:/(\s)\d+[a-z]*(?!\S)/i,lookbehind:!0},variable:n}},punctuation:/[{};]/}}(Prism);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Prism.languages.objectivec=Prism.languages.extend("c",{string:{pattern:/@?"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/,greedy:!0},keyword:/\b(?:asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|in|inline|int|long|register|return|self|short|signed|sizeof|static|struct|super|switch|typedef|typeof|union|unsigned|void|volatile|while)\b|(?:@interface|@end|@implementation|@protocol|@class|@public|@protected|@private|@property|@try|@catch|@finally|@throw|@synthesize|@dynamic|@selector)\b/,operator:/-[->]?|\+\+?|!=?|<<?=?|>>?=?|==?|&&?|\|\|?|[~^%?*\/@]/}),delete Prism.languages.objectivec["class-name"],Prism.languages.objc=Prism.languages.objectivec;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
!function(e){var n="(?:\\((?:[^()\\\\]|\\\\[^])*\\)|\\{(?:[^{}\\\\]|\\\\[^])*\\}|\\[(?:[^[\\]\\\\]|\\\\[^])*\\]|<(?:[^<>\\\\]|\\\\[^])*>)";e.languages.perl={comment:[{pattern:/(^\s*)=\w[\s\S]*?=cut.*/m,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\$])#.*/,lookbehind:!0,greedy:!0}],string:[{pattern:RegExp("\\b(?:q|qq|qw|qx)(?![a-zA-Z0-9])\\s*(?:"+["([^a-zA-Z0-9\\s{(\\[<])(?:(?!\\1)[^\\\\]|\\\\[^])*\\1","([a-zA-Z0-9])(?:(?!\\2)[^\\\\]|\\\\[^])*\\2",n].join("|")+")"),greedy:!0},{pattern:/("|`)(?:(?!\1)[^\\]|\\[\s\S])*\1/,greedy:!0},{pattern:/'(?:[^'\\\r\n]|\\.)*'/,greedy:!0}],regex:[{pattern:RegExp("\\b(?:m|qr)(?![a-zA-Z0-9])\\s*(?:"+["([^a-zA-Z0-9\\s{(\\[<])(?:(?!\\1)[^\\\\]|\\\\[^])*\\1","([a-zA-Z0-9])(?:(?!\\2)[^\\\\]|\\\\[^])*\\2",n].join("|")+")[msixpodualngc]*"),greedy:!0},{pattern:RegExp("(^|[^-])\\b(?:s|tr|y)(?![a-zA-Z0-9])\\s*(?:"+["([^a-zA-Z0-9\\s{(\\[<])(?:(?!\\2)[^\\\\]|\\\\[^])*\\2(?:(?!\\2)[^\\\\]|\\\\[^])*\\2","([a-zA-Z0-9])(?:(?!\\3)[^\\\\]|\\\\[^])*\\3(?:(?!\\3)[^\\\\]|\\\\[^])*\\3",n+"\\s*"+n].join("|")+")[msixpodualngcer]*"),lookbehind:!0,greedy:!0},{pattern:/\/(?:[^\/\\\r\n]|\\.)*\/[msixpodualngc]*(?=\s*(?:$|[\r\n,.;})&|\-+*~<>!?^]|(?:and|cmp|eq|ge|gt|le|lt|ne|not|or|x|xor)\b))/,greedy:!0}],variable:[/[&*$@%]\{\^[A-Z]+\}/,/[&*$@%]\^[A-Z_]/,/[&*$@%]#?(?=\{)/,/[&*$@%]#?(?:(?:::)*'?(?!\d)[\w$]+(?![\w$]))+(?:::)*/,/[&*$@%]\d+/,/(?!%=)[$@%][!"#$%&'()*+,\-.\/:;<=>?@[\\\]^_`{|}~]/],filehandle:{pattern:/<(?![<=])\S*?>|\b_\b/,alias:"symbol"},"v-string":{pattern:/v\d+(?:\.\d+)*|\d+(?:\.\d+){2,}/,alias:"string"},function:{pattern:/(\bsub[ \t]+)\w+/,lookbehind:!0},keyword:/\b(?:any|break|continue|default|delete|die|do|else|elsif|eval|for|foreach|given|goto|if|last|local|my|next|our|package|print|redo|require|return|say|state|sub|switch|undef|unless|until|use|when|while)\b/,number:/\b(?:0x[\dA-Fa-f](?:_?[\dA-Fa-f])*|0b[01](?:_?[01])*|(?:(?:\d(?:_?\d)*)?\.)?\d(?:_?\d)*(?:[Ee][+-]?\d+)?)\b/,operator:/-[rwxoRWXOezsfdlpSbctugkTBMAC]\b|\+[+=]?|-[-=>]?|\*\*?=?|\/\/?=?|=[=~>]?|~[~=]?|\|\|?=?|&&?=?|<(?:=>?|<=?)?|>>?=?|![~=]?|[%^]=?|\.(?:=|\.\.?)?|[\\?]|\bx(?:=|\b)|\b(?:and|cmp|eq|ge|gt|le|lt|ne|not|or|xor)\b/,punctuation:/[{}[\];(),:]/}}(Prism);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Prism.languages.insertBefore("php","variable",{this:{pattern:/\$this\b/,alias:"keyword"},global:/\$(?:GLOBALS|HTTP_RAW_POST_DATA|_(?:COOKIE|ENV|FILES|GET|POST|REQUEST|SERVER|SESSION)|argc|argv|http_response_header|php_errormsg)\b/,scope:{pattern:/\b[\w\\]+::/,inside:{keyword:/\b(?:parent|self|static)\b/,punctuation:/::|\\/}}});
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
|||||||
Prism.languages.plsql=Prism.languages.extend("sql",{comment:{pattern:/\/\*[\s\S]*?\*\/|--.*/,greedy:!0},keyword:/\b(?:A|ACCESSIBLE|ADD|AGENT|AGGREGATE|ALL|ALTER|AND|ANY|ARRAY|AS|ASC|AT|ATTRIBUTE|AUTHID|AVG|BEGIN|BETWEEN|BFILE_BASE|BINARY|BLOB_BASE|BLOCK|BODY|BOTH|BOUND|BULK|BY|BYTE|C|CALL|CALLING|CASCADE|CASE|CHAR|CHARACTER|CHARSET|CHARSETFORM|CHARSETID|CHAR_BASE|CHECK|CLOB_BASE|CLONE|CLOSE|CLUSTER|CLUSTERS|COLAUTH|COLLECT|COLUMNS|COMMENT|COMMIT|COMMITTED|COMPILED|COMPRESS|CONNECT|CONSTANT|CONSTRUCTOR|CONTEXT|CONTINUE|CONVERT|COUNT|CRASH|CREATE|CREDENTIAL|CURRENT|CURSOR|CUSTOMDATUM|DANGLING|DATA|DATE|DATE_BASE|DAY|DECLARE|DEFAULT|DEFINE|DELETE|DESC|DETERMINISTIC|DIRECTORY|DISTINCT|DOUBLE|DROP|DURATION|ELEMENT|ELSE|ELSIF|EMPTY|END|ESCAPE|EXCEPT|EXCEPTION|EXCEPTIONS|EXCLUSIVE|EXECUTE|EXISTS|EXIT|EXTERNAL|FETCH|FINAL|FIRST|FIXED|FLOAT|FOR|FORALL|FORCE|FROM|FUNCTION|GENERAL|GOTO|GRANT|GROUP|HASH|HAVING|HEAP|HIDDEN|HOUR|IDENTIFIED|IF|IMMEDIATE|IMMUTABLE|IN|INCLUDING|INDEX|INDEXES|INDICATOR|INDICES|INFINITE|INSERT|INSTANTIABLE|INT|INTERFACE|INTERSECT|INTERVAL|INTO|INVALIDATE|IS|ISOLATION|JAVA|LANGUAGE|LARGE|LEADING|LENGTH|LEVEL|LIBRARY|LIKE|LIKE2|LIKE4|LIKEC|LIMIT|LIMITED|LOCAL|LOCK|LONG|LOOP|MAP|MAX|MAXLEN|MEMBER|MERGE|MIN|MINUS|MINUTE|MOD|MODE|MODIFY|MONTH|MULTISET|MUTABLE|NAME|NAN|NATIONAL|NATIVE|NCHAR|NEW|NOCOMPRESS|NOCOPY|NOT|NOWAIT|NULL|NUMBER_BASE|OBJECT|OCICOLL|OCIDATE|OCIDATETIME|OCIDURATION|OCIINTERVAL|OCILOBLOCATOR|OCINUMBER|OCIRAW|OCIREF|OCIREFCURSOR|OCIROWID|OCISTRING|OCITYPE|OF|OLD|ON|ONLY|OPAQUE|OPEN|OPERATOR|OPTION|OR|ORACLE|ORADATA|ORDER|ORGANIZATION|ORLANY|ORLVARY|OTHERS|OUT|OVERLAPS|OVERRIDING|PACKAGE|PARALLEL_ENABLE|PARAMETER|PARAMETERS|PARENT|PARTITION|PASCAL|PERSISTABLE|PIPE|PIPELINED|PLUGGABLE|POLYMORPHIC|PRAGMA|PRECISION|PRIOR|PRIVATE|PROCEDURE|PUBLIC|RAISE|RANGE|RAW|READ|RECORD|REF|REFERENCE|RELIES_ON|REM|REMAINDER|RENAME|RESOURCE|RESULT|RESULT_CACHE|RETURN|RETURNING|REVERSE|REVOKE|ROLLBACK|ROW|SAMPLE|SAVE|SAVEPOINT|SB1|SB2|SB4|SECOND|SEGMENT|SELECT|SELF|SEPARATE|SEQUENCE|SERIALIZABLE|SET|SHARE|SHORT|SIZE|SIZE_T|SOME|SPARSE|SQL|SQLCODE|SQLDATA|SQLNAME|SQLSTATE|STANDARD|START|STATIC|STDDEV|STORED|STRING|STRUCT|STYLE|SUBMULTISET|SUBPARTITION|SUBSTITUTABLE|SUBTYPE|SUM|SYNONYM|TABAUTH|TABLE|TDO|THE|THEN|TIME|TIMESTAMP|TIMEZONE_ABBR|TIMEZONE_HOUR|TIMEZONE_MINUTE|TIMEZONE_REGION|TO|TRAILING|TRANSACTION|TRANSACTIONAL|TRUSTED|TYPE|UB1|UB2|UB4|UNDER|UNION|UNIQUE|UNPLUG|UNSIGNED|UNTRUSTED|UPDATE|USE|USING|VALIST|VALUE|VALUES|VARIABLE|VARIANCE|VARRAY|VARYING|VIEW|VIEWS|VOID|WHEN|WHERE|WHILE|WITH|WORK|WRAPPED|WRITE|YEAR|ZONE)\b/i,operator:/:=?|=>|[<>^~!]=|\.\.|\|\||\*\*|[-+*/%<>=@]/}),Prism.languages.insertBefore("plsql","operator",{label:{pattern:/<<\s*\w+\s*>>/,alias:"symbol"}});
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
!function(e){var i=e.languages.powershell={comment:[{pattern:/(^|[^`])<#[\s\S]*?#>/,lookbehind:!0},{pattern:/(^|[^`])#.*/,lookbehind:!0}],string:[{pattern:/"(?:`[\s\S]|[^`"])*"/,greedy:!0,inside:null},{pattern:/'(?:[^']|'')*'/,greedy:!0}],namespace:/\[[a-z](?:\[(?:\[[^\]]*\]|[^\[\]])*\]|[^\[\]])*\]/i,boolean:/\$(?:false|true)\b/i,variable:/\$\w+\b/,function:[/\b(?:Add|Approve|Assert|Backup|Block|Checkpoint|Clear|Close|Compare|Complete|Compress|Confirm|Connect|Convert|ConvertFrom|ConvertTo|Copy|Debug|Deny|Disable|Disconnect|Dismount|Edit|Enable|Enter|Exit|Expand|Export|Find|ForEach|Format|Get|Grant|Group|Hide|Import|Initialize|Install|Invoke|Join|Limit|Lock|Measure|Merge|Move|New|Open|Optimize|Out|Ping|Pop|Protect|Publish|Push|Read|Receive|Redo|Register|Remove|Rename|Repair|Request|Reset|Resize|Resolve|Restart|Restore|Resume|Revoke|Save|Search|Select|Send|Set|Show|Skip|Sort|Split|Start|Step|Stop|Submit|Suspend|Switch|Sync|Tee|Test|Trace|Unblock|Undo|Uninstall|Unlock|Unprotect|Unpublish|Unregister|Update|Use|Wait|Watch|Where|Write)-[a-z]+\b/i,/\b(?:ac|cat|chdir|clc|cli|clp|clv|compare|copy|cp|cpi|cpp|cvpa|dbp|del|diff|dir|ebp|echo|epal|epcsv|epsn|erase|fc|fl|ft|fw|gal|gbp|gc|gci|gcs|gdr|gi|gl|gm|gp|gps|group|gsv|gu|gv|gwmi|iex|ii|ipal|ipcsv|ipsn|irm|iwmi|iwr|kill|lp|ls|measure|mi|mount|move|mp|mv|nal|ndr|ni|nv|ogv|popd|ps|pushd|pwd|rbp|rd|rdr|ren|ri|rm|rmdir|rni|rnp|rp|rv|rvpa|rwmi|sal|saps|sasv|sbp|sc|select|set|shcm|si|sl|sleep|sls|sort|sp|spps|spsv|start|sv|swmi|tee|trcm|type|write)\b/i],keyword:/\b(?:Begin|Break|Catch|Class|Continue|Data|Define|Do|DynamicParam|Else|ElseIf|End|Exit|Filter|Finally|For|ForEach|From|Function|If|InlineScript|Parallel|Param|Process|Return|Sequence|Switch|Throw|Trap|Try|Until|Using|Var|While|Workflow)\b/i,operator:{pattern:/(^|\W)(?:!|-(?:b?(?:and|x?or)|as|(?:Not)?(?:Contains|In|Like|Match)|eq|ge|gt|is(?:Not)?|Join|le|lt|ne|not|Replace|sh[lr])\b|-[-=]?|\+[+=]?|[*\/%]=?)/i,lookbehind:!0},punctuation:/[|{}[\];(),.]/};i.string[0].inside={function:{pattern:/(^|[^`])\$\((?:\$\([^\r\n()]*\)|(?!\$\()[^\r\n)])*\)/,lookbehind:!0,inside:i},boolean:i.boolean,variable:i.variable}}(Prism);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Prism.languages.properties={comment:/^[ \t]*[#!].*$/m,value:{pattern:/(^[ \t]*(?:\\(?:\r\n|[\s\S])|[^\\\s:=])+(?: *[=:] *(?! )| ))(?:\\(?:\r\n|[\s\S])|[^\\\r\n])+/m,lookbehind:!0,alias:"attr-value"},key:{pattern:/^[ \t]*(?:\\(?:\r\n|[\s\S])|[^\\\s:=])+(?= *[=:]| )/m,alias:"attr-name"},punctuation:/[=:]/};
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
!function(e){var s=/\b(?:bool|bytes|double|s?fixed(?:32|64)|float|[su]?int(?:32|64)|string)\b/;e.languages.protobuf=e.languages.extend("clike",{"class-name":[{pattern:/(\b(?:enum|extend|message|service)\s+)[A-Za-z_]\w*(?=\s*\{)/,lookbehind:!0},{pattern:/(\b(?:rpc\s+\w+|returns)\s*\(\s*(?:stream\s+)?)\.?[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*(?=\s*\))/,lookbehind:!0}],keyword:/\b(?:enum|extend|extensions|import|message|oneof|option|optional|package|public|repeated|required|reserved|returns|rpc(?=\s+\w)|service|stream|syntax|to)\b(?!\s*=\s*\d)/,function:/\b[a-z_]\w*(?=\s*\()/i}),e.languages.insertBefore("protobuf","operator",{map:{pattern:/\bmap<\s*[\w.]+\s*,\s*[\w.]+\s*>(?=\s+[a-z_]\w*\s*[=;])/i,alias:"class-name",inside:{punctuation:/[<>.,]/,builtin:s}},builtin:s,"positional-class-name":{pattern:/(?:\b|\B\.)[a-z_]\w*(?:\.[a-z_]\w*)*(?=\s+[a-z_]\w*\s*[=;])/i,alias:"class-name",inside:{punctuation:/\./}},annotation:{pattern:/(\[\s*)[a-z_]\w*(?=\s*=)/i,lookbehind:!0}})}(Prism);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Prism.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0,greedy:!0},"string-interpolation":{pattern:/(?:f|fr|rf)(?:("""|''')[\s\S]*?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i,greedy:!0,inside:{interpolation:{pattern:/((?:^|[^{])(?:\{\{)*)\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}])+\})+\})+\}/,lookbehind:!0,inside:{"format-spec":{pattern:/(:)[^:(){}]+(?=\}$)/,lookbehind:!0},"conversion-option":{pattern://,alias:"punctuation"},rest:null}},string:/[\s\S]+/}},"triple-quoted-string":{pattern:/(?:[rub]|br|rb)?("""|''')[\s\S]*?\1/i,greedy:!0,alias:"string"},string:{pattern:/(?:[rub]|br|rb)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i,greedy:!0},function:{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)\w+/i,lookbehind:!0},decorator:{pattern:/(^[\t ]*)@\w+(?:\.\w+)*/m,lookbehind:!0,alias:["annotation","punctuation"],inside:{punctuation:/\./}},keyword:/\b(?:_(?=\s*:)|and|as|assert|async|await|break|case|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|match|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\b/,builtin:/\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b/,boolean:/\b(?:False|None|True)\b/,number:/\b0(?:b(?:_?[01])+|o(?:_?[0-7])+|x(?:_?[a-f0-9])+)\b|(?:\b\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\B\.\d+(?:_\d+)*)(?:e[+-]?\d+(?:_\d+)*)?j?(?!\w)/i,operator:/[-+%=]=?|!=|:=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,punctuation:/[{}[\];(),.:]/},Prism.languages.python["string-interpolation"].inside.interpolation.inside.rest=Prism.languages.python,Prism.languages.py=Prism.languages.python;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Prism.languages.r={comment:/#.*/,string:{pattern:/(['"])(?:\\.|(?!\1)[^\\\r\n])*\1/,greedy:!0},"percent-operator":{pattern:/%[^%\s]*%/,alias:"operator"},boolean:/\b(?:FALSE|TRUE)\b/,ellipsis:/\.\.(?:\.|\d+)/,number:[/\b(?:Inf|NaN)\b/,/(?:\b0x[\dA-Fa-f]+(?:\.\d*)?|\b\d+(?:\.\d*)?|\B\.\d+)(?:[EePp][+-]?\d+)?[iL]?/],keyword:/\b(?:NA|NA_character_|NA_complex_|NA_integer_|NA_real_|NULL|break|else|for|function|if|in|next|repeat|while)\b/,operator:/->?>?|<(?:=|<?-)?|[>=!]=?|::?|&&?|\|\|?|[+*\/^$@~]/,punctuation:/[(){}\[\],;]/};
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
!function(a){var e={pattern:/\\[\\(){}[\]^$+*?|.]/,alias:"escape"},n=/\\(?:x[\da-fA-F]{2}|u[\da-fA-F]{4}|u\{[\da-fA-F]+\}|0[0-7]{0,2}|[123][0-7]{2}|c[a-zA-Z]|.)/,t="(?:[^\\\\-]|"+n.source+")",s=RegExp(t+"-"+t),i={pattern:/(<|')[^<>']+(?=[>']$)/,lookbehind:!0,alias:"variable"};a.languages.regex={"char-class":{pattern:/((?:^|[^\\])(?:\\\\)*)\[(?:[^\\\]]|\\[\s\S])*\]/,lookbehind:!0,inside:{"char-class-negation":{pattern:/(^\[)\^/,lookbehind:!0,alias:"operator"},"char-class-punctuation":{pattern:/^\[|\]$/,alias:"punctuation"},range:{pattern:s,inside:{escape:n,"range-punctuation":{pattern:/-/,alias:"operator"}}},"special-escape":e,"char-set":{pattern:/\\[wsd]|\\p\{[^{}]+\}/i,alias:"class-name"},escape:n}},"special-escape":e,"char-set":{pattern:/\.|\\[wsd]|\\p\{[^{}]+\}/i,alias:"class-name"},backreference:[{pattern:/\\(?![123][0-7]{2})[1-9]/,alias:"keyword"},{pattern:/\\k<[^<>']+>/,alias:"keyword",inside:{"group-name":i}}],anchor:{pattern:/[$^]|\\[ABbGZz]/,alias:"function"},escape:n,group:[{pattern:/\((?:\?(?:<[^<>']+>|'[^<>']+'|[>:]|<?[=!]|[idmnsuxU]+(?:-[idmnsuxU]+)?:?))?/,alias:"punctuation",inside:{"group-name":i}},{pattern:/\)/,alias:"punctuation"}],quantifier:{pattern:/(?:[+*?]|\{\d+(?:,\d*)?\})[?+]?/,alias:"number"},alternation:{pattern:/\|/,alias:"keyword"}}}(Prism);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
!function(e){e.languages.ruby=e.languages.extend("clike",{comment:{pattern:/#.*|^=begin\s[\s\S]*?^=end/m,greedy:!0},"class-name":{pattern:/(\b(?:class|module)\s+|\bcatch\s+\()[\w.\\]+|\b[A-Z_]\w*(?=\s*\.\s*new\b)/,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:BEGIN|END|alias|and|begin|break|case|class|def|define_method|defined|do|each|else|elsif|end|ensure|extend|for|if|in|include|module|new|next|nil|not|or|prepend|private|protected|public|raise|redo|require|rescue|retry|return|self|super|then|throw|undef|unless|until|when|while|yield)\b/,operator:/\.{2,3}|&\.|===|<?=>|[!=]?~|(?:&&|\|\||<<|>>|\*\*|[+\-*/%<>!^&|=])=?|[?:]/,punctuation:/[(){}[\].,;]/}),e.languages.insertBefore("ruby","operator",{"double-colon":{pattern:/::/,alias:"punctuation"}});var n={pattern:/((?:^|[^\\])(?:\\{2})*)#\{(?:[^{}]|\{[^{}]*\})*\}/,lookbehind:!0,inside:{content:{pattern:/^(#\{)[\s\S]+(?=\}$)/,lookbehind:!0,inside:e.languages.ruby},delimiter:{pattern:/^#\{|\}$/,alias:"punctuation"}}};delete e.languages.ruby.function;var t="(?:"+["([^a-zA-Z0-9\\s{(\\[<=])(?:(?!\\1)[^\\\\]|\\\\[^])*\\1","\\((?:[^()\\\\]|\\\\[^]|\\((?:[^()\\\\]|\\\\[^])*\\))*\\)","\\{(?:[^{}\\\\]|\\\\[^]|\\{(?:[^{}\\\\]|\\\\[^])*\\})*\\}","\\[(?:[^\\[\\]\\\\]|\\\\[^]|\\[(?:[^\\[\\]\\\\]|\\\\[^])*\\])*\\]","<(?:[^<>\\\\]|\\\\[^]|<(?:[^<>\\\\]|\\\\[^])*>)*>"].join("|")+")",i='(?:"(?:\\\\.|[^"\\\\\r\n])*"|(?:\\b[a-zA-Z_]\\w*|[^\\s\0-\\x7F]+)[?!]?|\\$.)';e.languages.insertBefore("ruby","keyword",{"regex-literal":[{pattern:RegExp("%r"+t+"[egimnosux]{0,6}"),greedy:!0,inside:{interpolation:n,regex:/[\s\S]+/}},{pattern:/(^|[^/])\/(?!\/)(?:\[[^\r\n\]]+\]|\\.|[^[/\\\r\n])+\/[egimnosux]{0,6}(?=\s*(?:$|[\r\n,.;})#]))/,lookbehind:!0,greedy:!0,inside:{interpolation:n,regex:/[\s\S]+/}}],variable:/[@$]+[a-zA-Z_]\w*(?:[?!]|\b)/,symbol:[{pattern:RegExp("(^|[^:]):"+i),lookbehind:!0,greedy:!0},{pattern:RegExp("([\r\n{(,][ \t]*)"+i+"(?=:(?!:))"),lookbehind:!0,greedy:!0}],"method-definition":{pattern:/(\bdef\s+)\w+(?:\s*\.\s*\w+)?/,lookbehind:!0,inside:{function:/\b\w+$/,keyword:/^self\b/,"class-name":/^\w+/,punctuation:/\./}}}),e.languages.insertBefore("ruby","string",{"string-literal":[{pattern:RegExp("%[qQiIwWs]?"+t),greedy:!0,inside:{interpolation:n,string:/[\s\S]+/}},{pattern:/("|')(?:#\{[^}]+\}|#(?!\{)|\\(?:\r\n|[\s\S])|(?!\1)[^\\#\r\n])*\1/,greedy:!0,inside:{interpolation:n,string:/[\s\S]+/}},{pattern:/<<[-~]?([a-z_]\w*)[\r\n](?:.*[\r\n])*?[\t ]*\1/i,alias:"heredoc-string",greedy:!0,inside:{delimiter:{pattern:/^<<[-~]?[a-z_]\w*|\b[a-z_]\w*$/i,inside:{symbol:/\b\w+/,punctuation:/^<<[-~]?/}},interpolation:n,string:/[\s\S]+/}},{pattern:/<<[-~]?'([a-z_]\w*)'[\r\n](?:.*[\r\n])*?[\t ]*\1/i,alias:"heredoc-string",greedy:!0,inside:{delimiter:{pattern:/^<<[-~]?'[a-z_]\w*'|\b[a-z_]\w*$/i,inside:{symbol:/\b\w+/,punctuation:/^<<[-~]?'|'$/}},string:/[\s\S]+/}}],"command-literal":[{pattern:RegExp("%x"+t),greedy:!0,inside:{interpolation:n,command:{pattern:/[\s\S]+/,alias:"string"}}},{pattern:/`(?:#\{[^}]+\}|#(?!\{)|\\(?:\r\n|[\s\S])|[^\\`#\r\n])*`/,greedy:!0,inside:{interpolation:n,command:{pattern:/[\s\S]+/,alias:"string"}}}]}),delete e.languages.ruby.string,e.languages.insertBefore("ruby","number",{builtin:/\b(?:Array|Bignum|Binding|Class|Continuation|Dir|Exception|FalseClass|File|Fixnum|Float|Hash|IO|Integer|MatchData|Method|Module|NilClass|Numeric|Object|Proc|Range|Regexp|Stat|String|Struct|Symbol|TMS|Thread|ThreadGroup|Time|TrueClass)\b/,constant:/\b[A-Z][A-Z0-9_]*(?:[?!]|\b)/}),e.languages.rb=e.languages.ruby}(Prism);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
!function(e){for(var a="/\\*(?:[^*/]|\\*(?!/)|/(?!\\*)|<self>)*\\*/",t=0;t<2;t++)a=a.replace(/<self>/g,(function(){return a}));a=a.replace(/<self>/g,(function(){return"[^\\s\\S]"})),e.languages.rust={comment:[{pattern:RegExp("(^|[^\\\\])"+a),lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/b?"(?:\\[\s\S]|[^\\"])*"|b?r(#*)"(?:[^"]|"(?!\1))*"\1/,greedy:!0},char:{pattern:/b?'(?:\\(?:x[0-7][\da-fA-F]|u\{(?:[\da-fA-F]_*){1,6}\}|.)|[^\\\r\n\t'])'/,greedy:!0},attribute:{pattern:/#!?\[(?:[^\[\]"]|"(?:\\[\s\S]|[^\\"])*")*\]/,greedy:!0,alias:"attr-name",inside:{string:null}},"closure-params":{pattern:/([=(,:]\s*|\bmove\s*)\|[^|]*\||\|[^|]*\|(?=\s*(?:\{|->))/,lookbehind:!0,greedy:!0,inside:{"closure-punctuation":{pattern:/^\||\|$/,alias:"punctuation"},rest:null}},"lifetime-annotation":{pattern:/'\w+/,alias:"symbol"},"fragment-specifier":{pattern:/(\$\w+:)[a-z]+/,lookbehind:!0,alias:"punctuation"},variable:/\$\w+/,"function-definition":{pattern:/(\bfn\s+)\w+/,lookbehind:!0,alias:"function"},"type-definition":{pattern:/(\b(?:enum|struct|trait|type|union)\s+)\w+/,lookbehind:!0,alias:"class-name"},"module-declaration":[{pattern:/(\b(?:crate|mod)\s+)[a-z][a-z_\d]*/,lookbehind:!0,alias:"namespace"},{pattern:/(\b(?:crate|self|super)\s*)::\s*[a-z][a-z_\d]*\b(?:\s*::(?:\s*[a-z][a-z_\d]*\s*::)*)?/,lookbehind:!0,alias:"namespace",inside:{punctuation:/::/}}],keyword:[/\b(?:Self|abstract|as|async|await|become|box|break|const|continue|crate|do|dyn|else|enum|extern|final|fn|for|if|impl|in|let|loop|macro|match|mod|move|mut|override|priv|pub|ref|return|self|static|struct|super|trait|try|type|typeof|union|unsafe|unsized|use|virtual|where|while|yield)\b/,/\b(?:bool|char|f(?:32|64)|[ui](?:8|16|32|64|128|size)|str)\b/],function:/\b[a-z_]\w*(?=\s*(?:::\s*<|\())/,macro:{pattern:/\b\w+!/,alias:"property"},constant:/\b[A-Z_][A-Z_\d]+\b/,"class-name":/\b[A-Z]\w*\b/,namespace:{pattern:/(?:\b[a-z][a-z_\d]*\s*::\s*)*\b[a-z][a-z_\d]*\s*::(?!\s*<)/,inside:{punctuation:/::/}},number:/\b(?:0x[\dA-Fa-f](?:_?[\dA-Fa-f])*|0o[0-7](?:_?[0-7])*|0b[01](?:_?[01])*|(?:(?:\d(?:_?\d)*)?\.)?\d(?:_?\d)*(?:[Ee][+-]?\d+)?)(?:_?(?:f32|f64|[iu](?:8|16|32|64|size)?))?\b/,boolean:/\b(?:false|true)\b/,punctuation:/->|\.\.=|\.{1,3}|::|[{}[\];(),:]/,operator:/[-+*\/%!^]=?|=[=>]?|&[&=]?|\|[|=]?|<<?=?|>>?=?|[@?]/},e.languages.rust["closure-params"].inside.rest=e.languages.rust,e.languages.rust.attribute.inside.string=e.languages.rust.string}(Prism);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
!function(e){e.languages.sass=e.languages.extend("css",{comment:{pattern:/^([ \t]*)\/[\/*].*(?:(?:\r?\n|\r)\1[ \t].+)*/m,lookbehind:!0,greedy:!0}}),e.languages.insertBefore("sass","atrule",{"atrule-line":{pattern:/^(?:[ \t]*)[@+=].+/m,greedy:!0,inside:{atrule:/(?:@[\w-]+|[+=])/}}}),delete e.languages.sass.atrule;var r=/\$[-\w]+|#\{\$[-\w]+\}/,t=[/[+*\/%]|[=!]=|<=?|>=?|\b(?:and|not|or)\b/,{pattern:/(\s)-(?=\s)/,lookbehind:!0}];e.languages.insertBefore("sass","property",{"variable-line":{pattern:/^[ \t]*\$.+/m,greedy:!0,inside:{punctuation:/:/,variable:r,operator:t}},"property-line":{pattern:/^[ \t]*(?:[^:\s]+ *:.*|:[^:\s].*)/m,greedy:!0,inside:{property:[/[^:\s]+(?=\s*:)/,{pattern:/(:)[^:\s]+/,lookbehind:!0}],punctuation:/:/,variable:r,operator:t,important:e.languages.sass.important}}}),delete e.languages.sass.property,delete e.languages.sass.important,e.languages.insertBefore("sass","punctuation",{selector:{pattern:/^([ \t]*)\S(?:,[^,\r\n]+|[^,\r\n]*)(?:,[^,\r\n]+)*(?:,(?:\r?\n|\r)\1[ \t]+\S(?:,[^,\r\n]+|[^,\r\n]*)(?:,[^,\r\n]+)*)*/m,lookbehind:!0,greedy:!0}})}(Prism);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Prism.languages.scala=Prism.languages.extend("java",{"triple-quoted-string":{pattern:/"""[\s\S]*?"""/,greedy:!0,alias:"string"},string:{pattern:/("|')(?:\\.|(?!\1)[^\\\r\n])*\1/,greedy:!0},keyword:/<-|=>|\b(?:abstract|case|catch|class|def|derives|do|else|enum|extends|extension|final|finally|for|forSome|given|if|implicit|import|infix|inline|lazy|match|new|null|object|opaque|open|override|package|private|protected|return|sealed|self|super|this|throw|trait|transparent|try|type|using|val|var|while|with|yield)\b/,number:/\b0x(?:[\da-f]*\.)?[\da-f]+|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e\d+)?[dfl]?/i,builtin:/\b(?:Any|AnyRef|AnyVal|Boolean|Byte|Char|Double|Float|Int|Long|Nothing|Short|String|Unit)\b/,symbol:/'[^\d\s\\]\w*/}),Prism.languages.insertBefore("scala","triple-quoted-string",{"string-interpolation":{pattern:/\b[a-z]\w*(?:"""(?:[^$]|\$(?:[^{]|\{(?:[^{}]|\{[^{}]*\})*\}))*?"""|"(?:[^$"\r\n]|\$(?:[^{]|\{(?:[^{}]|\{[^{}]*\})*\}))*")/i,greedy:!0,inside:{id:{pattern:/^\w+/,greedy:!0,alias:"function"},escape:{pattern:/\\\$"|\$[$"]/,greedy:!0,alias:"symbol"},interpolation:{pattern:/\$(?:\w+|\{(?:[^{}]|\{[^{}]*\})*\})/,greedy:!0,inside:{punctuation:/^\$\{?|\}$/,expression:{pattern:/[\s\S]+/,inside:Prism.languages.scala}}},string:/[\s\S]+/}}}),delete Prism.languages.scala["class-name"],delete Prism.languages.scala.function,delete Prism.languages.scala.constant;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Prism.languages.scss=Prism.languages.extend("css",{comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0},atrule:{pattern:/@[\w-](?:\([^()]+\)|[^()\s]|\s+(?!\s))*?(?=\s+[{;])/,inside:{rule:/@[\w-]+/}},url:/(?:[-a-z]+-)?url(?=\()/i,selector:{pattern:/(?=\S)[^@;{}()]?(?:[^@;{}()\s]|\s+(?!\s)|#\{\$[-\w]+\})+(?=\s*\{(?:\}|\s|[^}][^:{}]*[:{][^}]))/,inside:{parent:{pattern:/&/,alias:"important"},placeholder:/%[-\w]+/,variable:/\$[-\w]+|#\{\$[-\w]+\}/}},property:{pattern:/(?:[-\w]|\$[-\w]|#\{\$[-\w]+\})+(?=\s*:)/,inside:{variable:/\$[-\w]+|#\{\$[-\w]+\}/}}}),Prism.languages.insertBefore("scss","atrule",{keyword:[/@(?:content|debug|each|else(?: if)?|extend|for|forward|function|if|import|include|mixin|return|use|warn|while)\b/i,{pattern:/( )(?:from|through)(?= )/,lookbehind:!0}]}),Prism.languages.insertBefore("scss","important",{variable:/\$[-\w]+|#\{\$[-\w]+\}/}),Prism.languages.insertBefore("scss","function",{"module-modifier":{pattern:/\b(?:as|hide|show|with)\b/i,alias:"keyword"},placeholder:{pattern:/%[-\w]+/,alias:"selector"},statement:{pattern:/\B!(?:default|optional)\b/i,alias:"keyword"},boolean:/\b(?:false|true)\b/,null:{pattern:/\bnull\b/,alias:"keyword"},operator:{pattern:/(\s)(?:[-+*\/%]|[=!]=|<=?|>=?|and|not|or)(?=\s)/,lookbehind:!0}}),Prism.languages.scss.atrule.inside.rest=Prism.languages.scss;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
!function(s){var n=['"(?:\\\\[^]|\\$\\([^)]+\\)|\\$(?!\\()|`[^`]+`|[^"\\\\`$])*"',"'[^']*'","\\$'(?:[^'\\\\]|\\\\[^])*'","<<-?\\s*([\"']?)(\\w+)\\1\\s[^]*?[\r\n]\\2"].join("|");s.languages["shell-session"]={command:{pattern:RegExp('^(?:[^\\s@:$#%*!/\\\\]+@[^\r\n@:$#%*!/\\\\]+(?::[^\0-\\x1F$#%*?"<>:;|]+)?|[/~.][^\0-\\x1F$#%*?"<>@:;|]*)?[$#%](?=\\s)'+"(?:[^\\\\\r\n \t'\"<$]|[ \t](?:(?!#)|#.*$)|\\\\(?:[^\r]|\r\n?)|\\$(?!')|<(?!<)|<<str>>)+".replace(/<<str>>/g,(function(){return n})),"m"),greedy:!0,inside:{info:{pattern:/^[^#$%]+/,alias:"punctuation",inside:{user:/^[^\s@:$#%*!/\\]+@[^\r\n@:$#%*!/\\]+/,punctuation:/:/,path:/[\s\S]+/}},bash:{pattern:/(^[$#%]\s*)\S[\s\S]*/,lookbehind:!0,alias:"language-bash",inside:s.languages.bash},"shell-symbol":{pattern:/^[$#%]/,alias:"important"}}},output:/.(?:.*(?:[\r\n]|.$))*/},s.languages["sh-session"]=s.languages.shellsession=s.languages["shell-session"]}(Prism);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Prism.languages.sql={comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|(?:--|\/\/|#).*)/,lookbehind:!0},variable:[{pattern:/@(["'`])(?:\\[\s\S]|(?!\1)[^\\])+\1/,greedy:!0},/@[\w.$]+/],string:{pattern:/(^|[^@\\])("|')(?:\\[\s\S]|(?!\2)[^\\]|\2\2)*\2/,greedy:!0,lookbehind:!0},identifier:{pattern:/(^|[^@\\])`(?:\\[\s\S]|[^`\\]|``)*`/,greedy:!0,lookbehind:!0,inside:{punctuation:/^`|`$/}},function:/\b(?:AVG|COUNT|FIRST|FORMAT|LAST|LCASE|LEN|MAX|MID|MIN|MOD|NOW|ROUND|SUM|UCASE)(?=\s*\()/i,keyword:/\b(?:ACTION|ADD|AFTER|ALGORITHM|ALL|ALTER|ANALYZE|ANY|APPLY|AS|ASC|AUTHORIZATION|AUTO_INCREMENT|BACKUP|BDB|BEGIN|BERKELEYDB|BIGINT|BINARY|BIT|BLOB|BOOL|BOOLEAN|BREAK|BROWSE|BTREE|BULK|BY|CALL|CASCADED?|CASE|CHAIN|CHAR(?:ACTER|SET)?|CHECK(?:POINT)?|CLOSE|CLUSTERED|COALESCE|COLLATE|COLUMNS?|COMMENT|COMMIT(?:TED)?|COMPUTE|CONNECT|CONSISTENT|CONSTRAINT|CONTAINS(?:TABLE)?|CONTINUE|CONVERT|CREATE|CROSS|CURRENT(?:_DATE|_TIME|_TIMESTAMP|_USER)?|CURSOR|CYCLE|DATA(?:BASES?)?|DATE(?:TIME)?|DAY|DBCC|DEALLOCATE|DEC|DECIMAL|DECLARE|DEFAULT|DEFINER|DELAYED|DELETE|DELIMITERS?|DENY|DESC|DESCRIBE|DETERMINISTIC|DISABLE|DISCARD|DISK|DISTINCT|DISTINCTROW|DISTRIBUTED|DO|DOUBLE|DROP|DUMMY|DUMP(?:FILE)?|DUPLICATE|ELSE(?:IF)?|ENABLE|ENCLOSED|END|ENGINE|ENUM|ERRLVL|ERRORS|ESCAPED?|EXCEPT|EXEC(?:UTE)?|EXISTS|EXIT|EXPLAIN|EXTENDED|FETCH|FIELDS|FILE|FILLFACTOR|FIRST|FIXED|FLOAT|FOLLOWING|FOR(?: EACH ROW)?|FORCE|FOREIGN|FREETEXT(?:TABLE)?|FROM|FULL|FUNCTION|GEOMETRY(?:COLLECTION)?|GLOBAL|GOTO|GRANT|GROUP|HANDLER|HASH|HAVING|HOLDLOCK|HOUR|IDENTITY(?:COL|_INSERT)?|IF|IGNORE|IMPORT|INDEX|INFILE|INNER|INNODB|INOUT|INSERT|INT|INTEGER|INTERSECT|INTERVAL|INTO|INVOKER|ISOLATION|ITERATE|JOIN|KEYS?|KILL|LANGUAGE|LAST|LEAVE|LEFT|LEVEL|LIMIT|LINENO|LINES|LINESTRING|LOAD|LOCAL|LOCK|LONG(?:BLOB|TEXT)|LOOP|MATCH(?:ED)?|MEDIUM(?:BLOB|INT|TEXT)|MERGE|MIDDLEINT|MINUTE|MODE|MODIFIES|MODIFY|MONTH|MULTI(?:LINESTRING|POINT|POLYGON)|NATIONAL|NATURAL|NCHAR|NEXT|NO|NONCLUSTERED|NULLIF|NUMERIC|OFF?|OFFSETS?|ON|OPEN(?:DATASOURCE|QUERY|ROWSET)?|OPTIMIZE|OPTION(?:ALLY)?|ORDER|OUT(?:ER|FILE)?|OVER|PARTIAL|PARTITION|PERCENT|PIVOT|PLAN|POINT|POLYGON|PRECEDING|PRECISION|PREPARE|PREV|PRIMARY|PRINT|PRIVILEGES|PROC(?:EDURE)?|PUBLIC|PURGE|QUICK|RAISERROR|READS?|REAL|RECONFIGURE|REFERENCES|RELEASE|RENAME|REPEAT(?:ABLE)?|REPLACE|REPLICATION|REQUIRE|RESIGNAL|RESTORE|RESTRICT|RETURN(?:ING|S)?|REVOKE|RIGHT|ROLLBACK|ROUTINE|ROW(?:COUNT|GUIDCOL|S)?|RTREE|RULE|SAVE(?:POINT)?|SCHEMA|SECOND|SELECT|SERIAL(?:IZABLE)?|SESSION(?:_USER)?|SET(?:USER)?|SHARE|SHOW|SHUTDOWN|SIMPLE|SMALLINT|SNAPSHOT|SOME|SONAME|SQL|START(?:ING)?|STATISTICS|STATUS|STRIPED|SYSTEM_USER|TABLES?|TABLESPACE|TEMP(?:ORARY|TABLE)?|TERMINATED|TEXT(?:SIZE)?|THEN|TIME(?:STAMP)?|TINY(?:BLOB|INT|TEXT)|TOP?|TRAN(?:SACTIONS?)?|TRIGGER|TRUNCATE|TSEQUAL|TYPES?|UNBOUNDED|UNCOMMITTED|UNDEFINED|UNION|UNIQUE|UNLOCK|UNPIVOT|UNSIGNED|UPDATE(?:TEXT)?|USAGE|USE|USER|USING|VALUES?|VAR(?:BINARY|CHAR|CHARACTER|YING)|VIEW|WAITFOR|WARNINGS|WHEN|WHERE|WHILE|WITH(?: ROLLUP|IN)?|WORK|WRITE(?:TEXT)?|YEAR)\b/i,boolean:/\b(?:FALSE|NULL|TRUE)\b/i,number:/\b0x[\da-f]+\b|\b\d+(?:\.\d*)?|\B\.\d+\b/i,operator:/[-+*\/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?|\b(?:AND|BETWEEN|DIV|ILIKE|IN|IS|LIKE|NOT|OR|REGEXP|RLIKE|SOUNDS LIKE|XOR)\b/i,punctuation:/[;[\]()`,.]/};
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Prism.languages.swift={comment:{pattern:/(^|[^\\:])(?:\/\/.*|\/\*(?:[^/*]|\/(?!\*)|\*(?!\/)|\/\*(?:[^*]|\*(?!\/))*\*\/)*\*\/)/,lookbehind:!0,greedy:!0},"string-literal":[{pattern:RegExp('(^|[^"#])(?:"(?:\\\\(?:\\((?:[^()]|\\([^()]*\\))*\\)|\r\n|[^(])|[^\\\\\r\n"])*"|"""(?:\\\\(?:\\((?:[^()]|\\([^()]*\\))*\\)|[^(])|[^\\\\"]|"(?!""))*""")(?!["#])'),lookbehind:!0,greedy:!0,inside:{interpolation:{pattern:/(\\\()(?:[^()]|\([^()]*\))*(?=\))/,lookbehind:!0,inside:null},"interpolation-punctuation":{pattern:/^\)|\\\($/,alias:"punctuation"},punctuation:/\\(?=[\r\n])/,string:/[\s\S]+/}},{pattern:RegExp('(^|[^"#])(#+)(?:"(?:\\\\(?:#+\\((?:[^()]|\\([^()]*\\))*\\)|\r\n|[^#])|[^\\\\\r\n])*?"|"""(?:\\\\(?:#+\\((?:[^()]|\\([^()]*\\))*\\)|[^#])|[^\\\\])*?""")\\2'),lookbehind:!0,greedy:!0,inside:{interpolation:{pattern:/(\\#+\()(?:[^()]|\([^()]*\))*(?=\))/,lookbehind:!0,inside:null},"interpolation-punctuation":{pattern:/^\)|\\#+\($/,alias:"punctuation"},string:/[\s\S]+/}}],directive:{pattern:RegExp("#(?:(?:elseif|if)\\b(?:[ \t]*(?:![ \t]*)?(?:\\b\\w+\\b(?:[ \t]*\\((?:[^()]|\\([^()]*\\))*\\))?|\\((?:[^()]|\\([^()]*\\))*\\))(?:[ \t]*(?:&&|\\|\\|))?)+|(?:else|endif)\\b)"),alias:"property",inside:{"directive-name":/^#\w+/,boolean:/\b(?:false|true)\b/,number:/\b\d+(?:\.\d+)*\b/,operator:/!|&&|\|\||[<>]=?/,punctuation:/[(),]/}},literal:{pattern:/#(?:colorLiteral|column|dsohandle|file(?:ID|Literal|Path)?|function|imageLiteral|line)\b/,alias:"constant"},"other-directive":{pattern:/#\w+\b/,alias:"property"},attribute:{pattern:/@\w+/,alias:"atrule"},"function-definition":{pattern:/(\bfunc\s+)\w+/,lookbehind:!0,alias:"function"},label:{pattern:/\b(break|continue)\s+\w+|\b[a-zA-Z_]\w*(?=\s*:\s*(?:for|repeat|while)\b)/,lookbehind:!0,alias:"important"},keyword:/\b(?:Any|Protocol|Self|Type|actor|as|assignment|associatedtype|associativity|async|await|break|case|catch|class|continue|convenience|default|defer|deinit|didSet|do|dynamic|else|enum|extension|fallthrough|fileprivate|final|for|func|get|guard|higherThan|if|import|in|indirect|infix|init|inout|internal|is|isolated|lazy|left|let|lowerThan|mutating|none|nonisolated|nonmutating|open|operator|optional|override|postfix|precedencegroup|prefix|private|protocol|public|repeat|required|rethrows|return|right|safe|self|set|some|static|struct|subscript|super|switch|throw|throws|try|typealias|unowned|unsafe|var|weak|where|while|willSet)\b/,boolean:/\b(?:false|true)\b/,nil:{pattern:/\bnil\b/,alias:"constant"},"short-argument":/\$\d+\b/,omit:{pattern:/\b_\b/,alias:"keyword"},number:/\b(?:[\d_]+(?:\.[\de_]+)?|0x[a-f0-9_]+(?:\.[a-f0-9p_]+)?|0b[01_]+|0o[0-7_]+)\b/i,"class-name":/\b[A-Z](?:[A-Z_\d]*[a-z]\w*)?\b/,function:/\b[a-z_]\w*(?=\s*\()/i,constant:/\b(?:[A-Z_]{2,}|k[A-Z][A-Za-z_]+)\b/,operator:/[-+*/%=!<>&|^~?]+|\.[.\-+*/%=!<>&|^~?]+/,punctuation:/[{}[\]();,.:\\]/},Prism.languages.swift["string-literal"].forEach((function(e){e.inside.interpolation.inside=Prism.languages.swift}));
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
!function(e){function n(e){return e.replace(/__/g,(function(){return"(?:[\\w-]+|'[^'\n\r]*'|\"(?:\\\\.|[^\\\\\"\r\n])*\")"}))}e.languages.toml={comment:{pattern:/#.*/,greedy:!0},table:{pattern:RegExp(n("(^[\t ]*\\[\\s*(?:\\[\\s*)?)__(?:\\s*\\.\\s*__)*(?=\\s*\\])"),"m"),lookbehind:!0,greedy:!0,alias:"class-name"},key:{pattern:RegExp(n("(^[\t ]*|[{,]\\s*)__(?:\\s*\\.\\s*__)*(?=\\s*=)"),"m"),lookbehind:!0,greedy:!0,alias:"property"},string:{pattern:/"""(?:\\[\s\S]|[^\\])*?"""|'''[\s\S]*?'''|'[^'\n\r]*'|"(?:\\.|[^\\"\r\n])*"/,greedy:!0},date:[{pattern:/\b\d{4}-\d{2}-\d{2}(?:[T\s]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?)?\b/i,alias:"number"},{pattern:/\b\d{2}:\d{2}:\d{2}(?:\.\d+)?\b/,alias:"number"}],number:/(?:\b0(?:x[\da-zA-Z]+(?:_[\da-zA-Z]+)*|o[0-7]+(?:_[0-7]+)*|b[10]+(?:_[10]+)*))\b|[-+]?\b\d+(?:_\d+)*(?:\.\d+(?:_\d+)*)?(?:[eE][+-]?\d+(?:_\d+)*)?\b|[-+]?\b(?:inf|nan)\b/,boolean:/\b(?:false|true)\b/,punctuation:/[.,=[\]{}]/}}(Prism);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
!function(e){var a=e.util.clone(e.languages.typescript);e.languages.tsx=e.languages.extend("jsx",a),delete e.languages.tsx.parameter,delete e.languages.tsx["literal-property"];var t=e.languages.tsx.tag;t.pattern=RegExp("(^|[^\\w$]|(?=</))(?:"+t.pattern.source+")",t.pattern.flags),t.lookbehind=!0}(Prism);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
!function(e){e.languages.typescript=e.languages.extend("javascript",{"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|type)\s+)(?!keyof\b)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?:\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>)?/,lookbehind:!0,greedy:!0,inside:null},builtin:/\b(?:Array|Function|Promise|any|boolean|console|never|number|string|symbol|unknown)\b/}),e.languages.typescript.keyword.push(/\b(?:abstract|declare|is|keyof|readonly|require)\b/,/\b(?:asserts|infer|interface|module|namespace|type)\b(?=\s*(?:[{_$a-zA-Z\xA0-\uFFFF]|$))/,/\btype\b(?=\s*(?:[\{*]|$))/),delete e.languages.typescript.parameter,delete e.languages.typescript["literal-property"];var s=e.languages.extend("typescript",{});delete s["class-name"],e.languages.typescript["class-name"].inside=s,e.languages.insertBefore("typescript","function",{decorator:{pattern:/@[$\w\xA0-\uFFFF]+/,inside:{at:{pattern:/^@/,alias:"operator"},function:/^[\s\S]+/}},"generic-function":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>(?=\s*\()/,greedy:!0,inside:{function:/^#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:s}}}}),e.languages.ts=e.languages.typescript}(Prism);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Prism.languages.vbnet=Prism.languages.extend("basic",{comment:[{pattern:/(?:!|REM\b).+/i,inside:{keyword:/^REM/i}},{pattern:/(^|[^\\:])'.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(^|[^"])"(?:""|[^"])*"(?!")/,lookbehind:!0,greedy:!0},keyword:/(?:\b(?:ADDHANDLER|ADDRESSOF|ALIAS|AND|ANDALSO|AS|BEEP|BLOAD|BOOLEAN|BSAVE|BYREF|BYTE|BYVAL|CALL(?: ABSOLUTE)?|CASE|CATCH|CBOOL|CBYTE|CCHAR|CDATE|CDBL|CDEC|CHAIN|CHAR|CHDIR|CINT|CLASS|CLEAR|CLNG|CLOSE|CLS|COBJ|COM|COMMON|CONST|CONTINUE|CSBYTE|CSHORT|CSNG|CSTR|CTYPE|CUINT|CULNG|CUSHORT|DATA|DATE|DECIMAL|DECLARE|DEF(?: FN| SEG|DBL|INT|LNG|SNG|STR)|DEFAULT|DELEGATE|DIM|DIRECTCAST|DO|DOUBLE|ELSE|ELSEIF|END|ENUM|ENVIRON|ERASE|ERROR|EVENT|EXIT|FALSE|FIELD|FILES|FINALLY|FOR(?: EACH)?|FRIEND|FUNCTION|GET|GETTYPE|GETXMLNAMESPACE|GLOBAL|GOSUB|GOTO|HANDLES|IF|IMPLEMENTS|IMPORTS|IN|INHERITS|INPUT|INTEGER|INTERFACE|IOCTL|IS|ISNOT|KEY|KILL|LET|LIB|LIKE|LINE INPUT|LOCATE|LOCK|LONG|LOOP|LSET|ME|MKDIR|MOD|MODULE|MUSTINHERIT|MUSTOVERRIDE|MYBASE|MYCLASS|NAME|NAMESPACE|NARROWING|NEW|NEXT|NOT|NOTHING|NOTINHERITABLE|NOTOVERRIDABLE|OBJECT|OF|OFF|ON(?: COM| ERROR| KEY| TIMER)?|OPEN|OPERATOR|OPTION(?: BASE)?|OPTIONAL|OR|ORELSE|OUT|OVERLOADS|OVERRIDABLE|OVERRIDES|PARAMARRAY|PARTIAL|POKE|PRIVATE|PROPERTY|PROTECTED|PUBLIC|PUT|RAISEEVENT|READ|READONLY|REDIM|REM|REMOVEHANDLER|RESTORE|RESUME|RETURN|RMDIR|RSET|RUN|SBYTE|SELECT(?: CASE)?|SET|SHADOWS|SHARED|SHELL|SHORT|SINGLE|SLEEP|STATIC|STEP|STOP|STRING|STRUCTURE|SUB|SWAP|SYNCLOCK|SYSTEM|THEN|THROW|TIMER|TO|TROFF|TRON|TRUE|TRY|TRYCAST|TYPE|TYPEOF|UINTEGER|ULONG|UNLOCK|UNTIL|USHORT|USING|VIEW PRINT|WAIT|WEND|WHEN|WHILE|WIDENING|WITH|WITHEVENTS|WRITE|WRITEONLY|XOR)|\B(?:#CONST|#ELSE|#ELSEIF|#END|#IF))(?:\$|\b)/i,punctuation:/[,;:(){}]/});
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Prism.languages["visual-basic"]={comment:{pattern:/(?:['‘’]|REM\b)(?:[^\r\n_]|_(?:\r\n?|\n)?)*/i,inside:{keyword:/^REM/i}},directive:{pattern:/#(?:Const|Else|ElseIf|End|ExternalChecksum|ExternalSource|If|Region)(?:\b_[ \t]*(?:\r\n?|\n)|.)+/i,alias:"property",greedy:!0},string:{pattern:/\$?["“”](?:["“”]{2}|[^"“”])*["“”]C?/i,greedy:!0},date:{pattern:/#[ \t]*(?:\d+([/-])\d+\1\d+(?:[ \t]+(?:\d+[ \t]*(?:AM|PM)|\d+:\d+(?::\d+)?(?:[ \t]*(?:AM|PM))?))?|\d+[ \t]*(?:AM|PM)|\d+:\d+(?::\d+)?(?:[ \t]*(?:AM|PM))?)[ \t]*#/i,alias:"number"},number:/(?:(?:\b\d+(?:\.\d+)?|\.\d+)(?:E[+-]?\d+)?|&[HO][\dA-F]+)(?:[FRD]|U?[ILS])?/i,boolean:/\b(?:False|Nothing|True)\b/i,keyword:/\b(?:AddHandler|AddressOf|Alias|And(?:Also)?|As|Boolean|ByRef|Byte|ByVal|Call|Case|Catch|C(?:Bool|Byte|Char|Date|Dbl|Dec|Int|Lng|Obj|SByte|Short|Sng|Str|Type|UInt|ULng|UShort)|Char|Class|Const|Continue|Currency|Date|Decimal|Declare|Default|Delegate|Dim|DirectCast|Do|Double|Each|Else(?:If)?|End(?:If)?|Enum|Erase|Error|Event|Exit|Finally|For|Friend|Function|Get(?:Type|XMLNamespace)?|Global|GoSub|GoTo|Handles|If|Implements|Imports|In|Inherits|Integer|Interface|Is|IsNot|Let|Lib|Like|Long|Loop|Me|Mod|Module|Must(?:Inherit|Override)|My(?:Base|Class)|Namespace|Narrowing|New|Next|Not(?:Inheritable|Overridable)?|Object|Of|On|Operator|Option(?:al)?|Or(?:Else)?|Out|Overloads|Overridable|Overrides|ParamArray|Partial|Private|Property|Protected|Public|RaiseEvent|ReadOnly|ReDim|RemoveHandler|Resume|Return|SByte|Select|Set|Shadows|Shared|short|Single|Static|Step|Stop|String|Structure|Sub|SyncLock|Then|Throw|To|Try|TryCast|Type|TypeOf|U(?:Integer|Long|Short)|Until|Using|Variant|Wend|When|While|Widening|With(?:Events)?|WriteOnly|Xor)\b/i,operator:/[+\-*/\\^<=>&#@$%!]|\b_(?=[ \t]*[\r\n])/,punctuation:/[{}().,:?]/},Prism.languages.vb=Prism.languages["visual-basic"],Prism.languages.vba=Prism.languages["visual-basic"];
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
!function(a){function e(e,n){a.languages[e]&&a.languages.insertBefore(e,"comment",{"doc-comment":n})}var n=a.languages.markup.tag,t={pattern:/\/\/\/.*/,greedy:!0,alias:"comment",inside:{tag:n}},g={pattern:/'''.*/,greedy:!0,alias:"comment",inside:{tag:n}};e("csharp",t),e("fsharp",t),e("vbnet",g)}(Prism);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
!function(e){var n=/[*&][^\s[\]{},]+/,r=/!(?:<[\w\-%#;/?:@&=+$,.!~*'()[\]]+>|(?:[a-zA-Z\d-]*!)?[\w\-%#;/?:@&=+$.~*'()]+)?/,t="(?:"+r.source+"(?:[ \t]+"+n.source+")?|"+n.source+"(?:[ \t]+"+r.source+")?)",a="(?:[^\\s\\x00-\\x08\\x0e-\\x1f!\"#%&'*,\\-:>?@[\\]`{|}\\x7f-\\x84\\x86-\\x9f\\ud800-\\udfff\\ufffe\\uffff]|[?:-]<PLAIN>)(?:[ \t]*(?:(?![#:])<PLAIN>|:<PLAIN>))*".replace(/<PLAIN>/g,(function(){return"[^\\s\\x00-\\x08\\x0e-\\x1f,[\\]{}\\x7f-\\x84\\x86-\\x9f\\ud800-\\udfff\\ufffe\\uffff]"})),d="\"(?:[^\"\\\\\r\n]|\\\\.)*\"|'(?:[^'\\\\\r\n]|\\\\.)*'";function o(e,n){n=(n||"").replace(/m/g,"")+"m";var r="([:\\-,[{]\\s*(?:\\s<<prop>>[ \t]+)?)(?:<<value>>)(?=[ \t]*(?:$|,|\\]|\\}|(?:[\r\n]\\s*)?#))".replace(/<<prop>>/g,(function(){return t})).replace(/<<value>>/g,(function(){return e}));return RegExp(r,n)}e.languages.yaml={scalar:{pattern:RegExp("([\\-:]\\s*(?:\\s<<prop>>[ \t]+)?[|>])[ \t]*(?:((?:\r?\n|\r)[ \t]+)\\S[^\r\n]*(?:\\2[^\r\n]+)*)".replace(/<<prop>>/g,(function(){return t}))),lookbehind:!0,alias:"string"},comment:/#.*/,key:{pattern:RegExp("((?:^|[:\\-,[{\r\n?])[ \t]*(?:<<prop>>[ \t]+)?)<<key>>(?=\\s*:\\s)".replace(/<<prop>>/g,(function(){return t})).replace(/<<key>>/g,(function(){return"(?:"+a+"|"+d+")"}))),lookbehind:!0,greedy:!0,alias:"atrule"},directive:{pattern:/(^[ \t]*)%.+/m,lookbehind:!0,alias:"important"},datetime:{pattern:o("\\d{4}-\\d\\d?-\\d\\d?(?:[tT]|[ \t]+)\\d\\d?:\\d{2}:\\d{2}(?:\\.\\d*)?(?:[ \t]*(?:Z|[-+]\\d\\d?(?::\\d{2})?))?|\\d{4}-\\d{2}-\\d{2}|\\d\\d?:\\d{2}(?::\\d{2}(?:\\.\\d*)?)?"),lookbehind:!0,alias:"number"},boolean:{pattern:o("false|true","i"),lookbehind:!0,alias:"important"},null:{pattern:o("null|~","i"),lookbehind:!0,alias:"important"},string:{pattern:o(d),lookbehind:!0,greedy:!0},number:{pattern:o("[+-]?(?:0x[\\da-f]+|0o[0-7]+|(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:e[+-]?\\d+)?|\\.inf|\\.nan)","i"),lookbehind:!0},tag:r,important:n,punctuation:/---|[:[\]{}\-,|>?]|\.\.\./},e.languages.yml=e.languages.yaml}(Prism);
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user