1
0
Fork 0
TweetDuck/resources/Plugins/edit-design/browser.js

810 lines
23 KiB
JavaScript

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 &amp; 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();
}