\n * All rights reserved\n *\n * Licensed under the MIT license.\n *\n * http://www.opensource.org/licenses/mit-license.php\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *****************************************************************************/\nvar dijkstra = {\n single_source_shortest_paths: function(graph, s, d) {\n // Predecessor map for each node that has been encountered.\n // node ID => predecessor node ID\n var predecessors = {};\n\n // Costs of shortest paths from s to all nodes encountered.\n // node ID => cost\n var costs = {};\n costs[s] = 0;\n\n // Costs of shortest paths from s to all nodes encountered; differs from\n // `costs` in that it provides easy access to the node that currently has\n // the known shortest path from s.\n // XXX: Do we actually need both `costs` and `open`?\n var open = dijkstra.PriorityQueue.make();\n open.push(s, 0);\n\n var closest,\n u, v,\n cost_of_s_to_u,\n adjacent_nodes,\n cost_of_e,\n cost_of_s_to_u_plus_cost_of_e,\n cost_of_s_to_v,\n first_visit;\n while (!open.empty()) {\n // In the nodes remaining in graph that have a known cost from s,\n // find the node, u, that currently has the shortest path from s.\n closest = open.pop();\n u = closest.value;\n cost_of_s_to_u = closest.cost;\n\n // Get nodes adjacent to u...\n adjacent_nodes = graph[u] || {};\n\n // ...and explore the edges that connect u to those nodes, updating\n // the cost of the shortest paths to any or all of those nodes as\n // necessary. v is the node across the current edge from u.\n for (v in adjacent_nodes) {\n if (adjacent_nodes.hasOwnProperty(v)) {\n // Get the cost of the edge running from u to v.\n cost_of_e = adjacent_nodes[v];\n\n // Cost of s to u plus the cost of u to v across e--this is *a*\n // cost from s to v that may or may not be less than the current\n // known cost to v.\n cost_of_s_to_u_plus_cost_of_e = cost_of_s_to_u + cost_of_e;\n\n // If we haven't visited v yet OR if the current known cost from s to\n // v is greater than the new cost we just found (cost of s to u plus\n // cost of u to v across e), update v's cost in the cost list and\n // update v's predecessor in the predecessor list (it's now u).\n cost_of_s_to_v = costs[v];\n first_visit = (typeof costs[v] === 'undefined');\n if (first_visit || cost_of_s_to_v > cost_of_s_to_u_plus_cost_of_e) {\n costs[v] = cost_of_s_to_u_plus_cost_of_e;\n open.push(v, cost_of_s_to_u_plus_cost_of_e);\n predecessors[v] = u;\n }\n }\n }\n }\n\n if (typeof d !== 'undefined' && typeof costs[d] === 'undefined') {\n var msg = ['Could not find a path from ', s, ' to ', d, '.'].join('');\n throw new Error(msg);\n }\n\n return predecessors;\n },\n\n extract_shortest_path_from_predecessor_list: function(predecessors, d) {\n var nodes = [];\n var u = d;\n var predecessor;\n while (u) {\n nodes.push(u);\n predecessor = predecessors[u];\n u = predecessors[u];\n }\n nodes.reverse();\n return nodes;\n },\n\n find_path: function(graph, s, d) {\n var predecessors = dijkstra.single_source_shortest_paths(graph, s, d);\n return dijkstra.extract_shortest_path_from_predecessor_list(\n predecessors, d);\n },\n\n /**\n * A very naive priority queue implementation.\n */\n PriorityQueue: {\n make: function (opts) {\n var T = dijkstra.PriorityQueue,\n t = {},\n key;\n opts = opts || {};\n for (key in T) {\n if (T.hasOwnProperty(key)) {\n t[key] = T[key];\n }\n }\n t.queue = [];\n t.sorter = opts.sorter || T.default_sorter;\n return t;\n },\n\n default_sorter: function (a, b) {\n return a.cost - b.cost;\n },\n\n /**\n * Add a new item to the queue and ensure the highest priority element\n * is at the front of the queue.\n */\n push: function (value, cost) {\n var item = {value: value, cost: cost};\n this.queue.push(item);\n this.queue.sort(this.sorter);\n },\n\n /**\n * Return the highest priority element in the queue.\n */\n pop: function () {\n return this.queue.shift();\n },\n\n empty: function () {\n return this.queue.length === 0;\n }\n }\n};\n\n\n// node.js module exports\nif (typeof module !== 'undefined') {\n module.exports = dijkstra;\n}\n","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\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\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\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\n const segs = numSegs.concat(alphaNumSegs, byteSegs, kanjiSegs)\n\n return segs\n .sort(function (s1, s2) {\n return s1.index - s2.index\n })\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\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\n switch (seg.mode) {\n case Mode.NUMERIC:\n nodes.push([seg,\n { data: seg.data, mode: Mode.ALPHANUMERIC, length: seg.length },\n { data: seg.data, mode: Mode.BYTE, length: seg.length }\n ])\n break\n case Mode.ALPHANUMERIC:\n nodes.push([seg,\n { data: seg.data, mode: Mode.BYTE, length: seg.length }\n ])\n break\n case Mode.KANJI:\n nodes.push([seg,\n { data: seg.data, mode: Mode.BYTE, length: getStringByteLength(seg.data) }\n ])\n break\n case Mode.BYTE:\n nodes.push([\n { data: seg.data, mode: Mode.BYTE, length: getStringByteLength(seg.data) }\n ])\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 = { start: {} }\n let prevNodeIds = ['start']\n\n for (let i = 0; i < nodes.length; i++) {\n const nodeGroup = nodes[i]\n const currentNodeIds = []\n\n for (let j = 0; j < nodeGroup.length; j++) {\n const node = nodeGroup[j]\n const key = '' + i + j\n\n currentNodeIds.push(key)\n table[key] = { node: node, lastCount: 0 }\n graph[key] = {}\n\n for (let n = 0; n < prevNodeIds.length; n++) {\n const prevNodeId = prevNodeIds[n]\n\n if (table[prevNodeId] && table[prevNodeId].node.mode === node.mode) {\n graph[prevNodeId][key] =\n getSegmentBitsLength(table[prevNodeId].lastCount + node.length, node.mode) -\n getSegmentBitsLength(table[prevNodeId].lastCount, node.mode)\n\n table[prevNodeId].lastCount += node.length\n } else {\n if (table[prevNodeId]) table[prevNodeId].lastCount = node.length\n\n graph[prevNodeId][key] = getSegmentBitsLength(node.length, node.mode) +\n 4 + Mode.getCharCountIndicator(node.mode, version) // switch cost\n }\n }\n }\n\n prevNodeIds = currentNodeIds\n }\n\n for (let n = 0; n < prevNodeIds.length; n++) {\n graph[prevNodeIds[n]].end = 0\n }\n\n return { map: graph, table: table }\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\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 + '\"' +\n ' cannot be encoded with mode ' + Mode.toString(mode) +\n '.\\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\n switch (mode) {\n case Mode.NUMERIC:\n return new NumericData(data)\n\n case Mode.ALPHANUMERIC:\n return new AlphanumericData(data)\n\n case Mode.KANJI:\n return new KanjiData(data)\n\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\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\n const nodes = buildNodes(segs)\n const graph = buildGraph(nodes, version)\n const path = dijkstra.find_path(graph.map, 'start', 'end')\n\n const optimizedSegs = []\n for (let i = 1; i < path.length - 1; i++) {\n optimizedSegs.push(graph.table[path[i]].node)\n }\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(\n getSegmentsFromString(data, Utils.isKanjiModeEnabled())\n )\n}\n","const Utils = require('./utils')\nconst ECLevel = require('./error-correction-level')\nconst BitBuffer = require('./bit-buffer')\nconst BitMatrix = require('./bit-matrix')\nconst AlignmentPattern = require('./alignment-pattern')\nconst FinderPattern = require('./finder-pattern')\nconst MaskPattern = require('./mask-pattern')\nconst ECCode = require('./error-correction-code')\nconst ReedSolomonEncoder = require('./reed-solomon-encoder')\nconst Version = require('./version')\nconst FormatInfo = require('./format-info')\nconst Mode = require('./mode')\nconst Segments = require('./segments')\n\n/**\n * QRCode for JavaScript\n *\n * modified by Ryan Day for nodejs support\n * Copyright (c) 2011 Ryan Day\n *\n * Licensed under the MIT license:\n * http://www.opensource.org/licenses/mit-license.php\n *\n//---------------------------------------------------------------------\n// QRCode for JavaScript\n//\n// Copyright (c) 2009 Kazuhiko Arase\n//\n// URL: http://www.d-project.com/\n//\n// Licensed under the MIT license:\n// http://www.opensource.org/licenses/mit-license.php\n//\n// The word \"QR Code\" is registered trademark of\n// DENSO WAVE INCORPORATED\n// http://www.denso-wave.com/qrcode/faqpatent-e.html\n//\n//---------------------------------------------------------------------\n*/\n\n/**\n * Add finder patterns bits to matrix\n *\n * @param {BitMatrix} matrix Modules matrix\n * @param {Number} version QR Code version\n */\nfunction setupFinderPattern (matrix, version) {\n const size = matrix.size\n const pos = FinderPattern.getPositions(version)\n\n for (let i = 0; i < pos.length; i++) {\n const row = pos[i][0]\n const col = pos[i][1]\n\n for (let r = -1; r <= 7; r++) {\n if (row + r <= -1 || size <= row + r) continue\n\n for (let c = -1; c <= 7; c++) {\n if (col + c <= -1 || size <= col + c) continue\n\n if ((r >= 0 && r <= 6 && (c === 0 || c === 6)) ||\n (c >= 0 && c <= 6 && (r === 0 || r === 6)) ||\n (r >= 2 && r <= 4 && c >= 2 && c <= 4)) {\n matrix.set(row + r, col + c, true, true)\n } else {\n matrix.set(row + r, col + c, false, true)\n }\n }\n }\n }\n}\n\n/**\n * Add timing pattern bits to matrix\n *\n * Note: this function must be called before {@link setupAlignmentPattern}\n *\n * @param {BitMatrix} matrix Modules matrix\n */\nfunction setupTimingPattern (matrix) {\n const size = matrix.size\n\n for (let r = 8; r < size - 8; r++) {\n const value = r % 2 === 0\n matrix.set(r, 6, value, true)\n matrix.set(6, r, value, true)\n }\n}\n\n/**\n * Add alignment patterns bits to matrix\n *\n * Note: this function must be called after {@link setupTimingPattern}\n *\n * @param {BitMatrix} matrix Modules matrix\n * @param {Number} version QR Code version\n */\nfunction setupAlignmentPattern (matrix, version) {\n const pos = AlignmentPattern.getPositions(version)\n\n for (let i = 0; i < pos.length; i++) {\n const row = pos[i][0]\n const col = pos[i][1]\n\n for (let r = -2; r <= 2; r++) {\n for (let c = -2; c <= 2; c++) {\n if (r === -2 || r === 2 || c === -2 || c === 2 ||\n (r === 0 && c === 0)) {\n matrix.set(row + r, col + c, true, true)\n } else {\n matrix.set(row + r, col + c, false, true)\n }\n }\n }\n }\n}\n\n/**\n * Add version info bits to matrix\n *\n * @param {BitMatrix} matrix Modules matrix\n * @param {Number} version QR Code version\n */\nfunction setupVersionInfo (matrix, version) {\n const size = matrix.size\n const bits = Version.getEncodedBits(version)\n let row, col, mod\n\n for (let i = 0; i < 18; i++) {\n row = Math.floor(i / 3)\n col = i % 3 + size - 8 - 3\n mod = ((bits >> i) & 1) === 1\n\n matrix.set(row, col, mod, true)\n matrix.set(col, row, mod, true)\n }\n}\n\n/**\n * Add format info bits to matrix\n *\n * @param {BitMatrix} matrix Modules matrix\n * @param {ErrorCorrectionLevel} errorCorrectionLevel Error correction level\n * @param {Number} maskPattern Mask pattern reference value\n */\nfunction setupFormatInfo (matrix, errorCorrectionLevel, maskPattern) {\n const size = matrix.size\n const bits = FormatInfo.getEncodedBits(errorCorrectionLevel, maskPattern)\n let i, mod\n\n for (i = 0; i < 15; i++) {\n mod = ((bits >> i) & 1) === 1\n\n // vertical\n if (i < 6) {\n matrix.set(i, 8, mod, true)\n } else if (i < 8) {\n matrix.set(i + 1, 8, mod, true)\n } else {\n matrix.set(size - 15 + i, 8, mod, true)\n }\n\n // horizontal\n if (i < 8) {\n matrix.set(8, size - i - 1, mod, true)\n } else if (i < 9) {\n matrix.set(8, 15 - i - 1 + 1, mod, true)\n } else {\n matrix.set(8, 15 - i - 1, mod, true)\n }\n }\n\n // fixed module\n matrix.set(size - 8, 8, 1, true)\n}\n\n/**\n * Add encoded data bits to matrix\n *\n * @param {BitMatrix} matrix Modules matrix\n * @param {Uint8Array} data Data codewords\n */\nfunction setupData (matrix, data) {\n const size = matrix.size\n let inc = -1\n let row = size - 1\n let bitIndex = 7\n let byteIndex = 0\n\n for (let col = size - 1; col > 0; col -= 2) {\n if (col === 6) col--\n\n while (true) {\n for (let c = 0; c < 2; c++) {\n if (!matrix.isReserved(row, col - c)) {\n let dark = false\n\n if (byteIndex < data.length) {\n dark = (((data[byteIndex] >>> bitIndex) & 1) === 1)\n }\n\n matrix.set(row, col - c, dark)\n bitIndex--\n\n if (bitIndex === -1) {\n byteIndex++\n bitIndex = 7\n }\n }\n }\n\n row += inc\n\n if (row < 0 || size <= row) {\n row -= inc\n inc = -inc\n break\n }\n }\n }\n}\n\n/**\n * Create encoded codewords from data input\n *\n * @param {Number} version QR Code version\n * @param {ErrorCorrectionLevel} errorCorrectionLevel Error correction level\n * @param {ByteData} data Data input\n * @return {Uint8Array} Buffer containing encoded codewords\n */\nfunction createData (version, errorCorrectionLevel, segments) {\n // Prepare data buffer\n const buffer = new BitBuffer()\n\n segments.forEach(function (data) {\n // prefix data with mode indicator (4 bits)\n buffer.put(data.mode.bit, 4)\n\n // Prefix data with character count indicator.\n // The character count indicator is a string of bits that represents the\n // number of characters that are being encoded.\n // The character count indicator must be placed after the mode indicator\n // and must be a certain number of bits long, depending on the QR version\n // and data mode\n // @see {@link Mode.getCharCountIndicator}.\n buffer.put(data.getLength(), Mode.getCharCountIndicator(data.mode, version))\n\n // add binary data sequence to buffer\n data.write(buffer)\n })\n\n // Calculate required number of bits\n const totalCodewords = Utils.getSymbolTotalCodewords(version)\n const ecTotalCodewords = ECCode.getTotalCodewordsCount(version, errorCorrectionLevel)\n const dataTotalCodewordsBits = (totalCodewords - ecTotalCodewords) * 8\n\n // Add a terminator.\n // If the bit string is shorter than the total number of required bits,\n // a terminator of up to four 0s must be added to the right side of the string.\n // If the bit string is more than four bits shorter than the required number of bits,\n // add four 0s to the end.\n if (buffer.getLengthInBits() + 4 <= dataTotalCodewordsBits) {\n buffer.put(0, 4)\n }\n\n // If the bit string is fewer than four bits shorter, add only the number of 0s that\n // are needed to reach the required number of bits.\n\n // After adding the terminator, if the number of bits in the string is not a multiple of 8,\n // pad the string on the right with 0s to make the string's length a multiple of 8.\n while (buffer.getLengthInBits() % 8 !== 0) {\n buffer.putBit(0)\n }\n\n // Add pad bytes if the string is still shorter than the total number of required bits.\n // Extend the buffer to fill the data capacity of the symbol corresponding to\n // the Version and Error Correction Level by adding the Pad Codewords 11101100 (0xEC)\n // and 00010001 (0x11) alternately.\n const remainingByte = (dataTotalCodewordsBits - buffer.getLengthInBits()) / 8\n for (let i = 0; i < remainingByte; i++) {\n buffer.put(i % 2 ? 0x11 : 0xEC, 8)\n }\n\n return createCodewords(buffer, version, errorCorrectionLevel)\n}\n\n/**\n * Encode input data with Reed-Solomon and return codewords with\n * relative error correction bits\n *\n * @param {BitBuffer} bitBuffer Data to encode\n * @param {Number} version QR Code version\n * @param {ErrorCorrectionLevel} errorCorrectionLevel Error correction level\n * @return {Uint8Array} Buffer containing encoded codewords\n */\nfunction createCodewords (bitBuffer, version, errorCorrectionLevel) {\n // Total codewords for this QR code version (Data + Error correction)\n const totalCodewords = Utils.getSymbolTotalCodewords(version)\n\n // Total number of error correction codewords\n const ecTotalCodewords = ECCode.getTotalCodewordsCount(version, errorCorrectionLevel)\n\n // Total number of data codewords\n const dataTotalCodewords = totalCodewords - ecTotalCodewords\n\n // Total number of blocks\n const ecTotalBlocks = ECCode.getBlocksCount(version, errorCorrectionLevel)\n\n // Calculate how many blocks each group should contain\n const blocksInGroup2 = totalCodewords % ecTotalBlocks\n const blocksInGroup1 = ecTotalBlocks - blocksInGroup2\n\n const totalCodewordsInGroup1 = Math.floor(totalCodewords / ecTotalBlocks)\n\n const dataCodewordsInGroup1 = Math.floor(dataTotalCodewords / ecTotalBlocks)\n const dataCodewordsInGroup2 = dataCodewordsInGroup1 + 1\n\n // Number of EC codewords is the same for both groups\n const ecCount = totalCodewordsInGroup1 - dataCodewordsInGroup1\n\n // Initialize a Reed-Solomon encoder with a generator polynomial of degree ecCount\n const rs = new ReedSolomonEncoder(ecCount)\n\n let offset = 0\n const dcData = new Array(ecTotalBlocks)\n const ecData = new Array(ecTotalBlocks)\n let maxDataSize = 0\n const buffer = new Uint8Array(bitBuffer.buffer)\n\n // Divide the buffer into the required number of blocks\n for (let b = 0; b < ecTotalBlocks; b++) {\n const dataSize = b < blocksInGroup1 ? dataCodewordsInGroup1 : dataCodewordsInGroup2\n\n // extract a block of data from buffer\n dcData[b] = buffer.slice(offset, offset + dataSize)\n\n // Calculate EC codewords for this data block\n ecData[b] = rs.encode(dcData[b])\n\n offset += dataSize\n maxDataSize = Math.max(maxDataSize, dataSize)\n }\n\n // Create final data\n // Interleave the data and error correction codewords from each block\n const data = new Uint8Array(totalCodewords)\n let index = 0\n let i, r\n\n // Add data codewords\n for (i = 0; i < maxDataSize; i++) {\n for (r = 0; r < ecTotalBlocks; r++) {\n if (i < dcData[r].length) {\n data[index++] = dcData[r][i]\n }\n }\n }\n\n // Apped EC codewords\n for (i = 0; i < ecCount; i++) {\n for (r = 0; r < ecTotalBlocks; r++) {\n data[index++] = ecData[r][i]\n }\n }\n\n return data\n}\n\n/**\n * Build QR Code symbol\n *\n * @param {String} data Input string\n * @param {Number} version QR Code version\n * @param {ErrorCorretionLevel} errorCorrectionLevel Error level\n * @param {MaskPattern} maskPattern Mask pattern\n * @return {Object} Object containing symbol data\n */\nfunction createSymbol (data, version, errorCorrectionLevel, maskPattern) {\n let segments\n\n if (Array.isArray(data)) {\n segments = Segments.fromArray(data)\n } else if (typeof data === 'string') {\n let estimatedVersion = version\n\n if (!estimatedVersion) {\n const rawSegments = Segments.rawSplit(data)\n\n // Estimate best version that can contain raw splitted segments\n estimatedVersion = Version.getBestVersionForData(rawSegments, errorCorrectionLevel)\n }\n\n // Build optimized segments\n // If estimated version is undefined, try with the highest version\n segments = Segments.fromString(data, estimatedVersion || 40)\n } else {\n throw new Error('Invalid data')\n }\n\n // Get the min version that can contain data\n const bestVersion = Version.getBestVersionForData(segments, errorCorrectionLevel)\n\n // If no version is found, data cannot be stored\n if (!bestVersion) {\n throw new Error('The amount of data is too big to be stored in a QR Code')\n }\n\n // If not specified, use min version as default\n if (!version) {\n version = bestVersion\n\n // Check if the specified version can contain the data\n } else if (version < bestVersion) {\n throw new Error('\\n' +\n 'The chosen QR Code version cannot contain this amount of data.\\n' +\n 'Minimum version required to store current data is: ' + bestVersion + '.\\n'\n )\n }\n\n const dataBits = createData(version, errorCorrectionLevel, segments)\n\n // Allocate matrix buffer\n const moduleCount = Utils.getSymbolSize(version)\n const modules = new BitMatrix(moduleCount)\n\n // Add function modules\n setupFinderPattern(modules, version)\n setupTimingPattern(modules)\n setupAlignmentPattern(modules, version)\n\n // Add temporary dummy bits for format info just to set them as reserved.\n // This is needed to prevent these bits from being masked by {@link MaskPattern.applyMask}\n // since the masking operation must be performed only on the encoding region.\n // These blocks will be replaced with correct values later in code.\n setupFormatInfo(modules, errorCorrectionLevel, 0)\n\n if (version >= 7) {\n setupVersionInfo(modules, version)\n }\n\n // Add data codewords\n setupData(modules, dataBits)\n\n if (isNaN(maskPattern)) {\n // Find best mask pattern\n maskPattern = MaskPattern.getBestMask(modules,\n setupFormatInfo.bind(null, modules, errorCorrectionLevel))\n }\n\n // Apply mask pattern\n MaskPattern.applyMask(maskPattern, modules)\n\n // Replace format info bits with correct values\n setupFormatInfo(modules, errorCorrectionLevel, maskPattern)\n\n return {\n modules: modules,\n version: version,\n errorCorrectionLevel: errorCorrectionLevel,\n maskPattern: maskPattern,\n segments: segments\n }\n}\n\n/**\n * QR Code\n *\n * @param {String | Array} data Input data\n * @param {Object} options Optional configurations\n * @param {Number} options.version QR Code version\n * @param {String} options.errorCorrectionLevel Error correction level\n * @param {Function} options.toSJISFunc Helper func to convert utf8 to sjis\n */\nexports.create = function create (data, options) {\n if (typeof data === 'undefined' || data === '') {\n throw new Error('No input text')\n }\n\n let errorCorrectionLevel = ECLevel.M\n let version\n let mask\n\n if (typeof options !== 'undefined') {\n // Use higher error correction level as default\n errorCorrectionLevel = ECLevel.from(options.errorCorrectionLevel, ECLevel.M)\n version = Version.from(options.version)\n mask = MaskPattern.from(options.maskPattern)\n\n if (options.toSJISFunc) {\n Utils.setToSJISFunction(options.toSJISFunc)\n }\n }\n\n return createSymbol(data, version, errorCorrectionLevel, mask)\n}\n","function hex2rgba (hex) {\n if (typeof hex === 'number') {\n hex = hex.toString()\n }\n\n if (typeof hex !== 'string') {\n throw new Error('Color should be defined as hex string')\n }\n\n let hexCode = hex.slice().replace('#', '').split('')\n if (hexCode.length < 3 || hexCode.length === 5 || hexCode.length > 8) {\n throw new Error('Invalid hex color: ' + hex)\n }\n\n // Convert from short to long form (fff -> ffffff)\n if (hexCode.length === 3 || hexCode.length === 4) {\n hexCode = Array.prototype.concat.apply([], hexCode.map(function (c) {\n return [c, c]\n }))\n }\n\n // Add default alpha value\n if (hexCode.length === 6) hexCode.push('F', 'F')\n\n const hexValue = parseInt(hexCode.join(''), 16)\n\n return {\n r: (hexValue >> 24) & 255,\n g: (hexValue >> 16) & 255,\n b: (hexValue >> 8) & 255,\n a: hexValue & 255,\n hex: '#' + hexCode.slice(0, 6).join('')\n }\n}\n\nexports.getOptions = function getOptions (options) {\n if (!options) options = {}\n if (!options.color) options.color = {}\n\n const margin = typeof options.margin === 'undefined' ||\n options.margin === null ||\n options.margin < 0\n ? 4\n : options.margin\n\n const width = options.width && options.width >= 21 ? options.width : undefined\n const scale = options.scale || 4\n\n return {\n width: width,\n scale: width ? 4 : scale,\n margin: margin,\n color: {\n dark: hex2rgba(options.color.dark || '#000000ff'),\n light: hex2rgba(options.color.light || '#ffffffff')\n },\n type: options.type,\n rendererOpts: options.rendererOpts || {}\n }\n}\n\nexports.getScale = function getScale (qrSize, opts) {\n return opts.width && opts.width >= qrSize + opts.margin * 2\n ? opts.width / (qrSize + opts.margin * 2)\n : opts.scale\n}\n\nexports.getImageWidth = function getImageWidth (qrSize, opts) {\n const scale = exports.getScale(qrSize, opts)\n return Math.floor((qrSize + opts.margin * 2) * scale)\n}\n\nexports.qrToImageData = function qrToImageData (imgData, qr, opts) {\n const size = qr.modules.size\n const data = qr.modules.data\n const scale = exports.getScale(size, opts)\n const symbolSize = Math.floor((size + opts.margin * 2) * scale)\n const scaledMargin = opts.margin * scale\n const palette = [opts.color.light, opts.color.dark]\n\n for (let i = 0; i < symbolSize; i++) {\n for (let j = 0; j < symbolSize; j++) {\n let posDst = (i * symbolSize + j) * 4\n let pxColor = opts.color.light\n\n if (i >= scaledMargin && j >= scaledMargin &&\n i < symbolSize - scaledMargin && j < symbolSize - scaledMargin) {\n const iSrc = Math.floor((i - scaledMargin) / scale)\n const jSrc = Math.floor((j - scaledMargin) / scale)\n pxColor = palette[data[iSrc * size + jSrc] ? 1 : 0]\n }\n\n imgData[posDst++] = pxColor.r\n imgData[posDst++] = pxColor.g\n imgData[posDst++] = pxColor.b\n imgData[posDst] = pxColor.a\n }\n }\n}\n","const Utils = require('./utils')\n\nfunction clearCanvas (ctx, canvas, size) {\n ctx.clearRect(0, 0, canvas.width, canvas.height)\n\n if (!canvas.style) canvas.style = {}\n canvas.height = size\n canvas.width = size\n canvas.style.height = size + 'px'\n canvas.style.width = size + 'px'\n}\n\nfunction getCanvasElement () {\n try {\n return document.createElement('canvas')\n } catch (e) {\n throw new Error('You need to specify a canvas element')\n }\n}\n\nexports.render = function render (qrData, canvas, options) {\n let opts = options\n let canvasEl = canvas\n\n if (typeof opts === 'undefined' && (!canvas || !canvas.getContext)) {\n opts = canvas\n canvas = undefined\n }\n\n if (!canvas) {\n canvasEl = getCanvasElement()\n }\n\n opts = Utils.getOptions(opts)\n const size = Utils.getImageWidth(qrData.modules.size, opts)\n\n const ctx = canvasEl.getContext('2d')\n const image = ctx.createImageData(size, size)\n Utils.qrToImageData(image.data, qrData, opts)\n\n clearCanvas(ctx, canvasEl, size)\n ctx.putImageData(image, 0, 0)\n\n return canvasEl\n}\n\nexports.renderToDataURL = function renderToDataURL (qrData, canvas, options) {\n let opts = options\n\n if (typeof opts === 'undefined' && (!canvas || !canvas.getContext)) {\n opts = canvas\n canvas = undefined\n }\n\n if (!opts) opts = {}\n\n const canvasEl = exports.render(qrData, canvas, opts)\n\n const type = opts.type || 'image/png'\n const rendererOpts = opts.rendererOpts || {}\n\n return canvasEl.toDataURL(type, rendererOpts.quality)\n}\n","const Utils = require('./utils')\n\nfunction getColorAttrib (color, attrib) {\n const alpha = color.a / 255\n const str = attrib + '=\"' + color.hex + '\"'\n\n return alpha < 1\n ? str + ' ' + attrib + '-opacity=\"' + alpha.toFixed(2).slice(1) + '\"'\n : str\n}\n\nfunction svgCmd (cmd, x, y) {\n let str = cmd + x\n if (typeof y !== 'undefined') str += ' ' + y\n\n return str\n}\n\nfunction qrToPath (data, size, margin) {\n let path = ''\n let moveBy = 0\n let newRow = false\n let lineLength = 0\n\n for (let i = 0; i < data.length; i++) {\n const col = Math.floor(i % size)\n const row = Math.floor(i / size)\n\n if (!col && !newRow) newRow = true\n\n if (data[i]) {\n lineLength++\n\n if (!(i > 0 && col > 0 && data[i - 1])) {\n path += newRow\n ? svgCmd('M', col + margin, 0.5 + row + margin)\n : svgCmd('m', moveBy, 0)\n\n moveBy = 0\n newRow = false\n }\n\n if (!(col + 1 < size && data[i + 1])) {\n path += svgCmd('h', lineLength)\n lineLength = 0\n }\n } else {\n moveBy++\n }\n }\n\n return path\n}\n\nexports.render = function render (qrData, options, cb) {\n const opts = Utils.getOptions(options)\n const size = qrData.modules.size\n const data = qrData.modules.data\n const qrcodesize = size + opts.margin * 2\n\n const bg = !opts.color.light.a\n ? ''\n : ''\n\n const path =\n ''\n\n const viewBox = 'viewBox=\"' + '0 0 ' + qrcodesize + ' ' + qrcodesize + '\"'\n\n const width = !opts.width ? '' : 'width=\"' + opts.width + '\" height=\"' + opts.width + '\" '\n\n const svgTag = '\\n'\n\n if (typeof cb === 'function') {\n cb(null, svgTag)\n }\n\n return svgTag\n}\n","\nconst canPromise = require('./can-promise')\n\nconst QRCode = require('./core/qrcode')\nconst CanvasRenderer = require('./renderer/canvas')\nconst SvgRenderer = require('./renderer/svg-tag.js')\n\nfunction renderCanvas (renderFunc, canvas, text, opts, cb) {\n const args = [].slice.call(arguments, 1)\n const argsNum = args.length\n const isLastArgCb = typeof args[argsNum - 1] === 'function'\n\n if (!isLastArgCb && !canPromise()) {\n throw new Error('Callback required as last argument')\n }\n\n if (isLastArgCb) {\n if (argsNum < 2) {\n throw new Error('Too few arguments provided')\n }\n\n if (argsNum === 2) {\n cb = text\n text = canvas\n canvas = opts = undefined\n } else if (argsNum === 3) {\n if (canvas.getContext && typeof cb === 'undefined') {\n cb = opts\n opts = undefined\n } else {\n cb = opts\n opts = text\n text = canvas\n canvas = undefined\n }\n }\n } else {\n if (argsNum < 1) {\n throw new Error('Too few arguments provided')\n }\n\n if (argsNum === 1) {\n text = canvas\n canvas = opts = undefined\n } else if (argsNum === 2 && !canvas.getContext) {\n opts = text\n text = canvas\n canvas = undefined\n }\n\n return new Promise(function (resolve, reject) {\n try {\n const data = QRCode.create(text, opts)\n resolve(renderFunc(data, canvas, opts))\n } catch (e) {\n reject(e)\n }\n })\n }\n\n try {\n const data = QRCode.create(text, opts)\n cb(null, renderFunc(data, canvas, opts))\n } catch (e) {\n cb(e)\n }\n}\n\nexports.create = QRCode.create\nexports.toCanvas = renderCanvas.bind(null, CanvasRenderer.render)\nexports.toDataURL = renderCanvas.bind(null, CanvasRenderer.renderToDataURL)\n\n// only svg for now.\nexports.toString = renderCanvas.bind(null, function (data, _, opts) {\n return SvgRenderer.render(data, opts)\n})\n","import * as QRCode from 'qrcode';\r\nwindow.Dashboard = (function () {\r\n var ws;\r\n var surveyId;\r\n\r\n function setActiveView(activeView) {\r\n $('[data-view]').hide();\r\n if (activeView) {\r\n $('[data-view=' + activeView + ']').show();\r\n }\r\n }\r\n\r\n var updateUI = function (phase, message) {\r\n setActiveView();\r\n $('#selectedOptionsHeader').hide();\r\n $('.js-options').empty();\r\n\r\n if (phase === 'Initialized') {\r\n $('.js-surveyName').html(message.Category.Description);\r\n $('.js-numUsers').html(message.NumUsers);\r\n setActiveView('initialized');\r\n } else if (phase === 'ReadyForStart') {\r\n setActiveView('readyForStart');\r\n $('.js-numUsers').html(message.NumUsers);\r\n } else if (phase === 'Active' || phase === 'Waiting' || phase === 'QuestionsFinished') {\r\n setActiveView('question');\r\n\r\n switch (phase) {\r\n case 'Active':\r\n $('.presenter-button--show-answers').show();\r\n $('.presenter-button--next-question').hide();\r\n break;\r\n case 'Waiting':\r\n case 'QuestionsFinished':\r\n $('.presenter-button--show-answers').hide();\r\n $('.presenter-button--next-question').show();\r\n break;\r\n }\r\n\r\n var numTotalAnswers = 0;\r\n message.ActiveQuestion.Options.forEach(function (option) {\r\n numTotalAnswers += new Number(option.NumAnswers);\r\n });\r\n $('.js-numTotalAnswers').html(numTotalAnswers);\r\n\r\n $('.dashboard-question').toggleClass(\r\n 'dashboard-question--completed',\r\n phase !== 'Active'\r\n );\r\n $('.js-surveyName').html(message.Category.Description);\r\n $('.js-numUsers').html(message.NumUsers);\r\n\r\n var levelNum = new Number(message.ActiveQuestion.Level.Id + 1);\r\n var levelMarkup = Array(3)\r\n .fill()\r\n .map(function (_, idx) {\r\n return levelNum > idx\r\n ? ''\r\n : '';\r\n });\r\n\r\n $('.js-activeLevel').html(levelMarkup);\r\n $('.js-activeQuestionNum').html(new Number(message.State.ActiveQuestionId + 1));\r\n\r\n populateAnswers(phase, message);\r\n\r\n $('.js-options').attr('data-count', message.ActiveQuestion.Options.length);\r\n $('.js-activeQuestion').html('' + message.ActiveQuestion.Description + '');\r\n\r\n var textfill = function () {\r\n // If there are more than 2 options the text needs to be limited to 47 pixels. Otherwise it can be up to 70 pixels\r\n var maxFontPixels =\r\n message.ActiveQuestion.Options.length > 2 && $(window).width() <= 1920\r\n ? 47\r\n : 70;\r\n\r\n $('.js-activeQuestion').textfill({ maxFontPixels: maxFontPixels });\r\n };\r\n\r\n textfill();\r\n\r\n var debouncedTextfill = window.Common.debounce(function () {\r\n textfill();\r\n }, 250);\r\n\r\n window.removeEventListener('resize', debouncedTextfill);\r\n window.addEventListener('resize', debouncedTextfill);\r\n\r\n // Change button text when we've reached the last question\r\n if (parseInt(message.State.ActiveQuestionId) === parseInt(message.NumQuestions - 1)) {\r\n var $btn = $('.js-advanceButton-finish');\r\n $btn.text($btn.data().finishText);\r\n }\r\n } else if (phase === 'Level') {\r\n var level = parseInt(message.ActiveQuestion.Level.Id) + 1;\r\n $('.js-numLevel').html(level);\r\n $('[data-view=level]').attr('data-level', level);\r\n $('.dashboard-level__stars .js-star').removeClass('js-star--filled');\r\n $('.dashboard-level__stars .js-star:lt(' + level + ')').addClass('js-star--filled');\r\n setActiveView('level');\r\n } else if (phase === 'SurveyFinished') {\r\n var totalAverage = window.Common.CalculateAverageAnswerValue(message.Questions);\r\n\r\n var resultMessage;\r\n if (totalAverage < 0) resultMessage = 'ERROR';\r\n\r\n if (totalAverage < 0.67) {\r\n resultMessage = message.ResultMessages[0];\r\n $('#result-advice-green').show();\r\n } else if (totalAverage < 1.33) {\r\n resultMessage = message.ResultMessages[1];\r\n $('#result-advice-yellow').show();\r\n } else {\r\n resultMessage = message.ResultMessages[2];\r\n $('#result-advice-red').show();\r\n }\r\n\r\n $('.js-result').html(resultMessage);\r\n\r\n $('#completeResults').html('');\r\n\r\n message.Questions.forEach(function (question, idx) {\r\n $('#completeResults').append(\r\n \"Fråga \" +\r\n (idx + 1) +\r\n ' - ' +\r\n question.Description +\r\n '
'\r\n );\r\n var numAnswers = question.Options.reduce(function (acc, val) {\r\n return acc + val.NumAnswers;\r\n }, 0);\r\n\r\n var highestPercentage = question.Options.reduce(function (acc, option) {\r\n var percentage = Math.floor((option.NumAnswers / numAnswers) * 100);\r\n if (percentage > acc) {\r\n acc = percentage;\r\n }\r\n return acc;\r\n }, 0);\r\n\r\n question.Options.forEach(function (option) {\r\n var percentage =\r\n numAnswers > 0 ? Math.floor((option.NumAnswers / numAnswers) * 100) : 0;\r\n\r\n var className = '';\r\n\r\n if (percentage === 0) {\r\n className = 'unpicked-answer';\r\n } else if (percentage === highestPercentage) {\r\n className = 'most-picked-answer';\r\n } else {\r\n className = 'picked-answer';\r\n }\r\n\r\n $('#completeResults').append(\r\n $(\r\n \"\" +\r\n option.Description +\r\n '
'\r\n )\r\n );\r\n });\r\n });\r\n\r\n setActiveView('surveyFinished');\r\n }\r\n };\r\n\r\n var populateAnswers = function (phase, message) {\r\n if (phase === 'Active') {\r\n message.ActiveQuestion.Options.forEach(function (option) {\r\n $('.js-options').append(\r\n $(\"\" + option.Description + '
')\r\n );\r\n });\r\n } else {\r\n var numAnswers = message.ActiveQuestion.Options.reduce(function (acc, val) {\r\n return acc + val.NumAnswers;\r\n }, 0);\r\n var highestPercentage = message.ActiveQuestion.Options.reduce(function (acc, option) {\r\n var percentage = Math.floor((option.NumAnswers / numAnswers) * 100);\r\n if (percentage > acc) {\r\n acc = percentage;\r\n }\r\n return acc;\r\n }, 0);\r\n message.ActiveQuestion.Options.forEach(function (option) {\r\n var className = 'flex-col';\r\n var percentage = Math.floor((option.NumAnswers / numAnswers) * 100);\r\n if (percentage === 0) {\r\n className += ' flex-col--unpicked';\r\n } else if (percentage === highestPercentage) {\r\n className += ' flex-col--most-picked';\r\n } else {\r\n className += ' flex-col--picked';\r\n }\r\n $('.js-options').append(\r\n $(\"\" + option.Description + '
')\r\n );\r\n });\r\n }\r\n };\r\n\r\n var initDashboardWsListeners = function () {\r\n if (!ws) {\r\n console.error('Failed to init listener, connection not ready');\r\n return;\r\n }\r\n\r\n ws.onopen = function (e) {\r\n console.log('Connection open', e);\r\n };\r\n\r\n ws.onclose = function (e) {\r\n console.log('Connection closed', e);\r\n };\r\n\r\n ws.onmessage = function (e) {\r\n var message = JSON.parse(e.data);\r\n var phase = window.Common.PhaseIntToString(message.State.ActivePhase);\r\n console.log('Message received:', e, 'Parsed message:', message, 'New phase:', phase);\r\n updateUI(phase, message);\r\n };\r\n };\r\n\r\n var checkConnectionState = function () {\r\n if (!ws || ws.readyState !== 1) {\r\n console.error('Connection not open', ws);\r\n return false;\r\n }\r\n return true;\r\n };\r\n\r\n var initKeepAliveLoop = function () {\r\n var interval = setInterval(function () {\r\n if (!checkConnectionState()) {\r\n clearInterval(interval);\r\n return;\r\n }\r\n ws.send('');\r\n }, 5000);\r\n };\r\n\r\n var initDashboardInteraction = function () {\r\n $('.js-advanceButton').click(function () {\r\n if (!checkConnectionState) return;\r\n ws.send(JSON.stringify({ surveyId: surveyId, instruction: 'advance' }));\r\n });\r\n\r\n $('.js-gobackButton').click(function () {\r\n if (!checkConnectionState) return;\r\n ws.send(JSON.stringify({ surveyId: surveyId, instruction: 'goback' }));\r\n });\r\n\r\n $('#startButton').click(function () {\r\n if (!checkConnectionState) return;\r\n ws.send(JSON.stringify({ surveyId: surveyId, instruction: 'advance' }));\r\n });\r\n\r\n $('#finishButton').click(function () {\r\n if (!checkConnectionState) return;\r\n ws.send(JSON.stringify({ surveyId: surveyId, instruction: 'advance' }));\r\n });\r\n\r\n $('.js-selectable-text').click(function () {\r\n var selectText = function (elem) {\r\n var selection = window.getSelection();\r\n var range = document.createRange();\r\n\r\n range.selectNodeContents(elem);\r\n selection.removeAllRanges();\r\n selection.addRange(range);\r\n };\r\n\r\n selectText($(this).get(0));\r\n });\r\n\r\n $('.save-button--email').click(function (ev) {\r\n $('.email-modal, .email-modal-backdrop').removeClass('is-hidden');\r\n });\r\n\r\n $('.email-modal-backdrop').click(function (ev) {\r\n $('.email-modal, .email-modal-backdrop').addClass('is-hidden');\r\n });\r\n\r\n $('.email-modal').submit(function (ev) {\r\n ev.preventDefault();\r\n if (!$(this)[0].checkValidity()) {\r\n return;\r\n }\r\n\r\n var $input = $('.email-modal input');\r\n var email = $input.val();\r\n var surveyId = $input.data('surveyId');\r\n var url = $input.data('url');\r\n url = url.substring(0, url.length - 1); // Remove trailing slash\r\n\r\n var $error = $('.email-modal__error');\r\n var $success = $('.email-modal__success');\r\n\r\n $error.addClass('is-hidden');\r\n\r\n $.post(url + '?toAddress=' + email + '&surveyId=' + surveyId, function (isSuccessful) {\r\n if (isSuccessful) {\r\n $success.removeClass('is-hidden');\r\n $('.email-modal label, .email-modal button').hide();\r\n } else {\r\n $error.removeClass('is-hidden');\r\n }\r\n }).fail(function () {\r\n $error.removeClass('is-hidden');\r\n });\r\n });\r\n };\r\n\r\n var openConnection = function (url) {\r\n console.log('Opening websocket connection...');\r\n ws = new WebSocket(url);\r\n };\r\n\r\n var renderUrlQrCode = function () {\r\n var urlElement = document.getElementById('livesurvey-url');\r\n var canvas = document.getElementById('livesurvey-url-qr-code');\r\n if (!urlElement || !canvas) {\r\n return;\r\n }\r\n\r\n QRCode.toCanvas(canvas, urlElement.innerText, { width: 400 }, function (error) {\r\n if (error) console.error(error);\r\n canvas.classList.remove('d-none');\r\n });\r\n };\r\n\r\n var initDashboard = function (id) {\r\n surveyId = id;\r\n var wsProtocol = window.location.protocol.indexOf('https') !== -1 ? 'wss://' : 'ws://';\r\n openConnection(\r\n wsProtocol +\r\n window.location.hostname +\r\n '/API/LiveSurvey/Connect?id=' +\r\n surveyId +\r\n '&dashboard=true'\r\n );\r\n initDashboardWsListeners();\r\n initKeepAliveLoop();\r\n initDashboardInteraction();\r\n };\r\n\r\n renderUrlQrCode();\r\n\r\n return {\r\n Init: initDashboard\r\n };\r\n})();\r\n","window.User = (function () {\r\n var ws;\r\n var surveyId;\r\n\r\n var checkConnectionState = function () {\r\n if (!ws || ws.readyState !== 1) {\r\n console.error('Connection not open', ws);\r\n return false;\r\n }\r\n return true;\r\n };\r\n\r\n // FIXME: Refactor to be more clean\r\n function setActiveView(activeView) {\r\n $('[data-view]').hide();\r\n if (activeView) {\r\n $('[data-view=' + activeView + ']').show();\r\n }\r\n $('body').removeClass(function (_, css) {\r\n // Remove all `livesurvey-user-body--` classes\r\n return (css.match(/\\blivesurvey-user-body--\\S+/g) || []).join(' ');\r\n });\r\n if (activeView) {\r\n $('body').addClass('livesurvey-user-body--' + activeView);\r\n }\r\n }\r\n\r\n var updateQuestionInteraction = function (phase, activeQuestion, numQuestions, numUsers) {\r\n function hasAnsweredCurrentQuestion() {\r\n var activeQuestionNum = new Number(activeQuestion.Id);\r\n var lastAnsweredQuestionStr = window.Common.GetCookie('lastAnswered');\r\n if (lastAnsweredQuestionStr) {\r\n var lastAnsweredQuestion = JSON.parse(lastAnsweredQuestionStr);\r\n if (\r\n lastAnsweredQuestion &&\r\n lastAnsweredQuestion.surveyId === surveyId &&\r\n lastAnsweredQuestion.questionId >= activeQuestionNum\r\n ) {\r\n return true;\r\n }\r\n }\r\n return false;\r\n }\r\n\r\n function submitAnswer(qId, oId) {\r\n if (!checkConnectionState()) return;\r\n ws.send(\r\n JSON.stringify({\r\n surveyId: surveyId,\r\n instruction: 'answer',\r\n questionId: qId,\r\n optionId: oId\r\n })\r\n );\r\n window.Common.SetCookie(\r\n 'lastAnswered',\r\n JSON.stringify({ surveyId: surveyId, questionId: qId }),\r\n 1\r\n );\r\n setActiveView('answered');\r\n }\r\n\r\n if (phase === 'Initialized') {\r\n // User has connected, survey not yet started\r\n setActiveView('initialized');\r\n } else if (phase === 'ReadyForStart') {\r\n // Survey is about to start\r\n setActiveView('rules');\r\n } else if (phase === 'Active') {\r\n // The current question can be answered\r\n $('.js-numUsers').html(numUsers);\r\n if (hasAnsweredCurrentQuestion()) {\r\n setActiveView('answered');\r\n } else {\r\n if (!$('body').hasClass('livesurvey-user-body--active')) {\r\n generateQuestionsMarkup(activeQuestion, submitAnswer);\r\n }\r\n setActiveView('active');\r\n }\r\n } else if (phase === 'Waiting') {\r\n // Question is closed\r\n $('.js-numUsers').html(numUsers);\r\n setActiveView('waiting');\r\n } else if (phase === 'Level') {\r\n // Show level advancement screen\r\n var level = parseInt(activeQuestion.Level.Id) + 1;\r\n $('.js-numLevel').html(level);\r\n $('[data-view=level]').attr('data-level', level);\r\n $('.user-level__stars .js-star').removeClass('js-star--filled');\r\n $('.user-level__stars .js-star:lt(' + level + ')').addClass('js-star--filled');\r\n setActiveView('level');\r\n } else if (phase === 'QuestionsFinished') {\r\n // All questions have been answered\r\n setActiveView('waiting');\r\n } else if (phase === 'SurveyFinished') {\r\n // Presenter has finished\r\n setActiveView('finished');\r\n }\r\n };\r\n\r\n function generateQuestionsMarkup(activeQuestion, submitAnswer) {\r\n $('.js-activeQuestionNum').html(new Number(activeQuestion.Id) + 1);\r\n var levelNum = activeQuestion.Level.Id + 1;\r\n var levelMarkup = Array(3)\r\n .fill()\r\n .map(function (_, idx) {\r\n return levelNum > idx\r\n ? ''\r\n : '';\r\n });\r\n $('.js-activeLevel').html(levelMarkup);\r\n $('.js-questionDescription').html(activeQuestion.Description.replace(/\\n/g, '
'));\r\n $('.js-options').empty();\r\n $('.js-selectUserOption').removeClass('select-user-option-button--active');\r\n\r\n $('.js-selectUserOption, .user-option-btn').off('click.livesurvey');\r\n\r\n for (var i = 0, l = activeQuestion.Options.length; i < l; i++) {\r\n var option = activeQuestion.Options[i];\r\n $('.js-options').append(\r\n $(\r\n \"'\r\n )\r\n );\r\n }\r\n\r\n $('.user-option-btn').on('click.livesurvey', function () {\r\n $('.user-option-btn').removeClass('user-option-btn--active');\r\n $(this).addClass('user-option-btn--active');\r\n $('.js-selectUserOption').addClass('select-user-option-button--active');\r\n // Scroll to submit button when selecting an option\r\n $('.js-selectUserOption')[0].scrollIntoView();\r\n //$(\"html, body\").animate({ scrollTop: document.body.scrollHeight }, \"slow\");\r\n });\r\n\r\n $('.js-selectUserOption').on('click.livesurvey', function () {\r\n var $selOpt = $('.user-option-btn--active');\r\n if (!$selOpt.length) {\r\n return;\r\n }\r\n var selectedOptionId = $selOpt.data('option');\r\n submitAnswer(activeQuestion.Id, selectedOptionId);\r\n });\r\n }\r\n\r\n var initUserWsListeners = function () {\r\n ws.onopen = function () {\r\n console.log('Connection open');\r\n };\r\n\r\n ws.onclose = function (e) {\r\n console.log('Connection closed', e);\r\n setActiveView('error');\r\n };\r\n\r\n ws.onmessage = function (e) {\r\n var message = JSON.parse(e.data);\r\n var phase = window.Common.PhaseIntToString(message.State.ActivePhase);\r\n console.log('Message received:', e, 'Parsed message:', message, 'New phase:', phase);\r\n updateQuestionInteraction(\r\n phase,\r\n message.ActiveQuestion,\r\n message.NumQuestions,\r\n message.NumUsers\r\n );\r\n };\r\n };\r\n\r\n var openConnection = function (url) {\r\n console.log('Opening websocket connection...');\r\n ws = new WebSocket(url);\r\n };\r\n\r\n var initUser = function (id) {\r\n surveyId = id;\r\n\r\n var wsProtocol = window.location.protocol.indexOf('https') !== -1 ? 'wss://' : 'ws://';\r\n openConnection(\r\n wsProtocol + window.location.hostname + '/API/LiveSurvey/Connect?id=' + surveyId\r\n );\r\n\r\n initUserWsListeners();\r\n };\r\n\r\n return {\r\n Init: initUser\r\n };\r\n})();\r\n","var win = window;\n\nexport var raf = win.requestAnimationFrame\n || win.webkitRequestAnimationFrame\n || win.mozRequestAnimationFrame\n || win.msRequestAnimationFrame\n || function(cb) { return setTimeout(cb, 16); };\n","var win = window;\n\nexport var caf = win.cancelAnimationFrame\n || win.mozCancelAnimationFrame\n || function(id){ clearTimeout(id); };\n","export function extend() {\n var obj, name, copy,\n target = arguments[0] || {},\n i = 1,\n length = arguments.length;\n\n for (; i < length; i++) {\n if ((obj = arguments[i]) !== null) {\n for (name in obj) {\n copy = obj[name];\n\n if (target === copy) {\n continue;\n } else if (copy !== undefined) {\n target[name] = copy;\n }\n }\n }\n }\n return target;\n}","export function checkStorageValue (value) {\n return ['true', 'false'].indexOf(value) >= 0 ? JSON.parse(value) : value;\n}","export function setLocalStorage(storage, key, value, access) {\n if (access) {\n try { storage.setItem(key, value); } catch (e) {}\n }\n return value;\n}","export function getSlideId() {\n var id = window.tnsId;\n window.tnsId = !id ? 1 : id + 1;\n \n return 'tns' + window.tnsId;\n}","export function getBody () {\n var doc = document,\n body = doc.body;\n\n if (!body) {\n body = doc.createElement('body');\n body.fake = true;\n }\n\n return body;\n}","export var docElement = document.documentElement;","import { docElement } from './docElement.js';\n\nexport function setFakeBody (body) {\n var docOverflow = '';\n if (body.fake) {\n docOverflow = docElement.style.overflow;\n //avoid crashing IE8, if background image is used\n body.style.background = '';\n //Safari 5.13/5.1.4 OSX stops loading if ::-webkit-scrollbar is used and scrollbars are visible\n body.style.overflow = docElement.style.overflow = 'hidden';\n docElement.appendChild(body);\n }\n\n return docOverflow;\n}","import { docElement } from './docElement.js';\n\nexport function resetFakeBody (body, docOverflow) {\n if (body.fake) {\n body.remove();\n docElement.style.overflow = docOverflow;\n // Trigger layout so kinetic scrolling isn't disabled in iOS6+\n // eslint-disable-next-line\n docElement.offsetHeight;\n }\n}","// get css-calc \n// @return - false | calc | -webkit-calc | -moz-calc\n// @usage - var calc = getCalc(); \nimport { getBody } from './getBody.js';\nimport { setFakeBody } from './setFakeBody.js';\nimport { resetFakeBody } from './resetFakeBody.js';\n\nexport function calc() {\n var doc = document, \n body = getBody(),\n docOverflow = setFakeBody(body),\n div = doc.createElement('div'), \n result = false;\n\n body.appendChild(div);\n try {\n var str = '(10px * 10)',\n vals = ['calc' + str, '-moz-calc' + str, '-webkit-calc' + str],\n val;\n for (var i = 0; i < 3; i++) {\n val = vals[i];\n div.style.width = val;\n if (div.offsetWidth === 100) { \n result = val.replace(str, ''); \n break;\n }\n }\n } catch (e) {}\n \n body.fake ? resetFakeBody(body, docOverflow) : div.remove();\n\n return result;\n}","// get subpixel support value\n// @return - boolean\nimport { getBody } from './getBody.js';\nimport { setFakeBody } from './setFakeBody.js';\nimport { resetFakeBody } from './resetFakeBody.js';\n\nexport function percentageLayout() {\n // check subpixel layout supporting\n var doc = document,\n body = getBody(),\n docOverflow = setFakeBody(body),\n wrapper = doc.createElement('div'),\n outer = doc.createElement('div'),\n str = '',\n count = 70,\n perPage = 3,\n supported = false;\n\n wrapper.className = \"tns-t-subp2\";\n outer.className = \"tns-t-ct\";\n\n for (var i = 0; i < count; i++) {\n str += '';\n }\n\n outer.innerHTML = str;\n wrapper.appendChild(outer);\n body.appendChild(wrapper);\n\n supported = Math.abs(wrapper.getBoundingClientRect().left - outer.children[count - perPage].getBoundingClientRect().left) < 2;\n\n body.fake ? resetFakeBody(body, docOverflow) : wrapper.remove();\n\n return supported;\n}","import { getBody } from './getBody.js';\nimport { setFakeBody } from './setFakeBody.js';\nimport { resetFakeBody } from './resetFakeBody.js';\n\nexport function mediaquerySupport () {\n if (window.matchMedia || window.msMatchMedia) {\n return true;\n }\n \n var doc = document,\n body = getBody(),\n docOverflow = setFakeBody(body),\n div = doc.createElement('div'),\n style = doc.createElement('style'),\n rule = '@media all and (min-width:1px){.tns-mq-test{position:absolute}}',\n position;\n\n style.type = 'text/css';\n div.className = 'tns-mq-test';\n\n body.appendChild(style);\n body.appendChild(div);\n\n if (style.styleSheet) {\n style.styleSheet.cssText = rule;\n } else {\n style.appendChild(doc.createTextNode(rule));\n }\n\n position = window.getComputedStyle ? window.getComputedStyle(div).position : div.currentStyle['position'];\n\n body.fake ? resetFakeBody(body, docOverflow) : div.remove();\n\n return position === \"absolute\";\n}\n","// create and append style sheet\nexport function createStyleSheet (media, nonce) {\n // Create the