compare.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. /**
  2. * Copyright © Magento, Inc. All rights reserved.
  3. * See COPYING.txt for license details.
  4. */
  5. define([
  6. 'underscore',
  7. 'mage/utils/objects'
  8. ], function (_, utils) {
  9. 'use strict';
  10. var result = [];
  11. /**
  12. * Checks if all of the provided arrays contains equal values.
  13. *
  14. * @param {(Boolean|Array)} [keepOrder=false]
  15. * @param {Array} target
  16. * @returns {Boolean}
  17. */
  18. function equalArrays(keepOrder, target) {
  19. var args = _.toArray(arguments),
  20. arrays;
  21. if (!Array.isArray(keepOrder)) {
  22. arrays = args.slice(2);
  23. } else {
  24. target = keepOrder;
  25. keepOrder = false;
  26. arrays = args.slice(1);
  27. }
  28. if (!arrays.length) {
  29. return true;
  30. }
  31. return arrays.every(function (array) {
  32. if (array === target) {
  33. return true;
  34. } else if (array.length !== target.length) {
  35. return false;
  36. } else if (!keepOrder) {
  37. return !_.difference(target, array).length;
  38. }
  39. return array.every(function (value, index) {
  40. return target.indexOf(value) === index;
  41. });
  42. });
  43. }
  44. /**
  45. * Checks if two values are different.
  46. *
  47. * @param {*} a - First value.
  48. * @param {*} b - Second value.
  49. * @returns {Boolean}
  50. */
  51. function isDifferent(a, b) {
  52. var oldIsPrimitive = utils.isPrimitive(a);
  53. if (Array.isArray(a) && Array.isArray(b)) {
  54. return !equalArrays(true, a, b);
  55. }
  56. return oldIsPrimitive ? a !== b : true;
  57. }
  58. /**
  59. * @param {String} prefix
  60. * @param {String} part
  61. */
  62. function getPath(prefix, part) {
  63. return prefix ? prefix + '.' + part : part;
  64. }
  65. /**
  66. * Checks if object has own specified property.
  67. *
  68. * @param {*} obj - Value to be checked.
  69. * @param {String} key - Key of the property.
  70. * @returns {Boolean}
  71. */
  72. function hasOwn(obj, key) {
  73. return Object.prototype.hasOwnProperty.call(obj, key);
  74. }
  75. /**
  76. * @param {Array} changes
  77. */
  78. function getContainers(changes) {
  79. var containers = {},
  80. indexed = _.indexBy(changes, 'path');
  81. _.each(indexed, function (change, name) {
  82. var path;
  83. name.split('.').forEach(function (part) {
  84. path = getPath(path, part);
  85. if (path in indexed) {
  86. return;
  87. }
  88. (containers[path] = containers[path] || []).push(change);
  89. });
  90. });
  91. return containers;
  92. }
  93. /**
  94. * @param {String} path
  95. * @param {String} name
  96. * @param {String} type
  97. * @param {String} newValue
  98. * @param {String} oldValue
  99. */
  100. function addChange(path, name, type, newValue, oldValue) {
  101. var data;
  102. data = {
  103. path: path,
  104. name: name,
  105. type: type
  106. };
  107. if (type !== 'remove') {
  108. data.value = newValue;
  109. data.oldValue = oldValue;
  110. } else {
  111. data.oldValue = newValue;
  112. }
  113. result.push(data);
  114. }
  115. /**
  116. * @param {String} ns
  117. * @param {String} name
  118. * @param {String} type
  119. * @param {String} iterator
  120. * @param {String} placeholder
  121. */
  122. function setAll(ns, name, type, iterator, placeholder) {
  123. var key;
  124. if (arguments.length > 4) {
  125. type === 'add' ?
  126. addChange(ns, name, 'update', iterator, placeholder) :
  127. addChange(ns, name, 'update', placeholder, iterator);
  128. } else {
  129. addChange(ns, name, type, iterator);
  130. }
  131. if (!utils.isObject(iterator)) {
  132. return;
  133. }
  134. for (key in iterator) {
  135. if (hasOwn(iterator, key)) {
  136. setAll(getPath(ns, key), key, type, iterator[key]);
  137. }
  138. }
  139. }
  140. /*eslint-disable max-depth*/
  141. /**
  142. * @param {Object} old
  143. * @param {Object} current
  144. * @param {String} ns
  145. * @param {String} name
  146. */
  147. function compare(old, current, ns, name) {
  148. var key,
  149. oldIsObj = utils.isObject(old),
  150. newIsObj = utils.isObject(current);
  151. if (oldIsObj && newIsObj) {
  152. for (key in old) {
  153. if (hasOwn(old, key) && !hasOwn(current, key)) {
  154. setAll(getPath(ns, key), key, 'remove', old[key]);
  155. }
  156. }
  157. for (key in current) {
  158. if (hasOwn(current, key)) {
  159. hasOwn(old, key) ?
  160. compare(old[key], current[key], getPath(ns, key), key) :
  161. setAll(getPath(ns, key), key, 'add', current[key]);
  162. }
  163. }
  164. } else if (oldIsObj) {
  165. setAll(ns, name, 'remove', old, current);
  166. } else if (newIsObj) {
  167. setAll(ns, name, 'add', current, old);
  168. } else if (isDifferent(old, current)) {
  169. addChange(ns, name, 'update', current, old);
  170. }
  171. }
  172. /*eslint-enable max-depth*/
  173. return {
  174. /**
  175. *
  176. * @returns {Object}
  177. */
  178. compare: function () {
  179. var changes;
  180. compare.apply(null, arguments);
  181. changes = result.splice(0);
  182. return {
  183. containers: getContainers(changes),
  184. changes: changes,
  185. equal: !changes.length
  186. };
  187. },
  188. equalArrays: equalArrays
  189. };
  190. });