UNPKG

zxcvbn3

Version:

realistic password strength estimation

69 lines 3.6 kB
/** * returns the six adjacent coordinates on a standard keyboard, where each row is slanted to the * right from the last. adjacencies are clockwise, starting with key to the left, then two keys * above, then right key, then two keys below. (that is, only near-diagonal keys are adjacent, * so g's coordinate is adjacent to those of t,y,b,v, but not those of r,u,n,c.) */ export function get_slanted_adjacent_coords(x, y) { return [[x - 1, y], [x, y - 1], [x + 1, y - 1], [x + 1, y], [x, y + 1], [x - 1, y + 1]]; } /** * returns the nine clockwise adjacent coordinates on a keypad, where each row is vert aligned. */ export function get_aligned_adjacent_coords(x, y) { return [[x - 1, y], [x - 1, y - 1], [x, y - 1], [x + 1, y - 1], [x + 1, y], [x + 1, y + 1], [x, y + 1], [x - 1, y + 1]]; } /** * builds an adjacency graph as a dictionary: {character: [adjacent_characters]}. * adjacent characters occur in a clockwise order. * for example: * on qwerty layout, 'g' maps to ['fF', 'tT', 'yY', 'hH', 'bB', 'vV'] * on keypad layout, '7' maps to [None, None, None, '=', '8', '5', '4', None] */ export function build_graph(layout_str, slanted) { layout_str = layout_str.replace(/^\n|\n$/g, ""); // replace line breaks at start and end const position_table = []; // maps from tuple (x,y) -> characters at that position. const tokens = layout_str.split(/\n| /g).filter(t => t !== "").map(t => t.replace(/\n/g, "")); const token_size = tokens[0].length; const x_unit = token_size + 1; // x position unit len is token len plus 1 for the following whitespace. const adjacency_func = slanted ? get_slanted_adjacent_coords : get_aligned_adjacent_coords; for (const token of tokens) { if (token.length != token_size) { throw new Error('token len mismatch:\n ' + layout_str); } } const lines = layout_str.split('\n'); for (const y of lines.keys()) { const line = lines[y]; // the way I illustrated keys above, each qwerty row is indented one space in from the last const slant = slanted ? y - 1 : 0; for (const token of line.split(" ").filter(t => t !== "")) { const x = Math.floor((line.indexOf(token) - slant - (slanted ? 1 : 0)) / x_unit); const remainder = (line.indexOf(token) - slant - (slanted ? 1 : 0)) % x_unit; if (remainder) { throw new Error(`unexpected x offset ${remainder} for ${token} in:\n${layout_str}`); } if (!position_table[x]) { position_table[x] = []; } position_table[x][y] = token; } } const adjacency_graph = {}; for (const line of position_table) { for (const chars of line) { for (const char of chars || []) { adjacency_graph[char] = []; for (const [x2, y2] of adjacency_func(position_table.indexOf(line), line.indexOf(chars))) { /* position in the list indicates direction for qwerty, 0 is left, 1 is top, 2 is top right, ...) for edge chars like 1 or m, insert None as a placeholder when needed so that each character in the graph has a same - length adjacency list. */ adjacency_graph[char].push(position_table[x2] && position_table[x2][y2] ? position_table[x2][y2] : null); } } } } return adjacency_graph; } //# sourceMappingURL=generate_adjacency_graph.js.map