You've already forked joplin
							
							
				mirror of
				https://github.com/laurent22/joplin.git
				synced 2025-10-31 00:07:48 +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:
		| @@ -41,6 +41,7 @@ | ||||
|     "base64-stream": "^1.0.0", | ||||
|     "clean-html": "^1.5.0", | ||||
|     "compare-version": "^0.1.2", | ||||
|     "countable": "^3.0.1", | ||||
|     "diacritics": "^1.3.0", | ||||
|     "diff-match-patch": "^1.0.4", | ||||
|     "es6-promise-pool": "^2.5.0", | ||||
| @@ -90,6 +91,7 @@ | ||||
|     "read-chunk": "^2.1.0", | ||||
|     "redux": "^3.7.2", | ||||
|     "relative": "^3.0.2", | ||||
|     "remove-markdown": "^0.3.0", | ||||
|     "request": "^2.88.0", | ||||
|     "sax": "^1.2.4", | ||||
|     "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 { themeStyle } = require('../theme.js'); | ||||
| const DialogButtonRow = require('./DialogButtonRow.min'); | ||||
| const Countable = require('countable'); | ||||
| const { stripMarkdown } = require('lib/markdownUtils'); | ||||
| const { countElements } = require('lib/string-utils'); | ||||
|  | ||||
| interface NoteContentPropertiesDialogProps { | ||||
| 	theme: number, | ||||
| @@ -21,19 +22,25 @@ interface KeyToLabelMap { | ||||
|  | ||||
| export default function NoteContentPropertiesDialog(props:NoteContentPropertiesDialogProps) { | ||||
| 	const theme = themeStyle(props.theme); | ||||
| 	const textComps: JSX.Element[] = []; | ||||
| 	const tableBodyComps: JSX.Element[] = []; | ||||
| 	// For the source Markdown | ||||
| 	const [lines, setLines] = useState<number>(0); | ||||
| 	const [words, setWords] = useState<number>(0); | ||||
| 	const [characters, setCharacters] = 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(() => { | ||||
| 		Countable.count(props.text, (counter: { words: number; all: number; characters: number; }) => { | ||||
| 			setWords(counter.words); | ||||
| 			setCharacters(counter.all); | ||||
| 			setCharactersNoSpace(counter.characters); | ||||
| 		}); | ||||
| 		props.text === '' ? setLines(0) : setLines(props.text.split('\n').length); | ||||
| 		countElements(props.text, setWords, setCharacters, setCharactersNoSpace, setLines); | ||||
| 	}, [props.text]); | ||||
|  | ||||
| 	useEffect(() => { | ||||
| 		const strippedText: string = stripMarkdown(props.text); | ||||
| 		countElements(strippedText, setStrippedWords, setStrippedCharacters, setStrippedCharactersNoSpace, setStrippedLines); | ||||
| 	}, [props.text]); | ||||
|  | ||||
| 	const textProperties: TextPropertiesMap = { | ||||
| @@ -43,6 +50,13 @@ export default function NoteContentPropertiesDialog(props:NoteContentPropertiesD | ||||
| 		charactersNoSpace: charactersNoSpace, | ||||
| 	}; | ||||
|  | ||||
| 	const strippedTextProperties: TextPropertiesMap = { | ||||
| 		lines: strippedLines, | ||||
| 		words: strippedWords, | ||||
| 		characters: strippedCharacters, | ||||
| 		charactersNoSpace: strippedCharactersNoSpace, | ||||
| 	}; | ||||
|  | ||||
| 	const keyToLabel: KeyToLabelMap = { | ||||
| 		words: _('Words'), | ||||
| 		characters: _('Characters'), | ||||
| @@ -54,28 +68,62 @@ export default function NoteContentPropertiesDialog(props:NoteContentPropertiesD | ||||
| 		props.onClose(); | ||||
| 	}; | ||||
|  | ||||
| 	const createItemField = (key: string, value: number) => { | ||||
| 		const labelComp = <label style={Object.assign({}, theme.textStyle, theme.controlBoxLabel)}>{keyToLabel[key]}</label>; | ||||
| 		const controlComp = <div style={Object.assign({}, theme.textStyle, theme.controlBoxValue)}>{value}</div>; | ||||
| 	const labelCompStyle = { | ||||
| 		...theme.textStyle, | ||||
| 		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 ( | ||||
| 			<div key={key} style={theme.controlBox} className="note-text-property-box">{labelComp}{controlComp}</div> | ||||
| 			<tr key={key}>{labelComp}{controlComp}{strippedControlComp}</tr> | ||||
| 		); | ||||
| 	}; | ||||
|  | ||||
| 	if (textProperties) { | ||||
| 		for (const key in textProperties) { | ||||
| 			if (!textProperties.hasOwnProperty(key)) continue; | ||||
| 			const comp = createItemField(key, textProperties[key]); | ||||
| 			textComps.push(comp); | ||||
| 		} | ||||
| 	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) { | ||||
| 		const comp = createTableBodyRow(key, textProperties[key], strippedTextProperties[key]); | ||||
| 		tableBodyComps.push(comp); | ||||
| 	} | ||||
|  | ||||
| 	const dialogBoxHeadingStyle = { | ||||
| 		...theme.dialogTitle, | ||||
| 		textAlign: 'center', | ||||
| 	}; | ||||
|  | ||||
| 	return ( | ||||
| 		<div style={theme.dialogModalLayer}> | ||||
| 			<div style={theme.dialogBox}> | ||||
| 				<div style={theme.dialogTitle}>{_('Statistics')}</div> | ||||
| 				<div>{textComps}</div> | ||||
| 				<div style={dialogBoxHeadingStyle}>{_('Content properties')}</div> | ||||
| 				<table> | ||||
| 					<thead> | ||||
| 						{tableHeader} | ||||
| 					</thead> | ||||
| 					<tbody> | ||||
| 						{tableBodyComps} | ||||
| 					</tbody> | ||||
| 				</table> | ||||
| 				<DialogButtonRow theme={props.theme} onClick={buttonRow_click} okButtonShow={false} cancelButtonLabel={_('Close')}/> | ||||
| 			</div> | ||||
| 		</div> | ||||
|   | ||||
| @@ -164,6 +164,7 @@ | ||||
|     "readability-node": "^0.1.0", | ||||
|     "redux": "^3.7.2", | ||||
|     "relative": "^3.0.2", | ||||
|     "remove-markdown": "^0.3.0", | ||||
|     "reselect": "^4.0.0", | ||||
|     "sax": "^1.2.4", | ||||
|     "server-destroy": "^1.0.1", | ||||
|   | ||||
| @@ -2,6 +2,7 @@ const stringPadding = require('string-padding'); | ||||
| const urlUtils = require('lib/urlUtils'); | ||||
| const MarkdownIt = require('markdown-it'); | ||||
| const { setupLinkify } = require('lib/joplin-renderer'); | ||||
| const removeMarkdown = require('remove-markdown'); | ||||
|  | ||||
| const markdownUtils = { | ||||
| 	// Not really escaping because that's not supported by marked.js | ||||
| @@ -101,6 +102,11 @@ const markdownUtils = { | ||||
| 		const title = lines[0].trim(); | ||||
| 		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; | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| const stringUtilsCommon = require('./string-utils-common.js'); | ||||
| const Countable = require('countable'); | ||||
|  | ||||
| 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 }, | ||||
| @@ -285,4 +286,13 @@ function scriptType(s) { | ||||
| 	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); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user