Blend Swap Attribution Maker

Format the information from a content page

  1. // ==UserScript==
  2. // @name Blend Swap Attribution Maker
  3. // @namespace http://poikilos.org/
  4. // @version 1.0.9
  5. // @description Format the information from a content page
  6. // @author Poikilos (Jake Gustafson)
  7. // @license MIT
  8. // @include /^https?\:\/\/(www\.)?blendswap\.com\/blend\/.*/
  9. // @grant none
  10. // @run-at document-end
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. // based on openGameArtAttributionMaker (same author)
  15. var myName = "Blend Swap Attribution Maker";
  16. var verbose = true;
  17. /*
  18. if (!String.prototype.startsWith) {
  19. String.prototype.startsWith = function(searchString, position) {
  20. position = position || 0;
  21. return this.indexOf(searchString, position) === position;
  22. };
  23. }
  24. */
  25. var checkTimer = null;
  26. var dateTagName = "li";
  27. var submittedDateGrandParentClassName = "sticky-top";
  28. // var authorAncestorClassName = "field-name-author-submitter"; // this div's children[1].firstChild.firstChild.firstChild is the author element
  29. var authorAncestorClassName = null; // set to null to not search for it; otherwise requires authorGreatUncleFlag
  30. var authorGreatUncleFlag = "Author:"; // such as opengameart (this tag's sibling's first grandchild is the author)
  31. var authorParentTag = "li";
  32. var authorTag = "a";
  33. var authorParentFlag = "Creator:";
  34. var authorClassName = "list-group-item";
  35. var authorHrefBaseUrl = "https://www.blendswap.com"; // such as for blendswap.com: Creator: <a href="/profile/918677">fatacuciocolata</a>
  36. var programVersionTag = "li";
  37. var programVersionClass = "list-group-item"; // avoid <li><a class="dropdown-item" href="/blends/bversion/3.0x">Blender 3.0x</a> (one li for each version is on every model page!)
  38. var programVersionFlag = "Blender ";
  39. var programVersionTerm = "Blender"; // Display this term for the information that was originally after programVersionFlag.
  40. var mediumTag = "li";
  41. var mediumFlag = "Render: ";
  42. var mediumTerm = "Render"; // Display this term for the information that was originally after mediumFlag.
  43. var datePartsMin = 3; // 3 for blendswap.com, 4 for opengameart
  44. var dateLabel = "Published: ";
  45. // blendswap.com:
  46. /*
  47. ```
  48. <li class="list-group-item">
  49. ```
  50.  
  51. then many spaces, "\n", many more spaces, then:
  52.  
  53. ```
  54. Creator: <a href="/profile/918677">fatacuciocolata</a>
  55. ```
  56.  
  57. (there is a newline after "Creator:" too)
  58. */
  59.  
  60. // var madeDivClassName = "username"; // <span class='username'> (may or may not contain <a>)
  61. var madeSpanClassName = "username"; // <li class="list-group-item"><i class="far fa-user" style="color:#ae3ec9;margin-right: 0.25rem;"></i>Creator: <a href="/profile/918677">fatacuciocolata</a></li>
  62. // ^ such as in `<span rel="sioc:has_creator"><a href="/users/reeguy" title="View user profile." class="username" xml:lang="" about="/users/reeguy" typeof="sioc:UserAccount" property="foaf:name" datatype="">reeguy</a></span><div class="field field-name-date-joined field-type-ds field-label-hidden"><div class="field-items"><div class="field-item even"><small><em>joined 6 months 2 weeks ago</em></small></div></div></div><div class="field field-name-post-date field-type-ds field-label-hidden"><div class="field-items"><div class="field-item even">02/10/2021 - 12:37</div></div></div> </div>`
  63. // ^ where other usernames may appear but the container span rel="sioc:has_creator" is unique.
  64. //var licenseClauseImgPrefix = "License__img";
  65. //var licenseAnchorPrefix = "License__link";
  66. var descriptionTag = "div";
  67. var descriptionClassName = "card-body blend-description";
  68. var buttonContainerClassName = "card sticky-top"; // The first card-body says "Must be logged in" in it which may be confuse users (make them think this script is saying that).
  69.  
  70. // Find the URL and name such as in `<div class='license-icon'><a href='http://creativecommons.org/publicdomain/zero/1.0/' target='_blank'><img src='https://opengameart.org/sites/default/files/license_images/cc0.png' alt='' title=''><div class='license-name'>CC0`:
  71. var licenseAContainerClass = "list-group-item";
  72. var licenseNameClass = "license-name";
  73. var collaboratorsClassName = null;
  74.  
  75. // var titlePrefix = "ThingPage__modelName";
  76. // var headingCreatedPrefix = "ThingPage__createdBy";
  77. // var doneDivPrefixes = [titlePrefix, headingCreatedPrefix];
  78. // var doneDivPrefixes = [madeDivClassName];
  79. // ^ This is only necessary when the page has lazy loading.
  80. // var clausesContainerPrefix = "License__ccLicense";
  81. var tagHrefContains = "/blends/tag/";
  82. // var doneDivPrefixesMain = [clausesContainerPrefix];
  83. var doneClasses = [descriptionClassName];
  84. var blendSwapNames = {
  85. "CC-BY": "CC BY 4.0",
  86. "CC-BY-SA": "CC BY-SA 4.0",
  87. "CC-BY-NC": "CC BY-NC 4.0",
  88. "CC-BY-NC-SA": "CC BY-NC-SA 4.0",
  89. "CC-BY-ND": "CC BY-ND 4.0",
  90. "CC-BY-NC-ND": "CC BY-NC-ND 4.0",
  91. "CC-0": "CC0 1.0",
  92. "GAL": "GAL 1.0",
  93. };
  94. var urlSmallNames = {
  95. "/by/1.0": "CC BY 1.0",
  96. "/by/2.0": "CC BY 2.0",
  97. "/by/2.5": "CC BY 2.5",
  98. "/by/3.0": "CC BY 3.0",
  99. "/by/4.0": "CC BY 4.0",
  100. "/by-sa/1.0": "CC BY-SA 1.0",
  101. "/by-sa/2.0": "CC BY-SA 2.0",
  102. "/by-sa/2.5": "CC BY-SA 2.5",
  103. "/by-sa/3.0": "CC BY-SA 3.0",
  104. "/by-sa/4.0": "CC BY-SA 4.0",
  105. "/by-nc/1.0": "CC BY-NC 1.0",
  106. "/by-nc/2.0": "CC BY-NC 2.0",
  107. "/by-nc/2.5": "CC BY-NC 2.5",
  108. "/by-nc/3.0": "CC BY-NC 3.0",
  109. "/by-nc/4.0": "CC BY-NC 4.0",
  110. "/by-nc-sa/1.0": "CC BY-NC-SA 1.0",
  111. "/by-nc-sa/2.0": "CC BY-NC-SA 2.0",
  112. "/by-nc-sa/2.5": "CC BY-NC-SA 2.5",
  113. "/by-nc-sa/3.0": "CC BY-NC-SA 3.0",
  114. "/by-nc-sa/4.0": "CC BY-NC-SA 4.0",
  115. "/by-nd/1.0": "CC BY-ND 1.0",
  116. "/by-nd/2.0": "CC BY-ND 2.0",
  117. "/by-nd/2.5": "CC BY-ND 2.5",
  118. "/by-nd/3.0": "CC BY-ND 3.0",
  119. "/by-nd/4.0": "CC BY-ND 4.0",
  120. "/by-nd-nc/1.0": "CC BY-ND-NC 1.0",
  121. "/by-nc-nd/2.0": "CC BY-NC-ND 2.0",
  122. "/by-nc-nd/2.5": "CC BY-NC-ND 2.5",
  123. "/by-nc-nd/3.0": "CC BY-NC-ND 3.0",
  124. "/by-nc-nd/4.0": "CC BY-NC-ND 4.0",
  125. "creativecommons.org/share-your-work/public-domain/cc0": "CC0",
  126. "creativecommons.org/publicdomain/zero/1.0": "CC0 1.0",
  127. "https://generalassetlicense.org/GAL/1.0/": "GAL 1.0", // The version is derived from the URL in at least two instances in this script.
  128. };
  129. var bigNames = {
  130. "CC BY 1.0": "Creative Commons Attribution 1.0 Generic",
  131. "CC BY 2.0": "Creative Commons Attribution 2.0 Generic",
  132. "CC BY 2.5": "Creative Commons Attribution 2.5 Generic",
  133. "CC BY 3.0": "Creative Commons Attribution 3.0 Unported",
  134. "CC BY 4.0": "Creative Commons Attribution 4.0 International",
  135. "CC BY-SA 1.0": "Creative Commons Attribution-ShareAlike 1.0 Generic",
  136. "CC BY-SA 2.0": "Creative Commons Attribution-ShareAlike 2.0 Generic",
  137. "CC BY-SA 2.5": "Creative Commons Attribution-ShareAlike 2.5 Generic",
  138. "CC BY-SA 3.0": "Creative Commons Attribution-ShareAlike 3.0 Unported",
  139. "CC BY-SA 4.0": "Creative Commons Attribution-ShareAlike 4.0 International",
  140. "CC BY-NC 1.0": "Creative Commons Attribution-NonCommercial 1.0 Generic",
  141. "CC BY-NC 2.0": "Creative Commons Attribution-NonCommercial 2.0 Generic",
  142. "CC BY-NC 2.5": "Creative Commons Attribution-NonCommercial 2.5 Generic",
  143. "CC BY-NC 3.0": "Creative Commons Attribution-NonCommercial 3.0 Unported",
  144. "CC BY-NC 4.0": "Creative Commons Attribution-NonCommercial 4.0 International",
  145. "CC BY-NC-SA 1.0": "Creative Commons Attribution-NonCommercial-ShareAlike 1.0 Generic",
  146. "CC BY-NC-SA 2.0": "Creative Commons Attribution-NonCommercial-ShareAlike 2.0 Generic",
  147. "CC BY-NC-SA 2.5": "Creative Commons Attribution-NonCommercial-ShareAlike 2.5 Generic",
  148. "CC BY-NC-SA 3.0": "Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported",
  149. "CC BY-NC-SA 4.0": "Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International",
  150. "CC BY-ND 1.0": "Creative Commons Attribution-NoDerivs 1.0 Generic",
  151. "CC BY-ND 2.0": "Creative Commons Attribution-NoDerivs 2.0 Generic",
  152. "CC BY-ND 2.5": "Creative Commons Attribution-NoDerivs 2.5 Generic",
  153. "CC BY-ND 3.0": "Creative Commons Attribution-NoDerivs 3.0 Unported",
  154. "CC BY-ND 4.0": "Creative Commons Attribution-NoDerivatives 4.0 International",
  155. "CC BY-ND-NC 1.0": "Creative Commons Attribution-NoDerivs-NonCommercial 1.0 Generic",
  156. "CC BY-NC-ND 2.0": "Creative Commons Attribution-NonCommercial-NoDerivs 2.0 Generic",
  157. "CC BY-NC-ND 2.5": "Creative Commons Attribution-NonCommercial-NoDerivs 2.5 Generic",
  158. "CC BY-NC-ND 3.0": "Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported",
  159. "CC BY-NC-ND 4.0": "Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International",
  160. "CC0": "No Rights Reserved",
  161. "CC0 1.0": "Creative Commons CC0 1.0 Universal",
  162. "GAL 1.0": "General Asset License Version 1.0",
  163. };
  164. function getElementsByTagWhereClassStartsWith(tagName, str) {
  165. if (verbose) {
  166. //console.log("");
  167. console.log("\ngetElementsByTagAndClassName(\""+str+"\")...");
  168. }
  169. var els = [];
  170. var all = document.getElementsByTagName(tagName);
  171. if (verbose) {
  172. console.log("- " + all.length + " total...");
  173. }
  174. for (var i=0, max=all.length; i < max; i++) {
  175. var el = all[i];
  176. if (el.className == undefined) continue;
  177. else if (typeof (el.className) != "string") {
  178. // className type can be svg or path as opposed to string (WARNING:
  179. // `<i` becomes `<svg` in FontAwesome!!)
  180. if (verbose) {
  181. if (tagName != "*") {
  182. console.log("- typeof el.className is " + (typeof (el.className)) + " for tagName " + el.tagName)
  183. }
  184. }
  185. }
  186. else if (el.className && el.className.startsWith(str)) {
  187. // ^ or el.classList.includes(str) if not checking startsWith
  188. els.push(el);
  189. }
  190. }
  191. if (verbose) {
  192. console.log("- FOUND " + els.length + " (" + all.length + " total)");
  193. }
  194. return els;
  195. }
  196. function getElementsByTagAndContentTrimStart(tagName, str) {
  197. if (verbose) {
  198. // console.log("");
  199. console.log("\n" + "getElementsByTagAndContentTrimStart(\""+str+"\")...");
  200. }
  201. var els = [];
  202. var all = document.getElementsByTagName(tagName);
  203. if (verbose) {
  204. console.log("- " + all.length + " total...");
  205. }
  206. for (var i=0, max=all.length; i < max; i++) {
  207. var el = all[i];
  208. if (el.className == undefined) continue;
  209. else if (typeof (el.className) != "string") {
  210. // className type can be svg or path as opposed to string (WARNING:
  211. // `<i` becomes `<svg` in FontAwesome!!)
  212. if (verbose) {
  213. if (tagName != "*") {
  214. console.log("- typeof el.className is " + (typeof (el.className)) + " for tagName " + el.tagName)
  215. }
  216. }
  217. }
  218. else if (el.textContent && el.textContent.trim().startsWith(str)) {
  219. els.push(el);
  220. }
  221. }
  222. if (verbose) {
  223. console.log("- FOUND " + els.length + " (" + all.length + " total)");
  224. }
  225. return els;
  226. }
  227. function reduceElementsByClass(all, className) {
  228. if (verbose) {
  229. console.log("- reduceElementsByClass...");
  230. }
  231. if (!all) {
  232. if (verbose) {
  233. console.log(" - The list is "+all+".");
  234. }
  235. return all;
  236. }
  237. if (!className) {
  238. if (verbose) {
  239. console.log(" - The list will not be reduced since there is no className in reduceElementsByClass.");
  240. }
  241. return all; // assume the setting is null on purpose
  242. }
  243. var els = [];
  244. for (var i=0, max=all.length; i < max; i++) {
  245. var el = all[i];
  246. if (el.className == undefined) continue;
  247. else if (typeof (el.className) != "string") {
  248. // className type can be svg or path as opposed to string (WARNING:
  249. // `<i` becomes `<svg` in FontAwesome!!)
  250. if (verbose) {
  251. if (tagName != "*") {
  252. console.log("- typeof el.className is " + (typeof (el.className)) + " for tagName " + el.tagName)
  253. }
  254. }
  255. }
  256. // else if (el.textContent && el.textContent.trim().startsWith(str)) {
  257. else if (el.classList && el.classList.contains(className)) {
  258. els.push(el);
  259. }
  260. }
  261. return els;
  262. }
  263. function getElementsByTagAndClassAndContentTrimStart(tagName, className, str) {
  264. var more = getElementsByTagAndContentTrimStart(tagName, str);
  265. return reduceElementsByClass(more, className);
  266. }
  267. function getElementsWhereClassStartsWith(str) {
  268. return getElementsByTagWhereClassStartsWith("*", str);
  269. }
  270. function getElementsByTagWhereChildHasClass(tagName, className) {
  271. // This is useful when all fields are the same but contain something different before the textContent.
  272. if (verbose) {
  273. //console.log("");
  274. console.log("\ngetElementsByTagWhereChildHasClass(\""+tagName+"\",\""+className+"\")...");
  275. }
  276. var els = [];
  277. var all = document.getElementsByTagName(tagName);
  278. if (verbose) {
  279. console.log("- " + all.length + " total...");
  280. }
  281. for (var i=0, max=all.length; i < max; i++) {
  282. var el = all[i];
  283. var child = el.firstChild;
  284. if (child == undefined) {
  285. if (verbose) {
  286. // console.log(" - child is undefined in tagName " + el.tagName + " className " + el.className);
  287. }
  288. continue;
  289. }
  290. if (child.className == undefined) {
  291. if (verbose) {
  292. // console.log(" - child.className is undefined for tagName " + child.tagName + " in className " + el.className);
  293. }
  294. continue;
  295. }
  296. // WARNING: className doesn't work for svg (`<i` becomes `<svg` in FontAwesome!)
  297. var childClassName = child.className;
  298. var childClassNames = undefined;
  299. if (typeof (child.className) != "string") {
  300. // svg doesn't get className but class can be present:
  301. childClassName = child.getAttribute("class");
  302. if (childClassName && (childClassName.length > 0)) {
  303. childClassNames = childClassName.split(" ");
  304. }
  305. }
  306. else {
  307. childClassNames = child.classList;
  308. }
  309. if (typeof (childClassName) != "string") {
  310. // var className = document.querySelector("svg").getAttribute("class"); // https://stackoverflow.com/a/24438226/4541104
  311. if (verbose) {
  312. if (tagName != "*") {
  313. console.log(" - typeof el.className is " + typeof (child.className) + " for tagName " + child.tagName)
  314. // Possible outcomes:
  315. // className type can be svg or path as opposed to string (WARNING:
  316. // `<i` becomes `<svg` in FontAwesome!!)
  317. // WARNING: typeof el.className is object for tagName svg
  318. // WARNING: typeof el.className is undefined for tagName undefined
  319. }
  320. }
  321. }
  322. else if (childClassNames.includes && childClassNames.includes(className)) { // list (generated by split above)
  323. els.push(el);
  324. if (verbose) {
  325. // console.log(" - found " + className + " among " + child.className + " of tagName " + child.tagName + " in tagName " + el.tagName);
  326. }
  327. }
  328. else if (childClassNames.contains && childClassNames.contains(className)) { // classList (generated as part of the DOM)
  329. els.push(el);
  330. if (verbose) {
  331. // console.log(" - found " + className + " among " + child.className + " of tagName " + child.tagName + " in tagName " + el.tagName);
  332. }
  333. }
  334. else {
  335. if (verbose) {
  336. // console.log(" - " + className + " is not among " + child.className + " of tagName " + child.tagName + " in tagName " + el.tagName);
  337. }
  338. }
  339. }
  340. if (verbose) {
  341. console.log("- FOUND " + els.length + " (" + all.length + " total)");
  342. }
  343. return els;
  344. }
  345.  
  346. function getElementsWhere(tagName, attributeName, value) {
  347. if (verbose) {
  348. //console.log("");
  349. console.log("\ngetSubElementsWhere(\""+tagName+"\", \""+attributeName+"\", \""+value+"\")...");
  350. }
  351. var els = [];
  352. var all = document.getElementsByTagName(tagName);
  353. for (var i=0, max=all.length; i < max; i++) {
  354. var el = all[i];
  355. if (el.getAttribute(attributeName) == value) {
  356. els.push(el);
  357. }
  358. }
  359. if (verbose) {
  360. console.log("- FOUND " + els.length);
  361. }
  362. return els;
  363. }
  364. function getDivsWhereClassStartsWith(str) {
  365. if (verbose) {
  366. //console.log("");
  367. console.log("\nFIND getDivsWhereClassStartsWith(\"" + str + "\")...");
  368. }
  369. var els = [];
  370. var all = document.getElementsByTagName("div");
  371. for (var i=0, max=all.length; i < max; i++) {
  372. var el = all[i];
  373. if (el.className.startsWith(str)) {
  374. els.push(el);
  375. // console.log("- FOUND (" + els.length + ")");
  376. }
  377. else {
  378. // console.log("- " + el.className + " does not start with it.");
  379. }
  380. }
  381. if (verbose) {
  382. // console.log("Div count: " + all.length);
  383. console.log("- FOUND " + els.length);
  384. }
  385. return els;
  386. }
  387. function getWhereClassStartsWithIn(el, str) {
  388. if (el === undefined) {
  389. console.log("[getWhereClassStartsWithIn] Error: el is undefined.");
  390. return [];
  391. }
  392. if (verbose) {
  393. //console.log("");
  394. console.log("\nDETECT getWhereClassStartsWithIn(el, \""+str+"\")...");
  395. // console.log(" el: " + JSON.stringify(el)); // DON'T do (could be circular)
  396. console.log(" el.className: "+el.className);
  397. console.log(" el.childNodes.length:"+el.childNodes.length+"...");
  398. }
  399. var els = [];
  400. var all = el.childNodes;
  401. for (var i=0, max=all.length; i < max; i++) {
  402. var thisEl = all[i];
  403. if (thisEl.className.startsWith(str)) {
  404. els.push(thisEl);
  405. // console.log("- FOUND");
  406. }
  407. else {
  408. // console.log("- "+el.className+" does not start with it.");
  409. }
  410. }
  411. if (verbose) {
  412. console.log("- FOUND " + els.length);
  413. // console.log("- done (div count: " + all.length + ")");
  414. }
  415. return els;
  416. }
  417. function hasAllDivPrefixes(prefixes) {
  418. var found = 0;
  419. for (var i=0, max=prefixes.length; i < max; i++) {
  420. if (getDivsWhereClassStartsWith(prefixes[i]).length > 0) {
  421. found++;
  422. }
  423. }
  424. return found >= prefixes.length;
  425. }
  426. function hasAllClasses(classNames) {
  427. var found = 0;
  428. for (var i=0, max=classNames.length; i < max; i++) {
  429. if (document.getElementsByClassName(classNames[i]).length > 0) {
  430. found++;
  431. }
  432. else {
  433. if (verbose) {
  434. console.error("The className " + classNames[i] + " was not found.")
  435. }
  436. }
  437. }
  438. return found >= classNames.length;
  439. }
  440. function elementHasAllPrefixes(el, prefixes) {
  441. var found = 0;
  442. for (var i=0, max=prefixes.length; i < max; i++) {
  443. if (getWhereClassStartsWithIn(el, prefixes[i]).length > 0) {
  444. found++;
  445. }
  446. }
  447. return found >= prefixes.length;
  448. }
  449. function getImgsWhereClassStartsWith(str) {
  450. var els = [];
  451. var all = document.images; // document.getElementsByTagName("img");
  452. for (var i=0, max=all.length; i < max; i++) {
  453. var el = all[i];
  454. if (el.className.startsWith(str)) {
  455. els.push(el);
  456. }
  457. }
  458. return els;
  459. }
  460. function getAnchorsWhereClassStartsWith(str) {
  461. if (verbose) {
  462. console.log("getAnchorsWhereClassStartsWith(\""+str+"\")...")
  463. }
  464. var els = [];
  465. var all = document.getElementsByTagName("a");
  466. for (var i=0, max=all.length; i < max; i++) {
  467. var el = all[i];
  468. if (el.className.startsWith(str)) {
  469. els.push(el);
  470. }
  471. }
  472. if (verbose) {
  473. console.log("- FOUND " + els.length);
  474. // console.log("- done (div count: " + all.length + ")");
  475. }
  476. return els;
  477. }
  478. function getAnchorsWhereHrefContains(str) {
  479. // Example: str=field_art_tags_tid= finds <a href="/art-search-advanced?field_art_tags_tid=grass">...
  480. if (verbose) {
  481. console.log("getAnchorsWhereHrefContains(\""+str+"\")...")
  482. }
  483. var els = [];
  484. var all = document.getElementsByTagName("a");
  485. for (var i=0, max=all.length; i < max; i++) {
  486. var el = all[i];
  487. if (el.href.includes(str)) {
  488. els.push(el);
  489. }
  490. }
  491. if (verbose) {
  492. console.log("- FOUND " + els.length);
  493. // console.log("- done (div count: " + all.length + ")");
  494. }
  495. return els;
  496. }
  497.  
  498. function elementAToMarkdown(element) {
  499. var ret = null;
  500. if (element.href) {
  501. ret = "[" + element.textContent + "](" + element.href + ")";
  502. }
  503. else {
  504. if (verbose) {
  505. console.warn("- elementAToMarkdown " + element.textContent + " href is blank in elementAToMarkdown: \"" + element.href + "\"")
  506. }
  507. ret = element.textContent;
  508. }
  509. return ret;
  510. }
  511.  
  512. function getMarkdown(info) {
  513. if (verbose) {
  514. //console.log("");
  515. console.log("\ngetMarkdown...");
  516. }
  517. var outputStr = "";
  518. if (info.title) {
  519. if (info.titleHref) {
  520. outputStr += "[" + info.title + "](" + info.titleHref + ")";
  521. }
  522. else {
  523. outputStr += "" + info.title;
  524. }
  525. outputStr += "\n";
  526. if (info.authorE) {
  527. outputStr += "\nAuthor: " + elementAToMarkdown(info.authorE);
  528. // + " and <insert contributor's name here>";
  529. outputStr += "\n";
  530. }
  531.  
  532. if (info.collaboratorElements) {
  533. outputStr += "\nCollaborators:"
  534. if (verbose) {
  535. console.log("Collaborators:");
  536. }
  537. var thisAuthorE = null;
  538. for (var i=0, max=info.collaboratorElements.length; i < max; i++) {
  539. thisAuthorE = info.collaboratorElements[i];
  540. if (verbose) {
  541. console.log(thisAuthorE.textContent, " ", thisAuthorE.href);
  542. }
  543. outputStr += "\n- " + elementAToMarkdown(thisAuthorE);
  544. }
  545. outputStr += "\n";
  546. }
  547. if (info.year) {
  548. if (info.submittedByE) {
  549. outputStr += "\n(Submitted by **" + elementAToMarkdown(info.submittedByE) + "**) ";
  550. }
  551. else {
  552. if (verbose) {
  553. console.warn("info.submittedByE is false"); // Do not try to read textContent or other properties since they don't exist!
  554. }
  555. outputStr += "\n" + dateLabel;
  556. }
  557. if (info.month) {
  558. outputStr += info.month + " ";
  559. if (info.day) {
  560. outputStr += info.day + ", "
  561. }
  562. }
  563. outputStr += info.year;
  564. if (info.hasOwnProperty("time")) {
  565. outputStr += " " + info.time;
  566. }
  567. outputStr += "\n";
  568. }
  569. else {
  570. console.warn("There is no year.");
  571. }
  572. }
  573. if (info.licenses) {
  574. outputStr += "\nLicense(s):";
  575. var licenseE = null;
  576. var license = null;
  577. for (i=0, max=info.licenses.length; i < max; i++) {
  578. licenseE = info.licenses[i];
  579. license = {};
  580. license.href = licenseE.href;
  581. license.shortLicense = licenseE.shortLicense;
  582. license.textContent = licenseE.longName;
  583. if (licenseE.shortLicense) {
  584. license.textContent = licenseE.shortLicense; // Display the short license name if present.
  585. }
  586. outputStr += "\n- " + elementAToMarkdown(license);
  587. }
  588. outputStr += "\n";
  589. }
  590. else {
  591. outputStr += "\nLICENSE: [insert license name (&URL unless in each content ZIP) from the original here]\n";
  592. }
  593.  
  594. if (programVersionTerm in info) {
  595. outputStr += "\n" + programVersionTerm + ": " + info[programVersionTerm] + "\n";
  596. }
  597. if (mediumTerm in info) {
  598. outputStr += "\n" + mediumTerm + ": " + info[mediumTerm] + "\n";
  599. }
  600.  
  601. // TODO: Move the getAnchorsWhereHrefContains call to the info gathering function.
  602. var tagAnchors = getAnchorsWhereHrefContains(tagHrefContains);
  603. var tai;
  604. if (tagAnchors.length > 0) {
  605. outputStr += "\nTags:";
  606. for (tai = 0; tai < tagAnchors.length; tai++) {
  607. var tagA = tagAnchors[tai];
  608. outputStr += "\n- [" + tagA.textContent + "](" + tagA.href + ")";
  609. }
  610. outputStr += "\n";
  611. }
  612. else {
  613. console.error("The page had no tags ('a' tags with *"+tagHrefContains+"* href)")
  614. }
  615.  
  616. if (info.attributionNotice) {
  617. outputStr += "\n\n## " + info.goodAttributionNoticeFlag + "\n" + info.attributionNotice + "\n";
  618. }
  619.  
  620. if (info.description) {
  621. outputStr += "\nDescription:\n" + info.description + "\n";
  622. }
  623.  
  624. return outputStr;
  625. }
  626.  
  627. function populateCorrespondingLicenseFields(license) {
  628. var licenseShortStr = "";
  629. if (!license.longName) {
  630. if (license.href) {
  631. for (const [tryUrlEnding, tryShort] of Object.entries(urlSmallNames)) {
  632. if (license.href.endsWith(tryUrlEnding)) {
  633. licenseShortStr = tryShort;
  634. license.shortLicenseStr = tryShort;
  635. break;
  636. }
  637. }
  638. }
  639. if (license.shortLicenseStr) {
  640. for (const [tryShort, tryLong] of Object.entries(bigNames)) {
  641. if (tryShort == license.shortLicenseStr) {
  642. license.longName = tryLong;
  643. break;
  644. }
  645. }
  646. }
  647. if (!license.longName) {
  648. license.longName = license.textContent; // Store longName separately so it can be edited without modifying the page!
  649. }
  650. }
  651.  
  652. if (license && !licenseShortStr && license.longName) {
  653. var versionIsFound = false;
  654. var licenseLower = license.textContent.toLowerCase();
  655. if (license.textContent.startsWith("Creative Commons") || license.textContent.startsWith("CC")) {
  656. if (license.textContent.startsWith("CC0 1.0") || license.textContent.startsWith("Creative Commons 0 1.0") || license.textContent.startsWith("Creative Commons Zero 1.0")) {
  657. if (!license.href) {
  658. license.href = "https://creativecommons.org/publicdomain/zero/1.0/";
  659. }
  660. licenseShortStr = "CCO 1.0";
  661. }
  662. else if ((license.textContent == "Creative Commons 0") || (license.textContent == "Creative Commons Zero")) {
  663. licenseShortStr = "CCO";
  664. }
  665. else {
  666. console.log("Looking for license clauses in license name \""+licenseLower+"\"...");
  667. licenseShortStr = "CC ";
  668. if (licenseLower.includes("attribution")) {
  669. licenseShortStr += "BY";
  670. }
  671. if (licenseLower.includes("non-commercial") || licenseLower.includes("noncommercial") || licenseLower.includes("non commercial")) {
  672. licenseShortStr += "-NC";
  673. }
  674. if (licenseLower.includes("no derivatives") || licenseLower.includes("noderivs") || licenseLower.includes("no-derivatives") || licenseLower.includes("noderivatives")) {
  675. licenseShortStr += "-ND";
  676. }
  677. if (licenseLower.includes("sharealike") || licenseLower.includes("share-alike") || licenseLower.includes("share alike") ) {
  678. licenseShortStr += "-SA";
  679. }
  680.  
  681. if (license.textContent.includes("1.0")) {
  682. licenseShortStr += " 1.0";
  683. versionIsFound = true;
  684. }
  685. else if (license.textContent.includes("2.0")) {
  686. licenseShortStr += " 2.0";
  687. versionIsFound = true;
  688. }
  689. else if (license.textContent.includes("3.0")) {
  690. licenseShortStr += " 3.0";
  691. versionIsFound = true;
  692. }
  693. else if (license.textContent.includes("4.0")) {
  694. licenseShortStr += " 4.0";
  695. versionIsFound = true;
  696. }
  697. else if (license.exactLicenseVersion !== null) {
  698. licenseShortStr += " " + license.exactLicenseVersion;
  699. versionIsFound = true;
  700. }
  701. }
  702. }
  703. console.log("licenseShortStr: " + licenseShortStr);
  704. if (!license.href) {
  705. if (verbose) {
  706. console.log("deriving license href from licenceShortStr \"" + licenseShortStr + "\"...");
  707. }
  708. var parts = licenseShortStr.split(" ");
  709. if (parts.length == 3) {
  710. var partialHref = null;
  711. // such as ["CC", "BY-SA", "3.0"]
  712. if (parts[1] == "BY") {
  713. partialHref = "http://creativecommons.org/licenses/by/";
  714. }
  715. else if (parts[1] == "BY-SA") {
  716. partialHref = "http://creativecommons.org/licenses/by-sa/";
  717. }
  718. else if (parts[1] == "BY-NC-SA") {
  719. partialHref = "http://creativecommons.org/licenses/by-nc-sa/";
  720. }
  721. else if (parts[1] == "BY-NC-ND") {
  722. partialHref = "http://creativecommons.org/licenses/by-nc-nd/";
  723. }
  724. // NOTE: by-nc-nd-sa is NOT a valid license
  725. if (partialHref != null) {
  726. license.href = partialHref + parts[2] + "/";
  727. }
  728. }
  729. }
  730. }
  731. // NOT an else if: must be sequential (!):
  732. if (license.href) {
  733. if (!licenseShortStr) {
  734. if (verbose) {
  735. console.log("Generating short license name from URL instead of from clauses...");
  736. }
  737. for (var key in urlSmallNames) {
  738. // Check if the property/key is defined in the object itself, not in parent
  739. if (urlSmallNames.hasOwnProperty(key)) {
  740. if (license.href.includes(key)) {
  741. licenseShortStr = urlSmallNames[key];
  742. if (verbose) {
  743. console.log("- got \""+licenseShortStr+"\" from \""+key+"\"")
  744. }
  745. break;
  746. }
  747. }
  748. }
  749. }
  750. else {
  751. console.log("* using existing licenseShortStr \""+licenseShortStr+"\"");
  752. }
  753.  
  754. if (!licenseShortStr) {
  755. console.warn("The URL \""+license.href+"\" is not recognized (No key in "+myName+"'s urlSmallNames is a partial of the URL), so the long license name could not be generated.");
  756. }
  757. else {
  758. license.shortLicense = licenseShortStr;
  759. if (!license.textContent) {
  760. if (verbose) {
  761. console.log("Generating license longName from URL instead of from clauses...");
  762. }
  763. if (bigNames.hasOwnProperty(licenseShortStr)) {
  764. // ^ The clauses are only in this order for versions above 1.0!
  765. license.longName = bigNames[licenseShortStr];
  766. if (verbose) {
  767. console.log("- got \""+license.longName+"\"");
  768. }
  769. }
  770. else {
  771. console.warn("The short license name \""+licenseShortStr+"\" is not recognized (It is not a key in Thing Remix Attribution Maker's bigNames), so the long license name could not be generated.");
  772. }
  773. }
  774. }
  775. }
  776. if (!licenseShortStr) {
  777. if (!license.textContent) {
  778. console.warn("The license abbreviation cannot be generated because no license text was generated (no license elements were detected).");
  779. }
  780. else {
  781. console.warn("The license abbreviation cannot be generated for an unknown license: " + license.textContent);
  782. }
  783. }
  784. }
  785.  
  786. function setClipboardText(text, callbackBtn) {
  787. var msg = "(ERROR: Your browser API is unknown.)";
  788. var okMsg = " &#10003;";
  789. // See https://stackoverflow.com/questions/52177405/clipboard-writetext-doesnt-work-on-mozilla-ie
  790. if (navigator.clipboard != undefined) { // Chrome
  791. navigator.clipboard.writeText(text).then(
  792. function () {
  793. console.log('Async: Copying to clipboard was successful!');
  794. callbackBtn.innerHTML += okMsg;
  795. }, function (err) {
  796. console.error('Async: Could not copy text: ', err);
  797. callbackBtn.innerHTML += '<br/> (ERROR: Accessing the clipboard failed.)';
  798. }
  799. );
  800. msg = null;
  801. }
  802. else if (window.clipboardData) { // Internet Explorer
  803. window.clipboardData.setData("Text", text);
  804. msg = okMsg;
  805. }
  806. if (msg != null) {
  807. callbackBtn.innerHTML += msg;
  808. }
  809. }
  810.  
  811. function getButtonContainer() {
  812. // var pageInfoEs = document.getElementsByClassName("item-page-info");
  813. var pageInfoEs = getElementsWhereClassStartsWith(buttonContainerClassName);
  814. if (pageInfoEs == null) {
  815. return null;
  816. }
  817. if (pageInfoEs.length < 1) {
  818. return null;
  819. }
  820. return pageInfoEs[0];
  821. }
  822.  
  823. function getInfo() {
  824. 'use strict';
  825. var info = {};
  826. // There should only be one.
  827. // pageInfoE.innerHTML += "<button onclick=\"getRemixLicense()\">Copy Markdown</button>";
  828. // var licenseTextE = document.getElementsByClassName("license-text");
  829. // var licenseTextE = getDivsWhereClassStartsWith(clausesContainerPrefix);
  830. // var pageInfoEs = document.getElementsByClassName("item-page-info");
  831. // var pageInfoEs = getDivsWhereClassStartsWith(madeDivClassName);
  832. // console.log("Checking "+madeDivClassName+"* elements: " + JSON.stringify(pageInfoEs));
  833. // var titleH2ParentElements = getElementsWhere("div", "class", "far fa-blender"); // var headingParts = getDivsWhereClassStartsWith(titlePrefix);
  834. var titleH2ParentElements = getElementsByTagWhereClassStartsWith("h1", "page-title");
  835. /* ^ `<i` gets transformed to `<svg` by FontAwesome (!!):
  836. <div class="col-12">
  837. <h1 class="page-title">
  838. <i class="far fa-blender" style="color:#ff922b;margin-right: 0.5rem;"></i> Simple Street
  839.  
  840.  
  841.  
  842. </h1>
  843. </div>
  844. */
  845. if (titleH2ParentElements.length > 0) {
  846. // info.title = titleH2ParentElements[0].firstChild.textContent; // "<div ...><h2>Title..."
  847. var lines = titleH2ParentElements[0].textContent.split("\n");
  848. if (lines.length > 0) {
  849. // uh oh, its a nasty multi-line one.
  850. for (var lI = 0; lI < lines.length; lI++) {
  851. if (lines[lI].trim().length > 0) {
  852. info.title = lines[lI].trim();
  853. break;
  854. }
  855. }
  856. }
  857. else {
  858. info.title = titleH2ParentElements[0].textContent.trim();
  859. }
  860. }
  861. else {
  862. console.warn("The title is missing. There are no divs with a class starting with " + titlePrefix);
  863. }
  864.  
  865.  
  866.  
  867. if (authorAncestorClassName) {
  868. // such as opengameart format
  869. var authorAndSubmitterDivs = document.getElementsByClassName(authorAncestorClassName);
  870. var collaboratorElements = [];
  871. if (authorAndSubmitterDivs.length > 0) {
  872.  
  873. if (!authorAndSubmitterDivs[0].children[0].textContent.includes(authorGreatUncleFlag)) {
  874. console.warn("The first child of div class " + authorAncestorClassName + " should include \"" + authorGreatUncleFlag + "\" but is \"" + authorAndSubmitterDivs[0].children[0].textContent + "\"");
  875. }
  876. var authorE = authorAndSubmitterDivs[0].children[1].firstChild.firstChild.firstChild;
  877. // ^ such as on opengameart: <div class="field field-name-author-submitter field-type-ds field-label-above">
  878. // <div class="field-label">Author:&nbsp;</div>
  879. // <div class="field-items"> <!--children[1]-->
  880. // <div class="field-item even"> <!--.firstChild-->
  881. // <span class='username'> <!--.firstChild-->
  882. // <a href='http://3tdstudios.com/' target='_blank'>Ron Kapaun</a>
  883. // </span>
  884. // <br/>(Submitted by <strong><a href="/users/hreikin">hreikin</a></strong>)</div></div></div><div class="field field-name-post-date field-type-ds field-label-hidden"><div class="field-items"><div class="field-item even">Thursday, November 6, 2014 - 13:08</div></div></div><div class="field field-name-field-art-type field-type-taxonomy-term-reference field-label-above"><div class="field-label">Art Type:&nbsp;</div><div class="field-items"><div class="field-item even"><a href="/art-search-advanced?field_art_type_tid%5B%5D=10" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">3D Art</a></div></div></div><div class="field field-name-field-art-tags field-type-taxonomy-term-reference field-label-above"><div class="field-label">Tags:&nbsp;</div><div class="field-items"><div class="field-item even"><a href="/art-search-advanced?field_art_tags_tid=torque" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">torque</a></div><div class="field-item odd"><a href="/art-search-advanced?field_art_tags_tid=dae" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">dae</a></div><div class="field-item even"><a href="/art-search-advanced?field_art_tags_tid=furniture" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">furniture</a></div><div class="field-item odd"><a href="/art-search-advanced?field_art_tags_tid=bookshelf" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">bookshelf</a></div><div class="field-item even"><a href="/art-search-advanced?field_art_tags_tid=china" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">china</a></div><div class="field-item odd"><a href="/art-search-advanced?field_art_tags_tid=hutch" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">hutch</a></div><div class="field-item even"><a href="/art-search-advanced?field_art_tags_tid=cupboard" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">cupboard</a></div><div class="field-item odd"><a href="/art-search-advanced?field_art_tags_tid=Desk" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Desk</a></div><div class="field-item even"><a href="/art-search-advanced?field_art_tags_tid=table" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">table</a></div><div class="field-item odd"><a href="/art-search-advanced?field_art_tags_tid=end%20table" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">end table</a></div><div class="field-item even"><a href="/art-search-advanced?field_art_tags_tid=square" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">square</a></div><div class="field-item odd"><a href="/art-search-advanced?field_art_tags_tid=workbench" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">workbench</a></div><div class="field-item even"><a href="/art-search-advanced?field_art_tags_tid=prop" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">prop</a></div></div></div><div class="field field-name-field-art-licenses field-type-taxonomy-term-reference field-label-above"><div class="field-label">License(s):&nbsp;</div><div class="field-items"><div class="field-item even"><div class='license-icon'><a href='http://creativecommons.org/publicdomain/zero/1.0/' target='_blank'><img src='https://opengameart.org/sites/default/files/license_images/cc0.png' alt='' title=''><div class='license-name'>CC0</div></a></div></div></div></div><div class="field field-name-collect field-type-ds field-label-above"><div class="field-label">Collections:&nbsp;</div><div class="field-items"><div class="field-item even"><div class='collect-container'><ul><li><a href="/content/3d-packs">3D - Packs</a></li><li><a href="/content/3d-furniture-and-other-interiorexterior-decorables-under-cc0">3D Furniture and other interior/Exterior Decorables under CC0</a></li><li><a href="/content/3d-medieval-fantasy">3D Medieval Fantasy</a></li><li><a href="/content/3d-models">3D Models</a></li><li><a href="/content/3d-stuff-thangs">3D Stuff &amp; thangs</a></li><li><a href="/content/3td-studios-packs">3TD Studios Packs</a></li><li><a href="/content/arce-movens-2">Arce Movens 2</a></li><li><a href="/content/cc0-assets">CC0 Assets</a></li><li><a href="/content/cc0-assets-3d-low-poly">CC0 ASSETS 3D LOW POLY</a></li><li><a href="/content/cc0-furniture">CC0 Furniture</a></li><li><a href="/content/cco-3d-furniture">CCO 3D Furniture</a></li><li><a href="/content/dead-welcoming">Dead Welcoming</a></li><li><a href="/content/deco">Deco</a></li><li><a href="/content/freerpg-project-assets">FreeRPG Project Assets</a></li><li><a href="/content/game-project-models">Game Project Models</a></li><li><a href="/content/hq-items-interior">HQ Items Interior</a></li><li><a href="/content/legend-of-rathnor-parts">Legend of Rathnor Parts</a></li><li><a href="/content/low-poly-1">low poly</a></li><li><a href="/content/lr">LR</a></li><li><a href="/content/mysterious-sprites-and-housemansion-parts">Mysterious Sprites and House/Mansion Parts</a></li><li><a href="/content/openmw-showcase-possibilities">OpenMW showcase possibilities</a></li><li><a href="/content/torque3d-possible-modelstextures">Torque3D possible models/textures</a></li><li><a href="/content/turodas">Turodas</a></li></ul></div></div></div></div><div class="field field-name-favorites field-type-ds field-label-inline clearfix"><div class="field-label">Favorites:&nbsp;</div><div class="field-items"><div class="field-item even">23</div></div></div><div class="field field-name-flag-favorite field-type-ds field-label-hidden"><div class="field-items"><div class="field-item even"><span class="flag-wrapper flag-favorites flag-favorites-30816">
  885. // collaboratorElements.push(authorE);
  886. if (authorAndSubmitterDivs[0].children[1].firstChild.children[2]) {
  887. var textAfterBRNode = authorAndSubmitterDivs[0].children[1].firstChild.childNodes[2];
  888. // ^ INFO: authorAndSubmitterDivs[0].children[1].firstChild.children[1].textContent is "" because a <br/> doesn't have textContent.
  889. if (verbose) {
  890. // console.log("author + opener for submitter: " + authorAndSubmitterDivs[0].children[1].firstChild.textContent);
  891. console.log("A submitter opener was found: \"" + textAfterBRNode.textContent + "\"");
  892. }
  893. if (textAfterBRNode.textContent.includes("Submitted by")) {
  894. var submittedByE = authorAndSubmitterDivs[0].children[1].firstChild.children[2].firstChild; // children[1] is field-items . firstChild is field-item even . children[2] is <strong> . firstChild is <a ...>
  895. if (verbose) {
  896. console.log("Submitted by:", submittedByE.textContent, "href=" + submittedByE.href)
  897. }
  898. info.submittedByE = submittedByE;
  899. }
  900. else {
  901. if (verbose) {
  902. console.log("There is no \"Submitted by\" in the", authorAncestorClassName, "children[1].firstChild.children[2]")
  903. }
  904. }
  905. }
  906. if (verbose) {
  907. console.log("author: " + authorE.textContent, "href:", authorE.href);
  908. }
  909. // console.warn("Multiple authors (collaboratorElements) is not yet implemented.");
  910. info.authorE = authorE;
  911. }
  912. else {
  913. console.warn("The author is missing. There are no divs with a class " + authorAncestorClassName + " having a great grandchild of the second child");
  914. }
  915. }
  916. else {
  917. // such as blendswap.com format
  918. var authorParents = getElementsByTagAndContentTrimStart(authorParentTag, authorParentFlag);
  919. for (var parentI=0, parentMax=authorParents.length; parentI < parentMax; parentI++) {
  920. var authorParentE = authorParents[parentI];
  921. for (var authorI=0, authorMax=authorParentE.children.length; authorI < authorMax; authorI++) {
  922. if (authorParentE.children[authorI].tagName.toLowerCase() == authorTag.toLowerCase()) {
  923. // var authorStr = authorParentE.children[authorI].textContent.trim().substring(authorParentFlag.length).trim();
  924. info.authorE = authorParentE.children[authorI];
  925. // info.authorE.textContent = authorStr;
  926. break;
  927. }
  928. }
  929. if (verbose) console.log(authorParentFlag + info.authorE.textContent);
  930. }
  931. }
  932. var mediumTag = "li";
  933. var mediumFlag = "Render: ";
  934. var mediumTerm = "Render"; // Display this term for the information that was originally after mediumFlag.
  935. if (programVersionTag && programVersionFlag) {
  936. var programEls = getElementsByTagAndClassAndContentTrimStart(programVersionTag, programVersionClass, programVersionFlag);
  937. if (programEls && (programEls.length > 0)) {
  938. info[programVersionTerm] = programEls[0].textContent.trim().substring(programVersionFlag.length).trim();
  939. }
  940. if (programEls.length > 1) {
  941. console.warn("There were more than one "+programVersionTerm+" elements ("+programVersionTag+" containing \""+programVersionFlag+"\"");
  942. }
  943. }
  944. if (mediumTag && mediumFlag) {
  945. var mediumEls = getElementsByTagAndContentTrimStart(mediumTag, mediumFlag);
  946. if (mediumEls && (mediumEls.length > 0)) {
  947. info[mediumTerm] = mediumEls[0].textContent.trim().substring(mediumFlag.length).trim();
  948. }
  949. if (mediumEls.length > 1) {
  950. console.warn("There were more than one "+mediumTerm+" elements ("+mediumTag+" containing \""+mediumFlag+"\"");
  951. }
  952. }
  953. if (descriptionTag && descriptionClassName) {
  954. var descriptionEls = getElementsByTagWhereClassStartsWith(descriptionTag, descriptionClassName);
  955. if (descriptionEls && (descriptionEls.length > 0)) {
  956. info.description = descriptionEls[0].textContent.trim();
  957. var descriptionFlag = "Description:";
  958. if (info.description.startsWith(descriptionFlag)) {
  959. info.description = info.description.substring(descriptionFlag.length).trim();
  960. }
  961. }
  962. if (descriptionEls.length > 1) {
  963. console.warn("There were more than one description elements (tagName "+descriptionTag+" where className starts with \""+descriptionClassName+"\"");
  964. }
  965. }
  966. if (collaboratorsClassName) {
  967. var collaboratorDivs = document.getElementsByClassName(collaboratorsClassName);
  968. if (collaboratorDivs.length > 0) {
  969. info.collaboratorElements = collaboratorElements;
  970. var goodCollaboratorsFlag = "Collaborators:";
  971. if (collaboratorDivs[0].children.length >= 2) {
  972. if (!collaboratorDivs[0].children[0].textContent.includes(goodCollaboratorsFlag)) {
  973. console.warn("The first child of div class " + collaboratorsClassName + " does not say \"" + goodCollaboratorsFlag + "\"");
  974. }
  975. var collaboratorsContainerE = collaboratorDivs[0].children[1];
  976. var thisAuthorE = {};
  977. for (var i=0, max=collaboratorsContainerE.children.length; i < max; i++) {
  978. thisAuthorE = collaboratorsContainerE.children[i].firstChild; //firstChild of the <div ...> is <a ...>
  979. //var tmpAuthorObj = {};
  980. //tmpAuthorObj.textContent = thisAuthorE.textContent;
  981. //tmpAuthorObj.href = thisAuthorE.href; // TODO: Why is this necessary (href is blank during iteration otherwise)?
  982. if (verbose) {
  983. if (!thisAuthorE.href) {
  984. console.warn("- collaborator " + thisAuthorE.textContent + " href is blank in addButton: \"" + thisAuthorE.href + "\"")
  985. }
  986. // else {
  987. // console.warn("- collaborator " + thisAuthorE.textContent + " href is non-blank in addButton: \"" + thisAuthorE.href + "\"")
  988. // }
  989. }
  990. info.collaboratorElements.push(thisAuthorE);
  991. }
  992. }
  993. else {
  994. console.warn("div class " + collaboratorsClassName + " should have 2 children but has " + collaboratorDivs[0].children.length + ".");
  995. }
  996. }
  997. // ^ Such as on opengameart: <div class="field field-name-field-collaborators field-type-user-reference field-label-above">
  998. // <div class="field-label">Collaborators:&nbsp;</div>
  999. // <div class="field-items">
  1000. // <div class="field-item even"><a href="/users/daneeklu">daneeklu</a></div>
  1001. // <div class="field-item odd"><a href="/users/jetrel">Jetrel</a></div>
  1002. // <div class="field-item even"><a href="/users/hyptosis">Hyptosis</a></div>
  1003. // <div class="field-item odd"><a href="/users/redshrike">Redshrike</a></div>
  1004. // <div class="field-item even"><a href="/users/bertram">Bertram</a></div></div>
  1005. // </div>
  1006.  
  1007. // TODO: add artTypeStr
  1008. // ^ Such as on opengameart: <div class="field field-name-field-art-type field-type-taxonomy-term-reference field-label-above">
  1009. // <div class="field-label">Art Type:&nbsp;</div>
  1010. // <div class="field-items">
  1011. // <div class="field-item even"><a href="/art-search-advanced?field_art_type_tid%5B%5D=9" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">2D Art</a></div></div></div><div class="field field-name-field-art-tags field-type-taxonomy-term-reference field-label-above"><div class="field-label">Tags:&nbsp;</div><div class="field-items"><div class="field-item even"><a href="/art-search-advanced?field_art_tags_tid=RPG" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">RPG</a></div><div class="field-item odd"><a href="/art-search-advanced?field_art_tags_tid=tiles" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">tiles</a></div><div class="field-item even"><a href="/art-search-advanced?field_art_tags_tid=32x32" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">32x32</a></div><div class="field-item odd"><a href="/art-search-advanced?field_art_tags_tid=food" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">food</a></div><div class="field-item even"><a href="/art-search-advanced?field_art_tags_tid=Wood" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Wood</a></div><div class="field-item odd"><a href="/art-search-advanced?field_art_tags_tid=Market%20Booth" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Market Booth</a></div><div class="field-item even"><a href="/art-search-advanced?field_art_tags_tid=grass" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">grass</a></div><div class="field-item odd"><a href="/art-search-advanced?field_art_tags_tid=water" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">water</a></div><div class="field-item even"><a href="/art-search-advanced?field_art_tags_tid=path" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">path</a></div><div class="field-item odd"><a href="/art-search-advanced?field_art_tags_tid=cobblestone" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">cobblestone</a></div><div class="field-item even"><a href="/art-search-advanced?field_art_tags_tid=firewood" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">firewood</a></div><div class="field-item odd"><a href="/art-search-advanced?field_art_tags_tid=table" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">table</a></div><div class="field-item even"><a href="/art-search-advanced?field_art_tags_tid=dock" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">dock</a></div><div class="field-item odd"><a href="/art-search-advanced?field_art_tags_tid=boat" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">boat</a></div></div></div><div class="field field-name-field-art-licenses field-type-taxonomy-term-reference field-label-above"><div class="field-label">License(s):&nbsp;</div><div class="field-items"><div class="field-item even"><div class='license-icon'><a href='http://creativecommons.org/licenses/by-sa/3.0/' target='_blank'><img src='https://opengameart.org/sites/default/files/license_images/cc-by-sa.png' alt='' title=''><div class='license-name'>CC-BY-SA 3.0</div></a></div></div></div></div><div class="field field-name-collect field-type-ds field-label-above"><div class="field-label">Collections:&nbsp;</div><div class="field-items"><div class="field-item even"><div class='collect-container'><ul><li><a href="/content/04-pixel-art-terrain">04. Pixel Art - Terrain</a></li><li><a href="/content/2-acetoxy-nnn-trimethylethanaminium">2-Acetoxy-N,N,N-trimethylethanaminium</a></li><li><a href="/content/2d-rpg-lpc-compatible-tilessprites">2D - RPG - [LPC]-Compatible Tiles/Sprites</a></li><li><a href="/content/2d-32x32">2D 32x32</a></li><li><a href="/content/2d-tilesets-topview">2D tilesets topview</a></li><li><a href="/content/2dtileorthogonal">2D::Tile::Orthogonal</a></li><li><a href="/content/32x32-fantasy-tiles">32x32 Fantasy Tiles</a></li><li><a href="/content/acid">Acid</a></li><li><a href="/content/andruil-rpg">Andruil RPG</a></li><li><a href="/content/art-used-in-dusk-graphical-mud">Art used in Dusk Graphical MUD</a></li><li><a href="/content/assets-for-planned-use-in-realm-of-kuhraiy">Assets for Planned Use in Realm of Kuhraiy</a></li><li><a href="/content/awesome-game-art">Awesome Game Art</a></li><li><a href="/content/backgrounds-6">BACKGROUNDS</a></li><li><a href="/content/base-pixel-art-for-3d-pixelish-rpg">Base pixel art for 3D pixelish RPG</a></li><li><a href="/content/besidethevoids-downloads">BesideTheVoid&#039;s Downloads</a> (<a href="/collect/remove/14914/85607" class="collect-remove">remove</a>)</li><li><a href="/content/best-orthogonal-rectangular-tilesets-for-tilemaps">Best Orthogonal (rectangular) Tilesets for Tilemaps</a></li><li><a href="/content/bits-and-bobs">Bits and Bobs</a></li><li><a href="/content/bunchofheroes">BunchOfHeroes</a></li><li><a href="/content/c-dogs-sdl-art">C-Dogs SDL art</a></li><li><a href="/content/conquest-rpg-assets">Conquest RPG Assets</a></li><li><a href="/content/credits-0">Credits</a></li><li><a href="/content/ctf">CTF</a></li><li><a href="/content/dungeon-slayer-art">Dungeon Slayer Art</a></li><li><a href="/content/fantasy-6">Fantasy</a></li><li><a href="/content/field-guardians">Field Guardians</a></li><li><a href="/content/game-prototype-art-assets">Game Prototype Art Assets </a></li><li><a href="/content/gamecollection">GameCollection</a></li><li><a href="/content/golden-axe">Golden Axe</a></li><li><a href="/content/hero-game">hero game</a></li><li><a href="/content/hq-2d-isometric">HQ 2D &amp; Isometric</a></li><li><a href="/content/infinimon-procedurally-generated-pokemon-or-digimon-style-game-assets">Infinimon - Procedurally-Generated Pokemon- or Digimon-style Game Assets</a></li><li><a href="/content/liberated-pixel-cup-0">Liberated Pixel Cup</a></li><li><a href="/content/long-licence-fantasy-modern-game">long licence fantasy modern game</a></li><li><a href="/content/loot-run">Loot run</a></li><li><a href="/content/lpc-1">LPC</a></li><li><a href="/content/lpc-compatible-terraintiles">LPC Compatible Terrain/Tiles</a></li><li><a href="/content/lpc-gfx">LPC GFX</a></li><li><a href="/content/maybe-assets-for-treasure-other">maybe assets for Treasure (+other)</a></li><li><a href="/content/medicines-disordered-list-of-fantasy-rpg-tilesets">Medicine&#039;s disordered list of Fantasy RPG Tilesets</a></li><li><a href="/content/minimmo-project">Minimmo project</a></li><li><a href="/content/mittys-maize">Mitty&#039;s Maize</a></li><li><a href="/content/must-use">Must Use</a></li><li><a href="/content/non-commercial-art">Non-Commercial - Art</a></li><li><a href="/content/oddball-gamez-lpc-style">Oddball Gamez LPC Style</a></li><li><a href="/content/one-click-minecraft">one click minecraft</a></li><li><a href="/content/pixelfarm">PixelFarm</a></li><li><a href="/content/roguelike">Roguelike</a></li><li><a href="/content/rpg-2">RPG</a></li><li><a href="/content/rpg-5">RPG</a></li><li><a href="/content/rpg-stuff-collection">RPG Stuff Collection</a></li><li><a href="/content/sets">SETS</a></li><li><a href="/content/smartpoints-example-game">SmartPoints Example Game</a></li><li><a href="/content/snes-style-rpg">SNES Style RPG</a></li><li><a href="/content/snes-like-world-textures">snes-like world textures</a></li><li><a href="/content/sprites-2">Sprites</a></li><li><a href="/content/stardont">stardont</a></li><li><a href="/content/stendhal">Stendhal</a></li><li><a href="/content/terrain-transitions">Terrain transitions</a></li><li><a href="/content/test-10">test</a></li><li><a href="/content/test-5">Test</a></li><li><a href="/content/test-11">test</a></li><li><a href="/content/the-weary-adventurer">The Weary Adventurer</a></li><li><a href="/content/thing">Thing</a></li><li><a href="/content/tile-4">TILE</a></li><li><a href="/content/tilesets-and-backgrounds-pixelart">Tilesets and Backgrounds (PixelArt)</a></li><li><a href="/content/top-down-2d-jrpg-32x32-art-collection">Top Down 2D JRPG 32x32 Art Collection</a></li><li><a href="/content/top-down-rpg-pixel-art">Top Down RPG Pixel Art</a></li><li><a href="/content/top-down-2d-rpg">Top-down 2D RPG</a></li><li><a href="/content/topdown-tiles">topdown tiles</a></li><li><a href="/content/trevas">Trevas</a></li><li><a href="/content/ultimate-tabletop">Ultimate TableTop</a></li><li><a href="/content/used-in-hero-of-allacrost">Used in Hero of Allacrost</a></li><li><a href="/content/used-in-valyria-tear">Used in Valyria tear</a></li><li><a href="/content/wastelander">Wastelander</a></li><li><a href="/content/workable-style-32x32-tiles">Workable style 32x32 tiles</a></li><li><a href="/content/zed-interesting-yet-unsorted">Zed - Interesting Yet Unsorted</a></li><li><a href="/content/zelda-like-rpg">Zelda Like RPG</a></li><li><a href="/content/zombie-buster-project">zombie-buster project</a></li></ul></div></div></div></div><div class="field field-name-favorites field-type-ds field-label-inline clearfix"><div class="field-label">Favorites:&nbsp;</div><div class="field-items"><div class="field-item even">235</div></div></div><div class="field field-name-flag-favorite field-type-ds field-label-hidden"><div class="field-items"><div class="field-item even"><span class="flag-wrapper flag-favorites flag-favorites-14914">
  1012. }
  1013.  
  1014. var submittedDateStr = null; // formerly createdStr
  1015. // var headingCreatedParts = getDivsWhereClassStartsWith(headingCreatedPrefix);
  1016. // var submittedDateDivs = document.getElementsByClassName(submittedDateGrandParentClassName);
  1017. var submittedDateDivs = getElementsByTagWhereChildHasClass(dateTagName, "fa-calendar-alt");
  1018. /* ^ FontAwesome rewrites the HTML (!!):
  1019. <li class="list-group-item"><i class="far fa-calendar-alt"
  1020. style="color:#40c057;margin-right: 0.25rem;"></i>
  1021. August 29, 2018</li>
  1022. BECOMES:
  1023. <svg class="svg-inline--fa fa-calendar-alt fa-w-14" . . .
  1024. */
  1025.  
  1026. if (submittedDateDivs.length > 0) {
  1027. // var submittedDateE = submittedDateDivs[0].firstChild.firstChild;
  1028. // submittedDateStr = submittedDateE.textContent;
  1029. // ^ such as in <div class="field field-name-post-date field-type-ds field-label-hidden">
  1030. // <div class="field-items"> <!--.firstChild-->
  1031. // <div class="field-item even">Thursday, January 31, 2013 - 08:06</div></div></div><div class="field field-name-field-collaborators field-type-user-reference field-label-above"><div class="field-label">Collaborators:&nbsp;</div><div class="field-items"><div class="field-item even"><a href="/users/daneeklu">daneeklu</a></div><div class="field-item odd"><a href="/users/jetrel">Jetrel</a></div><div class="field-item even"><a href="/users/hyptosis">Hyptosis</a></div><div class="field-item odd"><a href="/users/redshrike">Redshrike</a></div><div class="field-item even"><a href="/users/bertram">Bertram</a></div></div></div><div class="field field-name-field-art-type field-type-taxonomy-term-reference field-label-above"><div class="field-label">Art Type:&nbsp;</div><div class="field-items"><div class="field-item even"><a href="/art-search-advanced?field_art_type_tid%5B%5D=9" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">2D Art</a></div></div></div><div class="field field-name-field-art-tags field-type-taxonomy-term-reference field-label-above"><div class="field-label">Tags:&nbsp;</div><div class="field-items"><div class="field-item even"><a href="/art-search-advanced?field_art_tags_tid=RPG" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">RPG</a></div><div class="field-item odd"><a href="/art-search-advanced?field_art_tags_tid=tiles" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">tiles</a></div><div class="field-item even"><a href="/art-search-advanced?field_art_tags_tid=32x32" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">32x32</a></div><div class="field-item odd"><a href="/art-search-advanced?field_art_tags_tid=food" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">food</a></div><div class="field-item even"><a href="/art-search-advanced?field_art_tags_tid=Wood" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Wood</a></div><div class="field-item odd"><a href="/art-search-advanced?field_art_tags_tid=Market%20Booth" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Market Booth</a></div><div class="field-item even"><a href="/art-search-advanced?field_art_tags_tid=grass" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">grass</a></div><div class="field-item odd"><a href="/art-search-advanced?field_art_tags_tid=water" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">water</a></div><div class="field-item even"><a href="/art-search-advanced?field_art_tags_tid=path" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">path</a></div><div class="field-item odd"><a href="/art-search-advanced?field_art_tags_tid=cobblestone" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">cobblestone</a></div><div class="field-item even"><a href="/art-search-advanced?field_art_tags_tid=firewood" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">firewood</a></div><div class="field-item odd"><a href="/art-search-advanced?field_art_tags_tid=table" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">table</a></div><div class="field-item even"><a href="/art-search-advanced?field_art_tags_tid=dock" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">dock</a></div><div class="field-item odd"><a href="/art-search-advanced?field_art_tags_tid=boat" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">boat</a></div></div></div><div class="field field-name-field-art-licenses field-type-taxonomy-term-reference field-label-above"><div class="field-label">License(s):&nbsp;</div><div class="field-items"><div class="field-item even"><div class='license-icon'><a href='http://creativecommons.org/licenses/by-sa/3.0/' target='_blank'><img src='https://opengameart.org/sites/default/files/license_images/cc-by-sa.png' alt='' title=''><div class='license-name'>CC-BY-SA 3.0</div></a></div></div></div></div><div class="field field-name-collect field-type-ds field-label-above"><div class="field-label">Collections:&nbsp;</div><div class="field-items"><div class="field-item even"><div class='collect-container'><ul><li><a href="/content/04-pixel-art-terrain">04. Pixel Art - Terrain</a></li><li><a href="/content/2-acetoxy-nnn-trimethylethanaminium">2-Acetoxy-N,N,N-trimethylethanaminium</a></li><li><a href="/content/2d-rpg-lpc-compatible-tilessprites">2D - RPG - [LPC]-Compatible Tiles/Sprites</a></li><li><a href="/content/2d-32x32">2D 32x32</a></li><li><a href="/content/2d-tilesets-topview">2D tilesets topview</a></li><li><a href="/content/2dtileorthogonal">2D::Tile::Orthogonal</a></li><li><a href="/content/32x32-fantasy-tiles">32x32 Fantasy Tiles</a></li><li><a href="/content/acid">Acid</a></li><li><a href="/content/andruil-rpg">Andruil RPG</a></li><li><a href="/content/art-used-in-dusk-graphical-mud">Art used in Dusk Graphical MUD</a></li><li><a href="/content/assets-for-planned-use-in-realm-of-kuhraiy">Assets for Planned Use in Realm of Kuhraiy</a></li><li><a href="/content/awesome-game-art">Awesome Game Art</a></li><li><a href="/content/backgrounds-6">BACKGROUNDS</a></li><li><a href="/content/base-pixel-art-for-3d-pixelish-rpg">Base pixel art for 3D pixelish RPG</a></li><li><a href="/content/besidethevoids-downloads">BesideTheVoid&#039;s Downloads</a> (<a href="/collect/remove/14914/85607" class="collect-remove">remove</a>)</li><li><a href="/content/best-orthogonal-rectangular-tilesets-for-tilemaps">Best Orthogonal (rectangular) Tilesets for Tilemaps</a></li><li><a href="/content/bits-and-bobs">Bits and Bobs</a></li><li><a href="/content/bunchofheroes">BunchOfHeroes</a></li><li><a href="/content/c-dogs-sdl-art">C-Dogs SDL art</a></li><li><a href="/content/conquest-rpg-assets">Conquest RPG Assets</a></li><li><a href="/content/credits-0">Credits</a></li><li><a href="/content/ctf">CTF</a></li><li><a href="/content/dungeon-slayer-art">Dungeon Slayer Art</a></li><li><a href="/content/fantasy-6">Fantasy</a></li><li><a href="/content/field-guardians">Field Guardians</a></li><li><a href="/content/game-prototype-art-assets">Game Prototype Art Assets </a></li><li><a href="/content/gamecollection">GameCollection</a></li><li><a href="/content/golden-axe">Golden Axe</a></li><li><a href="/content/hero-game">hero game</a></li><li><a href="/content/hq-2d-isometric">HQ 2D &amp; Isometric</a></li><li><a href="/content/infinimon-procedurally-generated-pokemon-or-digimon-style-game-assets">Infinimon - Procedurally-Generated Pokemon- or Digimon-style Game Assets</a></li><li><a href="/content/liberated-pixel-cup-0">Liberated Pixel Cup</a></li><li><a href="/content/long-licence-fantasy-modern-game">long licence fantasy modern game</a></li><li><a href="/content/loot-run">Loot run</a></li><li><a href="/content/lpc-1">LPC</a></li><li><a href="/content/lpc-compatible-terraintiles">LPC Compatible Terrain/Tiles</a></li><li><a href="/content/lpc-gfx">LPC GFX</a></li><li><a href="/content/maybe-assets-for-treasure-other">maybe assets for Treasure (+other)</a></li><li><a href="/content/medicines-disordered-list-of-fantasy-rpg-tilesets">Medicine&#039;s disordered list of Fantasy RPG Tilesets</a></li><li><a href="/content/minimmo-project">Minimmo project</a></li><li><a href="/content/mittys-maize">Mitty&#039;s Maize</a></li><li><a href="/content/must-use">Must Use</a></li><li><a href="/content/non-commercial-art">Non-Commercial - Art</a></li><li><a href="/content/oddball-gamez-lpc-style">Oddball Gamez LPC Style</a></li><li><a href="/content/one-click-minecraft">one click minecraft</a></li><li><a href="/content/pixelfarm">PixelFarm</a></li><li><a href="/content/roguelike">Roguelike</a></li><li><a href="/content/rpg-2">RPG</a></li><li><a href="/content/rpg-5">RPG</a></li><li><a href="/content/rpg-stuff-collection">RPG Stuff Collection</a></li><li><a href="/content/sets">SETS</a></li><li><a href="/content/smartpoints-example-game">SmartPoints Example Game</a></li><li><a href="/content/snes-style-rpg">SNES Style RPG</a></li><li><a href="/content/snes-like-world-textures">snes-like world textures</a></li><li><a href="/content/sprites-2">Sprites</a></li><li><a href="/content/stardont">stardont</a></li><li><a href="/content/stendhal">Stendhal</a></li><li><a href="/content/terrain-transitions">Terrain transitions</a></li><li><a href="/content/test-10">test</a></li><li><a href="/content/test-11">test</a></li><li><a href="/content/test-5">Test</a></li><li><a href="/content/the-weary-adventurer">The Weary Adventurer</a></li><li><a href="/content/thing">Thing</a></li><li><a href="/content/tile-4">TILE</a></li><li><a href="/content/tilesets-and-backgrounds-pixelart">Tilesets and Backgrounds (PixelArt)</a></li><li><a href="/content/top-down-2d-jrpg-32x32-art-collection">Top Down 2D JRPG 32x32 Art Collection</a></li><li><a href="/content/top-down-rpg-pixel-art">Top Down RPG Pixel Art</a></li><li><a href="/content/top-down-2d-rpg">Top-down 2D RPG</a></li><li><a href="/content/topdown-tiles">topdown tiles</a></li><li><a href="/content/trevas">Trevas</a></li><li><a href="/content/ultimate-tabletop">Ultimate TableTop</a></li><li><a href="/content/used-in-hero-of-allacrost">Used in Hero of Allacrost</a></li><li><a href="/content/used-in-valyria-tear">Used in Valyria tear</a></li><li><a href="/content/wastelander">Wastelander</a></li><li><a href="/content/workable-style-32x32-tiles">Workable style 32x32 tiles</a></li><li><a href="/content/zed-interesting-yet-unsorted">Zed - Interesting Yet Unsorted</a></li><li><a href="/content/zelda-like-rpg">Zelda Like RPG</a></li><li><a href="/content/zombie-buster-project">zombie-buster project</a></li></ul></div></div></div></div><div class="field field-name-favorites field-type-ds field-label-inline clearfix"><div class="field-label">Favorites:&nbsp;</div><div class="field-items"><div class="field-item even">235</div></div></div><div class="field field-name-flag-favorite field-type-ds field-label-hidden"><div class="field-items"><div class="field-item even"><span class="flag-wrapper flag-favorites flag-favorites-14914">
  1032. // <a href="/flag/flag/favorites/14914?destination=node/14914&amp;token=NF4Z1O1g9CwLU2n65PscJkJeHBLLkg75lOKLirEfrdI" title="" class="flag flag-action flag-link-toggle" rel="nofollow">Add to Favorites</a><span class="flag-throbber">&nbsp;</span>
  1033. submittedDateStr = submittedDateDivs[0].textContent.trim();
  1034. }
  1035. else {
  1036. console.warn("The date is missing. There are no divs with a class " + submittedDateGrandParentClassName + " having a great grandchild");
  1037. }
  1038. info.titleHref = window.location.href;
  1039. // console.log("info.title: " + info.title);
  1040. // console.log("info.titleHref: " + info.titleHref);
  1041. console.log("submittedDateStr: " + submittedDateStr);
  1042. /*
  1043. // OpenGameArt format:
  1044. if (submittedDateStr !== null) {
  1045. var createdParts = submittedDateStr.split(" ");
  1046. if (createdParts.length >= 4) {
  1047. // 0 is day of week spelled out
  1048. var yI = 3;
  1049. var dI = 2;
  1050. var mI = 1;
  1051. var tI = createdParts.length - 1;
  1052. var yStr = createdParts[yI];
  1053. var dStr = createdParts[dI];
  1054. var mStr = createdParts[mI];
  1055. if (dStr.endsWith(",")) {
  1056. info.month = mStr;
  1057. info.day = dStr.slice(0, -1);
  1058. info.year = yStr;
  1059. if (createdParts.length >= tI) {
  1060. var tStr = createdParts[tI];
  1061. if (tStr.includes(":")) {
  1062. info.time = tStr;
  1063. }
  1064. else {
  1065. console.warn("The time string wasn't found (doesn't have ':') as the last space-separated element of \""+submittedDateStr+"\"");
  1066. }
  1067. }
  1068. }
  1069. else {
  1070. console.warn("A date in the format \"DAY_OF_WEEK MON, D, YYYY HH:MM\" was expected but got: \""+submittedDateStr+"\"");
  1071. }
  1072. }
  1073. }
  1074. */
  1075. if (submittedDateStr !== null) {
  1076. var createdParts = submittedDateStr.split(" ");
  1077. if (createdParts.length >= datePartsMin) {
  1078. // blendswap.com format is like: "August 29, 2018"
  1079. var mI = 0;
  1080. var dI = 1;
  1081. var yI = 2;
  1082. var yStr = createdParts[yI];
  1083. var dStr = createdParts[dI];
  1084. var mStr = createdParts[mI];
  1085. if (dStr.endsWith(",")) {
  1086. info.month = mStr;
  1087. info.day = dStr.slice(0, -1);
  1088. info.year = yStr;
  1089. // info.time = null;
  1090. }
  1091. else {
  1092. console.warn("A date in the format \"MON D, YYYY\" was expected but got: \""+submittedDateStr+"\"");
  1093. }
  1094. }
  1095. else {
  1096. console.warn("There were only " + createdParts.length + " parts in the date, so it was assumed to be unparseable and skipped.");
  1097. }
  1098. }
  1099. else {
  1100. console.warn("No date string was found.");
  1101. }
  1102.  
  1103. // var licenseAnchors = getAnchorsWhereClassStartsWith(licenseAnchorPrefix);
  1104. var licenseContainerElements = document.getElementsByClassName(licenseAContainerClass);
  1105. if (licenseContainerElements.length > 0) {
  1106. info.licenses = [];
  1107. }
  1108. for (var lai=0, aMax=licenseContainerElements.length; lai < aMax; lai++) {
  1109. var thisLicenseEl = licenseContainerElements[lai];
  1110. var licenseOpener = "License: ";
  1111. var textContentTrim = thisLicenseEl.textContent.trim();
  1112. if (textContentTrim.startsWith(licenseOpener)) {
  1113. // blendswap.com format
  1114. var licenseNoAnchor = document.createElement('a');
  1115. var licenseStr = textContentTrim.substring(licenseOpener.length).trim();
  1116. var correctedLicenseStr = null;
  1117. for (const [tryBS, tryShort] of Object.entries(blendSwapNames)) {
  1118. if (tryBS == licenseStr) {
  1119. correctedLicenseStr = tryShort;
  1120. licenseNoAnchor.shortLicenseStr = correctedLicenseStr;
  1121. break;
  1122. }
  1123. }
  1124. if (correctedLicenseStr == null) {
  1125. alert("Error in " + myName + ": \""+licenseStr+"\" is not a known license even in known parts of blendswaps incorrect CC name formatting so it can't be corrected.")
  1126. }
  1127. // urlSmallNames has url / and following as key and name as value:
  1128. // licenseNoAnchor.href = undefined;
  1129. var licenseHref = undefined;
  1130. for (const [tryKey, tryValue] of Object.entries(urlSmallNames)) {
  1131. if (tryValue == correctedLicenseStr) {
  1132. licenseHref = tryKey; // Don't set licenseNoAnchor here or the partial URL will automatically get the BaseURL from the DOM!
  1133. break;
  1134. }
  1135. }
  1136. if (licenseHref) {
  1137. if (!licenseHref.includes(":")) {
  1138. if (licenseHref.startsWith("/")) {
  1139. licenseHref = "https://creativecommons.org/licenses" + licenseHref;
  1140. }
  1141. else {
  1142. licenseHref = "https://" + licenseHref;
  1143. }
  1144. }
  1145. // else has ":" so assume the href is correct now.
  1146. if (licenseHref.slice(-2, -1) == ".") { // -2 instead of -3 since using this scripts own URL substrings in the case that the URL is being generated
  1147. licenseNoAnchor.exactLicenseVersion = licenseHref.slice(-4, -1);
  1148. }
  1149. else {
  1150. console.warn("slice at -2 is not '.' but '" + licenseHref.slice(-2, -1) + "'. The license version seems to be malformed: \"" + licenseHref + "\"");
  1151. }
  1152. }
  1153. else {
  1154. alert("Error in " + myName + ": urlSmallNames had no key with a value matching the license \""+correctedLicenseStr+"\".");
  1155. }
  1156. if (verbose) {
  1157. console.log("* writing license with href: " + licenseHref)
  1158. console.log(" * with shortLicenseStr: " + licenseNoAnchor.shortLicenseStr)
  1159. }
  1160. licenseNoAnchor.href = licenseHref;
  1161. info.licenses.push(licenseNoAnchor);
  1162. continue;
  1163. }
  1164. else {
  1165. continue; // skip non-blendswap format
  1166. }
  1167. var licenseAnchors = licenseContainerElements[lai].children;
  1168. if ((licenseAnchors.length > 0) && (licenseAnchors[0].href)) {
  1169. console.log("Checking " + licenseAnchors.length + " license anchors...");
  1170. var licenseA = licenseAnchors[0];
  1171. licenseA.exactLicenseVersion = null;
  1172. if (verbose) {
  1173. console.log(" checking " + licenseA.className + "...");
  1174. // NOTE: .getAttribute("href") gets the raw value, but .href gets the resulting full URL.
  1175. console.log(" licenseA.href is a " + typeof licenseA.href);
  1176. console.log(" licenseA.href.toString is a " + typeof licenseA.href.toString);
  1177. console.log(" licenseA.href.toString().includes is a " + typeof licenseA.href.toString().includes);
  1178. }
  1179. if (licenseA.href === undefined) {
  1180. console.warn("A license a.href is undefined.");
  1181. }
  1182. // else if (typeof licenseA.href.toString !== 'function') {
  1183. // console.warn("A license a.href.toString is not a function.");
  1184. // }
  1185. else if (typeof licenseA.href.includes !== 'function') {
  1186. // NOTE: Firefox 48 removes the "contains" prototype--you must use includes!
  1187. // console.warn("A license a.getAttribute(\"href\").includes is not a function.");
  1188. console.warn("A license a.href.toString.includes is not a function.");
  1189. }
  1190. else {
  1191. if (licenseA.href.includes("opengameart.org") || licenseA.href.startsWith("/")) {
  1192. console.warn("The license anchor should be an external link to the license but is \"" + licenseA.href + "\"");
  1193. }
  1194. if (verbose) {
  1195. console.log("licenseA.href: '''", );
  1196. console.log(licenseA.href);
  1197. console.log("'''");
  1198. }
  1199. if (licenseA.href.slice(-3, -2) == ".") {
  1200. licenseA.exactLicenseVersion = licenseA.href.slice(-4, -1);
  1201. }
  1202. else {
  1203. console.warn("slice at -3 is not '.' but '" + licenseA.href.slice(-3, -2) + "'. The license version seems to be malformed: \"" + licenseA.href + "\"");
  1204. }
  1205. info.licenses.push(licenseA);
  1206. }
  1207.  
  1208. }
  1209. else {
  1210. console.warn("There is no anchor in a class like "+licenseAContainerClass+"*" + "[" + lai + "]");
  1211. }
  1212. }
  1213. if (info.licenses) {
  1214. if (info.licenses.length == 0) {
  1215. info.licenses = undefined;
  1216. }
  1217. }
  1218. var attributionAncestorClassName = "field-name-field-copyright-notice";
  1219. // ^ Can also be field-name-field-art-attribution (content: "Attribution Instructions")
  1220. var attributionAncestorElements = document.getElementsByClassName(attributionAncestorClassName);
  1221. if (attributionAncestorElements.length < 1) {
  1222. // The field is optional, so only warn if verbose.
  1223. if (verbose) {
  1224. // console.log("INFO: class \"" + attributionAncestorClassName + "\" is not present (This is an optional field).");
  1225. }
  1226. attributionAncestorClassName = "field-name-field-art-attribution";
  1227. attributionAncestorElements = document.getElementsByClassName(attributionAncestorClassName);
  1228. if (attributionAncestorElements.length < 1) {
  1229. if (verbose) {
  1230. // console.log("INFO: class \"" + attributionAncestorClassName + "\" is not present (This is an optional field).");
  1231. }
  1232. return info;
  1233. }
  1234. else {
  1235. info.goodAttributionNoticeFlag = "Attribution Instructions"
  1236. }
  1237. }
  1238. else {
  1239. info.goodAttributionNoticeFlag = "Copyright/Attribution Notice";
  1240. }
  1241.  
  1242. if (!attributionAncestorElements[0].children[0].textContent.includes(info.goodAttributionNoticeFlag)) {
  1243. console.warn("The " + attributionAncestorClassName + ".firstChild should contain \"" + info.goodAttributionNoticeFlag + "\" but is \"" + attributionAncestorElements[0].children[0].textContent + "\"")
  1244. }
  1245. var attributionE = attributionAncestorElements[0].children[1].firstChild;
  1246. // ^ Such as <div class="field field-name-field-copyright-notice field-type-text-long field-label-above"> <!--attributionAncestorElements[0]-->
  1247. // <div class="field-label">Copyright/Attribution Notice:&nbsp;</div> <!--attributionAncestorElements[0].children[0]-->
  1248. // <div class="field-items"> <!--attributionAncestorElements[0].children[1]-->
  1249. // <div class="field-item even">&quot;Adobe Town Set&quot;
  1250. // Artist: bluecarrot16
  1251. info.attributionNotice = attributionE.textContent;
  1252. return info;
  1253. }
  1254.  
  1255. function addButton() {
  1256. 'use strict';
  1257. var pageInfoE = getButtonContainer();
  1258. if (pageInfoE == null) {
  1259. var msg = 'The '+buttonContainerClassName+' class was not found for placing the button!';
  1260. console.log(msg);
  1261. pageInfoE = document.getElementsByTagName("body")[0];
  1262. // return;
  1263. }
  1264.  
  1265. //or:
  1266. // See https://www.w3schools.com/jsref/met_document_createelement.asp
  1267. var btn = document.createElement("BUTTON"); // Create a <button> element
  1268. btn.setAttribute("class", "btn btn-secondary");
  1269. btn.setAttribute("style", "background-color: rgb(50%, 50%, 50%)");
  1270. var btnText = "Copy Markdown";
  1271. btn.innerHTML = btnText; // Insert text
  1272. // Any URL starting with a slash comes after: "https://creativecommons.org/licenses"
  1273. // otherwise it comes after "https://"
  1274. // - A list of CC licenses is at <https://creativecommons.org/about/cclicenses/>.
  1275.  
  1276. btn.addEventListener("click", function(){
  1277. btn.innerHTML = btnText;
  1278. var info = getInfo();
  1279. if (info.licenses) {
  1280. for (var i=0, max=info.licenses.length; i < max; i++) {
  1281. populateCorrespondingLicenseFields(info.licenses[i]);
  1282. }
  1283. }
  1284. else {
  1285. alert("Error in " + myName + ": The license was not detected.");
  1286. }
  1287. var markdownStr = getMarkdown(info);
  1288. var nyiMsg = "";
  1289. if (!info[programVersionTerm]) nyiMsg += "\n- "+programVersionTerm+" "; // get list-group-item with textContent "Blender " // followed by version
  1290. if (!info[mediumTerm]) nyiMsg += "\n- "+mediumTerm+": "; // get list-group-item with textContent "Render: " // followed by renderer
  1291. if (!info.authorE || info.authorE.textContent.trim().length < 2) {
  1292. nyiMsg += "\n- Author: ";
  1293. }
  1294. if (!info.title || info.title.trim().length < 2) {
  1295. nyiMsg += "\n- Title: ";
  1296. }
  1297. if (!info.licenses || info.licenses.length < 1) {
  1298. nyiMsg += "\n- License: ";
  1299. }
  1300. info.licenses
  1301. if (!info.description) nyiMsg += "\n\n\n## Description";
  1302. if (nyiMsg.length > 0) {
  1303. // markdownStr = "\nError in " + myName + ": NotYetImplemented: " + nyiMsg + "\n\n\n" + markdownStr;
  1304. markdownStr += "\n\nError in " + myName + ": Everything below is NotYetImplemented so you'll have to copy it manually.\n\n" + nyiMsg + "\n";
  1305. }
  1306.  
  1307. setClipboardText(markdownStr, btn);
  1308. }); // end addEventListener click
  1309. // pageInfoE.appendChild(btn); // Append <button> for Markdown to whatever element was selected.
  1310. pageInfoE.prepend(btn); // prepend <button> for Markdown to whatever element was selected.
  1311. }//end addButton
  1312. function checkIfComplete() {
  1313. // console.log("Monitoring page loading...");
  1314. var ready = true;
  1315. if (!hasAllClasses(doneClasses)) {
  1316. ready = false;
  1317. }
  1318. if (ready) {
  1319. if (verbose) {
  1320. console.log("The page has loaded.");
  1321. }
  1322. clearInterval(checkTimer);
  1323. addButton();
  1324. console.log("The license detection will resume after a user clicks the generated \"Copy\" button.");
  1325. }
  1326. else {
  1327. console.log("The document is not ready (or is missing required fields)...");
  1328. }
  1329. }
  1330. checkTimer = setInterval(checkIfComplete, 200);
  1331. })();