Script Updater (userscripts.org)

Dead simple user script update checker for use with your user scripts. Includes simple means of showing your version history through meta tags.

אין להתקין סקריפט זה ישירות. זוהי ספריה עבור סקריפטים אחרים // @require https://update.greatest.deepsurf.us/scripts/7847/34757/Script%20Updater%20%28userscriptsorg%29.js

  1. // ==UserScript==
  2. // @name Script Updater (userscripts.org)
  3. // @namespace PhasmaExMachina
  4. // @description Dead simple user script update checker for use with your user scripts. Includes simple means of showing your version history through meta tags.
  5. // @version 1.07
  6. //
  7. // @history 1.07 Added SecretaryUpdater.installUrl property to overwrite default install location
  8. // @history 1.06 Fixed parsing of history when there's only one line
  9. // @history 1.06 Fixed conflicts when multiple scripts display update window at the same time
  10. // @history 1.04 Fixed mis-handling of DOM elements on XML pages
  11. // @history 1.03 Screen mask now fills entire document height as intended
  12. // @history 1.03 Notice is now centered and fixed regardless of scrolling
  13. // @history 1.03 Fixed minor wording error
  14. // @history 1.03 Fixed implementation of ScriptUpdater.forceNotce()
  15. // @history 1.03 Fixed implementation of callback functions
  16. // @history 1.02 Improved code performance based on community feedback
  17. // @history 1.01 Removed requirement for current version
  18. // @history 1.01 Cleaned up code
  19. // @history 1.01 Simplified metadata retrieval
  20. // @history 1.01 Updated license
  21. // @history 1.00 Initial release
  22. //
  23. // ==/UserScript==
  24.  
  25. ScriptUpdater = {
  26. version:"1.07",
  27. //------------------------------------------- "public" methods --------------------------------------
  28. check:function(scriptId, currentVersion, callback) {
  29. ScriptUpdater.initVars(scriptId, currentVersion, callback, true, false);
  30. var d = new Date();
  31. if(ScriptUpdater.getInterval() > 0 && d.getTime() - ScriptUpdater.getLastCheck() > ScriptUpdater.getInterval())
  32. ScriptUpdater.checkRemoteScript();
  33. },
  34. forceCheck:function(scriptId, currentVersion, callback) {
  35. ScriptUpdater.initVars(scriptId, currentVersion, callback, true, false);
  36. ScriptUpdater.checkRemoteScript();
  37. },
  38. getLatestVersion:function(scriptId, callback) {
  39. if(typeof(callback) != 'function')
  40. alert("ScriptUpdater error:\n\n scriptUpdater.getLatestVersion() requires a callback function as the third argument");
  41. ScriptUpdater.initVars(scriptId, callback, false, false, false);
  42. ScriptUpdater.checkRemoteScript();
  43. },
  44. forceNotice:function(scriptId, currentVersion, callback) {
  45. ScriptUpdater.initVars(scriptId, currentVersion, callback, true, true);
  46. ScriptUpdater.checkRemoteScript();
  47. },
  48. checkStored:function() {
  49. if(typeof(ScriptUpdater.scriptId) != 'undefined' && typeof(ScriptUpdater.scriptCurrentVersion) != 'undefined') {
  50. return (typeof(GM_getValue('ScriptUpdater_versionAvailable')) != 'undefined' && ScriptUpdater.scriptCurrentVersion.toString() != GM_getValue('ScriptUpdater_versionAvailable').toString());
  51. } else return false;
  52. },
  53. //------------------------------------------- "private" methods --------------------------------------
  54. $:function(id) {
  55. return document.getElementById(id);
  56. },
  57. initVars:function(scriptId, currentVersion, callbackFunction, useNotice, forceNoticeEnabled) {
  58. ScriptUpdater.scriptId = scriptId;
  59. ScriptUpdater.scriptCurrentVersion = typeof(currentVersion) != 'undefined' ? currentVersion.toString() : false;
  60. ScriptUpdater.callbackFunction = typeof(callbackFunction) == 'function' ? callbackFunction : false;
  61. ScriptUpdater.useNotice = useNotice;
  62. ScriptUpdater.forceNoticeEnabled = forceNoticeEnabled;
  63. },
  64. checkRemoteScript:function() {
  65. if(ScriptUpdater.scriptCurrentVersion && !ScriptUpdater.alreadyOffered(ScriptUpdater.scriptCurrentVersion))
  66. ScriptUpdater.addOffer(ScriptUpdater.scriptCurrentVersion);
  67. var d = new Date();
  68. ScriptUpdater.setVal('lastCheck_' + ScriptUpdater.scriptId, d.getTime());
  69. // check the userscripts.org code review page
  70. GM_xmlhttpRequest ({
  71. method: "GET",
  72. url: "http://userscripts.org/scripts/source/" + ScriptUpdater.scriptId + '.meta.js',
  73. headers: {"User-agent": "Mozilla/5.0", "Accept": "text/html"},
  74. onload: function (response) {
  75. ScriptUpdater.meta = ScriptUpdater.parseHeaders(response.responseText);
  76. // store latest version available
  77. GM_setValue('ScriptUpdater_versionAvailable', ScriptUpdater.meta.version);
  78. if(ScriptUpdater.forceNoticeEnabled || (!ScriptUpdater.alreadyOffered(ScriptUpdater.meta.version) && ScriptUpdater.useNotice)) {
  79. if(!ScriptUpdater.alreadyOffered(ScriptUpdater.meta.version))
  80. ScriptUpdater.addOffer(ScriptUpdater.meta.version);
  81. ScriptUpdater.showNotice();
  82. }
  83. if(typeof(ScriptUpdater.callbackFunction) == 'function')
  84. ScriptUpdater.callbackFunction(ScriptUpdater.meta.version);
  85. }
  86. });
  87. },
  88. parseHeaders:function(metadataBlock) {
  89. var source = metadataBlock;
  90. var headers = {};
  91. var tmp = source.match(/\/\/ ==UserScript==((.|\n|\r)*?)\/\/ ==\/UserScript==/);
  92. if (tmp) {
  93. var lines = tmp[0].match(/@(.*?)(\n|\r)/g);
  94. for (var i = 0; i < lines.length; i++) {
  95. var tmp = lines[i].match(/^@([^\s]*?)\s+(.*)/);
  96. var key = tmp[1];
  97. var value = tmp[2];
  98. if (headers[key] && !(headers[key] instanceof Array))
  99. headers[key] = new Array(headers[key]);
  100. if (headers[key] instanceof Array)
  101. headers[key].push(value);
  102. else
  103. headers[key] = value;
  104. }
  105. }
  106. return headers;
  107. },
  108. showNotice:function() {
  109. if(ScriptUpdater.meta.name && ScriptUpdater.meta.version) {
  110. GM_addStyle(
  111. "#ScriptUpdater" + ScriptUpdater.scriptId + "Mask { position:absolute; width:100%; top:0; left:0; height:100%; background-color:#000; opacity:.7; z-index:9000; } \
  112. #ScriptUpdater" + ScriptUpdater.scriptId + "Body * { border:none; font-size:12px; color:#333; font-weight:normal; margin:0; padding:0; background:none; text-decoration:none; font-family:Helvetica Neue,Arial,Helvetica,sans-serif; } \
  113. #ScriptUpdater" + ScriptUpdater.scriptId + "Body { width:500px; margin:auto; top:125px; position:fixed; left:35%; text-align:left; background:#f9f9f9; border:1px outset #333; padding:0; font-family:Arial; font-size:14px; -moz-border-radius:5px; cursor:default; z-index:9010; color:#333; padding-bottom:1em ; } \
  114. #ScriptUpdater" + ScriptUpdater.scriptId + "Body a { margin:0 .5em; text-decoration:underline; color:#000099; font-weight:bold; } \
  115. #ScriptUpdater" + ScriptUpdater.scriptId + "Body strong { font-weight:bold; } \
  116. #ScriptUpdater" + ScriptUpdater.scriptId + "Body h1 { font-size:13px; font-weight:bold; padding:.5em; border-bottom:1px solid #333; background-color:#999; margin-bottom:.75em; } \
  117. #ScriptUpdater" + ScriptUpdater.scriptId + "Body h2 { font-weight:bold; margin:.5em 1em; } \
  118. #ScriptUpdater" + ScriptUpdater.scriptId + "Body h1 a { font-size:13px; font-weight:bold; color:#fff; text-decoration:none; cursor:help; } \
  119. #ScriptUpdater" + ScriptUpdater.scriptId + "Body h1 a:hover { text-decoration:underline; } \
  120. #ScriptUpdater" + ScriptUpdater.scriptId + "Body table { width:auto; margin:0 1em; } \
  121. #ScriptUpdater" + ScriptUpdater.scriptId + "Body table tr th { padding-left:2em; text-align:right; padding-right:.5em; line-height:2em; } \
  122. #ScriptUpdater" + ScriptUpdater.scriptId + "Body table tr td { line-height:2em; font-weight:bold; } \
  123. #ScriptUpdater" + ScriptUpdater.scriptId + "Body li { list-style-type:circle; } \
  124. #ScriptUpdater" + ScriptUpdater.scriptId + "Body p { font-size:12px; font-weight:normal; margin:1em; } \
  125. #ScriptUpdater" + ScriptUpdater.scriptId + "History { margin:0 1em 1em 1em; max-height:150px; overflow-y:auto; border:1px inset #999; padding:0 1em 1em; width:448px; } \
  126. #ScriptUpdater" + ScriptUpdater.scriptId + "History ul { margin-left:2em; } \
  127. #ScriptUpdater" + ScriptUpdater.scriptId + "Close { float:right; cursor:pointer; height:14px; opacity:.5; } \
  128. #ScriptUpdater" + ScriptUpdater.scriptId + "Close:hover { opacity:.9; } \
  129. #ScriptUpdater" + ScriptUpdater.scriptId + "Footer { margin:.75em 1em; } \
  130. #ScriptUpdater" + ScriptUpdater.scriptId + "Footer input { border:1px outset #666; padding:3px 5px 5px 20px; background:no-repeat 4px center #eee; -moz-border-radius:3px; cursor:pointer; width:70px; float:right; margin-left:.5em; } \
  131. #ScriptUpdater" + ScriptUpdater.scriptId + "Footer input:hover { background-color:#f9f9f9; } \
  132. #ScriptUpdater" + ScriptUpdater.scriptId + "Footer select { border:1px inset #666; }"
  133. );
  134. var noticeBg = document.createElement('div');
  135. noticeBg.id = "ScriptUpdater" + ScriptUpdater.scriptId + "Mask";
  136. document.body.appendChild(noticeBg);
  137. var noticeWrapper = document.createElement('div');
  138. noticeWrapper.setAttribute('style', 'position:absolute; width:100%; top:0; left:0; z-index:9010; max-width:auto; min-width:auto; max-height:auto; min-height:auto;');
  139. noticeWrapper.id = "ScriptUpdater" + ScriptUpdater.scriptId + "BodyWrapper";
  140. var html = new Array();
  141. var notice = document.createElement('div');
  142. notice.id = "ScriptUpdater" + ScriptUpdater.scriptId + "Body";
  143. html.push('<h1><img id="ScriptUpdater' + ScriptUpdater.scriptId + 'Close" src="');
  144. html.push(ScriptUpdater.icons.close);
  145. html.push('" title="Close"/><img src="');
  146. html.push(ScriptUpdater.icons.uso);
  147. html.push('" align="absmiddle" style="margin-top:-2px;"/><a href="http://userscripts.org/scripts/show/57756" target="_blank" title="About the Userscripts.org Script Updater v');
  148. html.push(ScriptUpdater.meta.version);
  149. html.push('">Userscripts.org Updater</a></h1>');
  150. if(!ScriptUpdater.forceNoticeEnabled) {
  151. html.push('<p>There is a new version of <strong><a href="http://userscripts.org/scripts/show/');
  152. html.push(ScriptUpdater.scriptId);
  153. html.push('" target="_blank" title="Go to script page">');
  154. html.push(ScriptUpdater.meta.name);
  155. html.push('</a> </strong> available for installation.</p>');
  156. } else {
  157. html.push('<p><strong><a href="http://userscripts.org/scripts/show/');
  158. html.push(ScriptUpdater.scriptId);
  159. html.push('" target="_blank" title="Go to script page" style="margin:0; padding:0;">');
  160. html.push(ScriptUpdater.meta.name);
  161. html.push('</a> </strong></p>');
  162. }
  163. if(ScriptUpdater.scriptCurrentVersion) {
  164. html.push('<p>You currently have version <strong>');
  165. html.push(ScriptUpdater.scriptCurrentVersion)
  166. html.push('</strong> installed. The latest version is <strong>');
  167. html.push(ScriptUpdater.meta.version);
  168. html.push('</strong></p>');
  169. }
  170. if(ScriptUpdater.meta.history) {
  171. html.push('<h2>Version History:</h2><div id="ScriptUpdater' + ScriptUpdater.scriptId + 'History">');
  172. var history = new Array();
  173. var version, desc;
  174. if(typeof(ScriptUpdater.meta.history) != 'string') {
  175. for(var i = 0; i < ScriptUpdater.meta.history.length; i++) {
  176. var tmp = ScriptUpdater.meta.history[i].match(/(\S+)\s+(.*)$/);
  177. version = tmp[1];
  178. change = tmp[2];
  179. history[version] = typeof(history[version]) == 'undefined' ? new Array() : history[version];
  180. history[version].push(change);
  181. }
  182. } else {
  183. var tmp = ScriptUpdater.meta.history.match(/(\S+)\s+(.*)$/);
  184. version = tmp[1];
  185. change = tmp[2];
  186. history[version] = typeof(history[version]) == 'undefined' ? new Array() : history[version];
  187. history[version].push(change);
  188. }
  189. for(var v in history) {
  190. html.push('<div style="margin-top:.75em;"><strong>v' + v + '</strong></div><ul>');
  191. for(var i = 0; i < history[v].length; i++)
  192. html.push('<li>' + history[v][i] + '</li>');
  193. html.push('</ul>');
  194. }
  195. html.push('</div>');
  196. }
  197. /*
  198. */
  199. html.push('<div id="ScriptUpdater' + ScriptUpdater.scriptId + 'Footer">');
  200. html.push('<input type="button" id="ScriptUpdater' + ScriptUpdater.scriptId + 'CloseButton" value="Close" style="background-image:url(');
  201. html.push(ScriptUpdater.icons.close);
  202. html.push(')"/><input type="button" id="ScriptUpdater' + ScriptUpdater.scriptId + 'BodyInstall');
  203. html.push(ScriptUpdater.scriptId);
  204. html.push('" value="Install" style="background-image:url(');
  205. html.push(ScriptUpdater.icons.install);
  206. html.push(');"/>');
  207. html.push('Check this script for updates ');
  208.  
  209. html.push('<select id="ScriptUpdater' + ScriptUpdater.scriptId + 'Interval"> \
  210. <option value="3600000">every hour </option>\
  211. <option value="21600000">every 6 hours </option>\
  212. <option value="86400000">every day </option>\
  213. <option value="604800000">every week </option>\
  214. <option value="0">never </option>\
  215. </select>');
  216. html.push('</div>');
  217. notice.innerHTML = html.join('');
  218. noticeWrapper.appendChild(notice);
  219. document.body.appendChild(noticeWrapper);
  220. ScriptUpdater.$('ScriptUpdater' + ScriptUpdater.scriptId + 'Close').addEventListener('click', ScriptUpdater.closeNotice, true);
  221. ScriptUpdater.$('ScriptUpdater' + ScriptUpdater.scriptId + 'CloseButton').addEventListener('click', ScriptUpdater.closeNotice, true);
  222. ScriptUpdater.$('ScriptUpdater' + ScriptUpdater.scriptId + 'BodyInstall' + ScriptUpdater.scriptId).addEventListener('click', function() {
  223. setTimeout(ScriptUpdater.closeNotice, 500);
  224. document.location = typeof(ScriptUpdater.installUrl) == 'string' ? ScriptUpdater.installUrl : 'http://userscripts.org/scripts/source/' + ScriptUpdater.scriptId + '.user.js';
  225. }, true);
  226. window.addEventListener('keyup', ScriptUpdater.keyUpHandler, true);
  227. // set current interval in selector
  228. var selector = ScriptUpdater.$('ScriptUpdater' + ScriptUpdater.scriptId + 'Interval');
  229. for(var i = 0; i < selector.options.length; i++) {
  230. if(selector.options[i].value.toString() == ScriptUpdater.getInterval().toString())
  231. selector.options[i].selected = true;
  232. }
  233. selector.addEventListener('change', function() {
  234. ScriptUpdater.setInterval(this.value);
  235. }, true);
  236. noticeWrapper.style.height = document.documentElement.clientHeigh + 'px';
  237. // $('#ScriptUpdater" + ScriptUpdater.scriptId + "Body')[0].style.marginTop = (unsafeWindow.scrollY + 125) + 'px';
  238. $('#ScriptUpdater' + ScriptUpdater.scriptId + 'Mask')[0].style.height = (unsafeWindow.scrollMaxY + unsafeWindow.innerHeight) + 'px';
  239. }
  240. },
  241. closeNotice:function() {
  242. document.body.removeChild(ScriptUpdater.$('ScriptUpdater' + ScriptUpdater.scriptId + 'BodyWrapper'));
  243. document.body.removeChild(ScriptUpdater.$('ScriptUpdater' + ScriptUpdater.scriptId + 'Mask'));
  244. window.removeEventListener('keyup', ScriptUpdater.keyUpHandler, true);
  245. },
  246. keyUpHandler:function (e) {
  247. if(e.keyCode == 27) { ScriptUpdater.closeNotice(); }
  248. },
  249. getVal:function(key) {
  250. key = 'ScriptUpdator.' + key;
  251. return eval(GM_getValue(key, ('({})')));
  252. },
  253. setVal:function(key, value) {
  254. key = 'ScriptUpdator.' + key;
  255. GM_setValue(key, uneval(value));
  256. },
  257. alreadyOffered:function(version) {
  258. var offers = ScriptUpdater.getOffers();
  259. if(offers.length == 0) {
  260. ScriptUpdater.addOffer(version);
  261. return true;
  262. }
  263. for(var i = 0; i < offers.length; i++)
  264. if(version.toString() == offers[i].toString()) { return true; }
  265. return false;
  266. },
  267. getOffers:function() {
  268. var offers = ScriptUpdater.getVal('versionsOfferedFor_' + ScriptUpdater.scriptId);
  269. return (typeof(offers) == 'undefined' || typeof(offers.length) == 'undefined' || typeof(offers.push) == 'undefined') ? new Array() : offers;
  270. },
  271. addOffer:function(version) {
  272. var offers = ScriptUpdater.getOffers();
  273. offers.push(version);
  274. ScriptUpdater.setVal('versionsOfferedFor_' + ScriptUpdater.scriptId, offers);
  275. },
  276. getInterval:function() {
  277. var interval = ScriptUpdater.getVal('interval_' + ScriptUpdater.scriptId);
  278. return (typeof(interval) == 'undefined' || !interval.toString().match(/^\d+$/)) ? 86400000 : parseInt(interval.toString());
  279. },
  280. setInterval:function(interval) {
  281. ScriptUpdater.setVal('interval_' + ScriptUpdater.scriptId, parseInt(interval));
  282. },
  283. getLastCheck:function() {
  284. var lastCheck = ScriptUpdater.getVal('lastCheck_' + ScriptUpdater.scriptId);
  285. return (typeof(lastCheck) == 'undefined' || !lastCheck.toString().match(/^\d+$/)) ? 0: parseInt(lastCheck.toString());
  286. },
  287. icons:{
  288. install:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAALZSURBVBgZBcFLiFVlAADg7zzuPLzjzDjOMINMitIie5gF+UAkIZSgRQuXLZIWrY021dYIggJdJURElJsoqlWRYA9GshGFCNQeOjoTk6bjeOd5zzn/f07flzRNA459ObcHJ3cM9+1fq2prVa2qa+uh7mAZ9xCxiAV8iu9zgDqEvU9ODOx//dkxALBa1kNrZT202I2TZcVyEd28t+Lb66uHcTwHqEMYH+xJwNyDqJUk8oQsp7eV2tqbytJUK+OpyX5bhtojH07Pv58CxKoabOeEmuUy0al4UNDp0umysM5/KxG8eWbW/u1tj4+2xnKAWFUjG3tSqwWr3ShNEzmyjDQjk8gSaiRxyYUbiy7PduZzgFiW40P9mc56sFY00rSRpaQxkaVkGlmGJnNnqXDq7N9LOJYDhLLcNj7Y0uk2AjRkMZE2iGQaeZOqG2IrCmXY/s1rB+6nALEstk0M9VotG0lKliRSpEjw+YUjPjq3RxkKoSjEsoiQwvMnvusXQ09vK1VGUg1qjVrUqDWKUJoc3emVj3dbWeuEUJZLkEMoyrF2u0+aUEPD19OHNXVQ1kEZgy2bHrZzYq/l7qr766/m3VC0ub+SQyyLDXm7R56SpYlYJ0JdOvzYy2JTi3VUa8x35jwxecBKue7S7E+dXW+nI/nB42dGcWLPI1vdXmrcvBO1++iGUmxqtxb+UtVBqCtVrCwVy3Y/dNBKtZb+OjO1kMeyfA4vXLo6Y3E9t1I0qtjo6goxGB/cKtRRbGr/dmaNDEy4PHfe+etTd8vgSB6r6ukXD+3qf+ulfQDg6OnCJ7+8p6xL3VDaMfqofTuOuHhryrk/fl4tokPz7zRX8lhVM7fvdXx29qrhgX7Dg32G271OHv3dxg09entSvXnqmXcHJGm/6Ru/ad89dmrm9AdXIK9D+GLq4rXJqYvXtmEzNmMTNmGor6fV6utr6YxWfvjzR0P/vDGTh7GvAP4H2uh1wse2x/0AAAAASUVORK5CYII%3D",
  289. close:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAIhSURBVDjLlZPrThNRFIWJicmJz6BWiYbIkYDEG0JbBiitDQgm0PuFXqSAtKXtpE2hNuoPTXwSnwtExd6w0pl2OtPlrphKLSXhx07OZM769qy19wwAGLhM1ddC184+d18QMzoq3lfsD3LZ7Y3XbE5DL6Atzuyilc5Ciyd7IHVfgNcDYTQ2tvDr5crn6uLSvX+Av2Lk36FFpSVENDe3OxDZu8apO5rROJDLo30+Nlvj5RnTlVNAKs1aCVFr7b4BPn6Cls21AWgEQlz2+Dl1h7IdA+i97A/geP65WhbmrnZZ0GIJpr6OqZqYAd5/gJpKox4Mg7pD2YoC2b0/54rJQuJZdm6Izcgma4TW1WZ0h+y8BfbyJMwBmSxkjw+VObNanp5h/adwGhaTXF4NWbLj9gEONyCmUZmd10pGgf1/vwcgOT3tUQE0DdicwIod2EmSbwsKE1P8QoDkcHPJ5YESjgBJkYQpIEZ2KEB51Y6y3ojvY+P8XEDN7uKS0w0ltA7QGCWHCxSWWpwyaCeLy0BkA7UXyyg8fIzDoWHeBaDN4tQdSvAVdU1Aok+nsNTipIEVnkywo/FHatVkBoIhnFisOBoZxcGtQd4B0GYJNZsDSiAEadUBCkstPtN3Avs2Msa+Dt9XfxoFSNYF/Bh9gP0bOqHLAm2WUF1YQskwrVFYPWkf3h1iXwbvqGfFPSGW9Eah8HSS9fuZDnS32f71m8KFY7xs/QZyu6TH2+2+FAAAAABJRU5ErkJggg%3D%3D",
  290. uso:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAQCAYAAAAiYZ4HAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAh9JREFUeNp0krmLWnEQxyf7zLoajyIWXojIxkK0EiIGCRamCKQwEdIIgYQoQSR/wLY2goVVJGCa1BaL2liKBESFiOJFiMRb1xMVRbx+mfdA0RwDA4/3m+Mz3xmAf9hDNJ/P9zWXy935/f7A5eXlFfzPRCKROBgMfqvX62S5XBLabDbbh8M76zRYKpUqvF5vyGw2P+bz+cBisWCz2cB2u33wV2WFQvEoFArlW60WmUwmZLVakdFoRNxu9xd8Fp51UKlUWmS91ev11zweD5AZMAFmsxkgWhpDpsfKarVaE4lEqpVKhUynU4a73++TcrlMarUa6Xa7G7vd/u4QT93c3HzmcrlPSqUSiMVihrvX68F6vYZsNkvPcOFyuV5Uq9VuoVD4ztrv91wOhwMCgQAGgwEsFguYz+eMSyQSkMvlwGazqUAg8KnRaHSo4XA4Q9leYRdmHrpyJpMBehaDwQBCoRB2ux2gapRSqbymsP2PTqezsFqtz+6hpVIpprLRaGTw8BcgBVOo2WyOj8NbLJaP+Xx+k0gkCL00xGNEoJ2WOZlMznQ6nfVsFyaT6X273d4eAmkfj8ckHo+PNRrNSzrm4jRBq9XysDWF18Cg0OzpdPrO6XS+QRVvz6oj0nOch25NYrEYgxEOhxsymezpadyxA8p5HxUDXBTgSUA0Gv3pcDheI2LiNIE6fOAN/cKkK9RdUSwWkx6P5y0mZv+8ud8CDABidDMA4Sb2JAAAAABJRU5ErkJggg%3D%3D",
  291. },
  292.  
  293. };