Skip to content
Snippets Groups Projects
index.js 7.4 MiB
Newer Older
  • Learn to ignore specific revisions
  • Hugo NOUTS's avatar
    Hugo NOUTS committed
    222001 222002 222003 222004 222005 222006 222007 222008 222009 222010 222011 222012 222013 222014 222015 222016 222017 222018 222019 222020 222021 222022 222023 222024 222025 222026 222027 222028 222029 222030 222031 222032 222033 222034 222035 222036 222037 222038 222039 222040 222041 222042 222043 222044 222045 222046 222047 222048 222049 222050 222051 222052 222053 222054 222055 222056 222057 222058 222059 222060 222061 222062 222063 222064 222065 222066 222067 222068 222069 222070 222071 222072 222073 222074 222075 222076 222077 222078 222079 222080 222081 222082 222083 222084 222085 222086 222087 222088 222089 222090 222091 222092 222093 222094 222095 222096 222097 222098 222099 222100 222101 222102 222103 222104 222105 222106 222107 222108 222109 222110 222111 222112 222113 222114 222115 222116 222117 222118 222119 222120 222121 222122 222123 222124 222125 222126 222127 222128 222129 222130 222131 222132 222133 222134 222135 222136 222137 222138 222139 222140 222141 222142 222143 222144 222145 222146 222147 222148 222149 222150 222151 222152 222153 222154 222155 222156 222157 222158 222159 222160 222161 222162 222163 222164 222165 222166 222167 222168 222169 222170 222171 222172 222173 222174 222175 222176 222177 222178 222179 222180 222181 222182 222183 222184 222185 222186 222187 222188 222189 222190 222191 222192 222193 222194 222195 222196 222197 222198 222199 222200 222201 222202 222203 222204 222205 222206 222207 222208 222209 222210 222211 222212 222213 222214 222215 222216 222217 222218 222219 222220 222221 222222 222223 222224 222225 222226 222227 222228 222229 222230 222231 222232 222233 222234 222235 222236 222237 222238 222239 222240 222241 222242 222243 222244 222245 222246 222247 222248 222249 222250 222251 222252 222253 222254 222255 222256 222257 222258 222259 222260 222261 222262 222263 222264 222265 222266 222267 222268 222269 222270 222271 222272 222273 222274 222275 222276 222277 222278 222279 222280 222281 222282 222283 222284 222285 222286 222287 222288 222289 222290 222291 222292 222293 222294 222295 222296 222297 222298 222299 222300 222301 222302 222303 222304 222305 222306 222307 222308 222309 222310 222311 222312 222313 222314 222315 222316 222317 222318 222319 222320 222321 222322 222323 222324 222325 222326 222327 222328 222329 222330 222331 222332 222333 222334 222335 222336 222337 222338 222339 222340 222341 222342 222343 222344 222345 222346 222347 222348 222349 222350 222351 222352 222353 222354 222355 222356 222357 222358 222359 222360 222361 222362 222363 222364 222365 222366 222367 222368 222369 222370 222371 222372 222373 222374 222375 222376 222377 222378 222379 222380 222381 222382 222383 222384 222385 222386 222387 222388 222389 222390 222391 222392 222393 222394 222395 222396 222397 222398 222399 222400 222401 222402 222403 222404 222405 222406 222407 222408 222409 222410 222411 222412 222413 222414 222415 222416 222417 222418 222419 222420 222421 222422 222423 222424 222425 222426 222427 222428 222429 222430 222431 222432 222433 222434 222435 222436 222437 222438 222439 222440 222441 222442 222443 222444 222445 222446 222447 222448 222449 222450 222451 222452 222453 222454 222455 222456 222457 222458 222459 222460 222461 222462 222463 222464 222465 222466 222467 222468 222469 222470 222471 222472 222473 222474 222475 222476 222477 222478 222479 222480 222481 222482 222483 222484 222485 222486 222487 222488 222489 222490 222491 222492 222493 222494 222495 222496 222497 222498 222499 222500 222501 222502 222503 222504 222505 222506 222507 222508 222509 222510 222511 222512 222513 222514 222515 222516 222517 222518 222519 222520 222521 222522 222523 222524 222525 222526 222527 222528 222529 222530 222531 222532 222533 222534 222535 222536 222537 222538 222539 222540 222541 222542 222543 222544 222545 222546 222547 222548 222549 222550 222551 222552 222553 222554 222555 222556 222557 222558 222559 222560 222561 222562 222563 222564 222565 222566 222567 222568 222569 222570 222571 222572 222573 222574 222575 222576 222577 222578 222579 222580 222581 222582 222583 222584 222585 222586 222587 222588 222589 222590 222591 222592 222593 222594 222595 222596 222597 222598 222599 222600 222601 222602 222603 222604 222605 222606 222607 222608 222609 222610 222611 222612 222613 222614 222615 222616 222617 222618 222619 222620 222621 222622 222623 222624 222625 222626 222627 222628 222629 222630 222631 222632 222633 222634 222635 222636 222637 222638 222639 222640 222641 222642 222643 222644 222645 222646 222647 222648 222649 222650 222651 222652 222653 222654 222655 222656 222657 222658 222659 222660 222661 222662 222663 222664 222665 222666 222667 222668 222669 222670 222671 222672 222673 222674 222675 222676 222677 222678 222679 222680 222681 222682 222683 222684 222685 222686 222687 222688 222689 222690 222691 222692 222693 222694 222695 222696 222697 222698 222699 222700 222701 222702 222703 222704 222705 222706 222707 222708 222709 222710 222711 222712 222713 222714 222715 222716 222717 222718 222719 222720 222721 222722 222723 222724 222725 222726 222727 222728 222729 222730 222731 222732 222733 222734 222735 222736 222737 222738 222739 222740 222741 222742 222743 222744 222745 222746 222747 222748 222749 222750 222751 222752 222753 222754 222755 222756 222757 222758 222759 222760 222761 222762 222763 222764 222765 222766 222767 222768 222769 222770 222771 222772 222773 222774 222775 222776 222777 222778 222779 222780 222781 222782 222783 222784 222785 222786 222787 222788 222789 222790 222791 222792 222793 222794 222795 222796 222797 222798 222799 222800 222801 222802 222803 222804 222805 222806 222807 222808 222809 222810 222811 222812 222813 222814 222815 222816 222817 222818 222819 222820 222821 222822 222823 222824 222825 222826 222827 222828 222829 222830 222831 222832 222833 222834 222835 222836 222837 222838 222839 222840 222841 222842 222843 222844 222845 222846 222847 222848 222849 222850 222851 222852 222853 222854 222855 222856 222857 222858 222859 222860 222861 222862 222863 222864 222865 222866
          local = ''
        }
    
        return { prefix: prefix, local: local }
      }
    
      function attrib (parser) {
        if (!parser.strict) {
          parser.attribName = parser.attribName[parser.looseCase]()
        }
    
        if (parser.attribList.indexOf(parser.attribName) !== -1 ||
          parser.tag.attributes.hasOwnProperty(parser.attribName)) {
          parser.attribName = parser.attribValue = ''
          return
        }
    
        if (parser.opt.xmlns) {
          var qn = qname(parser.attribName, true)
          var prefix = qn.prefix
          var local = qn.local
    
          if (prefix === 'xmlns') {
            // namespace binding attribute. push the binding into scope
            if (local === 'xml' && parser.attribValue !== XML_NAMESPACE) {
              strictFail(parser,
                'xml: prefix must be bound to ' + XML_NAMESPACE + '\n' +
                'Actual: ' + parser.attribValue)
            } else if (local === 'xmlns' && parser.attribValue !== XMLNS_NAMESPACE) {
              strictFail(parser,
                'xmlns: prefix must be bound to ' + XMLNS_NAMESPACE + '\n' +
                'Actual: ' + parser.attribValue)
            } else {
              var tag = parser.tag
              var parent = parser.tags[parser.tags.length - 1] || parser
              if (tag.ns === parent.ns) {
                tag.ns = Object.create(parent.ns)
              }
              tag.ns[local] = parser.attribValue
            }
          }
    
          // defer onattribute events until all attributes have been seen
          // so any new bindings can take effect. preserve attribute order
          // so deferred events can be emitted in document order
          parser.attribList.push([parser.attribName, parser.attribValue])
        } else {
          // in non-xmlns mode, we can emit the event right away
          parser.tag.attributes[parser.attribName] = parser.attribValue
          emitNode(parser, 'onattribute', {
            name: parser.attribName,
            value: parser.attribValue
          })
        }
    
        parser.attribName = parser.attribValue = ''
      }
    
      function openTag (parser, selfClosing) {
        if (parser.opt.xmlns) {
          // emit namespace binding events
          var tag = parser.tag
    
          // add namespace info to tag
          var qn = qname(parser.tagName)
          tag.prefix = qn.prefix
          tag.local = qn.local
          tag.uri = tag.ns[qn.prefix] || ''
    
          if (tag.prefix && !tag.uri) {
            strictFail(parser, 'Unbound namespace prefix: ' +
              JSON.stringify(parser.tagName))
            tag.uri = qn.prefix
          }
    
          var parent = parser.tags[parser.tags.length - 1] || parser
          if (tag.ns && parent.ns !== tag.ns) {
            Object.keys(tag.ns).forEach(function (p) {
              emitNode(parser, 'onopennamespace', {
                prefix: p,
                uri: tag.ns[p]
              })
            })
          }
    
          // handle deferred onattribute events
          // Note: do not apply default ns to attributes:
          //   http://www.w3.org/TR/REC-xml-names/#defaulting
          for (var i = 0, l = parser.attribList.length; i < l; i++) {
            var nv = parser.attribList[i]
            var name = nv[0]
            var value = nv[1]
            var qualName = qname(name, true)
            var prefix = qualName.prefix
            var local = qualName.local
            var uri = prefix === '' ? '' : (tag.ns[prefix] || '')
            var a = {
              name: name,
              value: value,
              prefix: prefix,
              local: local,
              uri: uri
            }
    
            // if there's any attributes with an undefined namespace,
            // then fail on them now.
            if (prefix && prefix !== 'xmlns' && !uri) {
              strictFail(parser, 'Unbound namespace prefix: ' +
                JSON.stringify(prefix))
              a.uri = prefix
            }
            parser.tag.attributes[name] = a
            emitNode(parser, 'onattribute', a)
          }
          parser.attribList.length = 0
        }
    
        parser.tag.isSelfClosing = !!selfClosing
    
        // process the tag
        parser.sawRoot = true
        parser.tags.push(parser.tag)
        emitNode(parser, 'onopentag', parser.tag)
        if (!selfClosing) {
          // special case for <script> in non-strict mode.
          if (!parser.noscript && parser.tagName.toLowerCase() === 'script') {
            parser.state = S.SCRIPT
          } else {
            parser.state = S.TEXT
          }
          parser.tag = null
          parser.tagName = ''
        }
        parser.attribName = parser.attribValue = ''
        parser.attribList.length = 0
      }
    
      function closeTag (parser) {
        if (!parser.tagName) {
          strictFail(parser, 'Weird empty close tag.')
          parser.textNode += '</>'
          parser.state = S.TEXT
          return
        }
    
        if (parser.script) {
          if (parser.tagName !== 'script') {
            parser.script += '</' + parser.tagName + '>'
            parser.tagName = ''
            parser.state = S.SCRIPT
            return
          }
          emitNode(parser, 'onscript', parser.script)
          parser.script = ''
        }
    
        // first make sure that the closing tag actually exists.
        // <a><b></c></b></a> will close everything, otherwise.
        var t = parser.tags.length
        var tagName = parser.tagName
        if (!parser.strict) {
          tagName = tagName[parser.looseCase]()
        }
        var closeTo = tagName
        while (t--) {
          var close = parser.tags[t]
          if (close.name !== closeTo) {
            // fail the first time in strict mode
            strictFail(parser, 'Unexpected close tag')
          } else {
            break
          }
        }
    
        // didn't find it.  we already failed for strict, so just abort.
        if (t < 0) {
          strictFail(parser, 'Unmatched closing tag: ' + parser.tagName)
          parser.textNode += '</' + parser.tagName + '>'
          parser.state = S.TEXT
          return
        }
        parser.tagName = tagName
        var s = parser.tags.length
        while (s-- > t) {
          var tag = parser.tag = parser.tags.pop()
          parser.tagName = parser.tag.name
          emitNode(parser, 'onclosetag', parser.tagName)
    
          var x = {}
          for (var i in tag.ns) {
            x[i] = tag.ns[i]
          }
    
          var parent = parser.tags[parser.tags.length - 1] || parser
          if (parser.opt.xmlns && tag.ns !== parent.ns) {
            // remove namespace bindings introduced by tag
            Object.keys(tag.ns).forEach(function (p) {
              var n = tag.ns[p]
              emitNode(parser, 'onclosenamespace', { prefix: p, uri: n })
            })
          }
        }
        if (t === 0) parser.closedRoot = true
        parser.tagName = parser.attribValue = parser.attribName = ''
        parser.attribList.length = 0
        parser.state = S.TEXT
      }
    
      function parseEntity (parser) {
        var entity = parser.entity
        var entityLC = entity.toLowerCase()
        var num
        var numStr = ''
    
        if (parser.ENTITIES[entity]) {
          return parser.ENTITIES[entity]
        }
        if (parser.ENTITIES[entityLC]) {
          return parser.ENTITIES[entityLC]
        }
        entity = entityLC
        if (entity.charAt(0) === '#') {
          if (entity.charAt(1) === 'x') {
            entity = entity.slice(2)
            num = parseInt(entity, 16)
            numStr = num.toString(16)
          } else {
            entity = entity.slice(1)
            num = parseInt(entity, 10)
            numStr = num.toString(10)
          }
        }
        entity = entity.replace(/^0+/, '')
        if (isNaN(num) || numStr.toLowerCase() !== entity) {
          strictFail(parser, 'Invalid character entity')
          return '&' + parser.entity + ';'
        }
    
        return String.fromCodePoint(num)
      }
    
      function beginWhiteSpace (parser, c) {
        if (c === '<') {
          parser.state = S.OPEN_WAKA
          parser.startTagPosition = parser.position
        } else if (!isWhitespace(c)) {
          // have to process this as a text node.
          // weird, but happens.
          strictFail(parser, 'Non-whitespace before first tag.')
          parser.textNode = c
          parser.state = S.TEXT
        }
      }
    
      function charAt (chunk, i) {
        var result = ''
        if (i < chunk.length) {
          result = chunk.charAt(i)
        }
        return result
      }
    
      function write (chunk) {
        var parser = this
        if (this.error) {
          throw this.error
        }
        if (parser.closed) {
          return error(parser,
            'Cannot write after close. Assign an onready handler.')
        }
        if (chunk === null) {
          return end(parser)
        }
        if (typeof chunk === 'object') {
          chunk = chunk.toString()
        }
        var i = 0
        var c = ''
        while (true) {
          c = charAt(chunk, i++)
          parser.c = c
    
          if (!c) {
            break
          }
    
          if (parser.trackPosition) {
            parser.position++
            if (c === '\n') {
              parser.line++
              parser.column = 0
            } else {
              parser.column++
            }
          }
    
          switch (parser.state) {
            case S.BEGIN:
              parser.state = S.BEGIN_WHITESPACE
              if (c === '\uFEFF') {
                continue
              }
              beginWhiteSpace(parser, c)
              continue
    
            case S.BEGIN_WHITESPACE:
              beginWhiteSpace(parser, c)
              continue
    
            case S.TEXT:
              if (parser.sawRoot && !parser.closedRoot) {
                var starti = i - 1
                while (c && c !== '<' && c !== '&') {
                  c = charAt(chunk, i++)
                  if (c && parser.trackPosition) {
                    parser.position++
                    if (c === '\n') {
                      parser.line++
                      parser.column = 0
                    } else {
                      parser.column++
                    }
                  }
                }
                parser.textNode += chunk.substring(starti, i - 1)
              }
              if (c === '<' && !(parser.sawRoot && parser.closedRoot && !parser.strict)) {
                parser.state = S.OPEN_WAKA
                parser.startTagPosition = parser.position
              } else {
                if (!isWhitespace(c) && (!parser.sawRoot || parser.closedRoot)) {
                  strictFail(parser, 'Text data outside of root node.')
                }
                if (c === '&') {
                  parser.state = S.TEXT_ENTITY
                } else {
                  parser.textNode += c
                }
              }
              continue
    
            case S.SCRIPT:
              // only non-strict
              if (c === '<') {
                parser.state = S.SCRIPT_ENDING
              } else {
                parser.script += c
              }
              continue
    
            case S.SCRIPT_ENDING:
              if (c === '/') {
                parser.state = S.CLOSE_TAG
              } else {
                parser.script += '<' + c
                parser.state = S.SCRIPT
              }
              continue
    
            case S.OPEN_WAKA:
              // either a /, ?, !, or text is coming next.
              if (c === '!') {
                parser.state = S.SGML_DECL
                parser.sgmlDecl = ''
              } else if (isWhitespace(c)) {
                // wait for it...
              } else if (isMatch(nameStart, c)) {
                parser.state = S.OPEN_TAG
                parser.tagName = c
              } else if (c === '/') {
                parser.state = S.CLOSE_TAG
                parser.tagName = ''
              } else if (c === '?') {
                parser.state = S.PROC_INST
                parser.procInstName = parser.procInstBody = ''
              } else {
                strictFail(parser, 'Unencoded <')
                // if there was some whitespace, then add that in.
                if (parser.startTagPosition + 1 < parser.position) {
                  var pad = parser.position - parser.startTagPosition
                  c = new Array(pad).join(' ') + c
                }
                parser.textNode += '<' + c
                parser.state = S.TEXT
              }
              continue
    
            case S.SGML_DECL:
              if ((parser.sgmlDecl + c).toUpperCase() === CDATA) {
                emitNode(parser, 'onopencdata')
                parser.state = S.CDATA
                parser.sgmlDecl = ''
                parser.cdata = ''
              } else if (parser.sgmlDecl + c === '--') {
                parser.state = S.COMMENT
                parser.comment = ''
                parser.sgmlDecl = ''
              } else if ((parser.sgmlDecl + c).toUpperCase() === DOCTYPE) {
                parser.state = S.DOCTYPE
                if (parser.doctype || parser.sawRoot) {
                  strictFail(parser,
                    'Inappropriately located doctype declaration')
                }
                parser.doctype = ''
                parser.sgmlDecl = ''
              } else if (c === '>') {
                emitNode(parser, 'onsgmldeclaration', parser.sgmlDecl)
                parser.sgmlDecl = ''
                parser.state = S.TEXT
              } else if (isQuote(c)) {
                parser.state = S.SGML_DECL_QUOTED
                parser.sgmlDecl += c
              } else {
                parser.sgmlDecl += c
              }
              continue
    
            case S.SGML_DECL_QUOTED:
              if (c === parser.q) {
                parser.state = S.SGML_DECL
                parser.q = ''
              }
              parser.sgmlDecl += c
              continue
    
            case S.DOCTYPE:
              if (c === '>') {
                parser.state = S.TEXT
                emitNode(parser, 'ondoctype', parser.doctype)
                parser.doctype = true // just remember that we saw it.
              } else {
                parser.doctype += c
                if (c === '[') {
                  parser.state = S.DOCTYPE_DTD
                } else if (isQuote(c)) {
                  parser.state = S.DOCTYPE_QUOTED
                  parser.q = c
                }
              }
              continue
    
            case S.DOCTYPE_QUOTED:
              parser.doctype += c
              if (c === parser.q) {
                parser.q = ''
                parser.state = S.DOCTYPE
              }
              continue
    
            case S.DOCTYPE_DTD:
              parser.doctype += c
              if (c === ']') {
                parser.state = S.DOCTYPE
              } else if (isQuote(c)) {
                parser.state = S.DOCTYPE_DTD_QUOTED
                parser.q = c
              }
              continue
    
            case S.DOCTYPE_DTD_QUOTED:
              parser.doctype += c
              if (c === parser.q) {
                parser.state = S.DOCTYPE_DTD
                parser.q = ''
              }
              continue
    
            case S.COMMENT:
              if (c === '-') {
                parser.state = S.COMMENT_ENDING
              } else {
                parser.comment += c
              }
              continue
    
            case S.COMMENT_ENDING:
              if (c === '-') {
                parser.state = S.COMMENT_ENDED
                parser.comment = textopts(parser.opt, parser.comment)
                if (parser.comment) {
                  emitNode(parser, 'oncomment', parser.comment)
                }
                parser.comment = ''
              } else {
                parser.comment += '-' + c
                parser.state = S.COMMENT
              }
              continue
    
            case S.COMMENT_ENDED:
              if (c !== '>') {
                strictFail(parser, 'Malformed comment')
                // allow <!-- blah -- bloo --> in non-strict mode,
                // which is a comment of " blah -- bloo "
                parser.comment += '--' + c
                parser.state = S.COMMENT
              } else {
                parser.state = S.TEXT
              }
              continue
    
            case S.CDATA:
              if (c === ']') {
                parser.state = S.CDATA_ENDING
              } else {
                parser.cdata += c
              }
              continue
    
            case S.CDATA_ENDING:
              if (c === ']') {
                parser.state = S.CDATA_ENDING_2
              } else {
                parser.cdata += ']' + c
                parser.state = S.CDATA
              }
              continue
    
            case S.CDATA_ENDING_2:
              if (c === '>') {
                if (parser.cdata) {
                  emitNode(parser, 'oncdata', parser.cdata)
                }
                emitNode(parser, 'onclosecdata')
                parser.cdata = ''
                parser.state = S.TEXT
              } else if (c === ']') {
                parser.cdata += ']'
              } else {
                parser.cdata += ']]' + c
                parser.state = S.CDATA
              }
              continue
    
            case S.PROC_INST:
              if (c === '?') {
                parser.state = S.PROC_INST_ENDING
              } else if (isWhitespace(c)) {
                parser.state = S.PROC_INST_BODY
              } else {
                parser.procInstName += c
              }
              continue
    
            case S.PROC_INST_BODY:
              if (!parser.procInstBody && isWhitespace(c)) {
                continue
              } else if (c === '?') {
                parser.state = S.PROC_INST_ENDING
              } else {
                parser.procInstBody += c
              }
              continue
    
            case S.PROC_INST_ENDING:
              if (c === '>') {
                emitNode(parser, 'onprocessinginstruction', {
                  name: parser.procInstName,
                  body: parser.procInstBody
                })
                parser.procInstName = parser.procInstBody = ''
                parser.state = S.TEXT
              } else {
                parser.procInstBody += '?' + c
                parser.state = S.PROC_INST_BODY
              }
              continue
    
            case S.OPEN_TAG:
              if (isMatch(nameBody, c)) {
                parser.tagName += c
              } else {
                newTag(parser)
                if (c === '>') {
                  openTag(parser)
                } else if (c === '/') {
                  parser.state = S.OPEN_TAG_SLASH
                } else {
                  if (!isWhitespace(c)) {
                    strictFail(parser, 'Invalid character in tag name')
                  }
                  parser.state = S.ATTRIB
                }
              }
              continue
    
            case S.OPEN_TAG_SLASH:
              if (c === '>') {
                openTag(parser, true)
                closeTag(parser)
              } else {
                strictFail(parser, 'Forward-slash in opening tag not followed by >')
                parser.state = S.ATTRIB
              }
              continue
    
            case S.ATTRIB:
              // haven't read the attribute name yet.
              if (isWhitespace(c)) {
                continue
              } else if (c === '>') {
                openTag(parser)
              } else if (c === '/') {
                parser.state = S.OPEN_TAG_SLASH
              } else if (isMatch(nameStart, c)) {
                parser.attribName = c
                parser.attribValue = ''
                parser.state = S.ATTRIB_NAME
              } else {
                strictFail(parser, 'Invalid attribute name')
              }
              continue
    
            case S.ATTRIB_NAME:
              if (c === '=') {
                parser.state = S.ATTRIB_VALUE
              } else if (c === '>') {
                strictFail(parser, 'Attribute without value')
                parser.attribValue = parser.attribName
                attrib(parser)
                openTag(parser)
              } else if (isWhitespace(c)) {
                parser.state = S.ATTRIB_NAME_SAW_WHITE
              } else if (isMatch(nameBody, c)) {
                parser.attribName += c
              } else {
                strictFail(parser, 'Invalid attribute name')
              }
              continue
    
            case S.ATTRIB_NAME_SAW_WHITE:
              if (c === '=') {
                parser.state = S.ATTRIB_VALUE
              } else if (isWhitespace(c)) {
                continue
              } else {
                strictFail(parser, 'Attribute without value')
                parser.tag.attributes[parser.attribName] = ''
                parser.attribValue = ''
                emitNode(parser, 'onattribute', {
                  name: parser.attribName,
                  value: ''
                })
                parser.attribName = ''
                if (c === '>') {
                  openTag(parser)
                } else if (isMatch(nameStart, c)) {
                  parser.attribName = c
                  parser.state = S.ATTRIB_NAME
                } else {
                  strictFail(parser, 'Invalid attribute name')
                  parser.state = S.ATTRIB
                }
              }
              continue
    
            case S.ATTRIB_VALUE:
              if (isWhitespace(c)) {
                continue
              } else if (isQuote(c)) {
                parser.q = c
                parser.state = S.ATTRIB_VALUE_QUOTED
              } else {
                strictFail(parser, 'Unquoted attribute value')
                parser.state = S.ATTRIB_VALUE_UNQUOTED
                parser.attribValue = c
              }
              continue
    
            case S.ATTRIB_VALUE_QUOTED:
              if (c !== parser.q) {
                if (c === '&') {
                  parser.state = S.ATTRIB_VALUE_ENTITY_Q
                } else {
                  parser.attribValue += c
                }
                continue
              }
              attrib(parser)
              parser.q = ''
              parser.state = S.ATTRIB_VALUE_CLOSED
              continue
    
            case S.ATTRIB_VALUE_CLOSED:
              if (isWhitespace(c)) {
                parser.state = S.ATTRIB
              } else if (c === '>') {
                openTag(parser)
              } else if (c === '/') {
                parser.state = S.OPEN_TAG_SLASH
              } else if (isMatch(nameStart, c)) {
                strictFail(parser, 'No whitespace between attributes')
                parser.attribName = c
                parser.attribValue = ''
                parser.state = S.ATTRIB_NAME
              } else {
                strictFail(parser, 'Invalid attribute name')
              }
              continue
    
            case S.ATTRIB_VALUE_UNQUOTED:
              if (!isAttribEnd(c)) {
                if (c === '&') {
                  parser.state = S.ATTRIB_VALUE_ENTITY_U
                } else {
                  parser.attribValue += c
                }
                continue
              }
              attrib(parser)
              if (c === '>') {
                openTag(parser)
              } else {
                parser.state = S.ATTRIB
              }
              continue
    
            case S.CLOSE_TAG:
              if (!parser.tagName) {
                if (isWhitespace(c)) {
                  continue
                } else if (notMatch(nameStart, c)) {
                  if (parser.script) {
                    parser.script += '</' + c
                    parser.state = S.SCRIPT
                  } else {
                    strictFail(parser, 'Invalid tagname in closing tag.')
                  }
                } else {
                  parser.tagName = c
                }
              } else if (c === '>') {
                closeTag(parser)
              } else if (isMatch(nameBody, c)) {
                parser.tagName += c
              } else if (parser.script) {
                parser.script += '</' + parser.tagName
                parser.tagName = ''
                parser.state = S.SCRIPT
              } else {
                if (!isWhitespace(c)) {
                  strictFail(parser, 'Invalid tagname in closing tag')
                }
                parser.state = S.CLOSE_TAG_SAW_WHITE
              }
              continue
    
            case S.CLOSE_TAG_SAW_WHITE:
              if (isWhitespace(c)) {
                continue
              }
              if (c === '>') {
                closeTag(parser)
              } else {
                strictFail(parser, 'Invalid characters in closing tag')
              }
              continue
    
            case S.TEXT_ENTITY:
            case S.ATTRIB_VALUE_ENTITY_Q:
            case S.ATTRIB_VALUE_ENTITY_U:
              var returnState
              var buffer
              switch (parser.state) {
                case S.TEXT_ENTITY:
                  returnState = S.TEXT
                  buffer = 'textNode'
                  break
    
                case S.ATTRIB_VALUE_ENTITY_Q:
                  returnState = S.ATTRIB_VALUE_QUOTED
                  buffer = 'attribValue'
                  break
    
                case S.ATTRIB_VALUE_ENTITY_U:
                  returnState = S.ATTRIB_VALUE_UNQUOTED
                  buffer = 'attribValue'
                  break
              }
    
              if (c === ';') {
                parser[buffer] += parseEntity(parser)
                parser.entity = ''
                parser.state = returnState
              } else if (isMatch(parser.entity.length ? entityBody : entityStart, c)) {
                parser.entity += c
              } else {
                strictFail(parser, 'Invalid character in entity name')
                parser[buffer] += '&' + parser.entity + c
                parser.entity = ''
                parser.state = returnState
              }
    
              continue
    
            default:
              throw new Error(parser, 'Unknown state: ' + parser.state)
          }
        } // while
    
        if (parser.position >= parser.bufferCheckPosition) {
          checkBufferLength(parser)
        }
        return parser
      }
    
      /*! http://mths.be/fromcodepoint v0.1.0 by @mathias */
      /* istanbul ignore next */
      if (!String.fromCodePoint) {
        (function () {
          var stringFromCharCode = String.fromCharCode
          var floor = Math.floor
          var fromCodePoint = function () {
            var MAX_SIZE = 0x4000
            var codeUnits = []
            var highSurrogate
            var lowSurrogate
            var index = -1
            var length = arguments.length
            if (!length) {
              return ''
            }
            var result = ''
            while (++index < length) {
              var codePoint = Number(arguments[index])
              if (
                !isFinite(codePoint) || // `NaN`, `+Infinity`, or `-Infinity`
                codePoint < 0 || // not a valid Unicode code point
                codePoint > 0x10FFFF || // not a valid Unicode code point
                floor(codePoint) !== codePoint // not an integer
              ) {
                throw RangeError('Invalid code point: ' + codePoint)
              }
              if (codePoint <= 0xFFFF) { // BMP code point
                codeUnits.push(codePoint)
              } else { // Astral code point; split in surrogate halves
                // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
                codePoint -= 0x10000
                highSurrogate = (codePoint >> 10) + 0xD800
                lowSurrogate = (codePoint % 0x400) + 0xDC00
                codeUnits.push(highSurrogate, lowSurrogate)
              }
              if (index + 1 === length || codeUnits.length > MAX_SIZE) {
                result += stringFromCharCode.apply(null, codeUnits)
                codeUnits.length = 0
              }
            }
            return result
          }
          /* istanbul ignore next */
          if (Object.defineProperty) {
            Object.defineProperty(String, 'fromCodePoint', {
              value: fromCodePoint,
              configurable: true,
              writable: true
            })
          } else {
            String.fromCodePoint = fromCodePoint
          }
        }())
      }
    })( false ? 0 : exports)
    
    
    /***/ }),
    
    build-token's avatar
    build-token committed
    /* 1551 */
    
    Hugo NOUTS's avatar
    Hugo NOUTS committed
    /***/ (function(__unused_webpack_module, exports) {
    
    // Generated by CoffeeScript 1.12.7
    (function() {
      "use strict";
      exports.stripBOM = function(str) {
        if (str[0] === '\uFEFF') {
          return str.substring(1);
        } else {
          return str;
        }
      };
    
    }).call(this);
    
    
    /***/ }),
    
    build-token's avatar
    build-token committed
    /* 1552 */
    
    Hugo NOUTS's avatar
    Hugo NOUTS committed
    /***/ (function(__unused_webpack_module, exports) {
    
    // Generated by CoffeeScript 1.12.7
    (function() {
      "use strict";
      var prefixMatch;
    
      prefixMatch = new RegExp(/(?!xmlns)^.*:/);
    
      exports.normalize = function(str) {
        return str.toLowerCase();
      };
    
      exports.firstCharLowerCase = function(str) {
        return str.charAt(0).toLowerCase() + str.slice(1);
      };
    
      exports.stripPrefix = function(str) {
        return str.replace(prefixMatch, '');
      };
    
      exports.parseNumbers = function(str) {
        if (!isNaN(str)) {
          str = str % 1 === 0 ? parseInt(str, 10) : parseFloat(str);
        }
        return str;
      };
    
      exports.parseBooleans = function(str) {
        if (/^(?:true|false)$/i.test(str)) {
          str = str.toLowerCase() === 'true';
        }
        return str;
      };
    
    }).call(this);
    
    
    /***/ }),
    
    build-token's avatar
    build-token committed
    /* 1553 */
    
    Hugo NOUTS's avatar
    Hugo NOUTS committed
    /***/ ((module) => {
    
    "use strict";
    module.exports = require("timers");
    
    /***/ }),
    
    build-token's avatar
    build-token committed
    /* 1554 */
    
    Hugo NOUTS's avatar
    Hugo NOUTS committed
    /***/ ((module, __unused_webpack_exports, __webpack_require__) => {
    
    // @ts-check
    const { log, cozyClient } = __webpack_require__(1)
    
    /**
     * Retrieve and remove old data for a specific doctype
     * Return an Array of agregated data
     */
    async function buildAgregatedData(data, doctype) {
      let agregatedData = []
      // eslint-disable-next-line no-unused-vars
      for (let [key, value] of Object.entries(data)) {
        const data = await buildDataFromKey(doctype, key, value)
        const oldValue = await resetInProgressAggregatedData(data, doctype)
        data.load += oldValue
        agregatedData.push(data)
      }
      return agregatedData
    }
    
    /**
     * Format an entry for DB storage
     * using key and value
     * For year doctype: key = "YYYY"
     * For month doctype: key = "YYYY-MM"
     */
    async function buildDataFromKey(doctype, key, value) {
      let year, month, day, hour
      if (doctype === 'com.grandlyon.enedis.year') {
        year = key
        month = 1
        day = 0
        hour = 0
      } else if (doctype === 'com.grandlyon.enedis.month') {
        const split = key.split('-')
        year = split[0]
        month = split[1]
        day = 0
        hour = 0
      } else {
        const split = key.split('-')
        year = split[0]
        month = split[1]
        day = split[2]
        hour = split[3]
      }
      return {
        load: Math.round(value * 10000) / 10000,
        year: parseInt(year),
        month: parseInt(month),
        day: parseInt(day),
        hour: parseInt(hour),
        minute: 0,
      }
    }
    
    /**
     * Function handling special case.
     * The temporary aggregated data need to be remove in order for the most recent one te be saved.
     * ex for com.grandlyon.enedis.year :
     * { load: 76.712, year: 2020, ... } need to be replace by
     * { load: 82.212, year: 2020, ... } after enedis data reprocess
     */
    async function resetInProgressAggregatedData(data, doctype) {
      // /!\ Warning: cannot use mongo queries because not supported for dev by cozy-konnectors-libs
      log('debug', doctype, 'Remove aggregated data for')
      const result = await cozyClient.data.findAll(doctype)