// ==UserScript==
// @name DTextStyler
// @namespace https://github.com/BrokenEagle/JavaScripts
// @version 5.6
// @description Danbooru DText UI addon.
// @source https://danbooru.donmai.us/users/23799
// @author BrokenEagle
// @match *://*.donmai.us/*
// @exclude /^https?://\w+\.donmai\.us/.*\.(xml|json|atom)(\?|$)/
// @grant none
// @run-at document-end
// @downloadURL https://raw.githubusercontent.com/BrokenEagle/JavaScripts/master/DTextStyler.user.js
// @updateURL https://raw.githubusercontent.com/BrokenEagle/JavaScripts/master/DTextStyler.user.js
// @require https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.3.2/papaparse.min.js
// @require https://raw.githubusercontent.com/BrokenEagle/JavaScripts/20220515/lib/module.js
// @require https://raw.githubusercontent.com/BrokenEagle/JavaScripts/20220515/lib/debug.js
// @require https://raw.githubusercontent.com/BrokenEagle/JavaScripts/20220515/lib/utility.js
// @require https://raw.githubusercontent.com/BrokenEagle/JavaScripts/20220515/lib/validate.js
// @require https://raw.githubusercontent.com/BrokenEagle/JavaScripts/20220515/lib/storage.js
// @require https://raw.githubusercontent.com/BrokenEagle/JavaScripts/20220515/lib/network.js
// @require https://raw.githubusercontent.com/BrokenEagle/JavaScripts/20220515/lib/load.js
// @require https://raw.githubusercontent.com/BrokenEagle/JavaScripts/20240223-menu/lib/menu.js
// ==/UserScript==
/* global JSPLib $ Danbooru Papa */
/****Global variables****/
// Library constants
////NONE
//Exterior script variables
const DANBOORU_TOPIC_ID = '14229';
const GITHUB_WIKI_PAGE = 'https://github.com/BrokenEagle/JavaScripts/wiki/DtextStyler';
//Variables for load.js
const PROGRAM_LOAD_REQUIRED_VARIABLES = ['window.jQuery', 'window.Danbooru', 'Danbooru.Upload'];
const PROGRAM_LOAD_OPTIONAL_SELECTORS = ['.dtext-previewable textarea', '#add-commentary-dialog', '.upload_artist_commentary_container', '#c-users #a-edit'];
//Program name constants
const PROGRAM_SHORTCUT = 'ds';
const PROGRAM_CLICK = 'click.ds';
const PROGRAM_KEYUP = 'keyup.ds';
const PROGRAM_NAME = 'DTextStyler';
//Main program variable
const DS = {};
const DEFAULT_VALUES = {
mode: 'edit',
};
//Available setting values
const ALL_TYPES = ['comment', 'forum', 'wiki', 'pool', 'dmail'];
const ALL_MARKUP = ['bold', 'italic', 'underline', 'strikethrough', 'translation', 'spoiler', 'code', 'nodtext', 'quote', 'expand', 'textile_link', 'wiki_link', 'named_link', 'search_link', 'full_table', 'headless_table'];
const ALL_ACTIONS = ['undo', 'redo'];
//Main settings
const SETTINGS_CONFIG = {
post_commentary_enabled: {
reset: true,
validate: JSPLib.validate.isBoolean,
hint: "Show dtext controls on the post commentary dialog."
},
upload_commentary_enabled: {
reset: true,
validate: JSPLib.validate.isBoolean,
hint: "Show dtext controls above the upload commentary inputs."
},
dtext_types_handled: {
allitems: ALL_TYPES,
reset: ALL_TYPES,
validate: (data) => JSPLib.menu.validateCheckboxRadio(data, 'checkbox', ALL_TYPES),
hint: "Show dtext controls above the preview area for the available types.",
},
available_dtext_markup: {
allitems: ALL_MARKUP,
reset: ALL_MARKUP,
validate: (data) => (JSPLib.menu.validateCheckboxRadio(data, 'checkbox', ALL_MARKUP) && (data.length > 0)),
hint: "Select the list of available DText tags to be shown. Must have at least one.",
},
available_dtext_actions: {
allitems: ALL_ACTIONS,
reset: ALL_ACTIONS,
validate: (data) => JSPLib.menu.validateCheckboxRadio(data, 'checkbox', ALL_ACTIONS),
hint: "Select the list of available DText actions to be shown.",
},
}
const MENU_CONFIG = {
topic_id: DANBOORU_TOPIC_ID,
wiki_page: GITHUB_WIKI_PAGE,
settings: [{
name: 'general',
},{
name: 'main',
},{
name: 'commentary',
},{
name: 'controls',
}],
controls: [],
};
//CSS constants
const PROGRAM_CSS = `
/** General **/
/**** Preview ****/
.ds-preview-display {
max-height: 300px;
overflow-y: auto;
}
.ds-preview-display .ds-section {
border: 1px solid #EEE;
padding: 5px;
}
.ds-preview-display .ds-section-header {
font-size: var(--text-lg);
font-weight: bold;
text-decoration: underline;
margin: 0.5rem 0;
}
/**** Markup buttons ****/
.ds-markup-headers > div,
.ds-markup-headers > div > div,
.ds-buttons > div,
.ds-buttons > div > div {
display: inline-block;
}
.ds-markup-headers > div > div {
text-align: center;
font-weight: bold;
}
.dtext-button {
width: 40px;
height: 40px;
position: relative;
}
.dtext-button > * {
position: absolute;
}
.ds-translate-content {
font-size: 16px;
font-weight: bold;
white-space: nowrap;
}`;
const POST_CSS = `
/** Posts page **/
form#fetch-commentary input#commentary_source {
max-width: 75%;
}
form#edit-commentary input#artist_commentary_original_title,
form#edit-commentary input#artist_commentary_translated_title {
max-width: 100%;
}
button.ds-dialog-button[name=Cancel] {
color: white;
background: red;
}
button.ds-dialog-button[name=Submit] {
color: white;
background: green;
}`;
const UPLOAD_CSS = `
/** Uploads page **/
#ds-commentary-container {
width: 65rem;
background: #F8F8F8;
border: 2px solid #EEE;
box-shadow: 0 0 0 2px #DEF;
padding: 10px;
margin: 10px 0;
}
#ds-commentary-container .ds-markup-controls {
border-bottom: 1px solid #DDD;
padding-bottom: 5px;
margin-bottom: 10px;
}
#edit-dialog #ds-commentary-container {
display: none; /* Hide commentary when the tag box is detached */
}
#ds-commentary-buttons {
border-top: 2px solid #DDD;
padding-top: 5px;
}
.ds-commentary-button {
display: inline-block;
}
#ds-remove-button {
color: white;
background: red;
}`;
const MENU_CSS = `
.jsplib-selectors.ds-selectors[data-setting="dtext_types_handled"] label {
width: 120px;
}
.jsplib-selectors.ds-selectors[data-setting="available_dtext_markup"] label {
width: 150px;
}`;
//HTML constants
const BOLD_CONTENT = ' ';
const ITALIC_CONTENT = ' ';
const UNDERLINE_CONTENT = ' ';
const STRIKETHROUGH_CONTENT = ' ';
const TRANSLATION_CONTENT = '
Aあ
';
const QUOTE_CONTENT = ' ';
const EXPAND_CONTENT = ' ';
const SPOILER_CONTENT = ' ';
const CODE_CONTENT = ' ';
const NODTEXT_CONTENT = ' ';
const TEXTILELINK_CONTENT = ' ';
const NAMEDLINK_CONTENT = ' ';
const WIKILINK_CONTENT = ' ';
const SEARCHLINK_CONTENT = ' ';
const FULL_TABLE_CONTENT = ' ';
const HEADLESS_TABLE_CONTENT = ' ';
const UNDO_CONTENT = ' ';
const REDO_CONTENT = ' ';
const MARKUP_CONTROLS = `
`;
const PREVIEW_SECTION = `
%s
`;
const PREVIEW_BUTTONS = `
`;
//Config constants
const MARKUP_BUTTON_CONFIG = {
bold: {
stripped: false,
inline: true,
block: false,
prefix: '[b]',
suffix: '[/b]',
top: '12px',
left: '15px',
content: BOLD_CONTENT,
},
italic: {
stripped: false,
inline: true,
block: false,
prefix: '[i]',
suffix: '[/i]',
top: '12px',
left: '16px',
content: ITALIC_CONTENT,
},
underline: {
stripped: false,
inline: true,
block: false,
prefix: '[u]',
suffix: '[/u]',
top: '12px',
left: '11px',
content: UNDERLINE_CONTENT,
},
strikethrough: {
stripped: false,
inline: true,
block: false,
prefix: '[s]',
suffix: '[/s]',
top: '12px',
left: '11px',
content: STRIKETHROUGH_CONTENT,
},
translation: {
stripped: true,
inline: true,
block: true,
prefix: '[tn]',
suffix: '[/tn]',
top: '10px',
left: '6px',
content: TRANSLATION_CONTENT,
},
spoiler: {
stripped: true,
inline: true,
block: true,
prefix: '[spoiler]',
suffix: '[/spoiler]',
top: '12px',
left: '10px',
content: SPOILER_CONTENT,
},
code: {
stripped: false,
inline: true,
block: true,
prefix: '[code]',
suffix: '[/code]',
top: '12px',
left: '14px',
content: CODE_CONTENT,
},
nodtext: {
stripped: false,
inline: true,
block: true,
prefix: '[nodtext]',
suffix: '[/nodtext]',
top: '4px',
left: '5px',
content: NODTEXT_CONTENT,
},
quote: {
stripped: true,
inline: false,
block: true,
prefix: '[quote]',
suffix: '[/quote]',
top: '12px',
left: '14px',
content: QUOTE_CONTENT,
},
expand: {
stripped: true,
inline: false,
block: true,
prefix: '[expand]',
suffix: '[/expand]',
top: '12px',
left: '10px',
content: EXPAND_CONTENT,
},
textile_link: {
stripped: false,
inline: true,
block: false,
prefix: '"',
suffix: '":[url]',
select_func: (text_area, _cursor_start, cursor_end)=>{
text_area.setSelectionRange(cursor_end - 4, cursor_end - 1);
},
top: '12px',
left: '12px',
content: TEXTILELINK_CONTENT,
},
wiki_link: {
stripped: false,
inline: true,
block: false,
prefix: '[[',
suffix: ']]',
top: '13px',
left: '11px',
content: WIKILINK_CONTENT,
},
named_link: {
stripped: false,
inline: true,
block: false,
prefix: '[[',
suffix: '|wiki_name]]',
select_func: (text_area, _cursor_start, cursor_end)=>{
text_area.setSelectionRange(cursor_end - 11, cursor_end - 2);
},
top: '11px',
left: '12px',
content: NAMEDLINK_CONTENT,
},
search_link: {
stripped: false,
inline: true,
block: false,
prefix: '{{',
suffix: '}}',
top: '13px',
left: '10px',
content: SEARCHLINK_CONTENT,
},
full_table: {
markup: (text_area)=>{TableMarkup(text_area, true);},
top: '12px',
left: '10px',
content: FULL_TABLE_CONTENT,
},
headless_table: {
markup: (text_area)=>{TableMarkup(text_area, false);},
top: '10px',
left: '10px',
content: HEADLESS_TABLE_CONTENT,
},
};
const ACTION_BUTTON_CONFIG = {
undo: {
action: (text_area)=>{UndoAction(text_area);},
top: '9px',
left: '9px',
content: UNDO_CONTENT,
},
redo: {
action: (text_area)=>{RedoAction(text_area);},
top: '9px',
left: '9px',
content: REDO_CONTENT,
},
};
const MARKUP_SECTION_CONFIG = {
font: {
color: 'beige',
buttons: ['bold', 'italic', 'underline', 'strikethrough'],
},
special: {
color: 'lightgreen',
buttons: ['translation', 'spoiler', 'code', 'nodtext'],
},
blocks: {
color: 'orange',
buttons: ['quote', 'expand'],
},
links: {
color: 'lightblue',
buttons: ['textile_link', 'wiki_link', 'named_link', 'search_link'],
},
tables: {
color: 'pink',
buttons: ['full_table', 'headless_table'],
},
};
const ACTION_SECTION_CONFIG = {
actions: {
color: 'lightgrey',
buttons: ['undo', 'redo'],
},
};
const DIALOG_CONFIG = {
Preview: function() {
DS.$dialog_buttons.filter('[name=Preview]').hide();
DS.$dialog_buttons.filter('[name=Edit]').show();
DtextPreview();
},
Edit: function() {
DS.$dialog_buttons.filter('[name=Preview]').show();
DS.$dialog_buttons.filter('[name=Edit]').hide();
DtextEdit();
},
};
//Other
const DTEXT_SELECTORS = {
comment: ['comment_body'],
forum: ['forum_topic_original_post_body', 'forum_post_body'],
wiki: ['wiki_page_body'],
pool: ['pool_description'],
dmail: ['dmail_body'],
};
/****Functions****/
//Library functions
////NONE
//Auxiliary functions
function GetTextArea($obj) {
let $text_areas = $obj.closest('.ds-container').find('textarea, input');
if ($text_areas.length <= 1) {
var text_area = $text_areas.get(0);
} else {
text_area = (['TEXTAREA', 'INPUT'].includes(document.activeElement.tagName) && [...document.activeElement.classList].includes('ds-commentary-input') ? document.activeElement : null);
}
if (!text_area) {
JSPLib.notice.error("No text input selected.");
} else {
DS.$close_notice.click();
}
return text_area;
}
function MarkupSelectionText(text_area, config) {
if ([...text_area.classList].includes('string') && config.stripped) {
JSPLib.notice.notice("Block elements not available for inline DText.");
return false;
}
SaveMarkup(text_area);
let {prefix, suffix, block, inline, select_func} = config;
let start_text = text_area.value.slice(0, text_area.selectionStart);
let selection_text = text_area.value.slice(text_area.selectionStart, text_area.selectionEnd);
let end_text = text_area.value.slice(text_area.selectionEnd);
if ((block && !inline) || ((block && inline) && (selection_text.search('\n') >= 0))) {
let starting_CRs = start_text.replace(/ /g, "").match(/\n*$/)[0].length;
let ending_CRs = end_text.replace(/ /g, "").match(/^\n*/)[0].length;
start_text = start_text + '\n'.repeat(Math.max(2 - starting_CRs));
end_text = '\n'.repeat(Math.max(2 - ending_CRs)) + end_text;
var markup_text = prefix + '\n\n' + selection_text.trim() + '\n\n' + suffix;
} else {
markup_text = prefix + selection_text + suffix;
}
let cursor_start = start_text.length;
let cursor_end = cursor_start + markup_text.length;
let final_text = start_text + markup_text + end_text;
final_text = final_text.replace(/^\s+/, "");
text_area.value = final_text;
text_area.focus();
if (select_func) {
select_func(text_area, cursor_start, cursor_end);
} else {
text_area.setSelectionRange(cursor_start, cursor_end);
}
return true;
}
function TableMarkup(text_area, has_header) {
if ([...text_area.classList].includes('string')) {
JSPLib.notice.notice("Block elements not available for inline DText.");
return false;
}
SaveMarkup(text_area);
let final_text = text_area.value.slice(0, text_area.selectionStart);
let selection_text = text_area.value.slice(text_area.selectionStart, text_area.selectionEnd);
final_text += CSVtoDtextTable(selection_text, has_header);
final_text = final_text.replace(/^\s+/, "");
let cursor = final_text.length;
final_text += text_area.value.slice(text_area.selectionEnd);
text_area.value = final_text;
text_area.focus();
text_area.setSelectionRange(cursor, cursor);
return true;
}
function SaveMarkup(text_area) {
let $text_area = $(text_area);
let undo_actions = $text_area.data('undo_actions') || [];
let undo_index = $text_area.data('undo_index') || 0;
undo_actions = undo_actions.slice(0, undo_index);
undo_actions.push(text_area.value);
$text_area.data('undo_actions', undo_actions);
$text_area.data('undo_index', undo_actions.length);
$text_area.data('undo_saved', true);
JSPLib.debug.debuglog('SaveMarkup', {undo_actions, undo_index});
}
function UndoAction(text_area) {
let $text_area = $(text_area);
let {undo_actions = [], undo_index = 0, undo_saved} = $text_area.data();
if (undo_saved) {
undo_actions.push(text_area.value);
$text_area.data('undo_actions', undo_actions);
}
let undo_html = undo_actions.slice(undo_index - 1, undo_index)[0];
if (undo_html) {
text_area.value = undo_html;
} else {
JSPLib.notice.notice("Beginning of actions buffer reached.");
}
let new_index = Math.max(0, undo_index - 1);
$text_area.data('undo_index', new_index);
$text_area.data('undo_saved', false);
JSPLib.debug.debuglog('UndoAction', {undo_actions, undo_index, new_index});
return Boolean(undo_html);
}
function RedoAction(text_area) {
let $text_area = $(text_area);
let {undo_actions = [], undo_index = 0} = $text_area.data();
let undo_html = undo_actions.slice(undo_index + 1, undo_index + 2)[0];
if (undo_html) {
text_area.value = undo_html;
} else {
JSPLib.notice.notice("End of actions buffer reached.");
}
let new_index = Math.min(undo_actions.length - 1, undo_index + 1);
$text_area.data('undo_index', new_index);
$text_area.data('undo_saved', false);
JSPLib.debug.debuglog('RedoAction', {undo_actions, undo_index, new_index});
return Boolean(undo_html);
}
function ClearActions(event) {
let $text_area = $(event.currentTarget);
$text_area.data('undo_actions', []);
$text_area.data('undo_index', 0);
$text_area.data('undo_saved', false);
JSPLib.debug.debuglog('Cleared actions.');
}
function DisplayUploadCommentary(open) {
if (open) {
DS.$commentary_buttons.show();
DS.$markup_controls.show();
DS.$remove_button.show();
DS.$preview_button.show();
DS.$edit_button.hide();
} else {
DS.$commentary_buttons.hide();
DS.$markup_controls.hide();
DS.$preview_display.hide();
}
}
//Render functions
function RenderMarkupButton(type, name, config) {
let title = JSPLib.utility.displayCase(name);
return `${config.content} `;
}
function RenderSectionControls(type, section_config, button_config, available_controls) {
let header_html = "";
let button_html = "";
for (let section in section_config) {
let html = "";
let button_length = 0;
for (let i = 0; i < section_config[section].buttons.length; i++) {
let button = section_config[section].buttons[i];
if (!available_controls.includes(button)) continue;
html += RenderMarkupButton(type, button, button_config[button]);
button_length++;
}
if (button_length === 0) continue;
button_html += `${html}
`;
let width = (button_length * 40);
let color = section_config[section].color;
let name = (button_length > 1 ? JSPLib.utility.displayCase(section) : " ");
header_html += `${name}
`;
}
return [header_html, button_html];
}
function RenderMarkupControls() {
if (DS.user_settings.available_dtext_markup.length === 0 && DS.user_settings.available_dtext_actions.length === 0) return;
let header_html = "";
let button_html = "";
if (DS.user_settings.available_dtext_markup.length) {
let [markup_header, markup_buttons] = RenderSectionControls('markup', MARKUP_SECTION_CONFIG, MARKUP_BUTTON_CONFIG, DS.user_settings.available_dtext_markup);
header_html += `${markup_header}
`;
button_html += `${markup_buttons}
`;
}
if (DS.user_settings.available_dtext_actions.length > 0) {
let [action_header, action_buttons] = RenderSectionControls('action', ACTION_SECTION_CONFIG, ACTION_BUTTON_CONFIG, DS.user_settings.available_dtext_actions);
header_html += `${action_header}
`;
button_html += `${action_buttons}
`;
}
let width = ((DS.user_settings.available_dtext_markup.length + DS.user_settings.available_dtext_actions.length) * 40) + 20;
return JSPLib.utility.sprintf(MARKUP_CONTROLS, String(width), header_html, button_html);
}
function RenderPreviewSection(name, has_header=false) {
let section_header = (has_header ? `` : "");
return JSPLib.utility.sprintf(PREVIEW_SECTION, name, section_header);
}
//Dtext functions
function CSVtoDtextTable(csvtext, has_header) {
let tabletext = "";
let sectiontext = "";
let csvdata = Papa.parse(csvtext);
JSPLib.debug.debuglog('CSVtoDtextTable', {csvdata, has_header});
csvdata.data.forEach((row, i)=>{
let rowtext = "";
row.forEach((col)=>{
if (i === 0 && has_header) {
rowtext += AddTableHeader(col);
} else {
rowtext += AddTableData(col);
}
});
sectiontext += AddTableRow(rowtext);
if (i === 0 && has_header) {
tabletext += AddTableHead(sectiontext);
sectiontext = "";
}
});
tabletext += AddTableBody(sectiontext);
return AddTable(tabletext);
}
function AddTable(input) {
return '[table]\n' + input + '[/table]\n';
}
function AddTableHead(input) {
return '[thead]\n' + input + '[/thead]\n';
}
function AddTableBody(input) {
return '[tbody]\n' + input + '[/tbody]\n';
}
function AddTableRow(input) {
return '[tr]\n' + input + '[/tr]\n';
}
function AddTableHeader(input) {
return '[th]' + input + '[/th]\n';
}
function AddTableData(input) {
return '[td]' + input + '[/td]\n';
}
//Event handlers
function DtextMarkup(event) {
let $button = $(event.currentTarget);
let text_area = GetTextArea($button);
if (!text_area) return;
let name = $button.attr('name');
let markup_func = (MARKUP_BUTTON_CONFIG[name].markup ? MARKUP_BUTTON_CONFIG[name].markup : MarkupSelectionText);
if (markup_func(text_area, MARKUP_BUTTON_CONFIG[name])) {
DS.$close_notice.click();
}
}
function DtextAction(event) {
let $button = $(event.currentTarget);
let text_area = GetTextArea($button);
if (!text_area) return;
let name = $button.attr('name');
let action_func = ACTION_BUTTON_CONFIG[name].action;
if (action_func(text_area, ACTION_BUTTON_CONFIG[name])) {
DS.$close_notice.click();
}
}
function OpenDialog() {
if (!DS.$add_commentary_dialog.data('initialized')) {
(DS.$dialog_buttons = DS.$add_commentary_dialog.closest('.ui-dialog').find('.ui-dialog-buttonset button'))
.each((_i, button)=>{
$(button).addClass('ds-dialog-button').attr('name', button.innerText);
});
DS.$add_commentary_dialog.data('initialized', true);
}
DS.$dialog_buttons.filter('[name=Preview]').show();
DS.$dialog_buttons.filter('[name=Edit]').hide();
DS.$edit_commentary = $('.ds-edit-commentary');
DS.$preview_display = $('.ds-preview-display');
DtextEdit();
}
function ClearPreview(event) {
setTimeout(()=>{
if (event.currentTarget.value === 'Preview') {
$(event.currentTarget).closest('form').find('.dtext-preview').html("");
}
}, 500);
}
function DtextPreview() {
DS.$edit_commentary.hide();
let promise_array = [];
let preview_array = [];
(DS.$commentary_input ||= $('.ds-commentary-input')).each((_i, input)=>{
let {section, part} = $(input).data();
let preview = {section, part};
let inline = preview.inline = [...input.classList].includes('string');
let body = preview.body = input.value;
let promise = (body.trim(/\s+/).length > 0 ? JSPLib.network.post('/dtext_preview', {data: {body, inline}}) : Promise.resolve(null));
promise_array.push(promise);
preview_array.push(preview);
});
Promise.all(promise_array).then((data)=>{
data.forEach((body, i)=>{
preview_array[i].body = body;
});
['original', 'artist', 'translated'].forEach((section)=>{
let $display = DS.$preview_display.filter(`[data-section=${section}]`);
if ($display.length === 0) return;
let is_shown = false;
let $section = $display.find('.ds-section').html("");
['title', 'description', 'desc'].forEach((part)=>{
let preview = preview_array.find((item) => (item.section === section && item.part === part));
if (!preview?.body) return;
if (part === 'title') {
$section.append(`${preview.body} `);
} else if (part === 'description' || part === 'desc') {
$section.append(`${preview.body}
`);
}
is_shown = true;
});
let action = (is_shown ? 'show' : 'hide');
$display[action]();
});
JSPLib.debug.debuglog('DtextPreview', preview_array);
});
DS.mode = 'preview';
}
function DtextEdit() {
DS.$preview_display.hide();
DS.$edit_commentary.show();
DS.mode = 'edit';
}
function ToggleCommentary() {
if (DS.commentary_open) {
DS.$toggle_artist_commentary.text('show »');
DS.$artist_commentary.slideUp();
DS.$upload_commentary_translation_container.slideUp();
DisplayUploadCommentary(false);
} else {
DS.$toggle_artist_commentary.text('« hide');
DS.$artist_commentary.slideDown();
DS.$upload_commentary_translation_container.slideDown();
DisplayUploadCommentary(true);
}
DS.commentary_open = !DS.commentary_open;
DS.mode = 'edit';
}
function ToggleTranslation() {
DS.$translation_edit_commentary ||= DS.$upload_commentary_translation_container.find('.ds-edit-commentary');
DS.$translation_preview_display ||= DS.$upload_commentary_translation_container.find('.ds-preview-display');
if (DS.translation_open) {
DS.$toggle_commentary_translation.text('show »');
DS.$translation_edit_commentary.slideUp();
DS.$translation_preview_display.slideUp();
} else {
DS.$toggle_commentary_translation.text('« hide');
if (DS.mode === 'preview') {
DS.$translation_preview_display.slideDown();
} else if (DS.mode === 'edit') {
DS.$translation_edit_commentary.slideDown();
}
}
DS.translation_open = !DS.translation_open;
}
//Initialize
function InitializeButtons($button_container) {
let all_configs = Object.assign({}, MARKUP_BUTTON_CONFIG, ACTION_BUTTON_CONFIG);
for (let key in all_configs) {
let {top, left} = all_configs[key];
$button_container.find(`button[name=${key}] > *:first-of-type`)
.css({top, left});
}
}
function InitializeDtextPreviews() {
let containers = JSPLib.utility.multiConcat(...DS.user_settings.dtext_types_handled.map((type) => DTEXT_SELECTORS[type]));
let final_selector = JSPLib.utility.joinList(containers, '.', ' .dtext-previewable textarea', ', ');
$(final_selector).each((_i, textarea)=>{
let $textarea = $(textarea);
let $container = $textarea.closest('.input.dtext');
$container.addClass('ds-container');
$container.find('.dtext-previewable').before(RenderMarkupControls());
InitializeButtons($container.find('.ds-buttons'));
$textarea.on(PROGRAM_KEYUP, ClearActions);
});
$('.dtext-preview-button').on(PROGRAM_CLICK, ClearPreview);
}
function InitializeCommentaryDialog() {
DS.$add_commentary_dialog = $('#add-commentary-dialog');
if (DS.$add_commentary_dialog.length === 0) return;
DS.$add_commentary_dialog.addClass('ds-container');
DS.$add_commentary_dialog.find('#fetch-commentary')
.after(RenderMarkupControls());
InitializeButtons(DS.$add_commentary_dialog.find('.ds-buttons'));
$('#edit-commentary')
.addClass('ds-edit-commentary')
.after(RenderPreviewSection('original', true) + RenderPreviewSection('translated', true));
DS.$add_commentary_dialog.data('initialized', false);
['original', 'translated'].forEach((section)=>{
['title', 'description'].forEach((part)=>{
$(`#artist_commentary_${section}_${part}`).data({section, part}).addClass('ds-commentary-input');
});
});
//Wait for the dialog to be initialized before performing the final step
JSPLib.utility.recheckTimer({
check: () => JSPLib.utility.hasDOMDataKey('#add-commentary-dialog', 'uiDialog'),
exec: ()=>{
let buttons = DS.$add_commentary_dialog.dialog('option', 'buttons');
buttons = Object.assign(DIALOG_CONFIG, buttons);
let dialog_width = Math.max((DS.user_settings.available_dtext_markup.length + DS.user_settings.available_dtext_actions.length) * 40 + 50, DS.$add_commentary_dialog.dialog('option', 'width'));
DS.$add_commentary_dialog.dialog('option', 'buttons', buttons);
DS.$add_commentary_dialog.dialog('option', 'width', dialog_width);
DS.$add_commentary_dialog.on('dialogopen.ds', OpenDialog);
},
}, 500, JSPLib.utility.one_second * 15);
DS.$add_commentary_dialog.find('.ds-commentary-input').on(PROGRAM_KEYUP, ClearActions);
}
function InitializeUploadCommentary() {
//Move existing sections and add controls
DS.$upload_artist_commentary_container = $('.upload_artist_commentary_container').detach();
if (DS.$upload_artist_commentary_container.length === 0) return;
DS.$upload_artist_commentary_container
.addClass('ds-container')
.prepend(RenderMarkupControls());
InitializeButtons(DS.$upload_artist_commentary_container.find('.ds-buttons'));
(DS.$artist_commentary = DS.$upload_artist_commentary_container.find('.artist-commentary'))
.addClass('ds-edit-commentary')
.after(RenderPreviewSection('artist'));
DS.$upload_commentary_translation_container = $('.upload_commentary_translation_container').detach();
(DS.$commentary_translation = DS.$upload_commentary_translation_container.find('.commentary-translation'))
.addClass('ds-edit-commentary')
.after(RenderPreviewSection('translated'));
['artist', 'translated'].forEach((section)=>{
let $container = (section === 'artist' ? DS.$upload_artist_commentary_container : DS.$upload_commentary_translation_container);
['title', 'desc'].forEach((part)=>{
$container.find(`#post_${section}_commentary_${part}`).data({section, part}).addClass('ds-commentary-input');
});
});
(DS.$overall_container = $(''))
.append(DS.$upload_artist_commentary_container)
.append(DS.$upload_commentary_translation_container).
append(PREVIEW_BUTTONS);
$('#tags-container').before(DS.$overall_container);
//Initialize commentary handlers
(DS.$preview_button = $('#ds-preview-button')).on(PROGRAM_CLICK, ()=>{
DtextPreview();
DS.$preview_button.hide();
DS.$edit_button.show();
});
(DS.$edit_button = $('#ds-edit-button')).on(PROGRAM_CLICK, ()=>{
DtextEdit();
DS.$preview_button.show();
DS.$edit_button.hide();
});
(DS.$remove_button = $('#ds-remove-button')).on(PROGRAM_CLICK, ()=>{
DS.$overall_container.remove();
});
//Wait until Danbooru binds the click, otherwise 2 click handlers will be bound
JSPLib.utility.recheckTimer({
check: () => JSPLib.utility.isNamespaceBound('#toggle-artist-commentary', 'click', 'danbooru'),
exec: ()=>{
DS.commentary_open = DS.$artist_commentary.is(':visible');
Danbooru.Upload.toggle_commentary = ToggleCommentary;
if (DS.commentary_open) {
DisplayUploadCommentary(true);
}
(DS.$toggle_artist_commentary = $('#toggle-artist-commentary')).off('click.danbooru').on(PROGRAM_CLICK, function(event) {
Danbooru.Upload.toggle_commentary();
event.preventDefault();
});
DS.translation_open = DS.$commentary_translation.is(':visible');
Danbooru.Upload.toggle_translation = ToggleTranslation;
(DS.$toggle_commentary_translation = $('#toggle-commentary-translation')).off('click.danbooru').on(PROGRAM_CLICK, function(event) {
Danbooru.Upload.toggle_translation();
event.preventDefault();
});
},
}, 500, JSPLib.utility.one_second * 15);
DS.$edit_commentary = $('.ds-edit-commentary');
DS.$preview_display = $('.ds-preview-display');
DS.$commentary_buttons = $('#ds-commentary-buttons');
DS.$markup_controls = $('.ds-markup-controls');
DS.$overall_container.find('.ds-commentary-input').on(PROGRAM_KEYUP, ClearActions);
}
// Settings functions
function InitializeProgramValues() {
Object.assign(DS, {
$close_notice: $('#close-notice-link'),
});
return true;
}
function RenderSettingsMenu() {
$('#dtext-styler').append(JSPLib.menu.renderMenuFramework(MENU_CONFIG));
$("#ds-general-settings").append(JSPLib.menu.renderDomainSelectors());
$("#ds-main-settings").append(JSPLib.menu.renderInputSelectors('dtext_types_handled', 'checkbox'));
$('#ds-commentary-settings').append(JSPLib.menu.renderCheckbox('post_commentary_enabled'));
$('#ds-commentary-settings').append(JSPLib.menu.renderCheckbox('upload_commentary_enabled'));
$("#ds-controls-settings").append(JSPLib.menu.renderInputSelectors('available_dtext_markup', 'checkbox'));
$("#ds-controls-settings").append(JSPLib.menu.renderInputSelectors('available_dtext_actions', 'checkbox'));
JSPLib.menu.engageUI(true);
JSPLib.menu.saveUserSettingsClick();
JSPLib.menu.resetUserSettingsClick();
}
//Main program
function Main() {
JSPLib.debug.debuglog("Initialize start:", JSPLib.utility.getProgramTime());
const preload = {
run_on_settings: false,
default_data: DEFAULT_VALUES,
initialize_func: InitializeProgramValues,
menu_css: MENU_CSS,
};
if (!JSPLib.menu.preloadScript(DS, RenderSettingsMenu, preload)) return;
if (DS.user_settings.dtext_types_handled.length) {
InitializeDtextPreviews();
}
if (DS.user_settings.post_commentary_enabled && DS.controller === 'posts' && DS.action === 'show') {
InitializeCommentaryDialog();
JSPLib.utility.setCSSStyle(POST_CSS, 'post');
} else if (DS.user_settings.upload_commentary_enabled && ((DS.controller === 'uploads' && DS.action === 'show') || (DS.controller === 'upload-media-assets' && DS.action === 'show'))) {
InitializeUploadCommentary();
JSPLib.utility.setCSSStyle(UPLOAD_CSS, 'upload');
}
$('.dtext-markup').on(PROGRAM_CLICK, DtextMarkup);
$('.dtext-action').on(PROGRAM_CLICK, DtextAction);
JSPLib.utility.blockActiveElementSwitch('.dtext-markup, .dtext-action');
JSPLib.utility.setCSSStyle(PROGRAM_CSS, 'program');
}
/****Initialization****/
//Variables for debug.js
JSPLib.debug.debug_console = false;
JSPLib.debug.level = JSPLib.debug.INFO;
JSPLib.debug.program_shortcut = PROGRAM_SHORTCUT;
//Variables for menu.js
JSPLib.menu.program_shortcut = PROGRAM_SHORTCUT;
JSPLib.menu.program_name = PROGRAM_NAME;
JSPLib.menu.program_data = DS;
JSPLib.menu.settings_config = SETTINGS_CONFIG;
//Export JSPLib
JSPLib.load.exportData(PROGRAM_NAME, DS);
/****Execution start****/
JSPLib.load.programInitialize(Main, {program_name: PROGRAM_NAME, required_variables: PROGRAM_LOAD_REQUIRED_VARIABLES, optional_selectors: PROGRAM_LOAD_OPTIONAL_SELECTORS});