UNPKG

ncr

Version:

A parser and converter for Unicode Characters, including (standard and extended) Numerical Character References

278 lines (241 loc) 11.1 kB
(function() { "use strict"; var CLOAK, CND, DIFF, alert, badge, cloak, cloaked_text, debug, echo, esc_re, help, info, isa, log, rainbow, rpr, text, type_of, types, uncloaked_text, urge, validate, warn, whisper; //########################################################################################################### CND = require('cnd'); rpr = CND.rpr; badge = 'CLOAK'; log = CND.get_logger('plain', badge); info = CND.get_logger('info', badge); whisper = CND.get_logger('whisper', badge); alert = CND.get_logger('alert', badge); debug = CND.get_logger('debug', badge); warn = CND.get_logger('warn', badge); help = CND.get_logger('help', badge); urge = CND.get_logger('urge', badge); echo = CND.echo.bind(CND); rainbow = CND.rainbow.bind(CND); types = require('./types'); ({isa, validate, type_of} = types.export()); /* Cloaking characters by chained replacements: Assuming an alphabet `/[0-9]/`, cloaking characters starting from `0`. To cloak only `0`, we have to free the string of all occurrences of that character. In order to do so, we choose a primary escapement character, '1', and a secondary escapement character, conveniently also `1`. With those, we can replace all occurrences of `0` as `11`. However, that alone would produce ambiguous sequences. For example, the string `011` results in `1111`, but so does the string `1111` itself (because it does not contain a `0`, it remains unchanged when replacing `0`). Therefore, we have to escape the secondary escapement character itself, too; we choose the secondary replacement `1 -> 12` which has to come *first* when cloaking and *second* when uncloaking. This results in the following cloaking chain: CLOAK.new '012' 0123456789 1 -> 12: 01223456789 0 -> 11: 111223456789 The resulting string is free of `0`s. Because all original `0`s and `1`s have been preserved in disguise, we are now free to insert additional data into the string. Let's assume we have a text transformer `f`, say, `f ( x ) -> x.replace /456/g, '15'`, and a more comprehensive text transformer `g` which includes calls to `f` and other elementary transforms. Now, we would like to apply `g` to our text `0123456789`, but specifically omit the transformation performed by `f` (which would turn `0123456789` into `012315789`). We can do so by choosing a cloaking character—`0` in this example—and one or more signal characters that will pass unmodified through `g`. Assuming we cloak `456` as `01`, we first escape `0123456789` to `111223456789` so that all `0`s are removed. Then, we symbolize all occurrances of `456` as `01`, leading to `11122301789`. This string may be fed to `g` and will pass through `f` untouched. We can then reverse our steps: `11122301789` ... `111223456789` ... `01223456789` ... `0123456789`—which is indeed the string we're started with. Of course, this could not have worked if `g` had somehow transformed any of our cloaking devices; therefore, it is important to choose codepoints that are certain to be transparent to the intended text transformation. In case more primary escapement characters are needed, the chain may be expanded to include more replacement steps. In particular, it is interesting to use exactly two primary escapements; that way, we can define cloaked sequences of arbitrary lengths, using the two escapements—`0` and `1` in this example—as start and stop brackets: CLOAK.new '01234' 0123456789 2 -> 24: 01243456789 1 -> 23: 023243456789 0 -> 22: 2223243456789 Using more than two primary escapements is possible: CLOAK.new '0123456' 0123456789 3 -> 36: 01236456789 2 -> 35: 013536456789 1 -> 34: 0343536456789 0 -> 33: 33343536456789 CLOAK.new '012345678' 0123456789 4 -> 48: 01234856789 3 -> 47: 012474856789 2 -> 46: 0146474856789 1 -> 45: 04546474856789 0 -> 44: 444546474856789 */ //----------------------------------------------------------------------------------------------------------- /* from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions */ esc_re = function(text) { return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); }; //----------------------------------------------------------------------------------------------------------- this.new = function(chrs) { var R, chr_count, cloaked, delta, hide, idx, master, meta_chr_patterns, reveal, target_seq_chrs, target_seq_patterns; //......................................................................................................... if (chrs == null) { chrs = ['\x10', '\x11', '\x12', '\x13', '\x14']; //......................................................................................................... } else if (isa.text(chrs)) { chrs = Array.from(chrs); //......................................................................................................... } else if (!isa.list(chrs)) { throw new Error(`expected a text or a list, got a ${type_of(chrs)}`); } //......................................................................................................... if (!((chr_count = chrs.length) >= 3)) { throw new Error(`expected at least 3 characters, got ${chr_count}`); } //......................................................................................................... if (chr_count % 2 === 0) { throw new Error(`expected an odd number of characters, got ${chr_count}`); } //......................................................................................................... delta = (chr_count + 1) / 2 - 1; master = chrs[chr_count - delta - 1]; meta_chr_patterns = (function() { var i, ref, results; results = []; for (idx = i = 0, ref = delta; (0 <= ref ? i <= ref : i >= ref); idx = 0 <= ref ? ++i : --i) { results.push(RegExp(`${esc_re(chrs[idx])}`, "g")); } return results; })(); target_seq_chrs = (function() { var i, ref, results; results = []; for (idx = i = 0, ref = delta; (0 <= ref ? i <= ref : i >= ref); idx = 0 <= ref ? ++i : --i) { results.push(`${master}${chrs[idx + delta]}`); } return results; })(); target_seq_patterns = (function() { var i, ref, results; results = []; for (idx = i = 0, ref = delta; (0 <= ref ? i <= ref : i >= ref); idx = 0 <= ref ? ++i : --i) { results.push(RegExp(`${esc_re(target_seq_chrs[idx])}`, "g")); } return results; })(); cloaked = chrs.slice(0, delta); //--------------------------------------------------------------------------------------------------------- hide = (text) => { var R_, i, ref; R_ = text; for (idx = i = ref = delta; i >= 0; idx = i += -1) { R_ = R_.replace(meta_chr_patterns[idx], target_seq_chrs[idx]); } return R_; }; //......................................................................................................... reveal = (text) => { var R_, i, ref; R_ = text; for (idx = i = 0, ref = delta; (0 <= ref ? i <= ref : i >= ref); idx = 0 <= ref ? ++i : --i) { R_ = R_.replace(target_seq_patterns[idx], chrs[idx]); } return R_; }; //--------------------------------------------------------------------------------------------------------- R = { '~isa': 'CLOAK/cloak', hide, reveal, cloaked, master }; this._mixin_backslashed(R); return R; }; //----------------------------------------------------------------------------------------------------------- this._mixin_backslashed = function(cloak) { /* `oc`: 'original character' */ /* `op`: 'original pattern' */ /* `rm`: 'remove' */ var _mcp_backslash, _oc_backslash, _oce_backslash, _rm_backslash, _tsp_backslash, base, cloaked, hide, remove, reveal, start_chr, stop_chr; ({cloaked} = cloak); if (cloaked.length < 2) { start_chr = stop_chr = cloaked[0]; } else { [start_chr, stop_chr] = cloaked; } base = 10; _oc_backslash = '\\'; _oce_backslash = esc_re(_oc_backslash); _mcp_backslash = RegExp(`${esc_re(_oc_backslash)}((?:[\\ud800-\\udbff][\\udc00-\\udfff])|.)`, "g"); _tsp_backslash = RegExp(`${esc_re(start_chr)}([0-9a-z]+)${esc_re(stop_chr)}`, "g"); _rm_backslash = RegExp(`${esc_re(_oc_backslash)}(.)`, "g"); //--------------------------------------------------------------------------------------------------------- hide = (text) => { var R; R = text; R = R.replace(_mcp_backslash, function(_, $1) { var cid_hex; cid_hex = ($1.codePointAt(0)).toString(base); return `${start_chr}${cid_hex}${stop_chr}`; }); return R; }; //......................................................................................................... reveal = (text) => { var R; R = text; R = R.replace(_tsp_backslash, function(_, $1) { var chr; chr = String.fromCodePoint(parseInt($1, base)); return `${_oc_backslash}${chr}`; }); return R; }; //......................................................................................................... remove = (text) => { return text.replace(_rm_backslash, '$1'); }; //--------------------------------------------------------------------------------------------------------- cloak['backslashed'] = {hide, reveal, remove}; return null; }; //########################################################################################################### if (module.parent == null) { CLOAK = this; DIFF = require('coffeenode-diff'); cloak = CLOAK.new('()LTX'); cloak = CLOAK.new('*+?^$'); help(cloak); text = `% & ! ; some <<unlicensed>> (stuff here). \\𠄨 *20128+? &%!%A&123; some more \\\\<<unlicensed\\\\>> (stuff here). some \\<<licensed\\>> stuff here, and <\\< The <<<\\LaTeX{}>>> Logo: \`<<<\\LaTeX{}>>>\``; // debug '©94643', @_mcp_backslash // text = "% ; 2 3 \\ \\\\ \\𠄨" // text = "0 1 2 3 4 5 6 7 8" // text = "<<" log('(1) -', CND.rainbow(text)); cloaked_text = text; log('(2) -', CND.rainbow((cloaked_text = cloak.hide(cloaked_text)))); log('(3) -', CND.rainbow((cloaked_text = cloak.backslashed.hide(cloaked_text)))); uncloaked_text = cloaked_text; log('(4) -', CND.rainbow((uncloaked_text = cloak.backslashed.reveal(uncloaked_text)))); log('(5) -', CND.rainbow((uncloaked_text = cloak.reveal(uncloaked_text)))); log('(7) -', CND.rainbow('©79011', cloak.backslashed.remove(uncloaked_text))); if (uncloaked_text !== text) { log(DIFF.colorize(text, uncloaked_text)); } log(CND.steel('########################################################################')); } }).call(this); //# sourceMappingURL=cloak.js.map