You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

303 lines
9.6 KiB

4 years ago
  1. var shallowClone = require('./clone').shallow;
  2. var Token = require('../../tokenizer/token');
  3. var Marker = require('../../tokenizer/marker');
  4. function isInheritOnly(values) {
  5. for (var i = 0, l = values.length; i < l; i++) {
  6. var value = values[i][1];
  7. if (value != 'inherit' && value != Marker.COMMA && value != Marker.FORWARD_SLASH)
  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 && descriptor.defaultValue.length == 1) {
  22. return component.value[0][1] == descriptor.defaultValue[0] && (component.value[1] ? component.value[1][1] == descriptor.defaultValue[0] : true);
  23. } else if (descriptor.doubleValues && descriptor.defaultValue.length != 1) {
  24. return component.value[0][1] == descriptor.defaultValue[0] && (component.value[1] ? component.value[1][1] : component.value[0][1]) == descriptor.defaultValue[1];
  25. } else {
  26. return component.value[0][1] == descriptor.defaultValue;
  27. }
  28. }
  29. for (var i = components.length - 1; i >= 0; i--) {
  30. var component = components[i];
  31. var isDefault = isDefaultValue(component);
  32. if (component.name == 'background-clip') {
  33. var originComponent = components[i - 1];
  34. var isOriginDefault = isDefaultValue(originComponent);
  35. needsOne = component.value[0][1] == originComponent.value[0][1];
  36. needsBoth = !needsOne && (
  37. (isOriginDefault && !isDefault) ||
  38. (!isOriginDefault && !isDefault) ||
  39. (!isOriginDefault && isDefault && component.value[0][1] != originComponent.value[0][1]));
  40. if (needsOne) {
  41. restoreValue(originComponent);
  42. } else if (needsBoth) {
  43. restoreValue(component);
  44. restoreValue(originComponent);
  45. }
  46. i--;
  47. } else if (component.name == 'background-size') {
  48. var positionComponent = components[i - 1];
  49. var isPositionDefault = isDefaultValue(positionComponent);
  50. needsOne = !isPositionDefault && isDefault;
  51. needsBoth = !needsOne &&
  52. (isPositionDefault && !isDefault || !isPositionDefault && !isDefault);
  53. if (needsOne) {
  54. restoreValue(positionComponent);
  55. } else if (needsBoth) {
  56. restoreValue(component);
  57. restored.unshift([Token.PROPERTY_VALUE, Marker.FORWARD_SLASH]);
  58. restoreValue(positionComponent);
  59. } else if (positionComponent.value.length == 1) {
  60. restoreValue(positionComponent);
  61. }
  62. i--;
  63. } else {
  64. if (isDefault || compactable[component.name].multiplexLastOnly && !lastInMultiplex)
  65. continue;
  66. restoreValue(component);
  67. }
  68. }
  69. if (restored.length === 0 && property.value.length == 1 && property.value[0][1] == '0')
  70. restored.push(property.value[0]);
  71. if (restored.length === 0)
  72. restored.push([Token.PROPERTY_VALUE, compactable[property.name].defaultValue]);
  73. if (isInheritOnly(restored))
  74. return [restored[0]];
  75. return restored;
  76. }
  77. function borderRadius(property, compactable) {
  78. if (property.multiplex) {
  79. var horizontal = shallowClone(property);
  80. var vertical = shallowClone(property);
  81. for (var i = 0; i < 4; i++) {
  82. var component = property.components[i];
  83. var horizontalComponent = shallowClone(property);
  84. horizontalComponent.value = [component.value[0]];
  85. horizontal.components.push(horizontalComponent);
  86. var verticalComponent = shallowClone(property);
  87. // FIXME: only shorthand compactor (see breakup#borderRadius) knows that border radius
  88. // longhands have two values, whereas tokenizer does not care about populating 2nd value
  89. // if it's missing, hence this fallback
  90. verticalComponent.value = [component.value[1] || component.value[0]];
  91. vertical.components.push(verticalComponent);
  92. }
  93. var horizontalValues = fourValues(horizontal, compactable);
  94. var verticalValues = fourValues(vertical, compactable);
  95. if (horizontalValues.length == verticalValues.length &&
  96. horizontalValues[0][1] == verticalValues[0][1] &&
  97. (horizontalValues.length > 1 ? horizontalValues[1][1] == verticalValues[1][1] : true) &&
  98. (horizontalValues.length > 2 ? horizontalValues[2][1] == verticalValues[2][1] : true) &&
  99. (horizontalValues.length > 3 ? horizontalValues[3][1] == verticalValues[3][1] : true)) {
  100. return horizontalValues;
  101. } else {
  102. return horizontalValues.concat([[Token.PROPERTY_VALUE, Marker.FORWARD_SLASH]]).concat(verticalValues);
  103. }
  104. } else {
  105. return fourValues(property, compactable);
  106. }
  107. }
  108. function font(property, compactable) {
  109. var components = property.components;
  110. var restored = [];
  111. var component;
  112. var componentIndex = 0;
  113. var fontFamilyIndex = 0;
  114. if (property.value[0][1].indexOf(Marker.INTERNAL) === 0) {
  115. property.value[0][1] = property.value[0][1].substring(Marker.INTERNAL.length);
  116. return property.value;
  117. }
  118. // first four components are optional
  119. while (componentIndex < 4) {
  120. component = components[componentIndex];
  121. if (component.value[0][1] != compactable[component.name].defaultValue) {
  122. Array.prototype.push.apply(restored, component.value);
  123. }
  124. componentIndex++;
  125. }
  126. // then comes font-size
  127. Array.prototype.push.apply(restored, components[componentIndex].value);
  128. componentIndex++;
  129. // then may come line-height
  130. if (components[componentIndex].value[0][1] != compactable[components[componentIndex].name].defaultValue) {
  131. Array.prototype.push.apply(restored, [[Token.PROPERTY_VALUE, Marker.FORWARD_SLASH]]);
  132. Array.prototype.push.apply(restored, components[componentIndex].value);
  133. }
  134. componentIndex++;
  135. // then comes font-family
  136. while (components[componentIndex].value[fontFamilyIndex]) {
  137. restored.push(components[componentIndex].value[fontFamilyIndex]);
  138. if (components[componentIndex].value[fontFamilyIndex + 1]) {
  139. restored.push([Token.PROPERTY_VALUE, Marker.COMMA]);
  140. }
  141. fontFamilyIndex++;
  142. }
  143. if (isInheritOnly(restored)) {
  144. return [restored[0]];
  145. }
  146. return restored;
  147. }
  148. function fourValues(property) {
  149. var components = property.components;
  150. var value1 = components[0].value[0];
  151. var value2 = components[1].value[0];
  152. var value3 = components[2].value[0];
  153. var value4 = components[3].value[0];
  154. if (value1[1] == value2[1] && value1[1] == value3[1] && value1[1] == value4[1]) {
  155. return [value1];
  156. } else if (value1[1] == value3[1] && value2[1] == value4[1]) {
  157. return [value1, value2];
  158. } else if (value2[1] == value4[1]) {
  159. return [value1, value2, value3];
  160. } else {
  161. return [value1, value2, value3, value4];
  162. }
  163. }
  164. function multiplex(restoreWith) {
  165. return function (property, compactable) {
  166. if (!property.multiplex)
  167. return restoreWith(property, compactable, true);
  168. var multiplexSize = 0;
  169. var restored = [];
  170. var componentMultiplexSoFar = {};
  171. var i, l;
  172. // At this point we don't know what's the multiplex size, e.g. how many background layers are there
  173. for (i = 0, l = property.components[0].value.length; i < l; i++) {
  174. if (property.components[0].value[i][1] == Marker.COMMA)
  175. multiplexSize++;
  176. }
  177. for (i = 0; i <= multiplexSize; i++) {
  178. var _property = shallowClone(property);
  179. // We split multiplex into parts and restore them one by one
  180. for (var j = 0, m = property.components.length; j < m; j++) {
  181. var componentToClone = property.components[j];
  182. var _component = shallowClone(componentToClone);
  183. _property.components.push(_component);
  184. // The trick is some properties has more than one value, so we iterate over values looking for
  185. // a multiplex separator - a comma
  186. for (var k = componentMultiplexSoFar[_component.name] || 0, n = componentToClone.value.length; k < n; k++) {
  187. if (componentToClone.value[k][1] == Marker.COMMA) {
  188. componentMultiplexSoFar[_component.name] = k + 1;
  189. break;
  190. }
  191. _component.value.push(componentToClone.value[k]);
  192. }
  193. }
  194. // No we can restore shorthand value
  195. var lastInMultiplex = i == multiplexSize;
  196. var _restored = restoreWith(_property, compactable, lastInMultiplex);
  197. Array.prototype.push.apply(restored, _restored);
  198. if (i < multiplexSize)
  199. restored.push([Token.PROPERTY_VALUE, Marker.COMMA]);
  200. }
  201. return restored;
  202. };
  203. }
  204. function withoutDefaults(property, compactable) {
  205. var components = property.components;
  206. var restored = [];
  207. for (var i = components.length - 1; i >= 0; i--) {
  208. var component = components[i];
  209. var descriptor = compactable[component.name];
  210. if (component.value[0][1] != descriptor.defaultValue || ('keepUnlessDefault' in descriptor) && !isDefault(components, compactable, descriptor.keepUnlessDefault)) {
  211. restored.unshift(component.value[0]);
  212. }
  213. }
  214. if (restored.length === 0)
  215. restored.push([Token.PROPERTY_VALUE, compactable[property.name].defaultValue]);
  216. if (isInheritOnly(restored))
  217. return [restored[0]];
  218. return restored;
  219. }
  220. function isDefault(components, compactable, propertyName) {
  221. var component;
  222. var i, l;
  223. for (i = 0, l = components.length; i < l; i++) {
  224. component = components[i];
  225. if (component.name == propertyName && component.value[0][1] == compactable[propertyName].defaultValue) {
  226. return true;
  227. }
  228. }
  229. return false;
  230. }
  231. module.exports = {
  232. background: background,
  233. borderRadius: borderRadius,
  234. font: font,
  235. fourValues: fourValues,
  236. multiplex: multiplex,
  237. withoutDefaults: withoutDefaults
  238. };