Skip to content
Snippets Groups Projects
index.js 7.36 MiB
Newer Older
  • Learn to ignore specific revisions
  • Hugo NOUTS's avatar
    Hugo NOUTS committed
    220001 220002 220003 220004 220005 220006 220007 220008 220009 220010 220011 220012 220013 220014 220015 220016 220017 220018 220019 220020 220021 220022 220023 220024 220025 220026 220027 220028 220029 220030 220031 220032 220033 220034 220035 220036 220037 220038 220039 220040 220041 220042 220043 220044 220045 220046 220047 220048 220049 220050 220051 220052 220053 220054 220055 220056 220057 220058 220059 220060 220061 220062 220063 220064 220065 220066 220067 220068 220069 220070 220071 220072 220073 220074 220075 220076 220077 220078 220079 220080 220081 220082 220083 220084 220085 220086 220087 220088 220089 220090 220091 220092 220093 220094 220095 220096 220097 220098 220099 220100 220101 220102 220103 220104 220105 220106 220107 220108 220109 220110 220111 220112 220113 220114 220115 220116 220117 220118 220119 220120 220121 220122 220123 220124 220125 220126 220127 220128 220129 220130 220131 220132 220133 220134 220135 220136 220137 220138 220139 220140 220141 220142 220143 220144 220145 220146 220147 220148 220149 220150 220151 220152 220153 220154 220155 220156 220157 220158 220159 220160 220161 220162 220163 220164 220165 220166 220167 220168 220169 220170 220171 220172 220173 220174 220175 220176 220177 220178 220179 220180 220181 220182 220183 220184 220185 220186 220187 220188 220189 220190 220191 220192 220193 220194 220195 220196 220197 220198 220199 220200 220201 220202 220203 220204 220205 220206 220207 220208 220209 220210 220211 220212 220213 220214 220215 220216 220217 220218 220219 220220 220221 220222 220223 220224 220225 220226 220227 220228 220229 220230 220231 220232 220233 220234 220235 220236 220237 220238 220239 220240 220241 220242 220243 220244 220245 220246 220247 220248 220249 220250 220251 220252 220253 220254 220255 220256 220257 220258 220259 220260 220261 220262 220263 220264 220265 220266 220267 220268 220269 220270 220271 220272 220273 220274 220275 220276 220277 220278 220279 220280 220281 220282 220283 220284 220285 220286 220287 220288 220289 220290 220291 220292 220293 220294 220295 220296 220297 220298 220299 220300 220301 220302 220303 220304 220305 220306 220307 220308 220309 220310 220311 220312 220313 220314 220315 220316 220317 220318 220319 220320 220321 220322 220323 220324 220325 220326 220327 220328 220329 220330 220331 220332 220333 220334 220335 220336 220337 220338 220339 220340 220341 220342 220343 220344 220345 220346 220347 220348 220349 220350 220351 220352 220353 220354 220355 220356 220357 220358 220359 220360 220361 220362 220363 220364 220365 220366 220367 220368 220369 220370 220371 220372 220373 220374 220375 220376 220377 220378 220379 220380 220381 220382 220383 220384 220385 220386 220387 220388 220389 220390 220391 220392 220393 220394 220395 220396 220397 220398 220399 220400 220401 220402 220403 220404 220405 220406 220407 220408 220409 220410 220411 220412 220413 220414 220415 220416 220417 220418 220419 220420 220421 220422 220423 220424 220425 220426 220427 220428 220429 220430 220431 220432 220433 220434 220435 220436 220437 220438 220439 220440 220441 220442 220443 220444 220445 220446 220447 220448 220449 220450 220451 220452 220453 220454 220455 220456 220457 220458 220459 220460 220461 220462 220463 220464 220465 220466 220467 220468 220469 220470 220471 220472 220473 220474 220475 220476 220477 220478 220479 220480 220481 220482 220483 220484 220485 220486 220487 220488 220489 220490 220491 220492 220493 220494 220495 220496 220497 220498 220499 220500 220501 220502 220503 220504 220505 220506 220507 220508 220509 220510 220511 220512 220513 220514 220515 220516 220517 220518 220519 220520 220521 220522 220523 220524 220525 220526 220527 220528 220529 220530 220531 220532 220533 220534 220535 220536 220537 220538 220539 220540 220541 220542 220543 220544 220545 220546 220547 220548 220549 220550 220551 220552 220553 220554 220555 220556 220557 220558 220559 220560 220561 220562 220563 220564 220565 220566 220567 220568 220569 220570 220571 220572 220573 220574 220575 220576 220577 220578 220579 220580 220581 220582 220583 220584 220585 220586 220587 220588 220589 220590 220591 220592 220593 220594 220595 220596 220597 220598 220599 220600 220601 220602 220603 220604 220605 220606 220607 220608 220609 220610 220611 220612 220613 220614 220615 220616 220617 220618 220619 220620 220621 220622 220623 220624 220625 220626 220627 220628 220629 220630 220631 220632 220633 220634 220635 220636 220637 220638 220639 220640 220641 220642 220643 220644 220645 220646 220647 220648 220649 220650 220651 220652 220653 220654 220655 220656 220657 220658 220659 220660 220661 220662 220663 220664 220665 220666 220667 220668 220669 220670 220671 220672 220673 220674 220675 220676 220677 220678 220679 220680 220681 220682 220683 220684 220685 220686 220687 220688 220689 220690 220691 220692 220693 220694 220695 220696 220697 220698 220699 220700 220701 220702 220703 220704 220705 220706 220707 220708 220709 220710 220711 220712 220713 220714 220715 220716 220717 220718 220719 220720 220721 220722 220723 220724 220725 220726 220727 220728 220729 220730 220731 220732 220733 220734 220735 220736 220737 220738 220739 220740 220741 220742 220743 220744 220745 220746 220747 220748 220749 220750 220751 220752 220753 220754 220755 220756 220757 220758 220759 220760 220761 220762 220763 220764 220765 220766 220767 220768 220769 220770 220771 220772 220773 220774 220775 220776 220777 220778 220779 220780 220781 220782 220783 220784
            })
          }
    
          // 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
    /* 1540 */
    
    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
    /* 1541 */
    
    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
    /* 1542 */
    
    Hugo NOUTS's avatar
    Hugo NOUTS committed
    /***/ ((module) => {
    
    "use strict";
    module.exports = require("timers");
    
    /***/ }),
    
    build-token's avatar
    build-token committed
    /* 1543 */
    
    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)
      if (result && result.length > 0) {
        // Filter data to remove
        var filtered = []
        if (doctype === 'com.grandlyon.enedis.year') {
          // Yearly case
          filtered = result.filter(function(el) {
            return el.year == data.year
          })
        } else if (doctype === 'com.grandlyon.enedis.month') {
          // Monthly case
          filtered = result.filter(function(el) {
            return el.year == data.year && el.month == data.month
          })
        } else {
          // Hourly case
          filtered = result.filter(function(el) {
            return (
              el.year == data.year &&
              el.month == data.month &&
              el.day == data.day &&
              el.hour == data.hour
            )
          })
        }
        // Remove data
        let sum = 0.0
        // eslint-disable-next-line no-unused-vars
        for (const doc of filtered) {
          sum += doc.load
          log('debug', doc, 'Removing this entry for ' + doctype)
          await cozyClient.data.delete(doctype, doc)
        }
        return sum
      }
      return 0.0
    }
    
    module.exports = {
      buildAgregatedData,
    }
    
    
    /***/ }),
    
    build-token's avatar
    build-token committed
    /* 1544 */
    
    Hugo NOUTS's avatar
    Hugo NOUTS committed
    /***/ ((module, __unused_webpack_exports, __webpack_require__) => {
    
    // @ts-check
    const { log } = __webpack_require__(1)
    
    build-token's avatar
    build-token committed
    const moment = __webpack_require__(1362)
    
    Hugo NOUTS's avatar
    Hugo NOUTS committed
    
    /**
     * Return start date
     * @param {string} result
     * @returns {string}
     */
    function parseSgeXmlTechnicalData(result) {
      log('info', 'Parsing technical data')
      let json = JSON.stringify(result)
      return JSON.parse(json)['Envelope']['Body'][
        'consulterDonneesTechniquesContractuellesResponse'
      ]['point']['donneesGenerales'][
        'dateDerniereModificationFormuleTarifaireAcheminement'
      ]
    }
    
    /**
     * Parsing SGE xml reply to get only mesure data
     * @param {string} result
     * @returns {SGEData[]}
     */
    function parseSgeXmlData(result) {
      log('info', 'Parsing list of documents')
      let json = JSON.stringify(result)
      return JSON.parse(json)['Envelope']['Body'][
        'consulterMesuresDetailleesResponse'
      ]['grandeur']['mesure']
    }
    
    /**
     * Format data for DB storage
     * @param {SGEData[]} data
     * @returns {Promise<EnedisKonnectorData[]>} Parsed timestamp array