您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Format the license information from a thing page
当前为
// ==UserScript== // @name Thing Remix Attribution Maker // @namespace http://poikilos.org/ // @version 2.0 // @description Format the license information from a thing page // @author Poikilos (Jake Gustafson) // @include https://www.thingiverse.com/thing:* // @grant none // @run-at document-end // ==/UserScript== (function() { // formerly @match https://www.thingiverse.com/thing:* var checkTimer = null; var madeDivClassName = "ThingPage__madeBy"; var licenseClauseImgPrefix = "License__img"; var licenseAnchorPrefix = "License__link"; // INFO: it could be author OR license link var titlePrefix = "ThingPage__modelName"; var headingCreatedPrefix = "ThingPage__createdBy"; var doneDivPrefixes = [titlePrefix, headingCreatedPrefix]; var clausesContainerPrefix = "License__ccLicense"; var doneDivPrefixesMain = [clausesContainerPrefix]; function getElementsWhereClassStartsWith(str) { // console.log("getElementsWhereClassStartsWith..."); var els = []; var all = document.getElementsByTagName("*"); for (var i=0, max=all.length; i < max; i++) { var el = all[i]; if (el.className.startsWith(str)) { els.push(el); } } return els; } function getDivsWhereClassStartsWith(str) { // console.log("getDivsWhereClassStartsWith..."); var els = []; var all = document.getElementsByTagName("div"); for (var i=0, max=all.length; i < max; i++) { var el = all[i]; if (el.className.startsWith(str)) { els.push(el); } else { // console.log(el.className + " does not start with it."); } } // console.log("Div count: " + all.length); return els; } function getWhereClassStartsWithIn(el, str) { if (el === undefined) { console.log("[getWhereClassStartsWithIn] Error: el is undefined."); return []; } // console.log("getWhereClassStartsWithIn..."); // console.log(" el: " + JSON.stringify(el)); // console.log(" el.className: "+el.className); // console.log(" el.childNodes.length:"+el.childNodes.length+"..."); var els = []; var all = el.childNodes; for (var i=0, max=all.length; i < max; i++) { var el = all[i]; if (el.className.startsWith(str)) { els.push(el); } else { // console.log(el.className + " does not start with it."); } } // console.log(" done (div count: " + all.length + ")"); return els; } function hasAllDivPrefixes(prefixes) { var found = 0; for (var i=0, max=prefixes.length; i < max; i++) { if (getDivsWhereClassStartsWith(prefixes[i]).length > 0) { found++; } } return found >= prefixes.length; } function elementHasAllPrefixes(el, prefixes) { var found = 0; for (var i=0, max=prefixes.length; i < max; i++) { if (getWhereClassStartsWithIn(el, prefixes[i]).length > 0) { found++; } } return found >= prefixes.length; } function getImgsWhereClassStartsWith(str) { var els = []; var all = document.images; // document.getElementsByTagName("img"); for (var i=0, max=all.length; i < max; i++) { var el = all[i]; if (el.className.startsWith(str)) { els.push(el); } } return els; } function getAnchorsWhereClassStartsWith(str) { var els = []; var all = document.getElementsByTagName("a"); for (var i=0, max=all.length; i < max; i++) { var el = all[i]; if (el.className.startsWith(str)) { els.push(el); } } return els; } function addButton() { 'use strict'; // This should run when ThingPage_galleryHeader* gets filled in, but only once to prevent an infinite loop. var info = {}; // var pageInfoEs = document.getElementsByClassName("item-page-info"); // NOTE: now ThingiVerse is a React app, so you must use inspect to see the HTML. // "ThingPage__madeBy*" includes parts such as: // - `ThingPage__modelName*` // - `<div class="ThingPage__createdBy*">by <a ...>UserName</a>MON D, YYYY` var pageInfoEs = getDivsWhereClassStartsWith(madeDivClassName); if (pageInfoEs.length >= 1) { var pageInfoE = pageInfoEs[0]; // There should only be one. // pageInfoE.innerHTML += "<button onclick=\"getRemixLicense()\">Copy License for Remix</button>"; //or: // See https://www.w3schools.com/jsref/met_document_createelement.asp var btn = document.createElement("BUTTON"); // Create a <button> element btn.setAttribute("class", "grey-button"); var btnText = "Copy License for Remix"; btn.innerHTML = btnText; // Insert text btn.addEventListener("click", function(){ btn.innerHTML = btnText; // var licenseTextE = document.getElementsByClassName("license-text"); // var licenseTextE = getDivsWhereClassStartsWith(clausesContainerPrefix); var outputStr = ""; // var pageInfoEs = document.getElementsByClassName("item-page-info"); // var pageInfoEs = getDivsWhereClassStartsWith(madeDivClassName); // console.log("Checking "+madeDivClassName+"* elements: " + JSON.stringify(pageInfoEs)); var headingParts = getDivsWhereClassStartsWith(titlePrefix); var headingCreatedParts = getDivsWhereClassStartsWith(headingCreatedPrefix); if (headingParts.length > 0) { info.title = headingParts[0].textContent; } else { console.warn("The title is missing. There are no divs with a class starting with " + titlePrefix); } var createdStr = null; if (headingCreatedParts.length > 0) { createdStr = headingCreatedParts[0].textContent; } else { console.warn("The date is missing. There are no divs with a class starting with " + headingCreatedParts); } info.titleHref = window.location.href; // console.log("info.title: " + info.title); // console.log("info.titleHref: " + info.titleHref); console.log("createdStr: " + createdStr); if (createdStr !== null) { var createdParts = createdStr.split(" "); if (createdParts.length >= 3) { var yI = createdParts.length - 1; var dI = createdParts.length - 2; var mI = createdParts.length - 3; var yStr = createdParts[yI]; var dStr = createdParts[dI]; var mStr = createdParts[mI]; if (dStr.endsWith(",")) { info.month = mStr; info.day = dStr.slice(0, -1); info.year = yStr; } else { console.warn("A date such as MON, D, YYYY was expected at the end of: \""+createdStr+"\""); } } } var aspects = []; aspects = getImgsWhereClassStartsWith(licenseClauseImgPrefix); var ai; if (aspects.length > 0) { info.license = ""; } else { console.error("The license had zero clauses (img tags with "+licenseClauseImgPrefix+"* class)!") } var sep = " - "; for (ai = 0; ai < aspects.length; ai++) { var aspectImg = aspects[ai]; if (aspectImg.src.endsWith("cc.svg")) { info.license += "Creative Commons"; } else if (aspectImg.src.endsWith("nc.svg")) { info.license += sep + "Non-Commercial"; } else if (aspectImg.src.endsWith("nd.svg")) { info.license += sep + "No Derivatives"; } else if (aspectImg.src.endsWith("by.svg")) { info.license += sep + "Attribution"; } else if (aspectImg.src.endsWith("sa.svg")) { info.license += sep + "ShareAlike"; // It has a space on ThingiVerse, but that is not correct. } else { console.error("The license symbol list has an unknown clause symbol: " + aspectImg.src); } } if (info.license != undefined) { console.log("The symbols indicate the following license: " + info.license); } // Format the info and copy it to the clipboard: outputStr = "## License"; var licenseShortStr = ""; var licenseAnchors = getAnchorsWhereClassStartsWith(licenseAnchorPrefix); var exactLicenseVersion = null; if (licenseAnchors.length > 0) { console.log("Checking " + licenseAnchors.length + " license anchors..."); for (var lai=0, max=licenseAnchors.length; lai < max; lai++) { var licenseA = licenseAnchors[lai]; // console.log(" checking " + licenseA.className + "..."); // NOTE: .getAttribute("href") gets the raw value, but .href gets the resulting full URL. // console.log(" licenseA.href: " + typeof licenseA.href); // console.log(" licenseA.href.toString: " + typeof licenseA.href.toString); // console.log(" licenseA.href.toString().includes: " + typeof licenseA.href.toString().includes); if (licenseA.href === undefined) { console.warn("A license a.href is undefined."); } // else if (typeof licenseA.href.toString !== 'function') { // console.warn("A license a.href.toString is not a function."); // } else if (typeof licenseA.href.includes !== 'function') { // NOTE: Firefox 48 removes the "contains" prototype--you must use includes! // console.warn("A license a.getAttribute(\"href\").includes is not a function."); console.warn("A license a.href.toString.includes is not a function."); } else if (!licenseA.href.includes("thingiverse.com")) { // console.log("licenseA.href: "); // console.log("'",licenseA.href, "'"); info.licenseHref = licenseA.href; if (info.licenseHref.slice(-3, -2) == ".") { exactLicenseVersion = licenseA.href.slice(-4, -1); } else { console.warn("slice at -3 is not .: " + info.licenseHref.slice(-3, -2)); } } else { info.author = licenseA.textContent; info.authorHref = licenseA.href; // console.log("unused[]: " + licenseA.href); // console.log("author: " + info.author); // console.log("authorHref: " + info.authorHref); } } } else { console.warn("There is no anchor with a class like "+licenseAnchorPrefix+"*"); } if (info.license) { var versionIsFound = false; var licenseLower = info.license.toLowerCase(); outputStr += "\n- "; if (info.license.startsWith("Creative Commons") || info.license.startsWith("CC")) { if (info.license.startsWith("CC0 1.0") || info.license.startsWith("Creative Commons 0 1.0") || info.license.startsWith("Creative Commons Zero 1.0")) { if (!info.licenseHref) { info.licenseHref = "https://creativecommons.org/publicdomain/zero/1.0/"; } licenseShortStr = "CCO 1.0"; } else if ((info.license == "Creative Commons 0") || (info.license == "Creative Commons Zero")) { licenseShortStr = "CCO"; } else { console.log("Looking for license clauses in license name \""+licenseLower+"\"..."); licenseShortStr = "CC "; if (licenseLower.includes("attribution")) { licenseShortStr += "BY"; } if (licenseLower.includes("non-commercial") || licenseLower.includes("noncommercial") || licenseLower.includes("non commercial")) { licenseShortStr += "-NC"; } if (licenseLower.includes("no derivatives") || licenseLower.includes("noderivs") || licenseLower.includes("no-derivatives") || licenseLower.includes("noderivatives")) { licenseShortStr += "-ND"; } if (licenseLower.includes("sharealike") || licenseLower.includes("share-alike") || licenseLower.includes("share alike") ) { licenseShortStr += "-SA"; } if (info.license.includes("1.0")) { licenseShortStr += " 1.0"; versionIsFound = true; } else if (info.license.includes("2.0")) { licenseShortStr += " 2.0"; versionIsFound = true; } else if (info.license.includes("3.0")) { licenseShortStr += " 3.0"; versionIsFound = true; } else if (info.license.includes("4.0")) { licenseShortStr += " 4.0"; versionIsFound = true; } else if (exactLicenseVersion !== null) { licenseShortStr += " " + exactLicenseVersion; versionIsFound = true; } } } console.log("licenseShortStr: " + licenseShortStr); if (!info.licenseHref) { var parts = licenseShortStr.split(" "); if (parts.length == 3) { var partialHref = null; // such as ["CC", "BY-SA", "3.0"] if (parts[1] == "BY") { partialHref = "http://creativecommons.org/licenses/by/"; } else if (parts[1] == "BY-SA") { partialHref = "http://creativecommons.org/licenses/by-sa/"; } else if (parts[1] == "BY-NC-SA") { partialHref = "http://creativecommons.org/licenses/by-nc-sa/"; } else if (parts[1] == "BY-NC-ND") { partialHref = "http://creativecommons.org/licenses/by-nc-nd/"; } // NOTE: by-nc-nd-sa is NOT a valid license if (partialHref != null) { info.licenseHref = partialHref + parts[2] + "/"; } } } if (info.licenseHref) outputStr += "[" + info.license + "](" + info.licenseHref + ")"; else outputStr += info.license; if (licenseShortStr.length > 0) { outputStr += "\n (" + licenseShortStr + ")"; } } else { console.warn("The license abbreviation cannot be generated for an unknown license: " + info.license); } if (info.author) { outputStr += "\n- by " + info.author + " and <insert remixer's name here>"; } if (info.title) { outputStr += "\n- based on"; if (info.titleHref) outputStr += " [" + info.title + "](" + info.titleHref + ")"; else outputStr += " " + info.title; if (info.author) { if (info.authorHref) outputStr += " by [" + info.author + "](" + info.authorHref + ")"; else outputStr += " by " + info.author; } if (info.year) { outputStr += " "; if (info.month) { outputStr += info.month + " "; if (info.day) { outputStr += info.day + ", " } } outputStr += info.year; } } var msg = "(ERROR: Your browser API is unknown.)"; var okMsg = " ✓"; // See https://stackoverflow.com/questions/52177405/clipboard-writetext-doesnt-work-on-mozilla-ie if (navigator.clipboard != undefined) { // Chrome navigator.clipboard.writeText(outputStr).then( function () { console.log('Async: Copying to clipboard was successful!'); btn.innerHTML += okMsg; }, function (err) { console.error('Async: Could not copy text: ', err); btn.innerHTML += '<br/> (ERROR: Accessing the clipboard failed.)'; } ); msg = null; } else if (window.clipboardData) { // Internet Explorer window.clipboardData.setData("Text", outputStr); msg = okMsg; } if (msg != null) { btn.innerHTML += msg; } i = null }); pageInfoE.appendChild(btn); // Append <button> to <body> } else { console.log('The '+madeDivClassName+' class was not found!'); } i = null; }//end addButton function checkIfComplete() { // console.log("Monitoring page loading..."); var ready = true; var containers = getDivsWhereClassStartsWith(madeDivClassName); // console.log("Checking for completed page content..."); if (containers.length == 1) { if (!elementHasAllPrefixes(containers[0], doneDivPrefixes)) { ready = false; // console.log("The "+containers[0].className+" container is not complete:"); // console.log("The document is not ready yet ("+containers[0].className+" does not contain the classes with the prefixes \""+JSON.stringify(doneDivPrefixes)+"\")."); } } else { ready = false; // console.log("The page is not formatted as expected:"); // console.log(containers.length + " is an unexpected count for divs with a class named like " + madeDivClassName + "*."); } if (!hasAllDivPrefixes(doneDivPrefixesMain)) { ready = false; // console.log("The document is not complete:"); // console.log("The document is not ready yet (the document does not contain the class(es) with the prefix(es) \""+JSON.stringify(doneDivPrefixesMain)+"\")."); } if (ready) { // console.log("The page has loaded."); clearInterval(checkTimer); addButton(); } } checkTimer = setInterval(checkIfComplete, 2000); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址