Alan Colon %!s(int64=7) %!d(string=hai) anos
achega
c484f1549b
Modificáronse 3 ficheiros con 287 adicións e 0 borrados
  1. 40 0
      anim-code.css
  2. 10 0
      anim-code.html
  3. 237 0
      anim-code.js

+ 40 - 0
anim-code.css

@@ -0,0 +1,40 @@
+body {
+  background-color: black;
+    color: white;
+  }
+  span {
+    outline: solid 1px #ffffff52;
+  }
+  
+
+  .code-root, .code-interpolation { color: white; }
+  .code-keyword { color: #569cd6; }
+  .code-def { color: #dcdcaa; }
+
+  .code-open-comment,
+  .code-comment,
+  .code-open-block-comment,
+  .code-block-comment,
+  .code-close-block-comment {
+    color: #6a9955!important;
+  }
+
+  .code-string,
+  .code-template,
+  .code-open-string,
+  .code-close-string,
+  .code-open-template,
+  .code-close-template { color: #ce9178; }
+  .code-template .code-bracket { color: #809bbd; }
+  
+  .code-template [text="$"],
+  .code-template .code-open-bracket,
+  .code-template .code-close-bracket {
+    color: #569cd6;
+  }
+  [text="."] + .code-default,  .code-property { color: #dcdcaa; }
+  
+  [text=","],
+  [text="."] { color: white; }
+  .code-active { color: #90abcd; }
+  

+ 10 - 0
anim-code.html

@@ -0,0 +1,10 @@
+<html>
+  <head>
+    <title>Animated Code</title>
+    <link rel="stylesheet" href="anim-code.css" />
+  </head>
+  <body>
+    <pre></pre>
+  </body>
+  <script type="text/javascript" src="anim-code.js"></script>
+</html>

+ 237 - 0
anim-code.js

@@ -0,0 +1,237 @@
+
+  function hello(one, two, three) {
+    console.log(`one: ${one}; two: ${two}; three: ${three}`)
+    console.log(`one: ${one[two(three)]}; two: ${two(three[one])}; three: [({${three}})]`)
+    one[two(three)]
+    //two(three[one])
+    ;[(three)]
+    while (nested) {
+      function nested() {
+        if (nested) {
+          console.log('I am nested');
+        } else {
+          console.log('I am not')
+        }
+      }
+    }
+  }
+  const dogfoodRegex = /(\(|\)|\{|\}|\[|\]|\:|\;|\.|\s+|\`|\'|\"|[^\(\)\{\}\[\]\:\;\.\s\,\`\'\"]+)/gm
+
+console.clear()
+const sampleCode = `
+  console.log(\`hello \${(world)}\`)
+  console.log('hello \${(world)}')
+  function hello(one, two, three) {
+    console.log(\`one: \${one}; two: \${two}; three: \${three}\`)
+    console.log(\`one: \${one[two(three)]}; two: \${two(three[one])}; three: [({\${three}\})]\`)
+    one[two(three)]
+    // two(three[one])
+    ;[(three)]
+    while (nested) {
+      function nested() {
+        if (nested) {
+          console.log('I am nested');
+        } else {
+          console.log('I am not')
+        }
+        /*
+        if (nested) {
+          console.log('I am nested');
+        } else {
+          console.log('I am not')
+        }
+        */
+      }
+    }
+  }
+  const dogfoodRegex = /(\\(|\\)|\\{|\\}|\\[|\\]|\\:|\\;|\\.|\\s+|\\\`|\\'|\\"|[^\\(\\)\\{\\}\\[\\]\\:\\;\\.\\s\\,\\\`\\'\\"]+)/gm
+`
+
+const keywordRegex = /^(do|if|in|for|let|new|try|var|case|else|enum|eval|null|this|true|void|with|break|catch|class|const|false|super|throw|while|yield|delete|export|import|public|return|static|switch|typeof|default|extends|finally|package|private|continue|debugger|function|arguments|interface|protected|implements|instanceof)$/
+
+const parse = code => {
+  const regex = /(\/\/|\/\*|\*\/|\${|\,|\/|\\|\(|\)|\{|\}|\[|\]|\`|\'|\"|\n| +|\w+|[^\w\s]+)/gm
+  let match
+  const ret = []
+  while (match = regex.exec(code)) {
+    ret.push(match[1])
+  }
+  return ret
+}
+
+const defaultScope = ['root', 'paren', 'bracket', 'square', 'interpolation']
+const pairs = [
+  { open: '(', close: ')', name: 'paren', scope: defaultScope },
+  { open: '{', close: '}', name: 'bracket', scope: defaultScope },
+  { open: '[', close: ']', name: 'square', scope: defaultScope },
+  { open: "'", close: "'", name: 'string', scope: defaultScope },
+  { open: '"', close: '"', name: 'string', scope: defaultScope },
+  { open: '`', close: '`', name: 'template', scope: defaultScope },
+  { open: '${', close: '}', name: 'interpolation', scope: ['template'] },
+  { open: '//', close: '\n', name: 'comment', scope: defaultScope }, 
+  { open: '/*', close: '*/', name: 'block-comment', scope: defaultScope }
+]
+
+const organize = codes => {
+  codes = codes.slice().reverse()
+  
+  const read = (closer, scope) => {
+    const ret = []
+    let escaped = false
+    while (codes.length) {
+      const c = codes.pop()
+      if (c === '\\' && !escaped) {
+        escaped = true
+        ret.push({
+          text: c,
+          type: 'default'
+        })
+        continue
+      } else if (c === closer && !escaped) {
+        ret.push({
+          text: c,
+          type: scope
+        })
+        return ret
+      } else {
+        const pair = pairs.find(pair => pair.open === c && pair.scope.includes(scope))
+        if (!escaped && pair) {
+          const next = codes[codes.length - 1]
+          if (/^\s/.test(next)) {
+            // Block begins with whitespace. Concatenate.
+            ret.push({
+              text: c + codes.pop(),
+              type: 'open-' + pair.name
+            })
+          } else {
+            // Block begins with character. Append only block opener
+            ret.push({
+              text: c,
+              type: 'open-' + pair.name
+            })
+          }
+          const children = read(pair.close, pair.name)
+          ret.push({
+            children,
+            type: pair.name
+          })
+          const blockEnd = []
+          if (peek(children).text === pair.close) {
+            const blockCloser = children.pop()
+            blockEnd.push(blockCloser.text)
+          }
+          while (/^\s+$/.test(peek(children).text)) {
+            blockEnd.unshift(children.pop().text)
+          }
+          if (blockEnd.length) {
+            ret.push({
+              text: blockEnd.join(''),
+              type: 'close-' + pair.name
+            })
+          }
+        } else {
+          ret.push({
+            text: c,
+            type: 'default'
+          })
+        }
+      }
+      escaped = false
+    } // while
+    return ret
+  }
+  return read(null, 'root')
+}
+const toDom = (codes) => {
+  const toDom = (codes) => {
+    return codes.map(code => {
+      if (Array.isArray(code)) {
+        const container = document.createElement('span')
+        toDom(code).forEach(child => container.appendChild(child))
+        return container
+      } else {
+        const textNode = document.createTextNode(code)
+        const container = document.createElement('span')
+        container.appendChild(textNode)
+        return container
+      }
+    })
+  }
+  return toDom([codes])[0]
+}
+
+const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
+const anim = ms => Promise.all([ sleep(ms), new Promise(requestAnimationFrame) ])
+const peek = (a => a && a.length  && a[a.length - 1]) || {}
+const isBlock = x => x && x.children
+
+const toDomAsync = async (codes, target, delay) => {
+  const toDom = async (codes, target) => {
+    codes = codes.slice().reverse()
+    const wrap = async (code, target, instant) => {
+      const container = document.createElement('span')
+      target.appendChild(container)
+      container.className = 'code-' + code.type
+      const checkKeyword = () => {
+        if (keywordRegex.test(container.innerText)) {
+          container.classList.add('code-keyword')
+        } else {
+          container.classList.remove('code-keyword')
+        }
+      }
+      if (instant) {
+        container.setAttribute('text', code.text)
+        container.appendChild(document.createTextNode(code.text))
+        checkKeyword()
+      } else {
+        for (let i = 0; i <= code.text.length; i++) {
+          container.innerText = ''
+          container.setAttribute('text', code.text.substr(0, i))
+          container.appendChild(document.createTextNode(code.text.substr(0, i)))
+          checkKeyword()
+          await anim(delay)
+        }
+      }
+      return container
+    }
+    while(codes.length) {
+      const code = codes.pop()
+      if (Array.isArray(code.children)) {
+        await toDom(code.children, target)
+      } else if (isBlock(peek(codes))) {
+        await wrap(code, target, true)
+        const container = document.createElement('span')
+        const children = codes.pop()
+        container.className = 'code-' + children.type
+        const next = codes.pop()
+        target.appendChild(container)
+        if (next) {
+          await wrap(next, target, true)
+        }
+        await anim(delay)
+        await toDom(children.children, container)
+      } else {
+        await wrap(code, target)
+      }
+      await anim(delay)
+    }
+  }
+  target.classList.add('code-root')
+  await toDom(codes, target)
+}
+
+const animateCode =async ({code, target}) => {
+  const codes = parse(code)
+  const organized = organize(codes)
+  await toDomAsync(organized, target, 0)
+}
+
+
+const main = async () => {
+  const target = document.querySelector('pre')
+  target.innerHTML = ''
+  await animateCode({code: sampleCode, target})
+  await sleep(15000)
+  await main()
+}
+main()