Editor: restructure word count

* The WordCounter should only do one thing: count words. This makes it also easier to test.
* Add some really basic unit tests.
* Instead of only refreshing the count on enter and delete, refresh the count when the user stops typing. Also look at paste and content changes in TinyMCE.
* Use `match` instead of `replace` when it is appropriate.
* More readable code.

See #30966. Fixes #26620.



git-svn-id: https://develop.svn.wordpress.org/trunk@32856 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Ella Iseulde Van Dorpe 2015-06-19 10:34:59 +00:00
parent bfb8734652
commit fa5ec5077b
6 changed files with 137 additions and 80 deletions

View File

@ -203,7 +203,6 @@ $(document).on( 'heartbeat-send.refresh-lock', function( e, data ) {
jQuery(document).ready( function($) {
var stamp, visibility, $submitButtons, updateVisibility, updateText,
sticky = '',
last = 0,
$textarea = $('#content'),
$document = $(document),
$editSlugWrap = $('#edit-slug-box'),
@ -788,24 +787,6 @@ jQuery(document).ready( function($) {
});
}
// word count
if ( typeof(wpWordCount) != 'undefined' ) {
$document.triggerHandler('wpcountwords', [ $textarea.val() ]);
$textarea.keyup( function(e) {
var k = e.keyCode || e.charCode;
if ( k == last )
return true;
if ( 13 == k || 8 == last || 46 == last )
$document.triggerHandler('wpcountwords', [ $textarea.val() ]);
last = k;
return true;
});
}
wptitlehint = function(id) {
id = id || 'title';
@ -935,3 +916,44 @@ jQuery(document).ready( function($) {
}
});
});
( function( $, counter ) {
$( function() {
var $content = $( '#content' ),
$count = $( '#wp-word-count' ).find( '.word-count' ),
prevCount = 0,
contentEditor;
function update() {
var text, count;
if ( ! contentEditor || contentEditor.isHidden() ) {
text = $content.val();
} else {
text = contentEditor.getContent( { format: 'raw' } );
}
count = counter.count( text );
if ( count !== prevCount ) {
$count.text( count );
}
prevCount = count;
}
$( document ).on( 'tinymce-editor-init', function( event, editor ) {
if ( editor.id !== 'content' ) {
return;
}
contentEditor = editor;
editor.on( 'nodechange keyup', _.debounce( update, 2000 ) );
} );
$content.on( 'input keyup', _.debounce( update, 2000 ) );
update();
} );
} )( jQuery, new wp.utils.WordCounter() );

View File

@ -1,44 +1,48 @@
/* global wordCountL10n */
var wpWordCount;
(function($,undefined) {
wpWordCount = {
( function() {
function WordCounter( settings ) {
var key;
settings : {
strip : /<[a-zA-Z\/][^<>]*>/g, // strip HTML tags
clean : /[0-9.(),;:!?%#$¿'"_+=\\/-]+/g, // regexp to remove punctuation, etc.
w : /\S\s+/g, // word-counting regexp
c : /\S/g // char-counting regexp for asian languages
},
block : 0,
wc : function(tx, type) {
var t = this, w = $('.word-count'), tc = 0;
if ( type === undefined )
type = wordCountL10n.type;
if ( type !== 'w' && type !== 'c' )
type = 'w';
if ( t.block )
return;
t.block = 1;
setTimeout( function() {
if ( tx ) {
tx = tx.replace( t.settings.strip, ' ' ).replace( /&nbsp;|&#160;/gi, ' ' );
tx = tx.replace( t.settings.clean, '' );
tx.replace( t.settings[type], function(){tc++;} );
if ( settings ) {
for ( key in settings ) {
if ( settings.hasOwnProperty( key ) ) {
this.settings[ key ] = settings[ key ];
}
w.html(tc.toString());
setTimeout( function() { t.block = 0; }, 2000 );
}, 1 );
}
}
}
WordCounter.prototype.settings = {
HTMLRegExp: /<\/?[a-z][^>]*?>/gi,
spaceRegExp: /&nbsp;|&#160;/gi,
removeRegExp: /[0-9.(),;:!?%#$¿'"_+=\\\/-]+/g,
wordsRegExp: /\S\s+/g,
charactersRegExp: /\S/g,
l10n: window.wordCountL10n || {}
};
$(document).bind( 'wpcountwords', function(e, txt) {
wpWordCount.wc(txt);
});
}(jQuery));
WordCounter.prototype.count = function( text, type ) {
var count = 0;
type = type || this.settings.l10n.type || 'words';
if ( text ) {
text = ' ' + text + ' ';
text = text.replace( this.settings.HTMLRegExp, ' ' );
text = text.replace( this.settings.spaceRegExp, ' ' );
text = text.replace( this.settings.removeRegExp, '' );
text = text.match( this.settings[ type + 'RegExp' ] );
if ( text ) {
count = text.length;
}
}
return count;
};
window.wp = window.wp || {};
window.wp.utils = window.wp.utils || {};
window.wp.utils.WordCounter = WordCounter;
} )();

View File

@ -7,8 +7,7 @@ tinymce.PluginManager.add( 'wordpress', function( editor ) {
var DOM = tinymce.DOM,
each = tinymce.each,
__ = editor.editorManager.i18n.translate,
wpAdvButton, style,
last = 0;
wpAdvButton, style;
if ( typeof window.jQuery !== 'undefined' ) {
window.jQuery( document ).triggerHandler( 'tinymce-editor-setup', [ editor ] );
@ -363,23 +362,6 @@ tinymce.PluginManager.add( 'wordpress', function( editor ) {
}
});
// Word count
if ( typeof window.jQuery !== 'undefined' ) {
editor.on( 'keyup', function( e ) {
var key = e.keyCode || e.charCode;
if ( key === last ) {
return;
}
if ( 13 === key || 8 === last || 46 === last ) {
window.jQuery( document ).triggerHandler( 'wpcountwords', [ editor.getContent({ format : 'raw' }) ] );
}
last = key;
});
}
editor.on( 'SaveContent', function( e ) {
// If editor is hidden, we just want the textarea's value to be saved
if ( ! editor.inline && editor.isHidden() ) {

View File

@ -371,11 +371,11 @@ function wp_default_scripts( &$scripts ) {
$scripts->add( 'wpdialogs', "/wp-includes/js/wpdialog$suffix.js", array( 'jquery-ui-dialog' ), false, 1 );
$scripts->add( 'word-count', "/wp-admin/js/word-count$suffix.js", array( 'jquery' ), false, 1 );
$scripts->add( 'word-count', "/wp-admin/js/word-count$suffix.js", array(), false, 1 );
did_action( 'init' ) && $scripts->localize( 'word-count', 'wordCountL10n', array(
/* translators: If your word count is based on single characters (East Asian characters),
enter 'characters'. Otherwise, enter 'words'. Do not translate into your own language. */
'type' => 'characters' == _x( 'words', 'word count: words or characters?' ) ? 'c' : 'w',
'type' => _x( 'words', 'word count: words or characters?' )
) );
$scripts->add( 'media-upload', "/wp-admin/js/media-upload$suffix.js", array( 'thickbox', 'shortcode' ), false, 1 );
@ -451,7 +451,7 @@ function wp_default_scripts( &$scripts ) {
'tagDelimiter' => _x( ',', 'tag delimiter' ),
) );
$scripts->add( 'post', "/wp-admin/js/post$suffix.js", array( 'suggest', 'wp-lists', 'postbox', 'tags-box' ), false, 1 );
$scripts->add( 'post', "/wp-admin/js/post$suffix.js", array( 'suggest', 'wp-lists', 'postbox', 'tags-box', 'underscore', 'word-count' ), false, 1 );
did_action( 'init' ) && $scripts->localize( 'post', 'postL10n', array(
'ok' => __('OK'),
'cancel' => __('Cancel'),

View File

@ -32,6 +32,7 @@
<script src="../../src/wp-includes/js/customize-models.js"></script>
<script src="../../src/wp-includes/js/shortcode.js"></script>
<script src="../../src/wp-admin/js/customize-controls.js"></script>
<script src="../../src/wp-admin/js/word-count.js"></script>
<!-- Unit tests -->
<script src="wp-admin/js/password-strength-meter.js"></script>
@ -40,6 +41,7 @@
<script src="wp-includes/js/shortcode.js"></script>
<script src="wp-admin/js/customize-controls.js"></script>
<script src="wp-admin/js/customize-controls-utils.js"></script>
<script src="wp-admin/js/word-count.js"></script>
<!-- Customizer templates for sections -->
<script type="text/html" id="tmpl-customize-section-default">

View File

@ -0,0 +1,47 @@
( function( QUnit ) {
var wordCounter = new window.wp.utils.WordCounter();
QUnit.module( 'word-count' );
QUnit.test( 'All.', function( assert ) {
var tests = [
{
message: 'Basic test.',
string: 'one two three',
wordCount: 3,
charCount: 11
},
{
message: 'HTML tags.',
string: 'one <em class="test">two</em><br />three',
wordCount: 3,
charCount: 11
},
{
message: 'Line breaks.',
string: 'one\ntwo\nthree',
wordCount: 3,
charCount: 11
},
{
message: 'Encoded spaces.',
string: 'one&nbsp;two&#160;three',
wordCount: 3,
charCount: 11
},
{
message: 'Punctuation.',
string: 'It\'s two three... 4?',
wordCount: 3,
charCount: 11
}
];
var i = tests.length;
while ( i-- ) {
assert.equal( wordCounter.count( tests[ i ].string ), tests[ i ].wordCount, tests[ i ].message + ' (words)' );
assert.equal( wordCounter.count( tests[ i ].string, 'characters' ), tests[ i ].charCount, tests[ i ].message + ' (characters)' );
}
} );
} )( window.QUnit );