Skip to content

Instantly share code, notes, and snippets.

@motemen
Created August 23, 2011 02:15
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save motemen/1164160 to your computer and use it in GitHub Desktop.
Save motemen/1164160 to your computer and use it in GitHub Desktop.
Textarea Character Count
// ==UserScript==
// @name textarea character count
// @namespace http://tokyoenvious.net/
// @include *
// ==/UserScript==
var Counter = {};
Counter.modeIndex = 0;
Counter.MODES = [ 'simple', 'genko' ];
Counter.count = function (textarea) {
var mode = Counter.MODES[this.modeIndex];
return Counter[mode](textarea);
};
Counter.simple = function (textarea) {
return textarea.value.length;
};
Counter.genko = function (textarea) {
var lineCount = 0;
var paragraphs = textarea.value.replace(/\n+$/).split(/\n/);
while (paragraphs.length) {
var p = paragraphs.shift();
for (var i = 0; i < p.length; i += 20) {
if (i > 0) {
if (/っ|。|、|」|』/.exec(p[i])) {
i++;
if (i >= p.length) {
break;
}
}
}
lineCount++;
}
}
var pageCount = Math.floor(lineCount / 20);
lineCount = lineCount % 20;
return '原稿用紙 ' + (pageCount ? pageCount + ' 枚' : '') + (pageCount && lineCount ? ' + ' : '') + (!pageCount || lineCount ? lineCount + ' 行' : '');
};
function embedIndicatorToTextareas (node) {
var textareas = node.querySelectorAll('textarea');
for (var i = 0, len = textareas.length; i < len; i++) {
embedIndicator(textareas[i]);
}
}
function fixIndicatorPosition (indicator, textarea) {
var rect = textarea.getBoundingClientRect();
indicator.style.top = (document.body.scrollTop + rect.bottom - indicator.offsetHeight - 5) + 'px';
indicator.style.right = (document.body.scrollWidth - rect.right + 5) + 'px';
}
function embedIndicator (textarea) {
var indicator = document.createElement('span');
indicator.textContent = '0';
indicator.setAttribute(
'style',
'position: absolute; font-size: 9pt; background-color: #333; color: white; padding: 0.1em 0.5em; font-family: Verdana; border-radius: 0.2em; opacity: 0'
);
document.body.appendChild(indicator);
fixIndicatorPosition(indicator, textarea);
var update = function () {
indicator.textContent = Counter.count(textarea);
indicator.style.display = 'inline';
indicator.style.opacity = 0.7;
if (arguments.callee.timer) {
clearTimeout(arguments.callee.timer);
}
var timer = arguments.callee.timer = setInterval(
function () {
if ('i' in arguments.callee) {
arguments.callee.i++;
} else {
arguments.callee.i = 1;
}
if (arguments.callee.i < 90) {
return;
}
indicator.style.opacity -= 0.02;
if (indicator.style.opacity <= 0.1) {
indicator.style.display = 'none'
clearTimeout(timer);
}
},
13
);
};
textarea.addEventListener(
'focus',
function () { fixIndicatorPosition(indicator, textarea); update() },
false
);
textarea.addEventListener('input', update, false);
textarea.addEventListener('keypress', update, false);
textarea.addEventListener('keyup', update, false);
textarea.addEventListener(
'keydown', function (e) {
var callee = arguments.callee;
var now = new Date().getTime();
if (callee.lastKeyCode == e.keyCode && e.keyCode == 16 /* SHIFT */ && now - callee.lastKeyTime <= 500) {
callee.lastKeyCode = undefined;
Counter.modeIndex = (Counter.modeIndex + 1) % Counter.MODES.length;
update();
} else {
callee.lastKeyCode = e.keyCode;
callee.lastKeyTime = now;
update();
}
}, false
);
/*
textarea.addEventListener(
'dblclick', function (e) {
Counter.modeIndex = (Counter.modeIndex + 1) % Counter.MODES.length;
update();
}, false
);
*/
}
embedIndicatorToTextareas(document);
document.addEventListener('DOMNodeInserted', function (e) {
embedIndicatorToTextareas(e.target);
}, false);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment