liveinput
Version:
Input text auto changer
1,169 lines (1,165 loc) • 47.3 kB
JavaScript
/**
* liveinput - Input text auto changer
* @version v1.0.8
* @link https://github.com/vahpetr/liveinput/
* @license Apache-2.0
*/
var liveinput = new function() {
var helper = function() {
return new function() {
var rnd = Math.random;
var bind = Function.prototype.bind ? function(func, thisArg) {
return func.bind(thisArg);
} : function(func, thisArg) {
var slice = Array.prototype.slice, args = slice.call(arguments, 2);
return function() {
return func.apply(thisArg, args.concat(slice.call(arguments)));
};
};
var extrude = function(thisArg) {
return bind(Function.prototype.call, thisArg);
};
var filter = Array.prototype.filter ? extrude(Array.prototype.filter) : function(arr, cb) {
var i, l, res = [];
for (i = 0, l = arr.length; i < l; i++) if (cb(arr[i], i, arr)) res.push(arr[i]);
return res;
};
var map = Array.prototype.map ? extrude(Array.prototype.map) : function(arr, cb) {
var i, l, res = [];
for (i = 0, l = arr.length; i < l; i++) res.push(cb(arr[i], i, arr));
return res;
};
var forEach = Array.prototype.forEach ? extrude(Array.prototype.forEach) : function(arr, cb) {
for (var i = 0, l = arr.length; i < l; i++) cb(arr[i], i, arr);
};
var indexOf = Array.prototype.indexOf ? extrude(Array.prototype.indexOf) : function(arr, v) {
var i, l;
for (i = 0, l = arr.length; i < l; i++) if (arr[i] == v) return i;
return -1;
};
var except = function(target, remove) {
return filter(target, function(i) {
return indexOf(remove, i) == -1;
});
};
var charToCode = function(c) {
return c.charCodeAt();
};
var textToCodes = function(text) {
return map(text.split(""), charToCode);
};
var codesToText = function(codes) {
return String.fromCharCode.apply(void 0, codes);
};
var addEvent = function(el, event, cb) {
if (el.addEventListener) return el.addEventListener(event, cb, true);
return el.attachEvent("on" + event, cb);
};
var removeEvent = function(el, event, cb) {
if (el.removeEventListener) return el.removeEventListener(event, cb, true);
return el.detachEvent("on" + event, cb);
};
var guidchar = function() {
return (16 * rnd() | 0).toString(16);
};
var guid = function() {
return "00000000-0000-0000-0000-000000000000".replace(/0/g, guidchar);
};
var format = function(text, arg) {
for (var i = 0, l = arg.length; i < l; i++) text = text.replace(new RegExp("\\{" + i + "\\}", "g"), arg[i]);
return text;
};
var fill = function(l, cb) {
var res = [];
for (var i = 0; i < l; i++) res.push(cb(i, l, res));
return res;
};
var getSelectionStart = function(el) {
if (void 0 != el.selectionEnd) return el.selectionStart;
var range = document.selection.createRange();
var startRange = el.createTextRange();
startRange.moveToBookmark(range.getBookmark());
var endRange = el.createTextRange();
endRange.collapse(false);
var len = el.value.length;
if (startRange.compareEndPoints("StartToEnd", endRange) > -1) return len;
var start = -startRange.moveStart("character", -len);
var value = el.value.replace(/\r\n/g, "\n");
start += value.slice(0, start).split("\n").length - 1;
return start;
};
var getSelectionEnd = function(el) {
if (void 0 != el.selectionEnd) return el.selectionEnd;
var range = document.selection.createRange();
var startRange = el.createTextRange();
startRange.moveToBookmark(range.getBookmark());
var endRange = el.createTextRange();
endRange.collapse(false);
var len = el.value.length;
if (startRange.compareEndPoints("StartToEnd", endRange) > -1) return len;
var value = el.value.replace(/\r\n/g, "\n");
var end = -startRange.moveEnd("character", -len);
end += value.slice(0, end).split("\n").length - 1;
return end;
};
var setCaretPosition = function(el, pos) {
if (el.setSelectionRange) return el.setSelectionRange(pos.start, pos.end);
var range = el.createTextRange();
range.collapse(true);
range.moveStart("character", pos.start);
range.moveEnd("character", pos.end - pos.start);
range.select();
};
var getOwnPropertyNames = function(obj) {
var props = [];
for (var p in obj) if (obj.hasOwnProperty(p)) props.push(p);
return props;
};
var isArray = Array.isArray || function(arg) {
return "[object Array]" === Object.prototype.toString.call(arg);
};
var extend = function(a, b) {
if (!b) return;
for (var p in b) a[p] = b[p];
};
var callEvent = function(el, event, obj) {
var e;
if (document.createEvent) {
e = document.createEvent("HTMLEvents");
extend(e, obj);
e.initEvent(event, false, false);
return el.dispatchEvent(e);
}
e = document.createEventObject();
extend(e, obj);
setTimeout(function() {
try {
el.fireEvent("on" + event, e);
} catch (ex) {}
}, 0);
};
var preventDefault = function(e) {
e.preventDefault ? e.preventDefault() : e.returnValue = false;
};
var copy = function(obj) {
return JSON.parse(JSON.stringify(obj));
};
return {
charToCode: charToCode,
textToCodes: textToCodes,
codesToText: codesToText,
GUID: guid,
format: format,
fill: fill,
getSelectionStart: getSelectionStart,
getSelectionEnd: getSelectionEnd,
setCaretPosition: setCaretPosition,
getOwnPropertyNames: getOwnPropertyNames,
bind: bind,
filter: filter,
map: map,
forEach: forEach,
except: except,
indexOf: indexOf,
isArray: isArray,
event: {
add: addEvent,
remove: removeEvent,
call: callEvent
},
preventDefault: preventDefault,
copy: copy
};
}();
}();
var Cursor = function(el) {
var self = this;
var max = Math.max;
var min = Math.min;
self.start = 0;
self.end = 0;
self.range = false;
self.moveBack = false;
self.press = function() {
if (self.range) return;
self.end = self.start = helper.getSelectionStart(el);
};
self.release = function() {
self.end = helper.getSelectionEnd(el);
};
self.change = function() {
self.start = helper.getSelectionStart(el);
self.end = helper.getSelectionEnd(el);
};
var movepos;
self.move = function(offset) {
if (self.range) {
self.restore();
self.range = false;
return;
}
movepos = (self.moveBack ? min : max)(self.start - offset, self.end - offset);
helper.setCaretPosition(el, {
start: movepos,
end: movepos
});
self.press();
self.moveBack = false;
};
self.selectAll = function() {
helper.setCaretPosition(el, {
start: 0,
end: el.value.length
});
self.change();
};
self.restore = function() {
helper.setCaretPosition(el, {
start: self.start,
end: self.end
});
};
return self;
};
var langs = [ "ru", "en" ];
var whitelist = [ 192, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 189, 187, 81, 87, 69, 82, 84, 89, 85, 73, 79, 80, 219, 221, 65, 83, 68, 70, 71, 72, 74, 75, 76, 186, 222, 220, 226, 90, 88, 67, 86, 66, 78, 77, 188, 190, 191, 111, 106, 109, 107, 12 ];
var additional = {
keyCodes: [ 32, 13 ],
charCodes: [ 32, 10, 8 ]
};
whitelist.push.apply(whitelist, additional.keyCodes);
var hotkeymap = {
control: {
50: "@",
51: "#",
52: "$",
53: "%",
54: "^",
55: "&",
222: "'"
},
shift: {
ru: {
192: "ё",
50: '"',
51: "№",
52: ";",
54: ":",
55: "?",
219: "х",
221: "ъ",
186: "ж",
222: "э",
220: "/",
188: "б",
190: "ю",
191: ",",
226: "/"
},
en: {
192: "~",
50: "@",
51: "#",
52: "$",
54: "^",
55: "&",
186: ":",
222: '"',
220: "|",
188: "<",
190: ">",
191: "?",
226: "|"
}
}
};
var parseCode = function(code) {
return Number(code);
};
var keymapper = function(map) {
var props = helper.getOwnPropertyNames(map);
return helper.map(props, parseCode);
};
var hotkey = {
control: keymapper(hotkeymap.control),
shift: {
ru: keymapper(hotkeymap.shift.ru),
en: keymapper(hotkeymap.shift.en)
}
};
var command = {};
command.layout = function(config, type) {
var self = this;
self.type = type;
var lang = config.lang;
var map = {
ru: [ 1105, 1081, 1094, 1091, 1082, 1077, 1085, 1075, 1096, 1097, 1079, 1092, 1099, 1074, 1072, 1087, 1088, 1086, 1083, 1076, 1103, 1095, 1089, 1084, 1080, 1090, 1100, 1025, 1049, 1062, 1059, 1050, 1045, 1053, 1043, 1064, 1065, 1047, 1060, 1067, 1042, 1040, 1055, 1056, 1054, 1051, 1044, 1071, 1063, 1057, 1052, 1048, 1058, 1068, 1093, 1098, 92, 49, 51, 53, 56, 57, 48, 45, 61, 1061, 1066, 47, 33, 8470, 37, 42, 40, 41, 95, 43 ],
en: [ 96, 113, 119, 101, 114, 116, 121, 117, 105, 111, 112, 97, 115, 100, 102, 103, 104, 106, 107, 108, 122, 120, 99, 118, 98, 110, 109, 126, 81, 87, 69, 82, 84, 89, 85, 73, 79, 80, 65, 83, 68, 70, 71, 72, 74, 75, 76, 90, 88, 67, 86, 66, 78, 77, 91, 93, 92, 49, 51, 53, 56, 57, 48, 45, 61, 123, 125, 124, 33, 35, 37, 42, 40, 41, 95, 43 ]
};
for (var p in map) map[p].push.apply(map[p], additional.charCodes);
var relation = {
50: [ [ 50, 34 ], [ 50, 64 ] ],
52: [ [ 52, 59 ], [ 52, 36 ] ],
54: [ [ 54, 58 ], [ 54, 94 ] ],
55: [ [ 55, 63 ], [ 55, 38 ] ],
186: [ [ 1078, 1046 ], [ 59, 58 ] ],
222: [ [ 1101, 1069 ], [ 39, 34 ] ],
188: [ [ 1073, 1041 ], [ 44, 60 ] ],
190: [ [ 1102, 1070 ], [ 46, 62 ] ],
191: [ [ 46, 44 ], [ 47, 63 ] ]
};
var special = {
50: 64,
51: 35,
52: 36,
53: 37,
54: 94,
222: 39
};
var convert = {};
var keyCode, i, l;
for (keyCode in relation) convert[keyCode] = {};
for (i = 0, l = langs.length; i < l; i++) for (keyCode in relation) convert[keyCode][langs[i]] = relation[keyCode][i];
var others = lang ? helper.except(langs, [ lang ]) : langs;
var j, diff, code, index, added, length, k = others.length;
self.exec = function(compute, data) {
if (!data.keydown.length) return compute;
diff = compute.diff;
length = diff.length;
var converted = [];
for (i = 0, l = diff.length; i < l; i++) {
code = diff[i];
added = false;
index = helper.indexOf(map[lang], code);
if (index != -1) {
converted.push(code);
continue;
}
for (j = 0; j < k; j++) {
index = helper.indexOf(map[others[j]], code);
if (index == -1) continue;
converted.push(map[lang][index]);
added = true;
break;
}
if (added) continue;
if (data.keydown[i].ctrlKey && special[data.keydown[i].keyCode] == code) {
converted.push(code);
continue;
}
if (!convert[data.keydown[i].keyCode]) break;
index = helper.indexOf(convert[data.keydown[i].keyCode][lang], code);
if (index != -1) {
converted.push(code);
continue;
}
for (j = 0; j < k; j++) {
index = helper.indexOf(convert[data.keydown[i].keyCode][others[j]], code);
if (index == -1) continue;
converted.push(convert[data.keydown[i].keyCode][lang][index]);
added = true;
break;
}
if (added) continue;
data.keydown.splice(i, 1);
}
compute.diff = converted;
compute.offset += length - compute.diff.length;
return compute;
};
self.get = function() {
return {
type: type,
enabled: config.lang ? true : false,
config: config
};
};
return self;
};
command.include = function(config, type) {
var self = this;
self.type = type;
var lang = config.lang || "";
var presets = [];
helper.forEach([ "numbers", "symbols" ], function(preset) {
if (!config[preset]) return;
presets.push(preset);
});
var special = config.special || "";
var i, l, index;
var sources = lang ? [ lang ] : langs;
for (i = 0, l = sources.length; i < l; i++) {
index = helper.indexOf(presets, sources[i]);
if (index != -1) continue;
presets.push(sources[i]);
}
special = helper.textToCodes(special);
if (special.length && helper.indexOf(presets, "special") == -1) presets.push("special");
if (!config.chars) helper.forEach(langs, function(l) {
index = helper.indexOf(presets, l);
if (index == -1) return;
presets.splice(index, 1);
});
var map = {
ru: [ 1092, 1099, 1074, 1072, 1087, 1088, 1086, 1083, 1076, 1081, 1094, 1091, 1082, 1077, 1085, 1075, 1096, 1097, 1079, 1103, 1095, 1089, 1084, 1080, 1090, 1100, 1093, 1098, 1078, 1101, 1073, 1102, 1105, 1060, 1067, 1042, 1040, 1055, 1056, 1054, 1051, 1044, 1049, 1062, 1059, 1050, 1045, 1053, 1043, 1064, 1065, 1047, 1071, 1063, 1057, 1052, 1048, 1058, 1068, 1061, 1066, 1046, 1069, 1041, 1070, 1025 ],
en: [ 97, 115, 100, 102, 103, 104, 106, 107, 108, 113, 119, 101, 114, 116, 121, 117, 105, 111, 112, 122, 120, 99, 118, 98, 110, 109, 65, 83, 68, 70, 71, 72, 74, 75, 76, 81, 87, 69, 82, 84, 89, 85, 73, 79, 80, 90, 88, 67, 86, 66, 78, 77 ],
numbers: [ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57 ],
symbols: [ 32, 10, 96, 45, 61, 126, 33, 64, 35, 36, 37, 94, 38, 42, 40, 41, 95, 43, 91, 93, 59, 39, 92, 44, 46, 47, 123, 125, 58, 34, 124, 60, 62, 63, 8470 ],
special: special
};
var j, diff, length, added, k = presets.length;
self.exec = function(compute, data) {
diff = compute.diff;
length = diff.length;
var allowed = [];
for (i = 0, l = diff.length; i < l; i++) {
added = false;
for (j = 0; j < k; j++) {
index = helper.indexOf(map[presets[j]], diff[i]);
if (index == -1) continue;
allowed.push(diff[i]);
added = true;
break;
}
if (added) continue;
data.keydown.splice(i, 1);
}
compute.diff = allowed;
compute.offset += length - compute.diff.length;
return compute;
};
self.get = function() {
return {
type: type,
enabled: presets.length || special.length ? true : false,
config: config
};
};
return self;
};
command.exclude = function(config, type) {
delete config.lang;
var self = this;
self.type = type;
var special = config.special || "";
special = helper.textToCodes(special);
var i, l, index, diff, length;
self.exec = function(compute) {
diff = compute.diff;
length = diff.length;
var allowed = [];
for (i = 0, l = diff.length; i < l; i++) {
index = helper.indexOf(special, diff[i]);
if (index == -1) allowed.push(diff[i]);
}
compute.diff = allowed;
compute.offset += length - compute.diff.length;
return compute;
};
self.get = function() {
return {
type: type,
enabled: special.length ? true : false,
config: config
};
};
return self;
};
command.input = function(config, type) {
var self = this;
self.type = type;
var lang = config.lang;
var register = config.register;
var capslock = config.capslock;
var map = {
ru: {
lower: [ 1092, 1099, 1074, 1072, 1087, 1088, 1086, 1083, 1076, 1078, 1101, 1081, 1094, 1091, 1082, 1077, 1085, 1075, 1096, 1097, 1079, 1093, 1098, 1103, 1095, 1089, 1084, 1080, 1090, 1100, 1073, 1102, 1105 ],
upper: [ 1060, 1067, 1042, 1040, 1055, 1056, 1054, 1051, 1044, 1046, 1069, 1049, 1062, 1059, 1050, 1045, 1053, 1043, 1064, 1065, 1047, 1061, 1066, 1071, 1063, 1057, 1052, 1048, 1058, 1068, 1041, 1070, 1025 ]
},
en: {
lower: [ 97, 115, 100, 102, 103, 104, 106, 107, 108, 113, 119, 101, 114, 116, 121, 117, 105, 111, 112, 122, 120, 99, 118, 98, 110, 109 ],
upper: [ 65, 83, 68, 70, 71, 72, 74, 75, 76, 81, 87, 69, 82, 84, 89, 85, 73, 79, 80, 90, 88, 67, 86, 66, 78, 77 ]
}
};
var sources = lang ? [ lang ] : langs;
var findhere = helper.except(helper.getOwnPropertyNames(map[sources[0]]), [ register ])[0];
var sequence = [ "register", "capslock" ];
var i, l, j, index, diff, added, right, wrong, length, m, k = sources.length, n = sequence.length;
var handler = {};
handler.register = function(compute) {
diff = compute.diff;
length = diff.length;
var result = [];
for (i = 0, l = diff.length; i < l; i++) {
added = false;
for (j = 0; j < k; j++) {
index = helper.indexOf(map[sources[j]][findhere], diff[i]);
if (index == -1) continue;
result.push(map[sources[j]][register][index]);
added = true;
break;
}
if (added) continue;
result.push(diff[i]);
}
compute.diff = result;
compute.offset += length - compute.diff.length;
return compute;
};
handler.capslock = function(compute, data) {
if (!data.keydown.length) return compute;
diff = compute.diff;
length = diff.length;
var result = [];
for (i = 0, l = diff.length; i < l; i++) {
added = false;
right = register || (data.keydown[i].shiftKey ? "upper" : "lower");
if (register && data.keydown[i].shiftKey) right = "upper" == right ? "lower" : "upper";
wrong = "upper" == right ? "lower" : "upper";
for (j = 0; j < k; j++) {
index = helper.indexOf(map[sources[j]][right], diff[i]);
if (index != -1) continue;
index = helper.indexOf(map[sources[j]][wrong], diff[i]);
if (index == -1) continue;
result.push(map[sources[j]][right][index]);
added = true;
break;
}
if (added) continue;
result.push(diff[i]);
}
compute.diff = result;
compute.offset += length - compute.diff.length;
return compute;
};
self.exec = function(compute, data) {
var result = compute;
for (m = 0; m < n; m++) {
if (!config[sequence[m]]) continue;
result = handler[sequence[m]](result, data);
}
return result;
};
self.get = function() {
return {
type: type,
enabled: register || capslock ? true : false,
config: config
};
};
return self;
};
command.regexulator = function(config, type) {
var self = this;
self.type = type;
delete config.lang;
var templates = {
"after-chars-remove-chars": "((?:{0})+)(?:{1})+",
"after-term-remove-chars": "((?:^|\\s)+{0}\\s+)(?:{1})+",
"after-chars-remove-term": "((?:{0})+)\\s+{1}\\s+",
"after-term-remove-term": "((?:^|\\s)+{0}\\s+){1}\\s+",
"before-chars-remove-chars": "(?:{1})+((?:{0})+)",
"before-term-remove-chars": "(?:{1})+(\\s+{0}\\s+)",
"before-chars-remove-term": "(?:^|\\s)+{1}\\s+((?:{0})+)",
"before-term-remove-term": "(?:^|\\s)+{1}\\s+({0}\\s+)",
"after-char-remove-repeat": "({0}){1}+",
"after-char-replace-expr": "({0}{1})",
"after-term-replace-expr": "(?:^|\\s)+({0}\\s+{1})",
"after-char-upper-char": "({0}[a-zа-яё])"
};
var map = {
"[": "\\[",
"]": "\\]",
"\\": "\\\\",
"/": "\\/",
"^": "\\^",
$: "\\$",
".": "\\.",
"|": "\\|",
"?": "\\?",
"*": "\\*",
"+": "\\+",
"(": "\\(",
")": "\\)",
"{": "\\{",
"}": "\\}",
"'": "\\'",
"": "^"
};
var i, l, m, n, removes = {}, interceptions = {}, regexeRemoves = [], regexeInterceptions = [];
var mapping = function(c) {
return map[c] || c;
};
var char = function(text) {
var chars = text.split("");
if (!chars.length) chars.push("");
return helper.map(chars, mapping);
};
var chars = function(val) {
return [ char(val).join("|") ];
};
var term = function(val) {
return [ char(val).join("") ];
};
var template = function(name, arr) {
return helper.format(templates[name], arr);
};
var handler = {};
handler["after-chars-remove-chars"] = function(config, name, flag) {
for (var text in config) helper.forEach(chars(text), function(param) {
helper.forEach(chars(config[text]), function(secondparam) {
removes[flag].push(template(name, [ param, secondparam ]));
});
});
};
handler["after-chars-remove-term"] = function(config, name, flag) {
for (var text in config) helper.forEach(chars(text), function(param) {
helper.forEach(term(config[text]), function(secondparam) {
removes[flag].push(template(name, [ param, secondparam ]));
});
});
};
handler["after-term-remove-chars"] = function(config, name, flag) {
for (var text in config) helper.forEach(term(text), function(param) {
helper.forEach(chars(config[text]), function(secondparam) {
removes[flag].push(template(name, [ param, secondparam ]));
});
});
};
handler["after-term-remove-term"] = function(config, name, flag) {
for (var text in config) helper.forEach(term(text), function(param) {
helper.forEach(term(config[text]), function(secondparam) {
removes[flag].push(template(name, [ param, secondparam ]));
});
});
};
handler["after-char-remove-repeat"] = function(config, name, flag) {
helper.forEach(char(config), function(param) {
removes[flag].push(template(name, [ param, param ]));
});
};
handler["before-chars-remove-chars"] = handler["after-chars-remove-chars"];
handler["before-chars-remove-term"] = handler["after-chars-remove-term"];
handler["before-term-remove-chars"] = handler["after-term-remove-chars"];
handler["before-term-remove-term"] = handler["after-term-remove-term"];
handler["after-char-replace-expr"] = function(config, name, flag) {
for (var text in config) helper.forEach(char(text), function(param) {
interceptions[flag].push({
expr: template(name, [ param, config[text].expr ]),
replacer: config[text].replacer
});
});
};
handler["after-char-upper-char"] = function(config, name, flag) {
var obj = {};
var arr = helper.isArray(config) ? config : [ config ];
for (var j = 0, k = arr.length; j < k; j++) obj[arr[j]] = {
expr: "[a-zа-яё]",
replacer: function(find, c, offset, str, data, noffset) {
if (1 == find.length) str = ""; else str = find.charAt(0);
if (!data.keydown.length) return str + find.charAt(find.length - 1).toLocaleUpperCase();
if (data.keydown[noffset].shiftKey) return str + find.charAt(find.length - 1).toLowerCase();
return str + find.charAt(find.length - 1).toLocaleUpperCase();
}
};
handler["after-char-replace-expr"](obj, "after-char-replace-expr", flag);
};
handler["after-term-replace-expr"] = function(config, name, flag) {
for (var text in config) helper.forEach(term(text), function(param) {
interceptions[flag].push({
expr: template(name, [ param, config[text].expr ]),
replacer: config[text].replacer
});
});
};
for (var flag in config) {
if (/[^igm]/g.test(flag)) throw new Error("Command regexulator can not support flag " + flag);
flag = flag.split("").sort().join("");
removes[flag] = removes[flag] || [];
interceptions[flag] = interceptions[flag] || [];
helper.forEach([ "after-chars-remove-chars", "after-term-remove-chars", "after-chars-remove-term", "after-term-remove-term", "before-chars-remove-chars", "before-term-remove-chars", "before-chars-remove-term", "before-term-remove-term", "after-char-replace-expr", "after-term-replace-expr", "after-char-remove-repeat", "after-char-upper-char" ], function(name) {
if (void 0 == config[flag][name]) return;
handler[name](config[flag][name], name, flag);
});
}
for (flag in removes) {
if (!removes[flag].length) continue;
regexeRemoves.push({
expr: new RegExp(removes[flag].join("|"), flag),
replacer: helper.fill(removes[flag].length, function(i) {
return "$" + (i + 1);
}).join("")
});
}
var selector = function(i) {
return i.expr;
};
for (flag in interceptions) {
if (!interceptions[flag].length) continue;
regexeInterceptions.push({
expr: new RegExp(helper.map(interceptions[flag], selector).join("|"), flag),
replacer: interceptions[flag][0].replacer
});
}
var find, c, offset, str;
var parseArgs = function(args) {
find = args[0];
offset = args[args.length - 2];
str = args[args.length - 1];
for (m = 1, n = args.length - 2; m < n; m++) if (void 0 != args[m]) {
c = args[m];
break;
}
};
self.exec = function(text, data) {
for (i = 0, l = regexeRemoves.length; i < l; i++) text = text.replace(regexeRemoves[i].expr, regexeRemoves[i].replacer);
for (i = 0, l = regexeInterceptions.length; i < l; i++) text = text.replace(regexeInterceptions[i].expr, function() {
parseArgs(arguments);
if (data.cursor.start <= offset + 1 && offset + 1 <= data.cursor.end) {
n = data.before.length + data.diff.length - offset - find.length;
if (!data.keydown[n]) n = 0;
return regexeInterceptions[i].replacer(find, c, offset, str, data, n);
}
return find;
});
return text;
};
self.get = function() {
return {
type: type,
enabled: regexeRemoves.length || regexeInterceptions.length ? true : false,
config: config
};
};
return self;
};
var Preprocessor = function(config) {
var self = this;
var i, l, result, processes = [];
var sequence = [ "layout", "include", "exclude", "input" ];
for (i = 0, l = sequence.length; i < l; i++) {
if (!config[sequence[i]]) continue;
if (true === config[sequence[i]]) config[sequence[i]] = {};
processes.push(new command[sequence[i]](config[sequence[i]], sequence[i]));
}
for (var j = 0; j < processes.length; j++) if (!processes[j].get().enabled) processes.splice(j--, 1);
l = processes.length;
self.pass = function(compute, data) {
result = compute;
for (i = 0, j = data.keydown.length; i < j; i++) {
if (!data.keydown[i] || 8 != data.keydown[i].keyCode) continue;
data.keydown.splice(i, 1);
result.diff.splice(i, 1);
}
for (i = 0; i < l; i++) result = processes[i].exec(result, data);
result.before = helper.codesToText(result.before);
result.diff = helper.codesToText(result.diff);
result.after = helper.codesToText(result.after);
result.offset = compute.offset;
return result;
};
self.config = function() {
var info, cfg = {};
for (i = 0; i < l; i++) {
info = processes[i].get();
cfg[info.type] = info.config;
}
return cfg;
};
return self;
};
var Postprocessor = function(config) {
var self = this;
var i, l, result, length, processes = [];
var sequence = [ "regexulator" ];
for (i = 0, l = sequence.length; i < l; i++) {
if (!config[sequence[i]]) continue;
if (true === config[sequence[i]]) config[sequence[i]] = {};
processes.push(new command[sequence[i]](config[sequence[i]], sequence[i]));
}
for (var j = 0; j < processes.length; j++) if (!processes[j].get().enabled) processes.splice(j--, 1);
l = processes.length;
self.pass = function(text, data) {
result = text;
length = result.length;
for (i = 0; i < l; i++) result = processes[i].exec(result, data);
data.result.offset += length - result.length;
return result;
};
self.config = function() {
var info, cfg = {};
for (i = 0; i < l; i++) {
info = processes[i].get();
cfg[info.type] = info.config;
}
return cfg;
};
return self;
};
var LiveInput = function(config) {
var self = this;
var lang = config.lang;
var interval = config.interval;
var preprocessor = new Preprocessor(config);
var postprocessor = new Postprocessor(config);
var heap = {};
var callElementEvent = function(el, name, event) {
if (event.old == el.value) return;
event.value = el.value;
helper.event.call(el, "liveinput", event);
};
var event, eventIndex, eventCount;
var callLiveinputEvent = function(el, events, name, ptr, arg) {
if (!events[name]) return;
event = events[name];
for (eventIndex = 0, eventCount = event.length; eventIndex < eventCount; eventIndex++) event[eventIndex].apply(ptr, arg);
};
var onkeyup = function(e, el, data, cursor, events, ptr) {
cursor.release();
if (8 == e.keyCode) {
data.before = el.value.substring(0, cursor.end);
data.diff = "";
data.after = el.value.substring(cursor.end);
} else {
data.before = el.value.substring(0, cursor.start);
data.diff = el.value.substring(cursor.start, cursor.end);
data.after = el.value.substring(cursor.end);
}
data.result.offset = e.ctrlKey ? -data.diff.length : 8 == e.keyCode && cursor.start == cursor.end + 1 ? 1 : 0;
if (e.ctrlKey && hotkeymap.control[e.keyCode]) {
data.diff += hotkeymap.control[e.keyCode];
data.result.offset--;
}
if (e.shiftKey) if (hotkeymap.shift[lang] && hotkeymap.shift[lang][e.keyCode]) {
data.diff += hotkeymap.shift[lang][e.keyCode];
data.result.offset--;
}
data.result = preprocessor.pass({
before: helper.textToCodes(data.before),
diff: helper.textToCodes(data.diff),
after: helper.textToCodes(data.after),
offset: data.result.offset
}, data);
data.result.value = data.result.before + data.result.diff + data.result.after;
data.result.value = postprocessor.pass(data.result.value, data);
el.value = data.result.value;
callLiveinputEvent(el, events, "change", ptr, [ data.result.value, data.old, lang ]);
callElementEvent(el, "liveinput", ptr.event);
ptr.event.old = data.old = el.value;
cursor.move(data.result.offset);
data.keydown = [];
ptr.timer = null;
return true;
};
var refresh = function(el) {
if (!el.value.length || !config.refresh) return;
var ptr = heap[el.GUID];
clearTimeout(ptr.timer);
if (!ptr.timer) ptr.cursor.selectAll();
onkeyup({
keyCode: 0
}, el, ptr.data, ptr.cursor, ptr.events, ptr);
};
var onkeydown = function(e, el, data, cursor, events, ptr) {
if (e.ctrlKey) switch (e.keyCode) {
case 90:
case 67:
return false;
case 89:
helper.preventDefault(e);
return false;
case 65:
cursor.selectAll();
cursor.range = true;
break;
case 8:
cursor.moveBack = true;
}
if (data.mousedown) {
helper.preventDefault(e);
return false;
}
if (helper.indexOf(whitelist, e.keyCode) == -1) {
if (ptr.timer) refresh(el);
return false;
}
clearTimeout(ptr.timer);
data.keydown.push({
keyCode: e.keyCode,
shiftKey: e.shiftKey,
ctrlKey: e.ctrlKey
});
if (!ptr.timer) cursor.press();
ptr.timer = setTimeout(function() {
onkeyup(data.keydown[data.keydown.length - 1], el, data, cursor, events, ptr);
}, interval);
if (e.ctrlKey) if (helper.indexOf(hotkey.control, e.keyCode) != -1) {
helper.preventDefault(e);
return false;
}
if (e.shiftKey) if (hotkey.shift[lang] && helper.indexOf(hotkey.shift[lang], e.keyCode) != -1) {
helper.preventDefault(e);
return false;
}
return true;
};
var bind = function(el) {
if (!el.GUID) el.GUID = helper.GUID();
var ptr = heap[el.GUID] = {};
ptr.el = el;
ptr.event = {
old: ""
};
ptr.data = {
keydown: [],
result: {},
old: ""
};
ptr.cursor = new Cursor(el);
var data = ptr.data;
var cursor = ptr.data.cursor = ptr.cursor;
var events = ptr.events = {};
ptr.keydown = function(e) {
void 0;
onkeydown(e, el, data, cursor, events, ptr);
};
ptr.paste = function() {
void 0;
data.keydown = [];
return true;
};
ptr.dragover = function(e) {
void 0;
helper.preventDefault(e);
return false;
};
ptr.mousedown = function() {
void 0;
ptr.data.mousedown = true;
refresh(el);
};
ptr.mouseup = function() {
void 0;
ptr.data.mousedown = false;
};
ptr.mouseleave = function() {
void 0;
ptr.data.mousedown = false;
};
ptr.blur = function() {
void 0;
refresh(el);
};
window.ptr = ptr;
helper.event.add(el, "keydown", ptr.keydown);
helper.event.add(el, "paste", ptr.paste);
helper.event.add(el, "mousedown", ptr.mousedown);
helper.event.add(el, "mouseup", ptr.mouseup);
helper.event.add(el, "mouseleave", ptr.mouseleave);
helper.event.add(el, "dragover", ptr.dragover);
helper.event.add(el, "blur", ptr.blur);
refresh(el);
};
var unbind = function(el) {
if (!el.GUID || !heap[el.GUID]) return;
var ptr = heap[el.GUID];
helper.event.remove(el, "keydown", ptr.keydown);
helper.event.remove(el, "paste", ptr.paste);
helper.event.remove(el, "mousedown", ptr.mousedown);
helper.event.remove(el, "mouseup", ptr.mouseup);
helper.event.remove(el, "mouseleave", ptr.mouseleave);
helper.event.remove(el, "dragover", ptr.dragover);
helper.event.remove(el, "blur", ptr.blur);
var events = ptr.events;
for (var name in events) {
events[name].length = 0;
delete events[name];
}
var props = helper.getOwnPropertyNames(ptr);
for (var prop in props) delete ptr[prop];
delete heap[el.GUID];
if (!helper.getOwnPropertyNames(heap).length) {
var key = JSON.stringify(config);
delete cache[key];
}
};
self.bind = function() {
for (var i = 0, l = arguments.length; i < l; i++) bind(arguments[i]);
return self;
};
self.unbind = function() {
for (var i = 0, l = arguments.length; i < l; i++) unbind(arguments[i]);
return self;
};
self.on = function(event, el, cb) {
if (!el.GUID || !heap[el.GUID]) return self;
heap[el.GUID].events[event] = heap[el.GUID].events[event] || [];
heap[el.GUID].events[event].push(cb);
switch (event) {
case "change":
if (el.value.length) refresh(el);
}
return self;
};
self.off = function(event, el, cb) {
if (!el.GUID || !heap[el.GUID]) return self;
eventIndex = helper.indexOf(heap[el.GUID].events[event], cb);
if (eventIndex == -1) return self;
heap[el.GUID].events[event].splice(eventIndex, 1);
return self;
};
self.refresh = function(el) {
if (!heap[el.GUID]) return;
refresh(el);
};
return self;
};
var setLang = function(config) {
if ("undefined" == typeof config.lang) return;
var lang = config.lang;
for (var p in config) {
if (!config[p]) continue;
if (true == config[p]) config[p] = {};
config[p].lang = lang;
}
};
var mergeConfig = function(a, b) {
if (!b) return a;
for (var p in b) if ("object" == typeof a[p]) a[p] = mergeConfig(a[p], b[p]); else a[p] = b[p];
return a;
};
var cache = {};
var types = {
"default": {
lang: "",
interval: 1e3 / 24,
refresh: true,
layout: true,
include: {
chars: true,
numbers: true,
symbols: true,
special: ""
},
exclude: {
special: "{}[]"
},
input: {
register: "",
capslock: false
},
regexulator: {
g: {}
}
},
fio: function() {
var special = " '-";
return {
include: {
numbers: false,
symbols: false,
special: special
},
regexulator: {
g: {
"after-char-remove-repeat": special,
"after-char-upper-char": [ "'", "" ],
"after-chars-remove-chars": {
"": special
}
}
}
};
}(),
numeric: {
include: {
chars: false,
numbers: true,
symbols: false,
special: ""
}
},
address: function() {
var special = "-/";
return {
lang: "",
include: {
symbols: false,
special: special
},
input: {
register: "upper",
capslock: true
},
regexulator: {
g: {
"after-char-remove-repeat": special,
"after-chars-remove-chars": {
"": special
}
}
}
};
}(),
month: {
interval: 700,
include: {
numbers: true,
symbols: false
},
input: {
capslock: false
},
regexulator: {
g: {
"after-char-remove-repeat": "0",
"after-char-upper-char": ""
}
}
}
};
var configuration = function(config) {
mergeConfig(types, config);
};
configuration.get = function(name) {
return helper.copy(types[name]);
};
configuration.merge = function() {
var config = configuration.get("default");
for (var i = 0, l = arguments.length; i < l; i++) {
if ("default" == arguments[i]) continue;
var options = arguments[i];
if ("string" == typeof options) {
if (!types[options]) throw new Error("Can not find liveinput type " + options);
options = types[options];
}
mergeConfig(config, options);
}
return config;
};
configuration.add = function(name, config) {
if (types[name]) throw new Error("Can not add exist liveinput type " + name);
types[name] = config;
};
var init = function(name, options) {
if ("object" == typeof name || "undefined" == typeof name) {
options = name;
name = "default";
}
var config = configuration.merge(name, options);
setLang(config);
var key = JSON.stringify(config);
var instance = cache[key] || (cache[key] = new LiveInput(config));
return instance;
};
var set = function(el, value) {
el.value = value;
for (var key in cache) cache[key].refresh(el);
};
return {
init: init,
configuration: configuration,
set: set
};
}();
if ("undefined" != typeof module) module.exports = liveinput; else if (window) window.liveinput = liveinput;