|
// ==UserScript== |
|
// @name Switch ctrl-enter and enter |
|
// @namespace http://tampermonkey.net/ |
|
// @version 0.1 |
|
// @description try to take over the world! |
|
// @author You |
|
// @match https://web.whatsapp.com/ |
|
// @icon https://www.google.com/s2/favicons?domain=whatsapp.com |
|
// @run-at document-start |
|
// @grant none |
|
// ==/UserScript== |
|
|
|
/** Clone an object even with all it's non owned properties |
|
* From https://stackoverflow.com/a/57109239/5759814 */ |
|
const SimplePropertyRetriever = { |
|
getOwnAndPrototypeEnumerablesAndNonenumerables: function(obj) { |
|
return this._getPropertyNames(obj, true, true, this._enumerableAndNotEnumerable); |
|
}, |
|
_enumerableAndNotEnumerable: function(obj, prop) { |
|
return true; |
|
}, |
|
// Inspired by http://stackoverflow.com/a/8024294/271577 |
|
_getPropertyNames: function getAllPropertyNames(obj, iterateSelfBool, iteratePrototypeBool, includePropCb) { |
|
var props = []; |
|
|
|
do { |
|
if (iterateSelfBool) { |
|
Object.getOwnPropertyNames(obj).forEach(function(prop) { |
|
if (props.indexOf(prop) === -1 && includePropCb(obj, prop)) { |
|
props.push(prop); |
|
} |
|
}); |
|
} |
|
if (!iteratePrototypeBool) { |
|
break; |
|
} |
|
iterateSelfBool = true; |
|
} while (obj = Object.getPrototypeOf(obj)); |
|
|
|
return props; |
|
} |
|
}; |
|
|
|
/** Clone an event and inject corresponding listener handlers |
|
* |
|
* Cloning the event and having a r/w object at the end allows |
|
* us to circumvent the 'isTrusted' check on keyevents as we can |
|
* now just inject that with whatever we want. On actuall event's |
|
* that wouldn't be possible as these are managed by the browser itself. |
|
*/ |
|
window.clone_event = function(e) { |
|
let new_event = {}; |
|
SimplePropertyRetriever.getOwnAndPrototypeEnumerablesAndNonenumerables(e).forEach((k) => { |
|
new_event[k] = e[k]; |
|
}); |
|
// also allow stop propagation and such |
|
new_event.preventDefault = function () { |
|
if (this.cancelable) { |
|
this.returnValue = false; |
|
this.defaultPrevented = true; |
|
} |
|
e.preventDefault(); |
|
} |
|
new_event.stopPropagation = function () { |
|
this.cancelBubble = true; |
|
e.stopPropagation(); |
|
} |
|
|
|
return new_event; |
|
}; |
|
|
|
(function() { |
|
// 'use strict'; |
|
/* we need to override the addEventListener handler in |
|
order to wrap corresponding functions into our own handlers */ |
|
window.EventTarget.prototype.addEventListenerBase = EventTarget.prototype.addEventListener; |
|
window.EventTarget.prototype.addEventListener = function(type, listener, ...args) { |
|
let new_listener = listener; |
|
// ony keydown events on #app are important for sending messages |
|
if (type.includes("key")) { |
|
let need_to_send = false; |
|
new_listener = function (e, ...args) { |
|
// only handle enter key |
|
if (e.keyCode == 13) { |
|
window.old_event = e; |
|
// check whether my target is actually the message textbox |
|
const is_textbox = e.target.getAttribute("contenteditable") == "true" |
|
&& e.target.getAttribute("role") == "textbox" |
|
&& e.target.hasAttribute("title"); |
|
const has_emoji_open = document.querySelector("span[class~='emojik'][class~='wa'][data-emoji-index][data-unicode]") !== null; |
|
const should_edit = e.key == "Enter" && is_textbox && !has_emoji_open; |
|
|
|
if (should_edit) { |
|
e = window.clone_event(e); |
|
e.ctrlKey = !e.ctrlKey; |
|
} |
|
|
|
if (parseFloat(navigator.userAgent.split('Firefox/').pop(), 10) >= 92) { |
|
/* Hot fix: the ctrl-enter input in whatsapp web is |
|
kinda broken rn as a new line will not be entered |
|
immidiently - this behaviour can be forced after typing |
|
in an emoji. so stop propagating the enter and let |
|
the input field handle it for now. |
|
|
|
this issue affects Firefox only*/ |
|
if (e.ctrlKey) { |
|
e.stopPropagation(); |
|
} |
|
} |
|
} |
|
|
|
listener(e, ...args); |
|
}; |
|
} |
|
|
|
this.addEventListenerBase(type, new_listener, ...args); |
|
}; |
|
|
|
window.EventTarget.prototype.removeEventListenerBase = EventTarget.prototype.removeEventListener; |
|
window.EventTarget.prototype.removeEventListener = function(type, listener, ...args) { |
|
this.removeEventListenerBase(type, listener, ...args); |
|
} |
|
|
|
console.log("[Info] Initialized the ctrl-enter / enter switch"); |
|
})(); |