1 2/** 3@license 4Copyright (c) 2017 The Polymer Project Authors. All rights reserved. 5This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt 6The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt 7The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt 8Code distributed by Google as part of the polymer project is also 9subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt 10*/ 11import './boot.js'; 12 13/** 14 * Module with utilities for manipulating structured data path strings. 15 * 16 * @summary Module with utilities for manipulating structured data path strings. 17 */ 18 19/** 20 * Returns true if the given string is a structured data path (has dots). 21 * 22 * Example: 23 * 24 * ``` 25 * isPath('foo.bar.baz') // true 26 * isPath('foo') // false 27 * ``` 28 * 29 * @param {string} path Path string 30 * @return {boolean} True if the string contained one or more dots 31 */ 32export function isPath(path) { 33 return path.indexOf('.') >= 0; 34} 35 36/** 37 * Returns the root property name for the given path. 38 * 39 * Example: 40 * 41 * ``` 42 * root('foo.bar.baz') // 'foo' 43 * root('foo') // 'foo' 44 * ``` 45 * 46 * @param {string} path Path string 47 * @return {string} Root property name 48 */ 49export function root(path) { 50 let dotIndex = path.indexOf('.'); 51 if (dotIndex === -1) { 52 return path; 53 } 54 return path.slice(0, dotIndex); 55} 56 57/** 58 * Given `base` is `foo.bar`, `foo` is an ancestor, `foo.bar` is not 59 * Returns true if the given path is an ancestor of the base path. 60 * 61 * Example: 62 * 63 * ``` 64 * isAncestor('foo.bar', 'foo') // true 65 * isAncestor('foo.bar', 'foo.bar') // false 66 * isAncestor('foo.bar', 'foo.bar.baz') // false 67 * ``` 68 * 69 * @param {string} base Path string to test against. 70 * @param {string} path Path string to test. 71 * @return {boolean} True if `path` is an ancestor of `base`. 72 */ 73export function isAncestor(base, path) { 74 // base.startsWith(path + '.'); 75 return base.indexOf(path + '.') === 0; 76} 77 78/** 79 * Given `base` is `foo.bar`, `foo.bar.baz` is an descendant 80 * 81 * Example: 82 * 83 * ``` 84 * isDescendant('foo.bar', 'foo.bar.baz') // true 85 * isDescendant('foo.bar', 'foo.bar') // false 86 * isDescendant('foo.bar', 'foo') // false 87 * ``` 88 * 89 * @param {string} base Path string to test against. 90 * @param {string} path Path string to test. 91 * @return {boolean} True if `path` is a descendant of `base`. 92 */ 93export function isDescendant(base, path) { 94 // path.startsWith(base + '.'); 95 return path.indexOf(base + '.') === 0; 96} 97 98/** 99 * Replaces a previous base path with a new base path, preserving the 100 * remainder of the path. 101 * 102 * User must ensure `path` has a prefix of `base`. 103 * 104 * Example: 105 * 106 * ``` 107 * translate('foo.bar', 'zot', 'foo.bar.baz') // 'zot.baz' 108 * ``` 109 * 110 * @param {string} base Current base string to remove 111 * @param {string} newBase New base string to replace with 112 * @param {string} path Path to translate 113 * @return {string} Translated string 114 */ 115export function translate(base, newBase, path) { 116 return newBase + path.slice(base.length); 117} 118 119/** 120 * @param {string} base Path string to test against 121 * @param {string} path Path string to test 122 * @return {boolean} True if `path` is equal to `base` 123 */ 124export function matches(base, path) { 125 return (base === path) || 126 isAncestor(base, path) || 127 isDescendant(base, path); 128} 129 130/** 131 * Converts array-based paths to flattened path. String-based paths 132 * are returned as-is. 133 * 134 * Example: 135 * 136 * ``` 137 * normalize(['foo.bar', 0, 'baz']) // 'foo.bar.0.baz' 138 * normalize('foo.bar.0.baz') // 'foo.bar.0.baz' 139 * ``` 140 * 141 * @param {string | !Array<string|number>} path Input path 142 * @return {string} Flattened path 143 */ 144export function normalize(path) { 145 if (Array.isArray(path)) { 146 let parts = []; 147 for (let i=0; i<path.length; i++) { 148 let args = path[i].toString().split('.'); 149 for (let j=0; j<args.length; j++) { 150 parts.push(args[j]); 151 } 152 } 153 return parts.join('.'); 154 } else { 155 return path; 156 } 157} 158 159/** 160 * Splits a path into an array of property names. Accepts either arrays 161 * of path parts or strings. 162 * 163 * Example: 164 * 165 * ``` 166 * split(['foo.bar', 0, 'baz']) // ['foo', 'bar', '0', 'baz'] 167 * split('foo.bar.0.baz') // ['foo', 'bar', '0', 'baz'] 168 * ``` 169 * 170 * @param {string | !Array<string|number>} path Input path 171 * @return {!Array<string>} Array of path parts 172 * @suppress {checkTypes} 173 */ 174export function split(path) { 175 if (Array.isArray(path)) { 176 return normalize(path).split('.'); 177 } 178 return path.toString().split('.'); 179} 180 181/** 182 * Reads a value from a path. If any sub-property in the path is `undefined`, 183 * this method returns `undefined` (will never throw. 184 * 185 * @param {Object} root Object from which to dereference path from 186 * @param {string | !Array<string|number>} path Path to read 187 * @param {Object=} info If an object is provided to `info`, the normalized 188 * (flattened) path will be set to `info.path`. 189 * @return {*} Value at path, or `undefined` if the path could not be 190 * fully dereferenced. 191 */ 192export function get(root, path, info) { 193 let prop = root; 194 let parts = split(path); 195 // Loop over path parts[0..n-1] and dereference 196 for (let i=0; i<parts.length; i++) { 197 if (!prop) { 198 return; 199 } 200 let part = parts[i];
201 prop = prop[part]; 202 } 203 if (info) { 204 info.path = parts.join('.'); 205 } 206 return prop; 207} 208 209/** 210 * Sets a value to a path. If any sub-property in the path is `undefined`, 211 * this method will no-op. 212 * 213 * @param {Object} root Object from which to dereference path from 214 * @param {string | !Array<string|number>} path Path to set 215 * @param {*} value Value to set to path 216 * @return {string | undefined} The normalized version of the input path 217 */ 218export function set(root, path, value) { 219 let prop = root; 220 let parts = split(path); 221 let last = parts[parts.length-1]; 222 if (parts.length > 1) { 223 // Loop over path parts[0..n-2] and dereference 224 for (let i=0; i<parts.length-1; i++) { 225 let part = parts[i]; 226 prop = prop[part]; 227 if (!prop) { 228 return; 229 } 230 } 231 // Set value to object at end of path 232 prop[last] = value; 233 } else { 234 // Simple property set 235 prop[path] = value; 236 } 237 return parts.join('.'); 238} 239 240/** 241 * Returns true if the given string is a structured data path (has dots). 242 * 243 * This function is deprecated. Use `isPath` instead. 244 * 245 * Example: 246 * 247 * ``` 248 * isDeep('foo.bar.baz') // true 249 * isDeep('foo') // false 250 * ``` 251 * 252 * @deprecated 253 * @param {string} path Path string 254 * @return {boolean} True if the string contained one or more dots 255 */ 256export const isDeep = isPath;
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.