mirror of
https://github.com/chylex/TweetDuck.git
synced 2025-04-14 03:15:49 +02:00
Make emoji keyboard replace tweet input with one that displays emoji
Closes #146
This commit is contained in:
parent
2a3dca4467
commit
1ff21f0ee0
Resources
@ -9,7 +9,7 @@ Emoji keyboard
|
||||
chylex
|
||||
|
||||
[version]
|
||||
1.2.2
|
||||
1.3
|
||||
|
||||
[website]
|
||||
https://tweetduck.chylex.com
|
||||
|
@ -39,6 +39,10 @@ enabled(){
|
||||
this.css.insert(".emoji-keyboard-skintones div { width: 0.8em; height: 0.8em; margin: 0.25em 0.1em; border-radius: 50%; display: inline-block; box-sizing: border-box; cursor: pointer }");
|
||||
this.css.insert(".emoji-keyboard-skintones .sel { border: 2px solid rgba(0, 0, 0, 0.35); box-shadow: 0 0 2px 0 rgba(255, 255, 255, 0.65), 0 0 1px 0 rgba(255, 255, 255, 0.4) inset }");
|
||||
this.css.insert(".emoji-keyboard-skintones :hover { border: 2px solid rgba(0, 0, 0, 0.25); box-shadow: 0 0 1px 0 rgba(255, 255, 255, 0.65), 0 0 1px 0 rgba(255, 255, 255, 0.25) inset }");
|
||||
this.css.insert("#emoji-keyboard-tweet-input { padding: 2px 0 !important; line-height: 18px }");
|
||||
this.css.insert("#emoji-keyboard-tweet-input img { padding: 0.1em !important; width: 1em; height: 1em; vertical-align: -0.25em }");
|
||||
this.css.insert("#emoji-keyboard-tweet-input:empty::before { content: \"What's happening?\"; display: inline-block; color: #ced8de }");
|
||||
this.css.insert(".js-docked-compose .js-compose-text:not(.debug) { position: absolute; z-index: -9999; left: 0; opacity: 0 }");
|
||||
|
||||
// layout
|
||||
|
||||
@ -124,7 +128,7 @@ enabled(){
|
||||
updateFilters();
|
||||
};
|
||||
|
||||
this.generateKeyboard = (input, left, top) => {
|
||||
this.generateKeyboard = (left, top) => {
|
||||
var outer = document.createElement("div");
|
||||
outer.classList.add("emoji-keyboard");
|
||||
outer.style.left = left+"px";
|
||||
@ -134,18 +138,10 @@ enabled(){
|
||||
keyboard.classList.add("emoji-keyboard-list");
|
||||
|
||||
keyboard.addEventListener("click", function(e){
|
||||
if (e.target.tagName === "IMG"){
|
||||
var val = input.val();
|
||||
var inserted = e.target.getAttribute("alt");
|
||||
var posStart = input[0].selectionStart;
|
||||
var posEnd = input[0].selectionEnd;
|
||||
|
||||
input.val(val.slice(0, posStart)+inserted+val.slice(posStart));
|
||||
input.trigger("change");
|
||||
input.focus();
|
||||
|
||||
input[0].selectionStart = posStart+inserted.length;
|
||||
input[0].selectionEnd = posEnd+inserted.length;
|
||||
let ele = e.target;
|
||||
|
||||
if (ele.tagName === "IMG"){
|
||||
insertEmoji(ele.getAttribute("src"), ele.getAttribute("alt"));
|
||||
}
|
||||
|
||||
e.stopPropagation();
|
||||
@ -204,6 +200,26 @@ enabled(){
|
||||
return button.offset().top+button.outerHeight()+me.composePanelScroller.scrollTop()+8;
|
||||
};
|
||||
|
||||
var focusWithCaretAtEnd = () => {
|
||||
let range = document.createRange();
|
||||
range.selectNodeContents(me.composeInputNew[0]);
|
||||
range.collapse(false);
|
||||
|
||||
let sel = window.getSelection();
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(range);
|
||||
};
|
||||
|
||||
var insertEmoji = (src, alt) => {
|
||||
let sel = window.getSelection();
|
||||
|
||||
if (!sel.anchorNode || $(sel.anchorNode).closest("#emoji-keyboard-tweet-input").length === 0){
|
||||
focusWithCaretAtEnd();
|
||||
}
|
||||
|
||||
document.execCommand("insertHTML", false, `<img src="${src}" alt="${alt}">`);
|
||||
};
|
||||
|
||||
// event handlers
|
||||
|
||||
this.emojiKeyboardButtonClickEvent = function(e){
|
||||
@ -212,7 +228,7 @@ enabled(){
|
||||
$(this).blur();
|
||||
}
|
||||
else{
|
||||
me.generateKeyboard($(".js-compose-text").first(), $(this).offset().left, getKeyboardTop());
|
||||
me.generateKeyboard($(this).offset().left, getKeyboardTop());
|
||||
$(this).addClass("is-selected");
|
||||
}
|
||||
|
||||
@ -230,7 +246,7 @@ enabled(){
|
||||
};
|
||||
|
||||
this.documentClickEvent = function(e){
|
||||
if (me.currentKeyboard && !e.target.classList.contains("js-compose-text")){
|
||||
if (me.currentKeyboard && !e.target.classList.contains("compose-text")){
|
||||
hideKeyboard();
|
||||
}
|
||||
};
|
||||
@ -246,7 +262,77 @@ enabled(){
|
||||
if (me.currentKeyboard){
|
||||
me.currentKeyboard.style.top = getKeyboardTop()+"px";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var prevOldInputVal = "";
|
||||
|
||||
this.composeOldInputFocusEvent = function(){
|
||||
let ele = $(this);
|
||||
let val = ele.val();
|
||||
|
||||
if (val != prevOldInputVal){
|
||||
prevOldInputVal = val;
|
||||
|
||||
setTimeout(function(){
|
||||
let selStart = ele[0].selectionStart;
|
||||
let selEnd = ele[0].selectionEnd;
|
||||
|
||||
me.composeInputNew.text(val);
|
||||
me.composeInputNew.focus();
|
||||
|
||||
let sel = window.getSelection();
|
||||
let range = document.createRange();
|
||||
range.setStart(me.composeInputNew[0].firstChild, selStart);
|
||||
range.setEnd(me.composeInputNew[0].firstChild, selEnd);
|
||||
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(range);
|
||||
}, 1);
|
||||
}
|
||||
else{
|
||||
focusWithCaretAtEnd();
|
||||
}
|
||||
};
|
||||
|
||||
var allowedShortcuts = [ 65 /* A */, 67 /* C */, 86 /* V */, 89 /* Y */, 90 /* Z */ ];
|
||||
|
||||
this.composeInputKeyEvent = function(e){
|
||||
if (e.type === "keydown" && (e.ctrlKey || e.metaKey)){
|
||||
if (e.keyCode === 13){
|
||||
$(".js-send-button", ".js-docked-compose").click();
|
||||
}
|
||||
else if (e.keyCode >= 48 && !allowedShortcuts.includes(e.keyCode)){
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
this.composeInputUpdateEvent = function(){
|
||||
let clone = $(this).clone();
|
||||
|
||||
clone.children("div").each(function(){
|
||||
let ele = $(this)[0];
|
||||
ele.outerHTML = "\n"+ele.innerHTML;
|
||||
});
|
||||
|
||||
clone.children("img").each(function(){
|
||||
let ele = $(this)[0];
|
||||
ele.outerHTML = ele.getAttribute("alt");
|
||||
});
|
||||
|
||||
me.composeInputOrig.val(prevOldInputVal = clone.text());
|
||||
me.composeInputOrig.trigger("change");
|
||||
};
|
||||
|
||||
this.composeInputPasteEvent = function(e){
|
||||
e.preventDefault();
|
||||
|
||||
let text = e.originalEvent.clipboardData.getData("text/plain");
|
||||
text && document.execCommand("insertText", false, text);
|
||||
};
|
||||
}
|
||||
|
||||
ready(){
|
||||
@ -261,6 +347,17 @@ ready(){
|
||||
$(document).on("uiComposeImageAdded", this.uploadFilesEvent);
|
||||
this.composeDrawer.on("uiComposeTweetSending", this.composerSendingEvent);
|
||||
|
||||
// Editor
|
||||
|
||||
this.composeInputOrig = $(".js-compose-text", ".js-docked-compose").first();
|
||||
this.composeInputNew = $('<div id="emoji-keyboard-tweet-input" contenteditable="true" class="compose-text txt-size--14 scroll-v scroll-styled-v scroll-styled-h scroll-alt td-detect-image-paste"></div>').insertAfter(this.composeInputOrig);
|
||||
|
||||
this.composeInputOrig.on("focus", this.composeOldInputFocusEvent);
|
||||
|
||||
this.composeInputNew.on("keydown keypress keyup", this.composeInputKeyEvent);
|
||||
this.composeInputNew.on("input", this.composeInputUpdateEvent);
|
||||
this.composeInputNew.on("paste", this.composeInputPasteEvent);
|
||||
|
||||
// HTML generation
|
||||
|
||||
var convUnicode = function(codePt){
|
||||
@ -280,12 +377,14 @@ ready(){
|
||||
|
||||
// declaration inserters
|
||||
|
||||
let mapUnicode = pt => convUnicode(parseInt(pt, 16));
|
||||
|
||||
let addDeclaration1 = decl => {
|
||||
this.emojiData1.push(decl.split(" ").map(pt => convUnicode(parseInt(pt, 16))).join(""));
|
||||
this.emojiData1.push(decl.split(" ").map(mapUnicode).join(""));
|
||||
};
|
||||
|
||||
let addDeclaration2 = (tone, decl) => {
|
||||
let gen = decl.split(" ").map(pt => convUnicode(parseInt(pt, 16))).join("");
|
||||
let gen = decl.split(" ").map(mapUnicode).join("");
|
||||
|
||||
if (tone === null){
|
||||
for(let skinTone of this.skinToneList){
|
||||
@ -298,7 +397,7 @@ ready(){
|
||||
};
|
||||
|
||||
let addDeclaration3 = decl => {
|
||||
this.emojiData3.push(decl.split(" ").map(pt => convUnicode(parseInt(pt, 16))).join(""));
|
||||
this.emojiData3.push(decl.split(" ").map(mapUnicode).join(""));
|
||||
};
|
||||
|
||||
// line reading
|
||||
@ -372,6 +471,9 @@ disabled(){
|
||||
$(this.currentSpanner).remove();
|
||||
}
|
||||
|
||||
this.composeInputNew.remove();
|
||||
|
||||
this.composeInputOrig.off("focus", this.composeOldInputFocusEvent);
|
||||
this.composePanelScroller.off("scroll", this.composerScrollEvent);
|
||||
|
||||
$(".emoji-keyboard-popup-btn").off("click", this.emojiKeyboardButtonClickEvent);
|
||||
|
@ -526,7 +526,7 @@
|
||||
onAppReady.push(function(){
|
||||
var uploader = $._data(document, "events")["uiComposeAddImageClick"][0].handler.context;
|
||||
|
||||
app.delegate(".js-compose-text,.js-reply-tweetbox", "paste", function(e){
|
||||
app.delegate(".js-compose-text,.js-reply-tweetbox,.td-detect-image-paste", "paste", function(e){
|
||||
for(let item of e.originalEvent.clipboardData.items){
|
||||
if (item.type.startsWith("image/")){
|
||||
if (!$(this).closest(".rpl").find(".js-reply-popout").click().length){ // popout direct messages
|
||||
|
Loading…
Reference in New Issue
Block a user