restore.js 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. var shallowClone = require('./clone').shallow;
  2. var MULTIPLEX_SEPARATOR = ',';
  3. var SIZE_POSITION_SEPARATOR = '/';
  4. function isInheritOnly(values) {
  5. for (var i = 0, l = values.length; i < l; i++) {
  6. var value = values[i][0];
  7. if (value != 'inherit' && value != MULTIPLEX_SEPARATOR && value != SIZE_POSITION_SEPARATOR)
  8. return false;
  9. }
  10. return true;
  11. }
  12. function background(property, compactable, lastInMultiplex) {
  13. var components = property.components;
  14. var restored = [];
  15. var needsOne, needsBoth;
  16. function restoreValue(component) {
  17. Array.prototype.unshift.apply(restored, component.value);
  18. }
  19. function isDefaultValue(component) {
  20. var descriptor = compactable[component.name];
  21. if (descriptor.doubleValues) {
  22. if (descriptor.defaultValue.length == 1)
  23. return component.value[0][0] == descriptor.defaultValue[0] && (component.value[1] ? component.value[1][0] == descriptor.defaultValue[0] : true);
  24. else
  25. return component.value[0][0] == descriptor.defaultValue[0] && (component.value[1] ? component.value[1][0] : component.value[0][0]) == descriptor.defaultValue[1];
  26. } else {
  27. return component.value[0][0] == descriptor.defaultValue;
  28. }
  29. }
  30. for (var i = components.length - 1; i >= 0; i--) {
  31. var component = components[i];
  32. var isDefault = isDefaultValue(component);
  33. if (component.name == 'background-clip') {
  34. var originComponent = components[i - 1];
  35. var isOriginDefault = isDefaultValue(originComponent);
  36. needsOne = component.value[0][0] == originComponent.value[0][0];
  37. needsBoth = !needsOne && (
  38. (isOriginDefault && !isDefault) ||
  39. (!isOriginDefault && !isDefault) ||
  40. (!isOriginDefault && isDefault && component.value[0][0] != originComponent.value[0][0]));
  41. if (needsOne) {
  42. restoreValue(originComponent);
  43. } else if (needsBoth) {
  44. restoreValue(component);
  45. restoreValue(originComponent);
  46. }
  47. i--;
  48. } else if (component.name == 'background-size') {
  49. var positionComponent = components[i - 1];
  50. var isPositionDefault = isDefaultValue(positionComponent);
  51. needsOne = !isPositionDefault && isDefault;
  52. needsBoth = !needsOne &&
  53. (isPositionDefault && !isDefault || !isPositionDefault && !isDefault);
  54. if (needsOne) {
  55. restoreValue(positionComponent);
  56. } else if (needsBoth) {
  57. restoreValue(component);
  58. restored.unshift([SIZE_POSITION_SEPARATOR]);
  59. restoreValue(positionComponent);
  60. } else if (positionComponent.value.length == 1) {
  61. restoreValue(positionComponent);
  62. }
  63. i--;
  64. } else {
  65. if (isDefault || compactable[component.name].multiplexLastOnly && !lastInMultiplex)
  66. continue;
  67. restoreValue(component);
  68. }
  69. }
  70. if (restored.length === 0 && property.value.length == 1 && property.value[0][0] == '0')
  71. restored.push(property.value[0]);
  72. if (restored.length === 0)
  73. restored.push([compactable[property.name].defaultValue]);
  74. if (isInheritOnly(restored))
  75. return [restored[0]];
  76. return restored;
  77. }
  78. function borderRadius(property, compactable) {
  79. if (property.multiplex) {
  80. var horizontal = shallowClone(property);
  81. var vertical = shallowClone(property);
  82. for (var i = 0; i < 4; i++) {
  83. var component = property.components[i];
  84. var horizontalComponent = shallowClone(property);
  85. horizontalComponent.value = [component.value[0]];
  86. horizontal.components.push(horizontalComponent);
  87. var verticalComponent = shallowClone(property);
  88. // FIXME: only shorthand compactor (see breakup#borderRadius) knows that border radius
  89. // longhands have two values, whereas tokenizer does not care about populating 2nd value
  90. // if it's missing, hence this fallback
  91. verticalComponent.value = [component.value[1] || component.value[0]];
  92. vertical.components.push(verticalComponent);
  93. }
  94. var horizontalValues = fourValues(horizontal, compactable);
  95. var verticalValues = fourValues(vertical, compactable);
  96. if (horizontalValues.length == verticalValues.length &&
  97. horizontalValues[0][0] == verticalValues[0][0] &&
  98. (horizontalValues.length > 1 ? horizontalValues[1][0] == verticalValues[1][0] : true) &&
  99. (horizontalValues.length > 2 ? horizontalValues[2][0] == verticalValues[2][0] : true) &&
  100. (horizontalValues.length > 3 ? horizontalValues[3][0] == verticalValues[3][0] : true)) {
  101. return horizontalValues;
  102. } else {
  103. return horizontalValues.concat([['/']]).concat(verticalValues);
  104. }
  105. } else {
  106. return fourValues(property, compactable);
  107. }
  108. }
  109. function fourValues(property) {
  110. var components = property.components;
  111. var value1 = components[0].value[0];
  112. var value2 = components[1].value[0];
  113. var value3 = components[2].value[0];
  114. var value4 = components[3].value[0];
  115. if (value1[0] == value2[0] && value1[0] == value3[0] && value1[0] == value4[0]) {
  116. return [value1];
  117. } else if (value1[0] == value3[0] && value2[0] == value4[0]) {
  118. return [value1, value2];
  119. } else if (value2[0] == value4[0]) {
  120. return [value1, value2, value3];
  121. } else {
  122. return [value1, value2, value3, value4];
  123. }
  124. }
  125. function multiplex(restoreWith) {
  126. return function (property, compactable) {
  127. if (!property.multiplex)
  128. return restoreWith(property, compactable, true);
  129. var multiplexSize = 0;
  130. var restored = [];
  131. var componentMultiplexSoFar = {};
  132. var i, l;
  133. // At this point we don't know what's the multiplex size, e.g. how many background layers are there
  134. for (i = 0, l = property.components[0].value.length; i < l; i++) {
  135. if (property.components[0].value[i][0] == MULTIPLEX_SEPARATOR)
  136. multiplexSize++;
  137. }
  138. for (i = 0; i <= multiplexSize; i++) {
  139. var _property = shallowClone(property);
  140. // We split multiplex into parts and restore them one by one
  141. for (var j = 0, m = property.components.length; j < m; j++) {
  142. var componentToClone = property.components[j];
  143. var _component = shallowClone(componentToClone);
  144. _property.components.push(_component);
  145. // The trick is some properties has more than one value, so we iterate over values looking for
  146. // a multiplex separator - a comma
  147. for (var k = componentMultiplexSoFar[_component.name] || 0, n = componentToClone.value.length; k < n; k++) {
  148. if (componentToClone.value[k][0] == MULTIPLEX_SEPARATOR) {
  149. componentMultiplexSoFar[_component.name] = k + 1;
  150. break;
  151. }
  152. _component.value.push(componentToClone.value[k]);
  153. }
  154. }
  155. // No we can restore shorthand value
  156. var lastInMultiplex = i == multiplexSize;
  157. var _restored = restoreWith(_property, compactable, lastInMultiplex);
  158. Array.prototype.push.apply(restored, _restored);
  159. if (i < multiplexSize)
  160. restored.push([',']);
  161. }
  162. return restored;
  163. };
  164. }
  165. function withoutDefaults(property, compactable) {
  166. var components = property.components;
  167. var restored = [];
  168. for (var i = components.length - 1; i >= 0; i--) {
  169. var component = components[i];
  170. var descriptor = compactable[component.name];
  171. if (component.value[0][0] != descriptor.defaultValue)
  172. restored.unshift(component.value[0]);
  173. }
  174. if (restored.length === 0)
  175. restored.push([compactable[property.name].defaultValue]);
  176. if (isInheritOnly(restored))
  177. return [restored[0]];
  178. return restored;
  179. }
  180. module.exports = {
  181. background: background,
  182. borderRadius: borderRadius,
  183. fourValues: fourValues,
  184. multiplex: multiplex,
  185. withoutDefaults: withoutDefaults
  186. };