assets

package
v0.50.0 Latest Latest
Warning

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

Go to latest
Published: Aug 20, 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 = `() => { // eslint-disable-line no-unused-expressions
  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('/pages')).json()
            let html = ""
            list.forEach((el) => {
                html += ` + "`" + `<a href='/page/${el.targetId}' title="${el.url}">${el.title}</a>` + "`" + `
            })

            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]
    let elImg = document.querySelector('.screen')
    let elTitle = document.querySelector('.title')
    let elUrl = document.querySelector('.url')
    let elRate = document.querySelector('.rate')
    let elErr = document.querySelector('.error')

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

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

        await new Promise((resolve, reject) => {
            let now = new Date()
            elImg.src = ` + "`" + `/screenshot/${id}?t=${now.getTime()}` + "`" + `
            elImg.style.maxWidth = innerWidth + 'px'
            elImg.onload = resolve
            elImg.onerror = () => reject('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