undefsafe.js 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. 'use strict';
  2. function undefsafe(obj, path, value, __res) {
  3. // I'm not super keen on this private function, but it's because
  4. // it'll also be use in the browser and I wont *one* function exposed
  5. function split(path) {
  6. var res = [];
  7. var level = 0;
  8. var key = '';
  9. for (var i = 0; i < path.length; i++) {
  10. var c = path.substr(i, 1);
  11. if (level === 0 && (c === '.' || c === '[')) {
  12. if (c === '[') {
  13. level++;
  14. i++;
  15. c = path.substr(i, 1);
  16. }
  17. if (key) {
  18. // the first value could be a string
  19. res.push(key);
  20. }
  21. key = '';
  22. continue;
  23. }
  24. if (c === ']') {
  25. level--;
  26. key = key.slice(0, -1);
  27. continue;
  28. }
  29. key += c;
  30. }
  31. res.push(key);
  32. return res;
  33. }
  34. // bail if there's nothing
  35. if (obj === undefined || obj === null) {
  36. return undefined;
  37. }
  38. var parts = split(path);
  39. var key = null;
  40. var type = typeof obj;
  41. var root = obj;
  42. var parent = obj;
  43. var star =
  44. parts.filter(function(_) {
  45. return _ === '*';
  46. }).length > 0;
  47. // we're dealing with a primitive
  48. if (type !== 'object' && type !== 'function') {
  49. return obj;
  50. } else if (path.trim() === '') {
  51. return obj;
  52. }
  53. key = parts[0];
  54. var i = 0;
  55. for (; i < parts.length; i++) {
  56. key = parts[i];
  57. parent = obj;
  58. if (key === '*') {
  59. // loop through each property
  60. var prop = '';
  61. var res = __res || [];
  62. for (prop in parent) {
  63. var shallowObj = undefsafe(
  64. obj[prop],
  65. parts.slice(i + 1).join('.'),
  66. value,
  67. res
  68. );
  69. if (shallowObj && shallowObj !== res) {
  70. if ((value && shallowObj === value) || value === undefined) {
  71. if (value !== undefined) {
  72. return shallowObj;
  73. }
  74. res.push(shallowObj);
  75. }
  76. }
  77. }
  78. if (res.length === 0) {
  79. return undefined;
  80. }
  81. return res;
  82. }
  83. if (Object.getOwnPropertyNames(obj).indexOf(key) == -1) {
  84. return undefined;
  85. }
  86. obj = obj[key];
  87. if (obj === undefined || obj === null) {
  88. break;
  89. }
  90. }
  91. // if we have a null object, make sure it's the one the user was after,
  92. // if it's not (i.e. parts has a length) then give undefined back.
  93. if (obj === null && i !== parts.length - 1) {
  94. obj = undefined;
  95. } else if (!star && value) {
  96. key = path.split('.').pop();
  97. parent[key] = value;
  98. }
  99. return obj;
  100. }
  101. if (typeof module !== 'undefined') {
  102. module.exports = undefsafe;
  103. }