Skip to content
Snippets Groups Projects
Commit b5e94af7 authored by Efe's avatar Efe
Browse files

Add 'packages/renderer/' from commit '80a1f56c'

git-subtree-dir: packages/renderer
git-subtree-mainline: 54859e55
git-subtree-split: 80a1f56c
parents 54859e55 80a1f56c
No related branches found
No related tags found
1 merge request!557Move hive renderer to internal packages
Showing
with 19468 additions and 0 deletions
{
"extends": "@engrave/eslint-config-engrave"
}
\ No newline at end of file
.idea
.vscode
/dist
/.DS_Store
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# next.js build output
.next
stages:
- test
- release
image: node:20.11.1
before_script:
- npm ci
lint:code:
stage: test
script:
- npm run lint
lint:commit message:
stage: test
script:
- echo "${CI_COMMIT_MESSAGE}" | npx commitlint
test:unit:
stage: test
script:
- npm run test:coverage
- NODE_ENV=production npm run build && node sample/demo-local.js
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
test:browser:
stage: test
image:
name: testcafe/testcafe
entrypoint: ["/bin/sh", "-c"]
script:
- NODE_ENV=production npm run build
- npm run verify:ci
release:
stage: release
script:
- npm run semantic-release
rules:
- if: '$CI_COMMIT_BRANCH == "master"'
pages:
stage: release
script:
- mkdir -p public
- cp sample/live-demo.html public/index.html
artifacts:
paths:
- public
rules:
- if: '$CI_COMMIT_BRANCH == "master"'
npx --no-install -- commitlint --edit $1
\ No newline at end of file
npm test
\ No newline at end of file
recursive: true
require: ts-node/register
\ No newline at end of file
/_dev
/.travis.yml
/.prettierrc.yml
/TODO
/README.md
/LICENSE
/src
/.DS_Store
/sample
/tslint.json
/tsconfig.json
/tsconfig.lint.json
/tsconfig.build.json
*.test.ts
/dist/browser/statistics.html
.gitlab-ci.yml
.mocharc.yml
.nvmrc
.eslintrc
.prettierrc.js
.husky
.run
browser-test
webpack.config.js
.idea
coverage
\ No newline at end of file
v20.11.1
\ No newline at end of file
module.exports = require('@engrave/eslint-config-engrave/prettier.config');
\ No newline at end of file
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Run all tests" type="mocha-javascript-test-runner">
<node-interpreter>project</node-interpreter>
<node-options />
<mocha-package>$PROJECT_DIR$/node_modules/mocha</mocha-package>
<working-directory>$PROJECT_DIR$</working-directory>
<pass-parent-env>true</pass-parent-env>
<ui>bdd</ui>
<extra-mocha-options>--require ts-node/register</extra-mocha-options>
<test-kind>PATTERN</test-kind>
<test-pattern>**/*.test.ts</test-pattern>
<method v="2" />
</configuration>
</component>
\ No newline at end of file
MIT License
Copyright (c) 2019 Wise Team
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# @hiveio/content-renderer
[![npm](https://img.shields.io/npm/v/@hiveio/content-renderer.svg?style=flat-square)](https://www.npmjs.com/package/@hiveio/content-renderer) [![](https://img.badgesize.io/https:/unpkg.com/@hiveio/content-renderer@1.0.2/dist/browser/hive-content-renderer.min.js.svg?compression=gzip)](https://www.npmjs.com/package/@hiveio/content-renderer) [![License](https://img.shields.io/github/license/wise-team/steem-content-renderer.svg?style=flat-square)](https://github.com/wise-team/steem-content-renderer/blob/master/LICENSE) [![](https://img.shields.io/badge/semantic--release-angular-e10079?logo=semantic-release)](https://github.com/semantic-release/semantic-release) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
👉 **[Online demo](https://hive.pages.syncad.com/hive-renderer/)**
Portable library that renders Hive posts and comments to string. It supports markdown and html and mimics the behaviour of condenser frontend.
Features:
- supports markdown and html
- sanitizes html and protects from XSS
- embeds images, videos, and other assets via links or iframes
- ensures links are safe to display and begins with `https://` protocol
- linkify #tags and @username mentions
- proxify images if needed and appropriate function is provided
- synchronous execution with no external calls
**Credit**: this library is based on the code from condenser. It's aim is to allow other projects display Hive content the right way without porting the same code over and over.
## Server side usage
Installation:
```bash
$ npm install --save @hiveio/content-renderer
```
**Typescript:**
```typescript
import { DefaultRenderer } from "@hiveio/content-renderer";
const renderer = new DefaultRenderer({
baseUrl: "https://hive.blog/",
breaks: true,
skipSanitization: false,
allowInsecureScriptTags: false,
addNofollowToLinks: true,
doNotShowImages: false,
assetsWidth: 640,
assetsHeight: 480,
imageProxyFn: (url: string) => url,
usertagUrlFn: (account: string) => "/@" + account,
hashtagUrlFn: (hashtag: string) => "/trending/" + hashtag,
isLinkSafeFn: (url: string) => true,
addExternalCssClassToMatchingLinksFn: (url: string) => true,
ipfsPrefix: "https://ipfs.io/ipfs/" // IPFS gateway to display ipfs images
});
const safeHtmlStr = renderer.render(postContent);
```
## Browser usage:
See [demo](https://hive.pages.syncad.com/hive-renderer/) and [its source](https://gitlab.syncad.com/hive/hive-renderer/-/blob/master/sample/live-demo.html).
```html
<script src="https://unpkg.com/@hiveio/content-renderer"></script>
<script>
const renderer = new HiveContentRenderer.DefaultRenderer({
baseUrl: "https://hive.blog/",
breaks: true,
skipSanitization: false,
allowInsecureScriptTags: false,
addNofollowToLinks: true,
doNotShowImages: false,
cssClassForInternalLinks: "link",
cssClassForExternalLinks: "external",
assetsWidth: 640,
assetsHeight: 480,
imageProxyFn: (url) => url,
usertagUrlFn: (account) => "/@" + account,
hashtagUrlFn: (hashtag) => "/trending/" + hashtag,
isLinkSafeFn: (url) => true,
addExternalCssClassToMatchingLinksFn: (url: string) => true,
ipfsPrefix: "https://ipfs.io/ipfs/"
});
$(document).ready(() => {
const renderMarkdownBtnElem = $("#render-button");
const inputElem = $("#input");
const outputElem = $("#output");
const outputMarkupElem = $("#output-markup");
renderMarkdownBtnElem.on("click", () => {
const input = inputElem.val();
const output = renderer.render(input);
console.log("Rendered", output);
outputElem.html(output);
outputMarkupElem.text(output);
});
});
</script>
</body>
</html>
```
## Renderer options
You can pass options to the renderer to customize its behaviour. Here is the list of available
```typescript
export interface RendererOptions {
baseUrl: string;
breaks: boolean;
skipSanitization: boolean;
allowInsecureScriptTags: boolean;
addNofollowToLinks: boolean;
doNotShowImages: boolean;
assetsWidth: number;
assetsHeight: number;
imageProxyFn: (url: string) => string;
hashtagUrlFn: (hashtag: string) => string;
usertagUrlFn: (account: string) => string;
isLinkSafeFn: (url: string) => boolean;
addExternalCssClassToMatchingLinksFn: (url: string) => boolean;
addTargetBlankToLinks?: boolean;
cssClassForInternalLinks?: string;
cssClassForExternalLinks?: string;
ipfsPrefix?: string;
}
```
## Options explained
- `baseUrl` - base url of the website. It's used to create links.
- `breaks` - if true, newlines characters (`\n`) are converted to `<br>` tags. This only applies to markdown input. Usually you want to set this to `true`.
- `skipSanitization` - if true, html is not sanitized. This is not recommended, as it can lead to XSS attacks. Set this to `false` always for production use.
- `allowInsecureScriptTags` - if true, script tags are not removed from the input. This is not recommended, as it can lead to XSS attacks. Set this to `false` always for production use.
- `addNofollowToLinks` - if true, `rel="nofollow"` is added to all links.
- `addTargetBlankToLinks` - if true, `target="_blank"` is added to all links.
- `doNotShowImages` - if true, images are not being rendered as `<img>` tags but as `<pre>` tags with the image url.
- `assetsWidth` - width of the images and embeds in pixels.
- `assetsHeight` - height of the images and embeds in pixels.
- `imageProxyFn` - function that takes an image url and returns a proxied url. This can be useful to use a proxy to display images. It's also useful to resize images.
- `hashtagUrlFn` - function that takes a hashtag and returns a url to the hashtag page.
- `usertagUrlFn` - function that takes a usertag and returns a url to the user profile. This might be useful if you want to differentiate between internal and external links for some specific accounts, like bad actors.
- `isLinkSafeFn` - function that takes a link and returns true if the link is safe to display. This can be useful to filter out links to phishing sites or other malicious content. If this function returns false, the link is not displayed and title is set to phishing warning.
- `addExternalCssClassToMatchingLinksFn` - function that takes a link and returns true if the link should have `cssClassForExternalLinks` added to it. This can be useful to differentiate appearance between internal and external links.
- `addTargetBlankToLinks` - if true, `target="_blank"` is added to all links.
- `cssClassForInternalLinks` - if set, this class is added to all internal links.
- `cssClassForExternalLinks` - if set, this class is added to all external links if `addExternalCssClassToMatchingLinksFn` returns true.
- `ipfsPrefix` - if set, this prefix is added to all IPFS links. This can be useful if you want to use a public IPFS gateway to display ipfs images. It may or may not end with a slash.
## Development
Library is written in typescript and expects NodeJS v20 or higher. If you use nvm, you can run `nvm install` and `nvm use` to switch to the right version automatically.
To start developing:
```bash
$ npm install
$ npm run build
```
## Testing
### Unit tests
The library is tested with mocha and chai. To run unit tests:
```bash
$ npm run test
```
### Integration tests
Integration tests are run with testcafe. Please note you need to rebuild the library before running the tests in order to have the latest version of the library embedded in the test page. Run `npm run build` before running the tests.
To run integration tests with your default browser (chrome):
```bash
$ npm run verify:chrome
$ npm run verify:firefox
```
## Linting
In order to provide consistent code style, the library is linted with eslint with prettier and typescript plugin. This is enforced in CI and git hooks.
To run linter:
```bash
$ npm run lint
```
## Semantic versioning
Library follows semantic versioning and is released to NPM registry automatically with CI after a merge to master. CI and husky is configured in a way to enforce [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/). Git hooks are installed to enforce this in local development.
Versioning is done automatically with `semantic-release`. Please note that the version is bumped automatically based on the commit messages.
\ No newline at end of file
import {ClientFunction, Selector} from 'testcafe';
fixture`Getting Started`.page`./index.html`;
const defaultOptions = {
baseUrl: 'https://hive.blog/',
breaks: true,
skipSanitization: false,
allowInsecureScriptTags: false,
addTargetBlankToLinks: true,
addNofollowToLinks: true,
cssClassForInternalLinks: 'hive-class',
cssClassForExternalLinks: 'hive-class external',
doNotShowImages: false,
assetsWidth: 640,
assetsHeight: 480,
imageProxyFn: (url) => url,
usertagUrlFn: (account) => `https://hive.blog/@${account}`,
hashtagUrlFn: (hashtag) => `/trending/${hashtag}`,
isLinkSafeFn: (url) => true, // !!url.match(/^(\/(?!\/)|https:\/\/hive.blog)/),
addExternalCssClassToMatchingLinksFn: (url) => !url.match(/^(\/(?!\/)|https:\/\/hive.blog)/)
};
const renderInBrowser = ClientFunction((options, markup) => {
// Create a new instance of SpoilerPlugin directly
// To test custom plugins
const spoilerPlugin = {
name: 'spoiler',
preProcess: (text) => {
return text.replace(
/^>! *\[(.*?)\] *([\s\S]*?)(?=^>! *\[|$)/gm,
(_, title, content) => {
const cleanContent = content
.split('\n')
.map(line => line.replace(/^> ?/, '').trim())
.join('\n')
.trim();
return `<details class="spoiler">
<summary>${title}</summary>
${cleanContent}
</details>`;
}
);
}
};
const mergedOptions = Object.assign({}, options, {
plugins: [spoilerPlugin]
});
const renderer = new HiveContentRenderer.DefaultRenderer(mergedOptions);
return renderer.render(markup);
});
test('Renders properly simple markup', async (t) => {
const markup = '# H1';
await t
.click(Selector('#awaiter'))
.expect(renderInBrowser({...defaultOptions}, markup))
.eql('<h1>H1</h1>\n');
});
test('Does not crash on mixed-img markup', async (t) => {
const markup = `<img src="![Sacrifice The Truth Logo.jpg](https://images.hive.blog/DQmUjNstssuPJpjPDDWfRnw1x2tY6AWWKcajDMGpPLA5iJf/Sacrifice%20The%20Truth%20Logo.jpg)"/>`;
const expected = `<p><img src="brokenimg.jpg" /></p>\n`;
await t
.click(Selector('#awaiter'))
.expect(renderInBrowser({...defaultOptions}, markup))
.eql(expected);
});
test('Renders properly simple link markup with classes hive-test, external', async (t) => {
const markup = '[Hive Link](https://hive.io)';
await t
.click(Selector('#awaiter'))
.expect(renderInBrowser({...defaultOptions}, markup))
.eql(`<p><a href="https://hive.io" class="hive-class external">Hive Link</a></p>\n`);
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Browser test of clean-stack</title>
<script>
module = {
exports: undefined,
};
</script>
<script src="../dist/browser/hive-content-renderer.min.js"></script>
<script>
console.log(HiveContentRenderer.DefaultRenderer);
</script>
</head>
<button id="awaiter">Awaiter</button>
<body>
</body>
</html>
This diff is collapsed.
{
"name": "@hiveio/content-renderer",
"version": "0.0.0-development",
"description": "Content renderer for Hive posts and comments. Markdown + HTML",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"unpkg": "dist/browser/hive-content-renderer.min.js",
"engines": {
"node": ">=20"
},
"publishConfig": {
"access": "public"
},
"scripts": {
"build:clean": "rm -rf dist",
"build:node": "tsc -p tsconfig.build.json",
"build:browser": "rm -rf dist/browser/ && NODE_ENV=production webpack --mode=production --config webpack.config.js",
"build": "npm run build:clean && npm run build:node && npm run build:browser",
"prepare": "npm run hooks:install && NODE_ENV=production npm run build",
"lint": "eslint ./src --ext .js,.jsx,.ts,.tsx",
"lint:fix": "eslint ./src --ext .js,.jsx,.ts,.tsx --fix",
"test": "mocha 'src/**/*.test.ts'",
"test:coverage": "nyc --reporter=cobertura mocha 'src/**/*.test.ts'",
"verify:ci": "/opt/testcafe/docker/testcafe-docker.sh --selector-timeout 2000 --assertion-timeout 2000 chromium,firefox browser-test/browser-test.js",
"verify:chrome": "testcafe --selector-timeout 2000 --assertion-timeout 2000 chrome browser-test/browser-test.js",
"verify:firefox": "testcafe --selector-timeout 2000 --assertion-timeout 2000 firefox browser-test/browser-test.js",
"semantic-release": "semantic-release",
"hooks:install": "husky"
},
"dependencies": {
"@xmldom/xmldom": "0.8.10",
"ow": "0.28.2",
"react-twitter-embed": "^4.0.4",
"remarkable": "2.0.1",
"sanitize-html": "2.13.0",
"typescript-chained-error": "1.6.0",
"universe-log": "5.2.0"
},
"devDependencies": {
"@commitlint/cli": "19.3.0",
"@commitlint/config-conventional": "19.2.2",
"@engrave/eslint-config-engrave": "1.0.0",
"@semantic-release/gitlab": "13.2.0",
"@types/chai": "4.3.16",
"@types/jsdom": "21.1.7",
"@types/lodash": "4.17.6",
"@types/mocha": "10.0.7",
"@types/node": "20.14.10",
"@types/remarkable": "2.0.8",
"@types/sanitize-html": "2.11.0",
"@types/uuid": "10.0.0",
"@typescript-eslint/eslint-plugin": "7.15.0",
"chai": "4.4.1",
"eslint": "8.57.0",
"eslint-plugin-import": "2.29.1",
"husky": "9.0.11",
"jsdom": "24.1.0",
"lodash": "4.17.21",
"mocha": "10.6.0",
"nyc": "17.0.0",
"prettier": "3.3.2",
"semantic-release": "24.0.0",
"testcafe": "3.6.2",
"ts-node": "10.9.2",
"typescript": "5.5.3",
"typescript-eslint": "7.15.0",
"uuid": "10.0.0",
"webpack": "5.92.1",
"webpack-cli": "5.1.4",
"webpack-visualizer-plugin2": "1.1.0"
},
"commitlint": {
"extends": [
"@commitlint/config-conventional"
],
"rules": {
"header-max-length": [
0
],
"scope-case": [
0
]
}
},
"repository": {
"type": "git",
"url": "https://gitlab.syncad.com/hive/hive-renderer.git"
},
"keywords": [
"hive",
"markdown",
"renderer",
"blockchain",
"hive",
"content",
"content-renderer"
],
"author": "Engrave (https://engrave.dev/)",
"contributors": [
"Jędrzej Lewandowski <jedrzejblew@gmail.com> (https://jedrzej.lewandowski.doctor/)",
"Bartłomiej Górnicki <contact@engrave.dev> (https://engrave.dev)"
],
"license": "MIT",
"bugs": {
"url": "https://gitlab.syncad.com/hive/hive-renderer/-/issues"
},
"homepage": "https://hive.io",
"release": {
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
"@semantic-release/gitlab",
"@semantic-release/npm"
]
}
}
const HiveContentRenderer = require("@hiveio/content-renderer");
const renderer = new HiveContentRenderer.DefaultRenderer({
baseUrl: "https://hive.blog/",
breaks: true,
skipSanitization: false,
allowInsecureScriptTags: false,
addNofollowToLinks: true,
addTargetBlankToLink: true,
cssClassForInternalLinks: "hive-class",
doNotShowImages: false,
assetsWidth: 640,
assetsHeight: 480,
imageProxyFn: (url) => url,
usertagUrlFn: (account) => "/@" + account,
hashtagUrlFn: (hashtag) => "/trending/" + hashtag,
isLinkSafeFn: (url) => true,
addExternalCssClassToMatchingLinksFn: (url) => true,
});
const input = `
# Sample post
and some content
Lets mention @engrave on #hive.
[Hive Link](https://hive.io)
`;
const output = renderer.render(input);
console.log();
console.log("+-------------------------------+");
console.log("| @hiveio/content-renderer demo |");
console.log("+-------------------------------+");
console.log();
console.log(output);
console.log();
console.log();
const HiveContentRenderer = require("../dist/index");
const renderer = new HiveContentRenderer.DefaultRenderer({
baseUrl: "https://hive.blog/",
breaks: true,
skipSanitization: false,
allowInsecureScriptTags: false,
addNofollowToLinks: true,
addTargetBlankToLink: true,
cssClassForInternalLinks: "hive-class",
cssClassForExternalLinks: "external",
doNotShowImages: false,
assetsWidth: 640,
assetsHeight: 480,
imageProxyFn: (url) => url,
usertagUrlFn: (account) => "/@" + account,
hashtagUrlFn: (hashtag) => "/trending/" + hashtag,
isLinkSafeFn: (url) => true,
addExternalCssClassToMatchingLinksFn: (url) => true,
});
const input = `
# Sample post
and some content
Lets mention @engrave on #hive.
[Hive Link](https://hive.io)
`;
const output = renderer.render(input);
console.log();
console.log("+-------------------------------+");
console.log("| @hiveio/content-renderer demo |");
console.log("+-------------------------------+");
console.log();
console.log(output);
console.log();
console.log();
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, height=device-height, user-scalable=no, initial-scale=1.0" />
<title>@hiveio/content-renderer live demo</title>
<style>
/* source: https://github.com/setetres/evenbettermotherfuckingwebsite */
body {
margin: 5% auto;
padding: 0 3rem;
background: #f2f2f2;
color: #444444;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
font-size: 16px;
line-height: 1.8;
text-shadow: 0 1px 0 #ffffff;
max-width: 800px;
}
code {
background: white;
}
a {
border-bottom: 1px solid #444444;
color: #444444;
text-decoration: none;
}
a:hover {
border-bottom: 0;
}
/**/
h1 {
font-size: 2.2em;
}
h2,
h3,
h4,
h5 {
margin-bottom: 0;
}
#output {
border: 1px solid #777;
padding: 0.5rem;
}
#output-markup {
width: 100%;
padding: 0.5rem;
background: #eee;
border: 1px solid #777;
overflow-x: scroll;
}
#render-button-container {
text-align: center;
}
#render-button {
padding: 0.5rem;
font-size: 1.2em;
border-radius: 0.5rem 0.5rem;
}
.load-post-form {
text-align: right;
}
header small {
color: #999;
font-size: 50%;
}
img {
max-width: 100%;
}
</style>
</head>
<body>
<header>
<h1>@hiveio/content-renderer <small>example</small></h1>
<aside>
@hiveio/content-renderer is aimed at unifying post rendering across all Hive interfaces. The rendering code was extracted from
<a href="https://gitlab.syncad.com/hive/condenser">condenser</a>, refactored, tested and bundled into a standalone library. This approach allows
independent development and continous improvement of post rendering in Hive blockchain. As for now it is fully compatible with the Hive.blog way of
rendering posts. See the <a href="https://gitlab.syncad.com/hive/hive-renderer">repository</a>, integrate into your project, star, make pull requests and
create issues. Let's make the project alive!
<hr />
This example uses some markdown and transforms it to html. The library is loaded from the unpkg CDN:
<em><a href="https://unpkg.com/@hiveio/content-renderer">https://unpkg.com/@hiveio/content-renderer</a></em
>.
</aside>
</header>
<h2>Render markdown:</h2>
<div class="load-post-form">
Link to post (hive.blog, peakd.com or ecency.com): <input type="text" id="post-link-input" />
<button id="load-post-button">Load Hive post</button>
</div>
<textarea rows="12" style="width: 100%" id="input">
# Sample post
and some content.
Let's mention @engrave
or include a tag #hive.
https://youtu.be/B7C83L6iWJQ
[Hive Link](https://hive.io)
</textarea>
<p id="render-button-container"><button id="render-button">Render markdown</button></p>
<h2>Output:</h2>
<p id="output">...press the button...</p>
<br />
<h2>Generated HTML markup</h2>
<pre id="output-markup">
...press the button...
</pre>
<script src="https://cdn.jsdelivr.net/npm/@hiveio/hive-js/dist/hive.min.js"></script>
<script src="https://unpkg.com/jquery"></script>
<script src="https://unpkg.com/@hiveio/content-renderer@latest"></script>
<script>
const renderer = new HiveContentRenderer.DefaultRenderer({
baseUrl: "https://hive.blog/",
breaks: true,
skipSanitization: false,
allowInsecureScriptTags: false,
addNofollowToLinks: true,
addTargetBlankToLinks: true,
cssClassForInternalLinks: "hive-test",
cssClassForExternalLinks: "external",
doNotShowImages: false,
assetsWidth: 640,
assetsHeight: 640,
imageProxyFn: (url) => url,
usertagUrlFn: (account) => "https://hive.blog/@" + account,
hashtagUrlFn: (hashtag) => "https://hive.blog/trending/" + hashtag,
isLinkSafeFn: (url) => true,
addExternalCssClassToMatchingLinksFn: (url) => true,
ipfsPrefix: "https://ipfs.io/ipfs/",
plugins: [
new HiveContentRenderer.SpoilerPlugin()
]
});
$(document).ready(() => {
const renderMarkdownBtnElem = $("#render-button");
const inputElem = $("#input");
const outputElem = $("#output");
const outputMarkupElem = $("#output-markup");
const loadPostButton = $("#load-post-button");
const postLinkInput = $("#post-link-input");
function setOutput(output) {
outputElem.html(output);
outputMarkupElem.text(output);
}
function render() {
const input = inputElem.val();
const output = renderer.render(input);
console.log("Rendered", output);
setOutput(output);
}
function getAuthorAndPermlinkFromLink(link) {
let author = "";
let permlink = "";
if (link.length > 0) {
const regex =
/^\/?(?:https?:\/\/(?:hive\.blog|peakd\.com|ecency\.com))?(?:\/?[^\/\n]*\/)?@?([^\/\n]+)\/([^\/\n]+)$/giu;
const match = regex.exec(link);
if (match && match.length > 1) {
author = match[1];
permlink = match[2];
}
}
return { author, permlink };
}
renderMarkdownBtnElem.on("click", () => render());
loadPostButton.on("click", () => {
const postLink = postLinkInput.val();
const { author, permlink } = getAuthorAndPermlinkFromLink(postLink);
if (!author || author.length === 0 || !permlink || permlink.length === 0) {
inputElem.text("Author or permlink is missing...");
return;
}
inputElem.text("Loading post @" + author + "/" + permlink + " ...");
(async () => {
try {
const post = await hive.api.getContentAsync(author, permlink);
const postMarkdown = post.body;
console.log("Content loaded", postMarkdown);
inputElem.text(postMarkdown);
render();
} catch (error) {
inputElem.text("Error while loading post @" + author + "/" + permlink + ": " + error);
}
})();
});
});
</script>
</body>
</html>
import {AbstractUniverseLog} from 'universe-log';
export class Log extends AbstractUniverseLog {
public static log(): Log {
return Log.INSTANCE;
}
private static INSTANCE: Log = new Log();
private constructor() {
super({
levelEnvs: ['HIVE_CONTENT_RENDERER_LOG_LEVEL', 'ENGRAVE_LOG_LEVEL'],
metadata: {
library: '@hiveio/content-renderer'
}
});
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment