Skip to content
This repository has been archived by the owner on Jun 7, 2024. It is now read-only.

Commit

Permalink
Merge branch 'develop' into gh-pages
Browse files Browse the repository at this point in the history
* develop:
  v18.3.0
  feat(core/list-sorter): teach ReSpec to sort OL + UL
  feat: teach ReSpec to sort definition lists
  chore(.travis): adhere to Node LTS
  • Loading branch information
marcoscaceres committed Nov 17, 2017
2 parents f255741 + 77753d5 commit 7f3b6a8
Show file tree
Hide file tree
Showing 9 changed files with 275 additions and 30 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ js/core/l10n.js
js/core/link-to-dfn.js
js/core/linter.js
js/core/LinterRule.js
js/core/list-sorter.js
js/core/location-hash.js
js/core/markdown.js
js/core/override-configuration.js
Expand Down
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
language: node_js
dist: trusty
node_js:
- v8
- lts/*

before_install:
# see https://docs.travis-ci.com/user/gui-and-headless-browsers/#Using-xvfb-to-Run-Tests-That-Require-a-GUI
Expand Down
6 changes: 4 additions & 2 deletions builds/respec-w3c-common.build.js.map

Large diffs are not rendered by default.

52 changes: 26 additions & 26 deletions builds/respec-w3c-common.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions js/profile-w3c-common.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ define(
"core/highlight",
"core/webidl-clipboard",
"core/data-tests",
"core/list-sorter",
/*Linter must be the last thing to run*/
"core/linter",
],
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "respec",
"version": "18.2.4",
"version": "18.3.0",
"license": "W3C",
"description": "A technical specification pre-processor.",
"engines": {
Expand Down
98 changes: 98 additions & 0 deletions src/core/list-sorter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { pub } from "core/pubsubhub";
export const name = "core/list-sorter";

function makeSorter(direction) {
return ({ textContent: a }, { textContent: b }) => {
return direction === "ascending" ? a.localeCompare(b) : b.localeCompare(a);
};
}
/**
* Shallow sort list items in OL, and UL elements.
*
* @param {HTMLUListElement} elem
* @returns {DocumentFragment}
*/
export function sortListItems(elem, dir) {
const elements = getDirectDescendents(elem, "li");
const sortedElements = elements.sort(makeSorter(dir)).reduce((frag, elem) => {
frag.appendChild(elem);
return frag;
}, document.createDocumentFragment());
return sortedElements;
}

function getDirectDescendents(elem, wantedDescendentName) {
let elements;
try {
elements = elem.querySelectorAll(`:scope > ${wantedDescendentName}`);
} catch (err) {
let tempId = "";
// We give a temporary id, to overcome lack of ":scope" support in Edge.
if (!elem.id) {
tempId = `temp-${Math.random()}`;
elem.id = tempId;
}
const query = `#${elem.id} > ${wantedDescendentName}`;
elements = elem.parentElement.querySelectorAll(query);
if (tempId) {
elem.id = "";
}
}
return [...elements];
}

/**
* Shallow sort a definition list based on its definition terms (dt) elements.
*
* @param {HTMLDListElement} dl
* @returns {DocumentFragment}
*/
export function sortDefinitionTerms(dl, dir) {
const elements = getDirectDescendents(dl, "dt");
const sortedElements = elements.sort(makeSorter(dir)).reduce((frag, elem) => {
const { nodeType, nodeName } = elem;
const children = document.createDocumentFragment();
let { nextSibling: next } = elem;
while (next) {
if (!next.nextSibling) {
break;
}
children.appendChild(next.cloneNode(true));
const { nodeType: nextType, nodeName: nextName } = next.nextSibling;
const isSameType = nextType === nodeType && nextName === nodeName;
if (isSameType) {
break;
}
next = next.nextSibling;
}
children.prepend(elem.cloneNode(true));
frag.appendChild(children);
return frag;
}, document.createDocumentFragment());
return sortedElements;
}

export function run(conf, doc, cb) {
for (const elem of document.querySelectorAll("[data-sort]")) {
let sortedElems;
const dir = elem.dataset.sort || "ascending";
switch (elem.localName) {
case "dl":
sortedElems = sortDefinitionTerms(elem, dir);
break;
case "ol":
case "ul":
sortedElems = sortListItems(elem, dir);
break;
default:
pub("warning", `ReSpec can't sort ${elem.localName} elements.`);
}
if (sortedElems) {
const range = document.createRange();
range.selectNodeContents(elem);
range.deleteContents();
elem.appendChild(sortedElems);
}
}
cb();
}
142 changes: 142 additions & 0 deletions tests/spec/core/list-sorter-spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
"use strict";
describe("Core — list-sorter", () => {
afterAll(flushIframes);
let doc;
beforeAll(async () => {
const ops = {
config: makeBasicConfig(),
body:
makeDefaultBody() +
`
<ol data-sort=ascending>
<li>F</li>
<li>Z</li>
<li>a</li>
<li>B</li>
</ol>
<ul data-sort=descending>
<li>F</li>
<li>Z</li>
<li>a</li>
<li>c</li>
</ul>
<ol id="ol-default" data-sort>
<li>F</li>
<li>Z</li>
<li>a</li>
<li>B</li>
</ol>
<ul data-sort=descending id="nested-list">
<li>F</li>
<li>A
<ol data-sort=ascending>
<li>3</li>
<li>9</li>
<li>1</li>
<li>5</li>
</ol>
</li>
<li>z</li>
<li>c</li>
</ul>
<dl data-sort="ascending">
<dt>Z</dt>
<dd>First</dd>
<dd>Second last</dd>
<dd>Last when sorted.</dd>
<dt>h</dt>
<dt>a</dt>
<dt>W</dt>
</dl>
<dl data-sort="descending">
<dt>3</dt>
<dt>9</dt>
<dd>First</dd>
<dt>1</dt>
<dd>First</dd>
<dd>Second last</dd>
<dd>Last when sorted.</dd>
</dl>
<dl id="dont-sort">
<dt>dont</dt>
<dt>sort</dt>
<dt>me</dt>
</dl>
<dl id="default-sort" data-sort>
<dt>9</dt>
<dt>3</dt>
<dt>1</dt>
</dl>
`,
};
doc = await makeRSDoc(ops);
});
describe("Ordered and unordered lists", ()=>{
it("sorts ordered lists in ascending order", () => {
const list = doc.querySelector("ol[data-sort='ascending']");
const first = list.querySelector("li:first-of-type");
const last = list.querySelector("li:last-of-type");
expect(first.textContent).toEqual("a");
expect(last.textContent).toEqual("Z");
});

it("sorts unordered lists in descending order", () => {
const list = doc.querySelector("ul[data-sort='descending']");
const first = list.querySelector("li:first-of-type");
const last = list.querySelector("li:last-of-type");
expect(first.textContent).toEqual("Z");
expect(last.textContent).toEqual("a");
});

it("defaults to sorting in ascending order", () => {
const list = doc.querySelector("#ol-default");
expect(list.firstElementChild.textContent).toEqual("a");
expect(list.lastElementChild.textContent).toEqual("Z");
});

it("sorts nested lists", ()=>{
const list = doc.querySelector("#nested-list");
const first = list.querySelector("li:first-of-type");
const last = list.querySelector("li:last-of-type");
expect(first.textContent).toEqual("z");
expect(last.firstChild.textContent.startsWith("A")).toBe(true);
});
});
describe("Definition lists", ()=>{
it("sorts definition lists in ascending order", () => {
const list = doc.querySelector("dl[data-sort='ascending']");
const firstDt = list.querySelector("dt:first-of-type");
const lastDt = list.querySelector("dt:last-of-type");
expect(firstDt.textContent).toEqual("a");
expect(lastDt.textContent).toEqual("Z");
expect(lastDt.nextElementSibling.textContent).toEqual("First");
expect(list.lastElementChild.textContent).toEqual("Last when sorted.");
expect(list.lastElementChild.previousElementSibling.textContent).toEqual(
"Second last"
);
});

it("sorts definition lists in descending order", () => {
const list = doc.querySelector("dl[data-sort='descending']");
expect(list.firstElementChild.textContent).toEqual("9");
const lastDt = list.querySelector("dt:last-of-type");
expect(lastDt.nextElementSibling.textContent).toEqual("First");
expect(list.lastElementChild.textContent).toEqual("Last when sorted.");
expect(list.lastElementChild.previousElementSibling.textContent).toEqual(
"Second last"
);
});

it("defaults to sorting in definition lists in ascending order", () => {
const list = doc.querySelector("#default-sort");
expect(list.firstElementChild.textContent).toEqual("1");
expect(list.lastElementChild.textContent).toEqual("9");
});

it("leaves unmarked lists alone", () => {
const list = doc.querySelector("#dont-sort");
expect(list.firstElementChild.textContent).toEqual("dont");
expect(list.lastElementChild.textContent).toEqual("me");
});
});
});
1 change: 1 addition & 0 deletions tests/testFiles.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"spec/core/link-to-dfn-spec.js",
"spec/core/linter-rules/no-headingless-sections-spec.js",
"spec/core/linter-rules/no-http-props-spec.js",
"spec/core/list-sorter-spec.js",
"spec/core/location-hash-spec.js",
"spec/core/markdown-spec.js",
"spec/core/override-configuration-spec.js",
Expand Down

0 comments on commit 7f3b6a8

Please sign in to comment.