您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds a few quality of life improvements on Sankaku Channel: 'Choose/Set Parent' mode, automatic image scaling, duplicate tagging/flagging, muting videos. Fully configurable using localStorage.
当前为
// ==UserScript== // @name SankakuAddon // @namespace SankakuAddon // @description Adds a few quality of life improvements on Sankaku Channel: 'Choose/Set Parent' mode, automatic image scaling, duplicate tagging/flagging, muting videos. Fully configurable using localStorage. // @include http://chan.sankakucomplex.com/* // @include https://chan.sankakucomplex.com/* // @include http://idol.sankakucomplex.com/* // @include https://idol.sankakucomplex.com/* // @run-at document-start // @version 0.98 // @grant none // ==/UserScript== "use strict"; var version = "v0.98"; if (!String.prototype.startsWith) { String.prototype.startsWith = function(searchString, position) { return this.substr(position || 0, searchString.length) === searchString; }; } /***************************/ /* configuration functions */ /***************************/ var default_config = { scroll_to_image:true, scale_image:true, //and video scale_only_downscale:false, scale_flash:false, scale_mode:0, scale_on_resize:false, video_pause:false, video_mute:true, video_controls:true, post_show_speaker_icon:true, setparent_deletepotentialduplicate:false, hide_headerlogo:false, tag_search_buttons:true, common_tags:"1girl 2girls 3girls 4girls 5girls 6+girls 1boy 2boys 3boys 4boys 5boys 6+boys" //experimental }; var config_value_types = ["scale_mode", "common_tags"]; var use_local_storage = (typeof(Storage) !== "undefined"); var key_prefix = "config."; var config = JSON.parse(JSON.stringify(default_config)); var dom_content_loaded = false; function save_config(key, value) { if (!use_local_storage) { notice("addon: couldn't save setting \"" + key + " = " + value + "\" to local storage. check permissions."); return; } var cfg_key = key_prefix + key; localStorage.setItem(cfg_key, JSON.stringify(value)); } function load_config() { for (var key in config) { if (config.hasOwnProperty(key)) { var value = config[key]; //default already loaded if (use_local_storage) { var cfg_key = key_prefix + key; var stored_value = localStorage.getItem(cfg_key); if (stored_value) value = JSON.parse(stored_value); } config_changed(key, value); //fire regardless } } } function local_storage_changed(e) { if (e.key.startsWith(key_prefix)) { var key = e.key.substring(key_prefix.length); var value = JSON.parse(e.newValue); config_changed(key, value); } } //event that is fired whenever a setting changes in the config dialog or the local storage function config_changed(key, value) { config[key] = value; if (!dom_content_loaded) return; //UI hasn't loaded yet update_config_dialog_by_key(key); if (key === "hide_headerlogo") update_headerlogo(); } function reset_config() { //clear local storage if (use_local_storage) { for (var key in config) { if (config.hasOwnProperty(key)) { var cfg_key = key_prefix + key; localStorage.removeItem(cfg_key); } } } for (var key in config) { if (config.hasOwnProperty(key)) { config_changed(key, default_config[key]); } } } //calls f(cfg_elem, key, get_value) for each existing config element function foreach_config_element(f) { for (var key in config) { if (config.hasOwnProperty(key)) { var cfg_key = key_prefix + key; var cfg_elem = document.getElementById(cfg_key); if (cfg_elem === null) continue; (function(cfg_elem) { if (config_value_types.indexOf(key) != -1) { f(cfg_elem, key, function() { return cfg_elem.value; }); } else { f(cfg_elem, key, function() { return cfg_elem.checked; }); } })(cfg_elem); } } } function update_config_dialog_by_key(key) { var cfg_key = key_prefix + key; var cfg_elem = document.getElementById(cfg_key); if (cfg_elem !== null) { if (config_value_types.indexOf(key) != -1) { cfg_elem.value = config[key]; } else { cfg_elem.checked = config[key]; } } } function update_config_dialog() { for (var key in config) { if (config.hasOwnProperty(key)) { update_config_dialog_by_key(key); } } } function update_headerlogo() { hide_headerlogo(config.hide_headerlogo); } function show_config_dialog(bool) { document.getElementById("cfg_dialog").style.display = (bool ? "" : "none"); } /********************/ /* helper functions */ /********************/ function get_scrollbar_width() { //from Stack Overflow var outer = document.createElement("DIV"); outer.style.visibility = "hidden"; outer.style.width = "100px"; document.body.appendChild(outer); var widthNoScroll = outer.offsetWidth; outer.style.overflow = "scroll"; //force scrollbars var inner = document.createElement("DIV"); inner.style.width = "100%"; outer.appendChild(inner); var widthWithScroll = inner.offsetWidth; outer.parentNode.removeChild(outer); return widthNoScroll - widthWithScroll; } //helper function to modify nodes on creation function register_observer(node_predicate, node_modifier) { var observer = new MutationObserver(function(mutations) { for (var i = 0; i < mutations.length; i++) { var added_nodes = mutations[i].addedNodes; for (var j = 0; j < added_nodes.length; j++) { var node = added_nodes[j]; if (node_predicate(node)) { if (node_modifier(node)) { //are we done? observer.disconnect(); return; } } } } }); observer.observe(document, {childList: true, subtree: true}); } /*********************/ /* general functions */ /*********************/ var header_offset_height = null; //distance needed to scroll if headerlogo is hidden/shown function hide_headerlogo(hide) { var headerlogo = document.getElementById("headerlogo"); var visible = (headerlogo.style.display != "none"); headerlogo.style.display = (hide ? "none" : ""); //scroll if needed if (visible === !!hide) window.scrollBy(0, (visible && hide ? -1 : 1) * header_offset_height); } function add_config_dialog() { var cfg_dialog = document.createElement("div"); cfg_dialog.id = "cfg_dialog"; cfg_dialog.style.display = "none"; cfg_dialog.style.border = "1px solid #DDD"; cfg_dialog.style.top = "300px"; cfg_dialog.style.width = "400px"; cfg_dialog.style.height = "370px"; cfg_dialog.style.position = "fixed"; cfg_dialog.style.left = "0"; cfg_dialog.style.right = "0"; cfg_dialog.style.margin = "0 auto"; cfg_dialog.style.overflow = "auto"; cfg_dialog.style.backgroundColor = "white"; cfg_dialog.style.zIndex = 10002; cfg_dialog.innerHTML = "" + "<div style='display: table; position: absolute; height: 100%; width: 100%'>" + "<div style='display: table-cell; vertical-align: middle'>" + "<div style='padding: 8px; margin-left: auto; margin-right: auto;'>" + "<b>Sankaku Addon "+version+"</b><br>" + "<hr style='margin-top: 0; margin-bottom: 2px; border:1px solid #DDD;'>" + "<span>" + "<input id='" + key_prefix + "scroll_to_image' type='checkbox'>" + "<span>Scroll to image/video when opening post</span><br>" + "</span>" + "<span>" + "<input id='" + key_prefix + "scale_image' type='checkbox'>" + "<span>Scale image/video when opening post</span><br>" + "</span>" + "<span>" + "<input id='" + key_prefix + "scale_only_downscale' type='checkbox'>" + "<span>Only downscale</span><br>" + "</span>" + "<span>" + "<input id='" + key_prefix + "scale_flash' type='checkbox'>" + "<span>Also scale flash videos</span><br>" + "</span>" + "<span>" + "<input id='" + key_prefix + "scale_on_resize' type='checkbox'>" + "<span title=\"This uses the 'scale image mode' setting, so it doesn't work well when using the manual scaling actions.\" style='cursor:help'>Scale image on window resize</span><br>" + "</span>" + "<span>" + "<span>Scale image/video mode: </span>" + "<select id='" + key_prefix + "scale_mode'>" + "<option value=0>Fit to window</option>" + "<option value=1>Fit horizontally</option>" + "<option value=2>Fit vertically</option>" + "</select>" + "<br>" + "</span>" + "<span>" + "<input id='" + key_prefix + "video_pause' type='checkbox'>" + "<span>Pause (non-flash) videos*</span><br>" + "</span>" + "<span>" + "<input id='" + key_prefix + "video_mute' type='checkbox'>" + "<span>Mute (non-flash) videos*</span><br>" + "</span>" + "<span>" + "<input id='" + key_prefix + "video_controls' type='checkbox'>" + "<span>Show video controls*</span><br>" + "</span>" + "<span>" + "<input id='" + key_prefix + "tag_search_buttons' type='checkbox'>" + "<span>Show + - tag search buttons*</span><br>" + "</span>" + "<span>" + "<input id='" + key_prefix + "post_show_speaker_icon' type='checkbox'>" + "<span>Show speaker icon on post if it has audio*</span><br>" + "</span>" + "<span>" + "<input id='" + key_prefix + "setparent_deletepotentialduplicate' type='checkbox'>" + "<span>Delete potential_duplicate tag when using \"Set Parent\"</span><br>" + "</span>" + "<span>" + "<input id='" + key_prefix + "hide_headerlogo' type='checkbox'>" + "<span>Hide header logo</span><br>" + "</span>" + "<span>" + "<input id='" + key_prefix + "common_tags'>" + "<span> Experimental common tags list*</span><br>" + "</span>" + "<button id='config_close'>Close</button>" + "<button id='config_reset'>Reset settings</button>" + "<span> *requires a page reload.</span>" + "</div>" + ""; document.body.appendChild(cfg_dialog); //add events document.getElementById("config_close").onclick = function() { show_config_dialog(false); return false; }; document.getElementById("config_reset").onclick = function() { reset_config(); return false; }; foreach_config_element(function(cfg_elem, key, get_value) { cfg_elem.addEventListener("change", function() { config_changed(key, get_value()); save_config(key, get_value()); }); }); } function add_config_button() { var navbar = document.getElementById("navbar"); if (!navbar) { notice("addon error: couldn't find \"navbar\" element! Config dialog disabled."); return } navbar.style.whiteSpace = "nowrap"; //hack to fit config button var a = document.createElement("A"); a.href = "#"; a.onclick = function() { show_config_dialog(true); return false; }; a.innerHTML = "<span style='font-size: 110%'>⚙</span> Addon config"; a.style.fontSize = "120%"; var li = document.createElement("LI"); li.className = "lang-select"; //match style of top bar li.appendChild(a); navbar.appendChild(li); } function add_tag_search_buttons() { var tagsidebar = document.getElementById("tag-sidebar"); if (tagsidebar === null) return; var items = tagsidebar.getElementsByTagName("LI"); for (var i = 0; i < items.length; i++) { var taglink = items[i].getElementsByTagName("A")[0]; var tagname = taglink.innerHTML.replace(/ /g, "_"); // " " -> "_" hopefully this is the only edgecase //generates onclick events var tag_search_button_func = function(tagname, add) { return function() { var search_field = document.getElementById("tags"); if (add) { search_field.value = (search_field.value + " " + tagname).trim(); } else { search_field.value = search_field.value.replace(new RegExp(tagname, "g"), "").replace(new RegExp(" ", "g"), " ").trim(); } //focus but don't scroll var x = window.scrollX, y = window.scrollY; search_field.focus(); window.scrollTo(x, y); return false; } }; var a = document.createElement("A"); a.href = "#"; a.innerHTML = "+ "; a.onclick = tag_search_button_func(tagname, true); items[i].insertBefore(a, taglink); a = document.createElement("A"); a.href = "#"; a.innerHTML = "- "; a.onclick = tag_search_button_func(tagname, false); items[i].insertBefore(a, taglink); } } /***********************************************/ /* main page / visually similar page functions */ /***********************************************/ function add_mode_options(mode_dropdown) { var option; if (mode_dropdown.options.namedItem("choose-parent") === null) { option = document.createElement("option"); option.text = "Choose Parent"; option.value = "choose-parent"; mode_dropdown.add(option); } if (mode_dropdown.options.namedItem("set-parent") === null) { option = document.createElement("option"); option.text = "Set Parent"; option.value = "set-parent"; mode_dropdown.add(option); } } var added_mode_options = function() { var i = 0; return function() { i++; if (i == 2) { //wait for mode options to be added and PostModeMenu to change PostModeMenu.change(); //guarantee that 'mode' variable correctly changes to new modes when loading page } }; }(); var updated_PostModeMenu = added_mode_options; function add_speaker_icon(span) { var img = span.querySelector(".preview"); if (img === null) { notice("addon error: preview image not found?"); return; } var a = span.getElementsByTagName("A"); if (a.length == 0) { notice("addon error: preview image not clickable?"); return; } if (img.title.trim().split(/\s+/).indexOf("has_audio") == -1) return; a[0].style.display = "inline-block"; //makes the element fit its content a[0].style.position = "relative"; var speaker_icon = document.createElement("SPAN"); speaker_icon.style.position = "absolute"; speaker_icon.style.top = "2px"; //account for border speaker_icon.style.right = "2px"; speaker_icon.style.fontSize = "200%"; speaker_icon.style.textShadow = "-1px 0 white, 0 1px white, 1px 0 white, 0 -1px white"; speaker_icon.style.transform = "translateX(50%) translateY(-50%)"; speaker_icon.innerHTML = "?"; a[0].appendChild(speaker_icon); } /***********************/ /* post page functions */ /***********************/ //original post/parent ids var post_id = null; var parent_id = null; //original image size var img_elem = null; //image or video var non_img_elem = null, emb_elem = null; //flash or unknown var img_is_flash; var img_width = null; var img_height = null; var img_aspect_ratio = null; var img_current_height = null; //store current height separately, because flash is a bitch var resize_timer; var tag_update_timer; function add_tag_menu() { var tag_menu = document.createElement("DIV"); tag_menu.id = "tag_menu"; tag_menu.style.display = "none"; tag_menu.style.border = "1px solid #DDD"; tag_menu.style.width = "100%"; tag_menu.style.height = "30%"; //TODO variable height tag_menu.style.position = "fixed"; tag_menu.style.bottom = "0"; tag_menu.style.overflow = "auto"; tag_menu.style.backgroundColor = "white"; tag_menu.style.zIndex = 10001; document.body.appendChild(tag_menu); //the inner div ensures tag_menu_close button doesn't scroll with the content tag_menu.innerHTML = "<div style='width: 100%; height: 100%; overflow: auto;'>common tags:<br>" + "<span id='common_tags'></span>" + "<br>current tags:<br>" + "<span id='current_tags'></span></div>"; var generate_tag_menu_button = function(id, text) { var button = document.createElement("DIV"); button.id = id; button.style.border = "1px solid #DDD"; button.style.width = "24px"; button.style.height = "24px"; button.style.position = "absolute"; button.style.backgroundColor = "#EEE"; button.innerHTML = "<span style='position: absolute; top: 50%; left: 50%; transform: translateX(-50%) translateY(-50%);'>" + text + "</span>"; button.style.zIndex = 10001; return button; }; var tag_menu_close = generate_tag_menu_button("tag_menu_close", "X"); tag_menu_close.style.right = "0"; tag_menu_close.style.top = "0"; tag_menu_close.onclick = function() { show_tag_menu(false); return false; }; tag_menu.appendChild(tag_menu_close); var tag_menu_open = generate_tag_menu_button("tag_menu_open", "«"); tag_menu_open.style.position = "fixed"; tag_menu_open.style.right = "0"; tag_menu_open.style.bottom = "0"; tag_menu_open.onclick = function() { show_tag_menu(true); update_tag_menu(); return false; }; document.body.appendChild(tag_menu_open); } function update_tag_menu() { var common_tags = document.getElementById("common_tags"); var current_tags = document.getElementById("current_tags"); if (common_tags === null || current_tags === null) return; var generate_tag_button = function(tag) { var a = document.createElement("A"); a.href = "#"; a.style.paddingLeft = "5px"; a.style.paddingRight = "5px"; a.style.borderStyle = "solid"; a.style.borderWidth = "1px"; a.className = (tag_is_present(tag) ? "" : "tag_nonexistent"); a.onclick = function(tag, a) { return function() { if (tag_is_present(tag)) { remove_tag(tag); a.className = "tag_nonexistent"; } else { add_tag(tag); a.className = ""; } return false; }; }(tag, a); a.innerHTML = tag; return a; }; var generate_tag_button_li = function(tag) { var li = document.createElement("LI"); li.style.paddingTop = "3px"; li.style.paddingBottom = "3px"; li.style.float = "left"; li.appendChild(generate_tag_button(tag)); return li; }; var create_tag_list = function() { var ul = document.createElement("UL"); ul.style.listStyleType = "none"; ul.style.margin = "0"; ul.style.padding = "0"; ul.style.display = "inline-block"; return ul; }; var ul1 = create_tag_list(); var ul2 = create_tag_list(); var common_tags_array = config.common_tags.trim().split(/\s+/); for (var i = 0; i < common_tags_array.length; i++) { ul1.appendChild(generate_tag_button_li(common_tags_array[i])); } var tags = get_tags_array(); for (var i = 0; i < tags.length; i++) { ul2.appendChild(generate_tag_button_li(tags[i])); } while (common_tags.hasChildNodes()) { common_tags.removeChild(common_tags.lastChild); } while (current_tags.hasChildNodes()) { current_tags.removeChild(current_tags.lastChild); } common_tags.appendChild(ul1); current_tags.appendChild(ul2); } function show_tag_menu(bool) { document.getElementById("tag_menu").style.display = (bool ? "" : "none"); document.getElementById("tag_menu_open").style.display = (!bool ? "" : "none"); } function add_addon_actions() { var li = document.getElementById("add-to-pool"); if (li === null) { notice("addon error: couldn't find \"add-to-pool\" element! Addon actions disabled."); return; } var actions_ul = li.parentElement; var separator = document.createElement("H5"); separator.innerHTML = "Addon actions"; var newli = document.createElement("LI"); newli.appendChild(separator); actions_ul.appendChild(newli); var add_action = function(func, name, id) { var a = document.createElement("A"); a.href = "#"; a.onclick = function() {func(); return false;}; a.innerHTML = name; var newli = document.createElement("LI"); newli.id = id; newli.appendChild(a); actions_ul.appendChild(newli); }; add_action(function() { scale_image( 0, true); scroll_to_image(); }, "Fit image", "scale-image-fit"); add_action(function() { scale_image( 1, true); scroll_to_image(); }, "Fit image (Horizontal)", "scale-image-hor"); add_action(function() { scale_image( 2, true); scroll_to_image(); }, "Fit image (Vertical)", "scale-image-ver"); add_action(function() { scale_image(-1, true); scroll_to_image(); }, "Reset image size", "reset-image"); if (parent_id === null) return; if (post_id !== null) { add_action(function() { flag_duplicate(post_id, ""); }, "Flag duplicate", "flag-duplicate"); add_action(function() { flag_duplicate(post_id, ", visually identical"); }, "Flag duplicate (identical)", "flag-duplicate-identical"); add_action(function() { flag_duplicate(post_id, " with worse quality"); }, "Flag duplicate (quality)", "flag-duplicate-quality"); add_action(function() { flag_duplicate(post_id, " with worse resolution"); }, "Flag duplicate (resolution)", "flag-duplicate-resolution"); } } function add_tag_buttons() { var edit_form = document.getElementById("edit-form"); if (edit_form === null) return; //not logged in var button_place = edit_form.children[1].children[0].children[0].children[0]; button_place.setAttribute("nowrap", "nowrap"); //hack to keep buttons from wrapping (not HTML5 conform, should use CSS) var el = document.createElement("BUTTON"); el.id = "clear_parent_id_button"; el.style.margin = "0 3px 0 6px"; el.innerHTML = "Clear"; el.onclick = function() { post_parent_id.clear(); return false;}; post_parent_id.parentNode.appendChild(el); el = document.createElement("BUTTON"); el.id = "reset_parent_id_button"; el.style.margin = "0 3px"; el.innerHTML = "Reset"; el.onclick = function() { reset_parent_id(); return false;}; post_parent_id.parentNode.appendChild(el); el = document.createElement("BUTTON"); el.id = "tag_reset_button"; el.style.margin = "0 3px 0 6px"; el.innerHTML = "Reset"; el.onclick = function() { reset_tags(); return false; }; button_place.appendChild(el); el = document.createElement("BUTTON"); el.id = "tag_dup_button"; el.style.margin = "0 3px"; button_place.appendChild(el); el = document.createElement("BUTTON"); el.id = "tag_var_button"; el.style.margin = "0 3px"; button_place.appendChild(el); el = document.createElement("BUTTON"); el.id = "tag_pot_button"; el.style.margin = "0 3px"; button_place.appendChild(el); } function update_tag_buttons() { var taglist = document.getElementById("post_tags"); var dup_button = document.getElementById("tag_dup_button"); var var_button = document.getElementById("tag_var_button"); var pot_button = document.getElementById("tag_pot_button"); if (taglist === null || dup_button === null || var_button === null || pot_button === null) return; var tags = get_tags_array(); if (tags.indexOf("duplicate") == -1) { dup_button.onclick = function() {add_tag("duplicate"); return false;}; dup_button.innerHTML = "Tag duplicate"; } else { dup_button.onclick = function() {remove_tag("duplicate"); return false;}; dup_button.innerHTML = "Untag duplicate"; } if (tags.indexOf("legitimate_variation") == -1) { var_button.onclick = function() {add_tag("legitimate_variation"); return false;}; var_button.innerHTML = "Tag legitimate_variation"; } else { var_button.onclick = function() {remove_tag("legitimate_variation"); return false;}; var_button.innerHTML = "Untag legitimate_variation"; } pot_button.innerHTML = "Untag potential_duplicate"; if (tags.indexOf("potential_duplicate") == -1) { pot_button.disabled = true; } else { pot_button.onclick = function() {remove_tag("potential_duplicate"); return false;}; pot_button.disabled = false; } } function reset_parent_id() { document.getElementById("post_parent_id").value = parent_id; } function get_old_tags_array() { return document.getElementById("post_old_tags").value.trim().split(/\s+/); } function get_tags_array() { return document.getElementById("post_tags").value.trim().split(/\s+/); } function add_tag(tag) { var tags = get_tags_array(); if ((tag == "duplicate" && tags.indexOf("legitimate_variation") != -1) || (tag == "legitimate_variation" && tags.indexOf("duplicate") != -1)) { notice("addon: cannot tag as duplicate and legitimate_variation at the same time."); return; } if (tags.indexOf(tag) != -1) { notice("addon: tag already present."); return; } document.getElementById("post_tags").value += " " + tag; updated_tags(); } function remove_tag(tag) { var tags = get_tags_array(); for (var i = 0; i < tags.length; i++) { if (tags[i] == tag) { tags[i] = ""; } } document.getElementById("post_tags").value = tags.join(" ").trim(); updated_tags(); } function tag_is_present(tag) { return get_tags_array().indexOf(tag) != -1; } function reset_tags() { document.getElementById("post_tags").value = document.getElementById("post_old_tags").value; updated_tags(); } //event that gets called whenever post_tags changes function updated_tags() { update_tag_buttons(); update_tag_menu(); } //flag option with default text function flag_duplicate(id, reason_suffix) { if (parent_id === null) { notice("addon: user not logged in"); return false; } var current_parent_id = document.getElementById("post_parent_id").value; if (current_parent_id != parent_id) { notice("addon: parent id was changed but not saved!"); return false; } if (!current_parent_id || current_parent_id.length === 0) { notice("addon: no parent id set!"); return false; } var tags = get_tags_array(); var old_tags = get_old_tags_array(); if (tags.indexOf("duplicate") == -1 && old_tags.indexOf("duplicate") != -1) { notice("addon: duplicate tag set but not saved!"); return false; } if (old_tags.indexOf("duplicate") == -1) { notice("addon: not tagged as duplicate!"); return false; } if (old_tags.indexOf("legitimate_variation") != -1) { notice("addon: tagged as legitimate_variation, are you sure it is a duplicate?"); return false; } var reason = prompt("Why should this post be reconsidered for moderation?", "duplicate of " + parent_id + reason_suffix); if (reason === null) { return false; } new Ajax.Request("/post/flag.json", { parameters: { "id": id, "reason": reason }, onComplete: function(response) { var resp = response.responseJSON; if (resp.success) { notice("Post was resent to moderation queue"); } else { notice("Error: " + resp.reason); } } }); } function read_image_data() { //TODO should probably be OOP var img = document.getElementById("image"); //image or video if (img) { img_elem = img; img_is_flash = false; } else { non_img_elem = document.getElementById("non-image-content"); img_elem = non_img_elem.getElementsByTagName("OBJECT")[0]; emb_elem = non_img_elem.getElementsByTagName("EMBED")[0]; img_is_flash = (img_elem !== null && emb_elem !== null); img = img_elem; //object contains width/height } //save original image size if (img_width === null) img_width = img.width; if (img_height === null) img_height = img.height; if (img_aspect_ratio === null) img_aspect_ratio = img.width / img.height; img_current_height = img_height; } //stretch image/video/flash, requires data from read_image_data() function scale_image(mode, always_scale) { mode = Number(mode); if (isNaN(mode)) { notice("addon error: scaling mode wasn't a number?"); return; } if (!always_scale && (!config.scale_flash && img_is_flash)) return; //reset image size if (mode === -1) { if (!img_is_flash) { img_elem.style.width = null; img_elem.style.height = null; } else { img_elem.width = img_width; img_elem.height = img_height; emb_elem.width = img_width; emb_elem.height = img_height; } img_current_height = img_height; return; } //target rect var img_rect_w = Math.max(window.innerWidth - img_elem.getBoundingClientRect().left - get_scrollbar_width() - 1, 1); var img_rect_h = Math.max(window.innerHeight - 1, 1); var img_rect_aspect_ratio = img_rect_w / img_rect_h; //fit into window if (mode === 0) { mode = (img_aspect_ratio > img_rect_aspect_ratio ? 1 : 2); } var new_width, new_height; //horizontal if (mode === 1) { new_width = Math.floor(img_rect_w); new_height = Math.floor(img_rect_w / img_aspect_ratio); //vertical } else if (mode === 2) { new_width = Math.floor(img_rect_h * img_aspect_ratio); new_height = Math.floor(img_rect_h); } if (!always_scale && (config.scale_only_downscale && (new_width > img_width || new_height > img_height))) return; var set_dimensions = function(obj, new_width, new_height) { obj.width = new_width + "px"; obj.height = new_height + "px"; }; if (img_is_flash) { set_dimensions(img_elem, new_width, new_height); set_dimensions(emb_elem, new_width, new_height); } else { set_dimensions(img_elem.style, new_width, new_height); } img_current_height = new_height; } function scroll_to_image() { var absolute_img_top = (img_is_flash ? non_img_elem : img_elem).getBoundingClientRect().top + window.pageYOffset; var top_of_centered_rect = absolute_img_top - (window.innerHeight - img_current_height) / 2; window.scrollTo(0, top_of_centered_rect); } /******************/ /* document-start */ /******************/ load_config(); /*************************************/ /* main page / visually similar page */ /*************************************/ if (location.pathname === "/" || location.pathname.startsWith("/post/similar")) { //add new modes right after dropdown and "apply-tag-script" mode was added to prevent it being reset to "view post" on page reloads register_observer(function(node) { return (node.value === "apply-tag-script"); }, function(node) { add_mode_options(node.parentNode); added_mode_options(); return true; }); //add speaker icons for dynamically loaded posts (from auto paging) if (config.post_show_speaker_icon) { //don't hog CPU when disabled, but requires page reload to activate register_observer(function (node) { return (node.classList !== null && node.classList.contains("content-page")); }, function (node) { var elems = node.getElementsByTagName("SPAN"); for (var i = 0; i < elems.length; i++) { if (elems[i].classList.contains("thumb")) { add_speaker_icon(elems[i]); } } return false; //listen forever }); } /*************/ /* post page */ /*************/ } else if (location.pathname.startsWith("/post/show/")) { var mute_video = function(node) { if (node.nodeType !== Node.ELEMENT_NODE) return; if (node.tagName !== "VIDEO") return; if (config.video_pause) node.pause(); if (config.video_mute) node.muted = true; if (config.video_controls) node.controls = true; }; register_observer(function(node) {return node.id === "image"; }, function(node) { mute_video(node); return true; }); } /******************/ /* content-loaded */ /******************/ document.addEventListener("DOMContentLoaded", function() { dom_content_loaded = true; //sitefix for flagged posts not always showing red border //problem: "flagged" style is defined before "has-parent" and "has-children" CSS styles, so these two take priority //fix: just add another copy of the "flagged" style at the end var sheet = document.createElement("style"); sheet.innerHTML = "img.has-children {padding:0px;border:2px solid #A7DF38;} img.has-parent {padding:0px;border:2px solid #CCCC00;} img.flagged {padding: 0px; border: 2px solid #F00;}"; sheet.innerHTML += " a.tag_nonexistent { color: #D00; }"; //custom style document.body.appendChild(sheet); header_offset_height = document.getElementById("headerlogo").offsetHeight; update_headerlogo(); add_config_dialog(); add_config_button(); update_config_dialog(); //listen for config changes in other windows window.addEventListener("storage", local_storage_changed); /*************************************/ /* main page / visually similar page */ /*************************************/ if (location.pathname === "/" || location.pathname.startsWith("/post/similar")) { if (config.post_show_speaker_icon) { var elems = document.getElementById("post-list").getElementsByTagName("SPAN"); for (var i = 0; i < elems.length; i++) { if (elems[i].classList.contains("thumb")) { add_speaker_icon(elems[i]); } } } if (config.tag_search_buttons) add_tag_search_buttons(); if (!PostModeMenu.old_change) PostModeMenu.old_change = PostModeMenu.change; if (!PostModeMenu.old_click) PostModeMenu.old_click = PostModeMenu.click; //add change events PostModeMenu.change = function() { var s = $F("mode"); PostModeMenu.old_change(); if (s == "apply-tag-script") { document.body.setStyle({ backgroundColor: "#FDF" //weaken color intensity }); } else if (s == "choose-parent") { document.body.setStyle({ backgroundColor: "#FFD" }); } else if (s == "set-parent") { if (Cookie.get("chosen-parent") === null) { notice("addon: Choose parent first!"); $("mode").value = "choose-parent"; PostModeMenu.change(); } else { document.body.setStyle({ backgroundColor: "#DFF" }); } } }; //add click events PostModeMenu.click = function(post_id) { if (PostModeMenu.old_click(post_id)) { return true; } var s = $("mode"); if (s.value == "choose-parent") { Cookie.put("chosen-parent", post_id); $("mode").value = "set-parent"; PostModeMenu.change(); } else if (s.value == "set-parent") { var parent_id = Cookie.get("chosen-parent"); TagScript.run(post_id, "parent:" + parent_id + (config.setparent_deletepotentialduplicate ? " -potential_duplicate" : "")); } return false; }; updated_PostModeMenu(); /*************/ /* post page */ /*************/ } else if (location.pathname.startsWith("/post/show/")) { var hidden_post_id = document.getElementById("hidden_post_id"); if (hidden_post_id !== null) { post_id = hidden_post_id.innerHTML; } var post_parent_id = document.getElementById("post_parent_id"); if (post_parent_id !== null) { parent_id = post_parent_id.value; } add_addon_actions(); add_tag_buttons(); add_tag_menu(); updated_tags(); if (config.tag_search_buttons) add_tag_search_buttons(); read_image_data(); if (config.scale_image) scale_image(config.scale_mode, false); if (config.scroll_to_image) scroll_to_image(); //TODO only add listener when setting is enabled window.addEventListener("resize", function() { clearTimeout(resize_timer); resize_timer = setTimeout(function() { if (config.scale_on_resize) scale_image(config.scale_mode, false); }, 100); }); var post_tags_area = document.getElementById("post_tags"); if (post_tags_area.addEventListener) post_tags_area.addEventListener("change", function() { clearTimeout(tag_update_timer); tag_update_timer = setTimeout(function() { updated_tags(); }, 500); }); /*************/ /* pool page */ /*************/ } else if (location.pathname.startsWith("/pool/index")) { //sitefix to show pool links even if missing english translation (they could not be clicked on otherwise) var pool_entries = document.getElementById("pool-index").getElementsByTagName("TABLE")[0].getElementsByTagName("TBODY")[0].getElementsByTagName("TR"); for (var i = 0; i < pool_entries.length; i++) { var pool_name = pool_entries[i].getElementsByTagName("TD")[0].getElementsByTagName("A")[0]; if (pool_name.innerHTML.trim().length == 0) pool_name.innerHTML = "<missing English translation>"; } } }, false);