shorthand.ml 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. (* CSS shorthand creation based on:
  2. * - http://www.cssshorthand.com/
  3. *)
  4. open Types
  5. open Util
  6. module SS = Set.Make(String)
  7. let pattern = Str.regexp ("^\\(background\\|border\\|font\\|list-style" ^
  8. "\\|outline\\|padding\\|margin\\)-\\(.*\\)$")
  9. let subprops = function
  10. | "background" -> ["color"; "image"; "repeat"; "attachment"; "position"]
  11. | "border" -> ["width"; "style"; "color"]
  12. | "font" -> ["style"; "variant"; "weight"; "size"; "family"]
  13. | "list-style" -> ["type"; "position"; "image"]
  14. | "outline" -> ["color"; "style"; "width"]
  15. | "margin"
  16. | "padding" -> ["top"; "right"; "bottom"; "left"]
  17. | _ -> failwith "not a shorthand property"
  18. let rec decls_mem name = function
  19. | [] -> false
  20. | (nm, _, false) :: _ when nm = name -> true
  21. | _ :: tl -> decls_mem name tl
  22. let rec decls_find name = function
  23. | [] -> raise Not_found
  24. | (nm, value, false) :: _ when nm = name -> value
  25. | _ :: tl -> decls_find name tl
  26. let order base decls =
  27. let rec filter = function
  28. | [] -> []
  29. (* `font-size` and `line-height` are slash-separated in `font` *)
  30. | "size" :: tl when base = "font" && decls_mem "line-height" decls ->
  31. let font_size = decls_find "font-size" decls in
  32. let line_height = decls_find "line-height" decls in
  33. Nary ("/", [font_size; line_height]) :: filter tl
  34. | name :: tl when decls_mem (base ^ "-" ^ name) decls ->
  35. decls_find (base ^ "-" ^ name) decls :: filter tl
  36. | _ :: tl -> filter tl
  37. in
  38. filter (subprops base)
  39. let shorten_box_dims = function
  40. | [top; right; bottom; left]
  41. when top = bottom && right = left && top = right -> [top]
  42. | [top; right; bottom; left] when top = bottom && right = left -> [top; right]
  43. | [top; right; bottom; left] when right = left -> [top; right; bottom]
  44. | dims -> dims
  45. let shorten decls = function
  46. (* `font-size` and `font-family` are required for `font` *)
  47. | "font" when decls_mem "font-size" decls && decls_mem "font-family" decls ->
  48. Some (Concat (order "font" decls))
  49. (* `border-style` is required for `border` *)
  50. | "border" when decls_mem "border-style" decls ->
  51. Some (Concat (order "border" decls))
  52. (* others require at least one property, which is the case when this function
  53. * is called *)
  54. | ("background" | "list-style" | "outline") as base ->
  55. Some (Concat (order base decls))
  56. (* margin and padding can only be shorthanded when all directions are known,
  57. * merging into even shorter shorthands is done by `shorten_box_dims` *)
  58. | ("margin" | "padding") as base when
  59. let has dir = decls_mem (base ^ "-" ^ dir) decls in
  60. has "top" && has "right" && has "bottom" && has "left" ->
  61. let get dir = decls_find (base ^ "-" ^ dir) decls in
  62. Some (Concat (shorten_box_dims [get "top"; get "right";
  63. get "bottom"; get "left"]))
  64. | _ -> None
  65. let make_shorthands decls =
  66. (* find shorthand names for which properties are present *)
  67. let rec find_props = function
  68. | [] -> SS.empty
  69. | (name, value, false) :: tl when Str.string_match pattern name 0 ->
  70. let base = Str.matched_group 1 name in
  71. let sub = Str.matched_group 2 name in
  72. if List.mem sub (subprops base)
  73. then SS.add base (find_props tl)
  74. else find_props tl
  75. | _ :: tl -> find_props tl
  76. in
  77. let try_shorthands = find_props decls in
  78. (* try to generate shorthands for the matched base properties *)
  79. let rec replace base tl =
  80. match shorten decls base with
  81. | None -> tl
  82. | Some short_value -> (base, short_value, false) :: tl
  83. in
  84. let shorthands = SS.fold replace try_shorthands [] in
  85. (* filter out the original, partial properties, and append the shorthands *)
  86. let keep_prop = function
  87. | (_, _, true) -> true
  88. | ("line-height", _, false) ->
  89. not (decls_mem "font" shorthands)
  90. | (name, _, false) ->
  91. not (Str.string_match pattern name 0) ||
  92. let base = Str.matched_group 1 name in
  93. let sub = Str.matched_group 2 name in
  94. not (List.mem sub (subprops base)) || not (decls_mem base shorthands)
  95. in
  96. List.filter keep_prop decls @ shorthands
  97. let transform = function
  98. | Statement (Ruleset (selectors, decls)) ->
  99. Statement (Ruleset (selectors, make_shorthands decls))
  100. | v -> v
  101. let compress = Util.transform_stylesheet transform