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] + ")"; }; let validatePositiveNumber = function(val) { if (/^[0-9]+$/.test(val) && parseInt(val, 10) > 0) { return true; } alert("The value must be a number larger than 0."); return false; }; 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 = () => { const val = me.config[key]; if (key === "columnWidth") { if (!item.val(val).val()) { const optionCustomPx = item.find("option[value='custom-px']"); const optionCustomPxNew = item.find("option[value='change-custom-px']"); const optionCustomCols = item.find("option[value='custom-cols']"); const optionCustomColsNew = item.find("option[value='change-custom-cols']"); if (val.length > 0 && val[0] === "/") { const cols = val.substring(1); item.val(optionCustomCols.attr("value")); optionCustomCols.text("Custom (" + cols + (cols === "1" ? " column" : " columns") + ")"); optionCustomColsNew.show(); optionCustomPx.text("Custom"); optionCustomPxNew.hide(); } else { item.val(optionCustomPx.attr("value")); optionCustomPx.text(getTextForCustom(key)); optionCustomPxNew.show(); optionCustomCols.text("Custom"); optionCustomColsNew.hide(); } return; } } else if (!item.val(val).val() && optionCustom.length === 1) { item.val(optionCustom.attr("value")); optionCustom.text(getTextForCustom(key)); optionCustomNew.show(); return; } 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 (validatePositiveNumber(val)) { updateKey(key, val + "px"); } } } else if (val.endsWith("custom-cols")) { val = (prompt("Enter how many columns you want to see on the screen:") || "").trim(); if (val && validatePositiveNumber(val)) { updateKey(key, "/" + val); } } 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"); font-weight: normal; font-style: normal; } ${iconData.map(entry => `#tduck .icon-${entry[0]}:before{content:\"\\f0${entry[1]}\";font-family:_of!important}`).join("")} .drawer .btn .icon, .app-header .btn .icon { line-height: 1em !important } .app-search-fake .icon { margin-top: -3px !important } #tduck .search-input-control .icon { font-size: 20px !important; top: -4px !important } #tduck .js-docked-compose .js-drawer-close { margin: 20px 0 0 !important } #tduck .compose-media-bar-remove .icon-close, #tduck .compose-media-grid-remove .icon-close { padding: 3px 2px 1px !important } .js-column-header .column-type-icon { margin-top: 0 !important } .inline-reply .pull-left .Button--link { margin-top: 3px !important } .js-inline-compose-pop .icon-popout { font-size: 23px !important } .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>", ` <style type='text/css'> html[data-td-font] { font-size: ${this.config.fontSize} !important } .avatar { border-radius: ${this.config.avatarRadius}% !important } ${this.config.forceArialFont ? ` #tduck { font-family: Arial, sans-serif; font-weight: 400 } ` : ``} ${this.config.increaseQuoteTextSize ? ` .quoted-tweet { font-size: 1em !important } ` : ``} ${this.config.revertIcons ? ` @font-face { font-family: '_of'; src: url(\"https://ton.twimg.com/tweetdeck-web/web/assets/fonts/tweetdeck-regular-webfont.5f4ea87976.woff\") format(\"woff\"); font-weight: normal; font-style: normal } #tduck .icon-reply:before{content:"\\f006";font-family:_of!important} #tduck .icon-heart-filled:before{content:"\\f055";font-family:_of!important} #tduck .icon-retweet-filled:before{content:"\\f008";font-family:_of!important} #tduck .icon-list-filled:before{content:"\\f014";font-family:_of!important} #tduck .icon-user-filled:before{content:"\\f035";font-family:_of!important} #tduck .icon-user-dd:before{content:"\\f01a";font-family:_of!important} ` : ``} ${currentTheme === "black" ? ` html.dark a, html.dark a:hover, html.dark a:focus, html.dark a:active { color: #8bd } #tduck-show-thread, .other-replies-link { color: #8bd !important } .quoted-tweet { border-color: #292f33 !important } ` : ``} ${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); }; } 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); } }; } 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); }); } 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(); }