summaryrefslogtreecommitdiff
path: root/public/inc
diff options
context:
space:
mode:
Diffstat (limited to 'public/inc')
-rw-r--r--public/inc/zensur.js169
1 files changed, 169 insertions, 0 deletions
diff --git a/public/inc/zensur.js b/public/inc/zensur.js
new file mode 100644
index 0000000..d6b4d29
--- /dev/null
+++ b/public/inc/zensur.js
@@ -0,0 +1,169 @@
+(function() {
+ // states
+ // 0: uncensored
+ // 1: start_censor
+ // 2: censored
+ // state transition probabilities:
+ var p_trans = [
+ // x uncensored -> uncensored
+ 0.08, // uncensored -> start_censor
+ // 0 uncensored -> censored
+ // 0 start_censor -> uncensored
+ // 0 start_censor -> start_censor
+ // 1 start_censor -> censored
+ 0.55, // censored -> uncensored
+ // 0 censored -> start_censor
+ // x censored -> censored
+ ];
+ var censored_tag = "span";
+ var censored_class = "censored";
+ var start_censor = "<" + censored_tag + " class=\"" + censored_class + "\">";
+ var stop_censor = "</" + censored_tag + ">";
+ var re_censor = /[0-9A-Za-z]/;
+ var isCensored = true;
+ var censoredElements = [];
+
+ function splitWords(text) {
+ // some older IEs don't suppport capturing parentheses
+ // return text.split(/([\t\n\r ])/);
+ if (text.length === 0) {
+ return [""];
+ }
+ var words = [];
+ var last_i = 0;
+ for (var i = 0; i < text.length; i++) {
+ switch(text[i]) {
+ case '\t':
+ case '\n':
+ case '\r':
+ case ' ':
+ if (last_i !== i) {
+ words.push(text.slice(last_i, i));
+ }
+ words.push(text[i]);
+ last_i = i + 1;
+ }
+ }
+ if (last_i !== text.length) {
+ words.push(text.slice(last_i));
+ }
+ return words;
+ }
+
+ function censorWord(state, word) {
+ // ignore words not containing letters (may be spaces only)
+ if (!word.match(re_censor)) {
+ return word;
+ }
+
+ var r = Math.random();
+ var prefix = "";
+ var suffix = "";
+
+ if (state.s === 1) {
+ // start_censor -> censored
+ state.s = 2;
+ prefix = start_censor;
+ }
+
+ if (state.s === 0 && r < p_trans[0]) {
+ // uncensored -> start_censor
+ state.s = 1;
+ }
+ else if (state.s === 2 && r < p_trans[1]) {
+ // censored -> uncensored
+ state.s = 0;
+ suffix = stop_censor;
+ }
+
+ return prefix + word + suffix;
+ }
+
+ function censorTextNode(node) {
+ var state = { s: 0 };
+ if (Math.random() < p_trans[0]) {
+ // start_censor
+ state.s = 1;
+ }
+ var words = splitWords(node.nodeValue);
+ for (var i in words) {
+ words[i] = censorWord(state, words[i]);
+ }
+ if (state.s === 2) {
+ words[words.length-1] = words[words.length-1] + stop_censor;
+ }
+ var newnode = document.createElement(censored_tag);
+ newnode.innerHTML = words.join("");
+ return newnode;
+ }
+
+ function censorElement(element, on) {
+ var childs = element.childNodes;
+ var uncensored = false;
+ for (var i in childs) {
+
+ if (childs[i].nodeType === 1 &&
+ childs[i].nodeName !== "OPTION" &&
+ childs[i].nodeName !== "SCRIPT" &&
+ childs[i].nodeName !== "SELECT" &&
+ childs[i].nodeName !== "STYLE" &&
+ childs[i].nodeName !== "TEXTAREA" &&
+ childs[i].nodeName !== "TITLE")
+ {
+ if (!on &&
+ childs[i].nodeName === censored_tag.toUpperCase() &&
+ childs[i].className === censored_class &&
+ childs[i].childNodes.length === 1)
+ {
+ uncensored = true;
+ element.replaceChild(childs[i].childNodes[0], childs[i]);
+ }
+
+ var childUncensored = censorElement(childs[i], on);
+ if (!on && childUncensored)
+ {
+ var newnode = document.createTextNode("");
+ for (var j in childs[i].childNodes) {
+ if (!childs[i].childNodes[j].nodeType === 3) {
+ return;
+ }
+ if (childs[i].childNodes[j].nodeValue) {
+ newnode.nodeValue += childs[i].childNodes[j].nodeValue;
+ }
+ }
+ element.replaceChild(newnode, childs[i]);
+ }
+ }
+
+ else if (on &&
+ childs[i].nodeType === 3 &&
+ childs[i].nodeValue.match(re_censor))
+ {
+ element.replaceChild(censorTextNode(childs[i]), childs[i]);
+ }
+
+ }
+
+ return uncensored;
+ }
+
+ function doCensor(on) {
+ for (var i in censoredElements) {
+ censorElement(censoredElements[i], on);
+ }
+ }
+
+ window.zensurjs = function(arg) {
+ if (arg === undefined) {
+ isCensored = !isCensored;
+ doCensor(isCensored);
+ }
+ else {
+ censoredElements.push(arg);
+ if (isCensored) {
+ censorElement(arg, true);
+ }
+ }
+ return isCensored;
+ };
+})();