Tampermonkey Support Library

to reduce repetition in creating scripts for Tampermonkey support

当前为 2022-06-15 提交的版本,查看 最新版本

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.cn-greasyfork.org/scripts/23115/1061283/Tampermonkey%20Support%20Library.js

  1. var areTmClassesAdded = false;
  2. var isTamperClickAdded = false;
  3. var defaultTamperlabelBkgd = 'radial-gradient(circle, rgba(63,94,251,1) 0%, rgba(0,0,0,0) 50%)';
  4. var tm = {
  5. addGlobalStyle: function (css) {
  6. var head, style;
  7. head = document.getElementsByTagName('head')[0];
  8. if (!head) { return; }
  9. style = document.createElement('style');
  10. style.type = 'text/css';
  11. style.innerHTML = css;
  12. head.appendChild(style);
  13. },
  14. log: function (msg) {
  15. console.log('Tampermonkey: ' + msg);
  16. },
  17. selectText: function (targetClass) {
  18. var textToCopy, range;
  19. try {
  20. if (document.selection) {
  21. range = document.body.createTextRange();
  22. range.moveToElementText(document.getElementsByClassName(targetClass)[0]);
  23. range.select();
  24. } else if (window.getSelection) {
  25. var selection = window.getSelection();
  26. range = document.createRange();
  27. range.selectNode(document.getElementsByClassName(targetClass)[0]);
  28. selection.removeAllRanges();
  29. selection.addRange(range);
  30. }
  31. } catch (err) {
  32. tm.log('Failed to select text');
  33. }
  34. },
  35. sysFriendly: function (el) {
  36. var text = $(el).text();
  37. $(el).text(text.replace(/\/|\:|\<|\>|\\|\||\*|\?/g, '-'));
  38. },
  39. ping: function (ip, callback) { // via http://jsfiddle.net/GSSCD/203/
  40. if (!this.inUse) {
  41. this.status = 'unchecked';
  42. this.inUse = true;
  43. this.callback = callback;
  44. this.ip = ip;
  45. var _that = this;
  46. this.img = new Image();
  47.  
  48. this.img.onload = function () {
  49. _that.inUse = false;
  50. _that.callback('responded');
  51. };
  52.  
  53. this.img.onerror = function (e) {
  54. if (_that.inUse) {
  55. _that.inUse = false;
  56. _that.callback('error', e);
  57. }
  58. };
  59.  
  60. this.start = new Date().getTime();
  61. this.img.src = "http://" + this.ip;
  62. this.timer = setTimeout(function () {
  63. if (_that.inUse) {
  64. _that.inUse = false;
  65. _that.callback('timeout');
  66. }
  67. }, 1500);
  68. }
  69. },
  70. isTagIn: function (targetString, tags) {
  71. var isFound = false;
  72. _.each(tags, function scanTarget (tag) {
  73. if (targetString.toLowerCase().indexOf(tag.toLowerCase()) > -1) {
  74. isFound = true;
  75. }
  76. });
  77. return isFound;
  78. },
  79. copyTextToClipboard: function (text) {
  80. var copyFrom = document.createElement("textarea");
  81. copyFrom.textContent = text;
  82. var body = document.getElementsByTagName('body')[0];
  83. body.appendChild(copyFrom);
  84. copyFrom.select();
  85. document.execCommand('copy');
  86. body.removeChild(copyFrom);
  87. },
  88. showModal: function (modalId, modalBody) {
  89. if ($('#' + modalId).is(":visible")) {
  90. $('#' + modalId).remove();
  91. } else {
  92. $('body').append('<div role="dialog" aria-label="modal for ' + modalId + '" class="popupDetailWindow" id="' + modalId + '">' +
  93. ' <div class="popupDetailTitle">&nbsp;</div>' +
  94. ' <div class="popupDetailContent fingery tamperModalClose" style="text-align:right;" onclick="$(this).parent().remove()">[CLOSE]</div>' +
  95. ' ' + modalBody +
  96. '</div>');
  97. }
  98. },
  99. waitTimers: [],
  100. getContainer: function (opts) {
  101. var options = {
  102. id: opts.id ? opts.id : opts.el.replace(/[ (:)]/g, ''),
  103. el: opts.el,
  104. try: 0,
  105. max: opts.max ? opts.max : 20,
  106. spd: opts.spd ? opts.spd : 500
  107. };
  108. clearTimeout(tm.waitTimers[options.id]);
  109. return new Promise(function (resolve, reject) {
  110. tm.waitForContainerElement(resolve, options);
  111. });
  112. },
  113. getElementsByText: function (str, tag) {
  114. var tagIndex = 0,
  115. foundElements,
  116. tagRange = ['a', 'button', 'input', 'div', 'span'],
  117. checkTag = function (checkStr, checkTag) {
  118. utils.log('checkTag( ' + checkStr + ', ' + checkTag + ')');
  119. return Array.prototype.slice.call(document.getElementsByTagName(checkTag)).filter(el => el.textContent.trim() === checkStr.trim());
  120. };
  121. if (tag != null) {
  122. return checkTag(str, tag);
  123. } else {
  124. while ((foundElements == null || foundElements.length === 0) && tagIndex < tagRange.length) {
  125. foundElements = checkTag(str, tagRange[tagIndex]);
  126. tagIndex++;
  127. };
  128. return foundElements;
  129. }
  130. },
  131. waitForContainerElement: function (resolve, options) {
  132. var $configElement = $(options.el);
  133. if ($configElement.length === 0) {
  134. options.try++;
  135. if (options.try < options.max) {
  136. tm.waitTimers[options.id] = setTimeout(tm.waitForContainerElement.bind(this, resolve, options), options.spd);
  137. } else {
  138. $('#output').val($('#output').val() + 'Maximum searches reached\n');
  139. }
  140. } else {
  141. resolve($configElement);
  142. }
  143. },
  144. copyPreferences: function (prefs) {
  145. tm.copyTextToClipboard(JSON.stringify(prefs));
  146. $.growl.notice({'message': 'Favorites copied to Clipboard'});
  147. },
  148. pastePreferences: function (prefsName) {
  149. let newPrefs = prompt('Paste your preferences JSON');
  150. if (newPrefs) {
  151. let confirmation = confirm('Overwrite current settings?');
  152. if (confirmation) {
  153. tm.savePreferences(prefsName, newPrefs);
  154. }
  155. }
  156. },
  157. savePreferences: function (name, value) {
  158. GM_setValue(name, JSON.stringify(value));
  159. },
  160. erasePreferences: function (name) {
  161. GM_setValue(name, JSON.stringify({}));
  162. },
  163. prettyPrint: function (jsonString) {
  164. try {
  165. var obj = JSON.parse(jsonString);
  166. var pretty = JSON.stringify(obj, undefined, 4);
  167. return(pretty);
  168. } catch (e) {
  169. return jsonString;
  170. }
  171. },
  172. setTamperIcon: function (global) {
  173. var scriptName = global.ids != null ? global.ids.scriptName : global.scriptName,
  174. prefsName = global.ids != null ? global.ids.prefsName : global.prefsName,
  175. memsName = global.ids != null ? global.ids.memsName : global.memsName,
  176. mems = global.mems,
  177. prefs = global.prefs,
  178. notes = global.notes;
  179. if (!scriptName || !prefsName || !prefs) {
  180. tm.log('setTamperIcon not properly configured; please send entire global object');
  181. return;
  182. }
  183. // Add Tampermonkey Icon with label to identify this script
  184. if($('.tamperlabel').length > 0) {
  185. if ($('.tamperlabel').prop('title').indexOf(scriptName) === -1) {
  186. $('.tamperlabel').prop('title', $('.tamperlabel').prop('title') + ' | ' + scriptName);
  187. }
  188. } else {
  189. $('body').append('<span class="tamperlabel" title="Tampermonkey scripts: ' + scriptName + '"></span>');
  190. }
  191. if (prefsName != null && prefs != null && !global.handlePrefsLocally) {
  192. var tamperAction = function () {
  193. var modalId = scriptName.replace(/\s/g, '') + 'Options',
  194. modalBody = '',
  195. notesInsert = '';
  196. modalBody += '<div class="popupDetailTitle">' + modalId + '</div><div class="popupDetailContent">&nbsp;</div>';
  197. _.each(prefs, function (value, key) {
  198. if (Array.isArray(value) || typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
  199. var thisVal = typeof value === 'object' ? JSON.stringify(value) : value;
  200. modalBody +=
  201. '<div class="popupDetailTitle">' + key + '</div>' +
  202. '<div class="popupDetailContent">';
  203. if (typeof (value) === 'boolean') {
  204. modalBody += ' <button id="toogle' + key + '" class="dds__button dds__button--mini" value="' + value + '">' + value + '</button>';
  205. } else {
  206. modalBody += ' <textarea style="width:100%" id="' + key + '" type="text">' + tm.prettyPrint(thisVal) + '</textarea>';
  207. }
  208. modalBody += '</div>';
  209. } else {
  210. _.each(value, function (value2, key2) {
  211. var thisVal2 = typeof value2 === 'object' ? JSON.stringify(value2) : value2;
  212. modalBody +=
  213. '<div class="popupDetailTitle">' + key2 + '</div>' +
  214. '<div class="popupDetailContent">' +
  215. ' <textarea style="width:100%" id="' + key2 + '" type="text">' + thisVal2 + '</textarea>' +
  216. '</div>';
  217. });
  218. }
  219. });
  220. if (notes != null) {
  221. modalBody += ' <div class="popupDetailTitle">&nbsp;</div><div class="popupDetailContent" style="margin-top:20px;">&nbsp;</div>';
  222. _.each(notes.messages, function (note) {
  223. modalBody += ' <div class="popupDetailTitle tmNote">NOTE:</div><div class="popupDetailContent tmNote">' + note + '</div>';
  224. });
  225. notesInsert = ' <button aria-label="Erase Tampermonkey Session Notes" class="dds__button dds__button--mini notery tBtn">Erase Notes</button>';
  226. }
  227. modalBody += '<div class="popupDetailTitle">&nbsp;</div><div class="popupDetailContent" style="text-align:right;">' +
  228. notesInsert +
  229. ' <button aria-label="Reset Plugin Memory" class="dds__button dds__button--mini memery tBtn">Reset Memory</button>' +
  230. ' <button aria-label="Copy Your Favorites to Clipboard" class="dds__button dds__button--mini copyify tBtn">Copy Preferences</button>' +
  231. ' <button aria-label="Import Your Favorites" class="dds__button dds__button--mini pasteify tBtn">Import Preferences</button>' +
  232. ' <button aria-label="Reset Plugin Preferences" class="dds__button dds__button--mini resetery tBtn tBtnMain">Reset Preferences</button>' +
  233. ' <button aria-label="Save Plugin Preferences" class="dds__button dds__button--mini savery tBtn tBtnMain">Save</button>' +
  234. ' <button aria-label="Close Preference Window" class="dds__button dds__button--mini uiClosify tBtn">Close</button>' +
  235. '</div>';
  236.  
  237. tm.showModal(modalId, modalBody);
  238. _.each(prefs, function (value, key) {
  239. if (typeof value === 'boolean') {
  240. $('#toogle' + key).on('click', function (e) {
  241. prefs[key] = !($('#toogle' + key).val() === 'true');
  242. $('#toogle' + key).val(prefs[key]).text(prefs[key]);
  243. tm.savePreferences(prefsName, prefs);
  244. });
  245. }
  246. });
  247.  
  248. // hide the default popup Close because for some weird reason it's not working
  249. $('.popupDetailContent.fingery').hide();
  250.  
  251. $('.savery').on('click', function () {
  252. _.each(prefs, function (value, key) {
  253. if (typeof (value) === 'boolean') {
  254. prefs[key] = $('#toogle' + key).val();
  255. } else {
  256. prefs[key] = $('#' + key).val();
  257. }
  258. });
  259. tm.savePreferences(prefsName, prefs);
  260. alert('Prefernces saved. You may need to refresh.');
  261. });
  262. $('.resetery').on('click', function () {
  263. tm.erasePreferences(prefsName);
  264. alert('Preferences erased. You may need to refresh.');
  265. });
  266. $('.memery').on('click', function () {
  267. tm.erasePreferences(memsName);
  268. alert('Page memory erased.');
  269. });
  270. $('.copyify').on('click', function (e) {
  271. tm.copyPreferences(prefs);
  272. });
  273. $('.pasteify').on('click', function (e) {
  274. tm.pastePreferences(prefsName);
  275. });
  276. $('.uiClosify').on('click', function () {
  277. $('#' + modalId).remove();
  278. });
  279. $('.notery').on('click', function () {
  280. $('.tmNote').remove();
  281. global.notes = null;
  282. tm.initNotes(global);
  283. });
  284.  
  285. return false;
  286. };
  287. if (!isTamperClickAdded) {
  288. $('.tamperlabel').unbind('click').click(tamperAction);
  289. isTamperClickAdded = true;
  290. }
  291. }
  292. },
  293. initNotes: function (global) {
  294. if (global.notes == null) {
  295. global.notes = {
  296. messages: [],
  297. notifiedCount: 0
  298. };
  299. }
  300. },
  301. checkNotes: function (global) {
  302. tm.initNotes(global);
  303. if (global.notes.messages.length !== global.notes.notifiedCount) {
  304. global.notes.notifiedCount = global.notes.messages.length;
  305. var blinkNotify = function () {
  306. if ($('.tamperlabel').css('background-color') === 'rgb(255, 0, 0)') {
  307. $('.tamperlabel').css('background-color', 'transparent');
  308. } else {
  309. $('.tamperlabel').css('background-color', 'rgb(255, 0, 0)');
  310. }
  311. }
  312. setTimeout(function () {
  313. for (var intI = 0; intI < 4; intI ++) {
  314. setTimeout(blinkNotify, 1000 * intI);
  315. };
  316. }, 3000);
  317. }
  318. },
  319. addNote: function (global, theNote) {
  320. var theType,
  321. typeAddedIndex = -1,
  322. compiledNote = function () {
  323. var returnNote = global.scriptName + ': ';
  324. returnNote += (theType != null && theType.length) > 0 ? theType + ': ' : '';
  325. returnNote += theNote;
  326. return returnNote;
  327. };
  328. tm.initNotes(global);
  329. if (typeof theNote === 'object') {
  330. theType = theNote.type;
  331. theNote = theNote.note;
  332. for (var intI = 0; intI < global.notes.messages.length; intI++) {
  333. if (global.notes.messages[intI].indexOf(theType) > -1) {
  334. typeAddedIndex = intI;
  335. }
  336. }
  337. if (typeAddedIndex > -1 && global.notes.messages[typeAddedIndex] !== compiledNote()) {
  338. global.notes.messages.splice(typeAddedIndex, 1);
  339. global.notes.notifiedCount--;
  340. }
  341. }
  342. if (global.notes.messages.indexOf(compiledNote()) === -1) {
  343. global.notes.messages.push(compiledNote());
  344. }
  345. },
  346. addClasses: function () {
  347. if (!areTmClassesAdded) {
  348. areTmClassesAdded = true;
  349.  
  350. // generic
  351. tm.addGlobalStyle('.fingery { margin:0px 13px; cursor:pointer; }');
  352.  
  353. // styles for modal popup
  354. tm.addGlobalStyle('.fingery { cursor: pointer; }');
  355. tm.addGlobalStyle('.popupDetailWindow { position:fixed; z-index: 999999999; top:50px; left:50px; width:75%; height:75%; background:white; border:1px solid black; border-radius: 10px; box-shadow: 10px 10px 5px 0px rgba(0,0,0,0.75); padding:10px; font-size:1.2em; overflow-y:scroll; }');
  356. tm.addGlobalStyle('.popupDetailTitle { float:left; margin-right:10px; width:15%; margin-bottom:5px; font-weight:bold; clear:both; margin-top:2px; }'); // width:6%; min-width:100px;
  357. tm.addGlobalStyle('.popupDetailContent { float:left; width:80%; line-height:0.9em; font-size:0.9em; margin-top:5px; }');
  358. tm.addGlobalStyle('.popupDetailContent .work-item-color { display:none; }');
  359.  
  360. // tamperlabel
  361. tm.addGlobalStyle('.tamperlabel { position:fixed; z-index:999999999; bottom:0px; right:20px; left:unset; cursor: pointer; width:16px; height:16px; background: ' + defaultTamperlabelBkgd + '}');
  362.  
  363. // tamperButtons
  364. tm.addGlobalStyle('.tBtn { background-color:#6c757d !important; height:30px; font-weight:400; color:white; vertical-align:middle; height:40px; border:0; cursor:pointer; }');
  365. tm.addGlobalStyle('.tBtnMain { background-color:#007bff !important; }');
  366.  
  367. //*-jQuery-Growl-*-Copyright-2015-Kevin-Sylvestre-*-1.3.5-*/-
  368. tm.addGlobalStyle('.ontop, #growls-default, #growls-tl, #growls-tr, #growls-bl, #growls-br, #growls-tc, #growls-bc, #growls-cc, #growls-cl, #growls-cr {z-index: 99999999; position: fixed; }');
  369. tm.addGlobalStyle('#growls-default {top: 10px;right: 10px; }');
  370. tm.addGlobalStyle('#growls-tl {top: 10px;left: 10px; }');
  371. tm.addGlobalStyle('#growls-tr {top: 10px;right: 10px; }');
  372. tm.addGlobalStyle('#growls-bl {bottom: 10px;left: 10px; }');
  373. tm.addGlobalStyle('#growls-br {bottom: 10px;right: 10px; }');
  374. tm.addGlobalStyle('#growls-tc {top: 10px;right: 10px;left: 10px; }');
  375. tm.addGlobalStyle('#growls-bc {bottom: 10px;right: 10px;left: 10px; }');
  376. tm.addGlobalStyle('#growls-cc {top: 50%;left: 50%;margin-left: -125px; }');
  377. tm.addGlobalStyle('#growls-cl {top: 50%;left: 10px; }');
  378. tm.addGlobalStyle('#growls-cr {top: 50%;right: 10px; }');
  379. tm.addGlobalStyle('#growls-tc .growl, #growls-bc .growl {margin-left: auto;margin-right: auto; }');
  380. tm.addGlobalStyle('.growl {opacity: 0.8;filter: alpha(opacity=80);position: relative;border-radius: 4px;-webkit-transition: all 0.4s ease-in-out;-moz-transition: all 0.4s ease-in-out;transition: all 0.4s ease-in-out; }');
  381. tm.addGlobalStyle('.growl.growl-incoming {opacity: 0;filter: alpha(opacity=0); }');
  382. tm.addGlobalStyle('.growl.growl-outgoing {opacity: 0;filter: alpha(opacity=0); }');
  383. tm.addGlobalStyle('.growl.growl-small {width: 200px;padding: 5px;margin: 5px; }');
  384. tm.addGlobalStyle('.growl.growl-medium {width: 250px;padding: 10px;margin: 10px; }');
  385. tm.addGlobalStyle('.growl.growl-large {width: 300px;padding: 15px;margin: 15px; }');
  386. tm.addGlobalStyle('.growl.growl-default {color: #FFF;background: #7f8c8d; }');
  387. tm.addGlobalStyle('.growl.growl-error {color: #FFF;background: #C0392B; }');
  388. tm.addGlobalStyle('.growl.growl-notice {color: #FFF;background: #2ECC71; }');
  389. tm.addGlobalStyle('.growl.growl-warning {color: #FFF;background: #F39C12; }');
  390. tm.addGlobalStyle('.growl .growl-close {cursor: pointer;float: right;font-size: 14px;line-height: 18px;font-weight: normal;font-family: helvetica, verdana, sans-serif; }');
  391. tm.addGlobalStyle('.growl .growl-title {font-size: 18px;line-height: 24px; }');
  392. tm.addGlobalStyle('.growl .growl-message {font-size: 12px;line-height: 16px; }');
  393.  
  394. }
  395. }
  396.  
  397. };