03f4db8a8628a1498b4c3ee97cd47db9d76959e7460ac0c2717211cbccd84ecf.json raw

   1  {"ast":null,"code":"const Mode = require('./mode');\nconst NumericData = require('./numeric-data');\nconst AlphanumericData = require('./alphanumeric-data');\nconst ByteData = require('./byte-data');\nconst KanjiData = require('./kanji-data');\nconst Regex = require('./regex');\nconst Utils = require('./utils');\nconst dijkstra = require('dijkstrajs');\n\n/**\n * Returns UTF8 byte length\n *\n * @param  {String} str Input string\n * @return {Number}     Number of byte\n */\nfunction getStringByteLength(str) {\n  return unescape(encodeURIComponent(str)).length;\n}\n\n/**\n * Get a list of segments of the specified mode\n * from a string\n *\n * @param  {Mode}   mode Segment mode\n * @param  {String} str  String to process\n * @return {Array}       Array of object with segments data\n */\nfunction getSegments(regex, mode, str) {\n  const segments = [];\n  let result;\n  while ((result = regex.exec(str)) !== null) {\n    segments.push({\n      data: result[0],\n      index: result.index,\n      mode: mode,\n      length: result[0].length\n    });\n  }\n  return segments;\n}\n\n/**\n * Extracts a series of segments with the appropriate\n * modes from a string\n *\n * @param  {String} dataStr Input string\n * @return {Array}          Array of object with segments data\n */\nfunction getSegmentsFromString(dataStr) {\n  const numSegs = getSegments(Regex.NUMERIC, Mode.NUMERIC, dataStr);\n  const alphaNumSegs = getSegments(Regex.ALPHANUMERIC, Mode.ALPHANUMERIC, dataStr);\n  let byteSegs;\n  let kanjiSegs;\n  if (Utils.isKanjiModeEnabled()) {\n    byteSegs = getSegments(Regex.BYTE, Mode.BYTE, dataStr);\n    kanjiSegs = getSegments(Regex.KANJI, Mode.KANJI, dataStr);\n  } else {\n    byteSegs = getSegments(Regex.BYTE_KANJI, Mode.BYTE, dataStr);\n    kanjiSegs = [];\n  }\n  const segs = numSegs.concat(alphaNumSegs, byteSegs, kanjiSegs);\n  return segs.sort(function (s1, s2) {\n    return s1.index - s2.index;\n  }).map(function (obj) {\n    return {\n      data: obj.data,\n      mode: obj.mode,\n      length: obj.length\n    };\n  });\n}\n\n/**\n * Returns how many bits are needed to encode a string of\n * specified length with the specified mode\n *\n * @param  {Number} length String length\n * @param  {Mode} mode     Segment mode\n * @return {Number}        Bit length\n */\nfunction getSegmentBitsLength(length, mode) {\n  switch (mode) {\n    case Mode.NUMERIC:\n      return NumericData.getBitsLength(length);\n    case Mode.ALPHANUMERIC:\n      return AlphanumericData.getBitsLength(length);\n    case Mode.KANJI:\n      return KanjiData.getBitsLength(length);\n    case Mode.BYTE:\n      return ByteData.getBitsLength(length);\n  }\n}\n\n/**\n * Merges adjacent segments which have the same mode\n *\n * @param  {Array} segs Array of object with segments data\n * @return {Array}      Array of object with segments data\n */\nfunction mergeSegments(segs) {\n  return segs.reduce(function (acc, curr) {\n    const prevSeg = acc.length - 1 >= 0 ? acc[acc.length - 1] : null;\n    if (prevSeg && prevSeg.mode === curr.mode) {\n      acc[acc.length - 1].data += curr.data;\n      return acc;\n    }\n    acc.push(curr);\n    return acc;\n  }, []);\n}\n\n/**\n * Generates a list of all possible nodes combination which\n * will be used to build a segments graph.\n *\n * Nodes are divided by groups. Each group will contain a list of all the modes\n * in which is possible to encode the given text.\n *\n * For example the text '12345' can be encoded as Numeric, Alphanumeric or Byte.\n * The group for '12345' will contain then 3 objects, one for each\n * possible encoding mode.\n *\n * Each node represents a possible segment.\n *\n * @param  {Array} segs Array of object with segments data\n * @return {Array}      Array of object with segments data\n */\nfunction buildNodes(segs) {\n  const nodes = [];\n  for (let i = 0; i < segs.length; i++) {\n    const seg = segs[i];\n    switch (seg.mode) {\n      case Mode.NUMERIC:\n        nodes.push([seg, {\n          data: seg.data,\n          mode: Mode.ALPHANUMERIC,\n          length: seg.length\n        }, {\n          data: seg.data,\n          mode: Mode.BYTE,\n          length: seg.length\n        }]);\n        break;\n      case Mode.ALPHANUMERIC:\n        nodes.push([seg, {\n          data: seg.data,\n          mode: Mode.BYTE,\n          length: seg.length\n        }]);\n        break;\n      case Mode.KANJI:\n        nodes.push([seg, {\n          data: seg.data,\n          mode: Mode.BYTE,\n          length: getStringByteLength(seg.data)\n        }]);\n        break;\n      case Mode.BYTE:\n        nodes.push([{\n          data: seg.data,\n          mode: Mode.BYTE,\n          length: getStringByteLength(seg.data)\n        }]);\n    }\n  }\n  return nodes;\n}\n\n/**\n * Builds a graph from a list of nodes.\n * All segments in each node group will be connected with all the segments of\n * the next group and so on.\n *\n * At each connection will be assigned a weight depending on the\n * segment's byte length.\n *\n * @param  {Array} nodes    Array of object with segments data\n * @param  {Number} version QR Code version\n * @return {Object}         Graph of all possible segments\n */\nfunction buildGraph(nodes, version) {\n  const table = {};\n  const graph = {\n    start: {}\n  };\n  let prevNodeIds = ['start'];\n  for (let i = 0; i < nodes.length; i++) {\n    const nodeGroup = nodes[i];\n    const currentNodeIds = [];\n    for (let j = 0; j < nodeGroup.length; j++) {\n      const node = nodeGroup[j];\n      const key = '' + i + j;\n      currentNodeIds.push(key);\n      table[key] = {\n        node: node,\n        lastCount: 0\n      };\n      graph[key] = {};\n      for (let n = 0; n < prevNodeIds.length; n++) {\n        const prevNodeId = prevNodeIds[n];\n        if (table[prevNodeId] && table[prevNodeId].node.mode === node.mode) {\n          graph[prevNodeId][key] = getSegmentBitsLength(table[prevNodeId].lastCount + node.length, node.mode) - getSegmentBitsLength(table[prevNodeId].lastCount, node.mode);\n          table[prevNodeId].lastCount += node.length;\n        } else {\n          if (table[prevNodeId]) table[prevNodeId].lastCount = node.length;\n          graph[prevNodeId][key] = getSegmentBitsLength(node.length, node.mode) + 4 + Mode.getCharCountIndicator(node.mode, version); // switch cost\n        }\n      }\n    }\n    prevNodeIds = currentNodeIds;\n  }\n  for (let n = 0; n < prevNodeIds.length; n++) {\n    graph[prevNodeIds[n]].end = 0;\n  }\n  return {\n    map: graph,\n    table: table\n  };\n}\n\n/**\n * Builds a segment from a specified data and mode.\n * If a mode is not specified, the more suitable will be used.\n *\n * @param  {String} data             Input data\n * @param  {Mode | String} modesHint Data mode\n * @return {Segment}                 Segment\n */\nfunction buildSingleSegment(data, modesHint) {\n  let mode;\n  const bestMode = Mode.getBestModeForData(data);\n  mode = Mode.from(modesHint, bestMode);\n\n  // Make sure data can be encoded\n  if (mode !== Mode.BYTE && mode.bit < bestMode.bit) {\n    throw new Error('\"' + data + '\"' + ' cannot be encoded with mode ' + Mode.toString(mode) + '.\\n Suggested mode is: ' + Mode.toString(bestMode));\n  }\n\n  // Use Mode.BYTE if Kanji support is disabled\n  if (mode === Mode.KANJI && !Utils.isKanjiModeEnabled()) {\n    mode = Mode.BYTE;\n  }\n  switch (mode) {\n    case Mode.NUMERIC:\n      return new NumericData(data);\n    case Mode.ALPHANUMERIC:\n      return new AlphanumericData(data);\n    case Mode.KANJI:\n      return new KanjiData(data);\n    case Mode.BYTE:\n      return new ByteData(data);\n  }\n}\n\n/**\n * Builds a list of segments from an array.\n * Array can contain Strings or Objects with segment's info.\n *\n * For each item which is a string, will be generated a segment with the given\n * string and the more appropriate encoding mode.\n *\n * For each item which is an object, will be generated a segment with the given\n * data and mode.\n * Objects must contain at least the property \"data\".\n * If property \"mode\" is not present, the more suitable mode will be used.\n *\n * @param  {Array} array Array of objects with segments data\n * @return {Array}       Array of Segments\n */\nexports.fromArray = function fromArray(array) {\n  return array.reduce(function (acc, seg) {\n    if (typeof seg === 'string') {\n      acc.push(buildSingleSegment(seg, null));\n    } else if (seg.data) {\n      acc.push(buildSingleSegment(seg.data, seg.mode));\n    }\n    return acc;\n  }, []);\n};\n\n/**\n * Builds an optimized sequence of segments from a string,\n * which will produce the shortest possible bitstream.\n *\n * @param  {String} data    Input string\n * @param  {Number} version QR Code version\n * @return {Array}          Array of segments\n */\nexports.fromString = function fromString(data, version) {\n  const segs = getSegmentsFromString(data, Utils.isKanjiModeEnabled());\n  const nodes = buildNodes(segs);\n  const graph = buildGraph(nodes, version);\n  const path = dijkstra.find_path(graph.map, 'start', 'end');\n  const optimizedSegs = [];\n  for (let i = 1; i < path.length - 1; i++) {\n    optimizedSegs.push(graph.table[path[i]].node);\n  }\n  return exports.fromArray(mergeSegments(optimizedSegs));\n};\n\n/**\n * Splits a string in various segments with the modes which\n * best represent their content.\n * The produced segments are far from being optimized.\n * The output of this function is only used to estimate a QR Code version\n * which may contain the data.\n *\n * @param  {string} data Input string\n * @return {Array}       Array of segments\n */\nexports.rawSplit = function rawSplit(data) {\n  return exports.fromArray(getSegmentsFromString(data, Utils.isKanjiModeEnabled()));\n};","map":null,"metadata":{},"sourceType":"script","externalDependencies":[]}