Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

substituteKeyboardEvents config option #30

Merged
merged 4 commits into from
Jun 7, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions src/publicapi.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ function getInterface(v) {

MQ.L = L;
MQ.R = R;
MQ.saneKeyboardEvents = saneKeyboardEvents;

function config(currentOptions, newOptions) {
if (newOptions && newOptions.handlers) {
Expand Down
21 changes: 15 additions & 6 deletions src/services/saneKeyboardEvents.util.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ var saneKeyboardEvents = (function() {
var textarea = jQuery(el);
var target = jQuery(handlers.container || textarea);

// checkTextareaFor() is called after keypress or paste events to
// say "Hey, I think something was just typed" or "pasted" (resp.),
// checkTextareaFor() is called after key or clipboard events to
// say "Hey, I think something was just typed" or "pasted" etc,
// so that at all subsequent opportune times (next event or timeout),
// will check for expected typed or pasted text.
// Need to check repeatedly because #135: in Safari 5.1 (at least),
Expand All @@ -109,6 +109,13 @@ var saneKeyboardEvents = (function() {
clearTimeout(timeoutId);
timeoutId = setTimeout(checker);
}
function checkTextareaOnce(checker) {
checkTextareaFor(function(e) {
checker(e);
checkTextarea = noop;
clearTimeout(timeoutId);
});
}
target.bind('keydown keypress input keyup focusout paste', function(e) { checkTextarea(e); });


Expand Down Expand Up @@ -148,12 +155,12 @@ var saneKeyboardEvents = (function() {
keydown = e;
keypress = null;

if (shouldBeSelected) checkTextareaFor(function(e) {
if (shouldBeSelected) checkTextareaOnce(function(e) {
if (!(e && e.type === 'focusout') && textarea[0].select) {
textarea[0].select(); // re-select textarea in case it's an unrecognized
// re-select textarea in case it's an unrecognized key that clears
// the selection, then never again, 'cos next thing might be blur
textarea[0].select();
}
checkTextarea = noop; // key that clears the selection, then never
clearTimeout(timeoutId); // again, 'cos next thing might be blur
});

handleKey();
Expand Down Expand Up @@ -229,6 +236,8 @@ var saneKeyboardEvents = (function() {
keydown: onKeydown,
keypress: onKeypress,
focusout: onBlur,
cut: function() { checkTextareaOnce(function() { handlers.cut(); }); },
copy: function() { checkTextareaOnce(function() { handlers.copy(); }); },
paste: onPaste
});

Expand Down
32 changes: 17 additions & 15 deletions src/services/textarea.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ Controller.open(function(_) {

var ctrlr = this;
ctrlr.cursor.selectionChanged = function() { ctrlr.selectionChanged(); };
ctrlr.container.bind('copy', function() { ctrlr.setTextareaSelection(); });
};
_.selectionChanged = function() {
var ctrlr = this;
Expand Down Expand Up @@ -52,6 +51,7 @@ Controller.open(function(_) {
this.container.prepend('<span class="mq-selectable">$'+ctrlr.exportLatex()+'$</span>');
ctrlr.blurred = true;
textarea.bind('cut paste', false)
.bind('copy', function() { ctrlr.setTextareaSelection(); })
.focus(function() { ctrlr.blurred = false; }).blur(function() {
if (cursor.selection) cursor.selection.clear();
setTimeout(detach); //detaching during blur explodes in WebKit
Expand All @@ -66,23 +66,13 @@ Controller.open(function(_) {
if (text) textarea.select();
};
};
Options.p.substituteKeyboardEvents = saneKeyboardEvents;
_.editablesTextareaEvents = function() {
var ctrlr = this, root = ctrlr.root, cursor = ctrlr.cursor,
textarea = ctrlr.textarea, textareaSpan = ctrlr.textareaSpan;
var ctrlr = this, textarea = ctrlr.textarea, textareaSpan = ctrlr.textareaSpan;

var keyboardEventsShim = saneKeyboardEvents(textarea, this);
var keyboardEventsShim = this.options.substituteKeyboardEvents(textarea, this);
this.selectFn = function(text) { keyboardEventsShim.select(text); };

this.container.prepend(textareaSpan)
.on('cut', function(e) {
if (cursor.selection) {
setTimeout(function() {
ctrlr.notify('edit'); // deletes selection if present
cursor.parent.bubble('reflow');
});
}
});

this.container.prepend(textareaSpan);
this.focusBlurEvents();
};
_.typedText = function(ch) {
Expand All @@ -91,6 +81,18 @@ Controller.open(function(_) {
cursor.parent.write(cursor, ch);
this.scrollHoriz();
};
_.cut = function() {
var ctrlr = this, cursor = ctrlr.cursor;
if (cursor.selection) {
setTimeout(function() {
ctrlr.notify('edit'); // deletes selection if present
cursor.parent.bubble('reflow');
});
}
};
_.copy = function() {
this.setTextareaSelection();
};
_.paste = function(text) {
// TODO: document `statelessClipboard` config option in README, after
// making it work like it should, that is, in both text and math mode
Expand Down
45 changes: 45 additions & 0 deletions test/unit/publicapi.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,51 @@ suite('Public API', function() {
});
});

suite('substituteKeyboardEvents', function() {
test('can intercept key events', function() {
var mq = MQ.MathField($('<span>').appendTo('#mock')[0], {
substituteKeyboardEvents: function(textarea, handlers) {
return MQ.saneKeyboardEvents(textarea, jQuery.extend({}, handlers, {
keystroke: function(_key, evt) {
key = _key;
return handlers.keystroke.apply(handlers, arguments);
}
}));
}
});
var key;

$(mq.el()).find('textarea').trigger({ type: 'keydown', which: '37' });
assert.equal(key, 'Left');

$(mq.el()).remove();
});
test('cut is async', function() {
var mq = MQ.MathField($('<span>').appendTo('#mock')[0], {
substituteKeyboardEvents: function(textarea, handlers) {
return MQ.saneKeyboardEvents(textarea, jQuery.extend({}, handlers, {
cut: function() {
count += 1;
return handlers.cut.apply(handlers, arguments);
}
}));
}
});
var count = 0;

$(mq.el()).find('textarea').trigger('cut');
assert.equal(count, 0);

$(mq.el()).find('textarea').trigger('input');
assert.equal(count, 1);

$(mq.el()).find('textarea').trigger('keyup');
assert.equal(count, 1);

$(mq.el()).remove();
});
});

suite('clickAt', function() {
test('inserts at coordinates', function() {
// Insert filler so that the page is taller than the window so this test is deterministic
Expand Down
29 changes: 29 additions & 0 deletions test/visual.html
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,12 @@ <h3>substituteTextarea</h3>

<p>In Safari on iOS, this should be focusable but not bring up the on-screen keyboard; to test, try focusing anything else and confirm this blurs: <span id="no-kbd-math"></span> (confirmed working on iOS 6.1.3)</p>

<h3>substituteKeyboardEvents</h3>

<p>Should be able to prevent cut, typing, and pasting in this field: <span id="disable-typing">1+2+3</span></p>

<p>Should wrap anything you type in '&lt;&gt;': <span id="wrap-typing">1+2+3</span></p>

</div>
<script type="text/javascript">
window.onerror = function() {
Expand Down Expand Up @@ -446,6 +452,29 @@ <h3>substituteTextarea</h3>
return $('<span tabindex=0 style="display:inline-block;width:1px;height:1px" />')[0];
}
});

MQ.MathField($('#disable-typing')[0], {
substituteKeyboardEvents: function(textarea, handlers) {
return MQ.saneKeyboardEvents(textarea, $.extend({}, handlers, {
cut: $.noop,
paste: $.noop,
keystroke: $.noop,
typedText: $.noop
}));
}
});

MQ.MathField($('#wrap-typing')[0], {
substituteKeyboardEvents: function(textarea, handlers) {
return MQ.saneKeyboardEvents(textarea, $.extend({}, handlers, {
typedText: function (text) {
handlers.typedText('<');
handlers.typedText(text);
handlers.typedText('>');
}
}));
}
});
</script>
</body>
</html>