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.

445 lines
12 KiB

4 years ago
  1. var everyValuesPair = require('./every-values-pair');
  2. var hasInherit = require('./has-inherit');
  3. var populateComponents = require('./populate-components');
  4. var compactable = require('../compactable');
  5. var deepClone = require('../clone').deep;
  6. var restoreWithComponents = require('../restore-with-components');
  7. var restoreFromOptimizing = require('../../restore-from-optimizing');
  8. var wrapSingle = require('../../wrap-for-optimizing').single;
  9. var serializeBody = require('../../../writer/one-time').body;
  10. var Token = require('../../../tokenizer/token');
  11. function mergeIntoShorthands(properties, validator) {
  12. var candidates = {};
  13. var descriptor;
  14. var componentOf;
  15. var property;
  16. var i, l;
  17. var j, m;
  18. // there is no shorthand property made up of less than 3 longhands
  19. if (properties.length < 3) {
  20. return;
  21. }
  22. for (i = 0, l = properties.length; i < l; i++) {
  23. property = properties[i];
  24. descriptor = compactable[property.name];
  25. if (property.unused) {
  26. continue;
  27. }
  28. if (property.hack) {
  29. continue;
  30. }
  31. if (property.block) {
  32. continue;
  33. }
  34. invalidateOrCompact(properties, i, candidates, validator);
  35. if (descriptor && descriptor.componentOf) {
  36. for (j = 0, m = descriptor.componentOf.length; j < m; j++) {
  37. componentOf = descriptor.componentOf[j];
  38. candidates[componentOf] = candidates[componentOf] || {};
  39. candidates[componentOf][property.name] = property;
  40. }
  41. }
  42. }
  43. invalidateOrCompact(properties, i, candidates, validator);
  44. }
  45. function invalidateOrCompact(properties, position, candidates, validator) {
  46. var invalidatedBy = properties[position];
  47. var shorthandName;
  48. var shorthandDescriptor;
  49. var candidateComponents;
  50. for (shorthandName in candidates) {
  51. if (undefined !== invalidatedBy && shorthandName == invalidatedBy.name) {
  52. continue;
  53. }
  54. shorthandDescriptor = compactable[shorthandName];
  55. candidateComponents = candidates[shorthandName];
  56. if (invalidatedBy && invalidates(candidates, shorthandName, invalidatedBy)) {
  57. delete candidates[shorthandName];
  58. continue;
  59. }
  60. if (shorthandDescriptor.components.length > Object.keys(candidateComponents).length) {
  61. continue;
  62. }
  63. if (mixedImportance(candidateComponents)) {
  64. continue;
  65. }
  66. if (!overridable(candidateComponents, shorthandName, validator)) {
  67. continue;
  68. }
  69. if (!mergeable(candidateComponents)) {
  70. continue;
  71. }
  72. if (mixedInherit(candidateComponents)) {
  73. replaceWithInheritBestFit(properties, candidateComponents, shorthandName, validator);
  74. } else {
  75. replaceWithShorthand(properties, candidateComponents, shorthandName, validator);
  76. }
  77. }
  78. }
  79. function invalidates(candidates, shorthandName, invalidatedBy) {
  80. var shorthandDescriptor = compactable[shorthandName];
  81. var invalidatedByDescriptor = compactable[invalidatedBy.name];
  82. var componentName;
  83. if ('overridesShorthands' in shorthandDescriptor && shorthandDescriptor.overridesShorthands.indexOf(invalidatedBy.name) > -1) {
  84. return true;
  85. }
  86. if (invalidatedByDescriptor && 'componentOf' in invalidatedByDescriptor) {
  87. for (componentName in candidates[shorthandName]) {
  88. if (invalidatedByDescriptor.componentOf.indexOf(componentName) > -1) {
  89. return true;
  90. }
  91. }
  92. }
  93. return false;
  94. }
  95. function mixedImportance(components) {
  96. var important;
  97. var componentName;
  98. for (componentName in components) {
  99. if (undefined !== important && components[componentName].important != important) {
  100. return true;
  101. }
  102. important = components[componentName].important;
  103. }
  104. return false;
  105. }
  106. function overridable(components, shorthandName, validator) {
  107. var descriptor = compactable[shorthandName];
  108. var newValuePlaceholder = [
  109. Token.PROPERTY,
  110. [Token.PROPERTY_NAME, shorthandName],
  111. [Token.PROPERTY_VALUE, descriptor.defaultValue]
  112. ];
  113. var newProperty = wrapSingle(newValuePlaceholder);
  114. var component;
  115. var mayOverride;
  116. var i, l;
  117. populateComponents([newProperty], validator, []);
  118. for (i = 0, l = descriptor.components.length; i < l; i++) {
  119. component = components[descriptor.components[i]];
  120. mayOverride = compactable[component.name].canOverride;
  121. if (!everyValuesPair(mayOverride.bind(null, validator), newProperty.components[i], component)) {
  122. return false;
  123. }
  124. }
  125. return true;
  126. }
  127. function mergeable(components) {
  128. var lastCount = null;
  129. var currentCount;
  130. var componentName;
  131. var component;
  132. var descriptor;
  133. var values;
  134. for (componentName in components) {
  135. component = components[componentName];
  136. descriptor = compactable[componentName];
  137. if (!('restore' in descriptor)) {
  138. continue;
  139. }
  140. restoreFromOptimizing([component.all[component.position]], restoreWithComponents);
  141. values = descriptor.restore(component, compactable);
  142. currentCount = values.length;
  143. if (lastCount !== null && currentCount !== lastCount) {
  144. return false;
  145. }
  146. lastCount = currentCount;
  147. }
  148. return true;
  149. }
  150. function mixedInherit(components) {
  151. var componentName;
  152. var lastValue = null;
  153. var currentValue;
  154. for (componentName in components) {
  155. currentValue = hasInherit(components[componentName]);
  156. if (lastValue !== null && lastValue !== currentValue) {
  157. return true;
  158. }
  159. lastValue = currentValue;
  160. }
  161. return false;
  162. }
  163. function replaceWithInheritBestFit(properties, candidateComponents, shorthandName, validator) {
  164. var viaLonghands = buildSequenceWithInheritLonghands(candidateComponents, shorthandName, validator);
  165. var viaShorthand = buildSequenceWithInheritShorthand(candidateComponents, shorthandName, validator);
  166. var longhandTokensSequence = viaLonghands[0];
  167. var shorthandTokensSequence = viaShorthand[0];
  168. var isLonghandsShorter = serializeBody(longhandTokensSequence).length < serializeBody(shorthandTokensSequence).length;
  169. var newTokensSequence = isLonghandsShorter ? longhandTokensSequence : shorthandTokensSequence;
  170. var newProperty = isLonghandsShorter ? viaLonghands[1] : viaShorthand[1];
  171. var newComponents = isLonghandsShorter ? viaLonghands[2] : viaShorthand[2];
  172. var all = candidateComponents[Object.keys(candidateComponents)[0]].all;
  173. var componentName;
  174. var oldComponent;
  175. var newComponent;
  176. var newToken;
  177. newProperty.position = all.length;
  178. newProperty.shorthand = true;
  179. newProperty.dirty = true;
  180. newProperty.all = all;
  181. newProperty.all.push(newTokensSequence[0]);
  182. properties.push(newProperty);
  183. for (componentName in candidateComponents) {
  184. oldComponent = candidateComponents[componentName];
  185. oldComponent.unused = true;
  186. if (oldComponent.name in newComponents) {
  187. newComponent = newComponents[oldComponent.name];
  188. newToken = findTokenIn(newTokensSequence, componentName);
  189. newComponent.position = all.length;
  190. newComponent.all = all;
  191. newComponent.all.push(newToken);
  192. properties.push(newComponent);
  193. }
  194. }
  195. }
  196. function buildSequenceWithInheritLonghands(components, shorthandName, validator) {
  197. var tokensSequence = [];
  198. var inheritComponents = {};
  199. var nonInheritComponents = {};
  200. var descriptor = compactable[shorthandName];
  201. var shorthandToken = [
  202. Token.PROPERTY,
  203. [Token.PROPERTY_NAME, shorthandName],
  204. [Token.PROPERTY_VALUE, descriptor.defaultValue]
  205. ];
  206. var newProperty = wrapSingle(shorthandToken);
  207. var component;
  208. var longhandToken;
  209. var newComponent;
  210. var nameMetadata;
  211. var i, l;
  212. populateComponents([newProperty], validator, []);
  213. for (i = 0, l = descriptor.components.length; i < l; i++) {
  214. component = components[descriptor.components[i]];
  215. if (hasInherit(component)) {
  216. longhandToken = component.all[component.position].slice(0, 2);
  217. Array.prototype.push.apply(longhandToken, component.value);
  218. tokensSequence.push(longhandToken);
  219. newComponent = deepClone(component);
  220. newComponent.value = inferComponentValue(components, newComponent.name);
  221. newProperty.components[i] = newComponent;
  222. inheritComponents[component.name] = deepClone(component);
  223. } else {
  224. newComponent = deepClone(component);
  225. newComponent.all = component.all;
  226. newProperty.components[i] = newComponent;
  227. nonInheritComponents[component.name] = component;
  228. }
  229. }
  230. nameMetadata = joinMetadata(nonInheritComponents, 1);
  231. shorthandToken[1].push(nameMetadata);
  232. restoreFromOptimizing([newProperty], restoreWithComponents);
  233. shorthandToken = shorthandToken.slice(0, 2);
  234. Array.prototype.push.apply(shorthandToken, newProperty.value);
  235. tokensSequence.unshift(shorthandToken);
  236. return [tokensSequence, newProperty, inheritComponents];
  237. }
  238. function inferComponentValue(components, propertyName) {
  239. var descriptor = compactable[propertyName];
  240. if ('oppositeTo' in descriptor) {
  241. return components[descriptor.oppositeTo].value;
  242. } else {
  243. return [[Token.PROPERTY_VALUE, descriptor.defaultValue]];
  244. }
  245. }
  246. function joinMetadata(components, at) {
  247. var metadata = [];
  248. var component;
  249. var originalValue;
  250. var componentMetadata;
  251. var componentName;
  252. for (componentName in components) {
  253. component = components[componentName];
  254. originalValue = component.all[component.position];
  255. componentMetadata = originalValue[at][originalValue[at].length - 1];
  256. Array.prototype.push.apply(metadata, componentMetadata);
  257. }
  258. return metadata.sort(metadataSorter);
  259. }
  260. function metadataSorter(metadata1, metadata2) {
  261. var line1 = metadata1[0];
  262. var line2 = metadata2[0];
  263. var column1 = metadata1[1];
  264. var column2 = metadata2[1];
  265. if (line1 < line2) {
  266. return -1;
  267. } else if (line1 === line2) {
  268. return column1 < column2 ? -1 : 1;
  269. } else {
  270. return 1;
  271. }
  272. }
  273. function buildSequenceWithInheritShorthand(components, shorthandName, validator) {
  274. var tokensSequence = [];
  275. var inheritComponents = {};
  276. var nonInheritComponents = {};
  277. var descriptor = compactable[shorthandName];
  278. var shorthandToken = [
  279. Token.PROPERTY,
  280. [Token.PROPERTY_NAME, shorthandName],
  281. [Token.PROPERTY_VALUE, 'inherit']
  282. ];
  283. var newProperty = wrapSingle(shorthandToken);
  284. var component;
  285. var longhandToken;
  286. var nameMetadata;
  287. var valueMetadata;
  288. var i, l;
  289. populateComponents([newProperty], validator, []);
  290. for (i = 0, l = descriptor.components.length; i < l; i++) {
  291. component = components[descriptor.components[i]];
  292. if (hasInherit(component)) {
  293. inheritComponents[component.name] = component;
  294. } else {
  295. longhandToken = component.all[component.position].slice(0, 2);
  296. Array.prototype.push.apply(longhandToken, component.value);
  297. tokensSequence.push(longhandToken);
  298. nonInheritComponents[component.name] = deepClone(component);
  299. }
  300. }
  301. nameMetadata = joinMetadata(inheritComponents, 1);
  302. shorthandToken[1].push(nameMetadata);
  303. valueMetadata = joinMetadata(inheritComponents, 2);
  304. shorthandToken[2].push(valueMetadata);
  305. tokensSequence.unshift(shorthandToken);
  306. return [tokensSequence, newProperty, nonInheritComponents];
  307. }
  308. function findTokenIn(tokens, componentName) {
  309. var i, l;
  310. for (i = 0, l = tokens.length; i < l; i++) {
  311. if (tokens[i][1][1] == componentName) {
  312. return tokens[i];
  313. }
  314. }
  315. }
  316. function replaceWithShorthand(properties, candidateComponents, shorthandName, validator) {
  317. var descriptor = compactable[shorthandName];
  318. var nameMetadata;
  319. var valueMetadata;
  320. var newValuePlaceholder = [
  321. Token.PROPERTY,
  322. [Token.PROPERTY_NAME, shorthandName],
  323. [Token.PROPERTY_VALUE, descriptor.defaultValue]
  324. ];
  325. var all;
  326. var newProperty = wrapSingle(newValuePlaceholder);
  327. newProperty.shorthand = true;
  328. newProperty.dirty = true;
  329. populateComponents([newProperty], validator, []);
  330. for (var i = 0, l = descriptor.components.length; i < l; i++) {
  331. var component = candidateComponents[descriptor.components[i]];
  332. newProperty.components[i] = deepClone(component);
  333. newProperty.important = component.important;
  334. all = component.all;
  335. }
  336. for (var componentName in candidateComponents) {
  337. candidateComponents[componentName].unused = true;
  338. }
  339. nameMetadata = joinMetadata(candidateComponents, 1);
  340. newValuePlaceholder[1].push(nameMetadata);
  341. valueMetadata = joinMetadata(candidateComponents, 2);
  342. newValuePlaceholder[2].push(valueMetadata);
  343. newProperty.position = all.length;
  344. newProperty.all = all;
  345. newProperty.all.push(newValuePlaceholder);
  346. properties.push(newProperty);
  347. }
  348. module.exports = mergeIntoShorthands;