Advertisement
Guest User

Untitled

a guest
Sep 29th, 2016
238
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 36.48 KB | None | 0 0
  1. // ==UserScript==
  2. // @name E-Hentai Highlighter
  3. // @namespace http://userscripts.org/users/106844
  4. // @description Highlighter for E-Hentai (e-hentai.org/exhentai.org). Supports regular expressions.
  5. // @include http://g.e-hentai.org/*
  6. // @include https://exhentai.org/*
  7. // @grant GM_getValue
  8. // @grant GM_setValue
  9. // @version 0.5.6.1
  10. // ==/UserScript==
  11.  
  12. // -------------------- DEFAULTS -------------------
  13.  
  14. var defaults = {
  15. defaultColor : '#ec7e7e' ,
  16. exColor : '#ed6464' ,
  17. highlighterEnabled : false ,
  18. filterEnabled : false ,
  19. opacityEnabled : false ,
  20. opacity : 0.1 ,
  21. showTags : true ,
  22. highlightTags : true ,
  23. reorderGalleries : false
  24. };
  25.  
  26. // -------------------- /DEFAULTS -------------------
  27. var apiurl = "http://g.e-hentai.org/api.php";
  28. var apimax = 20;
  29. var EHH = {
  30.  
  31.  
  32.  
  33. init: function() {
  34.  
  35. EHH.augmentJS();
  36.  
  37. // settings
  38.  
  39. EHH.settings = { };
  40. for (var key in defaults)
  41. EHH.settings[key] = Utils.load(key,defaults[key]);
  42. EHH.metadatas = [];
  43. EHH.dontWalk = false;
  44. EHH.onPanda = document.URL.indexOf('e-hentai') == -1;
  45. EHH.defaultColor = EHH.settings[EHH.onPanda ? 'exColor' : 'defaultColor'];
  46. EHH.thumbnails = document.querySelector('.itg .id1') !== null;
  47. EHH.gallery = document.querySelector('#taglist') !== null;
  48.  
  49. // User data
  50.  
  51. EHH.keywords = Utils.load('keywords',[ ]);
  52. EHH.filters = Utils.load('filters',[ ]);
  53.  
  54. if (Utils.onFirefox()) Utils.migrateSettings();
  55.  
  56. // Colors
  57.  
  58. var colors =
  59. EHH.onPanda ? { toggle: 'lightblue', toggleHover: 'lightcyan', disable: 'lightgreen',
  60. disableHover: 'springgreen', enable: 'salmon', enableHover: 'lightsalmon',
  61. row1: '#363940', row2: '#4F535B' }
  62. : { toggle: 'slateblue', toggleHover: 'skyblue', disable: 'forestgreen',
  63. disableHover: 'mediumseagreen', enable: 'indianred', enableHover: 'darkred',
  64. row1: '#F2F0E4', row2: '#EDEBDF' };
  65.  
  66. var format = function(text) { return text.replace(/%(\w+)/g,function(x) { return colors[x.slice(1)]; }); };
  67.  
  68. // Permanent style
  69.  
  70. var style = document.createElement('style');
  71. style.innerHTML = format(
  72. // popup (general)
  73. '#e-HentaiPopup {' +
  74. 'position: fixed; top: 0; right: 0; padding: 3px; border-radius: 0 !important;' +
  75. 'border: 1px black solid; z-index: 10; margin: 0 !important; min-width: 0 !important; width: auto !important;' +
  76. '}' +
  77. '#e-HentaiPopup:not(:hover) *:not(:first-child) { display: none; }' +
  78. '#e-HentaiPopup * {' +
  79. 'font-family: Verdana, Tahoma, Georgia, Dejavu, "Times New Roman", Serif;' +
  80. 'font-size: 10px;' +
  81. '} #e-Header { text-align: center; position: relative; }' +
  82. '[mode="default"] [mode="settings"], [mode="settings"] [mode="default"] { display: none; }' +
  83. '#e-ToggleMode {' +
  84. 'cursor: pointer; color: %toggle !important; font-weight: bold;' +
  85. 'position: absolute; right: 5px; border-bottom: 1px dotted;' +
  86. '}' +
  87. '#e-ToggleMode:hover { color: %toggleHover !important; }' +
  88. '#e-HentaiPopup div[mode] { width: 350px; text-align: left; }' +
  89. // popup (default view)
  90. '#e-HentaiPopup td:nth-child(2) { text-align: right; }' +
  91. '#e-HentaiPopup td:nth-child(2) a, #e-HentaiPopup tr:last-child a {' +
  92. 'cursor: pointer; font-weight: bold; border-bottom: 1px dotted;' +
  93. '}' +
  94. '#e-HentaiPopup .e-Disable { color: %disable; }' +
  95. '#e-HentaiPopup .e-Disable:hover { color: %disableHover; }' +
  96. '#e-HentaiPopup .e-Enable { color: %enable; }' +
  97. '#e-HentaiPopup .e-Enable:hover { color: %enableHover; }' +
  98. '#e-HentaiPopup tr:last-child a:hover { color: black; }' +
  99. '#e-HentaiPopup td > span { margin-right: 5px; float: right; }' +
  100. '#e-HentaiPopup table { width: 100%; }' +
  101. '#e-HentaiPopup textarea { width: 100%; height: 200px; box-sizing: border-box; -moz-box-sizing: border-box; }' +
  102. // popup (settings view)
  103. '#e-HentaiPopup label { display: block; padding: 2px; }' +
  104. '#e-HentaiPopup input[type="checkbox"] { margin: 0 5px 0 0; float: left; }' +
  105. '[name="slider"]:not([visible="true"]), [name="slider"]:not([visible="true"]) + span { display: none; }' +
  106. '[name="slider"] { margin-left: 20px; width: 250px; }' +
  107. '[name="slider"] + span { position: relative; bottom: 7px; }' +
  108. '#e-HentaiPopup [mode="settings"] { padding: 10px; box-sizing: border-box; -moz-box-sizing: border-box; }' +
  109. '#e-Buttons { text-align: center; padding-top: 20px; }' +
  110. '.e-Button {' +
  111. 'min-width: 100px; height: 25px; line-height: 25px; text-align: center; color: white;' +
  112. 'background: black; display: inline-block; cursor: pointer; margin-right: 10px;' +
  113. '}' +
  114. '.e-Button:hover { text-decoration: underline; }' +
  115. '.e-Button + input { display: none; }' +
  116. '#e-PickerLabel { margin-top: 10px; }' +
  117. '#e-ColorPicker + div { width: 30px; height: 18px; display: inline-block; margin-left: 10px; vertical-align: top; }' +
  118. // highlight/filter style
  119. '.e-Highlighted b { font-weight: inherit; }' +
  120. '.e-Highlighted, .e-Highlighted a, [id^="ta_"][style*="background"] { color: black !important; }' +
  121. '.e-Highlighted b { font-weight: bold !important; font-size: 115%; text-decoration: underline; }' +
  122. // tag divs
  123. '.e-Tags { position: absolute; top: 0px; left: 0px; text-align: left; color: black; ' +
  124. 'margin-left: 1px; text-shadow: -1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff, 1px 1px 0 #fff;' +
  125. 'font-weight: bold; font-family: "Segoe UI"; font-size: 12px; line-height: 11px;' +
  126. '}' +
  127. '.e-Tags > div { padding: 3px; max-width: 70px; overflow: hidden; transition: max-width .5s linear;' +
  128. 'white-space: nowrap; }' +
  129. '.id3:hover .e-Tags > div { max-width: 200px !important; }' +
  130. '.itg { display: flex; flex-flow: row wrap; }' +
  131. '.id1 { float: none !important; }'
  132. );
  133. document.head.appendChild(style);
  134.  
  135. // Mutable style
  136.  
  137. EHH.opaqueFilterCSS = document.createElement('style');
  138. EHH.opaqueFilterCSS.id = 'e-OpaqueFilter';
  139. EHH.opaqueFilterCSSMask = format('{0}#toppane ~ .c, .e-Filtered { display: none !important; }\n' +
  140. '{0}tr.color1 { background: %row1 }\n' +
  141. '{0}tr.color0 { background: %row2 }\n' +
  142. '{1}#toppane ~ .c, .e-Filtered { opacity: {opacity} !important;}\n' +
  143. '{1}.e-Filtered:hover { opacity: 1 !important; -webkit-transition: opacity .1s linear;' +
  144. '-moz-transition: opacity .1s linear; -o-transition: opacity .1s linear; }');
  145. document.head.appendChild(EHH.opaqueFilterCSS);
  146.  
  147. // Popup
  148.  
  149. EHH.generatePopup();
  150.  
  151. // Events
  152.  
  153. document.addEventListener('DOMNodeInserted',function(e) {
  154. if (e.target.nodeName == 'TBODY')
  155. EHH.walk(e.target);
  156. },false);
  157.  
  158. if (EHH.gallery)
  159. document.addEventListener('DOMNodeInserted',EHH.updateTagList,false);
  160.  
  161. if (!EHH.gallery && !EHH.thumbnails)
  162. EHH.interceptMouseHover();
  163.  
  164. // Data synchronization
  165.  
  166. EHH.link('keywords' , 'keywords' , EHH.updatePopup , EHH.clearRegexes , EHH.walk);
  167. EHH.link('filters' , 'filters' , EHH.updatePopup , EHH.clearRegexes , EHH.walk);
  168. EHH.link('defaultColor' , null , EHH.updatePopup , EHH.walk);
  169.  
  170. EHH.settings.link('defaultColor' , 'defaultColor');
  171. EHH.settings.link('exColor' , 'exColor' );
  172. EHH.settings.link('filterEnabled' , 'filterEnabled' , EHH.updatePopup , EHH.toggleOpacity , EHH.walk);
  173. EHH.settings.link('highlighterEnabled' , 'highlighterEnabled' , EHH.updatePopup , EHH.walk);
  174. EHH.settings.link('opacityEnabled' , 'opacityEnabled' , EHH.updatePopup , EHH.toggleOpacity);
  175. EHH.settings.link('opacity' , 'opacity' , EHH.updatePopup , EHH.toggleOpacity);
  176. EHH.settings.link('showTags' , 'showTags' , EHH.toggleTagDivs);
  177. EHH.settings.link('highlightTags' , 'highlightTags' , EHH.highlightTags);
  178. EHH.settings.link('reorderGalleries' , 'reorderGalleries' , EHH.walk);
  179.  
  180. // Start
  181.  
  182. EHH.toggleOpacity();
  183. EHH.walk();
  184.  
  185. },
  186.  
  187. augmentJS: function() {
  188.  
  189. /*Object.getOwnPropertyNames(Array.prototype).forEach(function(x) {
  190. NodeList.prototype[x] = Array.prototype[x];
  191. });*/
  192.  
  193. var linkedObjects = { };
  194. Object.defineProperty(Object.prototype,'link', {
  195. enumerable : false,
  196. configurable : false,
  197. writable : false,
  198. value : function(localProperty,storedProperty,onChangeCallbacks) {
  199. var currentValue = this[localProperty], args = arguments;
  200. var get = function() { return currentValue; };
  201. var set = function(value) {
  202. currentValue = value;
  203. if (storedProperty) Utils.save(storedProperty,currentValue);
  204. for (var i=2;i<args.length;++i) {
  205. if (args[i])
  206. args[i](currentValue);
  207. }
  208. };
  209. delete this[localProperty];
  210. var descriptor = { get: get, set: set, enumerable: true, configurable: true };
  211. Object.defineProperty(this,localProperty,descriptor);
  212. linkedObjects[storedProperty] = { object: this, key: localProperty };
  213. }
  214. });
  215.  
  216. if (!Utils.onFirefox()) {
  217. window.addEventListener('storage',function(e) {
  218. if (!linkedObjects.hasOwnProperty(e.key)) return;
  219. var target = linkedObjects[e.key];
  220. target.object[target.key] = JSON.parse(e.newValue);
  221. },false);
  222. }
  223.  
  224.  
  225. },
  226.  
  227. generatePopup: function() {
  228.  
  229. EHH.popup = document.createElement('div');
  230. EHH.popup.id = 'e-HentaiPopup';
  231. EHH.popup.className = 'ido';
  232. EHH.popup.setAttribute('mode','default');
  233. EHH.popup.innerHTML =
  234. '<div id="e-Header">' +
  235. '<b>E-H Highlighter</b>' +
  236. '<a id="e-ToggleMode" target="settings">Show settings</a>' +
  237. '</div><hr/>' +
  238. '<div mode="default">' +
  239. '<table align="right">' +
  240. '<tr><td style="text-align:left">Keywords:</td><td><a id="e-HighlighterSwitch">Highlighter: enabled</a></tr>' +
  241. '<tr><td colspan="2"><textarea></textarea></td></tr>' +
  242. '<tr><td style="text-align:left">Filters:</td><td><a id="e-FilterSwitch">Filter: enabled</a></td></tr>' +
  243. '<tr><td colspan="2"><textarea></textarea></td></tr>' +
  244. '<tr><td colspan="2"><a id="e-PopupSave">Save changes</a><span><b>Filtered items:</b> <span id="e-FilteredItems"></span></span></td></tr>' +
  245. '</table>' +
  246. '</div>' +
  247. '<div mode="settings">' +
  248. '<label><input type="checkbox" id="opacitySwitch"> Enable opacity mode for filtered items</label>' +
  249. '<input type="range" name="slider" min="0" max="100"> <span></span>' +
  250. '<label><input type="checkbox" id="tagDivSwitch">Display any tags matching one or more highlight keywords in front of the gallery thumbnails</label>' +
  251. '<label><input type="checkbox" id="highlightTagSwitch">Apply highlighting and filters to each gallery\'s tag list</label>' +
  252. '<label><input type="checkbox" id="reorderGalleriesSwitch">Move highlighted galleries to the top and filtered galleries to the bottom (thumbnail mode only)</label>' +
  253. '<label id="e-PickerLabel"><input type="checkbox" style="visibility: hidden">Default highlight color: <input type="color" id="e-ColorPicker"> <div></div></label>' +
  254. '<div id="e-Buttons">' +
  255. '<div class="e-Button" id="e-Export">Export data</div>' +
  256. '<div class="e-Button" id="e-Import">Import data</div><input type="file" accept="application/json">' +
  257. '</div>' +
  258. '</div>';
  259. document.body.appendChild(EHH.popup);
  260.  
  261. // Popup elements
  262.  
  263. EHH.highlighterSwitch = document.getElementById('e-HighlighterSwitch');
  264. EHH.filterSwitch = document.getElementById('e-FilterSwitch');
  265.  
  266. var textareas = Utils.query('#e-HentaiPopup textarea');
  267. EHH.highlighterArea = textareas[0];
  268. EHH.filterArea = textareas[1];
  269.  
  270. // Events (default view)
  271.  
  272. Utils.onClick(document.getElementById('e-ToggleMode'),function() {
  273. EHH.popup.setAttribute('mode',this.getAttribute('target'));
  274. var showSettings = this.getAttribute('target') == 'settings';
  275. this.innerHTML = (showSettings ? 'Show keywords' : 'Show settings');
  276. this.setAttribute('target',showSettings ? 'default' : 'settings');
  277. });
  278.  
  279. [EHH.highlighterSwitch,EHH.filterSwitch].forEach(function(x) {
  280. Utils.onClick(x,function() {
  281. var target = /Highlighter/.test(this.textContent) ? 'highlighterEnabled' : 'filterEnabled';
  282. var status = /enabled/.test(this.textContent);
  283. EHH.settings[target] = !status;
  284. });
  285. });
  286.  
  287. Utils.onClick(document.getElementById('e-PopupSave'),function() {
  288. var validate = function(regexes) { for (var i=0;i<regexes.length;++i) new RegExp(regexes[i]); };
  289. var keywords = EHH.highlighterArea.value.split(/[;\n]/).filter(function(x) { return x.length > 0; });
  290. var filters = EHH.filterArea.value.split(/[;\n]/).filter(function(x) { return x.length > 0; });
  291. try {
  292. validate(keywords);
  293. validate(filters);
  294. EHH.dontWalk = true;
  295. EHH.keywords = keywords;
  296. EHH.filters = filters;
  297. EHH.dontWalk = false;
  298. EHH.walk();
  299. } catch (e) {
  300. alert('Couldn\'t parse keyword. ' + e.message + '\nSettings have NOT been saved.');
  301. }
  302. });
  303.  
  304. // Events (settings view)
  305.  
  306. Utils.linkCheckbox(document.getElementById('opacitySwitch'),EHH.settings,'opacityEnabled');
  307. Utils.linkCheckbox(document.getElementById('tagDivSwitch'),EHH.settings,'showTags');
  308. Utils.linkCheckbox(document.getElementById('highlightTagSwitch'),EHH.settings,'highlightTags');
  309. Utils.linkCheckbox(document.getElementById('reorderGalleriesSwitch'),EHH.settings,'reorderGalleries');
  310.  
  311. // Opacity slider
  312.  
  313. EHH.slider = EHH.popup.querySelector('[name="slider"]');
  314. if (EHH.slider.type != 'range') EHH.slider = null; // not supported
  315. else {
  316. EHH.slider.value = EHH.settings.opacity * 100;
  317. EHH.slider.nextElementSibling.innerHTML = (Math.floor(EHH.settings.opacity * 10000) / 100) + '%';
  318. EHH.slider.addEventListener('change',function(e) {
  319. e.target.nextElementSibling.innerHTML = e.target.value + '%';
  320. EHH.settings.opacity = parseInt(e.target.value,10) / 100;
  321. },false);
  322. }
  323.  
  324. // Color picker
  325.  
  326. var picker = document.getElementById('e-ColorPicker'),
  327. preview = picker.nextElementSibling,
  328. supported = picker.type == 'color';
  329.  
  330. picker.value = preview.style.backgroundColor = EHH.defaultColor;
  331. if (supported) picker.style.cssText = 'padding: 0px; border: 0px; background: none; position: relative; top: 3px';
  332. else picker.style.cssText = 'width: 60px; color: black';
  333. preview.style.cssText = (supported ? 'display: none;' : 'background-color: ' + EHH.defaultColor);
  334.  
  335. var lastColor = preview.style.backgroundColor;
  336. picker.addEventListener(supported ? 'change' : 'input',function() {
  337. preview.style.backgroundColor = picker.value;
  338. if (preview.style.backgroundColor == lastColor) return;
  339. lastColor = preview.style.backgroundColor;
  340. EHH.settings[EHH.onPanda ? 'exColor' : 'defaultColor'] = picker.value;
  341. EHH.defaultColor = picker.value;
  342. },false);
  343.  
  344. // Import-export functions
  345.  
  346. var importButton = document.getElementById('e-Import'), importInput = importButton.nextElementSibling;
  347. Utils.onClick(importButton,function() { importInput.click(); });
  348. importInput.addEventListener('change',function(e) {
  349. var reader = new FileReader();
  350. reader.onerror = function(e) { alert('Couldn\'t read the selected file.'); };
  351. reader.onload = function(e) {
  352. try {
  353. var data = JSON.parse(this.result);
  354. if (!data.keywords || !data.filters || !data.settings) throw null;
  355. var confirmation = confirm('This will overwrite your data. Are you sure you want to proceed?');
  356. if (confirmation) {
  357. EHH.dontWalk = true;
  358. EHH.keywords = data.keywords;
  359. EHH.filters = data.filters;
  360. for (var key in EHH.settings) EHH.settings[key] = data.settings[key];
  361. setTimeout(function() { window.location.reload(); },50);
  362. }
  363. }
  364. catch (_) { alert('Couldn\'t recognize the selected file.'); }
  365. };
  366. reader.readAsText(this.files[0]);
  367. },false);
  368.  
  369. Utils.onClick(document.getElementById('e-Export'),function() {
  370. var result = { keywords: EHH.keywords, filters: EHH.filters, settings: EHH.settings };
  371. var blob = new Blob([JSON.stringify(result,null,2)],{ type: 'application/json' });
  372. var a = document.createElement('a');
  373. a.href = URL.createObjectURL(blob);
  374. a.download = 'EHH.settings.' + (new Date().valueOf()) + '.json';
  375. document.body.appendChild(a);
  376. a.click();
  377. document.body.removeChild(a);
  378. });
  379.  
  380. EHH.updatePopup();
  381.  
  382. },
  383.  
  384. updatePopup: function() {
  385.  
  386. var updateSwitch = function(target,enable) {
  387. target.textContent = target.textContent.replace(/[^\s]+$/,enable ? 'enabled' : 'disabled');
  388. target.className = enable ? 'e-Disable' : 'e-Enable';
  389. var filtered = document.getElementsByClassName('e-Filtered').length;
  390. document.getElementById('e-FilteredItems').textContent = filtered;
  391. };
  392.  
  393. EHH.highlighterArea.textContent = EHH.keywords.join('\n');
  394. EHH.filterArea.textContent = EHH.filters.join('\n');
  395. updateSwitch(EHH.highlighterSwitch,EHH.settings.highlighterEnabled);
  396. updateSwitch(EHH.filterSwitch,EHH.settings.filterEnabled);
  397.  
  398. document.getElementById('opacitySwitch').checked = EHH.settings.opacityEnabled;
  399.  
  400. },
  401.  
  402. toggleOpacity: function() {
  403. // changes the mutable style to enable or disable opacity
  404. EHH.opaqueFilterCSS.innerHTML = EHH.opaqueFilterCSSMask
  405. .replace(/\{0\}/g,EHH.settings.opacityEnabled ? '//' : '')
  406. .replace(/\{1\}/g,EHH.settings.opacityEnabled ? '' : '//')
  407. .replace(/\{opacity\}/,EHH.settings.opacity);
  408. if (EHH.slider) EHH.slider.setAttribute('visible',EHH.settings.opacityEnabled);
  409. },
  410.  
  411. toggleTagDivs: function() {
  412. Utils.query('.e-Tags').forEach(function(x) {
  413. x.style.display = EHH.settings.showTags ? null : 'none';
  414. });
  415. },
  416.  
  417. clearRegexes: function() {
  418. EHH.parse.regexes = null;
  419. },
  420.  
  421. prepareRegexes: function() {
  422.  
  423. /* returns an object containing two properties:
  424. * highlight : an array of keywords; each element is an object with a "type" property ("tag" or "title"),
  425. * a "regex" property and a "color" property (null if no color is specified for that keyword)
  426. * keywords will be checked sequentially; the first matching keyword with a color specified
  427. * will decide the item's color; if no color is found but the item still has to be
  428. * highlighted, EHH.defaultColor will be used instead
  429. * filters : an object with two properties (tag and title), each one a regular expression to
  430. * be applied to the relevant target
  431. */
  432.  
  433. var splitFilter = function(x,target) {
  434. if (x.length > 0 && x[0] == ':') target.tag.push(x.slice(1));
  435. else if (x.length > 0) target.title.push(x);
  436. };
  437.  
  438. var highlight = EHH.keywords.map(function(x) {
  439. var temp = x[0] == ':' ? { keyword : x.slice(1), type : 'tag' } :
  440. { keyword : x, type : 'title' };
  441. var tokens = temp.keyword.match(/^(.+?)(\/[^\/]+)?$/);
  442. return { regex: new RegExp(tokens[1],'gi'), type: temp.type, color: tokens[2] ? tokens[2].slice(1) : null };
  443. });
  444.  
  445. var filters = { title: [ ], tag: [ ] };
  446. EHH.filters.forEach(function(x) {
  447. if (x[0] == ':') filters.tag.push(x.slice(1));
  448. else filters.title.push(x);
  449. });
  450.  
  451. if (filters.title.length === 0) filters.title.push('EHH: no active title filter');
  452. if (filters.tag.length === 0) filters.tag.push('EHH: no active tag filter');
  453.  
  454. filters.title = new RegExp('(' + filters.title.join('|') + ')','i');
  455. filters.tag = new RegExp('(' + filters.tag.join('|') + ')','i');
  456.  
  457. return { highlight: highlight, filters: filters };
  458.  
  459. },
  460.  
  461. parse: function(title,tags) {
  462.  
  463. /* title : a string, the title of the gallery item to be parsed
  464. * tags : an array of objects, each one a tag contained in a tag flag
  465. * uses the object built by EHH.prepareRegexes to decide the course of action for each gallery
  466. * returns an object with a "result" property indicating what has to be done
  467. * possible values are "highlighted", "filtered" or null for no action
  468. * filters take precedence over highlight keywords
  469. * if the gallery item is to be highlighted, the result will also contain three additional properties:
  470. * - titleKeywords : a list of title substrings that match one or more keywords (can be empty)
  471. * - tagKeywords : a list of matching tags (to be passed to EHH.addTagDiv if EHH.settings.showTags is set, can be empty)
  472. * - color : the color the gallery needs to be highlighted in (EHH.defaultColor if the user did not specify any color)
  473. */
  474.  
  475. if (!EHH.parse.regexes)
  476. EHH.parse.regexes = EHH.prepareRegexes();
  477.  
  478. var regexes = EHH.parse.regexes;
  479.  
  480. if (EHH.settings.filterEnabled) {
  481. var filtered = regexes.filters.title.test(title) || tags.some(function(x) { return regexes.filters.tag.test(x.tag); });
  482. if (filtered) return { result: 'filtered' };
  483. }
  484.  
  485. if (EHH.settings.highlighterEnabled) {
  486. var titleKeywords = { }, tagKeywords = { }, color = null;
  487. regexes.highlight.forEach(function(data) {
  488. if (data.type == 'tag') {
  489. tags.forEach(function(tag) {
  490. if (tagKeywords.hasOwnProperty(tag.tag) || !tag.tag.match(data.regex)) return;
  491. tagKeywords[tag.tag] = tag.color;
  492. if (!color) color = data.color;
  493. });
  494. }
  495. else {
  496. var tokens = title.match(data.regex);
  497. if (!tokens) return;
  498. tokens = tokens.length == 1 ? tokens : tokens.slice(1);
  499. for (var i=0;i<tokens.length;++i) titleKeywords[tokens[i]] = true;
  500. if (!color) color = data.color;
  501. }
  502. });
  503.  
  504. titleKeywords = Object.keys(titleKeywords);
  505.  
  506. if (titleKeywords.length === 0 && Object.keys(tagKeywords).length === 0) return { result: null };
  507. return { result: 'highlighted', titleKeywords: titleKeywords, tagKeywords: tagKeywords, color: color || EHH.defaultColor };
  508. }
  509.  
  510. return { result: null };
  511.  
  512. },
  513.  
  514. computeTagColor: function(tag) {
  515. // don't use style.backgroundPositionY
  516. var y = parseInt(tag.style.cssText.split(/\s/).slice(-1)[0],10);
  517. y = -(y+1) / 17;
  518. return ['salmon','darkorange','gold','mediumaquamarine','skyblue','mediumorchid'][y];
  519. },
  520.  
  521. // extracts both the title and a list of tags for a given target
  522. // tags are parsed into objects with "tag" and "color" properties representing
  523. // respectively the tag itself and the color of their associated tag flag
  524. extractData: function(target) {
  525.  
  526. if (!target) return null;
  527.  
  528. var title = target.querySelector('.it5 > a, .id2 > a, [class^="t2"] > a') || target.querySelector('.itd a'),
  529. tags = Utils.query(target,'.tft, .tfl');
  530.  
  531. if (!title && target.className.indexOf('t2') === 0) title = target.firstChild;
  532. if (!title) return null;
  533.  
  534. if (tags.length > 0)
  535. tags = tags
  536. .map(function(x) {
  537. var color = EHH.computeTagColor(x);
  538. return x.title.split(/, /).map(function(y) { return { tag: y, color: color }; });
  539. })
  540. .reduce(function(x,y) { return x.concat(y); });
  541.  
  542. return { title: title, tags: tags };
  543.  
  544. },
  545.  
  546. addTagDiv: function(target,tags) {
  547. if (!tags || Object.keys(tags).length === 0) return;
  548. var div = document.createElement('div');
  549. div.className = 'e-Tags';
  550. var html = '';
  551. for (t in tags) {
  552. var tag = t.replace(/^.+:/,''), color = tags[t];
  553. html += '<div style="background-color: ' + color + '">' + tag + '</div>';
  554. }
  555. div.innerHTML = html;
  556. if (!EHH.settings.showTags) div.style.display = 'none';
  557. var temp = target.getElementsByClassName('id3')[0];
  558. if (temp) temp.firstElementChild.appendChild(div);
  559. else target.appendChild(div);
  560. },
  561.  
  562. walk: function(root) {
  563.  
  564. var reqs = EHH.get_reqs(apimax);
  565.  
  566. reqs.forEach(function(elems) {
  567. var ids = elems.map(function(e) {
  568. var anchor = (e.getElementsByClassName('id3')[0]).firstElementChild;
  569. var ref = anchor.href.split('/');
  570. return [ref[4], ref[5]];
  571. });
  572. var gdata = { "method" : "gdata", "gidlist" : ids };
  573. console.error("ids"+ids);
  574.  
  575. EHH.send_req(gdata, elems, apiurl);
  576. });
  577.  
  578. EHH.metadatas.forEach(function(test){
  579. console.error("hello"+test.tags);
  580. });
  581. /* walks the DOM to highlight and filter gallery items (or the taglist) */
  582.  
  583. if (EHH.gallery) {
  584. EHH.highlightTags();
  585. return;
  586. }
  587.  
  588. if (EHH.dontWalk) return;
  589.  
  590. var removeTagDiv = function(target) {
  591. Utils.query(target,'.e-Tags').forEach(function(x) { x.parentNode.removeChild(x); });
  592. };
  593.  
  594. var editTitle = function(target,keywords) {
  595. var temp = target.innerHTML;
  596. keywords.forEach(function(keyword) {
  597. var length = keyword.length, n = target.innerHTML.indexOf(keyword);
  598. while (n != -1) {
  599. temp = temp.slice(0,n) + new Array(length+1).join('\0') + temp.slice(n+length);
  600. n = target.innerHTML.indexOf(keyword,n+1);
  601. }
  602. });
  603. return temp.replace(/\0+/g,function(match,start) {
  604. return '<b>' + target.innerHTML.slice(start,start+match.length) + '</b>';
  605. });
  606. };
  607.  
  608. // ----------
  609.  
  610. var flip = 1,
  611. targets = Utils.query('[class^="gtr"], [class^="id1"], div[class^="t2"]'),
  612. groups = (EHH.settings.reorderGalleries && EHH.thumbnails ? [ [ ], [ ], [ ] ] : null);
  613. targets.forEach(function(target) {
  614.  
  615. var data = EHH.extractData(target);
  616. if (data === null) return;
  617.  
  618. // reset element
  619. target.className = target.className.replace(/\s?e-\w+/g,'');
  620. target.style.cssText = target.style.cssText.replace(/(background-color|order):.+?;/g,'');
  621. data.title.innerHTML = data.title.innerHTML = data.title.innerHTML.replace(/<\/?b>/g,'');
  622. removeTagDiv(target);
  623.  
  624. var parsed = EHH.parse(data.title.textContent,data.tags);
  625.  
  626. if (parsed.result == 'filtered') {
  627. target.className += ' e-Filtered';
  628. if (groups !== null) groups[2].push(target);
  629. }
  630.  
  631. else if (parsed.result == 'highlighted') {
  632. target.className += ' e-Highlighted';
  633. target.style.cssText += 'background-color: ' + parsed.color + ' !important;';
  634. data.title.innerHTML = editTitle(data.title,parsed.titleKeywords);
  635. if (EHH.thumbnails) EHH.addTagDiv(target,parsed.tagKeywords);
  636. if (groups !== null) groups[0].push(target);
  637. }
  638.  
  639. else if (groups !== null)
  640. groups[1].push(target);
  641.  
  642. if (!/^gtr/.test(target.className)) return;
  643. if (parsed.result == 'filtered' && !EHH.opaque) return;
  644. flip = (flip+1)%2;
  645. if (target.className.indexOf('color') == -1) target.className += ' color' + flip;
  646. else target.className = target.className.replace(/color\d/,'color' + flip);
  647.  
  648. });
  649.  
  650. if (groups !== null) {
  651. var order = 1;
  652. Array.prototype.concat.apply([ ],groups).forEach(function(g) {
  653. g.style.cssText += 'order: ' + (order++) + ';';
  654. });
  655. };
  656.  
  657. var filtered = document.
  658. ClassName('e-Filtered').length;
  659. document.getElementById('e-FilteredItems').textContent = filtered;
  660.  
  661. },
  662.  
  663. highlightTags: function() {
  664. Utils.query('[id^="ta_"]').forEach(function(x) {
  665. if (!EHH.settings.highlightTags) x.style.cssText = '';
  666. else {
  667. var fullName = x.id.slice(3).replace(/_/g,' ');
  668. var data = EHH.parse(fullName,[{ tag: fullName, color: null }]);
  669. x.style.cssText = data.result == 'filtered' ? 'text-decoration: line-through' :
  670. data.result == 'highlighted' ? 'background-color: ' + data.color + ' !important' :
  671. '';
  672. }
  673. });
  674. },
  675.  
  676. updateTagList: function(e) {
  677. if (e.target.nodeName != 'TABLE' || e.target.parentNode.id != 'taglist') return;
  678. if (EHH.settings.highlightTags) EHH.highlightTags();
  679. },
  680.  
  681. interceptMouseHover: function(e) {
  682. var observer = new MutationObserver(function(e) {
  683. var thumbnail = e[0].target;
  684. if (thumbnail.style.visibility == 'hidden')
  685. Utils.query(thumbnail,'.e-Tags').forEach(function(x) { x.parentNode.removeChild(x); });
  686. else if (EHH.settings.showTags) {
  687. var row = document.evaluate('ancestor::tr[1]',thumbnail,null,9,null).singleNodeValue,
  688. data = EHH.extractData(row);
  689. if (data === null) return;
  690. parsed = EHH.parse(data.title.textContent,data.tags);
  691. if (parsed.result == 'highlighted') EHH.addTagDiv(thumbnail,parsed.tagKeywords);
  692. }
  693. });
  694. Utils.query('.it2[id^="i"]').forEach(function(x) {
  695. observer.observe(x,{ attributes: true });
  696. });
  697. },
  698. get_reqs: function (maxlen) {
  699.  
  700. var classes=['id1','id1 e-Highlighted'];
  701. var elems = classes.map(function(x) {
  702. return Array.prototype.slice.call(document.getElementsByClassName(x));
  703. });
  704. var all = elems[0];
  705. for (var i=1; i < elems.length; i++) all = all.concat(elems[i]);
  706. var api_reqs = [];
  707.  
  708. for (var i=0; i<all.length; i+=maxlen) api_reqs.push(all.slice(i, i+maxlen));
  709. console.error('getting requests'+api_reqs.length);
  710.  
  711. return api_reqs;
  712. },
  713. addfcount: function (elem, metadata) {
  714. console.error('fcount');
  715.  
  716. var itds = elem.getElementsByClassName('itd'),
  717. filecount = " | ";
  718. if ("undefined" !== typeof metadata.error) {
  719. console.error('gallery-size: no tags in ' + metadata.gid);
  720. } else {
  721. EHH.metadatas=EHH.metadatas.concat(metadata);
  722. }
  723. },
  724. send_req: function (gdata, elems, aurl) {
  725.  
  726. var req = new XMLHttpRequest();
  727. req.onreadystatechange = (function() {
  728. if (4 === req.readyState) {
  729. if (200 !== req.status) {
  730. console.error('gallery-size: cannot send request to ' + aurl);
  731. } else {
  732. console.error('ready');
  733. var apirsp = JSON.parse(req.responseText);
  734. elems.forEach(function(e,i,arr) {
  735. EHH.addfcount(e,apirsp.gmetadata[i]); });
  736. }
  737. }
  738. else
  739. {
  740. console.error('failed?????');
  741. console.error(req.responseText);
  742. }
  743. });
  744. req.open("POST", "http://g.e-hentai.org/api.php", true);
  745. console.error("sending"+JSON.stringify(gdata));
  746. req.send(JSON.stringify(gdata));
  747. }
  748.  
  749. };
  750.  
  751. var Utils = {
  752.  
  753. onFirefox: function() {
  754. return (window.chrome === undefined);
  755. },
  756.  
  757. migrateSettings: function() { // this is only run on Firefox
  758. if (localStorage.getItem('migrationDone') !== null) return;
  759. for (var key in EHH.settings) {
  760. if (localStorage.getItem(key) === null) continue;
  761. EHH.settings[key] = JSON.parse(localStorage.getItem(key));
  762. Utils.save(key,EHH.settings[key]);
  763. localStorage.removeItem(key);
  764. }
  765. ['keywords','filters'].forEach(function(key) {
  766. if (localStorage.getItem(key) === null) return;
  767. EHH[key] = JSON.parse(localStorage.getItem(key));
  768. Utils.save(key,EHH[key]);
  769. localStorage.removeItem(key);
  770. });
  771. localStorage.setItem('migrationDone','true');
  772. },
  773.  
  774. save: function(key,value) {
  775. if (Utils.onFirefox()) GM_setValue(key,JSON.stringify(value));
  776. else localStorage.setItem(key,JSON.stringify(value));
  777. },
  778.  
  779. load: function(key,def) {
  780. var result = Utils.onFirefox() ? GM_getValue(key,null) : localStorage.getItem(key);
  781. return result ? JSON.parse(result) : def;
  782. },
  783.  
  784. onClick: function(element,f) {
  785. element.addEventListener('click',function(e) {
  786. if (e.which != 1) return;
  787. if (f) f.call(this);
  788. },false);
  789. },
  790.  
  791. linkCheckbox: function(checkbox,object,property) {
  792. if (object && object.hasOwnProperty(property))
  793. checkbox.checked = object[property];
  794. Utils.onClick(checkbox,function() { object[property] = checkbox.checked; });
  795. },
  796.  
  797. query: function(root,selector) {
  798. if (!selector)
  799. return Array.prototype.slice.call(document.querySelectorAll(root),0);
  800. else
  801. return Array.prototype.slice.call(root.querySelectorAll(selector),0);
  802. },
  803.  
  804.  
  805.  
  806. };
  807.  
  808. EHH.init();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement