mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-24 10:27:10 +02:00
* Updated commit * Update package.json * Update package.json Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
This commit is contained in:
parent
7d767cf0c9
commit
a09c7b72c7
@ -41,6 +41,7 @@
|
|||||||
"base64-stream": "^1.0.0",
|
"base64-stream": "^1.0.0",
|
||||||
"clean-html": "^1.5.0",
|
"clean-html": "^1.5.0",
|
||||||
"compare-version": "^0.1.2",
|
"compare-version": "^0.1.2",
|
||||||
|
"countable": "^3.0.1",
|
||||||
"diacritics": "^1.3.0",
|
"diacritics": "^1.3.0",
|
||||||
"diff-match-patch": "^1.0.4",
|
"diff-match-patch": "^1.0.4",
|
||||||
"es6-promise-pool": "^2.5.0",
|
"es6-promise-pool": "^2.5.0",
|
||||||
@ -90,6 +91,7 @@
|
|||||||
"read-chunk": "^2.1.0",
|
"read-chunk": "^2.1.0",
|
||||||
"redux": "^3.7.2",
|
"redux": "^3.7.2",
|
||||||
"relative": "^3.0.2",
|
"relative": "^3.0.2",
|
||||||
|
"remove-markdown": "^0.3.0",
|
||||||
"request": "^2.88.0",
|
"request": "^2.88.0",
|
||||||
"sax": "^1.2.4",
|
"sax": "^1.2.4",
|
||||||
"server-destroy": "^1.0.1",
|
"server-destroy": "^1.0.1",
|
||||||
|
@ -85,4 +85,41 @@ describe('markdownUtils', function() {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should remove Markdown syntax elements from the text', asyncTest(async () => {
|
||||||
|
const inputStrings = [
|
||||||
|
'', // Empty string
|
||||||
|
'This is some plain text', // Plain text
|
||||||
|
'## This is a header', // Header syntax
|
||||||
|
'This is a text with **bold** and *italicized* text', // Text with annotations
|
||||||
|
'This is a text with __bold__ and _italicized_ text', // Text with annotations alternate form
|
||||||
|
'[link to google](https://www.google.com/)', // Link
|
||||||
|
'> This is a blockquote\n And another line', // Blockquote
|
||||||
|
'* List item\n* List item', // Unordered list
|
||||||
|
'- List item\n- List item', // Unordered list
|
||||||
|
'1. List item\n2. List item', // Ordered list
|
||||||
|
'This is some `inline code`', // Inlined code
|
||||||
|
];
|
||||||
|
|
||||||
|
const expectedOutputStrings = [
|
||||||
|
'',
|
||||||
|
'This is some plain text',
|
||||||
|
'This is a header',
|
||||||
|
'This is a text with bold and italicized text',
|
||||||
|
'This is a text with bold and italicized text',
|
||||||
|
'link to google',
|
||||||
|
'This is a blockquote\n And another line',
|
||||||
|
'List item\nList item',
|
||||||
|
'List item\nList item',
|
||||||
|
'List item\nList item',
|
||||||
|
'This is some inline code',
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(inputStrings.length).toBe(expectedOutputStrings.length);
|
||||||
|
|
||||||
|
for (let i = 0; i < inputStrings.length; i++) {
|
||||||
|
const outputString = markdownUtils.stripMarkdown(inputStrings[i]);
|
||||||
|
expect(outputString).toBe(expectedOutputStrings[i]);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -3,7 +3,8 @@ import { useState, useEffect } from 'react';
|
|||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const { themeStyle } = require('../theme.js');
|
const { themeStyle } = require('../theme.js');
|
||||||
const DialogButtonRow = require('./DialogButtonRow.min');
|
const DialogButtonRow = require('./DialogButtonRow.min');
|
||||||
const Countable = require('countable');
|
const { stripMarkdown } = require('lib/markdownUtils');
|
||||||
|
const { countElements } = require('lib/string-utils');
|
||||||
|
|
||||||
interface NoteContentPropertiesDialogProps {
|
interface NoteContentPropertiesDialogProps {
|
||||||
theme: number,
|
theme: number,
|
||||||
@ -21,19 +22,25 @@ interface KeyToLabelMap {
|
|||||||
|
|
||||||
export default function NoteContentPropertiesDialog(props:NoteContentPropertiesDialogProps) {
|
export default function NoteContentPropertiesDialog(props:NoteContentPropertiesDialogProps) {
|
||||||
const theme = themeStyle(props.theme);
|
const theme = themeStyle(props.theme);
|
||||||
const textComps: JSX.Element[] = [];
|
const tableBodyComps: JSX.Element[] = [];
|
||||||
|
// For the source Markdown
|
||||||
const [lines, setLines] = useState<number>(0);
|
const [lines, setLines] = useState<number>(0);
|
||||||
const [words, setWords] = useState<number>(0);
|
const [words, setWords] = useState<number>(0);
|
||||||
const [characters, setCharacters] = useState<number>(0);
|
const [characters, setCharacters] = useState<number>(0);
|
||||||
const [charactersNoSpace, setCharactersNoSpace] = useState<number>(0);
|
const [charactersNoSpace, setCharactersNoSpace] = useState<number>(0);
|
||||||
|
// For source with Markdown syntax stripped out
|
||||||
|
const [strippedLines, setStrippedLines] = useState<number>(0);
|
||||||
|
const [strippedWords, setStrippedWords] = useState<number>(0);
|
||||||
|
const [strippedCharacters, setStrippedCharacters] = useState<number>(0);
|
||||||
|
const [strippedCharactersNoSpace, setStrippedCharactersNoSpace] = useState<number>(0);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
Countable.count(props.text, (counter: { words: number; all: number; characters: number; }) => {
|
countElements(props.text, setWords, setCharacters, setCharactersNoSpace, setLines);
|
||||||
setWords(counter.words);
|
}, [props.text]);
|
||||||
setCharacters(counter.all);
|
|
||||||
setCharactersNoSpace(counter.characters);
|
useEffect(() => {
|
||||||
});
|
const strippedText: string = stripMarkdown(props.text);
|
||||||
props.text === '' ? setLines(0) : setLines(props.text.split('\n').length);
|
countElements(strippedText, setStrippedWords, setStrippedCharacters, setStrippedCharactersNoSpace, setStrippedLines);
|
||||||
}, [props.text]);
|
}, [props.text]);
|
||||||
|
|
||||||
const textProperties: TextPropertiesMap = {
|
const textProperties: TextPropertiesMap = {
|
||||||
@ -43,6 +50,13 @@ export default function NoteContentPropertiesDialog(props:NoteContentPropertiesD
|
|||||||
charactersNoSpace: charactersNoSpace,
|
charactersNoSpace: charactersNoSpace,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const strippedTextProperties: TextPropertiesMap = {
|
||||||
|
lines: strippedLines,
|
||||||
|
words: strippedWords,
|
||||||
|
characters: strippedCharacters,
|
||||||
|
charactersNoSpace: strippedCharactersNoSpace,
|
||||||
|
};
|
||||||
|
|
||||||
const keyToLabel: KeyToLabelMap = {
|
const keyToLabel: KeyToLabelMap = {
|
||||||
words: _('Words'),
|
words: _('Words'),
|
||||||
characters: _('Characters'),
|
characters: _('Characters'),
|
||||||
@ -54,28 +68,62 @@ export default function NoteContentPropertiesDialog(props:NoteContentPropertiesD
|
|||||||
props.onClose();
|
props.onClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
const createItemField = (key: string, value: number) => {
|
const labelCompStyle = {
|
||||||
const labelComp = <label style={Object.assign({}, theme.textStyle, theme.controlBoxLabel)}>{keyToLabel[key]}</label>;
|
...theme.textStyle,
|
||||||
const controlComp = <div style={Object.assign({}, theme.textStyle, theme.controlBoxValue)}>{value}</div>;
|
fontWeight: 'bold',
|
||||||
|
width: '10em',
|
||||||
|
};
|
||||||
|
|
||||||
|
const controlCompStyle = {
|
||||||
|
...theme.textStyle,
|
||||||
|
textAlign: 'center',
|
||||||
|
};
|
||||||
|
|
||||||
|
const createTableBodyRow = (key: string, value: number, strippedValue: number) => {
|
||||||
|
const labelComp = <td style={labelCompStyle}>{keyToLabel[key]}</td>;
|
||||||
|
const controlComp = <td style={controlCompStyle}>{value}</td>;
|
||||||
|
const strippedControlComp = <td style={controlCompStyle}>{strippedValue}</td>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={key} style={theme.controlBox} className="note-text-property-box">{labelComp}{controlComp}</div>
|
<tr key={key}>{labelComp}{controlComp}{strippedControlComp}</tr>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (textProperties) {
|
const tableHeaderStyle = {
|
||||||
|
...theme.textStyle,
|
||||||
|
textAlign: 'center',
|
||||||
|
};
|
||||||
|
|
||||||
|
const tableHeader = (
|
||||||
|
<tr>
|
||||||
|
<th style={tableHeaderStyle}></th>
|
||||||
|
<th style={tableHeaderStyle}>{_('Editor')}</th>
|
||||||
|
<th style={tableHeaderStyle}>{_('Viewer')}</th>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
|
||||||
for (const key in textProperties) {
|
for (const key in textProperties) {
|
||||||
if (!textProperties.hasOwnProperty(key)) continue;
|
const comp = createTableBodyRow(key, textProperties[key], strippedTextProperties[key]);
|
||||||
const comp = createItemField(key, textProperties[key]);
|
tableBodyComps.push(comp);
|
||||||
textComps.push(comp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const dialogBoxHeadingStyle = {
|
||||||
|
...theme.dialogTitle,
|
||||||
|
textAlign: 'center',
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={theme.dialogModalLayer}>
|
<div style={theme.dialogModalLayer}>
|
||||||
<div style={theme.dialogBox}>
|
<div style={theme.dialogBox}>
|
||||||
<div style={theme.dialogTitle}>{_('Statistics')}</div>
|
<div style={dialogBoxHeadingStyle}>{_('Content properties')}</div>
|
||||||
<div>{textComps}</div>
|
<table>
|
||||||
|
<thead>
|
||||||
|
{tableHeader}
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{tableBodyComps}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
<DialogButtonRow theme={props.theme} onClick={buttonRow_click} okButtonShow={false} cancelButtonLabel={_('Close')}/>
|
<DialogButtonRow theme={props.theme} onClick={buttonRow_click} okButtonShow={false} cancelButtonLabel={_('Close')}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -164,6 +164,7 @@
|
|||||||
"readability-node": "^0.1.0",
|
"readability-node": "^0.1.0",
|
||||||
"redux": "^3.7.2",
|
"redux": "^3.7.2",
|
||||||
"relative": "^3.0.2",
|
"relative": "^3.0.2",
|
||||||
|
"remove-markdown": "^0.3.0",
|
||||||
"reselect": "^4.0.0",
|
"reselect": "^4.0.0",
|
||||||
"sax": "^1.2.4",
|
"sax": "^1.2.4",
|
||||||
"server-destroy": "^1.0.1",
|
"server-destroy": "^1.0.1",
|
||||||
|
@ -2,6 +2,7 @@ const stringPadding = require('string-padding');
|
|||||||
const urlUtils = require('lib/urlUtils');
|
const urlUtils = require('lib/urlUtils');
|
||||||
const MarkdownIt = require('markdown-it');
|
const MarkdownIt = require('markdown-it');
|
||||||
const { setupLinkify } = require('lib/joplin-renderer');
|
const { setupLinkify } = require('lib/joplin-renderer');
|
||||||
|
const removeMarkdown = require('remove-markdown');
|
||||||
|
|
||||||
const markdownUtils = {
|
const markdownUtils = {
|
||||||
// Not really escaping because that's not supported by marked.js
|
// Not really escaping because that's not supported by marked.js
|
||||||
@ -101,6 +102,11 @@ const markdownUtils = {
|
|||||||
const title = lines[0].trim();
|
const title = lines[0].trim();
|
||||||
return title.replace(filterRegex, '').replace(mdLinkRegex, '$1').replace(emptyMdLinkRegex, '$1').substring(0,80);
|
return title.replace(filterRegex, '').replace(mdLinkRegex, '$1').replace(emptyMdLinkRegex, '$1').substring(0,80);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
stripMarkdown(text, options = { gfm: false }) {
|
||||||
|
// Removes Markdown syntax elements from the given text
|
||||||
|
return removeMarkdown(text, options);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = markdownUtils;
|
module.exports = markdownUtils;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
const stringUtilsCommon = require('./string-utils-common.js');
|
const stringUtilsCommon = require('./string-utils-common.js');
|
||||||
|
const Countable = require('countable');
|
||||||
|
|
||||||
const defaultDiacriticsRemovalMap = [
|
const defaultDiacriticsRemovalMap = [
|
||||||
{ base: 'A', letters: /[\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F]/g },
|
{ base: 'A', letters: /[\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F]/g },
|
||||||
@ -285,4 +286,13 @@ function scriptType(s) {
|
|||||||
return 'en';
|
return 'en';
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Object.assign({ removeDiacritics, substrWithEllipsis, nextWhitespaceIndex, escapeFilename, wrap, splitCommandString, padLeft, toTitleCase, urlDecode, escapeHtml, surroundKeywords, scriptType, commandArgumentsToString }, stringUtilsCommon);
|
function countElements(text, wordSetter, characterSetter, characterNoSpaceSetter, lineSetter) {
|
||||||
|
Countable.count(text, counter => {
|
||||||
|
wordSetter(counter.words);
|
||||||
|
characterSetter(counter.all);
|
||||||
|
characterNoSpaceSetter(counter.characters);
|
||||||
|
});
|
||||||
|
text === '' ? lineSetter(0) : lineSetter(text.split('\n').length);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Object.assign({ removeDiacritics, substrWithEllipsis, nextWhitespaceIndex, escapeFilename, wrap, splitCommandString, padLeft, toTitleCase, urlDecode, escapeHtml, surroundKeywords, scriptType, commandArgumentsToString, countElements }, stringUtilsCommon);
|
||||||
|
Loading…
Reference in New Issue
Block a user