Skip to content

Commit

Permalink
Merge pull request #30 from desmosinc/feature.substituteKeyboardEvents
Browse files Browse the repository at this point in the history
`substituteKeyboardEvents` config option
  • Loading branch information
mikehaverstock committed Jun 7, 2016
2 parents 003e8cc + 0bda7bd commit 60224cd
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 21 deletions.
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 @@ -96,6 +86,18 @@ Controller.open(function(_) {
aria.alert();
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 @@ -794,6 +794,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>

0 comments on commit 60224cd

Please sign in to comment.