UNPKG

sugar

Version:

A Javascript utility library for working with native objects.

162 lines (132 loc) 4.75 kB
'use strict'; var PROPERTY_RANGE_REG = require('../var/PROPERTY_RANGE_REG'), CommonChars = require('../var/CommonChars'), map = require('./map'), isDefined = require('./isDefined'), classChecks = require('../var/classChecks'), periodSplit = require('./periodSplit'), assertArray = require('./assertArray'), isObjectType = require('./isObjectType'), assertWritable = require('./assertWritable'), coreUtilityAliases = require('../var/coreUtilityAliases'); var isString = classChecks.isString, hasOwn = coreUtilityAliases.hasOwn, HALF_WIDTH_PERIOD = CommonChars.HALF_WIDTH_PERIOD; function handleDeepProperty(obj, key, any, has, fill, fillLast, val) { var ns, bs, ps, cbi, set, isLast, isPush, isIndex, nextIsIndex, exists; ns = obj; if (key == null) return; if (isObjectType(key)) { // Allow array and array-like accessors bs = [key]; } else { key = String(key); if (key.indexOf('..') !== -1) { return handleArrayIndexRange(obj, key, any, val); } bs = key.split('['); } set = isDefined(val); for (var i = 0, blen = bs.length; i < blen; i++) { ps = bs[i]; if (isString(ps)) { ps = periodSplit(ps); } for (var j = 0, plen = ps.length; j < plen; j++) { key = ps[j]; // Is this the last key? isLast = i === blen - 1 && j === plen - 1; // Index of the closing ] cbi = key.indexOf(']'); // Is the key an array index? isIndex = cbi !== -1; // Is this array push syntax "[]"? isPush = set && cbi === 0; // If the bracket split was successful and this is the last element // in the dot split, then we know the next key will be an array index. nextIsIndex = blen > 1 && j === plen - 1; if (isPush) { // Set the index to the end of the array key = ns.length; } else if (isIndex) { // Remove the closing ] key = key.slice(0, -1); } // If the array index is less than 0, then // add its length to allow negative indexes. if (isIndex && key < 0) { key = +key + ns.length; } // Bracket keys may look like users[5] or just [5], so the leading // characters are optional. We can enter the namespace if this is the // 2nd part, if there is only 1 part, or if there is an explicit key. if (i || key || blen === 1) { // TODO: need to be sure this check handles ''.length when // we refactor. exists = any ? key in Object(ns) : hasOwn(ns, key); // Non-existent namespaces are only filled if they are intermediate // (not at the end) or explicitly filling the last. if (fill && (!isLast || fillLast) && !exists) { // For our purposes, last only needs to be an array. ns = ns[key] = nextIsIndex || (fillLast && isLast) ? [] : {}; continue; } if (has) { if (isLast || !exists) { return exists; } } else if (set && isLast) { assertWritable(ns); ns[key] = val; } ns = exists ? ns[key] : undefined; } } } return ns; } function handleArrayIndexRange(obj, key, any, val) { var match, start, end, leading, trailing, arr, set; match = key.match(PROPERTY_RANGE_REG); if (!match) { return; } set = isDefined(val); leading = match[1]; if (leading) { arr = handleDeepProperty(obj, leading, any, false, set ? true : false, true); } else { arr = obj; } assertArray(arr); trailing = match[4]; start = match[2] ? +match[2] : 0; end = match[3] ? +match[3] : arr.length; // A range of 0..1 is inclusive, so we need to add 1 to the end. If this // pushes the index from -1 to 0, then set it to the full length of the // array, otherwise it will return nothing. end = end === -1 ? arr.length : end + 1; if (set) { for (var i = start; i < end; i++) { handleDeepProperty(arr, i + trailing, any, false, true, false, val); } } else { arr = arr.slice(start, end); // If there are trailing properties, then they need to be mapped for each // element in the array. if (trailing) { if (trailing.charAt(0) === HALF_WIDTH_PERIOD) { // Need to chomp the period if one is trailing after the range. We // can't do this at the regex level because it will be required if // we're setting the value as it needs to be concatentated together // with the array index to be set. trailing = trailing.slice(1); } return map(arr, function(el) { return handleDeepProperty(el, trailing); }); } } return arr; } module.exports = handleDeepProperty;