restore.js 9.5 KB

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