diff --git a/ghost/admin/app/helpers/gh-count-characters.js b/ghost/admin/app/helpers/gh-count-characters.js
index 344dc4f307..107d2ea273 100644
--- a/ghost/admin/app/helpers/gh-count-characters.js
+++ b/ghost/admin/app/helpers/gh-count-characters.js
@@ -1,14 +1,17 @@
import {helper} from 'ember-helper';
import {htmlSafe} from 'ember-string';
-export default helper(function (params) {
+export function countCharacters(params) {
if (!params || !params.length) {
return;
}
let el = document.createElement('span');
let content = params[0] || '';
- let {length} = content;
+
+ // convert to array so that we get accurate symbol counts for multibyte chars
+ // this will still count emoji+modifer as two chars
+ let {length} = Array.from(content);
el.className = 'word-count';
@@ -21,4 +24,8 @@ export default helper(function (params) {
el.innerHTML = 200 - length;
return htmlSafe(el.outerHTML);
+}
+
+export default helper(function (params) {
+ return countCharacters(params);
});
diff --git a/ghost/admin/app/helpers/gh-count-down-characters.js b/ghost/admin/app/helpers/gh-count-down-characters.js
index 4f7077846d..3abab9c61a 100644
--- a/ghost/admin/app/helpers/gh-count-down-characters.js
+++ b/ghost/admin/app/helpers/gh-count-down-characters.js
@@ -1,17 +1,17 @@
import {helper} from 'ember-helper';
import {htmlSafe} from 'ember-string';
-export default helper(function (params) {
+export function countDownCharacters(params) {
if (!params || params.length < 2) {
return;
}
let el = document.createElement('span');
let [content, maxCharacters] = params;
- let length;
- content = content || '';
- length = content.length;
+ // convert to array so that we get accurate symbol counts for multibyte chars
+ // this will still count emoji+modifer as two chars
+ let {length} = Array.from(content || '');
el.className = 'word-count';
@@ -24,4 +24,8 @@ export default helper(function (params) {
el.innerHTML = length;
return htmlSafe(el.outerHTML);
+}
+
+export default helper(function (params) {
+ return countDownCharacters(params);
});
diff --git a/ghost/admin/tests/unit/helpers/gh-count-characters-test.js b/ghost/admin/tests/unit/helpers/gh-count-characters-test.js
new file mode 100644
index 0000000000..fabde5f636
--- /dev/null
+++ b/ghost/admin/tests/unit/helpers/gh-count-characters-test.js
@@ -0,0 +1,37 @@
+import {expect} from 'chai';
+import {describe, it} from 'mocha';
+import {countCharacters} from 'ghost-admin/helpers/gh-count-characters';
+
+describe('Unit: Helper: gh-count-characters', function() {
+ let defaultStyle = 'color: rgb(158, 157, 149);';
+ let errorStyle = 'color: rgb(226, 84, 64);';
+
+ it('counts remaining chars', function() {
+ let result = countCharacters(['test']);
+ expect(result.string)
+ .to.equal(`196`);
+ });
+
+ it('warns when nearing limit', function () {
+ let result = countCharacters([Array(195 + 1).join('x')]);
+ expect(result.string)
+ .to.equal(`5`);
+ });
+
+ it('indicates too many chars', function () {
+ let result = countCharacters([Array(205 + 1).join('x')]);
+ expect(result.string)
+ .to.equal(`-5`);
+ });
+
+ it('counts multibyte correctly', function () {
+ let result = countCharacters(['💩']);
+ expect(result.string)
+ .to.equal(`199`);
+
+ // emoji + modifier is still two chars
+ result = countCharacters(['💃🏻']);
+ expect(result.string)
+ .to.equal(`198`);
+ });
+});
diff --git a/ghost/admin/tests/unit/helpers/gh-count-down-characters-test.js b/ghost/admin/tests/unit/helpers/gh-count-down-characters-test.js
new file mode 100644
index 0000000000..41d4eb06bd
--- /dev/null
+++ b/ghost/admin/tests/unit/helpers/gh-count-down-characters-test.js
@@ -0,0 +1,31 @@
+import {expect} from 'chai';
+import {describe, it} from 'mocha';
+import {countDownCharacters} from 'ghost-admin/helpers/gh-count-down-characters';
+
+describe('Unit: Helper: gh-count-down-characters', function() {
+ let validStyle = 'color: rgb(159, 187, 88);';
+ let errorStyle = 'color: rgb(226, 84, 64);';
+
+ it('counts chars', function() {
+ let result = countDownCharacters(['test', 200]);
+ expect(result.string)
+ .to.equal(`4`);
+ });
+
+ it('warns with too many chars', function () {
+ let result = countDownCharacters([Array(205 + 1).join('x'), 200]);
+ expect(result.string)
+ .to.equal(`205`);
+ });
+
+ it('counts multibyte correctly', function () {
+ let result = countDownCharacters(['💩', 200]);
+ expect(result.string)
+ .to.equal(`1`);
+
+ // emoji + modifier is still two chars
+ result = countDownCharacters(['💃🏻', 200]);
+ expect(result.string)
+ .to.equal(`2`);
+ });
+});