You've already forked joplin
							
							
				mirror of
				https://github.com/laurent22/joplin.git
				synced 2025-10-31 00:07:48 +02:00 
			
		
		
		
	Desktop: Resolves #8083: Improved word count when em-dash is used
This commit is contained in:
		| @@ -75,6 +75,7 @@ packages/pdf-viewer/dist | ||||
| plugin_types/ | ||||
| readme/ | ||||
| packages/react-native-vosk/lib/ | ||||
| packages/lib/countable/Countable.js | ||||
|  | ||||
| # AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD | ||||
| packages/app-cli/app/LinkSelector.js | ||||
|   | ||||
| @@ -3,7 +3,7 @@ import { useState, useEffect } from 'react'; | ||||
| import { _ } from '@joplin/lib/locale'; | ||||
| import DialogButtonRow from './DialogButtonRow'; | ||||
| const { themeStyle } = require('@joplin/lib/theme'); | ||||
| const Countable = require('countable'); | ||||
| const Countable = require('@joplin/lib/countable/Countable'); | ||||
| import markupLanguageUtils from '../utils/markupLanguageUtils'; | ||||
|  | ||||
| interface NoteContentPropertiesDialogProps { | ||||
|   | ||||
							
								
								
									
										320
									
								
								packages/lib/countable/Countable.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										320
									
								
								packages/lib/countable/Countable.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,320 @@ | ||||
| // ======================================================================== | ||||
| // Imported from dead package https://github.com/RadLikeWhoa/Countable | ||||
| // | ||||
| // 2023-06-05: Added "—" as word separator. | ||||
| // ======================================================================== | ||||
|  | ||||
| /** | ||||
|  * Countable is a script to allow for live paragraph-, word- and character- | ||||
|  * counting on an HTML element. | ||||
|  * | ||||
|  * @author   Sacha Schmid (<https://github.com/RadLikeWhoa>) | ||||
|  * @version  3.0.1 | ||||
|  * @license  MIT | ||||
|  * @see      <http://radlikewhoa.github.io/Countable/> | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Note: For the purpose of this internal documentation, arguments of the type | ||||
|  * {Nodes} are to be interpreted as either {NodeList} or {Element}. | ||||
|  */ | ||||
|  | ||||
| ;(function (global) { | ||||
| 	'use strict' | ||||
|    | ||||
| 	/** | ||||
| 	 * @private | ||||
| 	 * | ||||
| 	 * `liveElements` holds all elements that have the live-counting | ||||
| 	 * functionality bound to them. | ||||
| 	 */ | ||||
|    | ||||
| 	let liveElements = [] | ||||
| 	const each = Array.prototype.forEach | ||||
|    | ||||
| 	/** | ||||
| 	 * `ucs2decode` function from the punycode.js library. | ||||
| 	 * | ||||
| 	 * Creates an array containing the decimal code points of each Unicode | ||||
| 	 * character in the string. While JavaScript uses UCS-2 internally, this | ||||
| 	 * function will convert a pair of surrogate halves (each of which UCS-2 | ||||
| 	 * exposes as separate characters) into a single code point, matching | ||||
| 	 * UTF-16. | ||||
| 	 * | ||||
| 	 * @see     <http://goo.gl/8M09r> | ||||
| 	 * @see     <http://goo.gl/u4UUC> | ||||
| 	 * | ||||
| 	 * @param   {String}  string   The Unicode input string (UCS-2). | ||||
| 	 * | ||||
| 	 * @return  {Array}   The new array of code points. | ||||
| 	 */ | ||||
|    | ||||
| 	function decode (string) { | ||||
| 	  const output = [] | ||||
| 		let counter = 0 | ||||
| 		const length = string.length | ||||
|    | ||||
| 		while (counter < length) { | ||||
| 			const value = string.charCodeAt(counter++) | ||||
|    | ||||
| 			if (value >= 0xD800 && value <= 0xDBFF && counter < length) { | ||||
|    | ||||
| 				// It's a high surrogate, and there is a next character. | ||||
|    | ||||
| 				const extra = string.charCodeAt(counter++) | ||||
|    | ||||
| 				if ((extra & 0xFC00) == 0xDC00) { // Low surrogate. | ||||
| 					output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000) | ||||
| 				} else { | ||||
|    | ||||
| 					// It's an unmatched surrogate; only append this code unit, in case the | ||||
| 					// next code unit is the high surrogate of a surrogate pair. | ||||
|    | ||||
| 					output.push(value) | ||||
| 					counter-- | ||||
| 				} | ||||
| 			} else { | ||||
| 				output.push(value) | ||||
| 			} | ||||
| 		} | ||||
|    | ||||
| 		return output | ||||
| 	} | ||||
|    | ||||
| 	/** | ||||
| 	 * `validateArguments` validates the arguments given to each function call. | ||||
| 	 * Errors are logged to the console as warnings, but Countable fails | ||||
| 	 * silently. | ||||
| 	 * | ||||
| 	 * @private | ||||
| 	 * | ||||
| 	 * @param   {Nodes|String}  targets   A (collection of) element(s) or a single | ||||
| 	   *                                    string to validate. | ||||
| 	 * | ||||
| 	 * @param   {Function}      callback  The callback function to validate. | ||||
| 	 * | ||||
| 	 * @return  {Boolean}       Returns whether all arguments are valid. | ||||
| 	 */ | ||||
|    | ||||
| 	function validateArguments (targets, callback) { | ||||
| 	  const nodes = Object.prototype.toString.call(targets) | ||||
| 	  const targetsValid = typeof targets === 'string' || ((nodes === '[object NodeList]' || nodes === '[object HTMLCollection]') || targets.nodeType === 1) | ||||
| 	  const callbackValid = typeof callback === 'function' | ||||
|    | ||||
| 	  if (!targetsValid) console.error('Countable: Not a valid target') | ||||
| 	  if (!callbackValid) console.error('Countable: Not a valid callback function') | ||||
|    | ||||
| 	  return targetsValid && callbackValid | ||||
| 	} | ||||
|    | ||||
| 	/** | ||||
| 	 * `count` trims an element's value, optionally strips HTML tags and counts | ||||
| 	 * paragraphs, sentences, words, characters and characters plus spaces. | ||||
| 	 * | ||||
| 	 * @private | ||||
| 	 * | ||||
| 	 * @param   {Node|String}  target   The target for the count. | ||||
| 	 * | ||||
| 	 * @param   {Object}   	   options  The options to use for the counting. | ||||
| 	 * | ||||
| 	 * @return  {Object}       The object containing the number of paragraphs, | ||||
| 	 *                         sentences, words, characters and characters | ||||
| 	   *                         plus spaces. | ||||
| 	 */ | ||||
|    | ||||
| 	function count (target, options) { | ||||
| 	  let original = '' + (typeof target === 'string' ? target : ('value' in target ? target.value : target.textContent)) | ||||
| 	  options = options || {} | ||||
|    | ||||
| 	  /** | ||||
| 	   * The initial implementation to allow for HTML tags stripping was created | ||||
| 	   * @craniumslows while the current one was created by @Rob--W. | ||||
| 	   * | ||||
| 	   * @see <http://goo.gl/Exmlr> | ||||
| 	   * @see <http://goo.gl/gFQQh> | ||||
| 	   */ | ||||
|    | ||||
| 	  if (options.stripTags) original = original.replace(/<\/?[a-z][^>]*>/gi, '') | ||||
|    | ||||
| 	  if (options.ignore) { | ||||
| 		  each.call(options.ignore, function (i) { | ||||
| 			  original = original.replace(i, '') | ||||
| 		  }) | ||||
| 	  } | ||||
|    | ||||
| 	  const trimmed = original.trim() | ||||
|    | ||||
| 	  /** | ||||
| 	   * Most of the performance improvements are based on the works of @epmatsw. | ||||
| 	   * | ||||
| 	   * @see <http://goo.gl/SWOLB> | ||||
| 	   */ | ||||
|    | ||||
| 	  return { | ||||
| 		paragraphs: trimmed ? (trimmed.match(options.hardReturns ? /\n{2,}/g : /\n+/g) || []).length + 1 : 0, | ||||
| 		sentences: trimmed ? (trimmed.match(/[.?!…]+./g) || []).length + 1 : 0, | ||||
| 		words: trimmed ? (trimmed.replace(/['";:,.?¿\-!¡]+/g, '').match(/\S+/g) || []).length : 0, | ||||
| 		characters: trimmed ? decode(trimmed.replace(/\s/g, '')).length : 0, | ||||
| 		all: decode(original).length | ||||
| 	  } | ||||
| 	} | ||||
|    | ||||
| 	/** | ||||
| 	 * This is the main object that will later be exposed to other scripts. It | ||||
| 	 * holds all the public methods that can be used to enable the Countable | ||||
| 	 * functionality. | ||||
| 	 * | ||||
| 	 * Some methods accept an optional options parameter. This includes the | ||||
| 	 * following options. | ||||
| 	 * | ||||
| 	 * {Boolean}      hardReturns  Use two returns to seperate a paragraph | ||||
| 	 *                             instead of one. (default: false) | ||||
| 	 * {Boolean}      stripTags    Strip HTML tags before counting the values. | ||||
| 	 *                             (default: false) | ||||
| 	 * {Array<Char>}  ignore       A list of characters that should be removed | ||||
| 	 *                             ignored when calculating the counters. | ||||
| 	 *                             (default: ) | ||||
| 	 */ | ||||
|    | ||||
| 	const Countable = { | ||||
|    | ||||
| 	  /** | ||||
| 	   * The `on` method binds the counting handler to all given elements. The | ||||
| 	   * event is either `oninput` or `onkeydown`, based on the capabilities of | ||||
| 	   * the browser. | ||||
| 	   * | ||||
| 	   * @param   {Nodes}     elements   All elements that should receive the | ||||
| 	   *                                 Countable functionality. | ||||
| 	   * | ||||
| 	   * @param   {Function}  callback   The callback to fire whenever the | ||||
| 	   *                                 element's value changes. The callback is | ||||
| 	   *                                 called with the relevant element bound | ||||
| 	   *                                 to `this` and the counted values as the | ||||
| 	   *                                 single parameter. | ||||
| 	   * | ||||
| 	   * @param   {Object}    [options]  An object to modify Countable's | ||||
| 	   *                                 behaviour. | ||||
| 	   * | ||||
| 	   * @return  {Object}    Returns the Countable object to allow for chaining. | ||||
| 	   */ | ||||
|    | ||||
| 	  on: function (elements, callback, options) { | ||||
| 		if (!validateArguments(elements, callback)) return | ||||
|    | ||||
| 		if (!Array.isArray(elements)) { | ||||
| 			elements = [ elements ] | ||||
| 		} | ||||
|    | ||||
| 		each.call(elements, function (e) { | ||||
| 			const handler = function () { | ||||
| 			  callback.call(e, count(e, options)) | ||||
| 			} | ||||
|    | ||||
| 			liveElements.push({ element: e, handler: handler }) | ||||
|    | ||||
| 			handler() | ||||
|    | ||||
| 			e.addEventListener('input', handler) | ||||
| 		}) | ||||
|    | ||||
| 		return this | ||||
| 	  }, | ||||
|    | ||||
| 	  /** | ||||
| 	   * The `off` method removes the Countable functionality from all given | ||||
| 	   * elements. | ||||
| 	   * | ||||
| 	   * @param   {Nodes}   elements  All elements whose Countable functionality | ||||
| 	   *                              should be unbound. | ||||
| 	   * | ||||
| 	   * @return  {Object}  Returns the Countable object to allow for chaining. | ||||
| 	   */ | ||||
|    | ||||
| 	  off: function (elements) { | ||||
| 		if (!validateArguments(elements, function () {})) return | ||||
|    | ||||
| 		if (!Array.isArray(elements)) { | ||||
| 			elements = [ elements ] | ||||
| 		} | ||||
|    | ||||
| 		liveElements.filter(function (e) { | ||||
| 			return elements.indexOf(e.element) !== -1 | ||||
| 		}).forEach(function (e) { | ||||
| 			e.element.removeEventListener('input', e.handler) | ||||
| 		}) | ||||
|    | ||||
| 		liveElements = liveElements.filter(function (e) { | ||||
| 			return elements.indexOf(e.element) === -1 | ||||
| 		}) | ||||
|    | ||||
| 		return this | ||||
| 	  }, | ||||
|    | ||||
| 	  /** | ||||
| 	   * The `count` method works mostly like the `live` method, but no events are | ||||
| 	   * bound, the functionality is only executed once. | ||||
| 	   * | ||||
| 	   * @param   {Nodes|String}  targets   All elements that should be counted. | ||||
| 	   * | ||||
| 	   * @param   {Function}      callback   The callback to fire whenever the | ||||
| 	   *                                     element's value changes. The callback | ||||
| 		   *                                     is called with the relevant element | ||||
| 		   *                                     bound to `this` and the counted values | ||||
| 		   *                                     as the single parameter. | ||||
| 	   * | ||||
| 	   * @param   {Object}        [options]  An object to modify Countable's | ||||
| 	   *                                     behaviour. | ||||
| 	   * | ||||
| 	   * @return  {Object}    Returns the Countable object to allow for chaining. | ||||
| 	   */ | ||||
|    | ||||
| 	  count: function (targets, callback, options) { | ||||
| 		if (!validateArguments(targets, callback)) return | ||||
|    | ||||
| 		if (!Array.isArray(targets)) { | ||||
| 			targets = [ targets ] | ||||
| 		} | ||||
|    | ||||
| 		each.call(targets, function (e) { | ||||
| 			callback.call(e, count(e, options)) | ||||
| 		}) | ||||
|    | ||||
| 		return this | ||||
| 	  }, | ||||
|    | ||||
| 	  /** | ||||
| 	   * The `enabled` method checks if the live-counting functionality is bound | ||||
| 	   * to an element. | ||||
| 	   * | ||||
| 	   * @param   {Node}     element  All elements that should be checked for the | ||||
| 	   *                              Countable functionality. | ||||
| 	   * | ||||
| 	   * @return  {Boolean}  A boolean value representing whether Countable | ||||
| 	   *                     functionality is bound to all given elements. | ||||
| 	   */ | ||||
|    | ||||
| 	  enabled: function (elements) { | ||||
| 		if (elements.length === undefined) { | ||||
| 		  elements = [ elements ] | ||||
| 		} | ||||
|    | ||||
| 		return liveElements.filter(function (e) { | ||||
| 			return elements.indexOf(e.element) !== -1 | ||||
| 		}).length === elements.length | ||||
| 	  } | ||||
|    | ||||
| 	} | ||||
|    | ||||
| 	/** | ||||
| 	 * Expose Countable depending on the module system used across the | ||||
| 	 * application. (Node / CommonJS, AMD, global) | ||||
| 	 */ | ||||
|    | ||||
| 	if (typeof exports === 'object') { | ||||
| 	  module.exports = Countable | ||||
| 	} else if (typeof define === 'function' && define.amd) { | ||||
| 	  define(function () { return Countable }) | ||||
| 	} else { | ||||
| 	  global.Countable = Countable | ||||
| 	} | ||||
|   }(this)); | ||||
		Reference in New Issue
	
	Block a user