assets

package
v0.55.1 Latest Latest
Warning

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

Go to latest
Published: Sep 11, 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 DeviceList = `` /* 37242-byte string literal not displayed */

DeviceList for rod

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
    },

    elementMatches(...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
    },

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

      const div = document.createElement('div')
      const msgDiv = 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'
      }

      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) {
      selectors.forEach((s) => {
        Array.from(this.options).find((el) => {
          try {
            if (el.innerText.includes(s) || el.matches(s)) {
              el.selected = true
              return true
            }
          } catch (e) {}
        })
      })
      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