mirror of
https://github.com/laurent22/joplin.git
synced 2025-01-08 13:06:15 +02:00
Server: Added Help page for Joplin Cloud
This commit is contained in:
parent
5805a41249
commit
6520a481ca
@ -1725,6 +1725,9 @@ packages/renderer/MdToHtml/setupLinkify.js.map
|
|||||||
packages/renderer/MdToHtml/validateLinks.d.ts
|
packages/renderer/MdToHtml/validateLinks.d.ts
|
||||||
packages/renderer/MdToHtml/validateLinks.js
|
packages/renderer/MdToHtml/validateLinks.js
|
||||||
packages/renderer/MdToHtml/validateLinks.js.map
|
packages/renderer/MdToHtml/validateLinks.js.map
|
||||||
|
packages/renderer/headerAnchor.d.ts
|
||||||
|
packages/renderer/headerAnchor.js
|
||||||
|
packages/renderer/headerAnchor.js.map
|
||||||
packages/renderer/htmlUtils.d.ts
|
packages/renderer/htmlUtils.d.ts
|
||||||
packages/renderer/htmlUtils.js
|
packages/renderer/htmlUtils.js
|
||||||
packages/renderer/htmlUtils.js.map
|
packages/renderer/htmlUtils.js.map
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -1710,6 +1710,9 @@ packages/renderer/MdToHtml/setupLinkify.js.map
|
|||||||
packages/renderer/MdToHtml/validateLinks.d.ts
|
packages/renderer/MdToHtml/validateLinks.d.ts
|
||||||
packages/renderer/MdToHtml/validateLinks.js
|
packages/renderer/MdToHtml/validateLinks.js
|
||||||
packages/renderer/MdToHtml/validateLinks.js.map
|
packages/renderer/MdToHtml/validateLinks.js.map
|
||||||
|
packages/renderer/headerAnchor.d.ts
|
||||||
|
packages/renderer/headerAnchor.js
|
||||||
|
packages/renderer/headerAnchor.js.map
|
||||||
packages/renderer/htmlUtils.d.ts
|
packages/renderer/htmlUtils.d.ts
|
||||||
packages/renderer/htmlUtils.js
|
packages/renderer/htmlUtils.js
|
||||||
packages/renderer/htmlUtils.js.map
|
packages/renderer/htmlUtils.js.map
|
||||||
|
92
packages/renderer/headerAnchor.ts
Normal file
92
packages/renderer/headerAnchor.ts
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
export default function(markdownIt: any) {
|
||||||
|
markdownIt.core.ruler.push('anchorHeader', (state: any): boolean => {
|
||||||
|
const tokens = state.tokens;
|
||||||
|
const Token = state.Token;
|
||||||
|
const doneNames = [];
|
||||||
|
|
||||||
|
const headingTextToAnchorName = (text: string, doneNames: string[]) => {
|
||||||
|
const allowed = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||||
|
let lastWasDash = true;
|
||||||
|
let output = '';
|
||||||
|
for (let i = 0; i < text.length; i++) {
|
||||||
|
const c = text[i];
|
||||||
|
if (allowed.indexOf(c) < 0) {
|
||||||
|
if (lastWasDash) continue;
|
||||||
|
lastWasDash = true;
|
||||||
|
output += '-';
|
||||||
|
} else {
|
||||||
|
lastWasDash = false;
|
||||||
|
output += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output = output.toLowerCase();
|
||||||
|
|
||||||
|
while (output.length && output[output.length - 1] === '-') {
|
||||||
|
output = output.substr(0, output.length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let temp = output;
|
||||||
|
let index = 1;
|
||||||
|
while (doneNames.indexOf(temp) >= 0) {
|
||||||
|
temp = `${output}-${index}`;
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
output = temp;
|
||||||
|
|
||||||
|
return output;
|
||||||
|
};
|
||||||
|
|
||||||
|
const createAnchorTokens = (anchorName: string) => {
|
||||||
|
const output = [];
|
||||||
|
|
||||||
|
{
|
||||||
|
const token = new Token('heading_anchor_open', 'a', 1);
|
||||||
|
token.attrs = [
|
||||||
|
['name', anchorName],
|
||||||
|
['href', `#${anchorName}`],
|
||||||
|
['class', 'heading-anchor'],
|
||||||
|
];
|
||||||
|
output.push(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const token = new Token('text', '', 0);
|
||||||
|
token.content = '🔗';
|
||||||
|
output.push(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const token = new Token('heading_anchor_close', 'a', -1);
|
||||||
|
output.push(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
};
|
||||||
|
|
||||||
|
let insideHeading = false;
|
||||||
|
for (let i = 0; i < tokens.length; i++) {
|
||||||
|
const token = tokens[i];
|
||||||
|
|
||||||
|
if (token.type === 'heading_open') {
|
||||||
|
insideHeading = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token.type === 'heading_close') {
|
||||||
|
insideHeading = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (insideHeading && token.type === 'inline') {
|
||||||
|
const anchorName = headingTextToAnchorName(token.content, doneNames);
|
||||||
|
doneNames.push(anchorName);
|
||||||
|
const anchorTokens = createAnchorTokens(anchorName);
|
||||||
|
// token.children = anchorTokens.concat(token.children);
|
||||||
|
token.children = token.children.concat(anchorTokens);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
@ -4,6 +4,7 @@ import HtmlToHtml from './HtmlToHtml';
|
|||||||
import utils from './utils';
|
import utils from './utils';
|
||||||
import setupLinkify from './MdToHtml/setupLinkify';
|
import setupLinkify from './MdToHtml/setupLinkify';
|
||||||
import validateLinks from './MdToHtml/validateLinks';
|
import validateLinks from './MdToHtml/validateLinks';
|
||||||
|
import headerAnchor from './headerAnchor';
|
||||||
const assetsToHeaders = require('./assetsToHeaders');
|
const assetsToHeaders = require('./assetsToHeaders');
|
||||||
|
|
||||||
export {
|
export {
|
||||||
@ -13,6 +14,7 @@ export {
|
|||||||
HtmlToHtml,
|
HtmlToHtml,
|
||||||
setupLinkify,
|
setupLinkify,
|
||||||
validateLinks,
|
validateLinks,
|
||||||
|
headerAnchor,
|
||||||
assetsToHeaders,
|
assetsToHeaders,
|
||||||
utils,
|
utils,
|
||||||
};
|
};
|
||||||
|
@ -63,4 +63,24 @@ ul.pagination-list li {
|
|||||||
|
|
||||||
.readable-block {
|
.readable-block {
|
||||||
max-width: 740px;
|
max-width: 740px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.heading-anchor {
|
||||||
|
display: inline-block;
|
||||||
|
opacity: 0;
|
||||||
|
width: 1.3em;
|
||||||
|
font-size: 0.7em;
|
||||||
|
margin-left: 0.4em;
|
||||||
|
line-height: 1em;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: opacity 0.3s;
|
||||||
|
}
|
||||||
|
a.heading-anchor:hover,
|
||||||
|
h1:hover a.heading-anchor,
|
||||||
|
h2:hover a.heading-anchor,
|
||||||
|
h3:hover a.heading-anchor,
|
||||||
|
h4:hover a.heading-anchor,
|
||||||
|
h5:hover a.heading-anchor,
|
||||||
|
h6:hover a.heading-anchor {
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
@ -149,7 +149,7 @@ export async function initConfig(envType: Env, env: EnvVariables, overrides: any
|
|||||||
config_ = {
|
config_ = {
|
||||||
appVersion: packageJson.version,
|
appVersion: packageJson.version,
|
||||||
appName,
|
appName,
|
||||||
isJoplinCloud: apiBaseUrl.includes('.joplincloud.com'),
|
isJoplinCloud: apiBaseUrl.includes('.joplincloud.com') || apiBaseUrl.includes('.joplincloud.local'),
|
||||||
env: envType,
|
env: envType,
|
||||||
rootDir: rootDir,
|
rootDir: rootDir,
|
||||||
viewDir: viewDir,
|
viewDir: viewDir,
|
||||||
|
21
packages/server/src/routes/index/help.ts
Normal file
21
packages/server/src/routes/index/help.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { SubPath } from '../../utils/routeUtils';
|
||||||
|
import Router from '../../utils/Router';
|
||||||
|
import { RouteType } from '../../utils/types';
|
||||||
|
import { AppContext } from '../../utils/types';
|
||||||
|
import { ErrorMethodNotAllowed } from '../../utils/errors';
|
||||||
|
import defaultView from '../../utils/defaultView';
|
||||||
|
|
||||||
|
const router: Router = new Router(RouteType.Web);
|
||||||
|
|
||||||
|
router.public = true;
|
||||||
|
|
||||||
|
router.get('help', async (_path: SubPath, ctx: AppContext) => {
|
||||||
|
if (ctx.method === 'GET') {
|
||||||
|
const view = defaultView('help', 'Help');
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ErrorMethodNotAllowed();
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
@ -25,6 +25,7 @@ import indexStripe from './index/stripe';
|
|||||||
import indexTerms from './index/terms';
|
import indexTerms from './index/terms';
|
||||||
import indexPrivacy from './index/privacy';
|
import indexPrivacy from './index/privacy';
|
||||||
import indexUpgrade from './index/upgrade';
|
import indexUpgrade from './index/upgrade';
|
||||||
|
import indexHelp from './index/help';
|
||||||
|
|
||||||
import defaultRoute from './default';
|
import defaultRoute from './default';
|
||||||
|
|
||||||
@ -54,6 +55,7 @@ const routes: Routers = {
|
|||||||
'terms': indexTerms,
|
'terms': indexTerms,
|
||||||
'privacy': indexPrivacy,
|
'privacy': indexPrivacy,
|
||||||
'upgrade': indexUpgrade,
|
'upgrade': indexUpgrade,
|
||||||
|
'help': indexHelp,
|
||||||
|
|
||||||
'': defaultRoute,
|
'': defaultRoute,
|
||||||
};
|
};
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
import * as Mustache from 'mustache';
|
import * as Mustache from 'mustache';
|
||||||
import * as fs from 'fs-extra';
|
import * as fs from 'fs-extra';
|
||||||
|
import { extname } from 'path';
|
||||||
import config from '../config';
|
import config from '../config';
|
||||||
import { filename } from '@joplin/lib/path-utils';
|
import { filename } from '@joplin/lib/path-utils';
|
||||||
import { NotificationView } from '../utils/types';
|
import { NotificationView } from '../utils/types';
|
||||||
import { User } from '../services/database/types';
|
import { User } from '../services/database/types';
|
||||||
import { makeUrl, UrlType } from '../utils/routeUtils';
|
import { makeUrl, UrlType } from '../utils/routeUtils';
|
||||||
|
import MarkdownIt = require('markdown-it');
|
||||||
|
import { headerAnchor } from '@joplin/renderer';
|
||||||
|
|
||||||
export interface RenderOptions {
|
export interface RenderOptions {
|
||||||
partials?: any;
|
partials?: any;
|
||||||
@ -37,6 +40,7 @@ interface GlobalParams {
|
|||||||
showErrorStackTraces?: boolean;
|
showErrorStackTraces?: boolean;
|
||||||
userDisplayName?: string;
|
userDisplayName?: string;
|
||||||
supportEmail?: string;
|
supportEmail?: string;
|
||||||
|
isJoplinCloud?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isView(o: any): boolean {
|
export function isView(o: any): boolean {
|
||||||
@ -50,6 +54,7 @@ export default class MustacheService {
|
|||||||
private baseAssetUrl_: string;
|
private baseAssetUrl_: string;
|
||||||
private prefersDarkEnabled_: boolean = true;
|
private prefersDarkEnabled_: boolean = true;
|
||||||
private partials_: Record<string, string> = {};
|
private partials_: Record<string, string> = {};
|
||||||
|
private fileContentCache_: Record<string, string> = {};
|
||||||
|
|
||||||
public constructor(viewDir: string, baseAssetUrl: string) {
|
public constructor(viewDir: string, baseAssetUrl: string) {
|
||||||
this.viewDir_ = viewDir;
|
this.viewDir_ = viewDir;
|
||||||
@ -92,11 +97,35 @@ export default class MustacheService {
|
|||||||
termsUrl: config().termsEnabled ? makeUrl(UrlType.Terms) : '',
|
termsUrl: config().termsEnabled ? makeUrl(UrlType.Terms) : '',
|
||||||
privacyUrl: config().termsEnabled ? makeUrl(UrlType.Privacy) : '',
|
privacyUrl: config().termsEnabled ? makeUrl(UrlType.Privacy) : '',
|
||||||
showErrorStackTraces: config().showErrorStackTraces,
|
showErrorStackTraces: config().showErrorStackTraces,
|
||||||
|
isJoplinCloud: config().isJoplinCloud,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async viewFilePath(name: string): Promise<string> {
|
||||||
|
const pathsToTry = [
|
||||||
|
`${this.viewDir_}/${name}.mustache`,
|
||||||
|
`${this.viewDir_}/${name}.md`,
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const p of pathsToTry) {
|
||||||
|
if (await fs.pathExists(p)) return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Cannot find view file: ${name}`);
|
||||||
|
}
|
||||||
|
|
||||||
private async loadTemplateContent(path: string): Promise<string> {
|
private async loadTemplateContent(path: string): Promise<string> {
|
||||||
return fs.readFile(path, 'utf8');
|
if (this.fileContentCache_[path]) return this.fileContentCache_[path];
|
||||||
|
|
||||||
|
try {
|
||||||
|
const output = await fs.readFile(path, 'utf8');
|
||||||
|
this.fileContentCache_[path] = output;
|
||||||
|
return output;
|
||||||
|
} catch (error) {
|
||||||
|
// Shouldn't have to do this but node.fs error messages are useless
|
||||||
|
// so throw a new error to get a proper stack trace.
|
||||||
|
throw new Error(`Cannot load view ${path}: ${error.message}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private resolvesFilePaths(type: string, paths: string[]): string[] {
|
private resolvesFilePaths(type: string, paths: string[]): string[] {
|
||||||
@ -114,10 +143,38 @@ export default class MustacheService {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async renderFileContent(filePath: string, view: View, globalParams: GlobalParams = null): Promise<string> {
|
||||||
|
const ext = extname(filePath);
|
||||||
|
|
||||||
|
if (ext === '.mustache') {
|
||||||
|
return Mustache.render(
|
||||||
|
await this.loadTemplateContent(filePath),
|
||||||
|
{
|
||||||
|
...view.content,
|
||||||
|
global: globalParams,
|
||||||
|
},
|
||||||
|
this.partials_
|
||||||
|
);
|
||||||
|
} else if (ext === '.md') {
|
||||||
|
const markdownIt = new MarkdownIt({
|
||||||
|
linkify: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
markdownIt.use(headerAnchor);
|
||||||
|
|
||||||
|
// Need to wrap in a `content` element so that default styles are
|
||||||
|
// applied to it.
|
||||||
|
// https://github.com/jgthms/bulma/issues/3232#issuecomment-909176563
|
||||||
|
return `<div class="content">${markdownIt.render(await this.loadTemplateContent(filePath))}</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Unsupported view extension: ${ext}`);
|
||||||
|
}
|
||||||
|
|
||||||
public async renderView(view: View, globalParams: GlobalParams = null): Promise<string> {
|
public async renderView(view: View, globalParams: GlobalParams = null): Promise<string> {
|
||||||
const cssFiles = this.resolvesFilePaths('css', view.cssFiles || []);
|
const cssFiles = this.resolvesFilePaths('css', view.cssFiles || []);
|
||||||
const jsFiles = this.resolvesFilePaths('js', view.jsFiles || []);
|
const jsFiles = this.resolvesFilePaths('js', view.jsFiles || []);
|
||||||
const filePath = `${this.viewDir_}/${view.path}.mustache`;
|
const filePath = await this.viewFilePath(view.path);
|
||||||
|
|
||||||
globalParams = {
|
globalParams = {
|
||||||
...this.defaultLayoutOptions,
|
...this.defaultLayoutOptions,
|
||||||
@ -125,14 +182,7 @@ export default class MustacheService {
|
|||||||
userDisplayName: this.userDisplayName(globalParams ? globalParams.owner : null),
|
userDisplayName: this.userDisplayName(globalParams ? globalParams.owner : null),
|
||||||
};
|
};
|
||||||
|
|
||||||
const contentHtml = Mustache.render(
|
const contentHtml = await this.renderFileContent(filePath, view, globalParams);
|
||||||
await this.loadTemplateContent(filePath),
|
|
||||||
{
|
|
||||||
...view.content,
|
|
||||||
global: globalParams,
|
|
||||||
},
|
|
||||||
this.partials_
|
|
||||||
);
|
|
||||||
|
|
||||||
const layoutView: any = {
|
const layoutView: any = {
|
||||||
global: globalParams,
|
global: globalParams,
|
||||||
|
20
packages/server/src/views/index/help.md
Normal file
20
packages/server/src/views/index/help.md
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# Joplin Cloud Help
|
||||||
|
|
||||||
|
## How can I change my details?
|
||||||
|
|
||||||
|
Most of your details can be found in your Profile page. To open it, click on the Profile button - this is the button in the top right corner, with your name or email on it.
|
||||||
|
|
||||||
|
## How can I cancel my account?
|
||||||
|
|
||||||
|
Click on the [Profile button](#how-can-i-change-my-details), then scroll down and click on "Cancel subscription".
|
||||||
|
|
||||||
|
## How can I get more space?
|
||||||
|
|
||||||
|
If you are on a Basic account, you may upgrade to a Pro account to get more space. Click on the [Profile button](#how-can-i-change-my-details), then scroll down and select "Upgrade account".
|
||||||
|
|
||||||
|
If you are already on a Pro account, and you need more space for specific reasons, please contact us as we may increase the cap in some cases.
|
||||||
|
|
||||||
|
## Further information
|
||||||
|
|
||||||
|
- [Joplin Cloud Privacy Policy](/privacy)
|
||||||
|
- [Joplin Cloud Terms & Conditions](/terms)
|
@ -18,6 +18,9 @@
|
|||||||
<a class="navbar-item" href="{{{global.baseUrl}}}/changes">Log</a>
|
<a class="navbar-item" href="{{{global.baseUrl}}}/changes">Log</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="navbar-end">
|
<div class="navbar-end">
|
||||||
|
{{#global.isJoplinCloud}}
|
||||||
|
<a class="navbar-item" href="{{{global.baseUrl}}}/help">Help</a>
|
||||||
|
{{/global.isJoplinCloud}}
|
||||||
<div class="navbar-item">
|
<div class="navbar-item">
|
||||||
<form method="post" action="{{{global.baseUrl}}}/logout">
|
<form method="post" action="{{{global.baseUrl}}}/logout">
|
||||||
<button class="button is-dark">Logout</button>
|
<button class="button is-dark">Logout</button>
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@joplin/lib": "~2.4",
|
"@joplin/lib": "~2.4",
|
||||||
|
"@joplin/renderer": "~2.4",
|
||||||
"execa": "^4.1.0",
|
"execa": "^4.1.0",
|
||||||
"fs-extra": "^4.0.3",
|
"fs-extra": "^4.0.3",
|
||||||
"gettext-parser": "^1.3.0",
|
"gettext-parser": "^1.3.0",
|
||||||
|
@ -2,6 +2,7 @@ import * as Mustache from 'mustache';
|
|||||||
import { filename } from '@joplin/lib/path-utils';
|
import { filename } from '@joplin/lib/path-utils';
|
||||||
import * as fs from 'fs-extra';
|
import * as fs from 'fs-extra';
|
||||||
import { TemplateParams } from './types';
|
import { TemplateParams } from './types';
|
||||||
|
import { headerAnchor } from '@joplin/renderer';
|
||||||
const MarkdownIt = require('markdown-it');
|
const MarkdownIt = require('markdown-it');
|
||||||
|
|
||||||
export async function loadMustachePartials(partialDir: string) {
|
export async function loadMustachePartials(partialDir: string) {
|
||||||
@ -46,94 +47,96 @@ export function markdownToPageHtml(md: string, templateParams: TemplateParams):
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
markdownIt.core.ruler.push('checkbox', (state: any) => {
|
markdownIt.use(headerAnchor);
|
||||||
const tokens = state.tokens;
|
|
||||||
const Token = state.Token;
|
|
||||||
const doneNames = [];
|
|
||||||
|
|
||||||
const headingTextToAnchorName = (text: string, doneNames: string[]) => {
|
// markdownIt.core.ruler.push('checkbox', (state: any) => {
|
||||||
const allowed = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
// const tokens = state.tokens;
|
||||||
let lastWasDash = true;
|
// const Token = state.Token;
|
||||||
let output = '';
|
// const doneNames = [];
|
||||||
for (let i = 0; i < text.length; i++) {
|
|
||||||
const c = text[i];
|
|
||||||
if (allowed.indexOf(c) < 0) {
|
|
||||||
if (lastWasDash) continue;
|
|
||||||
lastWasDash = true;
|
|
||||||
output += '-';
|
|
||||||
} else {
|
|
||||||
lastWasDash = false;
|
|
||||||
output += c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
output = output.toLowerCase();
|
// const headingTextToAnchorName = (text: string, doneNames: string[]) => {
|
||||||
|
// const allowed = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||||
|
// let lastWasDash = true;
|
||||||
|
// let output = '';
|
||||||
|
// for (let i = 0; i < text.length; i++) {
|
||||||
|
// const c = text[i];
|
||||||
|
// if (allowed.indexOf(c) < 0) {
|
||||||
|
// if (lastWasDash) continue;
|
||||||
|
// lastWasDash = true;
|
||||||
|
// output += '-';
|
||||||
|
// } else {
|
||||||
|
// lastWasDash = false;
|
||||||
|
// output += c;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
while (output.length && output[output.length - 1] === '-') {
|
// output = output.toLowerCase();
|
||||||
output = output.substr(0, output.length - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
let temp = output;
|
// while (output.length && output[output.length - 1] === '-') {
|
||||||
let index = 1;
|
// output = output.substr(0, output.length - 1);
|
||||||
while (doneNames.indexOf(temp) >= 0) {
|
// }
|
||||||
temp = `${output}-${index}`;
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
output = temp;
|
|
||||||
|
|
||||||
return output;
|
// let temp = output;
|
||||||
};
|
// let index = 1;
|
||||||
|
// while (doneNames.indexOf(temp) >= 0) {
|
||||||
|
// temp = `${output}-${index}`;
|
||||||
|
// index++;
|
||||||
|
// }
|
||||||
|
// output = temp;
|
||||||
|
|
||||||
const createAnchorTokens = (anchorName: string) => {
|
// return output;
|
||||||
const output = [];
|
// };
|
||||||
|
|
||||||
{
|
// const createAnchorTokens = (anchorName: string) => {
|
||||||
const token = new Token('heading_anchor_open', 'a', 1);
|
// const output = [];
|
||||||
token.attrs = [
|
|
||||||
['name', anchorName],
|
|
||||||
['href', `#${anchorName}`],
|
|
||||||
['class', 'heading-anchor'],
|
|
||||||
];
|
|
||||||
output.push(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
// {
|
||||||
const token = new Token('text', '', 0);
|
// const token = new Token('heading_anchor_open', 'a', 1);
|
||||||
token.content = '🔗';
|
// token.attrs = [
|
||||||
output.push(token);
|
// ['name', anchorName],
|
||||||
}
|
// ['href', `#${anchorName}`],
|
||||||
|
// ['class', 'heading-anchor'],
|
||||||
|
// ];
|
||||||
|
// output.push(token);
|
||||||
|
// }
|
||||||
|
|
||||||
{
|
// {
|
||||||
const token = new Token('heading_anchor_close', 'a', -1);
|
// const token = new Token('text', '', 0);
|
||||||
output.push(token);
|
// token.content = '🔗';
|
||||||
}
|
// output.push(token);
|
||||||
|
// }
|
||||||
|
|
||||||
return output;
|
// {
|
||||||
};
|
// const token = new Token('heading_anchor_close', 'a', -1);
|
||||||
|
// output.push(token);
|
||||||
|
// }
|
||||||
|
|
||||||
let insideHeading = false;
|
// return output;
|
||||||
for (let i = 0; i < tokens.length; i++) {
|
// };
|
||||||
const token = tokens[i];
|
|
||||||
|
|
||||||
if (token.type === 'heading_open') {
|
// let insideHeading = false;
|
||||||
insideHeading = true;
|
// for (let i = 0; i < tokens.length; i++) {
|
||||||
continue;
|
// const token = tokens[i];
|
||||||
}
|
|
||||||
|
|
||||||
if (token.type === 'heading_close') {
|
// if (token.type === 'heading_open') {
|
||||||
insideHeading = false;
|
// insideHeading = true;
|
||||||
continue;
|
// continue;
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (insideHeading && token.type === 'inline') {
|
// if (token.type === 'heading_close') {
|
||||||
const anchorName = headingTextToAnchorName(token.content, doneNames);
|
// insideHeading = false;
|
||||||
doneNames.push(anchorName);
|
// continue;
|
||||||
const anchorTokens = createAnchorTokens(anchorName);
|
// }
|
||||||
// token.children = anchorTokens.concat(token.children);
|
|
||||||
token.children = token.children.concat(anchorTokens);
|
// if (insideHeading && token.type === 'inline') {
|
||||||
}
|
// const anchorName = headingTextToAnchorName(token.content, doneNames);
|
||||||
}
|
// doneNames.push(anchorName);
|
||||||
});
|
// const anchorTokens = createAnchorTokens(anchorName);
|
||||||
|
// // token.children = anchorTokens.concat(token.children);
|
||||||
|
// token.children = token.children.concat(anchorTokens);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
return renderMustache(markdownIt.render(md), templateParams);
|
return renderMustache(markdownIt.render(md), templateParams);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user