object.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
  2. function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
  3. import { arrayEach } from './array';
  4. /**
  5. * Generate schema for passed object.
  6. *
  7. * @param {Array|Object} object
  8. * @returns {Array|Object}
  9. */
  10. export function duckSchema(object) {
  11. var schema;
  12. if (Array.isArray(object)) {
  13. schema = [];
  14. } else {
  15. schema = {};
  16. objectEach(object, function (value, key) {
  17. if (key === '__children') {
  18. return;
  19. }
  20. if (value && (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object' && !Array.isArray(value)) {
  21. schema[key] = duckSchema(value);
  22. } else if (Array.isArray(value)) {
  23. if (value.length && _typeof(value[0]) === 'object' && !Array.isArray(value[0])) {
  24. schema[key] = [duckSchema(value[0])];
  25. } else {
  26. schema[key] = [];
  27. }
  28. } else {
  29. schema[key] = null;
  30. }
  31. });
  32. }
  33. return schema;
  34. }
  35. /**
  36. * Inherit without without calling parent constructor, and setting `Child.prototype.constructor` to `Child` instead of `Parent`.
  37. * Creates temporary dummy function to call it as constructor.
  38. * Described in ticket: https://github.com/handsontable/handsontable/pull/516
  39. *
  40. * @param {Object} Child child class
  41. * @param {Object} Parent parent class
  42. * @return {Object} extended Child
  43. */
  44. export function inherit(Child, Parent) {
  45. Parent.prototype.constructor = Parent;
  46. Child.prototype = new Parent();
  47. Child.prototype.constructor = Child;
  48. return Child;
  49. }
  50. /**
  51. * Perform shallow extend of a target object with extension's own properties.
  52. *
  53. * @param {Object} target An object that will receive the new properties.
  54. * @param {Object} extension An object containing additional properties to merge into the target.
  55. */
  56. export function extend(target, extension) {
  57. objectEach(extension, function (value, key) {
  58. target[key] = value;
  59. });
  60. return target;
  61. }
  62. /**
  63. * Perform deep extend of a target object with extension's own properties.
  64. *
  65. * @param {Object} target An object that will receive the new properties.
  66. * @param {Object} extension An object containing additional properties to merge into the target.
  67. */
  68. export function deepExtend(target, extension) {
  69. objectEach(extension, function (value, key) {
  70. if (extension[key] && _typeof(extension[key]) === 'object') {
  71. if (!target[key]) {
  72. if (Array.isArray(extension[key])) {
  73. target[key] = [];
  74. } else if (Object.prototype.toString.call(extension[key]) === '[object Date]') {
  75. target[key] = extension[key];
  76. } else {
  77. target[key] = {};
  78. }
  79. }
  80. deepExtend(target[key], extension[key]);
  81. } else {
  82. target[key] = extension[key];
  83. }
  84. });
  85. }
  86. /**
  87. * Perform deep clone of an object.
  88. * WARNING! Only clones JSON properties. Will cause error when `obj` contains a function, Date, etc.
  89. *
  90. * @param {Object} obj An object that will be cloned
  91. * @return {Object}
  92. */
  93. export function deepClone(obj) {
  94. if ((typeof obj === 'undefined' ? 'undefined' : _typeof(obj)) === 'object') {
  95. return JSON.parse(JSON.stringify(obj));
  96. }
  97. return obj;
  98. }
  99. /**
  100. * Shallow clone object.
  101. *
  102. * @param {Object} object
  103. * @returns {Object}
  104. */
  105. export function clone(object) {
  106. var result = {};
  107. objectEach(object, function (value, key) {
  108. result[key] = value;
  109. });
  110. return result;
  111. }
  112. /**
  113. * Extend the Base object (usually prototype) of the functionality the `mixins` objects.
  114. *
  115. * @param {Object} Base Base object which will be extended.
  116. * @param {Object} mixins The object of the functionality will be "copied".
  117. * @returns {Object}
  118. */
  119. export function mixin(Base) {
  120. if (!Base.MIXINS) {
  121. Base.MIXINS = [];
  122. }
  123. for (var _len = arguments.length, mixins = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
  124. mixins[_key - 1] = arguments[_key];
  125. }
  126. arrayEach(mixins, function (mixin) {
  127. Base.MIXINS.push(mixin.MIXIN_NAME);
  128. objectEach(mixin, function (value, key) {
  129. if (Base.prototype[key] !== void 0) {
  130. throw new Error('Mixin conflict. Property \'' + key + '\' already exist and cannot be overwritten.');
  131. }
  132. if (typeof value === 'function') {
  133. Base.prototype[key] = value;
  134. } else {
  135. var getter = function _getter(propertyName, initialValue) {
  136. propertyName = '_' + propertyName;
  137. var initValue = function initValue(value) {
  138. if (Array.isArray(value) || isObject(value)) {
  139. value = deepClone(value);
  140. }
  141. return value;
  142. };
  143. return function () {
  144. if (this[propertyName] === void 0) {
  145. this[propertyName] = initValue(initialValue);
  146. }
  147. return this[propertyName];
  148. };
  149. };
  150. var setter = function _setter(propertyName) {
  151. propertyName = '_' + propertyName;
  152. return function (value) {
  153. this[propertyName] = value;
  154. };
  155. };
  156. Object.defineProperty(Base.prototype, key, {
  157. get: getter(key, value),
  158. set: setter(key),
  159. configurable: true
  160. });
  161. }
  162. });
  163. });
  164. return Base;
  165. }
  166. /**
  167. * Checks if two objects or arrays are (deep) equal
  168. *
  169. * @param {Object|Array} object1
  170. * @param {Object|Array} object2
  171. * @returns {Boolean}
  172. */
  173. export function isObjectEquals(object1, object2) {
  174. return JSON.stringify(object1) === JSON.stringify(object2);
  175. }
  176. /**
  177. * Determines whether given object is a plain Object.
  178. * Note: String and Array are not plain Objects
  179. * @param {*} obj
  180. * @returns {boolean}
  181. */
  182. export function isObject(obj) {
  183. return Object.prototype.toString.call(obj) == '[object Object]';
  184. }
  185. export function defineGetter(object, property, value, options) {
  186. options.value = value;
  187. options.writable = options.writable !== false;
  188. options.enumerable = options.enumerable !== false;
  189. options.configurable = options.configurable !== false;
  190. Object.defineProperty(object, property, options);
  191. }
  192. /**
  193. * A specialized version of `.forEach` for objects.
  194. *
  195. * @param {Object} object The object to iterate over.
  196. * @param {Function} iteratee The function invoked per iteration.
  197. * @returns {Object} Returns `object`.
  198. */
  199. export function objectEach(object, iteratee) {
  200. for (var key in object) {
  201. if (!object.hasOwnProperty || object.hasOwnProperty && Object.prototype.hasOwnProperty.call(object, key)) {
  202. if (iteratee(object[key], key, object) === false) {
  203. break;
  204. }
  205. }
  206. }
  207. return object;
  208. }
  209. /**
  210. * Get object property by its name. Access to sub properties can be achieved by dot notation (e.q. `'foo.bar.baz'`).
  211. *
  212. * @param {Object} object Object which value will be exported.
  213. * @param {String} name Object property name.
  214. * @returns {*}
  215. */
  216. export function getProperty(object, name) {
  217. var names = name.split('.');
  218. var result = object;
  219. objectEach(names, function (name) {
  220. result = result[name];
  221. if (result === void 0) {
  222. result = void 0;
  223. return false;
  224. }
  225. });
  226. return result;
  227. }
  228. /**
  229. * Return object length (recursively).
  230. *
  231. * @param {*} object Object for which we want get length.
  232. * @returns {Number}
  233. */
  234. export function deepObjectSize(object) {
  235. if (!isObject(object)) {
  236. return 0;
  237. }
  238. var recursObjLen = function recursObjLen(obj) {
  239. var result = 0;
  240. if (isObject(obj)) {
  241. objectEach(obj, function (key) {
  242. result += recursObjLen(key);
  243. });
  244. } else {
  245. result++;
  246. }
  247. return result;
  248. };
  249. return recursObjLen(object);
  250. }
  251. /**
  252. * Create object with property where its value change will be observed.
  253. *
  254. * @param {*} [defaultValue=undefined] Default value.
  255. * @param {String} [propertyToListen='value'] Property to listen.
  256. * @returns {Object}
  257. */
  258. export function createObjectPropListener(defaultValue) {
  259. var _holder;
  260. var propertyToListen = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'value';
  261. var privateProperty = '_' + propertyToListen;
  262. var holder = (_holder = {
  263. _touched: false
  264. }, _defineProperty(_holder, privateProperty, defaultValue), _defineProperty(_holder, 'isTouched', function isTouched() {
  265. return this._touched;
  266. }), _holder);
  267. Object.defineProperty(holder, propertyToListen, {
  268. get: function get() {
  269. return this[privateProperty];
  270. },
  271. set: function set(value) {
  272. this._touched = true;
  273. this[privateProperty] = value;
  274. },
  275. enumerable: true,
  276. configurable: true
  277. });
  278. return holder;
  279. }
  280. /**
  281. * Check if at specified `key` there is any value for `object`.
  282. *
  283. * @param {Object} object Object to search value at specyfic key.
  284. * @param {String} key String key to check.
  285. */
  286. export function hasOwnProperty(object, key) {
  287. return Object.prototype.hasOwnProperty.call(object, key);
  288. }