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