assets

package
v0.70.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 9, 2020 License: MIT Imports: 0 Imported by: 5

README

Asserts

helper.js

This lib is the helper that Rod will inject to each page to help quey, manipulate page contents.

Documentation

Index

Constants

View Source
const Helper = `() => {
` + `const rod = {
  _() {},

  element(...selectors) {
    const scope = ensureScope(this)
    for (const selector of selectors) {
      const el = scope.querySelector(selector)
      if (el) {
        return el
      }
    }
    return null
  },

  elements(selector) {
    return ensureScope(this).querySelectorAll(selector)
  },

  elementX(...xPaths) {
    const scope = ensureScope(this)
    for (const xPath of xPaths) {
      const el = document.evaluate(
        xPath,
        scope,
        null,
        XPathResult.FIRST_ORDERED_NODE_TYPE
      ).singleNodeValue
      if (el) {
        return el
      }
    }
    return null
  },

  elementsX(xpath) {
    const scope = ensureScope(this)
    const iter = document.evaluate(
      xpath,
      scope,
      null,
      XPathResult.ORDERED_NODE_ITERATOR_TYPE
    )
    const list = []
    let el
    while ((el = iter.iterateNext())) list.push(el)
    return list
  },

  elementR(...pairs) {
    for (let i = 0; i < pairs.length - 1; i += 2) {
      const selector = pairs[i]
      const pattern = pairs[i + 1]
      const reg = new RegExp(pattern)
      const el = Array.from(
        (this.document || this).querySelectorAll(selector)
      ).find((e) => reg.test(rod.text.call(e)))
      if (el) {
        return el
      }
    }
    return null
  },

  parents(selector) {
    let p = this.parentElement
    const list = []
    while (p) {
      if (p.matches(selector)) {
        list.push(p)
      }
      p = p.parentElement
    }
    return list
  },

  containsElement(target) {
    var node = target
    while (node != null) {
      if (node === this) {
        return true
      }
      node = node.parentElement
    }
    return false
  },

  async initMouseTracer(iconId, icon) {
    await rod.waitLoad()

    if (document.getElementById(iconId)) {
      return
    }

    const tmp = document.createElement('div')
    tmp.innerHTML = icon
    const svg = tmp.lastChild
    svg.id = iconId
    svg.style =
      'position: absolute; z-index: 2147483647; width: 17px; pointer-events: none;'
    svg.removeAttribute('width')
    svg.removeAttribute('height')
    document.body.appendChild(svg)
  },

  updateMouseTracer(iconId, x, y) {
    const svg = document.getElementById(iconId)
    if (!svg) {
      return false
    }
    svg.style.left = x - 2 + 'px'
    svg.style.top = y - 3 + 'px'
    return true
  },

  rect() {
    const b = ensureElement(this).getBoundingClientRect()
    return { x: b.x, y: b.y, width: b.width, height: b.height }
  },

  async overlay(id, left, top, width, height, msg) {
    await rod.waitLoad()

    const div = document.createElement('div')
    div.id = id
    div.style = ` + "`" + `position: fixed; z-index:2147483647; border: 2px dashed red;
        border-radius: 3px; box-shadow: #5f3232 0 0 3px; pointer-events: none;
        box-sizing: border-box;
        left: ${left}px;
        top: ${top}px;
        height: ${height}px;
        width: ${width}px;` + "`" + `

    if (width * height === 0) {
      div.style.border = 'none'
    }

    if (!msg) {
      document.body.appendChild(div)
      return
    }

    const msgDiv = document.createElement('div')
    msgDiv.style = ` + "`" + `position: absolute; color: #cc26d6; font-size: 12px; background: #ffffffeb;
        box-shadow: #333 0 0 3px; padding: 2px 5px; border-radius: 3px; white-space: nowrap;
        top: ${height}px;` + "`" + `

    msgDiv.innerHTML = msg
    div.appendChild(msgDiv)
    document.body.appendChild(div)

    if (window.innerHeight < msgDiv.offsetHeight + top + height) {
      msgDiv.style.top = -msgDiv.offsetHeight - 2 + 'px'
    }

    if (window.innerWidth < msgDiv.offsetWidth + left) {
      msgDiv.style.left = window.innerWidth - msgDiv.offsetWidth - left + 'px'
    }
  },

  async elementOverlay(id, msg) {
    const interval = 100
    const el = ensureElement(this)

    let pre = el.getBoundingClientRect()
    await rod.overlay(id, pre.left, pre.top, pre.width, pre.height, msg)

    const update = () => {
      const overlay = document.getElementById(id)
      if (overlay === null) return

      const box = el.getBoundingClientRect()
      if (
        pre.left === box.left &&
        pre.top === box.top &&
        pre.width === box.width &&
        pre.height === box.height
      ) {
        setTimeout(update, interval)
        return
      }

      overlay.style.left = box.left + 'px'
      overlay.style.top = box.top + 'px'
      overlay.style.width = box.width + 'px'
      overlay.style.height = box.height + 'px'
      pre = box

      setTimeout(update, interval)
    }

    setTimeout(update, interval)
  },

  removeOverlay(id) {
    const el = document.getElementById(id)
    el && el.remove()
  },

  waitIdle(timeout) {
    return new Promise((resolve) => {
      window.requestIdleCallback(resolve, { timeout })
    })
  },

  waitLoad() {
    const isWin = this === window
    return new Promise((resolve, reject) => {
      if (isWin) {
        if (document.readyState === 'complete') return resolve()
        window.addEventListener('load', resolve)
      } else {
        if (this.complete === undefined || this.complete) {
          resolve()
        } else {
          this.addEventListener('load', resolve)
          this.addEventListener('error', reject)
        }
      }
    })
  },

  inputEvent() {
    this.dispatchEvent(new Event('input', { bubbles: true }))
    this.dispatchEvent(new Event('change', { bubbles: true }))
  },

  selectText(pattern) {
    const m = this.value.match(new RegExp(pattern))
    if (m) {
      this.setSelectionRange(m.index, m.index + m[0].length)
    }
  },

  selectAllText() {
    this.select()
  },

  select(selectors, selected, type) {
    let matchers
    switch (type) {
      case 'regex':
        matchers = selectors.map((s) => {
          const reg = new RegExp(s)
          return (el) => reg.test(el.innerText)
        })
        break
      case 'css-selector':
        matchers = selectors.map((s) => (el) => el.matches(s))
        break
      default:
        matchers = selectors.map((s) => (el) => el.innerText.includes(s))
        break
    }

    const opts = Array.from(this.options)
    matchers.forEach((s) => {
      const el = opts.find(s)
      if (el) {
        el.selected = selected
        return
      }
    })

    this.dispatchEvent(new Event('input', { bubbles: true }))
    this.dispatchEvent(new Event('change', { bubbles: true }))
  },

  visible() {
    const el = ensureElement(this)
    const box = el.getBoundingClientRect()
    const style = window.getComputedStyle(el)
    return (
      style.display !== 'none' &&
      style.visibility !== 'hidden' &&
      !!(box.top || box.bottom || box.width || box.height)
    )
  },

  invisible() {
    return !rod.visible.apply(this)
  },

  text() {
    switch (this.tagName) {
      case 'INPUT':
      case 'TEXTAREA':
        return this.value
      case 'SELECT':
        return Array.from(this.selectedOptions)
          .map((el) => el.innerText)
          .join()
      case undefined:
        return this.textContent
      default:
        return this.innerText
    }
  },

  resource() {
    return new Promise((resolve, reject) => {
      if (this.complete) {
        return resolve(this.currentSrc)
      }
      this.addEventListener('load', () => resolve(this.currentSrc))
      this.addEventListener('error', (e) => reject(e))
    })
  },

  addScriptTag(id, url, content) {
    if (document.getElementById(id)) return

    return new Promise((resolve, reject) => {
      var s = document.createElement('script')

      if (url) {
        s.src = url
        s.onload = resolve
      } else {
        s.type = 'text/javascript'
        s.text = content
        resolve()
      }

      s.id = id
      s.onerror = reject
      document.head.appendChild(s)
    })
  },

  addStyleTag(id, url, content) {
    if (document.getElementById(id)) return

    return new Promise((resolve, reject) => {
      var el

      if (url) {
        el = document.createElement('link')
        el.rel = 'stylesheet'
        el.href = url
      } else {
        el = document.createElement('style')
        el.type = 'text/css'
        el.appendChild(document.createTextNode(content))
        resolve()
      }

      el.id = id
      el.onload = resolve
      el.onerror = reject
      document.head.appendChild(el)
    })
  },

  fetchAsDataURL(url) {
    return fetch(url)
      .then((res) => res.blob())
      .then(
        (data) =>
          new Promise((resolve, reject) => {
            var reader = new FileReader()
            reader.onload = () => resolve(reader.result)
            reader.onerror = () => reject(reader.error)
            reader.readAsDataURL(data)
          })
      )
  }
}

function ensureScope(s) {
  return s === window ? s.document : s
}

function ensureElement(el) {
  if (!el.tagName) {
    return el.parentElement
  }
  return el
}
` + `
return rod }

//# sourceURL=__rod_helper__
`

Helper for rod

View Source
const Monitor = `<html>
  <head>
    <title>Rod Monitor - Pages</title>
    <style>
      body {
        margin: 0;
        background: #2d2c2f;
        color: white;
        padding: 20px;
        font-family: sans-serif;
      }
      a {
        color: white;
        padding: 1em;
        margin: 0.5em 0;
        font-size: 1em;
        text-decoration: none;
        display: block;
        border-radius: 0.3em;
        border: 1px solid transparent;
        background: #212225;
      }
      a:visited {
        color: #c3c3c3;
      }
      a:hover {
        background: #25272d;
        border-color: #8d8d96;
      }
    </style>
  </head>
  <body>
    <h3>Choose a Page to Monitor</h3>

    <div id="targets"></div>

    <script>
      async function update() {
        const list = await (await fetch('/api/pages')).json()
        let html = ''
        list.forEach((el) => {
          html += ` + "`" + `<a href='/page/${el.targetId}' title="${el.url}">${el.title}</a>` + "`" + `
        })

        window.targets.innerHTML = html

        setTimeout(update, 1000)
      }

      update()
    </script>
  </body>
</html>
`

Monitor for rod

View Source
const MonitorPage = `<html>
  <head>
    <style>
      body {
        margin: 0;
        background: #2d2c2f;
        color: #ffffff;
      }
      .navbar {
        font-family: sans-serif;
        border-bottom: 1px solid #1413158c;
        display: flex;
        flex-direction: row;
      }
      .error {
        color: #ff3f3f;
        background: #3e1f1f;
        border-bottom: 1px solid #1413158c;
        display: none;
        padding: 10px;
        margin: 0;
      }
      input {
        background: transparent;
        color: white;
        border: none;
        border: 1px solid #4f475a;
        border-radius: 3px;
        padding: 5px;
        margin: 5px;
      }
      .title {
        flex: 2;
      }
      .url {
        flex: 5;
      }
      .rate {
        flex: 1;
      }
    </style>
  </head>
  <body>
    <div class="navbar">
      <input
        type="text"
        class="title"
        title="title of the remote page"
        readonly
      />
      <input type="text" class="url" title="url of the remote page" readonly />
      <input
        type="number"
        class="rate"
        value="0.5"
        min="0"
        step="0.1"
        title="refresh rate (second)"
      />
    </div>
    <pre class="error"></pre>
    <img class="screen" />
  </body>
  <script>
    const id = location.pathname.split('/').slice(-1)[0]
    const elImg = document.querySelector('.screen')
    const elTitle = document.querySelector('.title')
    const elUrl = document.querySelector('.url')
    const elRate = document.querySelector('.rate')
    const elErr = document.querySelector('.error')

    document.title = ` + "`" + `Rod Monitor - ${id}` + "`" + `

    async function update() {
      const res = await fetch(` + "`" + `/api/page/${id}` + "`" + `)
      const info = await res.json()
      elTitle.value = info.title
      elUrl.value = info.url

      await new Promise((resolve, reject) => {
        const now = new Date()
        elImg.src = ` + "`" + `/screenshot/${id}?t=${now.getTime()}` + "`" + `
        elImg.style.maxWidth = innerWidth + 'px'
        elImg.onload = resolve
        elImg.onerror = () => reject(new Error('error loading screenshots'))
      })
    }

    async function mainLoop() {
      try {
        await update()
        elErr.attributeStyleMap.delete('display')
      } catch (err) {
        elErr.style.display = 'block'
        elErr.textContent = err + ''
      }

      setTimeout(mainLoop, parseFloat(elRate.value) * 1000)
    }

    mainLoop()
  </script>
</html>
`

MonitorPage for rod

View Source
const MousePointer = `` /* 1518-byte string literal not displayed */

MousePointer for rod

Variables

This section is empty.

Functions

This section is empty.

Types

This section is empty.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL