diff --git a/Resources/Plugins/.debug/browser.js b/Resources/Plugins/.debug/browser.js index 95ff86a8..0d0b83b3 100644 --- a/Resources/Plugins/.debug/browser.js +++ b/Resources/Plugins/.debug/browser.js @@ -1,67 +1,67 @@ enabled(){ - this.isDebugging = false; - - this.onKeyDown = (e) => { - - // ========================== - // F4 key - toggle debug mode - // ========================== - - if (e.keyCode === 115){ - this.isDebugging = !this.isDebugging; - $(".nav-user-info").first().css("background-color", this.isDebugging ? "#5A6B75" : "#292F33"); - } - - else if (this.isDebugging){ - e.preventDefault(); - - // =================================== - // N key - simulate popup notification - // S key - simulate sound notification - // =================================== - - if (e.keyCode === 78 || e.keyCode === 83){ - let col = TD.controller.columnManager.getAllOrdered()[0]; - let model = col.model; - - let prevPopup = model.getHasNotification(); - let prevSound = model.getHasSound(); - - model.setHasNotification(e.keyCode === 78); - model.setHasSound(e.keyCode === 83); - - $.publish("/notifications/new", [{ - column: col, - items: [ - col.updateArray[Math.floor(Math.random()*col.updateArray.length)] - ] - }]); - - setTimeout(function(){ - model.setHasNotification(prevPopup); - model.setHasSound(prevSound); - }, 1); - } - - // ======================== - // D key - trigger debugger - // ======================== - - else if (e.keyCode === 68){ - debugger; - } - } - }; + this.isDebugging = false; + + this.onKeyDown = (e) => { + + // ========================== + // F4 key - toggle debug mode + // ========================== + + if (e.keyCode === 115) { + this.isDebugging = !this.isDebugging; + $(".nav-user-info").first().css("background-color", this.isDebugging ? "#5a6b75" : "#292f33"); + } + + else if (this.isDebugging) { + e.preventDefault(); + + // =================================== + // N key - simulate popup notification + // S key - simulate sound notification + // =================================== + + if (e.keyCode === 78 || e.keyCode === 83) { + let col = TD.controller.columnManager.getAllOrdered()[0]; + let model = col.model; + + let prevPopup = model.getHasNotification(); + let prevSound = model.getHasSound(); + + model.setHasNotification(e.keyCode === 78); + model.setHasSound(e.keyCode === 83); + + $.publish("/notifications/new", [ { + column: col, + items: [ + col.updateArray[Math.floor(Math.random() * col.updateArray.length)] + ] + } ]); + + setTimeout(function() { + model.setHasNotification(prevPopup); + model.setHasSound(prevSound); + }, 1); + } + + // ======================== + // D key - trigger debugger + // ======================== + + else if (e.keyCode === 68) { + debugger; + } + } + }; } ready(){ - $(document).on("keydown", this.onKeyDown); + $(document).on("keydown", this.onKeyDown); } disabled(){ - $(document).off("keydown", this.onKeyDown); + $(document).off("keydown", this.onKeyDown); } configure(){ - alert("Configure triggered"); + alert("Configure triggered"); } diff --git a/Resources/Plugins/.debug/notification.js b/Resources/Plugins/.debug/notification.js index 6ff1c50a..0fc4367a 100644 --- a/Resources/Plugins/.debug/notification.js +++ b/Resources/Plugins/.debug/notification.js @@ -1,3 +1,3 @@ run(){ - console.info("executed debug plugin in notification"); + console.info("executed debug plugin in notification"); } diff --git a/Resources/Plugins/clear-columns/browser.js b/Resources/Plugins/clear-columns/browser.js index 7fb3328e..ecf04d70 100644 --- a/Resources/Plugins/clear-columns/browser.js +++ b/Resources/Plugins/clear-columns/browser.js @@ -1,173 +1,173 @@ enabled(){ - const clearColumn = (columnName) => { - TD.controller.columnManager.get(columnName).clear(); - TD.controller.stats.columnActionClick("clear"); - }; - - const resetColumn = (columnName) => { - let col = TD.controller.columnManager.get(columnName); - col.model.setClearedTimestamp(0); - col.reloadTweets(); - }; - - const forEachColumn = (func) => { - Object.keys(TD.controller.columnManager.getAll()).forEach(func); - }; - - let wasShiftPressed = false; - - const updateShiftState = (pressed) => { - if (pressed != wasShiftPressed){ - wasShiftPressed = pressed; - - if (pressed){ - $(document).on("mousemove", this.eventKeyUp); - } - else{ - $(document).off("mousemove", this.eventKeyUp); - } - - $(".clear-columns-btn-all").text(pressed ? "Restore columns" : "Clear columns"); - } - }; - - // event handlers - - this.eventClickOneCapture = function(e){ - if (e.target.getAttribute("data-action") === "td-clearcolumns-dosingle"){ - let name = $(e.target).closest(".js-column").attr("data-column"); - e.shiftKey ? resetColumn(name) : clearColumn(name); - - e.preventDefault(); - e.stopPropagation(); - e.stopImmediatePropagation(); - } - }; - - this.eventClickAll = function(e){ - forEachColumn(e.shiftKey ? resetColumn : clearColumn); - }; - - this.eventKeyDown = function(e){ - if (!(document.activeElement === null || document.activeElement === document.body)) { - return; - } - - updateShiftState(e.shiftKey); - - if (e.keyCode === 46){ // 46 = delete - if (e.altKey){ - forEachColumn(e.shiftKey ? resetColumn : clearColumn); - } - else{ - let focusedColumn = $(".js-column.is-focused"); - - if (focusedColumn.length){ - let name = focusedColumn.attr("data-column"); - e.shiftKey ? resetColumn(name) : clearColumn(name); - } - } - } - }; - - this.eventKeyUp = function(e){ - if (!e.shiftKey){ - updateShiftState(false); - } - }; - - this.eventKeyboardShortcuts = function(e){ - $(".keyboard-shortcut-list").first().append(` + const clearColumn = (columnName) => { + TD.controller.columnManager.get(columnName).clear(); + TD.controller.stats.columnActionClick("clear"); + }; + + const resetColumn = (columnName) => { + let col = TD.controller.columnManager.get(columnName); + col.model.setClearedTimestamp(0); + col.reloadTweets(); + }; + + const forEachColumn = (func) => { + Object.keys(TD.controller.columnManager.getAll()).forEach(func); + }; + + let wasShiftPressed = false; + + const updateShiftState = (pressed) => { + if (pressed != wasShiftPressed) { + wasShiftPressed = pressed; + + if (pressed) { + $(document).on("mousemove", this.eventKeyUp); + } + else { + $(document).off("mousemove", this.eventKeyUp); + } + + $(".clear-columns-btn-all").text(pressed ? "Restore columns" : "Clear columns"); + } + }; + + // event handlers + + this.eventClickOneCapture = function(e) { + if (e.target.getAttribute("data-action") === "td-clearcolumns-dosingle") { + let name = $(e.target).closest(".js-column").attr("data-column"); + e.shiftKey ? resetColumn(name) : clearColumn(name); + + e.preventDefault(); + e.stopPropagation(); + e.stopImmediatePropagation(); + } + }; + + this.eventClickAll = function(e) { + forEachColumn(e.shiftKey ? resetColumn : clearColumn); + }; + + this.eventKeyDown = function(e) { + if (!(document.activeElement === null || document.activeElement === document.body)) { + return; + } + + updateShiftState(e.shiftKey); + + if (e.keyCode === 46) { // 46 = delete + if (e.altKey) { + forEachColumn(e.shiftKey ? resetColumn : clearColumn); + } + else { + let focusedColumn = $(".js-column.is-focused"); + + if (focusedColumn.length) { + let name = focusedColumn.attr("data-column"); + e.shiftKey ? resetColumn(name) : clearColumn(name); + } + } + } + }; + + this.eventKeyUp = function(e) { + if (!e.shiftKey) { + updateShiftState(false); + } + }; + + this.eventKeyboardShortcuts = function(e) { + $(".keyboard-shortcut-list").first().append(` <dd class="keyboard-shortcut-definition" style="white-space:nowrap"> <span class="text-like-keyboard-key">1</span> … <span class="text-like-keyboard-key">9</span> + <span class="text-like-keyboard-key">Del</span> Clear column 1-9 </dd> <dd class="keyboard-shortcut-definition"> <span class="text-like-keyboard-key">Alt</span> + <span class="text-like-keyboard-key">Del</span> Clear all columns </dd>`); - }; - - // update UI - - this.btnClearAllHTML = ` + }; + + // update UI + + this.btnClearAllHTML = ` <a class="clear-columns-btn-all-parent js-header-action link-clean cf app-nav-link padding-h--16 padding-v--2" data-title="Clear columns (hold Shift to restore)" data-action="td-clearcolumns-doall"> <div class="obj-left margin-l--2"><i class="icon icon-medium icon-clear-timeline"></i></div> <div class="clear-columns-btn-all nbfc padding-ts hide-condensed txt-size--14 app-nav-link-text">Clear columns</div> </a>`; - - this.btnClearOneHTML = ` + + this.btnClearOneHTML = ` <a class="js-action-header-button column-header-link" href="#" data-action="td-clearcolumns-dosingle"> <i class="icon icon-clear-timeline js-show-tip" data-placement="bottom" data-original-title="Clear column (hold Shift to restore)" data-action="td-clearcolumns-dosingle"></i> </a>`; - - this.prevNavMenuMustache = TD.mustaches["menus/column_nav_menu.mustache"]; - window.TDPF_injectMustache("menus/column_nav_menu.mustache", "replace", "{{_i}}Add column{{/i}}</div> </a> </div>", `{{_i}}Add column{{/i}}</div></a>${this.btnClearAllHTML}</div>`); - - this.prevColumnHeaderMustache = TD.mustaches["column/column_header.mustache"]; - window.TDPF_injectMustache("column/column_header.mustache", "prepend", "<a data-testid=\"optionsToggle\"", this.btnClearOneHTML); - - if (TD.ready){ - $(".js-header-add-column").after(this.btnClearAllHTML); - $("a[data-testid='optionsToggle']", ".js-column-header").before(this.btnClearOneHTML); - } - - // styles - - if (!document.getElementById("td-clearcolumns-workaround")){ - // TD started caching mustaches so disabling the plugin doesn't update the column headers properly... - let workaround = document.createElement("style"); - workaround.id = "td-clearcolumns-workaround"; - workaround.innerText = "#tduck a[data-action='td-clearcolumns-dosingle'] { display: none }"; - document.head.appendChild(workaround); - } - - this.css = window.TDPF_createCustomStyle(this); - - this.css.insert(".js-app-add-column.is-hidden + .clear-columns-btn-all-parent { display: none; }"); - this.css.insert(".column-navigator-overflow .clear-columns-btn-all-parent { display: none !important; }"); - this.css.insert(".column-navigator-overflow { bottom: 224px !important; }"); - this.css.insert(".app-navigator .clear-columns-btn-all-parent { font-weight: 700; }"); - - this.css.insert(".column-header-links { min-width: 51px !important; }"); - this.css.insert(".column[data-td-icon='icon-message'] .column-header-links { min-width: 110px !important; }"); - this.css.insert(".btn-options-tray[data-action='clear'] { display: none !important; }"); - - this.css.insert("#tduck a[data-action='td-clearcolumns-dosingle'] { display: inline-block; }"); - this.css.insert("#tduck .column[data-td-icon='icon-schedule'] a[data-action='td-clearcolumns-dosingle'] { display: none; }"); - this.css.insert("#tduck .column[data-td-icon='icon-custom-timeline'] a[data-action='td-clearcolumns-dosingle'] { display: none; }"); + + this.prevNavMenuMustache = TD.mustaches["menus/column_nav_menu.mustache"]; + window.TDPF_injectMustache("menus/column_nav_menu.mustache", "replace", "{{_i}}Add column{{/i}}</div> </a> </div>", `{{_i}}Add column{{/i}}</div></a>${this.btnClearAllHTML}</div>`); + + this.prevColumnHeaderMustache = TD.mustaches["column/column_header.mustache"]; + window.TDPF_injectMustache("column/column_header.mustache", "prepend", "<a data-testid=\"optionsToggle\"", this.btnClearOneHTML); + + if (TD.ready) { + $(".js-header-add-column").after(this.btnClearAllHTML); + $("a[data-testid='optionsToggle']", ".js-column-header").before(this.btnClearOneHTML); + } + + // styles + + if (!document.getElementById("td-clearcolumns-workaround")) { + // TD started caching mustaches so disabling the plugin doesn't update the column headers properly... + let workaround = document.createElement("style"); + workaround.id = "td-clearcolumns-workaround"; + workaround.innerText = "#tduck a[data-action='td-clearcolumns-dosingle'] { display: none }"; + document.head.appendChild(workaround); + } + + this.css = window.TDPF_createCustomStyle(this); + + this.css.insert(".js-app-add-column.is-hidden + .clear-columns-btn-all-parent { display: none; }"); + this.css.insert(".column-navigator-overflow .clear-columns-btn-all-parent { display: none !important; }"); + this.css.insert(".column-navigator-overflow { bottom: 224px !important; }"); + this.css.insert(".app-navigator .clear-columns-btn-all-parent { font-weight: 700; }"); + + this.css.insert(".column-header-links { min-width: 51px !important; }"); + this.css.insert(".column[data-td-icon='icon-message'] .column-header-links { min-width: 110px !important; }"); + this.css.insert(".btn-options-tray[data-action='clear'] { display: none !important; }"); + + this.css.insert("#tduck a[data-action='td-clearcolumns-dosingle'] { display: inline-block; }"); + this.css.insert("#tduck .column[data-td-icon='icon-schedule'] a[data-action='td-clearcolumns-dosingle'] { display: none; }"); + this.css.insert("#tduck .column[data-td-icon='icon-custom-timeline'] a[data-action='td-clearcolumns-dosingle'] { display: none; }"); } ready(){ - document.addEventListener("click", this.eventClickOneCapture, true); - $(document).on("click", "[data-action='td-clearcolumns-doall']", this.eventClickAll); - $(document).on("keydown", this.eventKeyDown); - $(document).on("keyup", this.eventKeyUp); - $(document).on("uiShowKeyboardShortcutList", this.eventKeyboardShortcuts); - - $(".js-app-add-column").first().after(this.btnClearAllHTML); - - // fix tooltip - - let tooltipEvents = $._data($(".js-header-action")[0], "events"); - - if (tooltipEvents.mouseover && tooltipEvents.mouseover.length && tooltipEvents.mouseout && tooltipEvents.mouseout.length){ - $(".clear-columns-btn-all-parent").on({ - mouseover: tooltipEvents.mouseover[0].handler, - mouseout: tooltipEvents.mouseout[0].handler - }); - } + document.addEventListener("click", this.eventClickOneCapture, true); + $(document).on("click", "[data-action='td-clearcolumns-doall']", this.eventClickAll); + $(document).on("keydown", this.eventKeyDown); + $(document).on("keyup", this.eventKeyUp); + $(document).on("uiShowKeyboardShortcutList", this.eventKeyboardShortcuts); + + $(".js-app-add-column").first().after(this.btnClearAllHTML); + + // fix tooltip + + let tooltipEvents = $._data($(".js-header-action")[0], "events"); + + if (tooltipEvents.mouseover && tooltipEvents.mouseover.length && tooltipEvents.mouseout && tooltipEvents.mouseout.length) { + $(".clear-columns-btn-all-parent").on({ + mouseover: tooltipEvents.mouseover[0].handler, + mouseout: tooltipEvents.mouseout[0].handler + }); + } } disabled(){ - this.css.remove(); - - document.removeEventListener("click", this.eventClickOneCapture); - $(document).off("click", "[data-action='td-clearcolumns-doall']", this.eventClickAll); - $(document).off("keydown", this.eventKeyDown); - $(document).off("keyup", this.eventKeyUp); - $(document).off("uiShowKeyboardShortcutList", this.eventKeyboardShortcuts); - - TD.mustaches["menus/column_nav_menu.mustache"] = this.prevNavMenuMustache; - TD.mustaches["column/column_header.mustache"] = this.prevColumnHeaderMustache; - - $("[data-action^='td-clearcolumns-']").remove(); + this.css.remove(); + + document.removeEventListener("click", this.eventClickOneCapture); + $(document).off("click", "[data-action='td-clearcolumns-doall']", this.eventClickAll); + $(document).off("keydown", this.eventKeyDown); + $(document).off("keyup", this.eventKeyUp); + $(document).off("uiShowKeyboardShortcutList", this.eventKeyboardShortcuts); + + TD.mustaches["menus/column_nav_menu.mustache"] = this.prevNavMenuMustache; + TD.mustaches["column/column_header.mustache"] = this.prevColumnHeaderMustache; + + $("[data-action^='td-clearcolumns-']").remove(); } diff --git a/Resources/Plugins/edit-design/browser.js b/Resources/Plugins/edit-design/browser.js index 16ec6136..32febc88 100644 --- a/Resources/Plugins/edit-design/browser.js +++ b/Resources/Plugins/edit-design/browser.js @@ -1,550 +1,562 @@ enabled(){ - // elements & data - this.css = null; - this.icons = null; - this.config = null; - - this.defaultConfig = { - _theme: "light", - themeOverride: false, - columnWidth: "310px", - composerWidth: "default", - fontSize: "12px", - hideTweetActions: true, - moveTweetActionsToRight: true, - themeColorTweaks: true, - revertIcons: true, - showCharacterCount: true, - forceArialFont: true, - increaseQuoteTextSize: false, - smallComposeTextSize: false, - optimizeAnimations: true, - avatarRadius: 2 - }; - - const prepareDefaultConfig = () => { - this.defaultConfig._theme = TD.settings.getTheme(); - - switch(TD.settings.getColumnWidth()){ - case "wide": this.defaultConfig.columnWidth = "350px"; break; - case "narrow": this.defaultConfig.columnWidth = "270px"; break; - } - - switch(TD.settings.getFontSize()){ - case "small": this.defaultConfig.fontSize = "13px"; break; - case "medium": this.defaultConfig.fontSize = "14px"; break; - case "large": this.defaultConfig.fontSize = "15px"; break; - case "largest": this.defaultConfig.fontSize = "16px"; break; - } - }; - - this.firstTimeLoad = null; - - const me = this; - - // configuration - const configFile = "config.json"; - - this.tmpConfig = null; - this.currentStage = 0; - - this.onStageReady = () => { - if (this.currentStage === 0){ - this.currentStage = 1; - } - else if (this.tmpConfig !== null){ - let needsResave = !("_theme" in this.tmpConfig); - - prepareDefaultConfig(); - this.config = $.extend(this.defaultConfig, this.tmpConfig); - this.tmpConfig = null; - this.reinjectAll(); - - if (this.firstTimeLoad || needsResave){ - $TDP.writeFile(this.$token, configFile, JSON.stringify(this.config)); - } - } - }; - - const loadConfigObject = obj => { - this.tmpConfig = obj || {}; - this.firstTimeLoad = obj === null; - - this.onStageReady(); - }; - - if (TD.ready){ - this.onStageReady(); - } - else{ - $(document).one("dataSettingsValues", () => this.onStageReady()); - } - - $TDP.checkFileExists(this.$token, configFile).then(exists => { - if (!exists){ - loadConfigObject(null); - } - else{ - $TDP.readFile(this.$token, configFile, true).then(contents => { - try{ - loadConfigObject(JSON.parse(contents)); - }catch(err){ - loadConfigObject(null); - } - }).catch(err => { - loadConfigObject(null); - $TD.alert("error", "Problem loading configuration for the design edit plugin: "+err.message); - }); - } - }); - - this.saveConfig = () => { - $TDP.writeFile(this.$token, configFile, JSON.stringify(this.config)).catch(err => { - $TD.alert("error", "Problem saving configuration for the design edit plugin: "+err.message); - }); - }; - - // settings click event - this.onSettingsMenuClickedEvent = () => { - if (this.config === null) { - return; - } - - setTimeout(() => { - let menu = $(".js-dropdown-content").children("ul").first(); - if (menu.length === 0) { - return; - } - - let itemTD = menu.children("[data-tweetduck]").first(); - if (itemTD.length === 0) { - return; - } - - if (!itemTD.prev().hasClass("drp-h-divider")){ - itemTD.before('<li class="drp-h-divider"></li>'); - } - - let itemEditDesign = $('<li class="is-selectable"><a href="#" data-action>Edit layout & design</a></li>'); - itemEditDesign.insertAfter(itemTD); - - itemEditDesign.on("click", "a", this.configure.bind(this)); - - itemEditDesign.hover(function(){ - $(this).addClass("is-selected"); - }, function(){ - $(this).removeClass("is-selected"); - }); - }, 2); - }; - - // modal dialog setup - const updateKey = function(key, value){ - me.config[key] = value; - - setTimeout(function(){ - me.saveConfig(); - me.reinjectAll(); - }, 1); // delays the slight lag caused by saving and reinjection - }; - - this.customDesignModal = TD.components.BaseModal.extend(function(){ - let modal = $("#td-design-plugin-modal"); - this.setAndShowContainer(modal, false); - - // RELOAD - this.reloadPage = false; - modal.find("[data-td-reload]").click(() => this.reloadPage = true); - - // UI EVENTS - let getTextForCustom = function(key){ - return "Custom ("+me.config[key]+")"; - }; - - modal.find("[data-td-key]").each(function(){ - let item = $(this); - let tag = item.prop("tagName"); - let key = item.attr("data-td-key"); - - // INPUTS - if (tag === "INPUT"){ - let type = item.attr("type"); - - if (type === "checkbox"){ - item.prop("checked", me.config[key]); - - item.change(function(){ - updateKey(key, item.prop("checked")); - }); - } - } - // SELECTS - else if (tag === "SELECT"){ - let optionCustom = item.find("option[value^='custom']"); - let optionCustomNew = item.find("option[value^='change-custom']"); - - let resetMyValue = () => { - if (!item.val(me.config[key]).val() && optionCustom.length === 1){ - item.val(optionCustom.attr("value")); - optionCustom.text(getTextForCustom(key)); - optionCustomNew.show(); - } - else{ - optionCustom.text("Custom"); - optionCustomNew.hide(); - } - }; - - resetMyValue(); - - item.change(function(){ - let val = item.val(); - - if (val.endsWith("custom-px")){ - val = (prompt("Enter custom value (px):") || "").trim(); - - if (val){ - if (val.endsWith("px")){ - val = val.slice(0, -2).trim(); - } - - if (/^[0-9]+$/.test(val)){ - updateKey(key, val+"px"); - } - else{ - alert("Invalid value, only px values are supported."); - } - } - } - else{ - updateKey(key, item.val()); - } - - resetMyValue(); - }); - } - // CUSTOM ELEMENTS - else{ - let value = item.attr("data-td-value"); - - if (value == me.config[key]){ - item.addClass("selected"); - } - - item.click(function(){ - modal.find("[data-td-key='"+key+"']").removeClass("selected"); - item.addClass("selected"); - updateKey(key, value); - }); - } - }); - - // THEMES - let selectedTheme = TD.settings.getTheme(); - - if (selectedTheme === "dark" && me.config.themeOverride === "black"){ - selectedTheme = me.config.themeOverride; - } - - modal.find("[data-td-theme='"+selectedTheme+"']").prop("checked", true); - - modal.find("[data-td-theme]").change(function(){ - let theme = $(this).attr("data-td-theme"); - me.config._theme = theme; - - if (theme === "black"){ - me.config.themeOverride = theme; - theme = "dark"; - } - else{ - me.config.themeOverride = false; - } - - setTimeout(function(){ - if (theme != TD.settings.getTheme()){ - TD.settings.setTheme(theme); - } - - me.saveConfig(); - me.reinjectAll(); - }, 1); - }); - }).methods({ - _render: function(){ - return $(me.htmlModal); - }, - destroy: function(){ - if (this.reloadPage){ - window.TDPF_requestReload(); - return; - } - - delete me.htmlModal; - - $("#td-design-plugin-modal").hide(); - this.supr(); - } - }); - - // animation optimization - this.optimizations = null; - this.optimizationTimer = null; - - let clearOptimizationTimer = () => { - if (this.optimizationTimer){ - window.clearTimeout(this.optimizationTimer); - this.optimizationTimer = null; - } - }; - - let runOptimizationTimer = timeout => { - if (!this.optimizationTimer){ - this.optimizationTimer = window.setTimeout(optimizationTimerFunc, timeout); - } - }; - - let optimizationTimerFunc = () => { - this.optimizationTimer = null; - - if (this.config.optimizeAnimations){ - $TD.getIdleSeconds().then(s => { - if (s >= 16){ - disableOptimizations(); - runOptimizationTimer(2500); - } - else{ - injectOptimizations(); - } - }); - } - }; - - let injectOptimizations = force => { - if (!this.optimizations && (force || document.hasFocus())){ - this.optimizations = window.TDPF_createCustomStyle(this); - this.optimizations.insert(".app-content { will-change: transform }"); - this.optimizations.insert(".column-holder { will-change: transform }"); - } - - clearOptimizationTimer(); - runOptimizationTimer(10000); - }; - - let disableOptimizations = () => { - if (this.optimizations){ - this.optimizations.remove(); - this.optimizations = null; - } - }; - - this.onWindowFocusEvent = () => { - if (this.config && this.config.optimizeAnimations){ - injectOptimizations(true); - } - }; - - this.onWindowBlurEvent = () => { - if (this.config && this.config.optimizeAnimations){ - disableOptimizations(); - clearOptimizationTimer(); - } - }; - - // css and layout injection - this.resetDesign = () => { - if (this.css){ - this.css.remove(); - } - - this.css = window.TDPF_createCustomStyle(this); - - if (this.theme){ - this.theme.remove(); - } - - if (this.config.themeOverride){ - this.theme = window.TDPF_createCustomStyle(this); - } - - if (this.icons){ - document.head.removeChild(this.icons); - this.icons = null; - } - }; - - this.reinjectAll = () => { - this.resetDesign(); - - clearOptimizationTimer(); - - if (this.config.optimizeAnimations){ - injectOptimizations(); - } - else{ - disableOptimizations(); - } - - this.css.insert("#general_settings .cf { display: none !important }"); - this.css.insert("#settings-modal .js-setting-list li:nth-child(3) { border-bottom: 1px solid #ccd6dd }"); - - this.css.insert(`html[data-td-font] { font-size: ${this.config.fontSize} !important }`); - this.css.insert(`.avatar { border-radius: ${this.config.avatarRadius}% !important }`); - - if (this.config.composerWidth !== "default"){ - const width = this.config.composerWidth; - this.css.insert(`.js-app-content.is-open { margin-right: ${width} !important; transform: translateX(${width}) !important }`); - this.css.insert(`#tduck .js-app-content.tduck-is-opening { margin-right: 0 !important }`); - this.css.insert(`.js-drawer { width: ${width} !important; left: -${width} !important }`); - } - - let currentTheme = TD.settings.getTheme(); - - if (currentTheme === "dark" && this.config.themeOverride){ - currentTheme = this.config.themeOverride; - } - - let notificationScrollbarColor = null; - - if (this.config.themeColorTweaks){ - switch(currentTheme){ - case "black": - this.css.insert(".app-content, .app-columns-container { background-color: #444448 !important }"); - this.css.insert(".column-header-temp { background-color: transparent !important }"); - this.css.insert(".column-drag-handle { opacity: 0.5 !important }"); - this.css.insert(".column-drag-handle:hover { opacity: 1 !important }"); - this.css.insert(".column-message.is-actionable span:hover > .icon-small-valigned { filter: saturate(20) }"); - this.css.insert(".scroll-styled-v:not(.scroll-alt)::-webkit-scrollbar-thumb:not(:hover), .scroll-styled-h:not(.scroll-alt)::-webkit-scrollbar-thumb:not(:hover) { background-color: #666 !important }"); - notificationScrollbarColor = "666"; - break; - - case "dark": - this.css.insert(".scroll-styled-v:not(.scroll-alt)::-webkit-scrollbar-track, .scroll-styled-h:not(.scroll-alt)::-webkit-scrollbar-track { border-left-color: #14171A !important }"); - break; - - case "light": - this.css.insert(".scroll-styled-v:not(.scroll-alt)::-webkit-scrollbar-thumb:not(:hover), .scroll-styled-h:not(.scroll-alt)::-webkit-scrollbar-thumb:not(:hover) { background-color: #d2d6da !important }"); - this.css.insert(".app-columns-container.scroll-styled-h::-webkit-scrollbar-thumb:not(:hover) { background-color: #a5aeb5 !important }"); - notificationScrollbarColor = "a5aeb5"; - break; - } - } - - if (this.config.showCharacterCount){ - this.css.insert("#tduck .js-character-count.is-hidden { display: inline !important }"); - } - - if (this.config.hideTweetActions){ - this.css.insert(".tweet-action { opacity: 0; }"); - this.css.insert(".tweet-actions.is-visible .tweet-action { opacity: 0.5 }"); - this.css.insert(".is-favorite .tweet-action, .is-retweet .tweet-action { opacity: 0.5; visibility: visible !important }"); - this.css.insert(".tweet:hover .tweet-action, .tweet-action.is-selected, .is-favorite .tweet-action[rel='favorite'], .is-retweet .tweet-action[rel='retweet'] { opacity: 1 !important; visibility: visible !important }"); - } - - if (this.config.moveTweetActionsToRight){ - this.css.insert("#tduck .tweet-actions { float: right !important; width: auto !important }"); - this.css.insert("#tduck .tweet-actions > li:nth-child(4) { margin-right: 2px !important }"); - } - - if (this.config.forceArialFont){ - this.css.insert("#tduck { font-family: Arial, sans-serif; font-weight: 400 }"); - this.css.insert("#tduck input, #tduck label, #tduck select, #tduck textarea { font-family: Arial }") - } - - if (this.config.increaseQuoteTextSize){ - this.css.insert(".quoted-tweet { font-size: 1em !important }"); - } - - if (this.config.smallComposeTextSize){ - this.css.insert("#tduck .compose-text { font-size: 12px !important; height: 120px !important }"); - } - - if (this.config.revertIcons){ - let iconData = [ - [ "twitter-bird", "00" ], - [ "mention", "01" ], - [ "following", "02" ], - [ "message", "03" ], - [ "home", "04" ], - [ "hashtag", "05" ], - [ "reply", "06" ], - [ "favorite", "55" ], - [ "retweet", "08" ], - [ "drafts", "09" ], - [ "search", "0a" ], - [ "trash", "0c" ], - [ "close", "0d" ], - [ "arrow-r:before,.Icon--caretRight", "0e" ], - [ "arrow-l:before,.Icon--caretLeft", "0f" ], - [ "protected", "13" ], - [ "list", "14" ], - [ "camera", "15" ], - [ "more", "16" ], - [ "settings", "18" ], - [ "notifications", "19" ], - [ "user-dd", "1a" ], - [ "activity", "1c" ], - [ "trending", "1d" ], - [ "minus", "1e" ], - [ "plus", "1f" ], - [ "geo", "20" ], - [ "check", "21" ], - [ "schedule", "22" ], - [ "dot", "23" ], - [ "user", "24" ], - [ "content", "25" ], - [ "arrow-d:before,.Icon--caretDown", "26" ], - [ "arrow-u", "27" ], - [ "share", "28" ], - [ "info", "29" ], - [ "verified", "2a" ], - [ "translator", "2b" ], - [ "blocked", "2c" ], - [ "constrain", "2d" ], - [ "play-video", "2e" ], - [ "empty", "2f" ], - [ "clear-input", "30" ], - [ "compose", "31" ], - [ "mark-read", "32" ], - [ "arrow-r-double", "33" ], - [ "arrow-l-double", "34" ], - [ "follow", "35" ], - [ "image", "36" ], - [ "popout", "37" ], - [ "move", "39" ], - [ "compose-grid", "3a" ], - [ "compose-minigrid", "3b" ], - [ "compose-list", "3c" ], - [ "edit", "40" ], - [ "clear-timeline", "41" ], - [ "sliders", "42" ], - [ "custom-timeline", "43" ], - [ "compose-dm", "44" ], - [ "bg-dot", "45" ], - [ "user-team-mgr", "46" ], - [ "user-switch", "47" ], - [ "conversation", "48" ], - [ "dataminr", "49" ], - [ "link", "4a", ], - [ "flash", "50" ], - [ "pointer-u", "51" ], - [ "analytics", "54" ], - [ "heart", "55" ], - [ "calendar", "56" ], - [ "attachment", "57" ], - [ "play", "58" ], - [ "bookmark", "59" ], - [ "play-badge", "60" ], - [ "gif-badge", "61" ], - [ "poll", "62" ], - - [ "heart-filled", "55" ], - [ "retweet-filled", "08" ], - [ "list-filled", "14" ], - [ "user-filled", "35" ], - ]; - - this.icons = document.createElement("style"); - this.icons.innerHTML = ` + // elements & data + this.css = null; + this.icons = null; + this.config = null; + + this.defaultConfig = { + _theme: "light", + themeOverride: false, + columnWidth: "310px", + composerWidth: "default", + fontSize: "12px", + hideTweetActions: true, + moveTweetActionsToRight: true, + themeColorTweaks: true, + revertIcons: true, + showCharacterCount: true, + forceArialFont: true, + increaseQuoteTextSize: false, + smallComposeTextSize: false, + optimizeAnimations: true, + avatarRadius: 2 + }; + + const prepareDefaultConfig = () => { + this.defaultConfig._theme = TD.settings.getTheme(); + + switch (TD.settings.getColumnWidth()) { + case "wide": + this.defaultConfig.columnWidth = "350px"; + break; + case "narrow": + this.defaultConfig.columnWidth = "270px"; + break; + } + + switch (TD.settings.getFontSize()) { + case "small": + this.defaultConfig.fontSize = "13px"; + break; + case "medium": + this.defaultConfig.fontSize = "14px"; + break; + case "large": + this.defaultConfig.fontSize = "15px"; + break; + case "largest": + this.defaultConfig.fontSize = "16px"; + break; + } + }; + + this.firstTimeLoad = null; + + const me = this; + + // configuration + const configFile = "config.json"; + + this.tmpConfig = null; + this.currentStage = 0; + + this.onStageReady = () => { + if (this.currentStage === 0) { + this.currentStage = 1; + } + else if (this.tmpConfig !== null) { + let needsResave = !("_theme" in this.tmpConfig); + + prepareDefaultConfig(); + this.config = $.extend(this.defaultConfig, this.tmpConfig); + this.tmpConfig = null; + this.reinjectAll(); + + if (this.firstTimeLoad || needsResave) { + $TDP.writeFile(this.$token, configFile, JSON.stringify(this.config)); + } + } + }; + + const loadConfigObject = obj => { + this.tmpConfig = obj || {}; + this.firstTimeLoad = obj === null; + + this.onStageReady(); + }; + + if (TD.ready) { + this.onStageReady(); + } + else { + $(document).one("dataSettingsValues", () => this.onStageReady()); + } + + $TDP.checkFileExists(this.$token, configFile).then(exists => { + if (!exists) { + loadConfigObject(null); + } + else { + $TDP.readFile(this.$token, configFile, true).then(contents => { + try { + loadConfigObject(JSON.parse(contents)); + } catch (err) { + loadConfigObject(null); + } + }).catch(err => { + loadConfigObject(null); + $TD.alert("error", "Problem loading configuration for the design edit plugin: " + err.message); + }); + } + }); + + this.saveConfig = () => { + $TDP.writeFile(this.$token, configFile, JSON.stringify(this.config)).catch(err => { + $TD.alert("error", "Problem saving configuration for the design edit plugin: " + err.message); + }); + }; + + // settings click event + this.onSettingsMenuClickedEvent = () => { + if (this.config === null) { + return; + } + + setTimeout(() => { + let menu = $(".js-dropdown-content").children("ul").first(); + if (menu.length === 0) { + return; + } + + let itemTD = menu.children("[data-tweetduck]").first(); + if (itemTD.length === 0) { + return; + } + + if (!itemTD.prev().hasClass("drp-h-divider")) { + itemTD.before("<li class=\"drp-h-divider\"></li>"); + } + + let itemEditDesign = $("<li class=\"is-selectable\"><a href=\"#\" data-action>Edit layout & design</a></li>"); + itemEditDesign.insertAfter(itemTD); + + itemEditDesign.on("click", "a", this.configure.bind(this)); + + itemEditDesign.hover(function() { + $(this).addClass("is-selected"); + }, function() { + $(this).removeClass("is-selected"); + }); + }, 2); + }; + + // modal dialog setup + const updateKey = function(key, value) { + me.config[key] = value; + + setTimeout(function() { + me.saveConfig(); + me.reinjectAll(); + }, 1); // delays the slight lag caused by saving and reinjection + }; + + this.customDesignModal = TD.components.BaseModal.extend(function() { + let modal = $("#td-design-plugin-modal"); + this.setAndShowContainer(modal, false); + + // RELOAD + this.reloadPage = false; + modal.find("[data-td-reload]").click(() => this.reloadPage = true); + + // UI EVENTS + let getTextForCustom = function(key) { + return "Custom (" + me.config[key] + ")"; + }; + + modal.find("[data-td-key]").each(function() { + let item = $(this); + let tag = item.prop("tagName"); + let key = item.attr("data-td-key"); + + // INPUTS + if (tag === "INPUT") { + let type = item.attr("type"); + + if (type === "checkbox") { + item.prop("checked", me.config[key]); + + item.change(function() { + updateKey(key, item.prop("checked")); + }); + } + } + // SELECTS + else if (tag === "SELECT") { + let optionCustom = item.find("option[value^='custom']"); + let optionCustomNew = item.find("option[value^='change-custom']"); + + let resetMyValue = () => { + if (!item.val(me.config[key]).val() && optionCustom.length === 1) { + item.val(optionCustom.attr("value")); + optionCustom.text(getTextForCustom(key)); + optionCustomNew.show(); + } + else { + optionCustom.text("Custom"); + optionCustomNew.hide(); + } + }; + + resetMyValue(); + + item.change(function() { + let val = item.val(); + + if (val.endsWith("custom-px")) { + val = (prompt("Enter custom value (px):") || "").trim(); + + if (val) { + if (val.endsWith("px")) { + val = val.slice(0, -2).trim(); + } + + if (/^[0-9]+$/.test(val)) { + updateKey(key, val + "px"); + } + else { + alert("Invalid value, only px values are supported."); + } + } + } + else { + updateKey(key, item.val()); + } + + resetMyValue(); + }); + } + // CUSTOM ELEMENTS + else { + let value = item.attr("data-td-value"); + + if (value == me.config[key]) { + item.addClass("selected"); + } + + item.click(function() { + modal.find("[data-td-key='" + key + "']").removeClass("selected"); + item.addClass("selected"); + updateKey(key, value); + }); + } + }); + + // THEMES + let selectedTheme = TD.settings.getTheme(); + + if (selectedTheme === "dark" && me.config.themeOverride === "black") { + selectedTheme = me.config.themeOverride; + } + + modal.find("[data-td-theme='" + selectedTheme + "']").prop("checked", true); + + modal.find("[data-td-theme]").change(function() { + let theme = $(this).attr("data-td-theme"); + me.config._theme = theme; + + if (theme === "black") { + me.config.themeOverride = theme; + theme = "dark"; + } + else { + me.config.themeOverride = false; + } + + setTimeout(function() { + if (theme != TD.settings.getTheme()) { + TD.settings.setTheme(theme); + } + + me.saveConfig(); + me.reinjectAll(); + }, 1); + }); + }).methods({ + _render: function() { + return $(me.htmlModal); + }, + destroy: function() { + if (this.reloadPage) { + window.TDPF_requestReload(); + return; + } + + delete me.htmlModal; + + $("#td-design-plugin-modal").hide(); + this.supr(); + } + }); + + // animation optimization + this.optimizations = null; + this.optimizationTimer = null; + + let clearOptimizationTimer = () => { + if (this.optimizationTimer) { + window.clearTimeout(this.optimizationTimer); + this.optimizationTimer = null; + } + }; + + let runOptimizationTimer = timeout => { + if (!this.optimizationTimer) { + this.optimizationTimer = window.setTimeout(optimizationTimerFunc, timeout); + } + }; + + let optimizationTimerFunc = () => { + this.optimizationTimer = null; + + if (this.config.optimizeAnimations) { + $TD.getIdleSeconds().then(s => { + if (s >= 16) { + disableOptimizations(); + runOptimizationTimer(2500); + } + else { + injectOptimizations(); + } + }); + } + }; + + let injectOptimizations = force => { + if (!this.optimizations && (force || document.hasFocus())) { + this.optimizations = window.TDPF_createCustomStyle(this); + this.optimizations.insert(".app-content { will-change: transform }"); + this.optimizations.insert(".column-holder { will-change: transform }"); + } + + clearOptimizationTimer(); + runOptimizationTimer(10000); + }; + + let disableOptimizations = () => { + if (this.optimizations) { + this.optimizations.remove(); + this.optimizations = null; + } + }; + + this.onWindowFocusEvent = () => { + if (this.config && this.config.optimizeAnimations) { + injectOptimizations(true); + } + }; + + this.onWindowBlurEvent = () => { + if (this.config && this.config.optimizeAnimations) { + disableOptimizations(); + clearOptimizationTimer(); + } + }; + + // css and layout injection + this.resetDesign = () => { + if (this.css) { + this.css.remove(); + } + + this.css = window.TDPF_createCustomStyle(this); + + if (this.theme) { + this.theme.remove(); + } + + if (this.config.themeOverride) { + this.theme = window.TDPF_createCustomStyle(this); + } + + if (this.icons) { + document.head.removeChild(this.icons); + this.icons = null; + } + }; + + this.reinjectAll = () => { + this.resetDesign(); + + clearOptimizationTimer(); + + if (this.config.optimizeAnimations) { + injectOptimizations(); + } + else { + disableOptimizations(); + } + + this.css.insert("#general_settings .cf { display: none !important }"); + this.css.insert("#settings-modal .js-setting-list li:nth-child(3) { border-bottom: 1px solid #ccd6dd }"); + + this.css.insert(`html[data-td-font] { font-size: ${this.config.fontSize} !important }`); + this.css.insert(`.avatar { border-radius: ${this.config.avatarRadius}% !important }`); + + if (this.config.composerWidth !== "default") { + const width = this.config.composerWidth; + this.css.insert(`.js-app-content.is-open { margin-right: ${width} !important; transform: translateX(${width}) !important }`); + this.css.insert(`#tduck .js-app-content.tduck-is-opening { margin-right: 0 !important }`); + this.css.insert(`.js-drawer { width: ${width} !important; left: -${width} !important }`); + } + + let currentTheme = TD.settings.getTheme(); + + if (currentTheme === "dark" && this.config.themeOverride) { + currentTheme = this.config.themeOverride; + } + + let notificationScrollbarColor = null; + + if (this.config.themeColorTweaks) { + switch (currentTheme) { + case "black": + this.css.insert(".app-content, .app-columns-container { background-color: #444448 !important }"); + this.css.insert(".column-header-temp { background-color: transparent !important }"); + this.css.insert(".column-drag-handle { opacity: 0.5 !important }"); + this.css.insert(".column-drag-handle:hover { opacity: 1 !important }"); + this.css.insert(".column-message.is-actionable span:hover > .icon-small-valigned { filter: saturate(20) }"); + this.css.insert(".scroll-styled-v:not(.scroll-alt)::-webkit-scrollbar-thumb:not(:hover), .scroll-styled-h:not(.scroll-alt)::-webkit-scrollbar-thumb:not(:hover) { background-color: #666 !important }"); + notificationScrollbarColor = "666"; + break; + + case "dark": + this.css.insert(".scroll-styled-v:not(.scroll-alt)::-webkit-scrollbar-track, .scroll-styled-h:not(.scroll-alt)::-webkit-scrollbar-track { border-left-color: #14171A !important }"); + break; + + case "light": + this.css.insert(".scroll-styled-v:not(.scroll-alt)::-webkit-scrollbar-thumb:not(:hover), .scroll-styled-h:not(.scroll-alt)::-webkit-scrollbar-thumb:not(:hover) { background-color: #d2d6da !important }"); + this.css.insert(".app-columns-container.scroll-styled-h::-webkit-scrollbar-thumb:not(:hover) { background-color: #a5aeb5 !important }"); + notificationScrollbarColor = "a5aeb5"; + break; + } + } + + if (this.config.showCharacterCount) { + this.css.insert("#tduck .js-character-count.is-hidden { display: inline !important }"); + } + + if (this.config.hideTweetActions) { + this.css.insert(".tweet-action { opacity: 0; }"); + this.css.insert(".tweet-actions.is-visible .tweet-action { opacity: 0.5 }"); + this.css.insert(".is-favorite .tweet-action, .is-retweet .tweet-action { opacity: 0.5; visibility: visible !important }"); + this.css.insert(".tweet:hover .tweet-action, .tweet-action.is-selected, .is-favorite .tweet-action[rel='favorite'], .is-retweet .tweet-action[rel='retweet'] { opacity: 1 !important; visibility: visible !important }"); + } + + if (this.config.moveTweetActionsToRight) { + this.css.insert("#tduck .tweet-actions { float: right !important; width: auto !important }"); + this.css.insert("#tduck .tweet-actions > li:nth-child(4) { margin-right: 2px !important }"); + } + + if (this.config.forceArialFont) { + this.css.insert("#tduck { font-family: Arial, sans-serif; font-weight: 400 }"); + this.css.insert("#tduck input, #tduck label, #tduck select, #tduck textarea { font-family: Arial }"); + } + + if (this.config.increaseQuoteTextSize) { + this.css.insert(".quoted-tweet { font-size: 1em !important }"); + } + + if (this.config.smallComposeTextSize) { + this.css.insert("#tduck .compose-text { font-size: 12px !important; height: 120px !important }"); + } + + if (this.config.revertIcons) { + let iconData = [ + [ "twitter-bird", "00" ], + [ "mention", "01" ], + [ "following", "02" ], + [ "message", "03" ], + [ "home", "04" ], + [ "hashtag", "05" ], + [ "reply", "06" ], + [ "favorite", "55" ], + [ "retweet", "08" ], + [ "drafts", "09" ], + [ "search", "0a" ], + [ "trash", "0c" ], + [ "close", "0d" ], + [ "arrow-r:before,.Icon--caretRight", "0e" ], + [ "arrow-l:before,.Icon--caretLeft", "0f" ], + [ "protected", "13" ], + [ "list", "14" ], + [ "camera", "15" ], + [ "more", "16" ], + [ "settings", "18" ], + [ "notifications", "19" ], + [ "user-dd", "1a" ], + [ "activity", "1c" ], + [ "trending", "1d" ], + [ "minus", "1e" ], + [ "plus", "1f" ], + [ "geo", "20" ], + [ "check", "21" ], + [ "schedule", "22" ], + [ "dot", "23" ], + [ "user", "24" ], + [ "content", "25" ], + [ "arrow-d:before,.Icon--caretDown", "26" ], + [ "arrow-u", "27" ], + [ "share", "28" ], + [ "info", "29" ], + [ "verified", "2a" ], + [ "translator", "2b" ], + [ "blocked", "2c" ], + [ "constrain", "2d" ], + [ "play-video", "2e" ], + [ "empty", "2f" ], + [ "clear-input", "30" ], + [ "compose", "31" ], + [ "mark-read", "32" ], + [ "arrow-r-double", "33" ], + [ "arrow-l-double", "34" ], + [ "follow", "35" ], + [ "image", "36" ], + [ "popout", "37" ], + [ "move", "39" ], + [ "compose-grid", "3a" ], + [ "compose-minigrid", "3b" ], + [ "compose-list", "3c" ], + [ "edit", "40" ], + [ "clear-timeline", "41" ], + [ "sliders", "42" ], + [ "custom-timeline", "43" ], + [ "compose-dm", "44" ], + [ "bg-dot", "45" ], + [ "user-team-mgr", "46" ], + [ "user-switch", "47" ], + [ "conversation", "48" ], + [ "dataminr", "49" ], + [ "link", "4a", ], + [ "flash", "50" ], + [ "pointer-u", "51" ], + [ "analytics", "54" ], + [ "heart", "55" ], + [ "calendar", "56" ], + [ "attachment", "57" ], + [ "play", "58" ], + [ "bookmark", "59" ], + [ "play-badge", "60" ], + [ "gif-badge", "61" ], + [ "poll", "62" ], + + [ "heart-filled", "55" ], + [ "retweet-filled", "08" ], + [ "list-filled", "14" ], + [ "user-filled", "35" ], + ]; + + this.icons = document.createElement("style"); + this.icons.innerHTML = ` @font-face { font-family: '_of'; src: url("https://ton.twimg.com/tweetdeck-web/web/assets/fonts/tweetdeck-regular-webfont.5f4ea87976.woff") format("woff"); @@ -567,52 +579,60 @@ ${iconData.map(entry => `#tduck .icon-${entry[0]}:before{content:\"\\f0${entry[1 .tweet-action-item .icon-favorite-toggle { font-size: 16px !important; } .tweet-action-item .heartsprite { top: -260% !important; left: -260% !important; transform: scale(0.4, 0.39) translateY(0.5px) !important; } .tweet-footer { margin-top: 6px !important }`; - - document.head.appendChild(this.icons); - } - - if (currentTheme === "black"){ - $TDP.readFileRoot(this.$token, "theme.black.css").then(contents => { - if (this.theme){ - this.theme.element.innerHTML = contents; - TD.settings.setTheme("dark"); // forces refresh of notification head tag - } - }); - } - - if (this.config.columnWidth[0] === '/'){ - let cols = this.config.columnWidth.slice(1); - - this.css.insert(".column { width: calc((100vw - 205px) / "+cols+" - 6px) !important; min-width: 160px }"); - this.css.insert(".is-condensed .column { width: calc((100vw - 65px) / "+cols+" - 6px) !important }"); - } - else{ - this.css.insert(".column { width: "+this.config.columnWidth+" !important }"); - } - - switch(this.config.columnWidth){ - case "/6": - TD.settings.setColumnWidth("narrow"); - break; - - case "310px": - case "/5": - TD.settings.setColumnWidth("medium"); - break; - - default: - TD.settings.setColumnWidth(parseInt(this.config.columnWidth, 10) < 310 ? "narrow" : "wide"); // NaN will give "wide" - break; - } - - switch(this.config.fontSize){ - case "13px": TD.settings.setFontSize("small"); break; - case "14px": TD.settings.setFontSize("medium"); break; - case "15px": TD.settings.setFontSize("large"); break; - default: TD.settings.setFontSize(parseInt(this.config.fontSize, 10) >= 16 ? "largest" : "smallest"); break; - } - - $TDP.injectIntoNotificationsBefore(this.$token, "css", "</head>", ` + + document.head.appendChild(this.icons); + } + + if (currentTheme === "black") { + $TDP.readFileRoot(this.$token, "theme.black.css").then(contents => { + if (this.theme) { + this.theme.element.innerHTML = contents; + TD.settings.setTheme("dark"); // forces refresh of notification head tag + } + }); + } + + if (this.config.columnWidth[0] === "/") { + let cols = this.config.columnWidth.slice(1); + + this.css.insert(".column { width: calc((100vw - 205px) / " + cols + " - 6px) !important; min-width: 160px }"); + this.css.insert(".is-condensed .column { width: calc((100vw - 65px) / " + cols + " - 6px) !important }"); + } + else { + this.css.insert(".column { width: " + this.config.columnWidth + " !important }"); + } + + switch (this.config.columnWidth) { + case "/6": + TD.settings.setColumnWidth("narrow"); + break; + + case "310px": + case "/5": + TD.settings.setColumnWidth("medium"); + break; + + default: + TD.settings.setColumnWidth(parseInt(this.config.columnWidth, 10) < 310 ? "narrow" : "wide"); // NaN will give "wide" + break; + } + + switch (this.config.fontSize) { + case "13px": + TD.settings.setFontSize("small"); + break; + case "14px": + TD.settings.setFontSize("medium"); + break; + case "15px": + TD.settings.setFontSize("large"); + break; + default: + TD.settings.setFontSize(parseInt(this.config.fontSize, 10) >= 16 ? "largest" : "smallest"); + break; + } + + $TDP.injectIntoNotificationsBefore(this.$token, "css", "</head>", ` <style type='text/css'> html[data-td-font] { font-size: ${this.config.fontSize} !important } .avatar { border-radius: ${this.config.avatarRadius}% !important } @@ -645,103 +665,102 @@ ${notificationScrollbarColor ? ` .scroll-styled-v::-webkit-scrollbar-thumb:not(:hover), .scroll-styled-h::-webkit-scrollbar-thumb:not(:hover) { background-color: #${notificationScrollbarColor} !important } ` : ``} </style>`); - }; - - this.uiShowActionsMenuEvent = () => { - if (this.config.moveTweetActionsToRight){ - $(".js-dropdown.pos-r").toggleClass("pos-r pos-l"); - } - }; - - this.uiDrawerActiveEvent = (e, data) => { - if (data.activeDrawer === null || this.config.composerWidth === "default") { - return; - } - - const ele = $(".js-app-content").addClass("tduck-is-opening"); - setTimeout(() => ele.removeClass("tduck-is-opening"), 250); - }; + }; + + this.uiShowActionsMenuEvent = () => { + if (this.config.moveTweetActionsToRight) { + $(".js-dropdown.pos-r").toggleClass("pos-r pos-l"); + } + }; + + this.uiDrawerActiveEvent = (e, data) => { + if (data.activeDrawer === null || this.config.composerWidth === "default") { + return; + } + + const ele = $(".js-app-content").addClass("tduck-is-opening"); + setTimeout(() => ele.removeClass("tduck-is-opening"), 250); + }; } ready(){ - // optimization events - $(window).on("focus", this.onWindowFocusEvent); - $(window).on("blur", this.onWindowBlurEvent); - - // layout events - $(document).on("uiShowActionsMenu", this.uiShowActionsMenuEvent); - $(document).on("uiDrawerActive", this.uiDrawerActiveEvent); - - // modal - $("[data-action='settings-menu']").on("click", this.onSettingsMenuClickedEvent); - $(".js-app").append('<div id="td-design-plugin-modal" class="js-modal settings-modal ovl scroll-v scroll-styled-v"></div>'); - - // global settings override - const me = this; - - this.prevFuncSettingsGetInfo = TD.components.GlobalSettings.prototype.getInfo; - this.prevFuncSettingsSwitchTab = TD.components.GlobalSettings.prototype.switchTab; - - TD.components.GlobalSettings.prototype.getInfo = function(){ - let data = me.prevFuncSettingsGetInfo.apply(this, arguments); - - data.tabs.push({ - title: "Layout & Design", - action: "tdp-edit-design" - }); - - return data; - }; - - TD.components.GlobalSettings.prototype.switchTab = function(tab){ - if (tab === "tdp-edit-design"){ - me.configure(); - } - else{ - return me.prevFuncSettingsSwitchTab.apply(this, arguments); - } - }; + // optimization events + $(window).on("focus", this.onWindowFocusEvent); + $(window).on("blur", this.onWindowBlurEvent); + + // layout events + $(document).on("uiShowActionsMenu", this.uiShowActionsMenuEvent); + $(document).on("uiDrawerActive", this.uiDrawerActiveEvent); + + // modal + $("[data-action='settings-menu']").on("click", this.onSettingsMenuClickedEvent); + $(".js-app").append("<div id=\"td-design-plugin-modal\" class=\"js-modal settings-modal ovl scroll-v scroll-styled-v\"></div>"); + + // global settings override + const me = this; + + this.prevFuncSettingsGetInfo = TD.components.GlobalSettings.prototype.getInfo; + this.prevFuncSettingsSwitchTab = TD.components.GlobalSettings.prototype.switchTab; + + TD.components.GlobalSettings.prototype.getInfo = function() { + let data = me.prevFuncSettingsGetInfo.apply(this, arguments); + + data.tabs.push({ + title: "Layout & Design", action: "tdp-edit-design" + }); + + return data; + }; + + TD.components.GlobalSettings.prototype.switchTab = function(tab) { + if (tab === "tdp-edit-design") { + me.configure(); + } + else { + return me.prevFuncSettingsSwitchTab.apply(this, arguments); + } + }; } configure(){ - $TDP.readFileRoot(this.$token, "modal.html").then(contents => { - this.htmlModal = contents; - new this.customDesignModal(); - }).catch(err => { - $TD.alert("error", "Error loading the configuration dialog: "+err.message); - }); + $TDP.readFileRoot(this.$token, "modal.html").then(contents => { + this.htmlModal = contents; + new this.customDesignModal(); + }).catch(err => { + $TD.alert("error", "Error loading the configuration dialog: " + err.message); + }); } disabled(){ - if (this.css){ - this.css.remove(); - } - - if (this.theme){ - this.theme.remove(); - } - - if (this.icons){ - document.head.removeChild(this.icons); - } - - if (this.optimizations){ - this.optimizations.remove(); - } - - if (this.optimizationTimer){ - window.clearTimeout(this.optimizationTimer); - } - - $(window).off("focus", this.onWindowFocusEvent); - $(window).off("blur", this.onWindowBlurEvent); - - $(document).off("uiShowActionsMenu", this.uiShowActionsMenuEvent); - $(document).off("uiDrawerActive", this.uiDrawerActiveEvent); - - TD.components.GlobalSettings.prototype.getInfo = this.prevFuncSettingsGetInfo; - TD.components.GlobalSettings.prototype.switchTab = this.prevFuncSettingsSwitchTab; - - $("[data-action='settings-menu']").off("click", this.onSettingsMenuClickedEvent); - $("#td-design-plugin-modal").remove(); + if (this.css) { + this.css.remove(); + } + + if (this.theme) { + this.theme.remove(); + } + + if (this.icons) { + document.head.removeChild(this.icons); + } + + if (this.optimizations) { + this.optimizations.remove(); + } + + if (this.optimizationTimer) { + window.clearTimeout(this.optimizationTimer); + } + + $(window).off("focus", this.onWindowFocusEvent); + $(window).off("blur", this.onWindowBlurEvent); + + $(document).off("uiShowActionsMenu", this.uiShowActionsMenuEvent); + $(document).off("uiDrawerActive", this.uiDrawerActiveEvent); + + TD.components.GlobalSettings.prototype.getInfo = this.prevFuncSettingsGetInfo; + TD.components.GlobalSettings.prototype.switchTab = this.prevFuncSettingsSwitchTab; + + $("[data-action='settings-menu']").off("click", this.onSettingsMenuClickedEvent); + $("#td-design-plugin-modal").remove(); } diff --git a/Resources/Plugins/edit-design/modal.html b/Resources/Plugins/edit-design/modal.html index 3924c9ed..b0c572c1 100644 --- a/Resources/Plugins/edit-design/modal.html +++ b/Resources/Plugins/edit-design/modal.html @@ -143,9 +143,9 @@ </div> <div class="l-column mdl-column"> - + <!-- AVATAR SHAPE --> - + <label class="txt-uppercase touch-larger-label"> <b>Avatar shape</b> </label> @@ -187,12 +187,12 @@ #edit-design-panel { width: 693px; height: 424px; - background-color: #FFF; + background-color: #fff; box-shadow: 0 0 10px rgba(17, 17, 17, 0.5); } #edit-design-panel .mdl-header { - color: #8899A6; + color: #8899a6; } #edit-design-panel .mdl-inner { @@ -200,8 +200,8 @@ } #edit-design-panel .mdl-content { - border: 1px solid #CCD6DD; - background: #EAEAEA; + border: 1px solid #ccd6dd; + background: #eaeaea; } #edit-design-panel-inner-cols { @@ -260,8 +260,8 @@ padding: 16px 14px 8px; box-sizing: border-box; box-shadow: 0 0 4px rgba(0, 0, 0, 0.1); - border: 2px solid #F5F8FA; - background-color: #F5F8FA; + border: 2px solid #f5f8fa; + background-color: #f5f8fa; } .td-avatar-shape-item-outer:hover { @@ -279,6 +279,6 @@ .td-avatar-shape { width: 48px; height: 48px; - background-color: #71BAF2; + background-color: #71baf2; } </style> diff --git a/Resources/Plugins/emoji-keyboard/browser.js b/Resources/Plugins/emoji-keyboard/browser.js index 79470660..07add591 100644 --- a/Resources/Plugins/emoji-keyboard/browser.js +++ b/Resources/Plugins/emoji-keyboard/browser.js @@ -1,565 +1,571 @@ enabled(){ - this.ENABLE_CUSTOM_KEYBOARD = false; - - this.selectedSkinTone = ""; - this.currentKeywords = []; - - this.skinToneList = [ - "", "1F3FB", "1F3FC", "1F3FD", "1F3FE", "1F3FF" - ]; - - this.skinToneNonDefaultList = [ - "1F3FB", "1F3FC", "1F3FD", "1F3FE", "1F3FF" - ]; - - this.skinToneData = [ - [ "", "#FFDD67" ], - [ "1F3FB", "#FFE1BD" ], - [ "1F3FC", "#FED0AC" ], - [ "1F3FD", "#D6A57C" ], - [ "1F3FE", "#B47D56" ], - [ "1F3FF", "#8A6859" ], - ]; - - this.emojiData1 = []; // no skin tones, prepended - this.emojiData2 = {}; // contains emojis with skin tones - this.emojiData3 = []; // no skin tones, appended - this.emojiNames = []; - - const me = this; - - // styles - - this.css = window.TDPF_createCustomStyle(this); - this.css.insert(".emoji-keyboard { position: absolute; width: 15.35em; background-color: white; border-radius: 1px; font-size: 24px; z-index: 9999 }"); - this.css.insert(".emoji-keyboard-popup-btn { height: 36px !important }"); - this.css.insert(".emoji-keyboard-popup-btn .icon { vertical-align: -4px !important }"); - - this.css.insert(".emoji-keyboard-list { height: 10.14em; padding: 0.1em; box-sizing: border-box; overflow-y: auto }"); - this.css.insert(".emoji-keyboard-list .separator { height: 26px }"); - this.css.insert(".emoji-keyboard-list img { padding: 0.1em !important; width: 1em; height: 1em; vertical-align: -0.1em; cursor: pointer }"); - - this.css.insert(".emoji-keyboard-search { height: auto; padding: 4px 10px 8px; background-color: #292f33; border-radius: 1px 1px 0 0 }"); - this.css.insert(".emoji-keyboard-search input { width: 100%; border-radius: 1px; }"); - - this.css.insert(".emoji-keyboard-skintones { height: 1.3em; text-align: center; background-color: #292f33; border-radius: 0 0 1px 1px }"); - 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(".js-compose-text { font-family: \"Twitter Color Emoji\", Helvetica, Arial, Verdana, sans-serif; }"); - - // layout - - let buttonHTML = '<button class="needsclick btn btn-on-blue txt-left padding-v--6 padding-h--8 emoji-keyboard-popup-btn"><i class="icon icon-heart"></i></button>'; - - this.prevComposeMustache = TD.mustaches["compose/docked_compose.mustache"]; - window.TDPF_injectMustache("compose/docked_compose.mustache", "append", '<div class="cf margin-t--12 margin-b--30">', buttonHTML); - - this.getDrawerInput = () => { - return $(".js-compose-text", me.composeDrawer); - }; - - this.getDrawerScroller = () => { - return $(".js-compose-scroller > .scroll-v", me.composeDrawer); - }; - - // keyboard generation - - this.currentKeyboard = null; - this.currentSpanner = null; - - let wasSearchFocused = false; - let lastEmojiKeyword, lastEmojiPosition, lastEmojiLength; - - const hideKeyboard = (refocus) => { - $(this.currentKeyboard).remove(); - this.currentKeyboard = null; - - $(this.currentSpanner).remove(); - this.currentSpanner = null; - - this.currentKeywords = []; - - this.getDrawerScroller().trigger("scroll"); - - $(".emoji-keyboard-popup-btn").removeClass("is-selected"); - - if (refocus){ - this.getDrawerInput().focus(); - - if (lastEmojiKeyword && lastEmojiPosition === 0){ - document.execCommand("insertText", false, lastEmojiKeyword); - } - } - - lastEmojiKeyword = null; - }; - - const generateEmojiHTML = skinTone => { - let index = 0; - let html = [ "<p style='font-size:13px;color:#444;margin:4px;text-align:center'>Please, note that some emoji may not show up correctly in the text box above, but they will display in the tweet.</p>" ]; - - for(let array of [ this.emojiData1, this.emojiData2[skinTone], this.emojiData3 ]){ - for(let emoji of array){ - if (emoji === "___"){ - html.push("<div class='separator'></div>"); - } - else{ - html.push(TD.util.cleanWithEmoji(emoji).replace(' class="emoji" draggable="false"', '')); - index++; - } - } - } - - return html.join(""); - }; - - const updateFilters = () => { - let keywords = this.currentKeywords; - let container = $(this.currentKeyboard.children[1]); - - let emoji = container.children("img"); - let info = container.children("p:first"); - let separators = container.children("div"); - - if (keywords.length === 0){ - info.css("display", "block"); - separators.css("display", "block"); - emoji.css("display", "inline"); - } - else{ - info.css("display", "none"); - separators.css("display", "none"); - - emoji.css("display", "none"); - emoji.filter(index => keywords.every(kw => me.emojiNames[index].includes(kw))).css("display", "inline"); - } - }; - - const selectSkinTone = skinTone => { - let selectedEle = this.currentKeyboard.children[2].querySelector("[data-tone='"+this.selectedSkinTone+"']"); - selectedEle && selectedEle.classList.remove("sel"); - - this.selectedSkinTone = skinTone; - this.currentKeyboard.children[1].innerHTML = generateEmojiHTML(skinTone); - this.currentKeyboard.children[2].querySelector("[data-tone='"+this.selectedSkinTone+"']").classList.add("sel"); - updateFilters(); - }; - - this.generateKeyboard = (left, top) => { - let outer = document.createElement("div"); - outer.classList.add("emoji-keyboard"); - outer.style.left = left+"px"; - outer.style.top = top+"px"; - - let keyboard = document.createElement("div"); - keyboard.classList.add("emoji-keyboard-list"); - - keyboard.addEventListener("click", function(e){ - let ele = e.target; - - if (ele.tagName === "IMG"){ - insertEmoji(ele.getAttribute("src"), ele.getAttribute("alt")); - } - - e.stopPropagation(); - }); - - let search = document.createElement("div"); - search.innerHTML = "<input type='text' placeholder='Search...'>"; - search.classList.add("emoji-keyboard-search"); - - let skintones = document.createElement("div"); - skintones.innerHTML = me.skinToneData.map(entry => "<div data-tone='"+entry[0]+"' style='background-color:"+entry[1]+"'></div>").join(""); - skintones.classList.add("emoji-keyboard-skintones"); - - outer.appendChild(search); - outer.appendChild(keyboard); - outer.appendChild(skintones); - $(".js-app").append(outer); - - skintones.addEventListener("click", function(e){ - if (e.target.hasAttribute("data-tone")){ - selectSkinTone(e.target.getAttribute("data-tone") || ""); - } - - e.stopPropagation(); - }); - - search.addEventListener("click", function(e){ - e.stopPropagation(); - }); - - let searchInput = search.children[0]; - searchInput.focus(); - - wasSearchFocused = false; - - searchInput.addEventListener("input", function(e){ - me.currentKeywords = e.target.value.split(" ").filter(kw => kw.length > 0).map(kw => kw.toLowerCase()); - updateFilters(); - - wasSearchFocused = $(this).val().length > 0; - e.stopPropagation(); - }); - - searchInput.addEventListener("keydown", function(e){ - if (e.keyCode === 13 && $(this).val().length){ // enter - let ele = $(".emoji-keyboard-list").children("img").filter(":visible").first(); - - if (ele.length > 0){ - insertEmoji(ele[0].getAttribute("src"), ele[0].getAttribute("alt")); - } - - e.preventDefault(); - } - }); - - searchInput.addEventListener("click", function(){ - $(this).select(); - }); - - searchInput.addEventListener("focus", function(){ - wasSearchFocused = true; - }); - - this.currentKeyboard = outer; - selectSkinTone(this.selectedSkinTone); - - this.currentSpanner = document.createElement("div"); - this.currentSpanner.style.height = ($(this.currentKeyboard).height()-10)+"px"; - $(".emoji-keyboard-popup-btn").parent().after(this.currentSpanner); - - this.getDrawerScroller().trigger("scroll"); - }; - - const getKeyboardTop = () => { - let button = $(".emoji-keyboard-popup-btn"); - return button.offset().top + button.outerHeight() + me.getDrawerScroller().scrollTop() + 8; - }; - - const insertEmoji = (src, alt) => { - let input = this.getDrawerInput(); - - let val = input.val(); - let posStart = input[0].selectionStart; - let posEnd = input[0].selectionEnd; - - input.val(val.slice(0, posStart)+alt+val.slice(posEnd)); - input.trigger("change"); - - input[0].selectionStart = posStart+alt.length; - input[0].selectionEnd = posStart+alt.length; - - lastEmojiKeyword = null; - - if (wasSearchFocused){ - $(".emoji-keyboard-search").children("input").focus(); - } - else{ - input.focus(); - } - }; - - // general event handlers - - this.emojiKeyboardButtonClickEvent = function(e){ - if (me.currentKeyboard){ - $(this).blur(); - hideKeyboard(true); - } - else{ - me.generateKeyboard($(this).offset().left, getKeyboardTop()); - $(this).addClass("is-selected"); - } - - e.stopPropagation(); - }; - - this.composerScrollEvent = function(e){ - if (me.currentKeyboard){ - me.currentKeyboard.style.marginTop = (-$(this).scrollTop())+"px"; - } - }; - - this.composeInputKeyDownEvent = function(e){ - if (lastEmojiKeyword && (e.keyCode === 8 || e.keyCode === 27)){ // backspace, escape - let ele = $(this)[0]; - - if (ele.selectionStart === lastEmojiPosition){ - ele.selectionStart -= lastEmojiLength; // selects the emoji - document.execCommand("insertText", false, lastEmojiKeyword); - - e.preventDefault(); - e.stopPropagation(); - } - - lastEmojiKeyword = null; - } - }; - - this.composeInputKeyPressEvent = function(e){ - if (String.fromCharCode(e.which) === ':'){ - let ele = $(this); - let val = ele.val(); - - let firstColon = val.lastIndexOf(':', ele[0].selectionStart); - if (firstColon === -1) { - return; - } - - let search = val.substring(firstColon+1, ele[0].selectionStart).toLowerCase(); - if (!/^[a-z_]+$/.test(search)) { - return; - } - - let keywords = search.split("_").filter(kw => kw.length > 0).map(kw => kw.toLowerCase()); - if (keywords.length === 0) { - return; - } - - let foundNames = me.emojiNames.filter(name => keywords.every(kw => name.includes(kw))); - - if (foundNames.length === 0){ - return; - } - else if (foundNames.length > 1 && foundNames.includes(search)){ - foundNames = [ search ]; - } - - lastEmojiKeyword = `:${search}:`; - lastEmojiPosition = lastEmojiLength = 0; - - if (foundNames.length === 1){ - let foundIndex = me.emojiNames.indexOf(foundNames[0]); - let foundEmoji; - - for(let array of [ me.emojiData1, me.emojiData2[me.selectedSkinTone], me.emojiData3 ]){ - let realArray = array.filter(ele => ele !== "___"); - - if (foundIndex >= realArray.length){ - foundIndex -= realArray.length; - } - else{ - foundEmoji = realArray[foundIndex]; - break; - } - } - - if (foundEmoji){ - e.preventDefault(); - - ele.val(val.substring(0, firstColon)+foundEmoji+val.substring(ele[0].selectionStart)); - ele[0].selectionEnd = ele[0].selectionStart = firstColon+foundEmoji.length; - ele.trigger("change"); - ele.focus(); - - lastEmojiPosition = firstColon+foundEmoji.length; - lastEmojiLength = foundEmoji.length; - } - } - else if (foundNames.length > 1 && $(".js-app-content").is(".is-open")){ - e.preventDefault(); - ele.val(val.substring(0, firstColon)+val.substring(ele[0].selectionStart)); - ele[0].selectionEnd = ele[0].selectionStart = firstColon; - ele.trigger("change"); - - if (!me.currentKeyboard){ - $(".emoji-keyboard-popup-btn").click(); - } - - $(".emoji-keyboard-search").children("input").focus().val(""); - document.execCommand("insertText", false, keywords.join(" ")); - } - } - else{ - lastEmojiKeyword = null; - } - }; - - this.composeInputFocusEvent = function(e){ - wasSearchFocused = false; - }; - - this.composerSendingEvent = function(e){ - hideKeyboard(); - }; - - this.composerActiveEvent = function(e){ - $(".emoji-keyboard-popup-btn", me.composeDrawer).on("click", me.emojiKeyboardButtonClickEvent); - $(".js-docked-compose .js-compose-scroller > .scroll-v", me.composeDrawer).on("scroll", me.composerScrollEvent); - }; - - this.documentClickEvent = function(e){ - if (me.currentKeyboard && $(e.target).closest(".compose-text-container").length === 0){ - hideKeyboard(); - } - }; - - this.documentKeyEvent = function(e){ - if (me.currentKeyboard && e.keyCode === 27){ // escape - hideKeyboard(true); - e.stopPropagation(); - } - }; - - this.uploadFilesEvent = function(e){ - if (me.currentKeyboard){ - me.currentKeyboard.style.top = getKeyboardTop()+"px"; - } - }; - - // re-enabling - - let maybeDockedComposePanel = $(".js-docked-compose"); - - if (maybeDockedComposePanel.length){ - maybeDockedComposePanel.find(".cf.margin-t--12.margin-b--30").first().append(buttonHTML); - this.composerActiveEvent(); - } + this.ENABLE_CUSTOM_KEYBOARD = false; + + this.selectedSkinTone = ""; + this.currentKeywords = []; + + this.skinToneList = [ + "", "1F3FB", "1F3FC", "1F3FD", "1F3FE", "1F3FF" + ]; + + this.skinToneNonDefaultList = [ + "1F3FB", "1F3FC", "1F3FD", "1F3FE", "1F3FF" + ]; + + this.skinToneData = [ + [ "", "#ffdd67" ], + [ "1F3FB", "#ffe1bd" ], + [ "1F3FC", "#fed0ac" ], + [ "1F3FD", "#d6a57c" ], + [ "1F3FE", "#b47d56" ], + [ "1F3FF", "#8a6859" ], + ]; + + this.emojiData1 = []; // no skin tones, prepended + this.emojiData2 = {}; // contains emojis with skin tones + this.emojiData3 = []; // no skin tones, appended + this.emojiNames = []; + + const me = this; + + // styles + + this.css = window.TDPF_createCustomStyle(this); + this.css.insert(".emoji-keyboard { position: absolute; width: 15.35em; background-color: white; border-radius: 1px; font-size: 24px; z-index: 9999 }"); + this.css.insert(".emoji-keyboard-popup-btn { height: 36px !important }"); + this.css.insert(".emoji-keyboard-popup-btn .icon { vertical-align: -4px !important }"); + + this.css.insert(".emoji-keyboard-list { height: 10.14em; padding: 0.1em; box-sizing: border-box; overflow-y: auto }"); + this.css.insert(".emoji-keyboard-list .separator { height: 26px }"); + this.css.insert(".emoji-keyboard-list img { padding: 0.1em !important; width: 1em; height: 1em; vertical-align: -0.1em; cursor: pointer }"); + + this.css.insert(".emoji-keyboard-search { height: auto; padding: 4px 10px 8px; background-color: #292f33; border-radius: 1px 1px 0 0 }"); + this.css.insert(".emoji-keyboard-search input { width: 100%; border-radius: 1px; }"); + + this.css.insert(".emoji-keyboard-skintones { height: 1.3em; text-align: center; background-color: #292f33; border-radius: 0 0 1px 1px }"); + 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(".js-compose-text { font-family: \"Twitter Color Emoji\", Helvetica, Arial, Verdana, sans-serif; }"); + + // layout + + let buttonHTML = "<button class=\"needsclick btn btn-on-blue txt-left padding-v--6 padding-h--8 emoji-keyboard-popup-btn\"><i class=\"icon icon-heart\"></i></button>"; + + this.prevComposeMustache = TD.mustaches["compose/docked_compose.mustache"]; + window.TDPF_injectMustache("compose/docked_compose.mustache", "append", "<div class=\"cf margin-t--12 margin-b--30\">", buttonHTML); + + this.getDrawerInput = () => { + return $(".js-compose-text", me.composeDrawer); + }; + + this.getDrawerScroller = () => { + return $(".js-compose-scroller > .scroll-v", me.composeDrawer); + }; + + // keyboard generation + + this.currentKeyboard = null; + this.currentSpanner = null; + + let wasSearchFocused = false; + let lastEmojiKeyword, lastEmojiPosition, lastEmojiLength; + + const hideKeyboard = (refocus) => { + $(this.currentKeyboard).remove(); + this.currentKeyboard = null; + + $(this.currentSpanner).remove(); + this.currentSpanner = null; + + this.currentKeywords = []; + + this.getDrawerScroller().trigger("scroll"); + + $(".emoji-keyboard-popup-btn").removeClass("is-selected"); + + if (refocus) { + this.getDrawerInput().focus(); + + if (lastEmojiKeyword && lastEmojiPosition === 0) { + document.execCommand("insertText", false, lastEmojiKeyword); + } + } + + lastEmojiKeyword = null; + }; + + const generateEmojiHTML = skinTone => { + let index = 0; + let html = [ "<p style='font-size:13px;color:#444;margin:4px;text-align:center'>Please, note that some emoji may not show up correctly in the text box above, but they will display in the tweet.</p>" ]; + + for (let array of [ this.emojiData1, this.emojiData2[skinTone], this.emojiData3 ]) { + for (let emoji of array) { + if (emoji === "___") { + html.push("<div class='separator'></div>"); + } + else { + html.push(TD.util.cleanWithEmoji(emoji).replace(" class=\"emoji\" draggable=\"false\"", "")); + index++; + } + } + } + + return html.join(""); + }; + + const updateFilters = () => { + let keywords = this.currentKeywords; + let container = $(this.currentKeyboard.children[1]); + + let emoji = container.children("img"); + let info = container.children("p:first"); + let separators = container.children("div"); + + if (keywords.length === 0) { + info.css("display", "block"); + separators.css("display", "block"); + emoji.css("display", "inline"); + } + else { + info.css("display", "none"); + separators.css("display", "none"); + + emoji.css("display", "none"); + emoji.filter(index => keywords.every(kw => me.emojiNames[index].includes(kw))).css("display", "inline"); + } + }; + + const selectSkinTone = skinTone => { + let selectedEle = this.currentKeyboard.children[2].querySelector("[data-tone='" + this.selectedSkinTone + "']"); + selectedEle && selectedEle.classList.remove("sel"); + + this.selectedSkinTone = skinTone; + this.currentKeyboard.children[1].innerHTML = generateEmojiHTML(skinTone); + this.currentKeyboard.children[2].querySelector("[data-tone='" + this.selectedSkinTone + "']").classList.add("sel"); + updateFilters(); + }; + + this.generateKeyboard = (left, top) => { + let outer = document.createElement("div"); + outer.classList.add("emoji-keyboard"); + outer.style.left = left + "px"; + outer.style.top = top + "px"; + + let keyboard = document.createElement("div"); + keyboard.classList.add("emoji-keyboard-list"); + + keyboard.addEventListener("click", function(e) { + let ele = e.target; + + if (ele.tagName === "IMG") { + insertEmoji(ele.getAttribute("src"), ele.getAttribute("alt")); + } + + e.stopPropagation(); + }); + + let search = document.createElement("div"); + search.innerHTML = "<input type='text' placeholder='Search...'>"; + search.classList.add("emoji-keyboard-search"); + + let skintones = document.createElement("div"); + skintones.innerHTML = me.skinToneData.map(entry => "<div data-tone='" + entry[0] + "' style='background-color:" + entry[1] + "'></div>").join(""); + skintones.classList.add("emoji-keyboard-skintones"); + + outer.appendChild(search); + outer.appendChild(keyboard); + outer.appendChild(skintones); + $(".js-app").append(outer); + + skintones.addEventListener("click", function(e) { + if (e.target.hasAttribute("data-tone")) { + selectSkinTone(e.target.getAttribute("data-tone") || ""); + } + + e.stopPropagation(); + }); + + search.addEventListener("click", function(e) { + e.stopPropagation(); + }); + + let searchInput = search.children[0]; + searchInput.focus(); + + wasSearchFocused = false; + + searchInput.addEventListener("input", function(e) { + me.currentKeywords = e.target.value.split(" ").filter(kw => kw.length > 0).map(kw => kw.toLowerCase()); + updateFilters(); + + wasSearchFocused = $(this).val().length > 0; + e.stopPropagation(); + }); + + searchInput.addEventListener("keydown", function(e) { + if (e.keyCode === 13 && $(this).val().length) { // enter + let ele = $(".emoji-keyboard-list").children("img").filter(":visible").first(); + + if (ele.length > 0) { + insertEmoji(ele[0].getAttribute("src"), ele[0].getAttribute("alt")); + } + + e.preventDefault(); + } + }); + + searchInput.addEventListener("click", function() { + $(this).select(); + }); + + searchInput.addEventListener("focus", function() { + wasSearchFocused = true; + }); + + this.currentKeyboard = outer; + selectSkinTone(this.selectedSkinTone); + + this.currentSpanner = document.createElement("div"); + this.currentSpanner.style.height = ($(this.currentKeyboard).height() - 10) + "px"; + $(".emoji-keyboard-popup-btn").parent().after(this.currentSpanner); + + this.getDrawerScroller().trigger("scroll"); + }; + + const getKeyboardTop = () => { + let button = $(".emoji-keyboard-popup-btn"); + return button.offset().top + button.outerHeight() + me.getDrawerScroller().scrollTop() + 8; + }; + + const insertEmoji = (src, alt) => { + let input = this.getDrawerInput(); + + let val = input.val(); + let posStart = input[0].selectionStart; + let posEnd = input[0].selectionEnd; + + input.val(val.slice(0, posStart) + alt + val.slice(posEnd)); + input.trigger("change"); + + input[0].selectionStart = posStart + alt.length; + input[0].selectionEnd = posStart + alt.length; + + lastEmojiKeyword = null; + + if (wasSearchFocused) { + $(".emoji-keyboard-search").children("input").focus(); + } + else { + input.focus(); + } + }; + + // general event handlers + + this.emojiKeyboardButtonClickEvent = function(e) { + if (me.currentKeyboard) { + $(this).blur(); + hideKeyboard(true); + } + else { + me.generateKeyboard($(this).offset().left, getKeyboardTop()); + $(this).addClass("is-selected"); + } + + e.stopPropagation(); + }; + + this.composerScrollEvent = function(e) { + if (me.currentKeyboard) { + me.currentKeyboard.style.marginTop = (-$(this).scrollTop()) + "px"; + } + }; + + this.composeInputKeyDownEvent = function(e) { + if (lastEmojiKeyword && (e.keyCode === 8 || e.keyCode === 27)) { // backspace, escape + let ele = $(this)[0]; + + if (ele.selectionStart === lastEmojiPosition) { + ele.selectionStart -= lastEmojiLength; // selects the emoji + document.execCommand("insertText", false, lastEmojiKeyword); + + e.preventDefault(); + e.stopPropagation(); + } + + lastEmojiKeyword = null; + } + }; + + this.composeInputKeyPressEvent = function(e) { + if (String.fromCharCode(e.which) === ":") { + let ele = $(this); + let val = ele.val(); + + let firstColon = val.lastIndexOf(":", ele[0].selectionStart); + if (firstColon === -1) { + return; + } + + let search = val.substring(firstColon + 1, ele[0].selectionStart).toLowerCase(); + if (!/^[a-z_]+$/.test(search)) { + return; + } + + let keywords = search.split("_").filter(kw => kw.length > 0).map(kw => kw.toLowerCase()); + if (keywords.length === 0) { + return; + } + + let foundNames = me.emojiNames.filter(name => keywords.every(kw => name.includes(kw))); + + if (foundNames.length === 0) { + return; + } + else if (foundNames.length > 1 && foundNames.includes(search)) { + foundNames = [ search ]; + } + + lastEmojiKeyword = `:${search}:`; + lastEmojiPosition = lastEmojiLength = 0; + + if (foundNames.length === 1) { + let foundIndex = me.emojiNames.indexOf(foundNames[0]); + let foundEmoji; + + for (let array of [ me.emojiData1, me.emojiData2[me.selectedSkinTone], me.emojiData3 ]) { + let realArray = array.filter(ele => ele !== "___"); + + if (foundIndex >= realArray.length) { + foundIndex -= realArray.length; + } + else { + foundEmoji = realArray[foundIndex]; + break; + } + } + + if (foundEmoji) { + e.preventDefault(); + + ele.val(val.substring(0, firstColon) + foundEmoji + val.substring(ele[0].selectionStart)); + ele[0].selectionEnd = ele[0].selectionStart = firstColon + foundEmoji.length; + ele.trigger("change"); + ele.focus(); + + lastEmojiPosition = firstColon + foundEmoji.length; + lastEmojiLength = foundEmoji.length; + } + } + else if (foundNames.length > 1 && $(".js-app-content").is(".is-open")) { + e.preventDefault(); + ele.val(val.substring(0, firstColon) + val.substring(ele[0].selectionStart)); + ele[0].selectionEnd = ele[0].selectionStart = firstColon; + ele.trigger("change"); + + if (!me.currentKeyboard) { + $(".emoji-keyboard-popup-btn").click(); + } + + $(".emoji-keyboard-search").children("input").focus().val(""); + document.execCommand("insertText", false, keywords.join(" ")); + } + } + else { + lastEmojiKeyword = null; + } + }; + + this.composeInputFocusEvent = function(e) { + wasSearchFocused = false; + }; + + this.composerSendingEvent = function(e) { + hideKeyboard(); + }; + + this.composerActiveEvent = function(e) { + $(".emoji-keyboard-popup-btn", me.composeDrawer).on("click", me.emojiKeyboardButtonClickEvent); + $(".js-docked-compose .js-compose-scroller > .scroll-v", me.composeDrawer).on("scroll", me.composerScrollEvent); + }; + + this.documentClickEvent = function(e) { + if (me.currentKeyboard && $(e.target).closest(".compose-text-container").length === 0) { + hideKeyboard(); + } + }; + + this.documentKeyEvent = function(e) { + if (me.currentKeyboard && e.keyCode === 27) { // escape + hideKeyboard(true); + e.stopPropagation(); + } + }; + + this.uploadFilesEvent = function(e) { + if (me.currentKeyboard) { + me.currentKeyboard.style.top = getKeyboardTop() + "px"; + } + }; + + // re-enabling + + let maybeDockedComposePanel = $(".js-docked-compose"); + + if (maybeDockedComposePanel.length) { + maybeDockedComposePanel.find(".cf.margin-t--12.margin-b--30").first().append(buttonHTML); + this.composerActiveEvent(); + } } ready(){ - this.composeDrawer = $(".js-drawer[data-drawer='compose']"); - this.composeSelector = ".js-compose-text,.js-reply-tweetbox"; - - $(document).on("click", this.documentClickEvent); - $(document).on("keydown", this.documentKeyEvent); - $(document).on("tduckOldComposerActive", this.composerActiveEvent); - $(document).on("uiComposeImageAdded", this.uploadFilesEvent); - - this.composeDrawer.on("uiComposeTweetSending", this.composerSendingEvent); - - $(document).on("keydown", this.composeSelector, this.composeInputKeyDownEvent); - $(document).on("keypress", this.composeSelector, this.composeInputKeyPressEvent); - $(document).on("focus", this.composeSelector, this.composeInputFocusEvent); - - // HTML generation - - const convUnicode = function(codePt){ - if (codePt > 0xFFFF){ - codePt -= 0x10000; - return String.fromCharCode(0xD800+(codePt>>10), 0xDC00+(codePt&0x3FF)); - } - else{ - return String.fromCharCode(codePt); - } - }; - - $TDP.readFileRoot(this.$token, "emoji-ordering.txt").then(contents => { - for(let skinTone of this.skinToneList){ - this.emojiData2[skinTone] = []; - } - - // declaration inserters - - let mapUnicode = pt => convUnicode(parseInt(pt, 16)); - - let addDeclaration1 = decl => { - this.emojiData1.push(decl.split(" ").map(mapUnicode).join("")); - }; - - let addDeclaration2 = (tone, decl) => { - let gen = decl.split(" ").map(mapUnicode).join(""); - - if (tone === null){ - for(let skinTone of this.skinToneList){ - this.emojiData2[skinTone].push(gen); - } - } - else{ - this.emojiData2[tone].push(gen); - } - }; - - let addDeclaration3 = decl => { - this.emojiData3.push(decl.split(" ").map(mapUnicode).join("")); - }; - - // line reading - - let skinToneState = 0; - - for(let line of contents.split("\n")){ - if (line[0] === '@'){ - switch(skinToneState){ - case 0: this.emojiData1.push("___"); break; - case 1: this.skinToneList.forEach(skinTone => this.emojiData2[skinTone].push("___")); break; - case 2: this.emojiData3.push("___"); break; - } - - continue; - } - else if (line[0] === '#'){ - if (line[1] === '1'){ - skinToneState = 1; - } - else if (line[1] === '2'){ - skinToneState = 2; - } - - continue; - } - - let semicolon = line.indexOf(';'); - let decl = line.slice(0, semicolon); - let desc = line.slice(semicolon+1).toLowerCase(); - - if (skinToneState === 1){ - let skinIndex = decl.indexOf('$'); - - if (skinIndex !== -1){ - let declPre = decl.slice(0, skinIndex); - let declPost = decl.slice(skinIndex+1); - - for(let skinTone of this.skinToneNonDefaultList){ - this.emojiData2[skinTone].pop(); - addDeclaration2(skinTone, declPre+skinTone+declPost); - } - } - else{ - addDeclaration2(null, decl); - this.emojiNames.push(desc); - } - } - else if (skinToneState === 2){ - addDeclaration3(decl); - this.emojiNames.push(desc); - } - else if (skinToneState === 0){ - addDeclaration1(decl); - this.emojiNames.push(desc); - } - } - }).catch(err => { - $TD.alert("error", "Problem loading emoji keyboard: "+err.message); - }); + this.composeDrawer = $(".js-drawer[data-drawer='compose']"); + this.composeSelector = ".js-compose-text,.js-reply-tweetbox"; + + $(document).on("click", this.documentClickEvent); + $(document).on("keydown", this.documentKeyEvent); + $(document).on("tduckOldComposerActive", this.composerActiveEvent); + $(document).on("uiComposeImageAdded", this.uploadFilesEvent); + + this.composeDrawer.on("uiComposeTweetSending", this.composerSendingEvent); + + $(document).on("keydown", this.composeSelector, this.composeInputKeyDownEvent); + $(document).on("keypress", this.composeSelector, this.composeInputKeyPressEvent); + $(document).on("focus", this.composeSelector, this.composeInputFocusEvent); + + // HTML generation + + const convUnicode = function(codePt) { + if (codePt > 0xFFFF) { + codePt -= 0x10000; + return String.fromCharCode(0xD800 + (codePt >> 10), 0xDC00 + (codePt & 0x3FF)); + } + else { + return String.fromCharCode(codePt); + } + }; + + $TDP.readFileRoot(this.$token, "emoji-ordering.txt").then(contents => { + for (let skinTone of this.skinToneList) { + this.emojiData2[skinTone] = []; + } + + // declaration inserters + + let mapUnicode = pt => convUnicode(parseInt(pt, 16)); + + let addDeclaration1 = decl => { + this.emojiData1.push(decl.split(" ").map(mapUnicode).join("")); + }; + + let addDeclaration2 = (tone, decl) => { + let gen = decl.split(" ").map(mapUnicode).join(""); + + if (tone === null) { + for (let skinTone of this.skinToneList) { + this.emojiData2[skinTone].push(gen); + } + } + else { + this.emojiData2[tone].push(gen); + } + }; + + let addDeclaration3 = decl => { + this.emojiData3.push(decl.split(" ").map(mapUnicode).join("")); + }; + + // line reading + + let skinToneState = 0; + + for (let line of contents.split("\n")) { + if (line[0] === "@") { + switch (skinToneState) { + case 0: + this.emojiData1.push("___"); + break; + case 1: + this.skinToneList.forEach(skinTone => this.emojiData2[skinTone].push("___")); + break; + case 2: + this.emojiData3.push("___"); + break; + } + + continue; + } + else if (line[0] === "#") { + if (line[1] === "1") { + skinToneState = 1; + } + else if (line[1] === "2") { + skinToneState = 2; + } + + continue; + } + + let semicolon = line.indexOf(";"); + let decl = line.slice(0, semicolon); + let desc = line.slice(semicolon + 1).toLowerCase(); + + if (skinToneState === 1) { + let skinIndex = decl.indexOf("$"); + + if (skinIndex !== -1) { + let declPre = decl.slice(0, skinIndex); + let declPost = decl.slice(skinIndex + 1); + + for (let skinTone of this.skinToneNonDefaultList) { + this.emojiData2[skinTone].pop(); + addDeclaration2(skinTone, declPre + skinTone + declPost); + } + } + else { + addDeclaration2(null, decl); + this.emojiNames.push(desc); + } + } + else if (skinToneState === 2) { + addDeclaration3(decl); + this.emojiNames.push(desc); + } + else if (skinToneState === 0) { + addDeclaration1(decl); + this.emojiNames.push(desc); + } + } + }).catch(err => { + $TD.alert("error", "Problem loading emoji keyboard: " + err.message); + }); } disabled(){ - this.css.remove(); - - if (this.currentKeyboard){ - $(this.currentKeyboard).remove(); - } - - if (this.currentSpanner){ - $(this.currentSpanner).remove(); - } - - $(".emoji-keyboard-popup-btn").remove(); - - $(document).off("click", this.documentClickEvent); - $(document).off("keydown", this.documentKeyEvent); - $(document).off("tduckOldComposerActive", this.composerActiveEvent); - $(document).off("uiComposeImageAdded", this.uploadFilesEvent); - - this.composeDrawer.off("uiComposeTweetSending", this.composerSendingEvent); - - $(document).off("keydown", this.composeSelector, this.composeInputKeyDownEvent); - $(document).off("keypress", this.composeSelector, this.composeInputKeyPressEvent); - $(document).off("focus", this.composeSelector, this.composeInputFocusEvent); - - TD.mustaches["compose/docked_compose.mustache"] = this.prevComposeMustache; + this.css.remove(); + + if (this.currentKeyboard) { + $(this.currentKeyboard).remove(); + } + + if (this.currentSpanner) { + $(this.currentSpanner).remove(); + } + + $(".emoji-keyboard-popup-btn").remove(); + + $(document).off("click", this.documentClickEvent); + $(document).off("keydown", this.documentKeyEvent); + $(document).off("tduckOldComposerActive", this.composerActiveEvent); + $(document).off("uiComposeImageAdded", this.uploadFilesEvent); + + this.composeDrawer.off("uiComposeTweetSending", this.composerSendingEvent); + + $(document).off("keydown", this.composeSelector, this.composeInputKeyDownEvent); + $(document).off("keypress", this.composeSelector, this.composeInputKeyPressEvent); + $(document).off("focus", this.composeSelector, this.composeInputFocusEvent); + + TD.mustaches["compose/docked_compose.mustache"] = this.prevComposeMustache; } diff --git a/Resources/Plugins/reply-account/browser.js b/Resources/Plugins/reply-account/browser.js index e33853f8..9bce7335 100644 --- a/Resources/Plugins/reply-account/browser.js +++ b/Resources/Plugins/reply-account/browser.js @@ -1,151 +1,151 @@ enabled(){ - let configuration = { defaultAccount: "#preferred" }; - - window.TDPF_loadConfigurationFile(this, "configuration.js", "configuration.default.js", obj => configuration = obj); - - this.lastSelectedAccount = null; - - this.uiComposeTweetEvent = (e, data) => { - if (!(data.type === "reply" || (data.type === "tweet" && "quotedTweet" in data)) || data.popFromInline || !("element" in data)) { - return; - } - - let query; - - if (configuration.useAdvancedSelector){ - if (configuration.customSelector){ - let customSelectorDef = configuration.customSelector.toString(); - - if (customSelectorDef.startsWith("function (column){")){ - $TD.alert("warning", "Plugin reply-account has invalid configuration: customSelector needs to be updated due to TweetDeck changes, please read the default configuration file for the updated guide"); - return; - } - else if (customSelectorDef.startsWith("function (type,")){ - $TD.alert("warning", "Plugin reply-account has invalid configuration: the type parameter is no longer present due to TweetDeck changes, please read the default configuration file for the updated guide"); - return; - } - - let section = data.element.closest("section.js-column"); - let column = TD.controller.columnManager.get(section.attr("data-column")); - - let feeds = column.getFeeds(); - let accountText = ""; - - if (feeds.length === 1){ - let metadata = feeds[0].getMetadata(); - let id = metadata.ownerId || metadata.id; - - if (id){ - accountText = TD.cache.names.getScreenName(id); - } - else{ - let account = TD.storage.accountController.get(feeds[0].getAccountKey()); - - if (account){ - accountText = "@"+account.getUsername(); - } - } - } - - let header = $(".column-header-title", section); - let title = header.children(".column-heading"); - let titleText = title.length ? title.text() : header.children(".column-title-edit-box").val(); - - try{ - query = configuration.customSelector(titleText, accountText, column, section.hasClass("column-temp")); - }catch(e){ - $TD.alert("warning", "Plugin reply-account has invalid configuration: customSelector threw an error: "+e.message); - return; - } - } - else{ - $TD.alert("warning", "Plugin reply-account has invalid configuration: useAdvancedSelector is true, but customSelector function is missing"); - return; - } - } - else{ - query = configuration.defaultAccount; - - if (query === ""){ - query = "#preferred"; - } - else if (typeof query !== "string"){ - query = "#default"; - } - } - - if (typeof query === "undefined"){ - query = "#preferred"; - } - - if (typeof query !== "string"){ - return; - } - else if (query.length === 0){ - $TD.alert("warning", "Plugin reply-account has invalid configuration: the requested account is empty"); - return; - } - else if (query[0] !== '@' && query[0] !== '#'){ - $TD.alert("warning", "Plugin reply-account has invalid configuration: the requested account does not begin with @ or #: "+query); - return; - } - - let identifier = null; - - switch(query){ - case "#preferred": - identifier = TD.storage.clientController.client.getDefaultAccount(); - break; - - case "#last": - if (this.lastSelectedAccount === null) { - return; - } - - identifier = this.lastSelectedAccount; - break; - - case "#default": - return; - - default: - if (query[0] === '@'){ - let obj = TD.storage.accountController.getAccountFromUsername(query.substring(1)); - - if (obj.length === 0){ - $TD.alert("warning", "Plugin reply-account has invalid configuration: requested account not found: "+query); - return; - } - else{ - identifier = obj[0].privateState.key; - } - } - else{ - $TD.alert("warning", "Plugin reply-account has invalid configuration: unknown requested account query: "+query); - return; - } - } - - data.singleFrom = data.from = [ identifier ]; - }; - - this.onSelectedAccountChanged = () => { - let selected = $(".js-account-item.is-selected", ".js-account-list"); - this.lastSelectedAccount = selected.length === 1 ? selected.attr("data-account-key") : null; - }; + let configuration = { defaultAccount: "#preferred" }; + + window.TDPF_loadConfigurationFile(this, "configuration.js", "configuration.default.js", obj => configuration = obj); + + this.lastSelectedAccount = null; + + this.uiComposeTweetEvent = (e, data) => { + if (!(data.type === "reply" || (data.type === "tweet" && "quotedTweet" in data)) || data.popFromInline || !("element" in data)) { + return; + } + + let query; + + if (configuration.useAdvancedSelector) { + if (configuration.customSelector) { + let customSelectorDef = configuration.customSelector.toString(); + + if (customSelectorDef.startsWith("function (column){")) { + $TD.alert("warning", "Plugin reply-account has invalid configuration: customSelector needs to be updated due to TweetDeck changes, please read the default configuration file for the updated guide"); + return; + } + else if (customSelectorDef.startsWith("function (type,")) { + $TD.alert("warning", "Plugin reply-account has invalid configuration: the type parameter is no longer present due to TweetDeck changes, please read the default configuration file for the updated guide"); + return; + } + + let section = data.element.closest("section.js-column"); + let column = TD.controller.columnManager.get(section.attr("data-column")); + + let feeds = column.getFeeds(); + let accountText = ""; + + if (feeds.length === 1) { + let metadata = feeds[0].getMetadata(); + let id = metadata.ownerId || metadata.id; + + if (id) { + accountText = TD.cache.names.getScreenName(id); + } + else { + let account = TD.storage.accountController.get(feeds[0].getAccountKey()); + + if (account) { + accountText = "@" + account.getUsername(); + } + } + } + + let header = $(".column-header-title", section); + let title = header.children(".column-heading"); + let titleText = title.length ? title.text() : header.children(".column-title-edit-box").val(); + + try { + query = configuration.customSelector(titleText, accountText, column, section.hasClass("column-temp")); + } catch (e) { + $TD.alert("warning", "Plugin reply-account has invalid configuration: customSelector threw an error: " + e.message); + return; + } + } + else { + $TD.alert("warning", "Plugin reply-account has invalid configuration: useAdvancedSelector is true, but customSelector function is missing"); + return; + } + } + else { + query = configuration.defaultAccount; + + if (query === "") { + query = "#preferred"; + } + else if (typeof query !== "string") { + query = "#default"; + } + } + + if (typeof query === "undefined") { + query = "#preferred"; + } + + if (typeof query !== "string") { + return; + } + else if (query.length === 0) { + $TD.alert("warning", "Plugin reply-account has invalid configuration: the requested account is empty"); + return; + } + else if (query[0] !== "@" && query[0] !== "#") { + $TD.alert("warning", "Plugin reply-account has invalid configuration: the requested account does not begin with @ or #: " + query); + return; + } + + let identifier = null; + + switch (query) { + case "#preferred": + identifier = TD.storage.clientController.client.getDefaultAccount(); + break; + + case "#last": + if (this.lastSelectedAccount === null) { + return; + } + + identifier = this.lastSelectedAccount; + break; + + case "#default": + return; + + default: + if (query[0] === "@") { + let obj = TD.storage.accountController.getAccountFromUsername(query.substring(1)); + + if (obj.length === 0) { + $TD.alert("warning", "Plugin reply-account has invalid configuration: requested account not found: " + query); + return; + } + else { + identifier = obj[0].privateState.key; + } + } + else { + $TD.alert("warning", "Plugin reply-account has invalid configuration: unknown requested account query: " + query); + return; + } + } + + data.singleFrom = data.from = [ identifier ]; + }; + + this.onSelectedAccountChanged = () => { + let selected = $(".js-account-item.is-selected", ".js-account-list"); + this.lastSelectedAccount = selected.length === 1 ? selected.attr("data-account-key") : null; + }; } ready(){ - for(let event of [ "uiInlineComposeTweet", "uiDockedComposeTweet" ]){ - $(document).on(event, this.uiComposeTweetEvent); - window.TDPF_prioritizeNewestEvent(document, event); - } - - $(document).on("click", ".js-account-list .js-account-item", this.onSelectedAccountChanged); + for (let event of [ "uiInlineComposeTweet", "uiDockedComposeTweet" ]) { + $(document).on(event, this.uiComposeTweetEvent); + window.TDPF_prioritizeNewestEvent(document, event); + } + + $(document).on("click", ".js-account-list .js-account-item", this.onSelectedAccountChanged); } disabled(){ - $(document).off("uiInlineComposeTweet", this.uiComposeTweetEvent); - $(document).off("uiDockedComposeTweet", this.uiComposeTweetEvent); - $(document).off("click", ".js-account-list .js-account-item", this.onSelectedAccountChanged); + $(document).off("uiInlineComposeTweet", this.uiComposeTweetEvent); + $(document).off("uiDockedComposeTweet", this.uiComposeTweetEvent); + $(document).off("click", ".js-account-list .js-account-item", this.onSelectedAccountChanged); } diff --git a/Resources/Plugins/templates/browser.js b/Resources/Plugins/templates/browser.js index 5affe0f5..c050cace 100644 --- a/Resources/Plugins/templates/browser.js +++ b/Resources/Plugins/templates/browser.js @@ -1,403 +1,403 @@ enabled(){ - let me = this; - - // configuration - - this.config = { - templates: {} // identifier: { name, contents } - }; - - const configFile = "config.json"; - - $TDP.checkFileExists(this.$token, configFile).then(exists => { - if (!exists){ - $TDP.writeFile(this.$token, configFile, JSON.stringify(this.config)); - } - else{ - $TDP.readFile(this.$token, configFile, true).then(contents => { - try{ - $.extend(true, this.config, JSON.parse(contents)); - }catch(err){ - // why :( - } - }).catch(err => { - $TD.alert("error", "Problem loading configuration for the template plugin: "+err.message); - }); - } - }); - - this.saveConfig = () => { - $TDP.writeFile(this.$token, configFile, JSON.stringify(this.config)).catch(err => { - $TD.alert("error", "Problem saving configuration for the template plugin: "+err.message); - }); - }; - - // setup - - this.htmlModal = null; - - $TDP.readFileRoot(this.$token, "modal.html").then(contents => { - this.htmlModal = contents; - }).catch(err => { - $TD.alert("error", "Problem loading data for the template plugin: "+err.message); - }); - - // button - - let buttonHTML = '<button class="manage-templates-btn needsclick btn btn-on-blue full-width txt-left margin-b--12 padding-v--6 padding-h--12"><i class="icon icon-bookmark"></i><span class="label padding-ls">Manage templates</span></button>'; - - this.prevComposeMustache = TD.mustaches["compose/docked_compose.mustache"]; - window.TDPF_injectMustache("compose/docked_compose.mustache", "prepend", '<div class="js-tweet-type-button">', buttonHTML); - - let dockedComposePanel = $(".js-docked-compose"); - - if (dockedComposePanel.length){ - dockedComposePanel.find(".js-tweet-type-button").first().before(buttonHTML); - } - - // template implementation - - const readTemplateTokens = (contents, tokenData) => { - let startIndex = -1; - let endIndex = -1; - - let data = []; - let tokenNames = Object.keys(tokenData); - - for(let currentIndex = 0; currentIndex < contents.length; currentIndex++){ - if (contents[currentIndex] === '\\'){ - contents = contents.substring(0, currentIndex)+contents.substring(currentIndex+1); - continue; - } - else if (contents[currentIndex] !== '{'){ - continue; - } - - startIndex = currentIndex+1; - - for(; startIndex < contents.length; startIndex++){ - if (!tokenNames.some(name => contents[startIndex] === name[startIndex-currentIndex-1])){ - break; - } - } - - endIndex = startIndex; - - let token = contents.substring(currentIndex+1, startIndex); - let replacement = tokenData[token] || ""; - - let entry = [ token, currentIndex ]; - - if (contents[endIndex] === '#'){ - ++endIndex; - - let bracketCount = 1; - - for(; endIndex < contents.length; endIndex++){ - if (contents[endIndex] === '{'){ - ++bracketCount; - } - else if (contents[endIndex] === '}'){ - if (--bracketCount === 0){ - entry.push(contents.substring(startIndex+1, endIndex)); - break; - } - } - else if (contents[endIndex] === '#'){ - entry.push(contents.substring(startIndex+1, endIndex)); - startIndex = endIndex; - } - else if (contents[endIndex] === '\\'){ - contents = contents.substring(0, endIndex)+contents.substring(endIndex+1); - } - } - } - else if (contents[endIndex] !== '}'){ - continue; - } - - data.push(entry); - - contents = contents.substring(0, currentIndex)+replacement+contents.substring(endIndex+1); - currentIndex += replacement.length; - } - - return [ contents, data ]; - }; - - const doAjaxRequest = (index, url, evaluator) => { - return new Promise((resolve, reject) => { - if (!url){ - resolve([ index, "{ajax}" ]); - return; - } - - $TD.makeGetRequest(url, function(data){ - if (evaluator){ - resolve([ index, eval(evaluator.replace(/\$/g, "'"+data.replace(/(["'\\\n\r\u2028\u2029])/g, "\\$1")+"'"))]); - } - else{ - resolve([ index, data ]); - } - }, function(err){ - resolve([ index, "" ]); - $TD.alert("error", "Error executing AJAX request: "+err); - }); - }); - }; - - const useTemplate = (contents, append) => { - let ele = $(".js-compose-text"); - if (ele.length === 0) { - return; - } - - let value = append ? ele.val()+contents : contents; - let prevLength = value.length; - - let tokens = null; - - [value, tokens] = readTemplateTokens(value, { - "cursor": "", - "ajax": "(...)" - }); - - ele.val(value); - ele.trigger("change"); - ele.focus(); - - ele[0].selectionStart = ele[0].selectionEnd = value.length; - - let promises = []; - let indexOffset = 0; - - for(let token of tokens){ - switch(token[0]){ - case "cursor": - let [, index1, length ] = token; - ele[0].selectionStart = index1; - ele[0].selectionEnd = index1+(length | 0 || 0); - break; - - case "ajax": - let [, index2, evaluator, url ] = token; - - if (!url){ - url = evaluator; - evaluator = null; - } - - promises.push(doAjaxRequest(index2, url, evaluator)); - break; - } - } - - if (promises.length > 0){ - let selStart = ele[0].selectionStart; - let selEnd = ele[0].selectionEnd; - - ele.prop("disabled", true); - - Promise.all(promises).then(values => { - const placeholderLen = 5; // "(...)".length - let indexOffset = 0; - - for(let value of values){ - let diff = value[1].length-placeholderLen; - let realIndex = indexOffset+value[0]; - - let val = ele.val(); - ele.val(val.substring(0, realIndex)+value[1]+val.substring(realIndex+placeholderLen)); - - indexOffset += diff; - } - - ele.prop("disabled", false); - ele.trigger("change"); - ele.focus(); - - ele[0].selectionStart = selStart+indexOffset; - ele[0].selectionEnd = selEnd+indexOffset; - }); - } - - if (!append){ - hideTemplateModal(); - } - }; - - // modal dialog - - this.editingTemplate = null; - - const showTemplateModal = () => { - $(".js-app-content").prepend(this.htmlModal); - - /* TODO possibly implement this later - - <li>{paste}</li> - <li>Paste text or an image from clipboard</li> - <li>{paste#text}</li> - <li>Paste only if clipboard has text</li> - <li>{paste#image}</li> - <li>Paste only if clipboard has an image</li> - - */ - - let ele = $("#templates-modal-wrap").first(); - - ele.on("click", "li[data-template]", function(e){ - let template = me.config.templates[$(this).attr("data-template")]; - useTemplate(template.contents, e.shiftKey); - }); - - ele.on("click", "li[data-template] i[data-action]", function(e){ - let identifier = $(this).closest("li").attr("data-template"); - - switch($(this).attr("data-action")){ - case "edit-template": - let editor = $("#template-editor"); - - if (editor.hasClass("invisible")){ - toggleEditor(); - } - - let template = me.config.templates[identifier]; - $("[name='template-name']", editor).val(template.name); - $("[name='template-contents']", editor).val(template.contents); - - me.editingTemplate = identifier; - break; - - case "delete-template": - delete me.config.templates[identifier]; - onTemplatesUpdated(true); - - if (me.editingTemplate === identifier){ - me.editingTemplate = null; - } - - break; - } - - e.stopPropagation(); - }); - - ele.on("click", ".template-editor-tips-button", function(e){ - $(this).children(".icon").toggleClass("icon-arrow-d icon-arrow-u"); - ele.find(".template-editor-tips").toggle(); - }); - - ele.on("click", "button", function(e){ - switch($(this).attr("data-action")){ - case "new-template": - case "editor-cancel": - toggleEditor(); - break; - - case "editor-confirm": - let editor = $("#template-editor"); - - if (me.editingTemplate !== null){ - delete me.config.templates[me.editingTemplate]; - } - - let name = $("[name='template-name']", editor).val(); - let identifier = name.toLowerCase().replace(/[^a-z0-9]/g, "")+"-"+(Math.random().toString(36).substring(2, 7)); - - if (name.trim().length === 0){ - alert("Please, include a name for your template."); - $("[name='template-name']", editor).focus(); - return; - } - - me.config.templates[identifier] = { - name: name, - contents: $("[name='template-contents']", editor).val() - }; - - toggleEditor(); - onTemplatesUpdated(true); - break; - - case "close": - hideTemplateModal(); - break; - } - - $(this).blur(); - }); - - onTemplatesUpdated(false); - }; - - const hideTemplateModal = () => { - $("#templates-modal-wrap").remove(); - }; - - const toggleEditor = () => { - let editor = $("#template-editor"); - $("[name]", editor).val(""); - - if ($("button[data-action='new-template']", "#template-list").add(editor).toggleClass("invisible").hasClass("invisible")){ - me.editingTemplate = null; - } - }; - - const onTemplatesUpdated = (save) => { - let eles = []; - - for(let identifier of Object.keys(this.config.templates)){ - eles.push(`<li data-template="${identifier}"> + let me = this; + + // configuration + + this.config = { + templates: {} // identifier: { name, contents } + }; + + const configFile = "config.json"; + + $TDP.checkFileExists(this.$token, configFile).then(exists => { + if (!exists) { + $TDP.writeFile(this.$token, configFile, JSON.stringify(this.config)); + } + else { + $TDP.readFile(this.$token, configFile, true).then(contents => { + try { + $.extend(true, this.config, JSON.parse(contents)); + } catch (err) { + // why :( + } + }).catch(err => { + $TD.alert("error", "Problem loading configuration for the template plugin: " + err.message); + }); + } + }); + + this.saveConfig = () => { + $TDP.writeFile(this.$token, configFile, JSON.stringify(this.config)).catch(err => { + $TD.alert("error", "Problem saving configuration for the template plugin: " + err.message); + }); + }; + + // setup + + this.htmlModal = null; + + $TDP.readFileRoot(this.$token, "modal.html").then(contents => { + this.htmlModal = contents; + }).catch(err => { + $TD.alert("error", "Problem loading data for the template plugin: " + err.message); + }); + + // button + + let buttonHTML = "<button class=\"manage-templates-btn needsclick btn btn-on-blue full-width txt-left margin-b--12 padding-v--6 padding-h--12\"><i class=\"icon icon-bookmark\"></i><span class=\"label padding-ls\">Manage templates</span></button>"; + + this.prevComposeMustache = TD.mustaches["compose/docked_compose.mustache"]; + window.TDPF_injectMustache("compose/docked_compose.mustache", "prepend", "<div class=\"js-tweet-type-button\">", buttonHTML); + + let dockedComposePanel = $(".js-docked-compose"); + + if (dockedComposePanel.length) { + dockedComposePanel.find(".js-tweet-type-button").first().before(buttonHTML); + } + + // template implementation + + const readTemplateTokens = (contents, tokenData) => { + let startIndex = -1; + let endIndex = -1; + + let data = []; + let tokenNames = Object.keys(tokenData); + + for (let currentIndex = 0; currentIndex < contents.length; currentIndex++) { + if (contents[currentIndex] === "\\") { + contents = contents.substring(0, currentIndex) + contents.substring(currentIndex + 1); + continue; + } + else if (contents[currentIndex] !== "{") { + continue; + } + + startIndex = currentIndex + 1; + + for (; startIndex < contents.length; startIndex++) { + if (!tokenNames.some(name => contents[startIndex] === name[startIndex - currentIndex - 1])) { + break; + } + } + + endIndex = startIndex; + + let token = contents.substring(currentIndex + 1, startIndex); + let replacement = tokenData[token] || ""; + + let entry = [ token, currentIndex ]; + + if (contents[endIndex] === "#") { + ++endIndex; + + let bracketCount = 1; + + for (; endIndex < contents.length; endIndex++) { + if (contents[endIndex] === "{") { + ++bracketCount; + } + else if (contents[endIndex] === "}") { + if (--bracketCount === 0) { + entry.push(contents.substring(startIndex + 1, endIndex)); + break; + } + } + else if (contents[endIndex] === "#") { + entry.push(contents.substring(startIndex + 1, endIndex)); + startIndex = endIndex; + } + else if (contents[endIndex] === "\\") { + contents = contents.substring(0, endIndex) + contents.substring(endIndex + 1); + } + } + } + else if (contents[endIndex] !== "}") { + continue; + } + + data.push(entry); + + contents = contents.substring(0, currentIndex) + replacement + contents.substring(endIndex + 1); + currentIndex += replacement.length; + } + + return [ contents, data ]; + }; + + const doAjaxRequest = (index, url, evaluator) => { + return new Promise((resolve, reject) => { + if (!url) { + resolve([ index, "{ajax}" ]); + return; + } + + $TD.makeGetRequest(url, function(data) { + if (evaluator) { + resolve([ index, eval(evaluator.replace(/\$/g, "'" + data.replace(/(["'\\\n\r\u2028\u2029])/g, "\\$1") + "'")) ]); + } + else { + resolve([ index, data ]); + } + }, function(err) { + resolve([ index, "" ]); + $TD.alert("error", "Error executing AJAX request: " + err); + }); + }); + }; + + const useTemplate = (contents, append) => { + let ele = $(".js-compose-text"); + if (ele.length === 0) { + return; + } + + let value = append ? ele.val() + contents : contents; + let prevLength = value.length; + + let tokens = null; + + [ value, tokens ] = readTemplateTokens(value, { + "cursor": "", + "ajax": "(...)" + }); + + ele.val(value); + ele.trigger("change"); + ele.focus(); + + ele[0].selectionStart = ele[0].selectionEnd = value.length; + + let promises = []; + let indexOffset = 0; + + for (let token of tokens) { + switch (token[0]) { + case "cursor": + let [ , index1, length ] = token; + ele[0].selectionStart = index1; + ele[0].selectionEnd = index1 + (length | 0 || 0); + break; + + case "ajax": + let [ , index2, evaluator, url ] = token; + + if (!url) { + url = evaluator; + evaluator = null; + } + + promises.push(doAjaxRequest(index2, url, evaluator)); + break; + } + } + + if (promises.length > 0) { + let selStart = ele[0].selectionStart; + let selEnd = ele[0].selectionEnd; + + ele.prop("disabled", true); + + Promise.all(promises).then(values => { + const placeholderLen = 5; // "(...)".length + let indexOffset = 0; + + for (let value of values) { + let diff = value[1].length - placeholderLen; + let realIndex = indexOffset + value[0]; + + let val = ele.val(); + ele.val(val.substring(0, realIndex) + value[1] + val.substring(realIndex + placeholderLen)); + + indexOffset += diff; + } + + ele.prop("disabled", false); + ele.trigger("change"); + ele.focus(); + + ele[0].selectionStart = selStart + indexOffset; + ele[0].selectionEnd = selEnd + indexOffset; + }); + } + + if (!append) { + hideTemplateModal(); + } + }; + + // modal dialog + + this.editingTemplate = null; + + const showTemplateModal = () => { + $(".js-app-content").prepend(this.htmlModal); + + /* TODO possibly implement this later + + <li>{paste}</li> + <li>Paste text or an image from clipboard</li> + <li>{paste#text}</li> + <li>Paste only if clipboard has text</li> + <li>{paste#image}</li> + <li>Paste only if clipboard has an image</li> + + */ + + let ele = $("#templates-modal-wrap").first(); + + ele.on("click", "li[data-template]", function(e) { + let template = me.config.templates[$(this).attr("data-template")]; + useTemplate(template.contents, e.shiftKey); + }); + + ele.on("click", "li[data-template] i[data-action]", function(e) { + let identifier = $(this).closest("li").attr("data-template"); + + switch ($(this).attr("data-action")) { + case "edit-template": + let editor = $("#template-editor"); + + if (editor.hasClass("invisible")) { + toggleEditor(); + } + + let template = me.config.templates[identifier]; + $("[name='template-name']", editor).val(template.name); + $("[name='template-contents']", editor).val(template.contents); + + me.editingTemplate = identifier; + break; + + case "delete-template": + delete me.config.templates[identifier]; + onTemplatesUpdated(true); + + if (me.editingTemplate === identifier) { + me.editingTemplate = null; + } + + break; + } + + e.stopPropagation(); + }); + + ele.on("click", ".template-editor-tips-button", function(e) { + $(this).children(".icon").toggleClass("icon-arrow-d icon-arrow-u"); + ele.find(".template-editor-tips").toggle(); + }); + + ele.on("click", "button", function(e) { + switch ($(this).attr("data-action")) { + case "new-template": + case "editor-cancel": + toggleEditor(); + break; + + case "editor-confirm": + let editor = $("#template-editor"); + + if (me.editingTemplate !== null) { + delete me.config.templates[me.editingTemplate]; + } + + let name = $("[name='template-name']", editor).val(); + let identifier = name.toLowerCase().replace(/[^a-z0-9]/g, "") + "-" + (Math.random().toString(36).substring(2, 7)); + + if (name.trim().length === 0) { + alert("Please, include a name for your template."); + $("[name='template-name']", editor).focus(); + return; + } + + me.config.templates[identifier] = { + name: name, + contents: $("[name='template-contents']", editor).val() + }; + + toggleEditor(); + onTemplatesUpdated(true); + break; + + case "close": + hideTemplateModal(); + break; + } + + $(this).blur(); + }); + + onTemplatesUpdated(false); + }; + + const hideTemplateModal = () => { + $("#templates-modal-wrap").remove(); + }; + + const toggleEditor = () => { + let editor = $("#template-editor"); + $("[name]", editor).val(""); + + if ($("button[data-action='new-template']", "#template-list").add(editor).toggleClass("invisible").hasClass("invisible")) { + me.editingTemplate = null; + } + }; + + const onTemplatesUpdated = (save) => { + let eles = []; + + for (let identifier of Object.keys(this.config.templates)) { + eles.push(`<li data-template="${identifier}"> <span class="template-name">${this.config.templates[identifier].name}</span> <span class="template-actions"><i class="icon icon-edit" data-action="edit-template"></i><i class="icon icon-close" data-action="delete-template"></i></span> </li>`); - } - - if (eles.length === 0){ - eles.push("<li>No templates available</li>"); - } - - $("#template-list").children("ul").html(eles.join("")); - - if (save){ - this.saveConfig(); - } - }; - - // event handlers - - this.manageTemplatesButtonClickEvent = function(e){ - if ($("#templates-modal-wrap").length){ - hideTemplateModal(); - } - else{ - showTemplateModal(); - } - - $(this).blur(); - }; - - this.drawerToggleEvent = function(e, data){ - if (typeof data === "undefined" || data.activeDrawer !== "compose"){ - hideTemplateModal(); - } - }; + } + + if (eles.length === 0) { + eles.push("<li>No templates available</li>"); + } + + $("#template-list").children("ul").html(eles.join("")); + + if (save) { + this.saveConfig(); + } + }; + + // event handlers + + this.manageTemplatesButtonClickEvent = function(e) { + if ($("#templates-modal-wrap").length) { + hideTemplateModal(); + } + else { + showTemplateModal(); + } + + $(this).blur(); + }; + + this.drawerToggleEvent = function(e, data) { + if (typeof data === "undefined" || data.activeDrawer !== "compose") { + hideTemplateModal(); + } + }; } ready(){ - $(".js-drawer[data-drawer='compose']").on("click", ".manage-templates-btn", this.manageTemplatesButtonClickEvent); - $(document).on("uiDrawerActive", this.drawerToggleEvent); - $(document).on("click", ".js-new-composer-opt-in", this.drawerToggleEvent); + $(".js-drawer[data-drawer='compose']").on("click", ".manage-templates-btn", this.manageTemplatesButtonClickEvent); + $(document).on("uiDrawerActive", this.drawerToggleEvent); + $(document).on("click", ".js-new-composer-opt-in", this.drawerToggleEvent); } disabled(){ - $(".manage-templates-btn").remove(); - $("#templates-modal-wrap").remove(); - - $(".js-drawer[data-drawer='compose']").off("click", ".manage-templates-btn", this.manageTemplatesButtonClickEvent); - $(document).off("uiDrawerActive", this.drawerToggleEvent); - $(document).off("click", ".js-new-composer-opt-in", this.drawerToggleEvent); - - TD.mustaches["compose/docked_compose.mustache"] = this.prevComposeMustache; + $(".manage-templates-btn").remove(); + $("#templates-modal-wrap").remove(); + + $(".js-drawer[data-drawer='compose']").off("click", ".manage-templates-btn", this.manageTemplatesButtonClickEvent); + $(document).off("uiDrawerActive", this.drawerToggleEvent); + $(document).off("click", ".js-new-composer-opt-in", this.drawerToggleEvent); + + TD.mustaches["compose/docked_compose.mustache"] = this.prevComposeMustache; } diff --git a/Resources/Plugins/timeline-polls/browser.js b/Resources/Plugins/timeline-polls/browser.js index 1b854f80..4802836c 100644 --- a/Resources/Plugins/timeline-polls/browser.js +++ b/Resources/Plugins/timeline-polls/browser.js @@ -1,76 +1,76 @@ enabled(){ - // styles - - this.css = window.TDPF_createCustomStyle(this); - this.css.insert("html[data-td-theme='dark'] .quoted-tweet .td-timeline-poll { color: #e1e8ed; }"); - - // utility functions - - const hasPoll = function(tweet){ - return tweet.hasPoll && tweet.hasPoll(); - }; - - const renderTweetPoll = function(tweet){ - return `<div class='td-timeline-poll'>${TD.ui.template.render("status/poll", $.extend({}, tweet, { - chirp: tweet - }))}</div>`; - }; - - const renderPollHook = function(tweet, html){ - let ele = null; - - if (hasPoll(tweet)){ - (ele || (ele = $(html))).find(".js-tweet-body").first().children("div").last().after(renderTweetPoll(tweet)); - } - - if (tweet.quotedTweet && hasPoll(tweet.quotedTweet)){ - (ele || (ele = $(html))).find(".js-quoted-tweet-text").first().after(renderTweetPoll(tweet.quotedTweet)); - } - - if (ele){ - ele.find(".js-card-container").css("display", "none"); - return ele.prop("outerHTML"); - } - else{ - return html; - } - }; - - // hooks - - const funcs = { - TwitterStatus: TD.services.TwitterStatus.prototype.render, - TwitterActionOnTweet: TD.services.TwitterActionOnTweet.prototype.render, - TweetDetailView: TD.components.TweetDetailView.prototype._renderChirp - }; - - TD.services.TwitterStatus.prototype.render = function(e){ - return renderPollHook(this, funcs.TwitterStatus.apply(this, arguments)); - }; - - TD.services.TwitterActionOnTweet.prototype.render = function(e){ - return renderPollHook(this.targetTweet, funcs.TwitterActionOnTweet.apply(this, arguments)); - }; - - TD.components.TweetDetailView.prototype._renderChirp = function(){ - let result = funcs.TweetDetailView.apply(this, arguments); - - if (this.mainChirp.quotedTweet && hasPoll(this.mainChirp.quotedTweet)){ - $(this.$tweetDetail).find(".js-quoted-tweet-text").first().after(renderTweetPoll(this.mainChirp.quotedTweet)); - } - - return result; - }; - - this.prevRenderFuncs = funcs; - window.TDPF_reloadColumns(); + // styles + + this.css = window.TDPF_createCustomStyle(this); + this.css.insert("html[data-td-theme='dark'] .quoted-tweet .td-timeline-poll { color: #e1e8ed; }"); + + // utility functions + + const hasPoll = function(tweet) { + return tweet.hasPoll && tweet.hasPoll(); + }; + + const renderTweetPoll = function(tweet) { + return `<div class='td-timeline-poll'>${TD.ui.template.render("status/poll", $.extend({}, tweet, { + chirp: tweet + }))}</div>`; + }; + + const renderPollHook = function(tweet, html) { + let ele = null; + + if (hasPoll(tweet)) { + (ele || (ele = $(html))).find(".js-tweet-body").first().children("div").last().after(renderTweetPoll(tweet)); + } + + if (tweet.quotedTweet && hasPoll(tweet.quotedTweet)) { + (ele || (ele = $(html))).find(".js-quoted-tweet-text").first().after(renderTweetPoll(tweet.quotedTweet)); + } + + if (ele) { + ele.find(".js-card-container").css("display", "none"); + return ele.prop("outerHTML"); + } + else { + return html; + } + }; + + // hooks + + const funcs = { + TwitterStatus: TD.services.TwitterStatus.prototype.render, + TwitterActionOnTweet: TD.services.TwitterActionOnTweet.prototype.render, + TweetDetailView: TD.components.TweetDetailView.prototype._renderChirp + }; + + TD.services.TwitterStatus.prototype.render = function(e) { + return renderPollHook(this, funcs.TwitterStatus.apply(this, arguments)); + }; + + TD.services.TwitterActionOnTweet.prototype.render = function(e) { + return renderPollHook(this.targetTweet, funcs.TwitterActionOnTweet.apply(this, arguments)); + }; + + TD.components.TweetDetailView.prototype._renderChirp = function() { + let result = funcs.TweetDetailView.apply(this, arguments); + + if (this.mainChirp.quotedTweet && hasPoll(this.mainChirp.quotedTweet)) { + $(this.$tweetDetail).find(".js-quoted-tweet-text").first().after(renderTweetPoll(this.mainChirp.quotedTweet)); + } + + return result; + }; + + this.prevRenderFuncs = funcs; + window.TDPF_reloadColumns(); } disabled(){ - TD.services.TwitterStatus.prototype.render = this.prevRenderFuncs.TwitterStatus; - TD.services.TwitterActionOnTweet.prototype.render = this.prevRenderFuncs.TwitterActionOnTweet; - TD.components.TweetDetailView.prototype._renderChirp = this.prevRenderFuncs.TweetDetailView; - - this.css.remove(); - window.TDPF_reloadColumns(); + TD.services.TwitterStatus.prototype.render = this.prevRenderFuncs.TwitterStatus; + TD.services.TwitterActionOnTweet.prototype.render = this.prevRenderFuncs.TwitterActionOnTweet; + TD.components.TweetDetailView.prototype._renderChirp = this.prevRenderFuncs.TweetDetailView; + + this.css.remove(); + window.TDPF_reloadColumns(); }