From a2fb73383bf3a172496f671201b6c1a07ee43fdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Wang?= Date: Sat, 14 May 2016 10:16:08 +0200 Subject: [PATCH] Initial commit --- .gitignore | 1 + LICENSE | 19 ++++++ Makefile | 27 ++++++++ README.md | 18 +++++ index.html | 197 +++++++++++++++++++++++++++++++++++++++++++++++++++++ mfenced.js | 118 ++++++++++++++++++++++++++++++++ 6 files changed, 380 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 index.html create mode 100644 mfenced.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..84e8ce4 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +mfenced-min.js diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..bcb3b9a --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2016 Igalia S.L. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b6f236d --- /dev/null +++ b/Makefile @@ -0,0 +1,27 @@ +# Copyright (c) 2016 Igalia S.L. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +CLOSURE_COMPILER=java -jar ~/node_modules/google-closure-compiler/compiler.jar + +mfenced-min.js: mfenced.js + $(CLOSURE_COMPILER) --compilation_level ADVANCED_OPTIMIZATIONS < $< > $@ + +clean: + rm -f mfenced-min.js diff --git a/README.md b/README.md new file mode 100644 index 0000000..f34d060 --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +# mfenced polyfill + +This repository contains a small polyfill for the MathML +[mfenced element](https://www.w3.org/TR/MathML/chapter3.html#presm.fenced). +In order to use it, just load the mfenced-min.js script: + + + + ... + + ... + + ... + + +At page load, all the mfenced elements will be converted into their equivalent +expanded form. A function window.expandMathMLFencedElements is also defined and +can be used to execute this conversion again later. diff --git a/index.html b/index.html new file mode 100644 index 0000000..7a41b16 --- /dev/null +++ b/index.html @@ -0,0 +1,197 @@ + + + + The mfenced element + + + + +

The mfenced element

+ +

This page contains some tests for the mfenced element. Click the following button to load a polyfill and convert them into an equivalent expanded form. +

+

+ +
    +
  1. empty: + + + + +
  2. + +
  3. 1 argument: + + + 0 + + +
  4. + +
  5. 2 arguments: + + + 0 + 1 + + +
  6. + +
  7. 3 arguments: + + + 0 + 1 + 2 + + +
  8. + +
  9. open: + + + 0 + 1 + + +
  10. + +
  11. close: + + + 0 + 1 + + +
  12. + +
  13. separators: + + + 0 + 1 + + +
  14. + +
  15. open, close, one separator: + + + 0 + 1 + + +
  16. + +
  17. several separators: + + + 0 + 1 + 2 + 3 + + +
  18. + +
  19. multiple characters in open/close attributes: + + + 0 + 1 + 2 + + +
  20. + +
  21. too few separators (last one is repeated): + + + 0 + 1 + 2 + 3 + 4 + 5 + + +
  22. + +
  23. too many separators (excess is ignored): + + + 0 + 1 + 2 + + +
  24. + +
  25. no separators (no <mo> separators should be output): + + + 0 + 1 + 2 + + +
  26. + +
  27. whitespace (they should be ignored): + + + 0 + 1 + 2 + 3 + + +
  28. + +
  29. surrogate pairs (each pair should be treated as one character): + + + 0 + 1 + 2 + 3 + + +
  30. + +
  31. Other attributes (they should be attached to the expanded mrow): + + + + 0 + 1 + 2 + + +
  32. + +
  33. Invalid rtl attribute (it should not be attached to the expanded mrow): + + + 0 + 1 + 2 + + +
  34. +
+ + + diff --git a/mfenced.js b/mfenced.js new file mode 100644 index 0000000..e657019 --- /dev/null +++ b/mfenced.js @@ -0,0 +1,118 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode:nil; c-basic-offset: 4 -*- */ +/* vim: set ts=4 et sw=4 tw=80: */ +/* + Copyright (c) 2016 Igalia S.L. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +(function () { + "use strict"; + + const namespaceURI = "http://www.w3.org/1998/Math/MathML"; + + function collapseWhiteSpace(text) { + // Collapse the whitespace as specified by the MathML specification. + // See https://www.w3.org/TR/MathML/chapter2.html#fund.collapse + return text.replace(/^[\s]+|[\s]+$/g, '').replace(/[\s]+/g, ' '); + } + + function newOperator(text, separator) { + // Create text or text. + let operator = document.createElementNS(namespaceURI, "mo"); + operator.appendChild(document.createTextNode(text)); + operator.setAttribute(separator ? "separator" : "fence", "true"); + return operator; + } + + function newMrow() { + // Create an empty . + return document.createElementNS(namespaceURI, "mrow"); + } + + function getSeparatorList(text) { + // Split the separators attribute into a list of characters. + // We ignore whitespace and handle surrogate pairs. + if (text === null) { + return [","]; + } + let separatorList = []; + for (let i = 0; i < text.length; i++) { + if (!/\s/g.test(text.charAt(i))) { + let c = text.charCodeAt(i); + if (c >= 0xD800 && c < 0xDC00 && i + 1 < text.length) { + separatorList.push(text.substr(i, 2)); + i++; + } else { + separatorList.push(text.charAt(i)); + } + } + } + return separatorList; + } + + function shouldCopyAttribute(attribute) { + // The and elements have the same attributes except + // that dir is only accepted on and open/close/separators are + // only accepted on . See https://www.w3.org/Math/RelaxNG/ + const excludedAttributes = ["dir", "open", "close", "separators"]; + return attribute.namespaceURI || !excludedAttributes.includes(attribute.localName); + } + + function expandFencedElement(mfenced) { + // Return an element representing the expanded . + // See https://www.w3.org/TR/MathML/chapter3.html#presm.fenced + let outerMrow = newMrow(); + outerMrow.appendChild(newOperator(collapseWhiteSpace(mfenced.getAttribute("open") || "("))); + if (mfenced.childElementCount === 1) { + outerMrow.appendChild(mfenced.firstElementChild.cloneNode(true)); + } else if (mfenced.childElementCount > 1) { + let separatorList = getSeparatorList(mfenced.getAttribute("separators")), + innerMrow = newMrow(), + child = mfenced.firstElementChild; + while (child) { + innerMrow.appendChild(child.cloneNode(true)); + child = child.nextElementSibling; + if (child && separatorList.length) { + innerMrow.appendChild(newOperator(separatorList.length > 1 ? separatorList.shift() : separatorList[0])); + } + } + outerMrow.appendChild(innerMrow); + } + outerMrow.appendChild(newOperator(collapseWhiteSpace(mfenced.getAttribute("close") || ")"))); + for (let i = 0; i < mfenced.attributes.length; i++) { + let attribute = mfenced.attributes[i]; + if (shouldCopyAttribute(attribute)) { + outerMrow.setAttributeNS(attribute.namespaceURI, attribute.localName, attribute.value); + } + } + return outerMrow; + } + + window["expandMathMLFencedElements"] = function () { + // Replace all the elements with their expanded equivalent. + let mfencedElements = document.body.getElementsByTagNameNS(namespaceURI, "mfenced"); + while (mfencedElements.length) { + let fenced = mfencedElements[0]; + fenced.parentNode.replaceChild(expandFencedElement(fenced), fenced); + } + }; + + window.addEventListener("load", window["expandMathMLFencedElements"]); +}());