diff --git a/better-better-booru.user.js b/better-better-booru.user.js index f472f97..57f6134 100644 --- a/better-better-booru.user.js +++ b/better-better-booru.user.js @@ -3,7 +3,7 @@ // @namespace https://greasyfork.org/scripts/3575-better-better-booru // @author otani, modified by Jawertae, A Pseudonymous Coder & Moebius Strip. // @description Several changes to make Danbooru much better. -// @version 8.1 +// @version 8.2 // @updateURL https://greasyfork.org/scripts/3575-better-better-booru/code/better_better_booru.meta.js // @downloadURL https://greasyfork.org/scripts/3575-better-better-booru/code/better_better_booru.user.js // @match *://*.donmai.us/* @@ -55,6 +55,24 @@ function bbbScript() { // Wrapper for injecting the script into the document. return hash >>> 0; }; + Number.prototype.bbbEncode62 = function() { + // Encode a number to base62. + var encodeChars = ["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"]; + var encoded = ""; + var num = this; + + if (num === 0) + encoded = encodeChars[0]; + else { + while (num > 0) { + encoded = encodeChars[num % 62] + encoded; + num = Math.floor(num / 62); + } + } + + return encoded; + }; + Element.prototype.bbbGetPadding = function() { // Get all the padding measurements of an element including the total width and height. if (window.getComputedStyle) { @@ -322,7 +340,7 @@ function bbbScript() { // Wrapper for injecting the script into the document. swapped: false // Whether the post content has been changed between the original and sample versions. }, options: { // Setting options and data. - bbb_version: "8.1", + bbb_version: "8.2", add_popular_link: newOption("checkbox", false, "Add Popular Link", "Add a link to the popular listing to the \"posts\" submenu"), add_random_post_link: newOption("checkbox", false, "Add Random Link", "Add a link to a random post to the post sidebar options menu."), alternate_image_swap: newOption("checkbox", false, "Alternate Image Swap", "Switch between the sample and original image by clicking the image. NoteNotes can be toggled by using the link in the sidebar options section."), @@ -1002,7 +1020,7 @@ function bbbScript() { // Wrapper for injecting the script into the document. // Create the new post. var childSpan = document.createElement("span"); - childSpan.innerHTML = '
' + postInfo.md5 + '
Date User ' + postInfo.uploader_name + ' Rating ' + postInfo.rating + ' Score ' + postInfo.score + '
Tags' + tagLinks + '
'; + childSpan.innerHTML = '
' + postInfo.md5 + '
Date Rating ' + postInfo.rating + ' Score ' + postInfo.score + '
Tags' + tagLinks + '
'; // Prepare thumbnails. prepThumbnails(childSpan); @@ -1423,7 +1441,8 @@ function bbbScript() { // Wrapper for injecting the script into the document. fav_count: Number(imgContainer.getAttribute("data-fav-count")) || 0, has_children: (imgContainer.getAttribute("data-has-children") === "true"), has_active_children: (postTag === "IMG" || postTag === "CANVAS" ? postEl.getAttribute("data-has-active-children") === "true" : !!target.getElementsByClassName("notice-parent")[0]), - fav_string: getMeta("favorites", docEl), + is_favorited: (imgContainer.getAttribute("data-is-favorited") === "true"), + normalized_source: imgContainer.getAttribute("data-normalized-source") || "", parent_id: (imgContainer.getAttribute("data-parent-id") ? Number(imgContainer.getAttribute("data-parent-id")) : null), rating: imgContainer.getAttribute("data-rating") || "", score: Number(imgContainer.getAttribute("data-score")) || 0, @@ -1435,6 +1454,7 @@ function bbbScript() { // Wrapper for injecting the script into the document. tag_string_general: scrapePostTags("general", target), pool_string: imgContainer.getAttribute("data-pools") || "", uploader_name: imgContainer.getAttribute("data-uploader") || "", + uploader_id: Number(imgContainer.getAttribute("data-uploader-id")) || 0, approver_id: imgContainer.getAttribute("data-approver-id") || null, is_deleted: (flags.indexOf("deleted") > -1), is_flagged: (flags.indexOf("flagged") > -1), @@ -1444,6 +1464,7 @@ function bbbScript() { // Wrapper for injecting the script into the document. image_width: Number(imgContainer.getAttribute("data-width")) || null }; + imgInfo.keeper_data = {uid: Number(imgContainer.getAttribute("data-top-tagger")) || imgInfo.uploader_id || 0}; imgInfo.has_large = (imgInfo.large_file_url !== imgInfo.file_url); // Grab any available Ugoira data. @@ -1507,7 +1528,8 @@ function bbbScript() { // Wrapper for injecting the script into the document. fav_count: Number(post.getAttribute("data-fav-count")) || 0, has_children: (post.getAttribute("data-has-children") === "true"), has_active_children: post.bbbHasClass("post-status-has-children"), // Assumption. Basically a flag for the children class. - fav_string: (post.getAttribute("data-is-favorited") === "true" ? "fav:" + getMeta("current-user-id") : ""), // Faked since thumbnails don't provide the full list of favorites. + is_favorited: (post.getAttribute("data-is-favorited") === "true"), + normalized_source: post.getAttribute("data-normalized-source") || "", parent_id: (post.getAttribute("data-parent-id") ? Number(post.getAttribute("data-parent-id")) : null), rating: post.getAttribute("data-rating") || "", score: Number(post.getAttribute("data-score")) || 0, @@ -1515,6 +1537,7 @@ function bbbScript() { // Wrapper for injecting the script into the document. tag_string: post.getAttribute("data-tags") || "", pool_string: post.getAttribute("data-pools") || "", uploader_name: post.getAttribute("data-uploader") || "", + uploader_id: Number(post.getAttribute("data-uploader-id")) || 0, approver_id: post.getAttribute("data-approver-id") || null, is_deleted: (flags.indexOf("deleted") > -1), is_flagged: (flags.indexOf("flagged") > -1), @@ -1524,6 +1547,8 @@ function bbbScript() { // Wrapper for injecting the script into the document. image_width: Number(post.getAttribute("data-width")) || null }; + imgInfo.keeper_data = {uid: Number(post.getAttribute("data-top-tagger")) || imgInfo.uploader_id || 0}; + if (imgInfo.file_url) imgInfo.has_large = (imgInfo.file_url !== imgInfo.large_file_url); else { @@ -2006,7 +2031,7 @@ function bbbScript() { // Wrapper for injecting the script into the document. helpPage.className = "bbb-page"; scrollDiv.appendChild(helpPage); - helpPage.bbbTextSection('Thumbnail Matching Rules', 'For creating thumbnail matching rules, please consult the following examples:
Wildcards can be used with any of the above methods:
Multiple match rules can be specified by using commas or separate lines when possible:
Tags can be nested/grouped together by using parentheses that only have spaces or commas next to them:
The following metatags are supported:
The id, score, favcount, width, and height metatags can also use number ranges for matching:'); + helpPage.bbbTextSection('Thumbnail Matching Rules', 'For creating thumbnail matching rules, please consult the following examples:
Wildcards can be used with any of the above methods:
Multiple match rules can be specified by using commas or separate lines when possible:
Tags can be nested/grouped together by using parentheses that only have spaces or commas next to them:
The following metatags are supported:
The id, score, favcount, width, and height metatags can also use number ranges for matching:'); helpPage.bbbTextSection('Hotkeys', 'Posts
Note: Numbers refer to the main typing keypad and not the numeric keypad.

General'); helpPage.bbbTextSection('Questions, Suggestions, or Bugs?', 'If you have any questions, please use the Greasy Fork feedback forums located here. If you\'d like to report a bug or make a suggestion, please create an issue on GitHub here.'); helpPage.bbbTocSection(); @@ -2672,17 +2697,12 @@ function bbbScript() { // Wrapper for injecting the script into the document. buttonDiv.appendChild(textBackup); var pageBackup = document.createElement("a"); - pageBackup.innerHTML = "Create Backup Page"; - pageBackup.href = "#"; + pageBackup.innerHTML = "Create Backup Text File"; + pageBackup.download = "Better Better Booru v" + bbb.user.bbb_version + " Backup (" + timestamp() + ").txt"; + pageBackup.target = "_blank"; + pageBackup.href = ('data:,Better Better Booru v' + bbb.user.bbb_version + ' Backup (' + timestamp() + '):%0D%0D' + JSON.stringify(bbb.user)).replace(/#/g, encodeURIComponent("#")); pageBackup.className = "bbb-button"; pageBackup.style.marginRight = "15px"; - pageBackup.addEventListener("click", function(event) { - if (event.button !== 0) - return; - - createBackupPage(); - event.preventDefault(); - }, false); buttonDiv.appendChild(pageBackup); var rightButtons = document.createElement("span"); @@ -2707,7 +2727,7 @@ function bbbScript() { // Wrapper for injecting the script into the document. helpButton.innerHTML = "Help"; helpButton.href = "#"; helpButton.className = "bbb-button"; - helpButton.bbbSetTip("Create copies of your settings that can be used for recovering lost/corrupted settings or transferring settings.DirectionsThere are two options for creating a backup. Creating a text backup will provide a plain text format backup in the area provided that can be copied and saved where desired. Creating a backup page will open a new page that can be saved with the browser's \"save page\" or bookmark options.

To restore a backup, copy and paste the desired backup into the provided area and click \"restore backup\"."); + helpButton.bbbSetTip("Create copies of your settings that can be used for recovering lost/corrupted settings or transferring settings.DirectionsThere are two options for creating a backup. Creating backup text will provide a plain text format backup in the area provided that can be copied and saved where desired. Creating a backup text file will attempt to download a plain text copy of your settings or open a new tab containing the plain text copy.

To restore a backup, copy and paste the desired backup into the provided area and click \"restore backup\"."); rightButtons.appendChild(helpButton); return sectionFrag; @@ -3367,6 +3387,7 @@ function bbbScript() { // Wrapper for injecting the script into the document. case "8.0": case "8.0.1": case "8.0.2": + case "8.1": break; } @@ -3424,11 +3445,6 @@ function bbbScript() { // Wrapper for injecting the script into the document. textarea.setSelectionRange(0,0); } - function createBackupPage() { - // Open a new tab/window and place the setting text in it. - window.open(('data:text/html,Better Better Booru v' + bbb.user.bbb_version + ' Backup (' + timestamp() + ')' + JSON.stringify(bbb.user) + '').replace(/#/g, encodeURIComponent("#"))); - } - function restoreBackupText() { // Load the backup text provided into the script. var textarea = bbb.el.menu.backupTextarea; @@ -3877,7 +3893,7 @@ function bbbScript() { // Wrapper for injecting the script into the document. var optionItem = optionItems[i]; if (downloadRegex.test(optionItem.textContent)) { - optionItem.innerHTML = 'Download'; + optionItem.innerHTML = 'Download'; break; } } @@ -4700,8 +4716,9 @@ function bbbScript() { // Wrapper for injecting the script into the document. var tagsStr = postInfo.tag_string || ""; var userStr = (postInfo.uploader_name ? " user:" + postInfo.uploader_name : ""); var ratingStr = (postInfo.rating ? " rating:" + postInfo.rating : ""); - var scoreStr = (postInfo.score ? " score:" + postInfo.score : ""); - var title = tagsStr + userStr + ratingStr + scoreStr; + var scoreStr = (typeof(postInfo.score) === "number" ? " score:" + postInfo.score : ""); + var titleStr = tagsStr + userStr + ratingStr + scoreStr; + var titleAttr = (getMeta("disable-post-tooltips") === "false" ? "oldtitle" : "title"); var secondary = []; var secondaryLength = 0; var styleList = bbb.custom_tag.style_list; @@ -4711,8 +4728,8 @@ function bbbScript() { // Wrapper for injecting the script into the document. if (link.bbbHasClass("bbb-thumb-link")) continue; - // Create title. - img.title = title; + // Create title information. + img.setAttribute(titleAttr, titleStr); // Add custom data attributes. post.bbbInfo("file-url-desc", postInfo.file_url_desc); @@ -4795,7 +4812,7 @@ function bbbScript() { // Wrapper for injecting the script into the document. function createThumbHTML(postInfo, query) { // Create a thumbnail HTML string. - return '
' + postInfo.tag_string + '
'; + return '
' + postInfo.tag_string + '
'; } function createThumb(postInfo, query) { @@ -4955,6 +4972,60 @@ function bbbScript() { // Wrapper for injecting the script into the document. return idCache.join(" "); } + function thumbInfo(target) { + // Add score, favorite count, and rating info to thumbnails. + var posts = getPosts(target); + + if (thumb_info === "disabled") + return; + + for (var i = 0, il = posts.length; i < il; i++) { + var post = posts[i]; + + // Skip thumbnails that already have the info added. + if (post.getElementsByClassName("bbb-thumb-info")[0]) + continue; + + var postInfo = post.bbbInfo(); + var tooShort = (150 / postInfo.image_width * postInfo.image_height < 30); // Short thumbnails will need the info div position adjusted. + + if (gLoc === "comments") { // Add favorites info to the existing info in the comments listing. + var firstInfo = post.getElementsByClassName("info")[0]; + var infoParent = (firstInfo ? firstInfo.parentNode : undefined); + + if (infoParent) { + var favSpan = document.createElement("span"); + favSpan.className = "info bbb-thumb-info"; + favSpan.innerHTML = 'Favorites ' + postInfo.fav_count; + infoParent.appendChild(favSpan); + } + } + else { // Add extra information inside of the thumbnail's parent element. + var thumbImg = post.getElementsByTagName("img")[0]; + + // Don't add the info if there isn't a thumbnail. + if (!thumbImg) + continue; + + var thumbEl = post.getElementsByClassName("preview")[0] || post; + thumbEl.bbbAddClass("bbb-thumb-info-parent"); + + var postLink = thumbEl.getElementsByTagName("a")[0]; + var before = (postLink ? postLink.nextElementSibling : undefined); + var scoreStr = (postInfo.score < 0 ? '' + postInfo.score + '' : postInfo.score); + + var infoDiv = document.createElement("div"); + infoDiv.className = "bbb-thumb-info" + (tooShort ? " bbb-thumb-info-short" : ""); + infoDiv.innerHTML = "★" + scoreStr + "   ♥" + postInfo.fav_count + (location.host.indexOf("safebooru") < 0 ? "    " + postInfo.rating.toUpperCase() : ""); + + if (before) + thumbEl.insertBefore(infoDiv, before); + else + thumbEl.appendChild(infoDiv); + } + } + } + function postDDL(target) { // Add direct downloads to thumbnails. if (!direct_downloads || (gLoc !== "search" && gLoc !== "pool" && gLoc !== "popular" && gLoc !== "popular_view" && gLoc !== "favorites" && gLoc !== "favorite_group")) @@ -6245,7 +6316,9 @@ function bbbScript() { // Wrapper for injecting the script into the document. // Missing API/data fixes. postInfo.has_sound = /(?:^|\s)(?:video|flash)_with_sound(?:$|\s)/.test(postInfo.tag_string); postInfo.flags = postFlags(postInfo); - postInfo.is_favorited = new RegExp("(?:^|\\s)fav:" + getMeta("current-user-id") + "(?:$|\\s)").test(postInfo.fav_string); + postInfo.normalized_source = postInfo.normalized_source || normalizedSource(postInfo); + postInfo.keeper_data = postInfo.keeper_data || {uid: postInfo.uploader_id}; + postInfo.uploader_name = (isModLevel() ? postInfo.uploader_name : ""); // Custom BBB properties. postInfo.file_url_desc = postFileUrlDesc(postInfo); @@ -6276,6 +6349,7 @@ function bbbScript() { // Wrapper for injecting the script into the document. delete postInfo.preview_img_src; delete postInfo.file_img_src; delete postInfo.large_file_img_src; + delete postInfo.normalized_source; return postInfo; } @@ -6398,6 +6472,103 @@ function bbbScript() { // Wrapper for injecting the script into the document. } } + function normalizedSource(postInfo) { + // Produce a normalized source from a post's source information. + var source = postInfo.source; + var urlReg, url, id, artist, title; // If/else variables. + + if (!!(urlReg = source.match(/^https?:\/\/img\d+\.pixiv\.net\/img\/[^\/]+\/(\d+)/i) || source.match(/^https?:\/\/i\d\.pixiv\.net\/img\d+\/img\/[^\/]+\/(\d+)/i))) + url = "http://www.pixiv.net/member_illust.php?mode=medium&illust_id=" + urlReg[1]; + else if (!!(urlReg = source.match(/^https?:\/\/(?:i\d+\.pixiv\.net|i\.pximg\.net)\/img-(?:master|original)\/img\/(?:\d+\/)+(\d+)_p/i) || source.match(/^https?:\/\/(?:i\d+\.pixiv\.net|i\.pximg\.net)\/c\/\d+x\d+\/img-master\/img\/(?:\d+\/)+(\d+)_p/i) || source.match(/^https?:\/\/(?:i\d+\.pixiv\.net|i\.pximg\.net)\/img-zip-ugoira\/img\/(?:\d+\/)+(\d+)_ugoira\d+x\d+\.zip/i))) + url = "http://www.pixiv.net/member_illust.php?mode=medium&illust_id=" + urlReg[1]; + else if (!!(urlReg = source.match(/^https?:\/\/lohas\.nicoseiga\.jp\/priv\/(\d+)\?e=\d+&h=[a-f0-9]+/i) || source.match(/^https?:\/\/lohas\.nicoseiga\.jp\/priv\/[a-f0-9]+\/\d+\/(\d+)/i))) + url = "http://seiga.nicovideo.jp/seiga/im" + urlReg[1]; + else if (!!(urlReg = source.match(/^https?:\/\/(?:d3j5vwomefv46c|dn3pm25xmtlyu)\.cloudfront\.net\/photos\/large\/(\d+)\./i))) { + id = parseInt(urlReg[1]).toString(36); + url = "http://twitpic.com/" + id; + } + else if (!!(urlReg = source.match(/^https?:\/\/(?:(?:fc|th|pre|orig|img|prnt)\d{2}|origin-orig)\.deviantart\.net\/.+\/([a-z0-9_]+)_by_([a-z0-9_]+)-d([a-z0-9]+)\./i))) { + title = urlReg[1].replace(/[^A-Za-z0-9]/g, " ").bbbSpaceClean().replace(/[ ]/g, "-"); + artist = urlReg[2].replace(/_/g, "-"); + id = parseInt(urlReg[3], 36); + url = "http://" + artist + ".deviantart.com/art/" + title + "-" + id; + } + else if (!!(urlReg = source.match(/^https?:\/\/(?:fc|th|pre|orig|img|prnt)\d{2}\.deviantart\.net\/.+\/[a-f0-9]{32}-d([a-z0-9]+)\./i))) { + id = parseInt(urlReg[1], 36); + url = "http://deviantart.com/deviation/" + id; + } + else if (!!(urlReg = source.match(/^http:\/\/www\.karabako\.net\/images(?:ub)?\/karabako_(\d+)(?:_\d+)?\./i))) + url = "http://www.karabako.net/post/view/" + urlReg[1]; + else if (!!(urlReg = source.match(/^http:\/\/p\.twpl\.jp\/show\/orig\/([a-z0-9]+)/i))) + url = "http://p.twipple.jp/" + urlReg[1]; + else if (!!(urlReg = source.match(/^https?:\/\/pictures\.hentai-foundry\.com\/\/?[^\/]\/([^\/]+)\/(\d+)/i))) + url = "http://www.hentai-foundry.com/pictures/user/" + urlReg[1] + "/" + urlReg[2]; + else if (!!(urlReg = source.match(/^http:\/\/blog(?:(?:-imgs-)?\d*(?:-origin)?)?\.fc2\.com\/(?:(?:[^\/]\/){3}|(?:[^\/]\/))([^\/]+)\/(?:file\/)?([^\.]+\.[^\?]+)/i))) + url = "http://" + urlReg[1] + ".blog.fc2.com/img/" + urlReg[2] + "/"; + else if (!!(urlReg = source.match(/^http:\/\/diary(\d?)\.fc2\.com\/user\/([^\/]+)\/img\/(\d+)_(\d+)\/(\d+)\./i))) + url = "http://diary" + urlReg[1] + ".fc2.com/cgi-sys/ed.cgi/" + urlReg[2] + "?Y=" + urlReg[3] + "&M=" + urlReg[4] + "&D=" + urlReg[5]; + else if (!!(urlReg = source.match(/^https?:\/\/(?:fbcdn-)?s(?:content|photos)-[^\/]+\.(?:fbcdn|akamaihd)\.net\/hphotos-.+\/\d+_(\d+)_(?:\d+_){1,3}[no]\./i))) + url = "https://www.facebook.com/photo.php?fbid=" + urlReg[1]; + else if (!!(urlReg = source.match(/^https?:\/\/c(?:s|han|[1-4])\.sankakucomplex\.com\/data(?:\/sample)?\/(?:[a-f0-9]{2}\/){2}(?:sample-|preview)?([a-f0-9]{32})/i))) + url = "http://chan.sankakucomplex.com/en/post/show?md5=" + urlReg[1]; + else if (!!(urlReg = source.match(/^http:\/\/s(?:tatic|[1-4])\.zerochan\.net\/.+(?:\.|\/)(\d+)\.(?:jpe?g?)$/i))) + url = "http://www.zerochan.net/" + urlReg[1] + "#full"; + else if (!!(urlReg = source.match(/^http:\/\/static[1-6]?\.minitokyo\.net\/(?:downloads|view)\/(?:\d{2}\/){2}(\d+)/i))) + url = "http://gallery.minitokyo.net/download/" + urlReg[1]; + else if (postInfo.md5 && !!(urlReg = source.match(/^https?:\/\/(?:(?:s?img|cdn|www)\d?\.)?gelbooru\.com\/{1,2}(?:images|samples)\/(?:\d+|[a-f0-9]{2}\/[a-f0-9]{2})\/(?:sample_)?(?:[a-f0-9]{32}|[a-f0-9]{40})\./i))) + url = "http://gelbooru.com/index.php?page=post&s=list&md5=" + postInfo.md5; + else if (!!(urlReg = source.match(/^https?:\/\/(?:slot\d*\.)?im(?:g|ages)\d*\.wikia\.(?:nocookie\.net|com)\/(?:_{2}cb\d{14}\/)?([^\/]+)(?:\/[a-z]{2})?\/images\/(?:(?:thumb|archive)?\/)?[a-f0-9]\/[a-f0-9]{2}\/(?:\d{14}(?:!|%21))?([^\/]+)/i))) + url = "http://" + urlReg[1] + ".wikia.com/wiki/File:" + urlReg[2]; + else if (!!(urlReg = source.match(/^https?:\/\/vignette(?:\d*)\.wikia\.nocookie\.net\/([^\/]+)\/images\/[a-f0-9]\/[a-f0-9]{2}\/([^\/]+)/i))) + url = "http://" + urlReg[1] + ".wikia.com/wiki/File:" + urlReg[2]; + else if (!!(urlReg = source.match(/^http:\/\/(?:(?:\d{1,3}\.){3}\d{1,3}):(?:\d{1,5})\/h\/([a-f0-9]{40})-(?:\d+-){3}(?:png|gif|(?:jpe?g?))\/keystamp=\d+-[a-f0-9]{10}\/([^\/]+)/i))) + url = "http://g.e-hentai.org/?f_shash=" + urlReg[1] + "&fs_from=" + urlReg[2]; + else if (!!(urlReg = source.match(/^http:\/\/e-shuushuu.net\/images\/\d{4}-(?:\d{2}-){2}(\d+)/i))) + url = "http://e-shuushuu.net/image/" + urlReg[1]; + else if (!!(urlReg = source.match(/^http:\/\/jpg\.nijigen-daiaru\.com\/(\d+)/i))) + url = "http://nijigen-daiaru.com/book.php?idb=" + urlReg[1]; + else if (!!(urlReg = source.match(/^https?:\/\/sozai\.doujinantena\.com\/contents_jpg\/([a-f0-9]{32})\//i))) + url = "http://doujinantena.com/page.php?id=" + urlReg[1]; + else if (!!(urlReg = source.match(/^http:\/\/rule34-(?:data-\d{3}|images)\.paheal\.net\/(?:_images\/)?([a-f0-9]{32})/i))) + url = "http://rule34.paheal.net/post/list/md5:" + urlReg[1] + "/1"; + else if (!!(urlReg = source.match(/^http:\/\/shimmie\.katawa-shoujo\.com\/image\/(\d+)/i))) + url = "http://shimmie.katawa-shoujo.com/post/view/" + urlReg[1]; + else if (postInfo.md5 && !!(urlReg = source.match(/^http:\/\/(?:(?:(?:img\d?|cdn)\.)?rule34\.xxx|img\.booru\.org\/(?:rule34|r34))(?:\/(?:img\/rule34|r34))?\/{1,2}images\/\d+\/(?:[a-f0-9]{32}|[a-f0-9]{40})\./i))) + url = "http://rule34.xxx/index.php?page=post&s=list&md5=" + postInfo.md5; + else if (!!(urlReg = source.match(/^https?:\/\/(?:s3\.amazonaws\.com\/imgly_production|img\.ly\/system\/uploads)\/((?:\d{3}\/){3}|\d+\/)/i))) { + id = parseInt((urlReg[1].replace(/[^0-9]/g, '')) || 0).bbbEncode62(); + url = "http://img.ly/" + id; + } + else if (!!(urlReg = source.match(/^(http:\/\/.+)\/diarypro\/d(?:ata\/upfile\/|iary\.cgi\?mode=image&upfile=)(\d+)/i))) + url = urlReg[1] + "/diarypro/diary.cgi?no=" + urlReg[2]; + else if (!!(urlReg = source.match(/^http:\/\/i(?:\d)?\.minus\.com\/(?:i|j)([^\.]{12,})/i))) + url = "http://minus.com/i/" + urlReg[1]; + else if (!!(urlReg = source.match(/^https?:\/\/pic0[1-4]\.nijie\.info\/nijie_picture\/(?:diff\/main\/)?\d+_(\d+)_(?:\d{10}|\d+_\d{14})/i))) + url = "http://nijie.info/view.php?id=" + urlReg[1]; + else if (!!(urlReg = source.match(/^https?:\/\/(?:ayase\.|yuno\.|files\.)?yande\.re\/(?:sample|image)\/[a-z0-9]{32}\/yande\.re%20([0-9]+)%20/i))) + url = "https://yande.re/post/show/" + urlReg[1]; + else if (!!(urlReg = source.match(/^https?:\/\/(?:ayase\.|yuno\.|files\.)?yande\.re\/(?:image|jpeg|sample)\/([a-z0-9]{32})(?:\/yande\.re.*|\/?\.(?:jpg|png))$/i))) + url = "https://yande.re/post?tags=md5:" + urlReg[1]; + else if (!!(urlReg = source.match(/^https?:\/\/\w+\.artstation.com\/(?:artwork|projects)\/([a-z0-9-]+)$/i))) + url = "https://www.artstation.com/artwork/" + urlReg[1]; + else if (!!(urlReg = source.match(/^https?:\/\/(?:o|image-proxy-origin)\.twimg\.com\/\d\/proxy\.jpg\?t=(\w+)&/i))) { + url = window.atob(urlReg[1]).match(/https?:\/\/[\x20-\x7e]+/i); + + if (url[0]) { + url = url[0]; + + if (url.match(/^https?:\/\/twitpic.com\/show\/large\/[a-z0-9]+/i)) + url = url.substring(0, url.lastIndexOf('.')).replace("show/large/", ""); + } + else + url = source; + } + else + url = source; + + return url; + } + function fixPaginator(target) { // Determine whether the paginator needs to be updated and request one as needed. var paginator = getPaginator(target); @@ -6442,7 +6613,7 @@ function bbbScript() { // Wrapper for injecting the script into the document. var thumb = document.createElement("a"); thumb.href = "/posts/" + postInfo.id; - thumb.innerHTML = '' + postInfo.md5 + ''; + thumb.innerHTML = '' + postInfo.md5 + ''; if (before) preview.insertBefore(thumb, before); @@ -6797,7 +6968,11 @@ function bbbScript() { // Wrapper for injecting the script into the document. var flags = postInfo.flags || "active"; var rating = " rating:" + postInfo.rating; var status = " status:" + (flags === "flagged" ? flags + " active" : flags).replace(/\s/g, " status:"); - var user = " user:" + postInfo.uploader_name.replace(/\s/g, "_").toLowerCase(); + var user = (postInfo.uploader_name ? " user:" + postInfo.uploader_name.replace(/\s/g, "_").toLowerCase() : ""); + var userId = " userid:" + postInfo.uploader_id; + var taggerId = " taggerid:" + postInfo.keeper_data.uid; + var approverId = (postInfo.approver_id ? " approverid:" + postInfo.approver_id : ""); + var source = (postInfo.source ? " source:" + postInfo.source + " source:" + postInfo.normalized_source : ""); var pools = " " + (/pool:\d+/.test(postInfo.pool_string) && !/pool:(collection|series)/.test(postInfo.pool_string) ? postInfo.pool_string + " pool:inactive" : postInfo.pool_string); var parent = (postInfo.parent_id ? " parent:" + postInfo.parent_id : ""); var child = (postInfo.has_children === true ? " child:true" : ""); @@ -6805,7 +6980,7 @@ function bbbScript() { // Wrapper for injecting the script into the document. postSearchInfo = { tags: postInfo.tag_string.bbbSpacePad(), - metatags:(rating + status + user + pools + parent + child + isFav).bbbSpacePad(), + metatags:(rating + status + user + pools + parent + child + isFav + userId + taggerId + source + approverId).bbbSpacePad(), score: postInfo.score, favcount: postInfo.fav_count, id: postInfo.id, @@ -7071,9 +7246,9 @@ function bbbScript() { // Wrapper for injecting the script into the document. if (!tagValue) continue; - if (tagValue === "any" && (tagName === "pool" || tagName === "parent" || tagName === "child")) + if (tagValue === "any" && (tagName === "pool" || tagName === "parent" || tagName === "child" || tagName === "source" || tagName === "approverid")) mode.push(new RegExp((tagName + ":\\S*").bbbSpacePad())); - else if (tagValue === "none" && (tagName === "pool" || tagName === "parent" || tagName === "child")) { + else if (tagValue === "none" && (tagName === "pool" || tagName === "parent" || tagName === "child" || tagName === "source" || tagName === "approverid")) { secondaryMode = (secondaryMode === "includes" ? "excludes" : "includes"); // Flip the include/exclude mode. mode = searchObject[primaryMode][secondaryMode]; @@ -7081,6 +7256,8 @@ function bbbScript() { // Wrapper for injecting the script into the document. } else if (tagValue === "active" && tagName === "pool") mode.push(new RegExp((tagName + ":(collection|series)").bbbSpacePad())); + else if (tagName === "source") // Append a wildcard to the end of "sources starting with the given value" searches. + mode.push(new RegExp((escapeRegEx(searchTerm) + "\\S*").bbbSpacePad())); else // Allow all other values through (ex: parent:# & pool:series). mode.push(searchTerm.bbbSpacePad()); } @@ -8259,60 +8436,6 @@ function bbbScript() { // Wrapper for injecting the script into the document. } } - function thumbInfo(target) { - // Add score, favorite count, and rating info to thumbnails. - var posts = getPosts(target); - - if (thumb_info === "disabled") - return; - - for (var i = 0, il = posts.length; i < il; i++) { - var post = posts[i]; - - // Skip thumbnails that already have the info added. - if (post.getElementsByClassName("bbb-thumb-info")[0]) - continue; - - var postInfo = post.bbbInfo(); - var tooShort = (150 / postInfo.image_width * postInfo.image_height < 30); // Short thumbnails will need the info div position adjusted. - - if (gLoc === "comments") { // Add favorites info to the existing info in the comments listing. - var firstInfo = post.getElementsByClassName("info")[0]; - var infoParent = (firstInfo ? firstInfo.parentNode : undefined); - - if (infoParent) { - var favSpan = document.createElement("span"); - favSpan.className = "info bbb-thumb-info"; - favSpan.innerHTML = 'Favorites ' + postInfo.fav_count; - infoParent.appendChild(favSpan); - } - } - else { // Add extra information inside of the thumbnail's parent element. - var thumbImg = post.getElementsByTagName("img")[0]; - - // Don't add the info if there isn't a thumbnail. - if (!thumbImg) - continue; - - var thumbEl = post.getElementsByClassName("preview")[0] || post; - thumbEl.bbbAddClass("bbb-thumb-info-parent"); - - var postLink = thumbEl.getElementsByTagName("a")[0]; - var before = (postLink ? postLink.nextElementSibling : undefined); - var scoreStr = (postInfo.score < 0 ? '' + postInfo.score + '' : postInfo.score); - - var infoDiv = document.createElement("div"); - infoDiv.className = "bbb-thumb-info" + (tooShort ? " bbb-thumb-info-short" : ""); - infoDiv.innerHTML = "★" + scoreStr + "   ♥" + postInfo.fav_count + (location.host.indexOf("safebooru") < 0 ? "    " + postInfo.rating.toUpperCase() : ""); - - if (before) - thumbEl.insertBefore(infoDiv, before); - else - thumbEl.appendChild(infoDiv); - } - } - } - function postLinkNewWindow() { // Make thumbnail clicks open in a new tab/window. if (post_link_new_window === "disabled" || (gLoc !== "search" && gLoc !== "pool" && gLoc !== "favorites" && gLoc !== "popular" && gLoc !== "popular_view" && gLoc !== "favorite_group")) @@ -8945,10 +9068,8 @@ function bbbScript() { // Wrapper for injecting the script into the document. // Test a URL to find which section of Danbooru the script is running on. var target; // If/else variable. - if (url) { - target = document.createElement("a"); - target.href = url; - } + if (url) + target = new URL(url); else target = location; @@ -9063,6 +9184,11 @@ function bbbScript() { // Wrapper for injecting the script into the document. return (getUserData("level") >= 30); } + function isModLevel() { + // Determine whether the user is at the moderator account level or higher. + return (getUserData("level") >= 40); + } + function accountSettingCheck(scriptSetting) { // Determine whether the script setting or account/anonymous setting should be used. var loggedIn = isLoggedIn(); @@ -9906,7 +10032,7 @@ function bbbScript() { // Wrapper for injecting the script into the document. else { var tagName = tag.split(":", 1)[0].bbbSpaceClean(); - if (tagName === "pool" || tagName === "user" || tagName === "status" || tagName === "rating" || tagName === "parent" || tagName === "child" || tagName === "isfav") + if (tagName === "pool" || tagName === "user" || tagName === "status" || tagName === "rating" || tagName === "parent" || tagName === "child" || tagName === "isfav" || tagName === "userid" || tagName === "taggerid" || tagName === "source" || tagName === "approverid") return true; else return false; @@ -10058,6 +10184,8 @@ function bbbScript() { // Wrapper for injecting the script into the document. Danbooru.Autocomplete.static_metatags.parent = ["any", "none"]; Danbooru.Autocomplete.static_metatags.isfav = ["true", "false"]; Danbooru.Autocomplete.static_metatags.pool = ["series", "collection", "any", "none", "active", "inactive"]; + Danbooru.Autocomplete.static_metatags.source = ["any", "none"]; + Danbooru.Autocomplete.static_metatags.approverid = ["any", "none"]; // Counter normal autocomplete getting turned back on after submitting an input. document.body.addEventListener("focus", function(event) { @@ -10079,7 +10207,7 @@ function bbbScript() { // Wrapper for injecting the script into the document. var $fields_multiple = $(searchInputs); var prefixes = "-~"; - var metatags = "status|rating|parent|child|user|pool|group|g|isfav"; + var metatags = "status|rating|parent|child|user|pool|group|g|isfav|userid|taggerid|approverid|source|id|score|favcount|height|width"; $fields_multiple.autocomplete({ delay: 100, @@ -10127,6 +10255,15 @@ function bbbScript() { // Wrapper for injecting the script into the document. } switch(metatag) { + case "userid": + case "taggerid": + case "id": + case "score": + case "favcount": + case "height": + case "width": + this.close(); + return; case "status": case "rating": case "parent": @@ -10135,6 +10272,8 @@ function bbbScript() { // Wrapper for injecting the script into the document. case "g": case "isfav": case "pool": + case "source": + case "approverid": Danbooru.Autocomplete.static_metatag_source(term, resp, metatag); return; } @@ -10258,4 +10397,4 @@ function bbbInit() { window.setTimeout(function() { document.body.removeChild(script); }, 0); } -bbbInit(); +bbbInit(); \ No newline at end of file diff --git a/changelog.md b/changelog.md index 865d3f5..a2cd60a 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,13 @@ Changelog ---------- +* Version 8.2: + * Added "userid", "taggerid", "approverid", and "source" to the script metatags. + * Updated the "user" metatag help text to clarify it can only be used by users at or above the moderator level. + * Updated thumbnail image titles to support Danbooru's new tooltips. + * Updated download links to support server changes on Danbooru. + * Fixed autocomplete behavior for some metatags. + * Fixed the backup page option to make it compatible with browser security changes for data URLs. + * Fixed the date information for hidden posts in the comments section. * Version 8.1: * Added a "hide favorite button" layout option for hiding the button underneath post content. * Added an "add random link" layout option for placing a link to a random post in the sidebar options menu of posts.