tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

qrcode.js (44411B)


      1 /**
      2 * @fileoverview
      3 * - Using the 'QRCode for Javascript library'
      4 * - Fixed dataset of 'QRCode for Javascript library' for support full-spec.
      5 * - this library has no dependencies.
      6 *
      7 * @author davidshimjs
      8 * @see <a href="http://www.d-project.com/" target="_blank">http://www.d-project.com/</a>
      9 * @see <a href="http://jeromeetienne.github.com/jquery-qrcode/" target="_blank">http://jeromeetienne.github.com/jquery-qrcode/</a>
     10 */
     11 var QRCode
     12 ;(function () {
     13  // ---------------------------------------------------------------------
     14  // QRCode for JavaScript
     15  //
     16  // Copyright (c) 2009 Kazuhiko Arase
     17  //
     18  // URL: http://www.d-project.com/
     19  //
     20  // Licensed under the MIT license:
     21  //   http://www.opensource.org/licenses/mit-license.php
     22  //
     23  // The word "QR Code" is registered trademark of
     24  // DENSO WAVE INCORPORATED
     25  //   http://www.denso-wave.com/qrcode/faqpatent-e.html
     26  //
     27  // ---------------------------------------------------------------------
     28  function QR8bitByte (data) {
     29    this.mode = QRMode.MODE_8BIT_BYTE
     30    this.data = data
     31    this.parsedData = []
     32 
     33    // Added to support UTF-8 Characters
     34    for (var i = 0, l = this.data.length; i < l; i++) {
     35      var byteArray = []
     36      var code = this.data.charCodeAt(i)
     37 
     38      if (code > 0x10000) {
     39        byteArray[0] = 0xf0 | ((code & 0x1c0000) >>> 18)
     40        byteArray[1] = 0x80 | ((code & 0x3f000) >>> 12)
     41        byteArray[2] = 0x80 | ((code & 0xfc0) >>> 6)
     42        byteArray[3] = 0x80 | (code & 0x3f)
     43      } else if (code > 0x800) {
     44        byteArray[0] = 0xe0 | ((code & 0xf000) >>> 12)
     45        byteArray[1] = 0x80 | ((code & 0xfc0) >>> 6)
     46        byteArray[2] = 0x80 | (code & 0x3f)
     47      } else if (code > 0x80) {
     48        byteArray[0] = 0xc0 | ((code & 0x7c0) >>> 6)
     49        byteArray[1] = 0x80 | (code & 0x3f)
     50      } else {
     51        byteArray[0] = code
     52      }
     53 
     54      this.parsedData.push(byteArray)
     55    }
     56 
     57    this.parsedData = Array.prototype.concat.apply([], this.parsedData)
     58 
     59    if (this.parsedData.length != this.data.length) {
     60      this.parsedData.unshift(191)
     61      this.parsedData.unshift(187)
     62      this.parsedData.unshift(239)
     63    }
     64  }
     65 
     66  QR8bitByte.prototype = {
     67    getLength: function (buffer) {
     68      return this.parsedData.length
     69    },
     70    write: function (buffer) {
     71      for (var i = 0, l = this.parsedData.length; i < l; i++) {
     72        buffer.put(this.parsedData[i], 8)
     73      }
     74    }
     75  }
     76 
     77  function QRCodeModel (typeNumber, errorCorrectLevel) {
     78    this.typeNumber = typeNumber
     79    this.errorCorrectLevel = errorCorrectLevel
     80    this.modules = null
     81    this.moduleCount = 0
     82    this.dataCache = null
     83    this.dataList = []
     84  }
     85 
     86  QRCodeModel.prototype = {
     87    addData: function (data) {
     88      var newData = new QR8bitByte(data)
     89      this.dataList.push(newData)
     90      this.dataCache = null
     91    },
     92    isDark: function (row, col) {
     93      if (
     94        row < 0 ||
     95        this.moduleCount <= row ||
     96        col < 0 ||
     97        this.moduleCount <= col
     98      ) {
     99        throw new Error(row + ',' + col)
    100      }
    101      return this.modules[row][col]
    102    },
    103    getModuleCount: function () {
    104      return this.moduleCount
    105    },
    106    make: function () {
    107      this.makeImpl(false, this.getBestMaskPattern())
    108    },
    109    makeImpl: function (test, maskPattern) {
    110      this.moduleCount = this.typeNumber * 4 + 17
    111      this.modules = new Array(this.moduleCount)
    112      for (var row = 0; row < this.moduleCount; row++) {
    113        this.modules[row] = new Array(this.moduleCount)
    114        for (var col = 0; col < this.moduleCount; col++) {
    115          this.modules[row][col] = null
    116        }
    117      }
    118      this.setupPositionProbePattern(0, 0)
    119      this.setupPositionProbePattern(this.moduleCount - 7, 0)
    120      this.setupPositionProbePattern(0, this.moduleCount - 7)
    121      this.setupPositionAdjustPattern()
    122      this.setupTimingPattern()
    123      this.setupTypeInfo(test, maskPattern)
    124      if (this.typeNumber >= 7) {
    125        this.setupTypeNumber(test)
    126      }
    127      if (this.dataCache == null) {
    128        this.dataCache = QRCodeModel.createData(
    129          this.typeNumber,
    130          this.errorCorrectLevel,
    131          this.dataList
    132        )
    133      }
    134      this.mapData(this.dataCache, maskPattern)
    135    },
    136    setupPositionProbePattern: function (row, col) {
    137      for (var r = -1; r <= 7; r++) {
    138        if (row + r <= -1 || this.moduleCount <= row + r) continue
    139        for (var c = -1; c <= 7; c++) {
    140          if (col + c <= -1 || this.moduleCount <= col + c) continue
    141          if (
    142            (r >= 0 && r <= 6 && (c == 0 || c == 6)) ||
    143            (c >= 0 && c <= 6 && (r == 0 || r == 6)) ||
    144            (r >= 2 && r <= 4 && c >= 2 && c <= 4)
    145          ) {
    146            this.modules[row + r][col + c] = true
    147          } else {
    148            this.modules[row + r][col + c] = false
    149          }
    150        }
    151      }
    152    },
    153    getBestMaskPattern: function () {
    154      var minLostPoint = 0
    155      var pattern = 0
    156      for (var i = 0; i < 8; i++) {
    157        this.makeImpl(true, i)
    158        var lostPoint = QRUtil.getLostPoint(this)
    159        if (i == 0 || minLostPoint > lostPoint) {
    160          minLostPoint = lostPoint
    161          pattern = i
    162        }
    163      }
    164      return pattern
    165    },
    166    createMovieClip: function (target_mc, instance_name, depth) {
    167      var qr_mc = target_mc.createEmptyMovieClip(instance_name, depth)
    168      var cs = 1
    169      this.make()
    170      for (var row = 0; row < this.modules.length; row++) {
    171        var y = row * cs
    172        for (var col = 0; col < this.modules[row].length; col++) {
    173          var x = col * cs
    174          var dark = this.modules[row][col]
    175          if (dark) {
    176            qr_mc.beginFill(0, 100)
    177            qr_mc.moveTo(x, y)
    178            qr_mc.lineTo(x + cs, y)
    179            qr_mc.lineTo(x + cs, y + cs)
    180            qr_mc.lineTo(x, y + cs)
    181            qr_mc.endFill()
    182          }
    183        }
    184      }
    185      return qr_mc
    186    },
    187    setupTimingPattern: function () {
    188      for (var r = 8; r < this.moduleCount - 8; r++) {
    189        if (this.modules[r][6] != null) {
    190          continue
    191        }
    192        this.modules[r][6] = r % 2 == 0
    193      }
    194      for (var c = 8; c < this.moduleCount - 8; c++) {
    195        if (this.modules[6][c] != null) {
    196          continue
    197        }
    198        this.modules[6][c] = c % 2 == 0
    199      }
    200    },
    201    setupPositionAdjustPattern: function () {
    202      var pos = QRUtil.getPatternPosition(this.typeNumber)
    203      for (var i = 0; i < pos.length; i++) {
    204        for (var j = 0; j < pos.length; j++) {
    205          var row = pos[i]
    206          var col = pos[j]
    207          if (this.modules[row][col] != null) {
    208            continue
    209          }
    210          for (var r = -2; r <= 2; r++) {
    211            for (var c = -2; c <= 2; c++) {
    212              if (
    213                r == -2 ||
    214                r == 2 ||
    215                c == -2 ||
    216                c == 2 ||
    217                (r == 0 && c == 0)
    218              ) {
    219                this.modules[row + r][col + c] = true
    220              } else {
    221                this.modules[row + r][col + c] = false
    222              }
    223            }
    224          }
    225        }
    226      }
    227    },
    228    setupTypeNumber: function (test) {
    229      var bits = QRUtil.getBCHTypeNumber(this.typeNumber)
    230      for (var i = 0; i < 18; i++) {
    231        var mod = !test && ((bits >> i) & 1) == 1
    232        this.modules[Math.floor(i / 3)][i % 3 + this.moduleCount - 8 - 3] = mod
    233      }
    234      for (var i = 0; i < 18; i++) {
    235        var mod = !test && ((bits >> i) & 1) == 1
    236        this.modules[i % 3 + this.moduleCount - 8 - 3][Math.floor(i / 3)] = mod
    237      }
    238    },
    239    setupTypeInfo: function (test, maskPattern) {
    240      var data = (this.errorCorrectLevel << 3) | maskPattern
    241      var bits = QRUtil.getBCHTypeInfo(data)
    242      for (var i = 0; i < 15; i++) {
    243        var mod = !test && ((bits >> i) & 1) == 1
    244        if (i < 6) {
    245          this.modules[i][8] = mod
    246        } else if (i < 8) {
    247          this.modules[i + 1][8] = mod
    248        } else {
    249          this.modules[this.moduleCount - 15 + i][8] = mod
    250        }
    251      }
    252      for (var i = 0; i < 15; i++) {
    253        var mod = !test && ((bits >> i) & 1) == 1
    254        if (i < 8) {
    255          this.modules[8][this.moduleCount - i - 1] = mod
    256        } else if (i < 9) {
    257          this.modules[8][15 - i - 1 + 1] = mod
    258        } else {
    259          this.modules[8][15 - i - 1] = mod
    260        }
    261      }
    262      this.modules[this.moduleCount - 8][8] = !test
    263    },
    264    mapData: function (data, maskPattern) {
    265      var inc = -1
    266      var row = this.moduleCount - 1
    267      var bitIndex = 7
    268      var byteIndex = 0
    269      for (var col = this.moduleCount - 1; col > 0; col -= 2) {
    270        if (col == 6) col--
    271        while (true) {
    272          for (var c = 0; c < 2; c++) {
    273            if (this.modules[row][col - c] == null) {
    274              var dark = false
    275              if (byteIndex < data.length) {
    276                dark = ((data[byteIndex] >>> bitIndex) & 1) == 1
    277              }
    278              var mask = QRUtil.getMask(maskPattern, row, col - c)
    279              if (mask) {
    280                dark = !dark
    281              }
    282              this.modules[row][col - c] = dark
    283              bitIndex--
    284              if (bitIndex == -1) {
    285                byteIndex++
    286                bitIndex = 7
    287              }
    288            }
    289          }
    290          row += inc
    291          if (row < 0 || this.moduleCount <= row) {
    292            row -= inc
    293            inc = -inc
    294            break
    295          }
    296        }
    297      }
    298    }
    299  }
    300  QRCodeModel.PAD0 = 0xec
    301  QRCodeModel.PAD1 = 0x11
    302  QRCodeModel.createData = function (typeNumber, errorCorrectLevel, dataList) {
    303    var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectLevel)
    304    var buffer = new QRBitBuffer()
    305    for (var i = 0; i < dataList.length; i++) {
    306      var data = dataList[i]
    307      buffer.put(data.mode, 4)
    308      buffer.put(
    309        data.getLength(),
    310        QRUtil.getLengthInBits(data.mode, typeNumber)
    311      )
    312      data.write(buffer)
    313    }
    314    var totalDataCount = 0
    315    for (var i = 0; i < rsBlocks.length; i++) {
    316      totalDataCount += rsBlocks[i].dataCount
    317    }
    318    if (buffer.getLengthInBits() > totalDataCount * 8) {
    319      throw new Error(
    320        'code length overflow. (' +
    321          buffer.getLengthInBits() +
    322          '>' +
    323          totalDataCount * 8 +
    324          ')'
    325      )
    326    }
    327    if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) {
    328      buffer.put(0, 4)
    329    }
    330    while (buffer.getLengthInBits() % 8 != 0) {
    331      buffer.putBit(false)
    332    }
    333    while (true) {
    334      if (buffer.getLengthInBits() >= totalDataCount * 8) {
    335        break
    336      }
    337      buffer.put(QRCodeModel.PAD0, 8)
    338      if (buffer.getLengthInBits() >= totalDataCount * 8) {
    339        break
    340      }
    341      buffer.put(QRCodeModel.PAD1, 8)
    342    }
    343    return QRCodeModel.createBytes(buffer, rsBlocks)
    344  }
    345  QRCodeModel.createBytes = function (buffer, rsBlocks) {
    346    var offset = 0
    347    var maxDcCount = 0
    348    var maxEcCount = 0
    349    var dcdata = new Array(rsBlocks.length)
    350    var ecdata = new Array(rsBlocks.length)
    351    for (var r = 0; r < rsBlocks.length; r++) {
    352      var dcCount = rsBlocks[r].dataCount
    353      var ecCount = rsBlocks[r].totalCount - dcCount
    354      maxDcCount = Math.max(maxDcCount, dcCount)
    355      maxEcCount = Math.max(maxEcCount, ecCount)
    356      dcdata[r] = new Array(dcCount)
    357      for (var i = 0; i < dcdata[r].length; i++) {
    358        dcdata[r][i] = 0xff & buffer.buffer[i + offset]
    359      }
    360      offset += dcCount
    361      var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount)
    362      var rawPoly = new QRPolynomial(dcdata[r], rsPoly.getLength() - 1)
    363      var modPoly = rawPoly.mod(rsPoly)
    364      ecdata[r] = new Array(rsPoly.getLength() - 1)
    365      for (var i = 0; i < ecdata[r].length; i++) {
    366        var modIndex = i + modPoly.getLength() - ecdata[r].length
    367        ecdata[r][i] = modIndex >= 0 ? modPoly.get(modIndex) : 0
    368      }
    369    }
    370    var totalCodeCount = 0
    371    for (var i = 0; i < rsBlocks.length; i++) {
    372      totalCodeCount += rsBlocks[i].totalCount
    373    }
    374    var data = new Array(totalCodeCount)
    375    var index = 0
    376    for (var i = 0; i < maxDcCount; i++) {
    377      for (var r = 0; r < rsBlocks.length; r++) {
    378        if (i < dcdata[r].length) {
    379          data[index++] = dcdata[r][i]
    380        }
    381      }
    382    }
    383    for (var i = 0; i < maxEcCount; i++) {
    384      for (var r = 0; r < rsBlocks.length; r++) {
    385        if (i < ecdata[r].length) {
    386          data[index++] = ecdata[r][i]
    387        }
    388      }
    389    }
    390    return data
    391  }
    392  var QRMode = {
    393    MODE_NUMBER: 1 << 0,
    394    MODE_ALPHA_NUM: 1 << 1,
    395    MODE_8BIT_BYTE: 1 << 2,
    396    MODE_KANJI: 1 << 3
    397  }
    398  var QRErrorCorrectLevel = { L: 1, M: 0, Q: 3, H: 2 }
    399  var QRMaskPattern = {
    400    PATTERN000: 0,
    401    PATTERN001: 1,
    402    PATTERN010: 2,
    403    PATTERN011: 3,
    404    PATTERN100: 4,
    405    PATTERN101: 5,
    406    PATTERN110: 6,
    407    PATTERN111: 7
    408  }
    409  var QRUtil = {
    410    PATTERN_POSITION_TABLE: [
    411      [],
    412      [6, 18],
    413      [6, 22],
    414      [6, 26],
    415      [6, 30],
    416      [6, 34],
    417      [6, 22, 38],
    418      [6, 24, 42],
    419      [6, 26, 46],
    420      [6, 28, 50],
    421      [6, 30, 54],
    422      [6, 32, 58],
    423      [6, 34, 62],
    424      [6, 26, 46, 66],
    425      [6, 26, 48, 70],
    426      [6, 26, 50, 74],
    427      [6, 30, 54, 78],
    428      [6, 30, 56, 82],
    429      [6, 30, 58, 86],
    430      [6, 34, 62, 90],
    431      [6, 28, 50, 72, 94],
    432      [6, 26, 50, 74, 98],
    433      [6, 30, 54, 78, 102],
    434      [6, 28, 54, 80, 106],
    435      [6, 32, 58, 84, 110],
    436      [6, 30, 58, 86, 114],
    437      [6, 34, 62, 90, 118],
    438      [6, 26, 50, 74, 98, 122],
    439      [6, 30, 54, 78, 102, 126],
    440      [6, 26, 52, 78, 104, 130],
    441      [6, 30, 56, 82, 108, 134],
    442      [6, 34, 60, 86, 112, 138],
    443      [6, 30, 58, 86, 114, 142],
    444      [6, 34, 62, 90, 118, 146],
    445      [6, 30, 54, 78, 102, 126, 150],
    446      [6, 24, 50, 76, 102, 128, 154],
    447      [6, 28, 54, 80, 106, 132, 158],
    448      [6, 32, 58, 84, 110, 136, 162],
    449      [6, 26, 54, 82, 110, 138, 166],
    450      [6, 30, 58, 86, 114, 142, 170]
    451    ],
    452    G15:
    453      (1 << 10) |
    454      (1 << 8) |
    455      (1 << 5) |
    456      (1 << 4) |
    457      (1 << 2) |
    458      (1 << 1) |
    459      (1 << 0),
    460    G18:
    461      (1 << 12) |
    462      (1 << 11) |
    463      (1 << 10) |
    464      (1 << 9) |
    465      (1 << 8) |
    466      (1 << 5) |
    467      (1 << 2) |
    468      (1 << 0),
    469    G15_MASK: (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1),
    470    getBCHTypeInfo: function (data) {
    471      var d = data << 10
    472      while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15) >= 0) {
    473        d ^=
    474          QRUtil.G15 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15))
    475      }
    476      return ((data << 10) | d) ^ QRUtil.G15_MASK
    477    },
    478    getBCHTypeNumber: function (data) {
    479      var d = data << 12
    480      while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18) >= 0) {
    481        d ^=
    482          QRUtil.G18 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18))
    483      }
    484      return (data << 12) | d
    485    },
    486    getBCHDigit: function (data) {
    487      var digit = 0
    488      while (data != 0) {
    489        digit++
    490        data >>>= 1
    491      }
    492      return digit
    493    },
    494    getPatternPosition: function (typeNumber) {
    495      return QRUtil.PATTERN_POSITION_TABLE[typeNumber - 1]
    496    },
    497    getMask: function (maskPattern, i, j) {
    498      switch (maskPattern) {
    499        case QRMaskPattern.PATTERN000:
    500          return (i + j) % 2 == 0
    501        case QRMaskPattern.PATTERN001:
    502          return i % 2 == 0
    503        case QRMaskPattern.PATTERN010:
    504          return j % 3 == 0
    505        case QRMaskPattern.PATTERN011:
    506          return (i + j) % 3 == 0
    507        case QRMaskPattern.PATTERN100:
    508          return (Math.floor(i / 2) + Math.floor(j / 3)) % 2 == 0
    509        case QRMaskPattern.PATTERN101:
    510          return (i * j) % 2 + (i * j) % 3 == 0
    511        case QRMaskPattern.PATTERN110:
    512          return ((i * j) % 2 + (i * j) % 3) % 2 == 0
    513        case QRMaskPattern.PATTERN111:
    514          return ((i * j) % 3 + (i + j) % 2) % 2 == 0
    515        default:
    516          throw new Error('bad maskPattern:' + maskPattern)
    517      }
    518    },
    519    getErrorCorrectPolynomial: function (errorCorrectLength) {
    520      var a = new QRPolynomial([1], 0)
    521      for (var i = 0; i < errorCorrectLength; i++) {
    522        a = a.multiply(new QRPolynomial([1, QRMath.gexp(i)], 0))
    523      }
    524      return a
    525    },
    526    getLengthInBits: function (mode, type) {
    527      if (type >= 1 && type < 10) {
    528        switch (mode) {
    529          case QRMode.MODE_NUMBER:
    530            return 10
    531          case QRMode.MODE_ALPHA_NUM:
    532            return 9
    533          case QRMode.MODE_8BIT_BYTE:
    534            return 8
    535          case QRMode.MODE_KANJI:
    536            return 8
    537          default:
    538            throw new Error('mode:' + mode)
    539        }
    540      } else if (type < 27) {
    541        switch (mode) {
    542          case QRMode.MODE_NUMBER:
    543            return 12
    544          case QRMode.MODE_ALPHA_NUM:
    545            return 11
    546          case QRMode.MODE_8BIT_BYTE:
    547            return 16
    548          case QRMode.MODE_KANJI:
    549            return 10
    550          default:
    551            throw new Error('mode:' + mode)
    552        }
    553      } else if (type < 41) {
    554        switch (mode) {
    555          case QRMode.MODE_NUMBER:
    556            return 14
    557          case QRMode.MODE_ALPHA_NUM:
    558            return 13
    559          case QRMode.MODE_8BIT_BYTE:
    560            return 16
    561          case QRMode.MODE_KANJI:
    562            return 12
    563          default:
    564            throw new Error('mode:' + mode)
    565        }
    566      } else {
    567        throw new Error('type:' + type)
    568      }
    569    },
    570    getLostPoint: function (qrCode) {
    571      var moduleCount = qrCode.getModuleCount()
    572      var lostPoint = 0
    573      for (var row = 0; row < moduleCount; row++) {
    574        for (var col = 0; col < moduleCount; col++) {
    575          var sameCount = 0
    576          var dark = qrCode.isDark(row, col)
    577          for (var r = -1; r <= 1; r++) {
    578            if (row + r < 0 || moduleCount <= row + r) {
    579              continue
    580            }
    581            for (var c = -1; c <= 1; c++) {
    582              if (col + c < 0 || moduleCount <= col + c) {
    583                continue
    584              }
    585              if (r == 0 && c == 0) {
    586                continue
    587              }
    588              if (dark == qrCode.isDark(row + r, col + c)) {
    589                sameCount++
    590              }
    591            }
    592          }
    593          if (sameCount > 5) {
    594            lostPoint += 3 + sameCount - 5
    595          }
    596        }
    597      }
    598      for (var row = 0; row < moduleCount - 1; row++) {
    599        for (var col = 0; col < moduleCount - 1; col++) {
    600          var count = 0
    601          if (qrCode.isDark(row, col)) count++
    602          if (qrCode.isDark(row + 1, col)) count++
    603          if (qrCode.isDark(row, col + 1)) count++
    604          if (qrCode.isDark(row + 1, col + 1)) count++
    605          if (count == 0 || count == 4) {
    606            lostPoint += 3
    607          }
    608        }
    609      }
    610      for (var row = 0; row < moduleCount; row++) {
    611        for (var col = 0; col < moduleCount - 6; col++) {
    612          if (
    613            qrCode.isDark(row, col) &&
    614            !qrCode.isDark(row, col + 1) &&
    615            qrCode.isDark(row, col + 2) &&
    616            qrCode.isDark(row, col + 3) &&
    617            qrCode.isDark(row, col + 4) &&
    618            !qrCode.isDark(row, col + 5) &&
    619            qrCode.isDark(row, col + 6)
    620          ) {
    621            lostPoint += 40
    622          }
    623        }
    624      }
    625      for (var col = 0; col < moduleCount; col++) {
    626        for (var row = 0; row < moduleCount - 6; row++) {
    627          if (
    628            qrCode.isDark(row, col) &&
    629            !qrCode.isDark(row + 1, col) &&
    630            qrCode.isDark(row + 2, col) &&
    631            qrCode.isDark(row + 3, col) &&
    632            qrCode.isDark(row + 4, col) &&
    633            !qrCode.isDark(row + 5, col) &&
    634            qrCode.isDark(row + 6, col)
    635          ) {
    636            lostPoint += 40
    637          }
    638        }
    639      }
    640      var darkCount = 0
    641      for (var col = 0; col < moduleCount; col++) {
    642        for (var row = 0; row < moduleCount; row++) {
    643          if (qrCode.isDark(row, col)) {
    644            darkCount++
    645          }
    646        }
    647      }
    648      var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5
    649      lostPoint += ratio * 10
    650      return lostPoint
    651    }
    652  }
    653  var QRMath = {
    654    glog: function (n) {
    655      if (n < 1) {
    656        throw new Error('glog(' + n + ')')
    657      }
    658      return QRMath.LOG_TABLE[n]
    659    },
    660    gexp: function (n) {
    661      while (n < 0) {
    662        n += 255
    663      }
    664      while (n >= 256) {
    665        n -= 255
    666      }
    667      return QRMath.EXP_TABLE[n]
    668    },
    669    EXP_TABLE: new Array(256),
    670    LOG_TABLE: new Array(256)
    671  }
    672  for (var i = 0; i < 8; i++) {
    673    QRMath.EXP_TABLE[i] = 1 << i
    674  }
    675  for (var i = 8; i < 256; i++) {
    676    QRMath.EXP_TABLE[i] =
    677      QRMath.EXP_TABLE[i - 4] ^
    678      QRMath.EXP_TABLE[i - 5] ^
    679      QRMath.EXP_TABLE[i - 6] ^
    680      QRMath.EXP_TABLE[i - 8]
    681  }
    682  for (var i = 0; i < 255; i++) {
    683    QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]] = i
    684  }
    685  function QRPolynomial (num, shift) {
    686    if (num.length == undefined) {
    687      throw new Error(num.length + '/' + shift)
    688    }
    689    var offset = 0
    690    while (offset < num.length && num[offset] == 0) {
    691      offset++
    692    }
    693    this.num = new Array(num.length - offset + shift)
    694    for (var i = 0; i < num.length - offset; i++) {
    695      this.num[i] = num[i + offset]
    696    }
    697  }
    698  QRPolynomial.prototype = {
    699    get: function (index) {
    700      return this.num[index]
    701    },
    702    getLength: function () {
    703      return this.num.length
    704    },
    705    multiply: function (e) {
    706      var num = new Array(this.getLength() + e.getLength() - 1)
    707      for (var i = 0; i < this.getLength(); i++) {
    708        for (var j = 0; j < e.getLength(); j++) {
    709          num[i + j] ^= QRMath.gexp(
    710            QRMath.glog(this.get(i)) + QRMath.glog(e.get(j))
    711          )
    712        }
    713      }
    714      return new QRPolynomial(num, 0)
    715    },
    716    mod: function (e) {
    717      if (this.getLength() - e.getLength() < 0) {
    718        return this
    719      }
    720      var ratio = QRMath.glog(this.get(0)) - QRMath.glog(e.get(0))
    721      var num = new Array(this.getLength())
    722      for (var i = 0; i < this.getLength(); i++) {
    723        num[i] = this.get(i)
    724      }
    725      for (var i = 0; i < e.getLength(); i++) {
    726        num[i] ^= QRMath.gexp(QRMath.glog(e.get(i)) + ratio)
    727      }
    728      return new QRPolynomial(num, 0).mod(e)
    729    }
    730  }
    731  function QRRSBlock (totalCount, dataCount) {
    732    this.totalCount = totalCount
    733    this.dataCount = dataCount
    734  }
    735  QRRSBlock.RS_BLOCK_TABLE = [
    736    [1, 26, 19],
    737    [1, 26, 16],
    738    [1, 26, 13],
    739    [1, 26, 9],
    740    [1, 44, 34],
    741    [1, 44, 28],
    742    [1, 44, 22],
    743    [1, 44, 16],
    744    [1, 70, 55],
    745    [1, 70, 44],
    746    [2, 35, 17],
    747    [2, 35, 13],
    748    [1, 100, 80],
    749    [2, 50, 32],
    750    [2, 50, 24],
    751    [4, 25, 9],
    752    [1, 134, 108],
    753    [2, 67, 43],
    754    [2, 33, 15, 2, 34, 16],
    755    [2, 33, 11, 2, 34, 12],
    756    [2, 86, 68],
    757    [4, 43, 27],
    758    [4, 43, 19],
    759    [4, 43, 15],
    760    [2, 98, 78],
    761    [4, 49, 31],
    762    [2, 32, 14, 4, 33, 15],
    763    [4, 39, 13, 1, 40, 14],
    764    [2, 121, 97],
    765    [2, 60, 38, 2, 61, 39],
    766    [4, 40, 18, 2, 41, 19],
    767    [4, 40, 14, 2, 41, 15],
    768    [2, 146, 116],
    769    [3, 58, 36, 2, 59, 37],
    770    [4, 36, 16, 4, 37, 17],
    771    [4, 36, 12, 4, 37, 13],
    772    [2, 86, 68, 2, 87, 69],
    773    [4, 69, 43, 1, 70, 44],
    774    [6, 43, 19, 2, 44, 20],
    775    [6, 43, 15, 2, 44, 16],
    776    [4, 101, 81],
    777    [1, 80, 50, 4, 81, 51],
    778    [4, 50, 22, 4, 51, 23],
    779    [3, 36, 12, 8, 37, 13],
    780    [2, 116, 92, 2, 117, 93],
    781    [6, 58, 36, 2, 59, 37],
    782    [4, 46, 20, 6, 47, 21],
    783    [7, 42, 14, 4, 43, 15],
    784    [4, 133, 107],
    785    [8, 59, 37, 1, 60, 38],
    786    [8, 44, 20, 4, 45, 21],
    787    [12, 33, 11, 4, 34, 12],
    788    [3, 145, 115, 1, 146, 116],
    789    [4, 64, 40, 5, 65, 41],
    790    [11, 36, 16, 5, 37, 17],
    791    [11, 36, 12, 5, 37, 13],
    792    [5, 109, 87, 1, 110, 88],
    793    [5, 65, 41, 5, 66, 42],
    794    [5, 54, 24, 7, 55, 25],
    795    [11, 36, 12],
    796    [5, 122, 98, 1, 123, 99],
    797    [7, 73, 45, 3, 74, 46],
    798    [15, 43, 19, 2, 44, 20],
    799    [3, 45, 15, 13, 46, 16],
    800    [1, 135, 107, 5, 136, 108],
    801    [10, 74, 46, 1, 75, 47],
    802    [1, 50, 22, 15, 51, 23],
    803    [2, 42, 14, 17, 43, 15],
    804    [5, 150, 120, 1, 151, 121],
    805    [9, 69, 43, 4, 70, 44],
    806    [17, 50, 22, 1, 51, 23],
    807    [2, 42, 14, 19, 43, 15],
    808    [3, 141, 113, 4, 142, 114],
    809    [3, 70, 44, 11, 71, 45],
    810    [17, 47, 21, 4, 48, 22],
    811    [9, 39, 13, 16, 40, 14],
    812    [3, 135, 107, 5, 136, 108],
    813    [3, 67, 41, 13, 68, 42],
    814    [15, 54, 24, 5, 55, 25],
    815    [15, 43, 15, 10, 44, 16],
    816    [4, 144, 116, 4, 145, 117],
    817    [17, 68, 42],
    818    [17, 50, 22, 6, 51, 23],
    819    [19, 46, 16, 6, 47, 17],
    820    [2, 139, 111, 7, 140, 112],
    821    [17, 74, 46],
    822    [7, 54, 24, 16, 55, 25],
    823    [34, 37, 13],
    824    [4, 151, 121, 5, 152, 122],
    825    [4, 75, 47, 14, 76, 48],
    826    [11, 54, 24, 14, 55, 25],
    827    [16, 45, 15, 14, 46, 16],
    828    [6, 147, 117, 4, 148, 118],
    829    [6, 73, 45, 14, 74, 46],
    830    [11, 54, 24, 16, 55, 25],
    831    [30, 46, 16, 2, 47, 17],
    832    [8, 132, 106, 4, 133, 107],
    833    [8, 75, 47, 13, 76, 48],
    834    [7, 54, 24, 22, 55, 25],
    835    [22, 45, 15, 13, 46, 16],
    836    [10, 142, 114, 2, 143, 115],
    837    [19, 74, 46, 4, 75, 47],
    838    [28, 50, 22, 6, 51, 23],
    839    [33, 46, 16, 4, 47, 17],
    840    [8, 152, 122, 4, 153, 123],
    841    [22, 73, 45, 3, 74, 46],
    842    [8, 53, 23, 26, 54, 24],
    843    [12, 45, 15, 28, 46, 16],
    844    [3, 147, 117, 10, 148, 118],
    845    [3, 73, 45, 23, 74, 46],
    846    [4, 54, 24, 31, 55, 25],
    847    [11, 45, 15, 31, 46, 16],
    848    [7, 146, 116, 7, 147, 117],
    849    [21, 73, 45, 7, 74, 46],
    850    [1, 53, 23, 37, 54, 24],
    851    [19, 45, 15, 26, 46, 16],
    852    [5, 145, 115, 10, 146, 116],
    853    [19, 75, 47, 10, 76, 48],
    854    [15, 54, 24, 25, 55, 25],
    855    [23, 45, 15, 25, 46, 16],
    856    [13, 145, 115, 3, 146, 116],
    857    [2, 74, 46, 29, 75, 47],
    858    [42, 54, 24, 1, 55, 25],
    859    [23, 45, 15, 28, 46, 16],
    860    [17, 145, 115],
    861    [10, 74, 46, 23, 75, 47],
    862    [10, 54, 24, 35, 55, 25],
    863    [19, 45, 15, 35, 46, 16],
    864    [17, 145, 115, 1, 146, 116],
    865    [14, 74, 46, 21, 75, 47],
    866    [29, 54, 24, 19, 55, 25],
    867    [11, 45, 15, 46, 46, 16],
    868    [13, 145, 115, 6, 146, 116],
    869    [14, 74, 46, 23, 75, 47],
    870    [44, 54, 24, 7, 55, 25],
    871    [59, 46, 16, 1, 47, 17],
    872    [12, 151, 121, 7, 152, 122],
    873    [12, 75, 47, 26, 76, 48],
    874    [39, 54, 24, 14, 55, 25],
    875    [22, 45, 15, 41, 46, 16],
    876    [6, 151, 121, 14, 152, 122],
    877    [6, 75, 47, 34, 76, 48],
    878    [46, 54, 24, 10, 55, 25],
    879    [2, 45, 15, 64, 46, 16],
    880    [17, 152, 122, 4, 153, 123],
    881    [29, 74, 46, 14, 75, 47],
    882    [49, 54, 24, 10, 55, 25],
    883    [24, 45, 15, 46, 46, 16],
    884    [4, 152, 122, 18, 153, 123],
    885    [13, 74, 46, 32, 75, 47],
    886    [48, 54, 24, 14, 55, 25],
    887    [42, 45, 15, 32, 46, 16],
    888    [20, 147, 117, 4, 148, 118],
    889    [40, 75, 47, 7, 76, 48],
    890    [43, 54, 24, 22, 55, 25],
    891    [10, 45, 15, 67, 46, 16],
    892    [19, 148, 118, 6, 149, 119],
    893    [18, 75, 47, 31, 76, 48],
    894    [34, 54, 24, 34, 55, 25],
    895    [20, 45, 15, 61, 46, 16]
    896  ]
    897  QRRSBlock.getRSBlocks = function (typeNumber, errorCorrectLevel) {
    898    var rsBlock = QRRSBlock.getRsBlockTable(typeNumber, errorCorrectLevel)
    899    if (rsBlock == undefined) {
    900      throw new Error(
    901        'bad rs block @ typeNumber:' +
    902          typeNumber +
    903          '/errorCorrectLevel:' +
    904          errorCorrectLevel
    905      )
    906    }
    907    var length = rsBlock.length / 3
    908    var list = []
    909    for (var i = 0; i < length; i++) {
    910      var count = rsBlock[i * 3 + 0]
    911      var totalCount = rsBlock[i * 3 + 1]
    912      var dataCount = rsBlock[i * 3 + 2]
    913      for (var j = 0; j < count; j++) {
    914        list.push(new QRRSBlock(totalCount, dataCount))
    915      }
    916    }
    917    return list
    918  }
    919  QRRSBlock.getRsBlockTable = function (typeNumber, errorCorrectLevel) {
    920    switch (errorCorrectLevel) {
    921      case QRErrorCorrectLevel.L:
    922        return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0]
    923      case QRErrorCorrectLevel.M:
    924        return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1]
    925      case QRErrorCorrectLevel.Q:
    926        return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2]
    927      case QRErrorCorrectLevel.H:
    928        return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3]
    929      default:
    930        return undefined
    931    }
    932  }
    933  function QRBitBuffer () {
    934    this.buffer = []
    935    this.length = 0
    936  }
    937  QRBitBuffer.prototype = {
    938    get: function (index) {
    939      var bufIndex = Math.floor(index / 8)
    940      return ((this.buffer[bufIndex] >>> (7 - index % 8)) & 1) == 1
    941    },
    942    put: function (num, length) {
    943      for (var i = 0; i < length; i++) {
    944        this.putBit(((num >>> (length - i - 1)) & 1) == 1)
    945      }
    946    },
    947    getLengthInBits: function () {
    948      return this.length
    949    },
    950    putBit: function (bit) {
    951      var bufIndex = Math.floor(this.length / 8)
    952      if (this.buffer.length <= bufIndex) {
    953        this.buffer.push(0)
    954      }
    955      if (bit) {
    956        this.buffer[bufIndex] |= 0x80 >>> (this.length % 8)
    957      }
    958      this.length++
    959    }
    960  }
    961  var QRCodeLimitLength = [
    962    [17, 14, 11, 7],
    963    [32, 26, 20, 14],
    964    [53, 42, 32, 24],
    965    [78, 62, 46, 34],
    966    [106, 84, 60, 44],
    967    [134, 106, 74, 58],
    968    [154, 122, 86, 64],
    969    [192, 152, 108, 84],
    970    [230, 180, 130, 98],
    971    [271, 213, 151, 119],
    972    [321, 251, 177, 137],
    973    [367, 287, 203, 155],
    974    [425, 331, 241, 177],
    975    [458, 362, 258, 194],
    976    [520, 412, 292, 220],
    977    [586, 450, 322, 250],
    978    [644, 504, 364, 280],
    979    [718, 560, 394, 310],
    980    [792, 624, 442, 338],
    981    [858, 666, 482, 382],
    982    [929, 711, 509, 403],
    983    [1003, 779, 565, 439],
    984    [1091, 857, 611, 461],
    985    [1171, 911, 661, 511],
    986    [1273, 997, 715, 535],
    987    [1367, 1059, 751, 593],
    988    [1465, 1125, 805, 625],
    989    [1528, 1190, 868, 658],
    990    [1628, 1264, 908, 698],
    991    [1732, 1370, 982, 742],
    992    [1840, 1452, 1030, 790],
    993    [1952, 1538, 1112, 842],
    994    [2068, 1628, 1168, 898],
    995    [2188, 1722, 1228, 958],
    996    [2303, 1809, 1283, 983],
    997    [2431, 1911, 1351, 1051],
    998    [2563, 1989, 1423, 1093],
    999    [2699, 2099, 1499, 1139],
   1000    [2809, 2213, 1579, 1219],
   1001    [2953, 2331, 1663, 1273]
   1002  ]
   1003 
   1004  function _isSupportCanvas () {
   1005    return typeof CanvasRenderingContext2D !== 'undefined'
   1006  }
   1007 
   1008  // android 2.x doesn't support Data-URI spec
   1009  function _getAndroid () {
   1010    var android = false
   1011    var sAgent = navigator.userAgent
   1012 
   1013    if (/android/i.test(sAgent)) {
   1014      // android
   1015      android = true
   1016      var aMat = sAgent.toString().match(/android ([0-9]\.[0-9])/i)
   1017 
   1018      if (aMat && aMat[1]) {
   1019        android = parseFloat(aMat[1])
   1020      }
   1021    }
   1022 
   1023    return android
   1024  }
   1025 
   1026  var svgDrawer = (function () {
   1027    var Drawing = function (el, htOption) {
   1028      this._el = el
   1029      this._htOption = htOption
   1030    }
   1031 
   1032    Drawing.prototype.draw = function (oQRCode) {
   1033      var _htOption = this._htOption
   1034      var _el = this._el
   1035      var nCount = oQRCode.getModuleCount()
   1036      var nWidth = Math.floor(_htOption.width / nCount)
   1037      var nHeight = Math.floor(_htOption.height / nCount)
   1038 
   1039      this.clear()
   1040 
   1041      function makeSVG (tag, attrs) {
   1042        var el = document.createElementNS('http://www.w3.org/2000/svg', tag)
   1043        for (var k in attrs) {
   1044          if (attrs.hasOwnProperty(k)) el.setAttribute(k, attrs[k])
   1045        }
   1046        return el
   1047      }
   1048 
   1049      var svg = makeSVG('svg', {
   1050        viewBox: '0 0 ' + String(nCount) + ' ' + String(nCount),
   1051        width: '100%',
   1052        height: '100%',
   1053        fill: _htOption.colorLight
   1054      })
   1055      svg.setAttributeNS(
   1056        'http://www.w3.org/2000/xmlns/',
   1057        'xmlns:xlink',
   1058        'http://www.w3.org/1999/xlink'
   1059      )
   1060      _el.appendChild(svg)
   1061 
   1062      svg.appendChild(
   1063        makeSVG('rect', {
   1064          fill: _htOption.colorLight,
   1065          width: '100%',
   1066          height: '100%'
   1067        })
   1068      )
   1069      svg.appendChild(
   1070        makeSVG('rect', {
   1071          fill: _htOption.colorDark,
   1072          width: '1',
   1073          height: '1',
   1074          id: 'template'
   1075        })
   1076      )
   1077 
   1078      for (var row = 0; row < nCount; row++) {
   1079        for (var col = 0; col < nCount; col++) {
   1080          if (oQRCode.isDark(row, col)) {
   1081            var child = makeSVG('use', { x: String(col), y: String(row) })
   1082            child.setAttributeNS(
   1083              'http://www.w3.org/1999/xlink',
   1084              'href',
   1085              '#template'
   1086            )
   1087            svg.appendChild(child)
   1088          }
   1089        }
   1090      }
   1091    }
   1092    Drawing.prototype.clear = function () {
   1093      while (this._el.hasChildNodes()) this._el.removeChild(this._el.lastChild)
   1094    }
   1095    return Drawing
   1096  })()
   1097 
   1098  var useSVG = document.documentElement.tagName.toLowerCase() === 'svg'
   1099 
   1100  // Drawing in DOM by using Table tag
   1101  var Drawing = useSVG
   1102    ? svgDrawer
   1103    : !_isSupportCanvas()
   1104      ? (function () {
   1105        var Drawing = function (el, htOption) {
   1106          this._el = el
   1107          this._htOption = htOption
   1108        }
   1109 
   1110        /**
   1111           * Draw the QRCode
   1112           *
   1113           * @param {QRCode} oQRCode
   1114           */
   1115        Drawing.prototype.draw = function (oQRCode) {
   1116          var _htOption = this._htOption
   1117          var _el = this._el
   1118          var nCount = oQRCode.getModuleCount()
   1119          var nWidth = Math.floor(_htOption.width / nCount)
   1120          var nHeight = Math.floor(_htOption.height / nCount)
   1121          var aHTML = ['<table style="border:0;border-collapse:collapse;">']
   1122 
   1123          for (var row = 0; row < nCount; row++) {
   1124            aHTML.push('<tr>')
   1125 
   1126            for (var col = 0; col < nCount; col++) {
   1127              aHTML.push(
   1128                '<td style="border:0;border-collapse:collapse;padding:0;margin:0;width:' +
   1129                    nWidth +
   1130                    'px;height:' +
   1131                    nHeight +
   1132                    'px;background-color:' +
   1133                    (oQRCode.isDark(row, col)
   1134                      ? _htOption.colorDark
   1135                      : _htOption.colorLight) +
   1136                    ';"></td>'
   1137              )
   1138            }
   1139 
   1140            aHTML.push('</tr>')
   1141          }
   1142 
   1143          aHTML.push('</table>')
   1144          _el.innerHTML = aHTML.join('')
   1145 
   1146          // Fix the margin values as real size.
   1147          var elTable = _el.childNodes[0]
   1148          var nLeftMarginTable = (_htOption.width - elTable.offsetWidth) / 2
   1149          var nTopMarginTable = (_htOption.height - elTable.offsetHeight) / 2
   1150 
   1151          if (nLeftMarginTable > 0 && nTopMarginTable > 0) {
   1152            elTable.style.margin =
   1153                nTopMarginTable + 'px ' + nLeftMarginTable + 'px'
   1154          }
   1155        }
   1156 
   1157        /**
   1158           * Clear the QRCode
   1159           */
   1160        Drawing.prototype.clear = function () {
   1161          this._el.innerHTML = ''
   1162        }
   1163 
   1164        return Drawing
   1165      })()
   1166      : (function () {
   1167        // Drawing in Canvas
   1168        function _onMakeImage () {
   1169          this._elImage.src = this._elCanvas.toDataURL('image/png')
   1170          this._elImage.style.display = 'block'
   1171          this._elCanvas.style.display = 'none'
   1172        }
   1173 
   1174        // Android 2.1 bug workaround
   1175        // http://code.google.com/p/android/issues/detail?id=5141
   1176        if (this._android && this._android <= 2.1) {
   1177          var factor = 1 / window.devicePixelRatio
   1178          var drawImage = CanvasRenderingContext2D.prototype.drawImage
   1179          CanvasRenderingContext2D.prototype.drawImage = function (
   1180            image,
   1181            sx,
   1182            sy,
   1183            sw,
   1184            sh,
   1185            dx,
   1186            dy,
   1187            dw,
   1188            dh
   1189          ) {
   1190            if ('nodeName' in image && /img/i.test(image.nodeName)) {
   1191              for (var i = arguments.length - 1; i >= 1; i--) {
   1192                arguments[i] = arguments[i] * factor
   1193              }
   1194            } else if (typeof dw === 'undefined') {
   1195              arguments[1] *= factor
   1196              arguments[2] *= factor
   1197              arguments[3] *= factor
   1198              arguments[4] *= factor
   1199            }
   1200 
   1201            drawImage.apply(this, arguments)
   1202          }
   1203        }
   1204 
   1205        /**
   1206           * Check whether the user's browser supports Data URI or not
   1207           *
   1208           * @private
   1209           * @param {Function} fSuccess Occurs if it supports Data URI
   1210           * @param {Function} fFail Occurs if it doesn't support Data URI
   1211           */
   1212        function _safeSetDataURI (fSuccess, fFail) {
   1213          var self = this
   1214          self._fFail = fFail
   1215          self._fSuccess = fSuccess
   1216 
   1217          // Check it just once
   1218          if (self._bSupportDataURI === null) {
   1219            var el = document.createElement('img')
   1220            var fOnError = function () {
   1221              self._bSupportDataURI = false
   1222 
   1223              if (self._fFail) {
   1224                self._fFail.call(self)
   1225              }
   1226            }
   1227            var fOnSuccess = function () {
   1228              self._bSupportDataURI = true
   1229 
   1230              if (self._fSuccess) {
   1231                self._fSuccess.call(self)
   1232              }
   1233            }
   1234 
   1235            el.onabort = fOnError
   1236            el.onerror = fOnError
   1237            el.onload = fOnSuccess
   1238            el.src =
   1239                'data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==' // the Image contains 1px data.
   1240          } else if (self._bSupportDataURI === true && self._fSuccess) {
   1241            self._fSuccess.call(self)
   1242          } else if (self._bSupportDataURI === false && self._fFail) {
   1243            self._fFail.call(self)
   1244          }
   1245        }
   1246 
   1247        /**
   1248           * Drawing QRCode by using canvas
   1249           *
   1250           * @constructor
   1251           * @param {HTMLElement} el
   1252           * @param {Object} htOption QRCode Options
   1253           */
   1254        var Drawing = function (el, htOption) {
   1255          this._bIsPainted = false
   1256          this._android = _getAndroid()
   1257 
   1258          this._htOption = htOption
   1259          this._elCanvas = document.createElement('canvas')
   1260          this._elCanvas.width = htOption.width
   1261          this._elCanvas.height = htOption.height
   1262          el.appendChild(this._elCanvas)
   1263          this._el = el
   1264          this._oContext = this._elCanvas.getContext('2d')
   1265          this._bIsPainted = false
   1266          this._elImage = document.createElement('img')
   1267          this._elImage.alt = 'Scan me!'
   1268          this._elImage.style.display = 'none'
   1269          this._el.appendChild(this._elImage)
   1270          this._bSupportDataURI = null
   1271        }
   1272 
   1273        /**
   1274           * Draw the QRCode
   1275           *
   1276           * @param {QRCode} oQRCode
   1277           */
   1278        Drawing.prototype.draw = function (oQRCode) {
   1279          var _elImage = this._elImage
   1280          var _oContext = this._oContext
   1281          var _htOption = this._htOption
   1282 
   1283          var nCount = oQRCode.getModuleCount()
   1284          var nWidth = _htOption.width / nCount
   1285          var nHeight = _htOption.height / nCount
   1286          var nRoundedWidth = Math.round(nWidth)
   1287          var nRoundedHeight = Math.round(nHeight)
   1288 
   1289          _elImage.style.display = 'none'
   1290          this.clear()
   1291 
   1292          for (var row = 0; row < nCount; row++) {
   1293            for (var col = 0; col < nCount; col++) {
   1294              var bIsDark = oQRCode.isDark(row, col)
   1295              var nLeft = col * nWidth
   1296              var nTop = row * nHeight
   1297              _oContext.strokeStyle = bIsDark
   1298                ? _htOption.colorDark
   1299                : _htOption.colorLight
   1300              _oContext.lineWidth = 1
   1301              _oContext.fillStyle = bIsDark
   1302                ? _htOption.colorDark
   1303                : _htOption.colorLight
   1304              _oContext.fillRect(nLeft, nTop, nWidth, nHeight)
   1305 
   1306              // 안티 앨리어싱 방지 처리
   1307              _oContext.strokeRect(
   1308                Math.floor(nLeft) + 0.5,
   1309                Math.floor(nTop) + 0.5,
   1310                nRoundedWidth,
   1311                nRoundedHeight
   1312              )
   1313 
   1314              _oContext.strokeRect(
   1315                Math.ceil(nLeft) - 0.5,
   1316                Math.ceil(nTop) - 0.5,
   1317                nRoundedWidth,
   1318                nRoundedHeight
   1319              )
   1320            }
   1321          }
   1322 
   1323          this._bIsPainted = true
   1324        }
   1325 
   1326        /**
   1327           * Make the image from Canvas if the browser supports Data URI.
   1328           */
   1329        Drawing.prototype.makeImage = function () {
   1330          if (this._bIsPainted) {
   1331            _safeSetDataURI.call(this, _onMakeImage)
   1332          }
   1333        }
   1334 
   1335        /**
   1336           * Return whether the QRCode is painted or not
   1337           *
   1338           * @return {Boolean}
   1339           */
   1340        Drawing.prototype.isPainted = function () {
   1341          return this._bIsPainted
   1342        }
   1343 
   1344        /**
   1345           * Clear the QRCode
   1346           */
   1347        Drawing.prototype.clear = function () {
   1348          this._oContext.clearRect(
   1349            0,
   1350            0,
   1351            this._elCanvas.width,
   1352            this._elCanvas.height
   1353          )
   1354          this._bIsPainted = false
   1355        }
   1356 
   1357        /**
   1358           * @private
   1359           * @param {Number} nNumber
   1360           */
   1361        Drawing.prototype.round = function (nNumber) {
   1362          if (!nNumber) {
   1363            return nNumber
   1364          }
   1365 
   1366          return Math.floor(nNumber * 1000) / 1000
   1367        }
   1368 
   1369        return Drawing
   1370      })()
   1371 
   1372  /**
   1373   * Get the type by string length
   1374   *
   1375   * @private
   1376   * @param {String} sText
   1377   * @param {Number} nCorrectLevel
   1378   * @return {Number} type
   1379   */
   1380  function _getTypeNumber (sText, nCorrectLevel) {
   1381    var nType = 1
   1382    var length = _getUTF8Length(sText)
   1383 
   1384    for (var i = 0, len = QRCodeLimitLength.length; i <= len; i++) {
   1385      var nLimit = 0
   1386 
   1387      switch (nCorrectLevel) {
   1388        case QRErrorCorrectLevel.L:
   1389          nLimit = QRCodeLimitLength[i][0]
   1390          break
   1391        case QRErrorCorrectLevel.M:
   1392          nLimit = QRCodeLimitLength[i][1]
   1393          break
   1394        case QRErrorCorrectLevel.Q:
   1395          nLimit = QRCodeLimitLength[i][2]
   1396          break
   1397        case QRErrorCorrectLevel.H:
   1398          nLimit = QRCodeLimitLength[i][3]
   1399          break
   1400      }
   1401 
   1402      if (length <= nLimit) {
   1403        break
   1404      } else {
   1405        nType++
   1406      }
   1407    }
   1408 
   1409    if (nType > QRCodeLimitLength.length) {
   1410      throw new Error('Too long data')
   1411    }
   1412 
   1413    return nType
   1414  }
   1415 
   1416  function _getUTF8Length (sText) {
   1417    var replacedText = encodeURI(sText)
   1418      .toString()
   1419      .replace(/\%[0-9a-fA-F]{2}/g, 'a')
   1420    return replacedText.length + (replacedText.length != sText ? 3 : 0)
   1421  }
   1422 
   1423  /**
   1424   * @class QRCode
   1425   * @constructor
   1426   * @example
   1427   * new QRCode(document.getElementById("test"), "http://jindo.dev.naver.com/collie");
   1428   *
   1429   * @example
   1430   * var oQRCode = new QRCode("test", {
   1431   *    text : "http://naver.com",
   1432   *    width : 128,
   1433   *    height : 128
   1434   * });
   1435   *
   1436   * oQRCode.clear(); // Clear the QRCode.
   1437   * oQRCode.makeCode("http://map.naver.com"); // Re-create the QRCode.
   1438   *
   1439   * @param {HTMLElement|String} el target element or 'id' attribute of element.
   1440   * @param {Object|String} vOption
   1441   * @param {String} vOption.text QRCode link data
   1442   * @param {Number} [vOption.width=256]
   1443   * @param {Number} [vOption.height=256]
   1444   * @param {String} [vOption.colorDark="#000000"]
   1445   * @param {String} [vOption.colorLight="#ffffff"]
   1446   * @param {QRCode.CorrectLevel} [vOption.correctLevel=QRCode.CorrectLevel.H] [L|M|Q|H]
   1447   */
   1448  QRCode = function (el, vOption) {
   1449    this._htOption = {
   1450      width: 256,
   1451      height: 256,
   1452      typeNumber: 4,
   1453      colorDark: '#000000',
   1454      colorLight: '#ffffff',
   1455      correctLevel: QRErrorCorrectLevel.H
   1456    }
   1457 
   1458    if (typeof vOption === 'string') {
   1459      vOption = {
   1460        text: vOption
   1461      }
   1462    }
   1463 
   1464    // Overwrites options
   1465    if (vOption) {
   1466      for (var i in vOption) {
   1467        this._htOption[i] = vOption[i]
   1468      }
   1469    }
   1470 
   1471    if (typeof el === 'string') {
   1472      el = document.getElementById(el)
   1473    }
   1474 
   1475    if (this._htOption.useSVG) {
   1476      Drawing = svgDrawer
   1477    }
   1478 
   1479    this._android = _getAndroid()
   1480    this._el = el
   1481    this._oQRCode = null
   1482    this._oDrawing = new Drawing(this._el, this._htOption)
   1483 
   1484    if (this._htOption.text) {
   1485      this.makeCode(this._htOption.text)
   1486    }
   1487  }
   1488 
   1489  /**
   1490   * Make the QRCode
   1491   *
   1492   * @param {String} sText link data
   1493   */
   1494  QRCode.prototype.makeCode = function (sText) {
   1495    this._oQRCode = new QRCodeModel(
   1496      _getTypeNumber(sText, this._htOption.correctLevel),
   1497      this._htOption.correctLevel
   1498    )
   1499    this._oQRCode.addData(sText)
   1500    this._oQRCode.make()
   1501    this._el.title = sText
   1502    this._oDrawing.draw(this._oQRCode)
   1503    this.makeImage()
   1504  }
   1505 
   1506  /**
   1507   * Make the Image from Canvas element
   1508   * - It occurs automatically
   1509   * - Android below 3 doesn't support Data-URI spec.
   1510   *
   1511   * @private
   1512   */
   1513  QRCode.prototype.makeImage = function () {
   1514    if (
   1515      typeof this._oDrawing.makeImage === 'function' &&
   1516      (!this._android || this._android >= 3)
   1517    ) {
   1518      this._oDrawing.makeImage()
   1519    }
   1520  }
   1521 
   1522  /**
   1523   * Clear the QRCode
   1524   */
   1525  QRCode.prototype.clear = function () {
   1526    this._oDrawing.clear()
   1527  }
   1528 
   1529  /**
   1530   * @name QRCode.CorrectLevel
   1531   */
   1532  QRCode.CorrectLevel = QRErrorCorrectLevel
   1533 })()