UNPKG

liveinput

Version:
1,709 lines (1,584 loc) 52.3 kB
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 res = [], i, l; 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 res = [], i, l; 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 codeToChar = function(code) { // return String.fromCharCode(code); //}; var codesToText = function(codes) { return String.fromCharCode.apply(undefined, codes); //map(codes, codeToChar).join(''); }; 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 (rnd() * 16 | 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 (el.selectionEnd != undefined) return el.selectionStart; //var r = document.selection.createRange().duplicate(); //r.moveEnd('character', el.value.length); //if (r.text == '') return el.value.length; //return el.value.lastIndexOf(r.text); //bug в старых браузерах положение каретки пока не работает 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 (el.selectionEnd != undefined) return el.selectionEnd; //var r = document.selection.createRange().duplicate(); //r.moveStart('character', -el.value.length); //console.log('getSelectionEnd character r.text.length', r.text.length); //return r.text.length; //bug в старых браузерах положение каретки пока не работает 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) { //el.focus(); if (el.setSelectionRange) return el.setSelectionRange(pos.start, pos.end); //if (!el.createTextRange) return; var range = el.createTextRange(); range.collapse(true); range.moveStart('character', pos.start); range.moveEnd('character', pos.end - pos.start); //range.move('character', 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.prototype.toString.call(arg) === '[object Array]'; }; var extend = function(a, b) { if (!b) return; // ReSharper disable once MissingHasOwnPropertyInForeach 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); // event type,bubbling,cancelable return el.dispatchEvent(e); } // else if (!document.createEventObject) return; e = document.createEventObject(); //window.event 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, //codeToChar: codeToChar, 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 }; } })(); // ReSharper disable once InconsistentNaming var Cursor = function(el) { var self = this; var max = Math.max; var min = Math.min; //var data = {}; self.start = 0; self.end = 0; self.range = false; self.moveBack = false; //var marker = {}; self.press = function () { if (self.range) return; self.end = self.start = helper.getSelectionStart(el); //console.log('press', self.start); //data.start = helper.getSelectionStart(el); //self.end = data.end = helper.getSelectionEnd(el); //self.text = data.text = el.value; //self.end = max(data.start, data.end); }; self.release = function() { self.end = helper.getSelectionEnd(el); //console.log('release', self.end); //self.start = data.oldStart = data.start; //data.oldEnd = data.end; //data.oldText = data.text; //self.end = min(data.start, data.end); }; self.change = function() { self.start = helper.getSelectionStart(el); self.end = helper.getSelectionEnd(el); //console.log('change', self.start, self.end); }; //self.save = function () { // marker.start = helper.getSelectionStart(el); // marker.end = helper.getSelectionEnd(el); // //console.log('change', self.start, self.end); //}; //self.load = function() { // helper.setCaretPosition(el, marker); //}; var movepos; self.move = function(offset) { if (self.range) { self.restore(); self.range = false; return; } //self.start = data.start - offset; //self.end = self.start; //if (self.start != self.end) return helper.setCaretPosition(el, { // start: self.start, // end: self.end //}); movepos = (self.moveBack ? min : max)(self.start - offset, self.end - offset); //console.log('move', movepos); helper.setCaretPosition(el, { start: movepos, end: movepos }); self.press(); self.moveBack = false; // ReSharper disable once NotAllPathsReturnValue }; 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 }); }; //self.get = function() { // return data; //}; return self; //http://javascript.nwbox.com/cursor_position/cursor.js // function getCaretPosition (el) { // el.focus (); // if (typeof el.selectionStart == 'number') // return { start: el.selectionStart, end: el.selectionEnd } // //http://stackoverflow.com/questions/4928586/get-caret-position-in-html-input // if (!document.selection) return; //var range = document.selection.createRange(); ////var len = el.value.length ////range.moveStart ('character', -len); ////var start = range.text.length; //// Create a working TextRange that lives only in the input //var startRange = el.createTextRange(); //startRange.moveToBookmark(range.getBookmark()); //// Check if the start and end of the selection are at the very end //// of the input, since moveStart/moveEnd doesn't return what we want //// in those cases //var endRange = el.createTextRange(); //endRange.collapse(false); //var start = 0; //var end = 0; //var len = el.value.length; //if (startRange.compareEndPoints("StartToEnd", endRange) > -1) { //start = end = len; //} //start = -startRange.moveStart("character", -len); //var value = el.value.replace(/\r\n/g, '\n'); //start += value.slice(0, start).split("\n").length - 1; //end = -startRange.moveEnd("character", -len); //end += value.slice(0, end).split("\n").length - 1; //return { start: start, end: end }; // } }; //var log = console.log.bind(console); //var forEach = Function.prototype.call.bind(Array.prototype.forEach); forEach([1, 2, 3], log); //TODO добавил язык - добавь во все комманды кейкоды языка 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]; //, 8, 45, 36, 33, 35, 34, 37, 38, 39, 40 //var blacklist = [27, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 104, 145, 19, 144, 9, 20, 16, 17, 91, 18, 92, 93, 45, 36, 33, 46, 35, 34, 37, 38, 39, 40];//, 8 var additional = { //space(32,32)/enter(13,10)/backspace(8) keyCodes: [32, 13], //, 8 charCodes: [32, 10, 8] } //char 10, 13 keyCode whitelist.push.apply(whitelist, additional.keyCodes); var hotkeymap = { control: { 50: '@', 51: '#', 52: '$', 53: '%', 54: '^', 55: '&', //56: '*', // ReSharper disable once StringLiteralWrongQuotes 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: '&', //TODO не поддерживаются //219: '{', //221: '}', 186: ':', 222: '"', 220: '|', 188: '<', 190: '>', 191: '?', 226: '|' } } }; var parseCode = function(code) { return Number(code); //parseInt(code, 10); } var keymapper = function(map) { //console.log('keymapper', map); var props = helper.getOwnPropertyNames(map); //console.log('keymapper', props); return helper.map(props, parseCode); } //TODO может искать без этого, сразу в hotkeymap var hotkey = { control: keymapper(hotkeymap.control), shift: { ru: keymapper(hotkeymap.shift.ru), en: keymapper(hotkeymap.shift.en) } }; //TODO разделить на комманды препроцессора и постпроцессорв 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], //[]{} 192,91,221,123 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] }; // ReSharper disable once MissingHasOwnPropertyInForeach for (var p in map) { map[p].push.apply(map[p], additional.charCodes); } //[];',. //[1093, 1098, 59, 39, 44, 46] //[219, 221, 186, 222, 188, 190] var relation = { //2 50: [ [50, 34], //ru [50, 64] //en ], //51: [ // [51, 8470], //ru // [51, 35] //en //], //4 52: [ [52, 59], [52, 36] ], //6 54: [ [54, 58], [54, 94] ], //7 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; // ReSharper disable once MissingHasOwnPropertyInForeach for (keyCode in relation) { convert[keyCode] = {}; } for (i = 0, l = langs.length; i < l; i++) { // ReSharper disable once MissingHasOwnPropertyInForeach for (keyCode in relation) { convert[keyCode][langs[i]] = relation[keyCode][i]; } } //console.log(convert); var others = lang ? helper.except(langs, [lang]) : langs; var j, k = others.length, diff, code, index, added, length; self.exec = function(compute, data) { //debugger //copypast 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; //map 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; //special if (data.keydown[i].ctrlKey && special[data.keydown[i].keyCode] == code) { converted.push(code); continue; } //convert 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, //low 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 // up ], 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 }; //for (i = 0, l = langs.length; i < l; i++) { // map[langs[i]].push.apply(map[langs[i]], additional.charCodes); //} var j, k = presets.length, diff, length, added; self.exec = function(compute, data) { //if (data.hotkey) return compute; 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, data) { 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, k = sources.length, index, diff, added, right, wrong, length, m, n = sequence.length; var handler = {}; handler.register = function(compute, data) { //if (data.hotkey) return compute;// || data.copypast? 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; //?//data.copypast || 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 = right == 'upper' ? 'lower' : 'upper'; } wrong = right == 'upper' ? '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 = { //TODO после term может быть ., ещё что то? '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+', //поидее на конце должно быть (?:$|\\s)+ 'before-chars-remove-chars': '(?:{1})+((?:{0})+)', 'before-term-remove-chars': '(?:{1})+(\\s+{0}\\s+)', //поидее на конце должно быть (?:$|\\s)+ 'before-chars-remove-term': '(?:^|\\s)+{1}\\s+((?:{0})+)', 'before-term-remove-term': '(?:^|\\s)+{1}\\s+({0}\\s+)', //поидее на конце должно быть (?:$|\\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 removes = {}, interceptions = {}, regexeRemoves = [], regexeInterceptions = [], i, l, m, n; 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) { // ReSharper disable once MissingHasOwnPropertyInForeach 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) { // ReSharper disable once MissingHasOwnPropertyInForeach 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) { // ReSharper disable once MissingHasOwnPropertyInForeach 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) { // ReSharper disable once MissingHasOwnPropertyInForeach 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) { // ReSharper disable once MissingHasOwnPropertyInForeach 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 (find.length == 1) { 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) { // ReSharper disable once MissingHasOwnPropertyInForeach 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 }); }); } }; // ReSharper disable once MissingHasOwnPropertyInForeach 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( //TODO сортируем очередь выполнения [ '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 (config[flag][name] == undefined) return; handler[name](config[flag][name], name, flag); }); } // ReSharper disable once MissingHasOwnPropertyInForeach 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; }; // ReSharper disable once MissingHasOwnPropertyInForeach 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 }); } //console.log(regexeRemoves, regexeInterceptions); 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 (args[m] != undefined) { c = args[m]; break; } } } self.exec = function(text, data) { for (i = 0, l = regexeRemoves.length; i < l; i++) { //bug необходимо реплейсить функцией и вырезать из data.keydown вхождения, шанс возникновения такой ситуации низок, пок так text = text.replace(regexeRemoves[i].expr, regexeRemoves[i].replacer); } for (i = 0, l = regexeInterceptions.length; i < l; i++) { //if (data.keydown.length) text = text.replace(regexeInterceptions[i].expr, function() { //if (!data.keydown.length) return find; parseArgs(arguments); //console.log(offset); //console.log(data.before.length, data.diff.length, data.after.length, offset, data.cursor.start, data.cursor.end);//log if (data.cursor.start <= offset + 1 && offset + 1 <= data.cursor.end) { n = data.before.length + data.diff.length - offset - find.length; // + data.after.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; } // ReSharper disable once InconsistentNaming 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 (config[sequence[i]] === true) 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; //TODO remove noop for (i = 0, j = data.keydown.length; i < j; i++) { if (!data.keydown[i] || data.keydown[i].keyCode != 8) continue;//backspace //debugger data.keydown.splice(i, 1); result.diff.splice(i, 1); } for (i = 0; i < l; i++) { result = processes[i].exec(result, data); //result.type = processes[i].type;//log //data.log.push(JSON.parse(JSON.stringify(result)));//log } 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 cfg = {}, info; for (i = 0; i < l; i++) { info = processes[i].get(); cfg[info.type] = info.config; } //console.log(JSON.stringify(cfg, null, 2), cfg);//log return cfg; } return self; }; var Postprocessor = function(config) { var self = this; var i, l, result, processes = [], length; var sequence = ['regexulator']; for (i = 0, l = sequence.length; i < l; i++) { if (!config[sequence[i]]) continue; if (config[sequence[i]] === true) 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); //result.type = processes[i].type;//log //data.log.push(JSON.parse(JSON.stringify(result)));//log } data.result.offset += length - result.length; return result; }; self.config = function() { var cfg = {}, info; for (i = 0; i < l; i++) { info = processes[i].get(); cfg[info.type] = info.config; } //console.log(JSON.stringify(cfg, null, 2), cfg);//log return cfg; } return self; }; // ReSharper disable once InconsistentNaming 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); //processor.config();//log 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); } }; //window.kb = []; var onkeyup = function(e, el, data, cursor, events, ptr) { //if (data.old == el.value) return; //console.log('onkeyup', e); //if (whitelist.indexOf(e.keyCode) == -1) return true; //data.log = [];//log //if (data.copypast) { // data.keydown = []; //} //data.shift = e.shiftKey && !data.copypast; //data.control = e.ctrlKey && !data.copypast; //data.alt = e.altKey && !data.copypast; //console.log('before', JSON.parse(JSON.stringify(cursor))); //cursor.update(); cursor.release(); //console.log('after', JSON.parse(JSON.stringify(cursor))); //data.cursor = cursor.get();//log //if (e.keyCode == 8) { // debugger //} //console.log('after', JSON.stringify(JSON.parse(cursor.get()))); if (e.keyCode == 8) { //backspace 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); //console.log(helper.charToCode(data.diff));//log //console.log('onkeyup', data.diff); //var press = {}; //press[data.diff] = e.keyCode; //press[data.diff.toLocaleUpperCase()] = e.keyCode; //window.kb.push(press); //console.log(press); data.after = el.value.substring(cursor.end); } data.result.offset = e.ctrlKey ? -data.diff.length : e.keyCode == 8 && cursor.start == cursor.end + 1 ? 1 : 0; if (e.ctrlKey && hotkeymap.control[e.keyCode]) { data.diff += hotkeymap.control[e.keyCode]; data.result.offset--; //data.result.offset -= hotkeymap.control[e.keyCode].length; //data.hotkey = true; } if (e.shiftKey) { if (hotkeymap.shift[lang] && hotkeymap.shift[lang][e.keyCode]) { data.diff += hotkeymap.shift[lang][e.keyCode]; data.result.offset--; } } //if (data.old == el.value) { // data.keydown = []; // ptr.timer = null; // return true; //} //console.log(e.keyCode, cursor.start, cursor.end); data.result = preprocessor.pass({ before: helper.textToCodes(data.before), diff: helper.textToCodes(data.diff), after: helper.textToCodes(data.after), offset: data.result.offset }, data); //var press = { // keyCode: e.keyCode, // charCode: helper.charToCode(data.result.diff) //} //console.log(press); 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; //data.old = data.result.value; //var copy = JSON.parse(JSON.stringify(data));//log //console.log(JSON.parse(JSON.stringify(data)));//log //cursor.range ? cursor.restore() : cursor.move(data.result.offset); //cursor.release(); data.keydown = []; //data.hotkey = false; //delete ptr.timer; ptr.timer = null; //cursor.range = false; return true; }; var refresh = function(el) { if (!el.value.length || !config.refresh) return; var ptr = heap[el.GUID]; clearTimeout(ptr.timer); //ptr.cursor.save(); if (!ptr.timer) { ptr.cursor.selectAll(); } onkeyup({ keyCode: 0 }, el, ptr.data, ptr.cursor, ptr.events, ptr); //ptr.cursor.load(); } var onkeydown = function(e, el, data, cursor, events, ptr) { //console.log('onkeydown',e); //data.copypast = false; //if (e.ctrlKey && !hotkey.control[e.keyCode]) { // e.preventDefault(); // return false; //} //cursor.range = false; if (e.ctrlKey) { switch (e.keyCode) { case 90: //Control+Z case 67: //Control+C return false; //bug добавить специфическую обработку, refresh(el) не работает, буква вовращается после case 89: //Control+Y helper.preventDefault(e); return false; case 65: //Control+A cursor.selectAll(); cursor.range = true; break; case 8: //Control+backspace cursor.moveBack = true; break; default: break; } } //switch (e.keyCode) { // case 8: //backspace // //refresh(el); // return false; // default: // break; //} if (data.mousedown) { helper.preventDefault(e); return false; } //TODO сделать список клавишь при нажатии на которые срабатывает refresh if (helper.indexOf(whitelist, e.keyCode) == -1) { if (ptr.timer) refresh(el); return false; } clearTimeout(ptr.timer); //console.log(Date.now()) data.keydown.push({ keyCode: e.keyCode, shiftKey: e.shiftKey, ctrlKey: e.ctrlKey }); //console.log('push', data.keydown[data.keydown.length-1]); if (!ptr.timer) {//!cursor.range 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) { //debugger if (hotkey.shift[lang] && helper.indexOf(hotkey.shift[lang], e.keyCode) != -1) { helper.preventDefault(e); return false; } } return true; }; //var onpaste = function(e, data) { // //data.copypast = true; // data.keydown = []; // 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) { console.log('keydown', e.keyCode); onkeydown(e, el, data, cursor, events, ptr); }; ptr.paste = function(e) { console.log('paste'); data.keydown = []; return true; }; ptr.dragover = function(e) { console.log('dragover'); helper.preventDefault(e); return false; }; ptr.mousedown = function() { console.log('onmousedown'); ptr.data.mousedown = true; refresh(el); }; ptr.mouseup = function() { console.log('onmouseup'); ptr.data.mousedown = false; }; ptr.mouseleave = function() { console.log('onmouseleave'); ptr.data.mousedown = false; }; ptr.blur = function() { console.log('blur'); 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); //TODO избавиться от фокуса в пользу рефреша //el.focus(); 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; // ReSharper disable once MissingHasOwnPropertyInForeach for (var name in events) { events[name].length = 0; delete events[name]; } var props = helper.getOwnPropertyNames(ptr); // ReSharper disable once MissingHasOwnPropertyInForeach for (var prop in props) { delete ptr[prop]; } delete heap[el.GUID]; //TODO dispose instance if (!helper.getOwnPropertyNames(heap).length) { var key = JSON.stringify(config); // ReSharper disable once VariableUsedInInnerScopeBeforeDeclared 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); break; default: break; } 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 (typeof config.lang == 'undefined') return; var lang = config.lang; //delete config.lang; // ReSharper disable once MissingHasOwnPropertyInForeach for (var p in config) { if (!config[p]) continue; if (config[p] == true) { config[p] = {}; } config[p].lang = lang; } }; var mergeConfig = function(a, b) { if (!b) return a; // ReSharper disable once MissingHasOwnPropertyInForeach for (var p in b) { if (typeof a[p] == 'object') { a[p] = mergeConfig(a[p], b[p]); } else { // if(a[p] === true) a[p] = b[p]; } } return a; }; var cache = {}; var types = { 'default': { //язык ru/en lang: '', //интервал обновления interval: 1000 / 24, //1, //отключает полную проверку поля при биндинге потере фиокуса и ещё некоторых случаях refresh: true, //отвечает за перевод одного языка в другой layout: true, //отвечает за разрешённые символы include: { //буквы chars: true, //цифры numbers: true, //символы symbols: true, //произвольный список разрещённых символов special: '' }, //отвечает за запрет символов exclude: { //список запрещённых символов special: '{}[]' }, //Отвечает за правила ввода input: { //lower/upper register: '', //отвечает за игнорирование CapsLock'а capslock: false }, //отвечает за регулярные выражения //remove-chars-after-chars, remove repeat regexulator: { //flag: g - multi search; i - ignore case; m - multi lines g: { ////после любого из символов удалить повторы //'after-char-remove-repeat': special, ////после любого из символов поднять символы //'after-char-upper-char': '\'',//['\'', ''],//'' - начало строки ////после любого из символов удаляет символы //'after-chars-remove-chars': { // '': special //} ////после слова удаляет символы //'after-term-remove-chars': { // 'имя': '0123456789' //}, ////после любого из символов удаляет слово //'after-chars-remove-term': { // '0123456789': '№' //}, ////после слова удаляет слова //'after-term-remove-term': { // 'из': 'в', // 'да': 'нет' //}, ////перед любым из символов удаляет символы ////before поддерживает всё тоже что и after //'before-chars-remove-chars': { // '№': '0123456789' //}, ////реализует тоже самое что и after-chars-upper-char но для кавычек //'after-char-replace-expr': { // '"': { // //регулярное выражение // expr: '[a-za-яё]', // //функция замены // replacer: function(find, c, offset, text, data, noffset) { // return find[0] + find[1].toLocaleUpperCase(); // } // } //} } } }, fio: (function() { var special = ' \'-'; return { //lang: 'ru', 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: { //lang: 'ru', 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 (arguments[i] == 'default') continue; var options = arguments[i]; if (typeof options == 'string') { if (!types[options]) { throw new Error('Can not find liveinput type ' + options); } options = types[options];