la-image-extractor

copy image source in hitomi.la notomi.la e-hentai.org to clipboard

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
  // @name                la-image-extractor
  // @name:zh-CN          la 图片地址复制
  // @description         copy image source in hitomi.la  notomi.la  e-hentai.org to clipboard
  // @description:zh-CN   复制 hitoma.la  notomi.la  e-hentai 图片链接到剪贴板
  // @version             0.2.5
  // @author              jferroal
  // @license             GPL-3.0
  // @require             https://greasyfork.org/scripts/31793-jmul/code/JMUL.js?version=209567
  // @include             https://hitomi.la/reader/*
  // @include             https://nozomi.la/tag/*
  // @include             https://e-hentai.org/s/*
  // @grant               GM_xmlhttpRequest
  // @run-at              document-idle
  // @namespace           https://greasyfork.org/users/34556-jferroal
  // ==/UserScript==

(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
class Button {
  constructor(label, eventHandlers = []) {
    this.element = document.createElement('div');
    this.addCss(Button.DefaultCss);
    this.element.innerText = label;
    this.listeners = {};
    this.listen(eventHandlers);
    this.parent = null;
  }

  onClick(handler) {
    this.listen({'click': handler});
  }

  listen(handlers) {
    for (const event in handlers) {
      if (handlers.hasOwnProperty(event)) {
        this.element.addEventListener(event, handlers[event]);
      }
    }
  }

  addCss(css) {
    for (const styleName in css) {
      if (css.hasOwnProperty(styleName)) {
        this.element.style[styleName] = css[styleName];
      }
    }
  }

  appendTo(element) {
    this.parent = element;
    element.appendChild(this.element);
  }

  removeFrom() {
    this.parent.removeChild(this.element);
  }

  removeListener() {
    for (const event in this.listeners) {
      if (this.listeners.hasOwnProperty(event)) {
        this.element.removeEventListener(event, this.listeners[event]);
      }
    }
  }
}

Button.DefaultCss = {
  textAlign: 'center',
  width: '120px',
  lineHeight: '40px',
  backgroundColor: 'skyblue',
  color: 'white',
  cursor: 'pointer',
  borderRadius: '8px',
  boxShadow: '0px 0px 8px 4px rgba(0, 0, 0, .2)',
  position: 'fixed',
  right: '80px',
  bottom: '80px'
};

module.exports = Button;

},{}],2:[function(require,module,exports){
const {createFullScreenElement, href, innerText, isEhentai} = require('./utils');

let _EhentaiState = {};

function constructImage(src) {
  const img = document.createElement('img');
  img.style.width = '100vw';
  img.style.maxWidth = '100vw';
  img.setAttribute('src', src);
  return img;
}

function loadMore() {
  _EhentaiState.ImageContainer.removeChild(_EhentaiState.LoadMoreBtn);
  const targets = _EhentaiState.ImageSources
  .slice(_EhentaiState.ImageAppendedCount, _EhentaiState.ImageAppendedCount + _EhentaiState.ImagePerPage);
  let i = 0;
  for (const src of targets) {
    let timer = setTimeout(() => {
      const img = constructImage(src);
      document.querySelector(_EhentaiState.ImageContainerSelector).appendChild(img);
      clearTimeout(timer);
      timer = null;
    }, i * _EhentaiState.ImageElementCreationDefer);
    _EhentaiState.ImageAppendedCount += 1;
    i += 1;
  }
  if (_EhentaiState.ImageAppendedCount < _EhentaiState.ImageSources.length) {
    _EhentaiState.ImageContainer.appendChild(_EhentaiState.LoadMoreBtn);
  }
}




module.exports = {
  initEhentai() {
    if (!isEhentai()) return;
    const [
      MainContainerSelector,
      TopPaginationSelector,
      ImageContainerSelector,
      BottomPaginationSelector,
      ImagesSelector,
      TitleSelector
    ] = ['#i1', '#i2', '#i3', '#i4', '#i3 img', 'title'];
    const NextPagePattern = /<div id="i3"><a onclick="return load_image\((\d+), '([\w\d]+)'\)"/gi;
    const ImageSourcePattern = /<img id="img" src="(.*)" style=".*?" onerror=".*?" \/>/gi;
    const ImageElementCreationDefer = 200;
    const ImagePerPage = 20;
    const PostId = href().split('/')[5].split('-')[0];
    _EhentaiState.ImageContainer = document.querySelector(ImageContainerSelector);
    _EhentaiState.LoadMoreBtn = document.createElement('div');
    _EhentaiState.LoadMoreBtn.style.width = '100%';
    _EhentaiState.LoadMoreBtn.style.lineHeight = '48px';
    _EhentaiState.LoadMoreBtn.style.margin = '24px 60px';
    _EhentaiState.LoadMoreBtn.style.cursor = 'pointer';
    _EhentaiState.LoadMoreBtn.style.backgroundColor = 'lightskyblue';
    _EhentaiState.LoadMoreBtn.style.borderRadius = '8px';
    _EhentaiState.LoadMoreBtn.innerText = 'LOAD MORE';
    _EhentaiState.LoadMoreBtn.addEventListener('click', loadMore);
    _EhentaiState.FetchAllRunning = false;
    _EhentaiState.ImageAppendedCount = 1;
    _EhentaiState.ImageSources = ['first_image_placeholder'];

    _EhentaiState = Object.assign(_EhentaiState, {
      MainContainerSelector,
      TopPaginationSelector,
      ImageContainerSelector,
      BottomPaginationSelector,
      ImagesSelector,
      TitleSelector,
      NextPagePattern,
      ImageSourcePattern,
      ImageElementCreationDefer,
      ImagePerPage,
      PostId,
    });
  },
  fetchEhentaiAll() {
    if (_EhentaiState.FetchAllRunning) return;
    _EhentaiState.FetchAllRunning = true;

    function getNextImage(page, hash) {
      const url = `https://e-hentai.org/s/${hash}/${_EhentaiState.PostId}-${page}`;
      const xmlhttp = new XMLHttpRequest();
      xmlhttp.open('GET', url);
      xmlhttp.onreadystatechange = function () {
        if (this.readyState !== 4) return;
        if (this.status === 200) {
          const [, p, h] = _EhentaiState.NextPagePattern.exec(this.responseText);
          _EhentaiState.NextPagePattern.lastIndex = -1;
          const hasNext = parseInt(p, 10) !== _EhentaiState.ImageSources.length;
          if (!hasNext) {
            const loadedElem = createFullScreenElement('ALL IMAGE SOURCES LOADED');
            document.body.appendChild(loadedElem);
            let timer = setTimeout(() => {
              document.body.removeChild(loadedElem);
              clearTimeout(timer);
              timer = null;
            }, 3000);
            _EhentaiState.FetchAllRunning = false;
            return;
          }
          const [, imageSource] = _EhentaiState.ImageSourcePattern.exec(this.responseText);
          _EhentaiState.ImageSourcePattern.lastIndex = -1;
          _EhentaiState.ImageSources.push(imageSource);
          if (_EhentaiState.ImageAppendedCount < _EhentaiState.ImagePerPage) {
            // load IMAGE PER PAGE COUNT image at first
            const img = constructImage(imageSource);
            _EhentaiState.ImageContainer.appendChild(img);
            _EhentaiState.ImageAppendedCount += 1;
          } else {
            _EhentaiState.ImageContainer.appendChild(_EhentaiState.LoadMoreBtn);
          }
          getNextImage(p, h);
        }
      };
      xmlhttp.send();
    }

    const mainContainer = document.querySelector(_EhentaiState.MainContainerSelector);
    window.addEventListener('resize', () => {
      document.querySelector(_EhentaiState.MainContainerSelector).style.maxWidth = '100vw';
    });
    mainContainer.style.width = '100vw';
    mainContainer.style.maxWidth = '100vw';
    document.querySelector(_EhentaiState.TopPaginationSelector).style.display = 'none';
    document.querySelector(_EhentaiState.BottomPaginationSelector).style.display = 'none';
    const [, page, hash] = _EhentaiState.NextPagePattern.exec(
      document.querySelector(_EhentaiState.MainContainerSelector).innerHTML
    );
    getNextImage(page, hash);
  },
  
  extractEhentaiImages() {
    const img = document.querySelector(_EhentaiState.ImagesSelector);
    const title = innerText(document.querySelector(_EhentaiState.TitleSelector));
    return `${title}\n${[img.src, ..._EhentaiState.ImageSources.slice(1)].join('\n')}\n${'= ='.repeat(20)}`;
  }
};

},{"./utils":6}],3:[function(require,module,exports){
const {href, innerText, isHitomi} = require('./utils');

let _HitomiState = {};

module.exports = {
  initHitomi() {
    if (!isHitomi()) return;
    _HitomiState.ImgsSrcSelector = '.img-url';
    _HitomiState.TitleSelector = 'title';
    _HitomiState.Adapost = false;
    _HitomiState.NumberOfFrontEnds = 2;

  },
  extractHitomiImages() {
    const {ImgsSrcSelector, TitleSelector, Adapost, NumberOfFrontEnds} = _HitomiState;
    let images = Array.from(document.querySelectorAll(ImgsSrcSelector));
    let title = encodeURIComponent(innerText(document.querySelector(TitleSelector), '- | -').split(' | ')[0]);
    const mat = /\/\d*(\d)\.html/.exec(href());
    let lv = mat && parseInt(mat[1], 10);
    if (!lv || Number.isNaN(lv)) {
      lv = '1';
    }
    const magic = Adapost ? 'a' : String.fromCharCode(((lv === 1 ? 0 : lv) % NumberOfFrontEnds) + 97);
    images = images.map(s => s.innerText.replace('//g.hitomi.la', `https://${magic}a.hitomi.la`));
    return `${title}\n${images.join('\n')}\n${'= ='.repeat(20)}`;
  }
};

},{"./utils":6}],4:[function(require,module,exports){
const {isHitomi, isNozomi, isEhentai, copyToClipboard} = require('./utils');
const Button = require('./Button');
const {initHitomi, extractHitomiImages} = require('./hitomi');
const {initNozomi, extractNozomiImages, fetchNozomiAll} = require('./nozomi');
const {initEhentai, extractEhentaiImages, fetchEhentaiAll} = require('./ehentai');

(function () {
  initHitomi();
  initNozomi();
  initEhentai();
  // create button to click
  if (isNozomi() || isEhentai()) {
    const btn = new Button('Fetch All');
    btn.addCss({bottom: '160px', zIndex: '999'});
    btn.appendTo(document.body);
    btn.onClick(() => {
      if (isNozomi()) {
        fetchNozomiAll();
      }
      if (isEhentai()) {
        fetchEhentaiAll();
      }
    });
  }
  const btn = new Button('Copy Sources');
  btn.addCss({zIndex: '999'});
  btn.onClick(() => {
    // prepare str2Paste
    let str2paste = '';
    if (isHitomi()) {
      str2paste = extractHitomiImages();
    } else if (isNozomi()) {
      str2paste = extractNozomiImages();
    } else {
      str2paste = extractEhentaiImages();
    }
    copyToClipboard(str2paste);
  });
  btn.appendTo(document.body);
})();

},{"./Button":1,"./ehentai":2,"./hitomi":3,"./nozomi":5,"./utils":6}],5:[function(require,module,exports){
const {innerText, createFullScreenElement} = require('./utils');

// TODO: refactor

const [
  ImgSrcSelector,
  TitleSelector,
  ContentSelector,
  ThumbnailDivsSelector
] = ['.tag-list-img', 'h1', '.content', '#thumbnail-divs'];

function JSPack() {
  // Module-level (private) variables
  var el, bBE = false, m = this;

  // Raw byte arrays
  m._DeArray = function (a, p, l) {
    return [a.slice(p, p + l)];
  };
  m._EnArray = function (a, p, l, v) {
    for (var i = 0; i < l; a[p + i] = v[i] ? v[i] : 0, i++) ;
  };

  // ASCII characters
  m._DeChar = function (a, p) {
    return String.fromCharCode(a[p]);
  };
  m._EnChar = function (a, p, v) {
    a[p] = v.charCodeAt(0);
  };

  // Little-endian (un)signed N-byte integers
  m._DeInt = function (a, p) {
    var lsb = bBE ? (el.len - 1) : 0, nsb = bBE ? -1 : 1, stop = lsb + nsb * el.len, rv, i, f;
    for (rv = 0, i = lsb, f = 1; i != stop; rv += (a[p + i] * f), i += nsb, f *= 256) ;
    if (el.bSigned && (rv & Math.pow(2, el.len * 8 - 1))) { rv -= Math.pow(2, el.len * 8); }
    return rv;
  };
  m._EnInt = function (a, p, v) {
    var lsb = bBE ? (el.len - 1) : 0, nsb = bBE ? -1 : 1, stop = lsb + nsb * el.len, i;
    v = (v < el.min) ? el.min : (v > el.max) ? el.max : v;
    for (i = lsb; i != stop; a[p + i] = v & 0xff, i += nsb, v >>= 8) ;
  };

  // ASCII character strings
  m._DeString = function (a, p, l) {
    for (var rv = new Array(l), i = 0; i < l; rv[i] = String.fromCharCode(a[p + i]), i++) ;
    return rv.join('');
  };
  m._EnString = function (a, p, l, v) {
    for (var t, i = 0; i < l; a[p + i] = (t = v.charCodeAt(i)) ? t : 0, i++) ;
  };

  // Little-endian N-bit IEEE 754 floating point
  m._De754 = function (a, p) {
    var s, e, m, i, d, nBits, mLen, eLen, eBias, eMax;
    mLen = el.mLen, eLen = el.len * 8 - el.mLen - 1, eMax = (1 << eLen) - 1, eBias = eMax >> 1;

    i = bBE ? 0 : (el.len - 1);
    d = bBE ? 1 : -1;
    s = a[p + i];
    i += d;
    nBits = -7;
    for (e = s & ((1 << (-nBits)) - 1), s >>= (-nBits), nBits += eLen; nBits > 0; e = e * 256 + a[p + i], i += d, nBits -= 8) ;
    for (m = e & ((1 << (-nBits)) - 1), e >>= (-nBits), nBits += mLen; nBits > 0; m = m * 256 + a[p + i], i += d, nBits -= 8) ;

    switch (e) {
      case 0:
        // Zero, or denormalized number
        e = 1 - eBias;
        break;
      case eMax:
        // NaN, or +/-Infinity
        return m ? NaN : ((s ? -1 : 1) * Infinity);
      default:
        // Normalized number
        m = m + Math.pow(2, mLen);
        e = e - eBias;
        break;
    }
    return (s ? -1 : 1) * m * Math.pow(2, e - mLen);
  };
  m._En754 = function (a, p, v) {
    var s, e, m, i, d, c, mLen, eLen, eBias, eMax;
    mLen = el.mLen, eLen = el.len * 8 - el.mLen - 1, eMax = (1 << eLen) - 1, eBias = eMax >> 1;

    s = v < 0 ? 1 : 0;
    v = Math.abs(v);
    if (isNaN(v) || (v === Infinity)) {
      m = isNaN(v) ? 1 : 0;
      e = eMax;
    }
    else {
      e = Math.floor(Math.log(v) / Math.LN2);			// Calculate log2 of the value
      if (v * (c = Math.pow(2, -e)) < 1) {
        e--;
        c *= 2;
      }		// Math.log() isn't 100% reliable

      // Round by adding 1/2 the significand's LSD
      if (e + eBias >= 1) { v += el.rt / c; }			// Normalized:  mLen significand digits
      else { v += el.rt * Math.pow(2, 1 - eBias); } 		// Denormalized:  <= mLen significand digits
      if (v * c >= 2) {
        e++;
        c /= 2;
      }				// Rounding can increment the exponent

      if (e + eBias >= eMax) {
        // Overflow
        m = 0;
        e = eMax;
      }
      else if (e + eBias >= 1) {
        // Normalized - term order matters, as Math.pow(2, 52-e) and v*Math.pow(2, 52) can overflow
        m = (v * c - 1) * Math.pow(2, mLen);
        e = e + eBias;
      }
      else {
        // Denormalized - also catches the '0' case, somewhat by chance
        m = v * Math.pow(2, eBias - 1) * Math.pow(2, mLen);
        e = 0;
      }
    }

    for (i = bBE ? (el.len - 1) : 0, d = bBE ? -1 : 1; mLen >= 8; a[p + i] = m & 0xff, i += d, m /= 256, mLen -= 8) ;
    for (e = (e << mLen) | m, eLen += mLen; eLen > 0; a[p + i] = e & 0xff, i += d, e /= 256, eLen -= 8) ;
    a[p + i - d] |= s * 128;
  };


  // Class data
  m._sPattern = '(\\d+)?([AxcbBhHsfdiIlL])';
  m._lenLut = {
    'A': 1,
    'x': 1,
    'c': 1,
    'b': 1,
    'B': 1,
    'h': 2,
    'H': 2,
    's': 1,
    'f': 4,
    'd': 8,
    'i': 4,
    'I': 4,
    'l': 4,
    'L': 4
  };
  m._elLut = {
    'A': {en: m._EnArray, de: m._DeArray},
    's': {en: m._EnString, de: m._DeString},
    'c': {en: m._EnChar, de: m._DeChar},
    'b': {en: m._EnInt, de: m._DeInt, len: 1, bSigned: true, min: -Math.pow(2, 7), max: Math.pow(2, 7) - 1},
    'B': {en: m._EnInt, de: m._DeInt, len: 1, bSigned: false, min: 0, max: Math.pow(2, 8) - 1},
    'h': {en: m._EnInt, de: m._DeInt, len: 2, bSigned: true, min: -Math.pow(2, 15), max: Math.pow(2, 15) - 1},
    'H': {en: m._EnInt, de: m._DeInt, len: 2, bSigned: false, min: 0, max: Math.pow(2, 16) - 1},
    'i': {en: m._EnInt, de: m._DeInt, len: 4, bSigned: true, min: -Math.pow(2, 31), max: Math.pow(2, 31) - 1},
    'I': {en: m._EnInt, de: m._DeInt, len: 4, bSigned: false, min: 0, max: Math.pow(2, 32) - 1},
    'l': {en: m._EnInt, de: m._DeInt, len: 4, bSigned: true, min: -Math.pow(2, 31), max: Math.pow(2, 31) - 1},
    'L': {en: m._EnInt, de: m._DeInt, len: 4, bSigned: false, min: 0, max: Math.pow(2, 32) - 1},
    'f': {en: m._En754, de: m._De754, len: 4, mLen: 23, rt: Math.pow(2, -24) - Math.pow(2, -77)},
    'd': {en: m._En754, de: m._De754, len: 8, mLen: 52, rt: 0}
  };

  // Unpack a series of n elements of size s from array a at offset p with fxn
  m._UnpackSeries = function (n, s, a, p) {
    for (var fxn = el.de, rv = [], i = 0; i < n; rv.push(fxn(a, p + i * s)), i++) ;
    return rv;
  };

  // Pack a series of n elements of size s from array v at offset i to array a at offset p with fxn
  m._PackSeries = function (n, s, a, p, v, i) {
    for (var fxn = el.en, o = 0; o < n; fxn(a, p + o * s, v[i + o]), o++) ;
  };

  // Unpack the octet array a, beginning at offset p, according to the fmt string
  m.Unpack = function (fmt, a, p) {
    // Set the private bBE flag based on the format string - assume big-endianness
    bBE = (fmt.charAt(0) != '<');

    p = p ? p : 0;
    var re = new RegExp(this._sPattern, 'g'), m, n, s, rv = [];
    while (m = re.exec(fmt)) {
      n = ((m[1] == undefined) || (m[1] == '')) ? 1 : parseInt(m[1]);
      s = this._lenLut[m[2]];
      if ((p + n * s) > a.length) {
        return undefined;
      }
      switch (m[2]) {
        case 'A':
        case 's':
          rv.push(this._elLut[m[2]].de(a, p, n));
          break;
        case 'c':
        case 'b':
        case 'B':
        case 'h':
        case 'H':
        case 'i':
        case 'I':
        case 'l':
        case 'L':
        case 'f':
        case 'd':
          el = this._elLut[m[2]];
          rv.push(this._UnpackSeries(n, s, a, p));
          break;
      }
      p += n * s;
    }
    return Array.prototype.concat.apply([], rv);
  };

  // Pack the supplied values into the octet array a, beginning at offset p, according to the fmt string
  m.PackTo = function (fmt, a, p, values) {
    // Set the private bBE flag based on the format string - assume big-endianness
    bBE = (fmt.charAt(0) !== '<');

    var re = new RegExp(this._sPattern, 'g'), m, n, s, i = 0, j;
    while (m = re.exec(fmt)) {
      n = ((m[1] === undefined) || (m[1] === '')) ? 1 : parseInt(m[1]);
      s = this._lenLut[m[2]];
      if ((p + n * s) > a.length) {
        return false;
      }
      switch (m[2]) {
        case 'A':
        case 's':
          if ((i + 1) > values.length) { return false; }
          this._elLut[m[2]].en(a, p, n, values[i]);
          i += 1;
          break;
        case 'c':
        case 'b':
        case 'B':
        case 'h':
        case 'H':
        case 'i':
        case 'I':
        case 'l':
        case 'L':
        case 'f':
        case 'd':
          el = this._elLut[m[2]];
          if ((i + n) > values.length) { return false; }
          this._PackSeries(n, s, a, p, values, i);
          i += n;
          break;
        case 'x':
          for (j = 0; j < n; j++) { a[p + j] = 0; }
          break;
      }
      p += n * s;
    }
    return a;
  };

  // Pack the supplied values into a new octet array, according to the fmt string
  m.Pack = function (fmt, values) {
    return this.PackTo(fmt, new Array(this.CalcLength(fmt)), 0, values);
  };

  // Determine the number of bytes represented by the format string
  m.CalcLength = function (fmt) {
    var re = new RegExp(this._sPattern, 'g'), m, sum = 0;
    while (m = re.exec(fmt)) {
      sum += (((m[1] == undefined) || (m[1] == '')) ? 1 : parseInt(m[1])) * this._lenLut[m[2]];
    }
    return sum;
  };
}


var nozomiextension = '.nozomi';
var nozomidir = 'nozomi';
var domain = 'n.nozomi.la';
var postdir = 'post';

var results_array = {};
var outstanding_requests = {};
var number_of_outstanding_requests = 0;
var nozomi, total_items;
var tag, tag_for_hash = '', area, page_number;
var nozomi_address;


function dgebi(id) { return document.getElementById(id); }

function path_from_postid(postid) {
  if (postid.length < 3) return postid;

  return postid.replace(/^(.*(..)(.))$/, '$3/$2/$1');
}

function resize_thumbnails() {
  var c = document.getElementsByClassName('content')[0];
  if (!c) {
    return;
  }

  var s = c.clientWidth;

  var n = Math.ceil(6.0 * s / 1000.0);
  var w = ((s - (10.0 * n + 10.0) - 0.5) / n) - 2.0;

  var divs = document.getElementsByClassName('thumbnail-div');
  for (var i = 0; i < divs.length; i++) {
    var div = divs[i];
    div.style.width = (w + 'px');
    div.style.height = (w + 'px');
  }
}

function check_dates() {
  var format = 'lll';

  var localize_title = function (el) {
    if (!el) {
      return;
    }

    var str = el.title;
    if (str) {
      var m = moment(str);
      el.title = m.format(format);
    }
  };
  //post view
  localize_title(document.querySelectorAll('.post img')[0]);
}

function urlencode(str) {
  return str.replace(/[;\/?:@=&]/g, function (c) {
    return '%' + c.charCodeAt(0).toString(16);
    return '%' + c.charCodeAt(0).toString(16);
  });
}

function fetch_nozomi(totalImageCount) {
  var filepath = decodeURIComponent(document.location.href.replace(/.*nozomi\.la\//, ''));
  if (!filepath) {
    tag = 'index';
    page_number = 1;
  } else {
    var elements = filepath.replace(/\.html$/, '').split('-');
    if (elements.length < 2) return;
    while (elements.length > 2) {
      elements[1] = elements[0] + '-' + elements[1];
      elements.shift();
    }

    tag = elements[0];
    if (tag.match(/\//)) {
      var area_elements = tag.split(/\//);
      if (area_elements.length !== 2) return;

      area = area_elements[0];
      if (!area || area.match(/[^A-Za-z0-9_]/)) return;

      tag = area_elements[1];
    }
    if (!tag || tag.match(/\//)) return;

    if (tag === 'index-Popular') {
      tag_for_hash = '-Popular';
    } else if (area) {
      tag_for_hash = tag;
    }

    page_number = parseInt(elements[1]);
    if (!page_number || page_number < 1) return;
  }
  nozomi_address = '//' + [domain, urlencode(tag)].join('/') + nozomiextension;
  if (area) {
    nozomi_address = '//' + [domain, nozomidir, urlencode(tag)].join('/') + nozomiextension;
  }

  var start_byte = 0;
  var end_byte = totalImageCount * 4 - 1;
  var xhr = new XMLHttpRequest();
  const loadingElement = createFullScreenElement();
  document.body.appendChild(loadingElement);
  xhr.open('GET', nozomi_address);
  xhr.responseType = 'arraybuffer';
  xhr.setRequestHeader('Range', 'bytes=' + start_byte.toString() + '-' + end_byte.toString());
  xhr.onreadystatechange = function (oEvent) {
    if (xhr.readyState === 4) {
      if (xhr.status === 200 || xhr.status === 206) {
        var arrayBuffer = xhr.response; // Note: not oReq.responseText
        if (arrayBuffer) {
          var arr = new Uint8Array(arrayBuffer); //e.g. [0x00, 0x5D, 0x39, 0x72,  0x00, 0x5D, 0x39, 0x82,  ...]
          var jspack = new JSPack();
          var total = arr.length / 4; //32-bit unsigned integers
          nozomi = jspack.Unpack(total + 'I', arr);
          total_items = parseInt(xhr.getResponseHeader('Content-Range').replace(/^[Bb]ytes \d+-\d+\//, '')) / 4;
          get_jsons();
          document.body.removeChild(loadingElement);
        }
      }
    }
  };
  xhr.send();
}

function get_jsons() {
  var datas = [];
  for (var i in nozomi) {
    var postid = nozomi[i];
    if (postid in results_array) {
      datas.push(results_array[postid]);
      continue;
    }
    if (!outstanding_requests[postid]) {
      outstanding_requests[postid] = 1;
      ++number_of_outstanding_requests;
      get_json(postid); //calling a function is REQUIRED to give postid its own scope
    }
  }
  if (number_of_outstanding_requests) return;

  results_to_page(datas);
}

function get_json(postid) {
  var url = '//j.nozomi.la/' + postdir + '/' + path_from_postid(postid.toString()) + '.json';

  var xmlhttp = new XMLHttpRequest();
  xmlhttp.open('GET', url);
  xmlhttp.onreadystatechange = function () {
    if (this.readyState === 4) {
      if (this.status === 200) {
        results_array[postid] = JSON.parse(this.responseText);
      } else {
        results_array[postid] = '';
      }
      delete outstanding_requests[postid];
      --number_of_outstanding_requests;
      get_jsons();
    }
  };
  xmlhttp.send();
}

function results_to_page(datas) {
  var tc = Object.create(null);
  var to = {};

  dgebi('thumbnail-divs').innerHTML = '';

  var ul = document.getElementsByClassName('title')[0].nextElementSibling;
  ul.innerHTML = '';

  var p = function (l) {
    var areas = ['general', 'artist', 'copyright', 'character'];
    for (var a in areas) {
      var area = areas[a];
      for (var i in l[area]) {
        var tag = l[area][i];

        if (tag.tag in tc === false) {
          tc[tag.tag] = 1;
        } else {
          tc[tag.tag]++;
        }

        to[tag.tag] = tag;
      }
    }
  };

  for (var d in datas) {
    var data = datas[d];
    if (!data) continue;

    var div = document.createElement('div');
    div.classList.add('thumbnail-div');

    var a = document.createElement('a');
    a.href = '/post/' + data.postid + '.html#' + tag_for_hash;

    var img = document.createElement('img');
    img.classList.add('tag-list-img');
    img.crossOrigin = 'Anonymous';
    img.src = data.imageurl.replace(/\/\/i\.nozomi\.la\//, '//tn.nozomi.la/') + '.jpg';
    img.title = data.date;

    a.appendChild(img);
    div.appendChild(a);
    dgebi('thumbnail-divs').appendChild(div);

    p(data);
  }


  resize_thumbnails();
  setTimeout(resize_thumbnails, 10);

  check_dates();
}


module.exports = {
  initNozomi() {
    return;
  },
  fetchNozomiAll() {
    const totalPage = parseInt(innerText(document.querySelector('.page-container li:last-child'), 1), 10);
    const totalImageCount = totalPage * 64;
    // prettify page
    const elementsToRemove = [
      document.querySelector('.page-container'),
      document.querySelector('.navbar'),
      document.querySelector('.sidebar')
    ];
    for (const elem of elementsToRemove) {
      elem.style.display = 'none';
    }
    const mainContent = document.querySelector('.content');
    mainContent.style.margin = '0 auto';
    const thumbnailsContainer = document.getElementById('thumbnail-divs');
    for (const e of Array.from(thumbnailsContainer.getElementsByClassName('thumbnail-div'))) {
      thumbnailsContainer.removeChild(e);
    }
    fetch_nozomi(totalImageCount);
  },
  extractNozomiImages() {
    let images = Array.from(document.querySelectorAll(ImgSrcSelector));
    let title = document.querySelector(TitleSelector).innerText;
    images = images.map(s => s.src.replace('//tn', '//i').split('.').slice(0, 4).join('.'));
    return `${title}\n${images.join('\n')}\n${'= ='.repeat(20)}`;
  }
};

},{"./utils":6}],6:[function(require,module,exports){
module.exports = {
  isHitomi() {
    return window && window.location && /hitomi/gi.test(window.location.href);
  },
  isNozomi() {
    return window && window.location && /nozomi.la\/tag/gi.test(window.location.href);
  },
  isEhentai() {
    return window && window.location && /e-hentai.org\/s/gi.test(window.location.href);
  },
  copyToClipboard(content) {
    const el = document.createElement('textarea');
    el.value = content;
    document.body.appendChild(el);
    el.select();
    document.execCommand('copy');
    document.body.removeChild(el);
  },
  href(d = '') {
    return (window && window.location && window.location.href) || d;
  },
  innerText(elem, d = '') {
    return (elem && elem.innerText) || d;
  },
  matchAll(content, pat) {
    let matches = [];
    let match = pat.exec(content);
    while (match) {
      matches.push(match[1]);
      match = pat.exec(content);
    }
    pat.lastIndex = -1;
    return matches;
  },
  createFullScreenElement(text) {
    const elem = document.createElement('div');
    elem.style.position = 'fixed';
    elem.style.top = '0';
    elem.style.right = '0';
    elem.style.bottom = '0';
    elem.style.left = '0';
    elem.style.display = 'flex';
    elem.style.backgroundColor = 'rgba(0, 0, 0, 0.56)';
    elem.style.justifyContent = 'center';
    elem.style.alignItems = 'center';
    elem.style.fontSize = '72px';
    elem.style.zIndex = '999';
    elem.innerText = text;
    return elem;
  }
};

},{}]},{},[4]);