undefsafe.js 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  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) { // the first value could be a string
  18. res.push(key);
  19. }
  20. key = '';
  21. continue;
  22. }
  23. if (c === ']') {
  24. level--;
  25. key = key.slice(0, -1);
  26. continue;
  27. }
  28. key += c;
  29. }
  30. res.push(key);
  31. return res;
  32. }
  33. // bail if there's nothing
  34. if (obj === undefined || obj === null) {
  35. return undefined;
  36. }
  37. var parts = split(path);
  38. var key = null;
  39. var type = typeof obj;
  40. var root = obj;
  41. var parent = obj;
  42. var star = parts.filter(function (_) { return _ === '*' }).length > 0;
  43. // we're dealing with a primative
  44. if (type !== 'object' && type !== 'function') {
  45. return obj;
  46. } else if (path.trim() === '') {
  47. return obj;
  48. }
  49. key = parts[0];
  50. var i = 0;
  51. for (; i < parts.length; i++) {
  52. key = parts[i];
  53. parent = obj;
  54. if (key === '*') {
  55. // loop through each property
  56. var prop = '';
  57. var res = __res || [];
  58. for (prop in parent) {
  59. var shallowObj = undefsafe(obj[prop], parts.slice(i + 1).join('.'), value, res);
  60. if (shallowObj && shallowObj !== res) {
  61. if ((value && shallowObj === value) || (value === undefined)) {
  62. if (value !== undefined) {
  63. return shallowObj;
  64. }
  65. res.push(shallowObj);
  66. }
  67. }
  68. }
  69. if (res.length === 0) {
  70. return undefined;
  71. }
  72. return res;
  73. }
  74. obj = obj[key];
  75. if (obj === undefined || obj === null) {
  76. break;
  77. }
  78. }
  79. // if we have a null object, make sure it's the one the user was after,
  80. // if it's not (i.e. parts has a length) then give undefined back.
  81. if (obj === null && i !== parts.length - 1) {
  82. obj = undefined;
  83. } else if (!star && value) {
  84. key = path.split('.').pop();
  85. parent[key] = value;
  86. }
  87. return obj;
  88. }
  89. if (typeof module !== 'undefined') {
  90. module.exports = undefsafe;
  91. }