From ede687065e08b58ae9d6dbca601788560545ad63 Mon Sep 17 00:00:00 2001 From: Kai Labusch Date: Wed, 11 Dec 2019 15:05:43 +0100 Subject: [PATCH] implement keyboard support --- docs/User_Guide.md | 37 +- keypress.js | 1155 ++++++++++++++++++++++++++++++++++++++++++++ neath.html | 9 +- neath.js | 475 +++++++++++++----- 4 files changed, 1537 insertions(+), 139 deletions(-) create mode 100644 keypress.js diff --git a/docs/User_Guide.md b/docs/User_Guide.md index 6e54a7c..c8ee606 100644 --- a/docs/User_Guide.md +++ b/docs/User_Guide.md @@ -82,7 +82,42 @@ No. TOKEN NE-TAG NE-EMB GND-ID url_id left,right,top,bottom The processing pipeline applied is documented in [Provenance](https://github.com/qurator-spk/neath/blob/master/docs/Provenance.md). We also provide some [Python tools](https://github.com/qurator-spk/neath/tree/master/tools) that help with data wrangling. -#### Navigation +#### Keyboard - Layout + +| Key Combi| Action | +|:---------|:-------------------------------------------| +| Left | Move one cell left | +| Right | Move one cell right | +| Up | Move one row up | +| Down | Move one row down | +| PageDown | Move page down | +| PageUp | Move page up | +|----------|--------------------------------------------| +| s t | Start new sentence in current row | +| m e | Merge current row with row above | +| s p | Create copy of current row one row below | +| 0 | Set POSITION of current row to zero | +|----------|--------------------------------------------| +| backspace| Set NE-TAG / NE-EMB to "O" | +| b p | Set NE-TAG / NE-EMB to "B-PER" | +| b l | Set NE-TAG / NE-EMB to "B-LOC" | +| b o | Set NE-TAG / NE-EMB to "B-ORG" | +| b w | Set NE-TAG / NE-EMB to "B-WORK" | +| b c | Set NE-TAG / NE-EMB to "B-CONF" | +| b e | Set NE-TAG / NE-EMB to "B-EVT" | +| b t | Set NE-TAG / NE-EMB to "B-TODO" | +| i p | Set NE-TAG / NE-EMB to "I-PER" | +| i l | Set NE-TAG / NE-EMB to "I-LOC" | +| i o | Set NE-TAG / NE-EMB to "I-ORG" | +| i w | Set NE-TAG / NE-EMB to "I-WORK" | +| i c | Set NE-TAG / NE-EMB to "I-CONF" | +| i e | Set NE-TAG / NE-EMB to "I-EVT" | +| i t | Set NE-TAG / NE-EMB to "I-TODO" | +|----------|--------------------------------------------| +| enter | Edit TOKEN or GND-ID | +|----------|--------------------------------------------| + +#### Mouse-Navigation * use mouse wheel to scroll up and down * use navigation `<<` and `>>` to move faster #### Image Support diff --git a/keypress.js b/keypress.js new file mode 100644 index 0000000..16fe270 --- /dev/null +++ b/keypress.js @@ -0,0 +1,1155 @@ +// Generated by CoffeeScript 1.8.0 + +/* +Copyright 2014 David Mauro + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Keypress is a robust keyboard input capturing Javascript utility +focused on input for games. + +version 2.1.5 + */ + + +/* +Combo options available and their defaults: + keys : [] - An array of the keys pressed together to activate combo. + count : 0 - The number of times a counting combo has been pressed. Reset on release. + is_unordered : false - Unless this is set to true, the keys can be pressed down in any order. + is_counting : false - Makes this a counting combo (see documentation). + is_exclusive : false - This combo will replace other exclusive combos when true. + is_solitary : false - This combo will only fire if ONLY it's keys are pressed down. + is_sequence : false - Rather than a key combo, this is an ordered key sequence. + prevent_default : false - Prevent default behavior for all component key keypresses. + prevent_repeat : false - Prevent the combo from repeating when keydown is held. + normalize_caps_lock : false - Do not allow turning caps lock on to prevent combos from being activated. + on_keydown : null - A function that is called when the combo is pressed. + on_keyup : null - A function that is called when the combo is released. + on_release : null - A function that is called when all keys in the combo are released. + this : undefined - Defines the scope for your callback functions. + */ + +(function() { + var Combo, keypress, _change_keycodes_by_browser, _compare_arrays, _compare_arrays_sorted, _convert_key_to_readable, _convert_to_shifted_key, _decide_meta_key, _factory_defaults, _filter_array, _index_of_in_array, _is_array_in_array, _is_array_in_array_sorted, _key_is_valid, _keycode_alternate_names, _keycode_dictionary, _keycode_shifted_keys, _log_error, _metakey, _modifier_event_mapping, _modifier_keys, _validate_combo, + __hasProp = {}.hasOwnProperty, + __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; + + _factory_defaults = { + is_unordered: false, + is_counting: false, + is_exclusive: false, + is_solitary: false, + prevent_default: false, + prevent_repeat: false, + normalize_caps_lock: false + }; + + _modifier_keys = ["meta", "alt", "option", "ctrl", "shift", "cmd"]; + + _metakey = "ctrl"; + + keypress = {}; + + keypress.debug = false; + + Combo = (function() { + function Combo(dictionary) { + var property, value; + for (property in dictionary) { + if (!__hasProp.call(dictionary, property)) continue; + value = dictionary[property]; + if (value !== false) { + this[property] = value; + } + } + this.keys = this.keys || []; + this.count = this.count || 0; + } + + Combo.prototype.allows_key_repeat = function() { + return !this.prevent_repeat && typeof this.on_keydown === "function"; + }; + + Combo.prototype.reset = function() { + this.count = 0; + return this.keyup_fired = null; + }; + + return Combo; + + })(); + + keypress.Listener = (function() { + function Listener(element, defaults) { + var attach_handler, property, value; + if ((typeof jQuery !== "undefined" && jQuery !== null) && element instanceof jQuery) { + if (element.length !== 1) { + _log_error("Warning: your jQuery selector should have exactly one object."); + } + element = element[0]; + } + this.should_suppress_event_defaults = false; + this.should_force_event_defaults = false; + this.sequence_delay = 800; + this._registered_combos = []; + this._keys_down = []; + this._active_combos = []; + this._sequence = []; + this._sequence_timer = null; + this._prevent_capture = false; + this._defaults = defaults || {}; + for (property in _factory_defaults) { + if (!__hasProp.call(_factory_defaults, property)) continue; + value = _factory_defaults[property]; + this._defaults[property] = this._defaults[property] || value; + } + this.element = element || document.body; + attach_handler = function(target, event, handler) { + if (target.addEventListener) { + target.addEventListener(event, handler); + } else if (target.attachEvent) { + target.attachEvent("on" + event, handler); + } + return handler; + }; + this.keydown_event = attach_handler(this.element, "keydown", (function(_this) { + return function(e) { + e = e || window.event; + _this._receive_input(e, true); + return _this._bug_catcher(e); + }; + })(this)); + this.keyup_event = attach_handler(this.element, "keyup", (function(_this) { + return function(e) { + e = e || window.event; + return _this._receive_input(e, false); + }; + })(this)); + this.blur_event = attach_handler(window, "blur", (function(_this) { + return function() { + var key, _i, _len, _ref; + _ref = _this._keys_down; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + key = _ref[_i]; + _this._key_up(key, {}); + } + return _this._keys_down = []; + }; + })(this)); + } + + Listener.prototype.destroy = function() { + var remove_handler; + remove_handler = function(target, event, handler) { + if (target.removeEventListener != null) { + return target.removeEventListener(event, handler); + } else if (target.removeEvent != null) { + return target.removeEvent("on" + event, handler); + } + }; + remove_handler(this.element, "keydown", this.keydown_event); + remove_handler(this.element, "keyup", this.keyup_event); + return remove_handler(window, "blur", this.blur_event); + }; + + Listener.prototype._bug_catcher = function(e) { + var _ref, _ref1; + if (_metakey === "cmd" && __indexOf.call(this._keys_down, "cmd") >= 0 && ((_ref = _convert_key_to_readable((_ref1 = e.keyCode) != null ? _ref1 : e.key)) !== "cmd" && _ref !== "shift" && _ref !== "alt" && _ref !== "caps" && _ref !== "tab")) { + return this._receive_input(e, false); + } + }; + + Listener.prototype._cmd_bug_check = function(combo_keys) { + if (_metakey === "cmd" && __indexOf.call(this._keys_down, "cmd") >= 0 && __indexOf.call(combo_keys, "cmd") < 0) { + return false; + } + return true; + }; + + Listener.prototype._prevent_default = function(e, should_prevent) { + if ((should_prevent || this.should_suppress_event_defaults) && !this.should_force_event_defaults) { + if (e.preventDefault) { + e.preventDefault(); + } else { + e.returnValue = false; + } + if (e.stopPropagation) { + return e.stopPropagation(); + } + } + }; + + Listener.prototype._get_active_combos = function(key) { + var active_combos, keys_down; + active_combos = []; + keys_down = _filter_array(this._keys_down, function(down_key) { + return down_key !== key; + }); + keys_down.push(key); + this._match_combo_arrays(keys_down, (function(_this) { + return function(match) { + if (_this._cmd_bug_check(match.keys)) { + return active_combos.push(match); + } + }; + })(this)); + this._fuzzy_match_combo_arrays(keys_down, (function(_this) { + return function(match) { + if (__indexOf.call(active_combos, match) >= 0) { + return; + } + if (!(match.is_solitary || !_this._cmd_bug_check(match.keys))) { + return active_combos.push(match); + } + }; + })(this)); + return active_combos; + }; + + Listener.prototype._get_potential_combos = function(key) { + var combo, potentials, _i, _len, _ref; + potentials = []; + _ref = this._registered_combos; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + combo = _ref[_i]; + if (combo.is_sequence) { + continue; + } + if (__indexOf.call(combo.keys, key) >= 0 && this._cmd_bug_check(combo.keys)) { + potentials.push(combo); + } + } + return potentials; + }; + + Listener.prototype._add_to_active_combos = function(combo) { + var active_combo, active_key, active_keys, already_replaced, combo_key, i, should_prepend, should_replace, _i, _j, _k, _len, _len1, _ref, _ref1; + should_replace = false; + should_prepend = true; + already_replaced = false; + if (__indexOf.call(this._active_combos, combo) >= 0) { + return true; + } else if (this._active_combos.length) { + for (i = _i = 0, _ref = this._active_combos.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { + active_combo = this._active_combos[i]; + if (!(active_combo && active_combo.is_exclusive && combo.is_exclusive)) { + continue; + } + active_keys = active_combo.keys; + if (!should_replace) { + for (_j = 0, _len = active_keys.length; _j < _len; _j++) { + active_key = active_keys[_j]; + should_replace = true; + if (__indexOf.call(combo.keys, active_key) < 0) { + should_replace = false; + break; + } + } + } + if (should_prepend && !should_replace) { + _ref1 = combo.keys; + for (_k = 0, _len1 = _ref1.length; _k < _len1; _k++) { + combo_key = _ref1[_k]; + should_prepend = false; + if (__indexOf.call(active_keys, combo_key) < 0) { + should_prepend = true; + break; + } + } + } + if (should_replace) { + if (already_replaced) { + active_combo = this._active_combos.splice(i, 1)[0]; + if (active_combo != null) { + active_combo.reset(); + } + } else { + active_combo = this._active_combos.splice(i, 1, combo)[0]; + if (active_combo != null) { + active_combo.reset(); + } + already_replaced = true; + } + should_prepend = false; + } + } + } + if (should_prepend) { + this._active_combos.unshift(combo); + } + return should_replace || should_prepend; + }; + + Listener.prototype._remove_from_active_combos = function(combo) { + var active_combo, i, _i, _ref; + for (i = _i = 0, _ref = this._active_combos.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { + active_combo = this._active_combos[i]; + if (active_combo === combo) { + combo = this._active_combos.splice(i, 1)[0]; + combo.reset(); + break; + } + } + }; + + Listener.prototype._get_possible_sequences = function() { + var combo, i, j, match, matches, sequence, _i, _j, _k, _len, _ref, _ref1, _ref2; + matches = []; + _ref = this._registered_combos; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + combo = _ref[_i]; + for (j = _j = 1, _ref1 = this._sequence.length; 1 <= _ref1 ? _j <= _ref1 : _j >= _ref1; j = 1 <= _ref1 ? ++_j : --_j) { + sequence = this._sequence.slice(-j); + if (!combo.is_sequence) { + continue; + } + if (__indexOf.call(combo.keys, "shift") < 0) { + sequence = _filter_array(sequence, function(key) { + return key !== "shift"; + }); + if (!sequence.length) { + continue; + } + } + for (i = _k = 0, _ref2 = sequence.length; 0 <= _ref2 ? _k < _ref2 : _k > _ref2; i = 0 <= _ref2 ? ++_k : --_k) { + if (combo.keys[i] === sequence[i]) { + match = true; + } else { + match = false; + break; + } + } + if (match) { + matches.push(combo); + } + } + } + return matches; + }; + + Listener.prototype._add_key_to_sequence = function(key, e) { + var combo, sequence_combos, _i, _len; + this._sequence.push(key); + sequence_combos = this._get_possible_sequences(); + if (sequence_combos.length) { + for (_i = 0, _len = sequence_combos.length; _i < _len; _i++) { + combo = sequence_combos[_i]; + this._prevent_default(e, combo.prevent_default); + } + if (this._sequence_timer) { + clearTimeout(this._sequence_timer); + } + if (this.sequence_delay > -1) { + this._sequence_timer = setTimeout((function(_this) { + return function() { + return _this._sequence = []; + }; + })(this), this.sequence_delay); + } + } else { + this._sequence = []; + } + }; + + Listener.prototype._get_sequence = function(key) { + var combo, i, j, match, seq_key, sequence, _i, _j, _k, _len, _ref, _ref1, _ref2; + _ref = this._registered_combos; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + combo = _ref[_i]; + if (!combo.is_sequence) { + continue; + } + for (j = _j = 1, _ref1 = this._sequence.length; 1 <= _ref1 ? _j <= _ref1 : _j >= _ref1; j = 1 <= _ref1 ? ++_j : --_j) { + sequence = (_filter_array(this._sequence, function(seq_key) { + if (__indexOf.call(combo.keys, "shift") >= 0) { + return true; + } + return seq_key !== "shift"; + })).slice(-j); + if (combo.keys.length !== sequence.length) { + continue; + } + for (i = _k = 0, _ref2 = sequence.length; 0 <= _ref2 ? _k < _ref2 : _k > _ref2; i = 0 <= _ref2 ? ++_k : --_k) { + seq_key = sequence[i]; + if (__indexOf.call(combo.keys, "shift") < 0) { + if (seq_key === "shift") { + continue; + } + } + if (key === "shift" && __indexOf.call(combo.keys, "shift") < 0) { + continue; + } + if (combo.keys[i] === seq_key) { + match = true; + } else { + match = false; + break; + } + } + } + if (match) { + if (combo.is_exclusive) { + this._sequence = []; + } + return combo; + } + } + return false; + }; + + Listener.prototype._receive_input = function(e, is_keydown) { + var key, _ref; + if (this._prevent_capture) { + if (this._keys_down.length) { + this._keys_down = []; + } + return; + } + key = _convert_key_to_readable((_ref = e.keyCode) != null ? _ref : e.key); + if (!is_keydown && !this._keys_down.length && (key === "alt" || key === _metakey)) { + return; + } + if (!key) { + return; + } + if (is_keydown) { + return this._key_down(key, e); + } else { + return this._key_up(key, e); + } + }; + + Listener.prototype._fire = function(event, combo, key_event, is_autorepeat) { + if (typeof combo["on_" + event] === "function") { + this._prevent_default(key_event, combo["on_" + event].call(combo["this"], key_event, combo.count, is_autorepeat) !== true); + } + if (event === "release") { + combo.count = 0; + } + if (event === "keyup") { + return combo.keyup_fired = true; + } + }; + + Listener.prototype._match_combo_arrays = function(potential_match, match_handler) { + var combo_potential_match, source_combo, _i, _len, _ref; + _ref = this._registered_combos; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + source_combo = _ref[_i]; + combo_potential_match = potential_match.slice(0); + if (source_combo.normalize_caps_lock && __indexOf.call(combo_potential_match, "caps") >= 0) { + combo_potential_match.splice(combo_potential_match.indexOf("caps"), 1); + } + if ((!source_combo.is_unordered && _compare_arrays_sorted(combo_potential_match, source_combo.keys)) || (source_combo.is_unordered && _compare_arrays(combo_potential_match, source_combo.keys))) { + match_handler(source_combo); + } + } + }; + + Listener.prototype._fuzzy_match_combo_arrays = function(potential_match, match_handler) { + var source_combo, _i, _len, _ref; + _ref = this._registered_combos; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + source_combo = _ref[_i]; + if ((!source_combo.is_unordered && _is_array_in_array_sorted(source_combo.keys, potential_match)) || (source_combo.is_unordered && _is_array_in_array(source_combo.keys, potential_match))) { + match_handler(source_combo); + } + } + }; + + Listener.prototype._keys_remain = function(combo) { + var key, keys_remain, _i, _len, _ref; + _ref = combo.keys; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + key = _ref[_i]; + if (__indexOf.call(this._keys_down, key) >= 0) { + keys_remain = true; + break; + } + } + return keys_remain; + }; + + Listener.prototype._key_down = function(key, e) { + var combo, combos, event_mod, i, mod, potential, potential_combos, sequence_combo, shifted_key, _i, _j, _k, _len, _len1, _ref; + shifted_key = _convert_to_shifted_key(key, e); + if (shifted_key) { + key = shifted_key; + } + this._add_key_to_sequence(key, e); + sequence_combo = this._get_sequence(key); + if (sequence_combo) { + this._fire("keydown", sequence_combo, e); + } + for (mod in _modifier_event_mapping) { + event_mod = _modifier_event_mapping[mod]; + if (!e[event_mod]) { + continue; + } + if (mod === key || __indexOf.call(this._keys_down, mod) >= 0) { + continue; + } + this._keys_down.push(mod); + } + for (mod in _modifier_event_mapping) { + event_mod = _modifier_event_mapping[mod]; + if (mod === key) { + continue; + } + if (__indexOf.call(this._keys_down, mod) >= 0 && !e[event_mod]) { + if (mod === "cmd" && _metakey !== "cmd") { + continue; + } + for (i = _i = 0, _ref = this._keys_down.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { + if (this._keys_down[i] === mod) { + this._keys_down.splice(i, 1); + } + } + } + } + combos = this._get_active_combos(key); + potential_combos = this._get_potential_combos(key); + for (_j = 0, _len = combos.length; _j < _len; _j++) { + combo = combos[_j]; + this._handle_combo_down(combo, potential_combos, key, e); + } + if (potential_combos.length) { + for (_k = 0, _len1 = potential_combos.length; _k < _len1; _k++) { + potential = potential_combos[_k]; + this._prevent_default(e, potential.prevent_default); + } + } + if (__indexOf.call(this._keys_down, key) < 0) { + this._keys_down.push(key); + } + }; + + Listener.prototype._handle_combo_down = function(combo, potential_combos, key, e) { + var is_autorepeat, is_other_exclusive, potential_combo, result, _i, _len; + if (__indexOf.call(combo.keys, key) < 0) { + return false; + } + this._prevent_default(e, combo && combo.prevent_default); + is_autorepeat = false; + if (__indexOf.call(this._keys_down, key) >= 0) { + is_autorepeat = true; + if (!combo.allows_key_repeat()) { + return false; + } + } + result = this._add_to_active_combos(combo, key); + combo.keyup_fired = false; + is_other_exclusive = false; + if (combo.is_exclusive) { + for (_i = 0, _len = potential_combos.length; _i < _len; _i++) { + potential_combo = potential_combos[_i]; + if (potential_combo.is_exclusive && potential_combo.keys.length > combo.keys.length) { + is_other_exclusive = true; + break; + } + } + } + if (!is_other_exclusive) { + if (combo.is_counting && typeof combo.on_keydown === "function") { + combo.count += 1; + } + if (result) { + return this._fire("keydown", combo, e, is_autorepeat); + } + } + }; + + Listener.prototype._key_up = function(key, e) { + var active_combo, active_combos_length, combo, combos, i, sequence_combo, shifted_key, unshifted_key, _i, _j, _k, _l, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3; + unshifted_key = key; + shifted_key = _convert_to_shifted_key(key, e); + if (shifted_key) { + key = shifted_key; + } + shifted_key = _keycode_shifted_keys[unshifted_key]; + if (e.shiftKey) { + if (!(shifted_key && __indexOf.call(this._keys_down, shifted_key) >= 0)) { + key = unshifted_key; + } + } else { + if (!(unshifted_key && __indexOf.call(this._keys_down, unshifted_key) >= 0)) { + key = shifted_key; + } + } + sequence_combo = this._get_sequence(key); + if (sequence_combo) { + this._fire("keyup", sequence_combo, e); + } + if (__indexOf.call(this._keys_down, key) < 0) { + return false; + } + for (i = _i = 0, _ref = this._keys_down.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { + if ((_ref1 = this._keys_down[i]) === key || _ref1 === shifted_key || _ref1 === unshifted_key) { + this._keys_down.splice(i, 1); + break; + } + } + active_combos_length = this._active_combos.length; + combos = []; + _ref2 = this._active_combos; + for (_j = 0, _len = _ref2.length; _j < _len; _j++) { + active_combo = _ref2[_j]; + if (__indexOf.call(active_combo.keys, key) >= 0) { + combos.push(active_combo); + } + } + for (_k = 0, _len1 = combos.length; _k < _len1; _k++) { + combo = combos[_k]; + this._handle_combo_up(combo, e, key); + } + if (active_combos_length > 1) { + _ref3 = this._active_combos; + for (_l = 0, _len2 = _ref3.length; _l < _len2; _l++) { + active_combo = _ref3[_l]; + if (active_combo === void 0 || __indexOf.call(combos, active_combo) >= 0) { + continue; + } + if (!this._keys_remain(active_combo)) { + this._remove_from_active_combos(active_combo); + } + } + } + }; + + Listener.prototype._handle_combo_up = function(combo, e, key) { + var keys_down, keys_remaining; + this._prevent_default(e, combo && combo.prevent_default); + keys_remaining = this._keys_remain(combo); + if (!combo.keyup_fired) { + keys_down = this._keys_down.slice(); + keys_down.push(key); + if (!combo.is_solitary || _compare_arrays(keys_down, combo.keys)) { + this._fire("keyup", combo, e); + if (combo.is_counting && typeof combo.on_keyup === "function" && typeof combo.on_keydown !== "function") { + combo.count += 1; + } + } + } + if (!keys_remaining) { + this._fire("release", combo, e); + this._remove_from_active_combos(combo); + } + }; + + Listener.prototype.simple_combo = function(keys, callback) { + return this.register_combo({ + keys: keys, + on_keydown: callback + }); + }; + + Listener.prototype.counting_combo = function(keys, count_callback) { + return this.register_combo({ + keys: keys, + is_counting: true, + is_unordered: false, + on_keydown: count_callback + }); + }; + + Listener.prototype.sequence_combo = function(keys, callback) { + return this.register_combo({ + keys: keys, + on_keydown: callback, + is_sequence: true, + is_exclusive: true + }); + }; + + Listener.prototype.register_combo = function(combo_dictionary) { + var combo, property, value, _ref; + if (typeof combo_dictionary["keys"] === "string") { + combo_dictionary["keys"] = combo_dictionary["keys"].split(" "); + } + _ref = this._defaults; + for (property in _ref) { + if (!__hasProp.call(_ref, property)) continue; + value = _ref[property]; + if (combo_dictionary[property] === void 0) { + combo_dictionary[property] = value; + } + } + combo = new Combo(combo_dictionary); + if (_validate_combo(combo)) { + this._registered_combos.push(combo); + return combo; + } + }; + + Listener.prototype.register_many = function(combo_array) { + var combo, _i, _len, _results; + _results = []; + for (_i = 0, _len = combo_array.length; _i < _len; _i++) { + combo = combo_array[_i]; + _results.push(this.register_combo(combo)); + } + return _results; + }; + + Listener.prototype.unregister_combo = function(keys_or_combo) { + var combo, i, unregister_combo, _i, _j, _len, _ref, _ref1, _results; + if (!keys_or_combo) { + return false; + } + unregister_combo = (function(_this) { + return function(combo) { + var i, _i, _ref, _results; + _results = []; + for (i = _i = 0, _ref = _this._registered_combos.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { + if (combo === _this._registered_combos[i]) { + _this._registered_combos.splice(i, 1); + break; + } else { + _results.push(void 0); + } + } + return _results; + }; + })(this); + if (keys_or_combo instanceof Combo) { + return unregister_combo(keys_or_combo); + } else { + if (typeof keys_or_combo === "string") { + keys_or_combo = keys_or_combo.split(" "); + for (i = _i = 0, _ref = keys_or_combo.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { + if (keys_or_combo[i] === "meta") { + keys_or_combo[i] = _metakey; + } + } + } + _ref1 = this._registered_combos; + _results = []; + for (_j = 0, _len = _ref1.length; _j < _len; _j++) { + combo = _ref1[_j]; + if (combo == null) { + continue; + } + if ((combo.is_unordered && _compare_arrays(keys_or_combo, combo.keys)) || (!combo.is_unordered && _compare_arrays_sorted(keys_or_combo, combo.keys))) { + _results.push(unregister_combo(combo)); + } else { + _results.push(void 0); + } + } + return _results; + } + }; + + Listener.prototype.unregister_many = function(combo_array) { + var combo, _i, _len, _results; + _results = []; + for (_i = 0, _len = combo_array.length; _i < _len; _i++) { + combo = combo_array[_i]; + _results.push(this.unregister_combo(combo)); + } + return _results; + }; + + Listener.prototype.get_registered_combos = function() { + return this._registered_combos; + }; + + Listener.prototype.reset = function() { + return this._registered_combos = []; + }; + + Listener.prototype.listen = function() { + return this._prevent_capture = false; + }; + + Listener.prototype.stop_listening = function() { + return this._prevent_capture = true; + }; + + Listener.prototype.get_meta_key = function() { + return _metakey; + }; + + return Listener; + + })(); + + _decide_meta_key = function() { + if (navigator.userAgent.indexOf("Mac OS X") !== -1) { + _metakey = "cmd"; + } + }; + + _change_keycodes_by_browser = function() { + if (navigator.userAgent.indexOf("Opera") !== -1) { + _keycode_dictionary["17"] = "cmd"; + } + }; + + _convert_key_to_readable = function(k) { + return _keycode_dictionary[k]; + }; + + _filter_array = function(array, callback) { + var element; + if (array.filter) { + return array.filter(callback); + } else { + return (function() { + var _i, _len, _results; + _results = []; + for (_i = 0, _len = array.length; _i < _len; _i++) { + element = array[_i]; + if (callback(element)) { + _results.push(element); + } + } + return _results; + })(); + } + }; + + _compare_arrays = function(a1, a2) { + var item, _i, _len; + if (a1.length !== a2.length) { + return false; + } + for (_i = 0, _len = a1.length; _i < _len; _i++) { + item = a1[_i]; + if (__indexOf.call(a2, item) >= 0) { + continue; + } + return false; + } + return true; + }; + + _compare_arrays_sorted = function(a1, a2) { + var i, _i, _ref; + if (a1.length !== a2.length) { + return false; + } + for (i = _i = 0, _ref = a1.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { + if (a1[i] !== a2[i]) { + return false; + } + } + return true; + }; + + _is_array_in_array = function(a1, a2) { + var item, _i, _len; + for (_i = 0, _len = a1.length; _i < _len; _i++) { + item = a1[_i]; + if (__indexOf.call(a2, item) < 0) { + return false; + } + } + return true; + }; + + _index_of_in_array = Array.prototype.indexOf || function(a, item) { + var i, _i, _ref; + for (i = _i = 0, _ref = a.length; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) { + if (a[i] === item) { + return i; + } + } + return -1; + }; + + _is_array_in_array_sorted = function(a1, a2) { + var index, item, prev, _i, _len; + prev = 0; + for (_i = 0, _len = a1.length; _i < _len; _i++) { + item = a1[_i]; + index = _index_of_in_array.call(a2, item); + if (index >= prev) { + prev = index; + } else { + return false; + } + } + return true; + }; + + _log_error = function() { + if (keypress.debug) { + return console.log.apply(console, arguments); + } + }; + + _key_is_valid = function(key) { + var valid, valid_key, _; + valid = false; + for (_ in _keycode_dictionary) { + valid_key = _keycode_dictionary[_]; + if (key === valid_key) { + valid = true; + break; + } + } + if (!valid) { + for (_ in _keycode_shifted_keys) { + valid_key = _keycode_shifted_keys[_]; + if (key === valid_key) { + valid = true; + break; + } + } + } + return valid; + }; + + _validate_combo = function(combo) { + var alt_name, i, key, mod_key, non_modifier_keys, property, validated, value, _i, _j, _k, _len, _len1, _ref, _ref1; + validated = true; + if (!combo.keys.length) { + _log_error("You're trying to bind a combo with no keys:", combo); + } + for (i = _i = 0, _ref = combo.keys.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { + key = combo.keys[i]; + alt_name = _keycode_alternate_names[key]; + if (alt_name) { + key = combo.keys[i] = alt_name; + } + if (key === "meta") { + combo.keys.splice(i, 1, _metakey); + } + if (key === "cmd") { + _log_error("Warning: use the \"meta\" key rather than \"cmd\" for Windows compatibility"); + } + } + _ref1 = combo.keys; + for (_j = 0, _len = _ref1.length; _j < _len; _j++) { + key = _ref1[_j]; + if (!_key_is_valid(key)) { + _log_error("Do not recognize the key \"" + key + "\""); + validated = false; + } + } + if (__indexOf.call(combo.keys, "meta") >= 0 || __indexOf.call(combo.keys, "cmd") >= 0) { + non_modifier_keys = combo.keys.slice(); + for (_k = 0, _len1 = _modifier_keys.length; _k < _len1; _k++) { + mod_key = _modifier_keys[_k]; + if ((i = _index_of_in_array.call(non_modifier_keys, mod_key)) > -1) { + non_modifier_keys.splice(i, 1); + } + } + if (non_modifier_keys.length > 1) { + _log_error("META and CMD key combos cannot have more than 1 non-modifier keys", combo, non_modifier_keys); + validated = false; + } + } + for (property in combo) { + value = combo[property]; + if (_factory_defaults[property] === "undefined") { + _log_error("The property " + property + " is not a valid combo property. Your combo has still been registered."); + } + } + return validated; + }; + + _convert_to_shifted_key = function(key, e) { + var k; + if (!e.shiftKey) { + return false; + } + k = _keycode_shifted_keys[key]; + if (k != null) { + return k; + } + return false; + }; + + _modifier_event_mapping = { + "cmd": "metaKey", + "ctrl": "ctrlKey", + "shift": "shiftKey", + "alt": "altKey" + }; + + _keycode_alternate_names = { + "escape": "esc", + "control": "ctrl", + "command": "cmd", + "break": "pause", + "windows": "cmd", + "option": "alt", + "caps_lock": "caps", + "apostrophe": "\'", + "semicolon": ";", + "tilde": "~", + "accent": "`", + "scroll_lock": "scroll", + "num_lock": "num" + }; + + _keycode_shifted_keys = { + "/": "?", + ".": ">", + ",": "<", + "\'": "\"", + ";": ":", + "[": "{", + "]": "}", + "\\": "|", + "`": "~", + "=": "+", + "-": "_", + "1": "!", + "2": "@", + "3": "#", + "4": "$", + "5": "%", + "6": "^", + "7": "&", + "8": "*", + "9": "(", + "0": ")" + }; + + _keycode_dictionary = { + 0: "\\", + 8: "backspace", + 9: "tab", + 12: "num", + 13: "enter", + 16: "shift", + 17: "ctrl", + 18: "alt", + 19: "pause", + 20: "caps", + 27: "esc", + 32: "space", + 33: "pageup", + 34: "pagedown", + 35: "end", + 36: "home", + 37: "left", + 38: "up", + 39: "right", + 40: "down", + 44: "print", + 45: "insert", + 46: "delete", + 48: "0", + 49: "1", + 50: "2", + 51: "3", + 52: "4", + 53: "5", + 54: "6", + 55: "7", + 56: "8", + 57: "9", + 65: "a", + 66: "b", + 67: "c", + 68: "d", + 69: "e", + 70: "f", + 71: "g", + 72: "h", + 73: "i", + 74: "j", + 75: "k", + 76: "l", + 77: "m", + 78: "n", + 79: "o", + 80: "p", + 81: "q", + 82: "r", + 83: "s", + 84: "t", + 85: "u", + 86: "v", + 87: "w", + 88: "x", + 89: "y", + 90: "z", + 91: "cmd", + 92: "cmd", + 93: "cmd", + 96: "num_0", + 97: "num_1", + 98: "num_2", + 99: "num_3", + 100: "num_4", + 101: "num_5", + 102: "num_6", + 103: "num_7", + 104: "num_8", + 105: "num_9", + 106: "num_multiply", + 107: "num_add", + 108: "num_enter", + 109: "num_subtract", + 110: "num_decimal", + 111: "num_divide", + 112: "f1", + 113: "f2", + 114: "f3", + 115: "f4", + 116: "f5", + 117: "f6", + 118: "f7", + 119: "f8", + 120: "f9", + 121: "f10", + 122: "f11", + 123: "f12", + 124: "print", + 144: "num", + 145: "scroll", + 186: ";", + 187: "=", + 188: ",", + 189: "-", + 190: ".", + 191: "/", + 192: "`", + 219: "[", + 220: "\\", + 221: "]", + 222: "\'", + 223: "`", + 224: "cmd", + 225: "alt", + 57392: "ctrl", + 63289: "num", + 59: ";", + 61: "=", + 173: "-" + }; + + keypress._keycode_dictionary = _keycode_dictionary; + + keypress._is_array_in_array_sorted = _is_array_in_array_sorted; + + _decide_meta_key(); + + _change_keycodes_by_browser(); + + if (typeof define === "function" && define.amd) { + define([], function() { + return keypress; + }); + } else if (typeof exports !== "undefined" && exports !== null) { + exports.keypress = keypress; + } else { + window.keypress = keypress; + } + +}).call(this); diff --git a/neath.html b/neath.html index c628ee1..0cff8f3 100644 --- a/neath.html +++ b/neath.html @@ -14,8 +14,8 @@ body{font-family:Verdana;font-size:16px} table{table-layout:fixed;width:100%;text-align:center} th{background-color:lightgray} - .editable:hover{background-color:yellow} - tr:hover{background-color:whitesmoke} + .editable:focus{background-color:yellow} + tr:focus-within{background-color:whitesmoke} .accordion:hover .accordion-item:hover .accordion-item-content, .accordion .accordion-item--default .accordion-item-content{height:10.5em} @@ -55,8 +55,8 @@
-

neath: named entity annotation tool in html

- User Guide | Annotation Guidelines | Issues
+

neath: named entity annotation tool in html

+ User Guide | Annotation Guidelines | Issues
@@ -88,6 +88,7 @@ + diff --git a/neath.js b/neath.js index 5474fa5..7069dd2 100644 --- a/neath.js +++ b/neath.js @@ -1,11 +1,11 @@ function loadFile(evt, onComplete) { - var file = evt.target.files[0]; + let file = evt.target.files[0]; - var urls = null; + let urls = null; - var reader = new FileReader(); + let reader = new FileReader(); reader.onload = function(event) { @@ -13,7 +13,7 @@ function loadFile(evt, onComplete) { let link_detector = /(https?:\/\/[^\s]+)/g; let lines = event.target.result.split(/\r\n|\n/); - for(var i = 0; i < lines.length; i++){ + for(let i = 0; i < lines.length; i++){ let line = lines[i]; @@ -52,8 +52,12 @@ function setupInterface(data, file, urls) { let startIndex=0; let endIndex=displayRows; + let do_not_display = new Set(['url_id', 'left', 'right', 'top', 'bottom']); + let tagClasses = 'ner_per ner_loc ner_org ner_work ner_conf ner_evt ner_todo'; + let has_changes = false; - var save_timeout = null; + + let save_timeout = null; function notifyChange() { if (save_timeout != null) clearTimeout(save_timeout); @@ -104,12 +108,6 @@ function setupInterface(data, file, urls) { save_timeout = setTimeout(checker, 3000); }; - // public interface - var that = - { - hasChanges: function () { return has_changes; } - }; - function updatePreview(nRow) { if (urls == null) return; @@ -139,8 +137,6 @@ function setupInterface(data, file, urls) { img_url = img_url.replace('width', width.toString()); img_url = img_url.replace('height', height.toString()); - //console.log(img_url) - $("#preview").attr("src", img_url); img_url = urls[data.data[nRow]['url_id']]; @@ -165,6 +161,8 @@ function setupInterface(data, file, urls) { } function colorCode() { + $(".editable").removeClass(tagClasses); + $("#table td:contains('B-PER')").addClass('ner_per'); $("#table td:contains('I-PER')").addClass('ner_per'); $("#table td:contains('B-LOC')").addClass('ner_loc'); @@ -183,18 +181,6 @@ function setupInterface(data, file, urls) { let editingTd; - function gotoLocation(evt) { - - if (urls == null) return; - if (editingTd != null) return; - - let tableInfo = $(evt.target).data('tableInfo'); - - let nRow = tableInfo.nRow; - - updatePreview(nRow) - } - function makeTdEditable(td) { editingTd = { @@ -219,7 +205,12 @@ function setupInterface(data, file, urls) { $(td).html(editingTd.data); } + let listener = new window.keypress.Listener(td); + + listener.simple_combo("enter", function() { td.click(); }); + editingTd = null; + $(td).focus(); } }; @@ -250,85 +241,89 @@ function setupInterface(data, file, urls) { function(evt) { editingTd.finish(editingTd.elem, false); }); + + let listener = new window.keypress.Listener(td); + + listener.simple_combo('enter', function() { $('#edit-ok').click(); } ); + listener.simple_combo('esc', function() { $('#edit-cancel').click(); } ); } - function makeLineSplitMerge(td) { + function tableEditAction(nRow, action) { - editingTd = { - elem: td, - data: td.innerHTML, - tokenizer_action: null, - finish: function(td, isOk) { - $(td).html(editingTd.data); - $(td).addClass('editable'); + if (action == null) return; - if (editingTd.tokenizer_action == null) { - editingTd = null; - return; - } + if (action.includes('merge')) { - let tableInfo = $(td).data('tableInfo'); + if (nRow < 1) return; - if (editingTd.tokenizer_action.includes('merge')) { + let pos = nRow + 1; + word_pos = data.data[nRow - 1]['No.'] + 1 + while((pos < data.data.length) && (data.data[pos]['No.'] > 1)) { + data.data[pos]['No.'] = word_pos; + pos++; + word_pos++; + } - if (tableInfo.nRow < 1) { - editingTd = null; - return; - } + data.data[nRow - 1]['TOKEN'] += data.data[nRow]['TOKEN']; - let pos = tableInfo.nRow + 1; - word_pos = data.data[tableInfo.nRow - 1]['No.'] + 1 - while((pos < data.data.length) && (data.data[pos]['No.'] > 1)) { - data.data[pos]['No.'] = word_pos; - pos++; - word_pos++; - } + data.data.splice(nRow, 1); - data.data[tableInfo.nRow - 1]['TOKEN'] += data.data[tableInfo.nRow]['TOKEN']; + notifyChange(); + } + else if (action.includes('split')) { - data.data.splice(tableInfo.nRow, 1); + data.data.splice(nRow, 0, JSON.parse(JSON.stringify(data.data[nRow]))); + data.data[nRow + 1]['No.'] += 1; - notifyChange(); - } - else if (editingTd.tokenizer_action.includes('split')) { + let pos = nRow + 2; + while ((pos < data.data.length) && (data.data[pos]['No.'] > 1)) { + data.data[pos]['No.']++; + pos++; + } - data.data.splice(tableInfo.nRow, 0, JSON.parse(JSON.stringify(data.data[tableInfo.nRow]))); - data.data[tableInfo.nRow + 1]['No.'] += 1 + notifyChange(); + } + else if (action.includes('sentence')) { - let pos = tableInfo.nRow + 2; - while ((pos < data.data.length) && (data.data[pos]['No.'] > 1)) { - data.data[pos]['No.']++; - pos++; - } + let pos = nRow; - notifyChange(); - } - else if (editingTd.tokenizer_action.includes('sentence')) { + let new_row = JSON.parse(JSON.stringify(data.data[pos])) - let pos = tableInfo.nRow; + console.log(new_row) - let new_row = JSON.parse(JSON.stringify(data.data[pos])) + data.data.splice(pos, 0, new_row); + data.data[pos]['No.'] = 0 + data.data[pos]['TOKEN'] = '' + pos += 1 - console.log(new_row) + let word_pos = 1; + while ((pos < data.data.length) && (data.data[pos]['No.'] != 1) && (data.data[pos]['TOKEN'] != '') ) { + data.data[pos]['No.'] = word_pos; + pos++; + word_pos++; + } - data.data.splice(pos, 0, new_row); - data.data[pos]['No.'] = 0 - data.data[pos]['TOKEN'] = '' - pos += 1 + notifyChange(); + } - let word_pos = 1; - while ((pos < data.data.length) && (data.data[pos]['No.'] != 1) && (data.data[pos]['TOKEN'] != '') ) { - data.data[pos]['No.'] = word_pos; - pos++; - word_pos++; - } + updateTable(); + } - notifyChange(); - } + function makeLineSplitMerge(td) { - editingTd = null; + editingTd = { + elem: td, + data: td.innerHTML, + tokenizer_action: null, + finish: function(td, isOk) { + $(td).html(editingTd.data); + $(td).addClass('editable'); - updateTable(); + let tableInfo = $(td).data('tableInfo'); + + tableEditAction(tableInfo.nRow, editingTd.tokenizer_action) + + editingTd = null; } }; @@ -337,11 +332,11 @@ function setupInterface(data, file, urls) {
↕  split
⟳ merge
☇ sentence
- - `; + `; - $(td).removeClass(); + $(td).removeClass('editable'); $(td).html(edit_html); + $('#tokenizer').mouseleave( function(event) { editingTd.finish(editingTd.elem, false); @@ -404,7 +399,7 @@ function setupInterface(data, file, urls) { `; - $(td).removeClass(); + $(td).removeClass('editable'); $(td).html(edit_html); $('#tagger').mouseleave( function(event) { @@ -417,16 +412,11 @@ function setupInterface(data, file, urls) { }); } - function updateTable() { - - let do_not_display = new Set(['url_id', 'left', 'right', 'top', 'bottom']); + function createTable() { editingTd = null; - let editable_html = - ` - - `; + let editable_html =``; $('#table-body').empty(); @@ -436,34 +426,98 @@ function setupInterface(data, file, urls) { if (nRow < startIndex) return; if (nRow >= endIndex) return; - var row = $(""); + let row = $("").data('tableInfo', { 'nRow': nRow }); + + row.focusin(function() { updatePreview(row.data('tableInfo').nRow); }); row.append($(''). text(nRow). data('tableInfo', { 'nRow': nRow }) ); + let row_listener = new window.keypress.Listener(row); + + row_listener.sequence_combo('0', + function() { + + data.data[row.data('tableInfo').nRow]['No.'] = 0; + + row.children('.editable').first().html('0'); + + notifyChange(); + }); + + row_listener.sequence_combo('s t', function() { + tableEditAction(row.data('tableInfo').nRow, 'sentence'); + }); + + row_listener.sequence_combo('s p', function() { + tableEditAction(row.data('tableInfo').nRow, 'split'); + }); + + row_listener.sequence_combo('m e', function() { + tableEditAction(row.data('tableInfo').nRow, 'merge'); + }); + $.each(el, function(column, content) { + let td = $(editable_html) + + let listener = new window.keypress.Listener(td); + if (do_not_display.has(column)) return - var clickAction = function() { console.log('Do something different');} + let clickAction = function() { console.log('Do something different');} + + if (column == 'No.') { - if (column == 'No.') clickAction = makeLineSplitMerge + } - if ((column == 'TOKEN') || (column == 'GND-ID')) + if ((column == 'TOKEN') || (column == 'GND-ID')) { clickAction = makeTdEditable - if ((column == 'NE-TAG') || (column == 'NE-EMB')) + listener.simple_combo("enter", function() { + td.click(); + }); + } + + if ((column == 'NE-TAG') || (column == 'NE-EMB')) { clickAction = makeTagEdit - row.append( - $(editable_html). - text(content). - data('tableInfo', { 'nRow': nRow, 'column': column , 'clickAction': clickAction }) - ); + let tagAction = function(tag) { + data.data[nRow][column] = tag; + + $(td).html(tag); + colorCode(); + notifyChange(); + }; + + listener.sequence_combo('b p', function() { tagAction('B-PER'); }); + listener.sequence_combo('b l', function() { tagAction('B-LOC'); }); + listener.sequence_combo('b o', function() { tagAction('B-ORG'); }); + listener.sequence_combo('b w', function() { tagAction('B-WORK'); }); + listener.sequence_combo('b c', function() { tagAction('B-CONF'); }); + listener.sequence_combo('b e', function() { tagAction('B-EVT'); }); + listener.sequence_combo('b t', function() { tagAction('B-TODO'); }); + + listener.sequence_combo('i p', function() { tagAction('I-PER'); }); + listener.sequence_combo('i l', function() { tagAction('I-LOC'); }); + listener.sequence_combo('i o', function() { tagAction('I-ORG'); }); + listener.sequence_combo('i w', function() { tagAction('I-WORK'); }); + listener.sequence_combo('i c', function() { tagAction('I-CONF'); }); + listener.sequence_combo('i e', function() { tagAction('I-EVT'); }); + listener.sequence_combo('i t', function() { tagAction('I-TODO'); }); + + listener.sequence_combo('backspace', function() { tagAction('O'); }); + } + + td.attr('tabindex', 0). + text(content). + data('tableInfo', { 'nRow': nRow, 'column': column , 'clickAction': clickAction }) + + row.append(td); }); $("#table tbody").append(row); @@ -471,7 +525,14 @@ function setupInterface(data, file, urls) { colorCode(); - $(".hover").on('mouseover', gotoLocation); + $(".hover").on('mouseover', + function (evt) { + + if (editingTd != null) return; + + $(evt.target).focus(); + } + ); if ($("#docpos").val() != startIndex) { @@ -479,6 +540,73 @@ function setupInterface(data, file, urls) { } } + function updateTable() { + + editingTd = null; + + let rows = $('tbody').children('tr'); + + let pRow = 0; + + $.each(data.data, + function(nRow, el) { + + if (nRow < startIndex) return; + if (nRow >= endIndex) return; + + + let row = $(rows[pRow]); + let tableInfo = row.data('tableInfo'); + + tableInfo.nRow = nRow; + + row.data('tableInfo', tableInfo); + + let loc = $(row.children('td').first()); + loc.data('tableInfo', tableInfo); + loc.text(nRow); + + let columns = $(rows[pRow]).children('.editable'); + let pColumn = 0; + + $.each(el, + function(column_name, content) { + + if (do_not_display.has(column_name)) return + + let td = $(columns[pColumn]); + + tableInfo = td.data('tableInfo'); + + tableInfo.nRow = nRow; + + td.text(content).data('tableInfo', tableInfo); + + pColumn++; + }); + + pRow++; + }); + + colorCode(); + + if ($("#docpos").val() != startIndex) { + + $("#docpos").val(data.data.length - startIndex); + } + + updatePreview($(':focus').data('tableInfo').nRow); + } + + let wnd_listener = new window.keypress.Listener(); + + wnd_listener.simple_combo('tab', + function () { + if (editingTd != null) + return false; // If we are in editing mode, we do not want to propagate the TAB event. + else return true; // In non-editing mode, we want to get the "normal" tab behaviour. + }); + let slider_pos = data.data.length - startIndex; let slider_min = displayRows; let slider_max = data.data.length; @@ -510,12 +638,12 @@ function setupInterface(data, file, urls) { - + - + @@ -525,7 +653,7 @@ function setupInterface(data, file, urls) { `; let save_html = - `` + `` $("#tableregion").html(table_html) @@ -552,12 +680,13 @@ function setupInterface(data, file, urls) { csv = [ lines[0] ]; let url_id = -1; - for(var i = 0; i < data.data.length; i++){ + for(let i = 0; i < data.data.length; i++){ if (data.data[i]['url_id'] > url_id) { url_id = data.data[i]['url_id']; - csv.push("# " + urls[url_id]); + if (urls != null) + csv.push("# " + urls[url_id]); } csv.push(lines[i+1]); } @@ -573,7 +702,7 @@ function setupInterface(data, file, urls) { if (!data) return; - var blob = data.constructor !== Blob + let blob = data.constructor !== Blob ? new Blob([data], {type: mimetype || 'application/octet-stream'}) : data ; @@ -582,7 +711,7 @@ function setupInterface(data, file, urls) { return; } - var lnk = document.createElement('a'), + let lnk = document.createElement('a'), url = window.URL, objectURL; @@ -615,7 +744,7 @@ function setupInterface(data, file, urls) { $(target).data('tableInfo').clickAction(target); }); - updateTable(); + createTable(); $('#tableregion')[0].addEventListener("wheel", function(event) { @@ -638,37 +767,115 @@ function setupInterface(data, file, urls) { updateTable(); }); - $('#back').on('click', - function(evt) { + function stepsBackward (nrows) { - if (startIndex >= displayRows) { - startIndex -= displayRows; - endIndex -= displayRows; + if (startIndex >= nrows) { + startIndex -= nrows; + endIndex -= nrows; + } + else { + startIndex = 0; + endIndex = displayRows; + } + + updateTable(); + } + + function stepsForward(nrows) { + if (endIndex + nrows < data.data.length) { + endIndex += nrows; + startIndex = endIndex - displayRows; + } + else { + endIndex = data.data.length; + startIndex = endIndex - displayRows; + } + + updateTable(); + } + + $('#back').on('click', function() { stepsBackward(displayRows); } ); + + $('#next').on('click', function() { stepsForward(displayRows); } ); + + wnd_listener.simple_combo('pageup', + function() { + if (editingTd != null) return; + + $('#back').click(); + }); + + wnd_listener.simple_combo('pagedown', + function() { + if (editingTd != null) return; + + $('#next').click(); + }); + + wnd_listener.simple_combo('left', + function() { + if (editingTd != null) return true; + + let prev = $(':focus').prev('.editable') + + if (prev.length==0) { + $(':focus').closest('tr').prev('tr').children('.editable').last().focus(); } else { - startIndex = 0; - endIndex = displayRows; + prev.focus(); } + }); + wnd_listener.simple_combo('right', + function() { + if (editingTd != null) return true; - updateTable(); - } - ); + let next = $(':focus').next('.editable') - $('#next').on('click', - function(evt) { + if (next.length==0) { + $(':focus').closest('tr').next('tr').children('.editable').first().focus(); + } + else { + next.focus(); + } + }); + + wnd_listener.simple_combo('up', + function() { + if (editingTd != null) return true; + + let prev = $(':focus').closest('tr').prev('tr') + + let pos = $(':focus').closest('tr').children('.editable').index($(':focus')) - if (endIndex + displayRows < data.data.length) { - endIndex += displayRows; - startIndex = endIndex - displayRows; + if (prev.length==0) { + stepsBackward(1); } else { - endIndex = data.data.length; - startIndex = endIndex - displayRows; + prev.children('.editable')[pos].focus(); } + }); - updateTable(); - } - ); + wnd_listener.simple_combo('down', + function() { + if (editingTd != null) return true; + + let next = $(':focus').closest('tr').next('tr') + + let pos = $(':focus').closest('tr').children('.editable').index($(':focus')) + + if (next.length==0) { + stepsForward(1); + } + else { + next.children('.editable')[pos].focus(); + } + }); + + // public interface + let that = + { + hasChanges: function () { return has_changes; } + }; return that; } @@ -683,7 +890,7 @@ $(document).ready( loadFile ( evt, function(results, file, urls) { - var neath = setupInterface(results, file, urls); + let neath = setupInterface(results, file, urls); $(window).bind("beforeunload", function() { @@ -698,4 +905,4 @@ $(document).ready( } ); } -); \ No newline at end of file +);
LOCATIONLOCATION POSITION TOKEN NE-TAG NE-EMBGND-IDGND-ID