mirror of
https://github.com/chylex/TweetDuck.git
synced 2025-04-25 00:15:48 +02:00
Reformat plugin code
This commit is contained in:
parent
24f5075116
commit
8149ed50e1
Resources/Plugins
.debug
clear-columns
edit-design
emoji-keyboard
reply-account
templates
timeline-polls
@ -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");
|
||||
}
|
||||
|
@ -1,3 +1,3 @@
|
||||
run(){
|
||||
console.info("executed debug plugin in notification");
|
||||
console.info("executed debug plugin in notification");
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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>
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user