-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #300 from Automattic/add/editor/word-count
Editor: Add word count
- Loading branch information
Showing
7 changed files
with
207 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
NODE_BIN := $(shell npm bin) | ||
MOCHA ?= $(NODE_BIN)/mocha | ||
BASE_DIR := $(NODE_BIN)/../.. | ||
NODE_PATH := test:$(BASE_DIR)/client:$(BASE_DIR)/shared | ||
COMPILERS ?= js:babel/register | ||
REPORTER ?= spec | ||
UI ?= bdd | ||
|
||
test: | ||
@NODE_ENV=test NODE_PATH=$(NODE_PATH) $(MOCHA) --compilers $(COMPILERS) --reporter $(REPORTER) --ui $(UI) | ||
|
||
.PHONY: test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
function countWords( content ) { | ||
// Adapted from TinyMCE wordcount plugin: | ||
// https://github.com/tinymce/tinymce/blob/4.2.6/js/tinymce/plugins/wordcount/plugin.js | ||
|
||
if ( content && typeof content === 'string' ) { | ||
// convert ellipses to spaces, remove HTML tags, and remove space chars | ||
content = content.replace( /\.\.\./g, ' ' ); | ||
content = content.replace( /<.[^<>]*?>/g, ' ' ); | ||
content = content.replace( / | /gi, ' ' ); | ||
|
||
// deal with HTML entities | ||
content = content.replace( /(\w+)(&#?[a-z0-9]+;)+(\w+)/i, '$1$3' ); // strip entities inside words | ||
content = content.replace( /&.+?;/g, ' ' ); // turn all other entities into spaces | ||
|
||
// remove numbers and punctuation | ||
content = content.replace( /[0-9.(),;:!?%#$?\x27\x22_+=\\\/\-]*/g, '' ); | ||
|
||
const words = content.match( /[\w\u2019\x27\-\u00C0-\u1FFF]+/g ); | ||
if ( words ) { | ||
return words.length; | ||
} | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
export default { countWords }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { expect } from 'chai'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import textUtils from '../'; | ||
|
||
// Adapted from TinyMCE word count tests: | ||
// https://github.com/tinymce/tinymce/blob/4.2.6/tests/plugins/wordcount.js | ||
|
||
describe( 'textUtils', () => { | ||
describe( 'wordCount', () => { | ||
it( 'should return 0 for blank content', () => { | ||
expect( textUtils.countWords( | ||
'' | ||
) ).to.equal( 0 ); | ||
} ); | ||
|
||
it( 'should strip HTML tags and count words for a simple sentence', () => { | ||
expect( textUtils.countWords( | ||
'<p>My sentence is this.</p>' | ||
) ).to.equal( 4 ); | ||
} ); | ||
|
||
it( 'should not count dashes', () => { | ||
expect( textUtils.countWords( | ||
'<p>Something -- ok</p>' | ||
) ).to.equal( 2 ); | ||
} ); | ||
|
||
it( 'should not count asterisks or other non-word characters', () => { | ||
expect( textUtils.countWords( | ||
'<p>* something\n\u00b7 something else</p>' | ||
) ).to.equal( 3 ); | ||
} ); | ||
|
||
it( 'should not count numbers', () => { | ||
expect( textUtils.countWords( | ||
'<p>Something 123 ok</p>' | ||
) ).to.equal( 2 ); | ||
} ); | ||
|
||
it( 'should not count HTML entities', () => { | ||
expect( textUtils.countWords( | ||
'<p>It’s my life – – – don\'t you forget.</p>' | ||
) ).to.equal( 6 ); | ||
} ); | ||
|
||
it( 'should count hyphenated words as one word', () => { | ||
expect( textUtils.countWords( | ||
'<p>Hello some-word here.</p>' | ||
) ).to.equal( 3 ); | ||
} ); | ||
|
||
it( 'should count words between blocks as two words', () => { | ||
expect( textUtils.countWords( | ||
'<p>Hello</p><p>world</p>' | ||
) ).to.equal( 2 ); | ||
} ); | ||
} ); | ||
} ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import React from 'react/addons'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import PostEditStore from 'lib/posts/post-edit-store'; | ||
import userModule from 'lib/user'; | ||
import Count from 'components/count'; | ||
import textUtils from 'lib/text-utils'; | ||
|
||
/** | ||
* Module variables | ||
*/ | ||
const user = userModule(); | ||
|
||
export default React.createClass( { | ||
displayName: 'EditorWordCount', | ||
|
||
mixins: [ React.addons.PureRenderMixin ], | ||
|
||
getInitialState() { | ||
return { | ||
rawContent: '' | ||
}; | ||
}, | ||
|
||
componentWillMount() { | ||
PostEditStore.on( 'rawContentChange', this.onRawContentChange ); | ||
}, | ||
|
||
componentDidMount() { | ||
this.onRawContentChange(); | ||
}, | ||
|
||
componentWillUnmount() { | ||
PostEditStore.removeListener( 'rawContentChange', this.onRawContentChange ); | ||
}, | ||
|
||
onRawContentChange() { | ||
this.setState( { | ||
rawContent: PostEditStore.getRawContent() | ||
} ); | ||
}, | ||
|
||
render() { | ||
const currentUser = user.get(); | ||
const localeSlug = currentUser && currentUser.localeSlug || 'en'; | ||
|
||
switch ( localeSlug ) { | ||
case 'ja': | ||
case 'th': | ||
case 'zh-cn': | ||
case 'zh-hk': | ||
case 'zh-sg': | ||
case 'zh-tw': | ||
// TODO these are character-based languages - count characters instead | ||
return null; | ||
|
||
case 'ko': | ||
// TODO Korean is not supported by our current word count regex | ||
return null; | ||
} | ||
|
||
return ( | ||
<div className="editor-word-count"> | ||
{ this.translate( 'Word Count' ) } | ||
<Count count={ this.getCount() } /> | ||
</div> | ||
); | ||
}, | ||
|
||
getCount() { | ||
return textUtils.countWords( this.state.rawContent ); | ||
} | ||
} ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters