1
0
Fork 0
mirror of https://github.com/codex-team/codex.docs.git synced 2025-07-19 05:09:41 +02:00

Editorjs checklist tool (#98)

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>
This commit is contained in:
Alexander Menshikov 2020-05-09 05:38:25 +03:00 committed by GitHub
parent c0a4f6f3fd
commit b744ed592a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 2628 additions and 2152 deletions

View file

@ -13,7 +13,7 @@
"build:dev": "webpack ./src/frontend/js/app.js --o='./public/dist/[name].bundle.js' --output-library=Docs --output-public-path=/dist/ -p --mode=development --watch",
"precommit": "yarn lint && yarn test --exit",
"generatePassword": "node ./generatePassword.js",
"editor-upgrade": "yarn add -D @editorjs/{editorjs,header,code,delimiter,list,image,table,inline-code,marker,warning}@latest"
"editor-upgrade": "yarn add -D @editorjs/{editorjs,header,code,delimiter,list,image,table,inline-code,marker,warning,checklist}@latest"
},
"dependencies": {
"bcrypt": "^3.0.3",
@ -35,6 +35,7 @@
"node-fetch": "^2.3.0",
"nodemon": "^1.18.3",
"twig": "~1.12.0",
"typescript-eslint": "^0.0.1-alpha.0",
"uuid4": "^1.0.0"
},
"devDependencies": {
@ -43,15 +44,16 @@
"@babel/polyfill": "^7.2.5",
"@babel/preset-env": "^7.1.0",
"@codexteam/misprints": "^1.0.0",
"@editorjs/checklist": "^1.1.0",
"@editorjs/code": "^2.4.1",
"@editorjs/delimiter": "^1.1.0",
"@editorjs/editorjs": "^2.16.0",
"@editorjs/header": "^2.3.2",
"@editorjs/image": "^2.3.3",
"@editorjs/editorjs": "^2.17.0",
"@editorjs/header": "^2.4.1",
"@editorjs/image": "^2.3.4",
"@editorjs/inline-code": "^1.3.1",
"@editorjs/list": "^1.4.0",
"@editorjs/marker": "^1.2.2",
"@editorjs/table": "^1.2.0",
"@editorjs/table": "^1.2.2",
"@editorjs/warning": "^1.1.1",
"autoprefixer": "^9.1.3",
"babel": "^6.23.0",
@ -61,8 +63,8 @@
"chai-http": "^4.0.0",
"css-loader": "^1.0.0",
"cssnano": "^4.1.0",
"eslint": "^5.3.0",
"eslint-config-codex": "github:codex-team/eslint-config",
"eslint": "^6.8.0",
"eslint-config-codex": "^1.3.4",
"eslint-plugin-chai-friendly": "^0.4.1",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-node": "^8.0.1",

File diff suppressed because one or more lines are too long

View file

@ -1,2 +1,2 @@
.hljs{display:block;background:#fff;padding:.5em;color:#333;overflow-x:auto}.hljs-comment,.hljs-meta{color:#969896}.hljs-emphasis,.hljs-quote,.hljs-string,.hljs-strong,.hljs-template-variable,.hljs-variable{color:#df5000}.hljs-keyword,.hljs-selector-tag,.hljs-type{color:#a71d5d}.hljs-attribute,.hljs-bullet,.hljs-literal,.hljs-symbol{color:#0086b3}.hljs-name,.hljs-section{color:#63a35c}.hljs-tag{color:#333}.hljs-attr,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo,.hljs-title{color:#795da3}.hljs-addition{color:#55a532;background-color:#eaffea}.hljs-deletion{color:#bd2c00;background-color:#ffecec}.hljs-link{text-decoration:underline}
.hljs{display:block;background:#fff;padding:.5em;color:#333;overflow-x:auto}.hljs-comment,.hljs-meta{color:#969896}.hljs-emphasis,.hljs-quote,.hljs-strong,.hljs-template-variable,.hljs-variable{color:#df5000}.hljs-keyword,.hljs-selector-tag,.hljs-type{color:#d73a49}.hljs-attribute,.hljs-bullet,.hljs-literal,.hljs-symbol{color:#0086b3}.hljs-name,.hljs-section{color:#63a35c}.hljs-tag{color:#333}.hljs-attr,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo,.hljs-title{color:#6f42c1}.hljs-addition{color:#55a532;background-color:#eaffea}.hljs-deletion{color:#bd2c00;background-color:#ffecec}.hljs-link{text-decoration:underline}.hljs-number{color:#005cc5}.hljs-string{color:#032f62}
.diff{display:inline-block;width:100%}.diff span{color:inherit!important}.diff--added{color:#277030;background-color:#e2fce7}.diff--added:before{content:"+";opacity:.4}.diff--removed{color:#ae363c;background-color:#ffe6e6}.diff--removed:before{content:"-";opacity:.4}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -6,7 +6,6 @@ const Alias = require('../models/alias');
*/
class Aliases {
/**
* @static
* Find and return entity with given alias
*
* @param {string} aliasName - alias name of entity

View file

@ -7,7 +7,6 @@ const Alias = require('../models/alias');
*/
class Pages {
/**
* @static
* Fields required for page model creation
*
* @returns {['title', 'body']}
@ -17,7 +16,6 @@ class Pages {
}
/**
* @static
* Find and return page model with passed id
*
* @param {string} id - page id
@ -43,20 +41,18 @@ class Pages {
}
/**
* @static
* Return all pages without children of passed page
*
* @param {string} parent - id of current page
* @returns {Promise<Page[]>}
*/
static async getAllExceptChildren(parent) {
let pagesAvailable = this.removeChildren(await Pages.getAll(), parent);
const pagesAvailable = this.removeChildren(await Pages.getAll(), parent);
return pagesAvailable.filter((item) => item !== null);
}
/**
* @static
* Set all children elements to null
*
* @param {Page[]} [pagesAvailable] - Array of all pages
@ -71,6 +67,7 @@ class Pages {
pagesAvailable[index] = null;
pagesAvailable = Pages.removeChildren(pagesAvailable, item._id);
});
return pagesAvailable;
}
@ -91,7 +88,7 @@ class Pages {
if (insertedPage.uri) {
const alias = new Alias({
id: insertedPage._id,
type: Alias.types.PAGE
type: Alias.types.PAGE,
}, insertedPage.uri);
alias.save();
@ -161,7 +158,7 @@ class Pages {
if (updatedPage.uri) {
const alias = new Alias({
id: updatedPage._id,
type: Alias.types.PAGE
type: Alias.types.PAGE,
}, updatedPage.uri);
alias.save();

View file

@ -70,8 +70,8 @@ class PagesOrder {
* @param {Page[]} pages - list of all available pages
* @param {string} currentPageId - page's id around which we are ordering
* @param {string} parentPageId - parent page's id that contains page above
* @param {Boolean} ignoreSelf - should we ignore current page in list or not
* @return {Page[]}
* @param {boolean} ignoreSelf - should we ignore current page in list or not
* @returns {Page[]}
*/
static async getOrderedChildren(pages, currentPageId, parentPageId, ignoreSelf = false) {
const children = await Model.get(parentPageId);

View file

@ -17,6 +17,7 @@ const config = require('../../config');
class Transport {
/**
* Saves file passed from client
*
* @param {object} multerData - file data from multer
* @param {string} multerData.originalname - original name of the file
* @param {string} multerData.filename - name of the uploaded file
@ -25,12 +26,18 @@ class Transport {
* @param {string} multerData.mimetype - MIME type of the uploaded file
*
* @param {object} map - object that represents how should fields of File object should be mapped to response
* @return {Promise<FileData>}
* @returns {Promise<FileData>}
*/
static async save(multerData, map) {
const { originalname: name, path, filename, size, mimetype } = multerData;
const file = new Model({ name, filename, path, size, mimetype });
const file = new Model({
name,
filename,
path,
size,
mimetype,
});
await file.save();
@ -45,9 +52,10 @@ class Transport {
/**
* Fetches file by passed URL
*
* @param {string} url - URL of the file
* @param {object} map - object that represents how should fields of File object should be mapped to response
* @return {Promise<FileData>}
* @returns {Promise<FileData>}
*/
static async fetch(url, map) {
const fetchedFile = await fetch(url);
@ -64,7 +72,7 @@ class Transport {
filename: `${filename}.${ext}`,
path: `${config.uploads}/${filename}.${ext}`,
size: buffer.length,
mimetype: type ? type.mime : fetchedFile.headers.get('content-type')
mimetype: type ? type.mime : fetchedFile.headers.get('content-type'),
});
await file.save();
@ -94,11 +102,12 @@ class Transport {
if (fields.length > 1) {
let object = {};
let result = object;
const result = object;
fields.forEach((field, i) => {
if (i === fields.length - 1) {
object[field] = data[name];
return;
}

View file

@ -6,7 +6,6 @@ const Model = require('../models/user');
*/
class Users {
/**
* @static
* Find and return user model.
*
* @returns {Promise<User>}

View file

@ -10,6 +10,7 @@ import List from '@editorjs/list';
import Delimiter from '@editorjs/delimiter';
import Table from '@editorjs/table';
import Warning from '@editorjs/warning';
import Checklist from '@editorjs/checklist';
/**
* Inline Tools for the Editor
@ -80,6 +81,11 @@ export default class Editor {
inlineToolbar: true
},
checklist: {
class: Checklist,
inlineToolbar: true,
},
/**
* Inline Tools
*/

View file

@ -362,3 +362,59 @@
padding-left: 15px;
}
}
/**
* Checklist
* ==================
*/
.block-checklist {
margin: 20px 0;
&__item {
display: flex;
box-sizing: content-box;
align-items: center;
&-checkbox {
display: inline-block;
flex-shrink: 0;
position: relative;
width: 20px;
height: 20px;
margin: 0 10px 0 0;
border-radius: 50%;
border: 1px solid #d0d0d0;
background: #fff;
user-select: none;
&::after {
position: absolute;
top: 5px;
left: 5px;
width: 8px;
height: 5px;
border: 2px solid #fcfff4;
border-top: none;
border-right: none;
background: transparent;
content: '';
opacity: 0;
transform: rotate(-45deg);
}
&--checked {
background: #388ae5;
border-color: #388ae5;
}
}
&-text {
outline: none;
flex-grow: 1;
padding: 5px 0;
}
}
}
.block-checklist__item-checkbox--checked, .block-checklist__item-checkbox::after {
opacity: 1;
}

View file

@ -2,7 +2,7 @@ const { aliases: aliasesDb } = require('../utils/database/index');
const { binaryMD5 } = require('../utils/crypto');
/**
* @typedef {Object} AliasData
* @typedef {object} AliasData
* @property {string} _id - alias id
* @property {string} hash - alias binary hash
* @property {string} type - entity type
@ -25,22 +25,26 @@ class Alias {
/**
* Return Alias types
*
* @returns {Object}
* @returns {object}
*/
static get types() {
return {
PAGE: 'page'
PAGE: 'page',
};
};
/**
* Find and return alias with given alias
*
* @param {string} aliasName - alias of entity
* @returns {Promise<Alias>}
*/
static async get(aliasName) {
const hash = binaryMD5(aliasName);
let data = await aliasesDb.findOne({ hash: hash, deprecated: false });
let data = await aliasesDb.findOne({
hash: hash,
deprecated: false,
});
if (!data) {
data = await aliasesDb.findOne({ hash: hash });
@ -50,7 +54,7 @@ class Alias {
}
/**
* @constructor
* @class
*
* @param {AliasData} data
* @param {string} aliasName - alias of entity
@ -110,12 +114,13 @@ class Alias {
id: this.id,
type: this.type,
hash: this.hash,
deprecated: this.deprecated
deprecated: this.deprecated,
};
}
/**
* Mark alias as deprecated
*
* @param {string} aliasName - alias of entity
* @returns {Promise<Alias>}
*/

View file

@ -1,7 +1,7 @@
const { files: filesDb } = require('../utils/database/index');
/**
* @typedef {Object} FileData
* @typedef {object} FileData
*
* @property {string} _id - file id
* @property {string} name - original file name
@ -25,6 +25,7 @@ const { files: filesDb } = require('../utils/database/index');
class File {
/**
* Find and return model of file with given id
*
* @param {string} _id - file id
* @returns {Promise<File>}
*/
@ -36,6 +37,7 @@ class File {
/**
* Find and return model of file with given id
*
* @param {string} filename - uploaded filename
* @returns {Promise<File>}
*/
@ -48,7 +50,7 @@ class File {
/**
* Find all files which match passed query object
*
* @param {Object} query
* @param {object} query
* @returns {Promise<File[]>}
*/
static async getAll(query = {}) {
@ -58,7 +60,7 @@ class File {
}
/**
* @constructor
* @class
*
* @param {FileData} data
*/
@ -101,7 +103,7 @@ class File {
filename: this.filename,
path: this.path,
mimetype: this.mimetype,
size: this.size
size: this.size,
};
}
@ -137,8 +139,9 @@ class File {
/**
* Removes unnecessary public folder prefix
*
* @param {string} path
* @return {string}
* @returns {string}
*/
processPath(path) {
return path.replace(/^public/, '');

View file

@ -2,7 +2,7 @@ const urlify = require('../utils/urlify');
const { pages: pagesDb } = require('../utils/database/index');
/**
* @typedef {Object} PageData
* @typedef {object} PageData
* @property {string} _id - page id
* @property {string} title - page title
* @property {string} uri - page uri
@ -23,6 +23,7 @@ const { pages: pagesDb } = require('../utils/database/index');
class Page {
/**
* Find and return model of page with given id
*
* @param {string} _id - page id
* @returns {Promise<Page>}
*/
@ -34,6 +35,7 @@ class Page {
/**
* Find and return model of page with given uri
*
* @param {string} uri - page uri
* @returns {Promise<Page>}
*/
@ -46,7 +48,7 @@ class Page {
/**
* Find all pages which match passed query object
*
* @param {Object} query
* @param {object} query
* @returns {Promise<Page[]>}
*/
static async getAll(query = {}) {
@ -56,7 +58,7 @@ class Page {
}
/**
* @constructor
* @class
*
* @param {PageData} data
*/
@ -89,7 +91,7 @@ class Page {
/**
* Return PageData object
*
* @return {PageData}
* @returns {PageData}
*/
get data() {
return {
@ -97,13 +99,14 @@ class Page {
title: this.title,
uri: this.uri,
body: this.body,
parent: this._parent
parent: this._parent,
};
}
/**
* Extract first header from editor data
* @return {string}
*
* @returns {string}
*/
extractTitleFromBody() {
const headerBlock = this.body ? this.body.blocks.find(block => block.type === 'header') : '';
@ -113,7 +116,8 @@ class Page {
/**
* Transform title for uri
* @return {string}
*
* @returns {string}
*/
transformTitleToUri() {
return urlify(this.title);
@ -184,6 +188,7 @@ class Page {
* Find and return available uri
*
* @returns {Promise<string>}
* @param uri
*/
async composeUri(uri) {
let pageWithSameUriCount = 0;

View file

@ -1,7 +1,7 @@
const { pagesOrder: db } = require('../utils/database/index');
/**
* @typedef {Object} PageOrderData
* @typedef {object} PageOrderData
* @property {string} _id - row unique id
* @property {string} page - page id
* @property {Array<string>} order - list of ordered pages
@ -37,7 +37,7 @@ class PageOrder {
/**
* Find all pages which match passed query object
*
* @param {Object} query
* @param {object} query
* @returns {Promise<Page[]>}
*/
static async getAll(query = {}) {
@ -47,7 +47,7 @@ class PageOrder {
}
/**
* @constructor
* @class
*
* @param {PageOrderData} data
*/
@ -65,6 +65,7 @@ class PageOrder {
/**
* constructor data setter
*
* @param {PageOrderData} pageOrderData
*/
set data(pageOrderData) {
@ -74,13 +75,14 @@ class PageOrder {
/**
* Return Page Children order
*
* @returns {PageOrderData}
*/
get data() {
return {
_id: this._id,
page: '' + this._page,
order: this._order
order: this._order,
};
}
@ -176,7 +178,7 @@ class PageOrder {
/**
* Returns ordered list
*
* @return {string[]}
* @returns {string[]}
*/
get order() {
return this._order;

View file

@ -24,9 +24,9 @@ class User {
}
/**
* @constructor
* @class
*
* @param {Object} userData
* @param {object} userData
*/
constructor(userData) {
this.passHash = userData.passHash;

View file

@ -23,19 +23,20 @@ router.get('*', verifyToken, async (req, res) => {
switch (alias.type) {
case Alias.types.PAGE: {
let page = await Pages.get(alias.id);
const page = await Pages.get(alias.id);
let pageParent = await page.parent;
const pageParent = await page.parent;
res.render('pages/page', {
page, pageParent
page,
pageParent,
});
}
}
} catch (err) {
res.status(400).json({
success: false,
error: err.message
error: err.message,
});
}
});

View file

@ -16,7 +16,7 @@ const parseForm = express.urlencoded({ extended: false });
router.get('/auth', csrfProtection, function (req, res) {
res.render('auth', {
title: 'Login page',
csrfToken: req.csrfToken()
csrfToken: req.csrfToken(),
});
});
@ -24,13 +24,13 @@ router.get('/auth', csrfProtection, function (req, res) {
* Process given password
*/
router.post('/auth', parseForm, csrfProtection, async (req, res) => {
let userDoc = await Users.get();
const userDoc = await Users.get();
if (!userDoc) {
res.render('auth', {
title: 'Login page',
header: 'Password not set',
csrfToken: req.csrfToken()
csrfToken: req.csrfToken(),
});
}
@ -41,14 +41,14 @@ router.post('/auth', parseForm, csrfProtection, async (req, res) => {
res.render('auth', {
title: 'Login page',
header: 'Wrong password',
csrfToken: req.csrfToken()
csrfToken: req.csrfToken(),
});
}
const token = jwt.sign({
'iss': 'Codex Team',
'sub': 'auth',
'iat': Date.now()
iss: 'Codex Team',
sub: 'auth',
iat: Date.now(),
}, passHash + config.secret);
res.cookie('authToken', token, { httpOnly: true });

View file

@ -10,11 +10,11 @@ const allowEdit = require('./middlewares/locals');
* Create new page form
*/
router.get('/page/new', verifyToken, allowEdit, async (req, res, next) => {
let pagesAvailable = await Pages.getAll();
const pagesAvailable = await Pages.getAll();
res.render('pages/form', {
pagesAvailable,
page: null
page: null,
});
});
@ -32,7 +32,7 @@ router.get('/page/edit/:id', verifyToken, allowEdit, async (req, res, next) => {
res.render('pages/form', {
page,
parentsChildrenOrdered,
pagesAvailable
pagesAvailable,
});
} catch (error) {
res.status(404);
@ -47,12 +47,13 @@ router.get('/page/:id', verifyToken, async (req, res, next) => {
const pageId = req.params.id;
try {
let page = await Pages.get(pageId);
const page = await Pages.get(pageId);
let pageParent = await page.parent;
const pageParent = await page.parent;
res.render('pages/page', {
page, pageParent
page,
pageParent,
});
} catch (error) {
res.status(404);

View file

@ -1,7 +1,8 @@
/**
* Helper for making async middlewares for express router
*
* @param fn
* @return {function(*=, *=, *=)}
* @returns {function(*=, *=, *=)}
*/
module.exports = function asyncMiddleware(fn) {
return (req, res, next) => {

View file

@ -2,6 +2,7 @@ const crypto = require('crypto');
/**
* Create binary md5
*
* @param stringToHash - string to hash
* @returns {string} - binary hash of argument
*/
@ -13,7 +14,8 @@ function binaryMD5(stringToHash) {
/**
* Returns 16 random bytes in hex format
* @return {Promise<string>}
*
* @returns {Promise<string>}
*/
function random16() {
return new Promise((resolve, reject) => {
@ -29,5 +31,5 @@ function random16() {
module.exports = {
binaryMD5,
random16
random16,
};

View file

@ -1,13 +1,16 @@
/**
* Merge to objects recursively
*
* @param {object} target
* @param {object[]} sources
* @return {object}
* @returns {object}
*/
function deepMerge(target, ...sources) {
const isObject = item => item && typeof item === 'object' && !Array.isArray(item);
if (!sources.length) return target;
if (!sources.length) {
return target;
}
const source = sources.shift();
if (isObject(target) && isObject(source)) {
@ -28,5 +31,5 @@ function deepMerge(target, ...sources) {
}
module.exports = {
deepMerge
deepMerge,
};

View file

@ -4,7 +4,7 @@ const config = require('../../config');
const rcPath = path.resolve(__dirname, '../../', config.rcFile || './.codexdocsrc');
/**
* @typedef {Object} RCData
* @typedef {object} RCData
* @property {string} title - website title
* @property {object[]} menu - options for website menu
* @property {string} menu[].title - menu option title
@ -20,12 +20,12 @@ module.exports = class RCParser {
* Default CodeX Docs configuration
*
* @static
* @return {{title: string, menu: Array}}
* @returns {{title: string, menu: Array}}
*/
static get DEFAULTS() {
return {
title: 'CodeX Docs',
menu: []
menu: [],
};
}
@ -33,7 +33,7 @@ module.exports = class RCParser {
* Find and parse runtime configuration file
*
* @static
* @return {{title: string, menu: []}}
* @returns {{title: string, menu: []}}
*/
static getConfiguration() {
if (!fs.existsSync(rcPath)) {
@ -48,11 +48,12 @@ module.exports = class RCParser {
userConfig = JSON.parse(file);
} catch (e) {
console.log('CodeX Docs rc file should be in JSON format.');
return RCParser.DEFAULTS;
}
for (let option in userConfig) {
if (userConfig.hasOwnProperty(option)) {
for (const option in userConfig) {
if (Object.prototype.hasOwnProperty.call(userConfig, option)) {
rConfig[option] = userConfig[option] || RCParser.DEFAULTS[option] || undefined;
}
}
@ -70,6 +71,7 @@ module.exports = class RCParser {
if (!option || option instanceof Array || typeof option !== 'object') {
console.log(`Menu option #${i} in rc file must be a string or an object`);
return false;
}
@ -77,11 +79,13 @@ module.exports = class RCParser {
if (!title || typeof title !== 'string') {
console.log(`Menu option #${i} title must be a string.`);
return false;
}
if (!uri || typeof uri !== 'string') {
console.log(`Menu option #${i} uri must be a string.`);
return false;
}
@ -93,7 +97,7 @@ module.exports = class RCParser {
return {
title: option,
/* Replace all non alpha- and numeric-symbols with '-' */
uri: '/' + option.toLowerCase().replace(/[ -/:-@[-`{-~]+/, '-')
uri: '/' + option.toLowerCase().replace(/[ -/:-@[-`{-~]+/, '-'),
};
}

View file

@ -1,70 +1,70 @@
const translationTable = {
'а': 'a',
'б': 'b',
'в': 'v',
'г': 'g',
'д': 'd',
'е': 'e',
'ж': 'g',
'з': 'z',
'и': 'i',
'й': 'y',
'к': 'k',
'л': 'l',
'м': 'm',
'н': 'n',
'о': 'o',
'п': 'p',
'р': 'r',
'с': 's',
'т': 't',
'у': 'u',
'ф': 'f',
'ы': 'i',
'э': 'e',
'А': 'A',
'Б': 'B',
'В': 'V',
'Г': 'G',
'Д': 'D',
'Е': 'E',
'Ж': 'G',
'З': 'Z',
'И': 'I',
'Й': 'Y',
'К': 'K',
'Л': 'L',
'М': 'M',
'Н': 'N',
'О': 'O',
'П': 'P',
'Р': 'R',
'С': 'S',
'Т': 'T',
'У': 'U',
'Ф': 'F',
'Ы': 'I',
'Э': 'E',
'ё': 'yo',
'х': 'h',
'ц': 'ts',
'ч': 'ch',
'ш': 'sh',
'щ': 'shch',
'ъ': "''",
'ь': "'",
'ю': 'yu',
'я': 'ya',
'Ё': 'YO',
'Х': 'H',
'Ц': 'TS',
'Ч': 'CH',
'Ш': 'SH',
'Щ': 'SHCH',
'Ъ': "''",
'Ь': "'",
'Ю': 'YU',
'Я': 'YA'
а: 'a',
б: 'b',
в: 'v',
г: 'g',
д: 'd',
е: 'e',
ж: 'g',
з: 'z',
и: 'i',
й: 'y',
к: 'k',
л: 'l',
м: 'm',
н: 'n',
о: 'o',
п: 'p',
р: 'r',
с: 's',
т: 't',
у: 'u',
ф: 'f',
ы: 'i',
э: 'e',
А: 'A',
Б: 'B',
В: 'V',
Г: 'G',
Д: 'D',
Е: 'E',
Ж: 'G',
З: 'Z',
И: 'I',
Й: 'Y',
К: 'K',
Л: 'L',
М: 'M',
Н: 'N',
О: 'O',
П: 'P',
Р: 'R',
С: 'S',
Т: 'T',
У: 'U',
Ф: 'F',
Ы: 'I',
Э: 'E',
ё: 'yo',
х: 'h',
ц: 'ts',
ч: 'ch',
ш: 'sh',
щ: 'shch',
ъ: "''",
ь: "'",
ю: 'yu',
я: 'ya',
Ё: 'YO',
Х: 'H',
Ц: 'TS',
Ч: 'CH',
Ш: 'SH',
Щ: 'SHCH',
Ъ: "''",
Ь: "'",
Ю: 'YU',
Я: 'YA',
};
/**
* Function to translate string

View file

@ -13,7 +13,7 @@ module.exports = (function () {
*
* @example svg('path/from/root/dir')
* @param filename - name of icon
* @returns {String} - svg code
* @returns {string} - svg code
*/
twig.extendFunction('svg', function (filename) {
return fs.readFileSync(`${__dirname}/../frontend/svg/${filename}.svg`, 'utf-8');

View file

@ -0,0 +1,12 @@
<div class="block-checklist">
{% for item in items %}
<div class="block-checklist__item">
{% if item.checked %}
<span class="block-checklist__item-checkbox block-checklist__item-checkbox--checked"></span>
{% else %}
<span class="block-checklist__item-checkbox"></span>
{% endif %}
<div class="block-checklist__item-text">{{ item.text }}</div>
</div>
{% endfor %}
</div>

View file

@ -32,7 +32,7 @@
{% for block in page.body.blocks %}
{# Skip first header, because it is already showed as a Title #}
{% if not (loop.first and block.type == 'header') %}
{% if block.type in ['paragraph', 'header', 'image', 'code', 'list', 'delimiter', 'table', 'warning'] %}
{% if block.type in ['paragraph', 'header', 'image', 'code', 'list', 'delimiter', 'table', 'warning', 'checklist'] %}
{% include './blocks/' ~ block.type ~ '.twig' with block.data %}
{% endif %}
{% endif %}

4300
yarn.lock

File diff suppressed because it is too large Load diff