shorthand.ml 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  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" | "padding" -> ["top"; "right"; "bottom"; "left"]
  16. | _ -> failwith "not a shorthand property"
  17. let rec decls_mem name = function
  18. | [] -> false
  19. | (nm, _, false) :: _ when nm = name -> true
  20. | _ :: tl -> decls_mem name tl
  21. let rec prop_value name = function
  22. | [] -> raise Not_found
  23. | (nm, value, false) :: _ when nm = name -> value
  24. | _ :: tl -> prop_value name tl
  25. let order base decls =
  26. let rec filter = function
  27. | [] -> []
  28. | "size" :: tl when base = "font" && decls_mem "line-height" decls ->
  29. let font_size = prop_value "font-size" decls in
  30. let line_height = prop_value "line-height" decls in
  31. Nary ("/", [font_size; line_height]) :: filter tl
  32. | name :: tl when decls_mem (base ^ "-" ^ name) decls ->
  33. prop_value (base ^ "-" ^ name) decls :: filter tl
  34. | _ :: tl -> filter tl
  35. in
  36. filter (subprops base)
  37. let rec shorten decls = function
  38. | "font" when not (decls_mem "font-size" decls) ->
  39. shorten (("font-size", Ident "medium", false) :: decls) "font"
  40. | "font" when decls_mem "font-family" decls ->
  41. Some (Concat (order "font" decls))
  42. | "border" when decls_mem "border-style" decls ->
  43. Some (Concat (order "border" decls))
  44. | ("background" | "list-style" | "outline") as base ->
  45. Some (Concat (order base decls))
  46. | ("margin" | "padding") as base when
  47. let has dir = decls_mem (base ^ "-" ^ dir) decls in
  48. has "top" && has "right" && has "bottom" && has "left" ->
  49. let get dir = prop_value (base ^ "-" ^ dir) decls in
  50. Some (Concat [get "top"; get "right"; get "bottom"; get "left"])
  51. | _ -> None
  52. let make_shorthands decls =
  53. (* find basenames for which properties are present *)
  54. let rec find_props = function
  55. | [] -> SS.empty
  56. | (name, value, false) :: tl when Str.string_match pattern name 0 ->
  57. let base = Str.matched_group 1 name in
  58. let sub = Str.matched_group 2 name in
  59. if List.mem sub (subprops base)
  60. then SS.add base (find_props tl)
  61. else find_props tl
  62. | _ :: tl -> find_props tl
  63. in
  64. let try_shorthands = find_props decls in
  65. (* try to generate shorthands for the matched base properties *)
  66. let rec replace base tl =
  67. match shorten decls base with
  68. | None -> tl
  69. | Some short_value -> (base, short_value, false) :: tl
  70. in
  71. let shorthands = SS.fold replace try_shorthands [] in
  72. (* filter out the original, partial properties, and prepend the shorthands *)
  73. let keep_prop = function
  74. | (_, _, true) -> true
  75. | ("line-height", _, false) ->
  76. not (decls_mem "font" shorthands)
  77. | (name, _, false) ->
  78. not (Str.string_match pattern name 0) ||
  79. let base = Str.matched_group 1 name in
  80. let sub = Str.matched_group 2 name in
  81. not (List.mem sub (subprops base)) || not (decls_mem base shorthands)
  82. in
  83. shorthands @ List.filter keep_prop decls
  84. let transform = function
  85. | Statement (Ruleset (selectors, decls)) ->
  86. Statement (Ruleset (selectors, make_shorthands decls))
  87. | v -> v
  88. let compress = Util.transform_stylesheet transform