Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Page
Library
Module
Module type
Parameter
Class
Class type
Source
attributes.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
(* TyXML * http://www.ocsigen.org/tyxml * Copyright (C) 2016 Anton Bachin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exception; * either version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1301, USA. *) let parse loc (language, element_name) attributes = let (module Reflected) = Namespace.get language in (* For prefix ["prefix"] and attribute names ["prefix-foo"], evaluates to [Some "foo"], otherwise evaluates to [None]. Used to parse user-data attributes (prefixed by "data-") and ARIA attributes (prefixed by "aria-"). *) let parse_prefixed prefix name = let length = String.length prefix in let is_prefixed = try String.sub name 0 length = prefix with Invalid_argument _ -> false in if not is_prefixed then None else Some (String.sub name length (String.length name - length)) in (* Applied to each attribute. Accumulates individually labeled attributes, such as img/src, in "labeled," and attributes passed in ~a in "regular." *) let parse_attribute (labeled, regular) ((_, local_name), value) = (* Convert the markup name of the attribute to a TyXML name without regard to renamed attributes such as "a_input_max." Renaming will be accounted for later. *) let tyxml_name = Name_convention.attrib local_name in let test_labeled (e, a, _) = e = element_name && a = local_name in let test_blacklisted (a, _, _) = a = tyxml_name in let test_renamed (_, a, es) = a = local_name && List.mem element_name es in let unknown () = Common.error loc "Unknown attribute in %s element: %s" (Common.lang language) local_name in (* Check whether this attribute is individually labeled. Parse its argument and accumulate the attribute if so. *) match Common.find test_labeled Reflected.labeled_attributes with | Some (_, label, parser) -> let e = match parser language loc local_name value with | None -> Common.error loc "Internal error: labeled attribute %s without an argument" label | Some e -> e in (Common.Label.labelled label, e)::labeled, regular | None -> (* The attribute is not individually labeled, so it is passed in ~a. First, check if the default TyXML name of this attribute collides with the TyXML name of a renamed attribute. For example, if the language is HTML, and this attribute has markup name "input-max" (which is invalid), then its default TyXML name will be "a_input_max", which is a *valid* value in TyXML. We want to avoid mapping "input-max" to "a_input_max", because "input-max" is invalid, and because "a_input_max" maps to "max" instead. *) if List.exists test_blacklisted Reflected.renamed_attributes then unknown () else let parse_prefixed_attribute tag tyxml_name = let parser = try List.assoc tyxml_name Reflected.attribute_parsers with Not_found -> Common.error loc "Internal error: no parser for %s" tyxml_name in let identifier = Common.make ~loc language tyxml_name in let tag = Common.string loc tag in let e = match parser language loc local_name value with | Some e' -> [%expr [%e identifier] [%e tag] [%e e']] [@metaloc loc] | None -> Common.error loc "Internal error: no expression for %s" tyxml_name in labeled, e::regular in (* Check if this is a "data-foo" or "aria-foo" attribute. Parse the attribute value, and accumulate it in the list of attributes passed in ~a. *) match parse_prefixed "data-" local_name, parse_prefixed "aria-" local_name with | Some tag, _ -> parse_prefixed_attribute tag "a_user_data" | _, Some tag -> parse_prefixed_attribute tag "a_aria" | None, None -> let tyxml_name = match Common.find test_renamed Reflected.renamed_attributes with | Some (name, _, _) -> name | None -> tyxml_name in let parser = try List.assoc tyxml_name Reflected.attribute_parsers with Not_found -> unknown () in let identifier = Common.make ~loc language tyxml_name in let e = match parser language loc local_name value with | None -> identifier | Some e' -> [%expr [%e identifier] [%e e']] [@metaloc loc] in labeled, e::regular in let labeled, regular = List.fold_left parse_attribute ([], []) attributes in (* If there are any attributes to pass in ~a, assemble them into a parse tree for a list, and prefix that with the ~a label. *) if regular = [] then List.rev labeled else let regular = Common.Label.labelled "a", Common.list loc (List.rev regular) in List.rev (regular::labeled)