diff --git a/Resources/Plugins/emoji-keyboard/.meta b/Resources/Plugins/emoji-keyboard/.meta index 7ebd72ee..ce497f98 100644 --- a/Resources/Plugins/emoji-keyboard/.meta +++ b/Resources/Plugins/emoji-keyboard/.meta @@ -9,7 +9,7 @@ Emoji keyboard chylex [version] -1.2.2 +1.3 [website] https://tweetduck.chylex.com diff --git a/Resources/Plugins/emoji-keyboard/browser.js b/Resources/Plugins/emoji-keyboard/browser.js index ec93abe0..b5a748f8 100644 --- a/Resources/Plugins/emoji-keyboard/browser.js +++ b/Resources/Plugins/emoji-keyboard/browser.js @@ -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); diff --git a/Resources/Scripts/code.js b/Resources/Scripts/code.js index 2a5ce9ff..8bc93fbc 100644 --- a/Resources/Scripts/code.js +++ b/Resources/Scripts/code.js @@ -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