From 4dfcd2c4b6ca11aa4b1347bd78ac695f41284659 Mon Sep 17 00:00:00 2001 From: Johannes Raggam Date: Thu, 16 Jun 2022 12:14:34 +0200 Subject: [PATCH 1/6] fix(pat slides): The slides initialization did not work since some time. This is now fixed. --- src/pat/slides/slides.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pat/slides/slides.js b/src/pat/slides/slides.js index ece5cc7f6..e2d1aa4a7 100644 --- a/src/pat/slides/slides.js +++ b/src/pat/slides/slides.js @@ -18,7 +18,7 @@ var slides = { }, async init($el) { - if (!this.el.querySelector(".slide")) { + if (!$el[0].querySelector(".slide")) { // no slides, nothing to do. return; } From 88ce8a77207dd66dcdb8efef0321d8d26d0e560c Mon Sep 17 00:00:00 2001 From: Johannes Raggam Date: Thu, 16 Jun 2022 15:07:20 +0200 Subject: [PATCH 2/6] maint(pat slides): Use URL web API instead custom url api from core. --- src/pat/slides/slides.js | 24 ++++++++---------------- src/pat/slides/slides.test.js | 14 +++++--------- 2 files changed, 13 insertions(+), 25 deletions(-) diff --git a/src/pat/slides/slides.js b/src/pat/slides/slides.js index e2d1aa4a7..0afc9f0d8 100644 --- a/src/pat/slides/slides.js +++ b/src/pat/slides/slides.js @@ -6,7 +6,6 @@ import $ from "jquery"; import registry from "../../core/registry"; import utils from "../../core/utils"; -import url from "../../core/url"; import "../../core/remove"; var slides = { @@ -24,10 +23,12 @@ var slides = { } await import("slides/src/slides"); // loads ``Presentation`` globally. - var parameters = url.parameters(); - if (parameters.slides !== undefined) { - var requested_ids = slides._collapse_ids(parameters.slides); - if (requested_ids) slides._remove_slides($el, requested_ids); + const slides_filter = new URL(window.location).searchParams.get("slides"); + if (slides_filter) { + const requested_ids = slides._collapse_ids(slides_filter); + if (requested_ids) { + slides._remove_slides($el, requested_ids); + } } $el.each(function () { var presentation = new window.Presentation(this), @@ -61,17 +62,8 @@ var slides = { }); }, - _collapse_ids: function (params) { - var ids = []; - params.forEach(function (param) { - if (param) - ids = ids.concat( - param.split(",").filter(function (id) { - return !!id; - }) - ); - }); - return ids; + _collapse_ids: function (id_string) { + return (id_string || "").split(",").filter((it) => !!it); }, _remove_slides: function ($shows, ids) { diff --git a/src/pat/slides/slides.test.js b/src/pat/slides/slides.test.js index c9c6940c1..67d919d61 100644 --- a/src/pat/slides/slides.test.js +++ b/src/pat/slides/slides.test.js @@ -10,27 +10,23 @@ describe("pat-slides", function () { describe("_collapse_ids", function () { it("Single id", function () { - expect(pattern._collapse_ids(["foo"])).toEqual(["foo"]); + expect(pattern._collapse_ids("foo")).toEqual(["foo"]); }); it("Comma-separated list of ids", function () { - expect(pattern._collapse_ids(["foo,bar"])).toEqual(["foo", "bar"]); + expect(pattern._collapse_ids("foo,bar")).toEqual(["foo", "bar"]); }); it("Skip empty ids", function () { - expect(pattern._collapse_ids(["foo,,bar"])).toEqual(["foo", "bar"]); + expect(pattern._collapse_ids("foo,,bar")).toEqual(["foo", "bar"]); }); it("Parameter without value", function () { - expect(pattern._collapse_ids([null])).toEqual([]); + expect(pattern._collapse_ids(null)).toEqual([]); }); it("Parameter with empty value", function () { - expect(pattern._collapse_ids([""])).toEqual([]); - }); - - it("Multiple parameters", function () { - expect(pattern._collapse_ids(["foo", "bar"])).toEqual(["foo", "bar"]); + expect(pattern._collapse_ids("")).toEqual([]); }); }); From 383d08a8032df8e5441d3d10392886496ffa6ada Mon Sep 17 00:00:00 2001 From: Johannes Raggam Date: Thu, 16 Jun 2022 16:21:52 +0200 Subject: [PATCH 3/6] maint(pat slides): Modernize pat-slides. --- src/pat/slides/slides.js | 92 +++++++++++++++-------------------- src/pat/slides/slides.test.js | 65 ++++++++++++++++--------- 2 files changed, 79 insertions(+), 78 deletions(-) diff --git a/src/pat/slides/slides.js b/src/pat/slides/slides.js index 0afc9f0d8..381ef233a 100644 --- a/src/pat/slides/slides.js +++ b/src/pat/slides/slides.js @@ -1,49 +1,38 @@ /** * Patterns slides - Automatic and customised slideshows. - * - * Copyright 2013 Simplon B.V. - Wichert Akkerman */ import $ from "jquery"; -import registry from "../../core/registry"; +import Base from "../../core/base"; import utils from "../../core/utils"; import "../../core/remove"; -var slides = { +export default Base.extend({ name: "slides", trigger: ".pat-slides", - setup: function () { - $(document).on("patterns-injected", utils.debounce(slides._reset, 100)); - }, - - async init($el) { - if (!$el[0].querySelector(".slide")) { - // no slides, nothing to do. - return; - } + async init() { await import("slides/src/slides"); // loads ``Presentation`` globally. const slides_filter = new URL(window.location).searchParams.get("slides"); if (slides_filter) { - const requested_ids = slides._collapse_ids(slides_filter); + const requested_ids = this._collapse_ids(slides_filter); if (requested_ids) { - slides._remove_slides($el, requested_ids); + this._remove_slides(requested_ids); } } - $el.each(function () { - var presentation = new window.Presentation(this), - $container = $(this); - $container - .data("pat-slide", presentation) - .on("SlideDisplay", slides._onSlideDisplay) - .on("SlideHide", slides._onSlideHide); - }); - return slides._hook($el); + this.presentation = new window.Presentation(this.el); + this.$el + .on("SlideDisplay", this._on_slide_display.bind(this)) + .on("SlideHide", this._on_slide_hide.bind(this)); + + $(document).on("patterns-injected", utils.debounce(this._reset.bind(this), 100)); + + this._hook(); }, - _onSlideDisplay: function (event) { - var slide = event.originalEvent.detail.slide.element, - $videos = $("video", slide); + _on_slide_display(event) { + const slide = event.originalEvent.detail.slide.element; + const $videos = $("video", slide); $videos.each(function () { if (this.paused) { @@ -53,45 +42,40 @@ var slides = { }); }, - _onSlideHide: function (event) { - var slide = event.originalEvent.detail.slide.element, - $videos = $("video", slide); + _on_slide_hide(event) { + const slide = event.originalEvent.detail.slide.element; + const $videos = $("video", slide); $videos.each(function () { - if (!this.paused) this.pause(); + if (!this.paused) { + this.pause(); + } }); }, - _collapse_ids: function (id_string) { + _collapse_ids(id_string) { return (id_string || "").split(",").filter((it) => !!it); }, - _remove_slides: function ($shows, ids) { - var has_bad_id = function (idx, el) { - return ids.indexOf(el.id) === -1; - }; - - for (var i = 0; i < $shows.length; i++) { - var $show = $shows.eq(i), - $bad_slides = $show.find(".slide[id]").filter(has_bad_id); - $bad_slides.remove(); + _remove_slides(keep_ids) { + for (const slide of this.el.querySelectorAll(".slide[id]")) { + if (keep_ids.indexOf(slide.id) !== -1) { + // Not an id to remove + continue; + } + console.log("remove slide", slide); + slide.remove(); } }, - _hook: function ($el) { - return $el + _hook() { + this.$el .off("destroy.pat-slide") - .on("destroy.pat-slide", utils.debounce(slides._reset, 100)); + .on("destroy.pat-slide", utils.debounce(this._reset.bind(this), 100)); }, - _reset: function () { - var $container = $(this).closest(".pat-slides"), - presentation = $container.data("pat-slide"); - if (presentation) presentation.scan(); - slides._hook($(this.trigger)); + _reset() { + this.presentation.scan(); + this._hook(); }, -}; - -slides.setup(); -registry.register(slides); -export default slides; +}); diff --git a/src/pat/slides/slides.test.js b/src/pat/slides/slides.test.js index 67d919d61..d160f4fe0 100644 --- a/src/pat/slides/slides.test.js +++ b/src/pat/slides/slides.test.js @@ -1,4 +1,4 @@ -import pattern from "./slides"; +import Pattern from "./slides"; import $ from "jquery"; import utils from "../../core/utils"; import { jest } from "@jest/globals"; @@ -8,35 +8,44 @@ describe("pat-slides", function () { jest.restoreAllMocks(); }); - describe("_collapse_ids", function () { + describe("1 - _collapse_ids", function () { it("Single id", function () { - expect(pattern._collapse_ids("foo")).toEqual(["foo"]); + const instance = new Pattern(document.createElement("div")); + expect(instance._collapse_ids("foo")).toEqual(["foo"]); }); it("Comma-separated list of ids", function () { - expect(pattern._collapse_ids("foo,bar")).toEqual(["foo", "bar"]); + const instance = new Pattern(document.createElement("div")); + expect(instance._collapse_ids("foo,bar")).toEqual(["foo", "bar"]); }); it("Skip empty ids", function () { - expect(pattern._collapse_ids("foo,,bar")).toEqual(["foo", "bar"]); + const instance = new Pattern(document.createElement("div")); + expect(instance._collapse_ids("foo,,bar")).toEqual(["foo", "bar"]); }); it("Parameter without value", function () { - expect(pattern._collapse_ids(null)).toEqual([]); + const instance = new Pattern(document.createElement("div")); + expect(instance._collapse_ids(null)).toEqual([]); }); it("Parameter with empty value", function () { - expect(pattern._collapse_ids("")).toEqual([]); + const instance = new Pattern(document.createElement("div")); + expect(instance._collapse_ids("")).toEqual([]); }); }); - describe("_remove_slides", function () { - it("Remove slides from DOM", function () { - var $show = $("
", { class: "pat-slides" }); - for (var i = 1; i <= 4; i++) + describe("2 - _remove_slides", function () { + it("Remove slides from DOM", async function () { + const $show = $("
", { class: "pat-slides" }); + for (let i = 1; i <= 4; i++) $("
", { class: "slide", id: "slide" + i }).appendTo($show); - pattern._remove_slides($show, ["slide1", "slide3"]); - var ids = $.makeArray( + + const instance = new Pattern($show); + await utils.timeout(1); // wait a tick for async to settle. + + instance._remove_slides(["slide1", "slide3"]); + const ids = $.makeArray( $show.find(".slide").map(function (idx, el) { return el.id; }) @@ -44,26 +53,34 @@ describe("pat-slides", function () { expect(ids).toEqual(["slide1", "slide3"]); }); - it.skip("Trigger reset when removing slides", function () { - var $show = $("
", { class: "pat-slides" }); - for (var i = 1; i <= 4; i++) { + it.skip("Trigger reset when removing slides", async function () { + const $show = $("
", { class: "pat-slides" }); + for (let i = 1; i <= 4; i++) { $("
", { class: "slide", id: "slide" + i }).appendTo($show); } jest.spyOn(utils, "debounce").mockImplementation((func) => { return func; }); - var spy_reset = jest.spyOn(pattern, "_reset"); - pattern._hook($show); - pattern._remove_slides($show, ["slide1", "slide3"]); + + const instance = new Pattern($show); + await utils.timeout(1); // wait a tick for async to settle. + + const spy_reset = jest.spyOn(instance, "_reset"); + instance._hook(); + instance._remove_slides(["slide1", "slide3"]); expect(spy_reset).toHaveBeenCalled(); }); - it("Do not trigger reset when not doing anything", function () { - var $show = $("
", { class: "pat-slides" }); - for (var i = 1; i <= 2; i++) + it("Do not trigger reset when not doing anything", async function () { + const $show = $("
", { class: "pat-slides" }); + for (let i = 1; i <= 2; i++) $("
", { class: "slide", id: "slide" + i }).appendTo($show); - var spy_reset = jest.spyOn(pattern, "_reset"); - pattern._remove_slides($show, ["slide1", "slide2"]); + + const instance = new Pattern($show); + await utils.timeout(1); // wait a tick for async to settle. + + const spy_reset = jest.spyOn(instance, "_reset"); + instance._remove_slides(["slide1", "slide2"]); expect(spy_reset).not.toHaveBeenCalled(); }); }); From f2655f67e0bd908522d76ec3d6c8ae6dec7bfb1a Mon Sep 17 00:00:00 2001 From: Johannes Raggam Date: Thu, 16 Jun 2022 15:20:16 +0200 Subject: [PATCH 4/6] maint(core remove): Make remove/destroy handler always active. Import remove module in registry, so that it is always active. We do want the destroy handler be called when pattern elements are remvoed. --- src/core/registry.js | 1 + src/pat/slides/slides.js | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/registry.js b/src/core/registry.js index 4418ca93a..c666adfee 100644 --- a/src/core/registry.js +++ b/src/core/registry.js @@ -20,6 +20,7 @@ import $ from "jquery"; import dom from "./dom"; import logging from "./logging"; import utils from "./utils"; +import "./remove"; const log = logging.getLogger("registry"); const disable_re = /patterns-disable=([^&]+)/g; diff --git a/src/pat/slides/slides.js b/src/pat/slides/slides.js index 381ef233a..bf5e1ba4d 100644 --- a/src/pat/slides/slides.js +++ b/src/pat/slides/slides.js @@ -4,7 +4,6 @@ import $ from "jquery"; import Base from "../../core/base"; import utils from "../../core/utils"; -import "../../core/remove"; export default Base.extend({ name: "slides", From 330fa4550186f1ab9d49b40bfc7f0e4542f24947 Mon Sep 17 00:00:00 2001 From: Johannes Raggam Date: Thu, 16 Jun 2022 15:09:01 +0200 Subject: [PATCH 5/6] breaking: Remove now unused url parsing utilities from core. Use URL web API instead. --- src/core/url.js | 63 ---------------------------------------- src/core/url.test.js | 68 -------------------------------------------- 2 files changed, 131 deletions(-) delete mode 100644 src/core/url.js delete mode 100644 src/core/url.test.js diff --git a/src/core/url.js b/src/core/url.js deleted file mode 100644 index 601231d4f..000000000 --- a/src/core/url.js +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Patterns URL - URL parsing utilities - * - * Copyright 2013 Simplon B.V. - */ - -function UrlArgumentParser() { - this._cache = null; - if (window.addEventListener) window.addEventListener("popstate", this._reset); -} - -UrlArgumentParser.prototype = { - space_pattern: /\+/g, - keyvalue_pattern: /^(.+?)(?:=(.*))/, - - _reset: function UrlArgumentParser_reset() { - this._cache = null; - }, - - _decodeQS: function UrlArgumentParser_decodeQS(bit) { - return decodeURIComponent(bit.replace(this.space_pattern, " ")); - }, - - _parse: function UrlArgumentParser_parse(qs) { - var query = /\?(.+)/.exec(qs), - params = {}; - - if (query === null) return params; - - var parameters = query[1].split("&"), - i, - parts, - key, - value; - - for (i = 0; i < parameters.length; i++) { - if ((parts = this.keyvalue_pattern.exec(parameters[i])) === null) { - key = this._decodeQS(parameters[i]); - value = null; - } else { - key = this._decodeQS(parts[1]); - value = this._decodeQS(parts[2]); - } - - if (params[key] === undefined) params[key] = []; - params[key].push(value); - } - - return params; - }, - - get: function UrlArgumentParser_get() { - if (this._cache === null) this._cache = this._parse(window.location.search); - return this._cache; - }, -}; - -var url_parser = new UrlArgumentParser(); - -export default { - UrlArgumentParser: UrlArgumentParser, - parameters: url_parser.get.bind(url_parser), -}; diff --git a/src/core/url.test.js b/src/core/url.test.js deleted file mode 100644 index 1f99fc728..000000000 --- a/src/core/url.test.js +++ /dev/null @@ -1,68 +0,0 @@ -import url from "./url"; - -describe("Core / url / UrlArgumentParser", function () { - describe("_decodeQS", function () { - it("Basic string", function () { - var parser = new url.UrlArgumentParser(); - expect(parser._decodeQS("Foo")).toBe("Foo"); - }); - - it("String with whitespace", function () { - var parser = new url.UrlArgumentParser(); - expect(parser._decodeQS("Aap+Noot+Mies")).toBe("Aap Noot Mies"); - }); - - it("String with encoded characters", function () { - var parser = new url.UrlArgumentParser(); - expect(parser._decodeQS("Jip%26Janneke")).toBe("Jip&Janneke"); - }); - }); - - describe("_parse", function () { - it("No query string", function () { - var parser = new url.UrlArgumentParser(); - expect(parser._parse("")).toEqual({}); - }); - - it("No parameter specified", function () { - var parser = new url.UrlArgumentParser(); - expect(parser._parse("?")).toEqual({}); - }); - - it("Parameter without value", function () { - var parser = new url.UrlArgumentParser(); - expect(parser._parse("?key")).toEqual({ key: [null] }); - }); - - it("Parameter with empty value", function () { - var parser = new url.UrlArgumentParser(); - expect(parser._parse("?key=")).toEqual({ key: [""] }); - }); - - it("Parameter with plain value", function () { - var parser = new url.UrlArgumentParser(); - expect(parser._parse("?key=value")).toEqual({ key: ["value"] }); - }); - - it("Multiple parameters", function () { - var parser = new url.UrlArgumentParser(); - expect(parser._parse("?one=en&two=to")).toEqual({ - one: ["en"], - two: ["to"], - }); - }); - - it("Parameter with multiple values", function () { - var parser = new url.UrlArgumentParser(); - expect(parser._parse("?key=value&key=other")).toEqual({ - key: ["value", "other"], - }); - }); - }); -}); - -describe("Core / url / parameters", function () { - it("Bound to working parser", function () { - expect(url.parameters()).not.toBe(undefined); - }); -}); From 067cd340c488038d29860192537aaead57540d8f Mon Sep 17 00:00:00 2001 From: Johannes Raggam Date: Sun, 12 Jan 2025 23:29:27 +0100 Subject: [PATCH 6/6] breaking: Remove jquery.ext module with jquery additions. jquery.ext isn't necessary anymore. Methods have been replaced and code modernized. --- src/core/jquery-ext.js | 117 +----------------------------------- src/core/jquery-ext.test.js | 17 ------ 2 files changed, 2 insertions(+), 132 deletions(-) delete mode 100644 src/core/jquery-ext.test.js diff --git a/src/core/jquery-ext.js b/src/core/jquery-ext.js index b7a46a211..367aeab35 100644 --- a/src/core/jquery-ext.js +++ b/src/core/jquery-ext.js @@ -12,7 +12,8 @@ var methods = { var settings = { time: 3 /* time it will wait before moving to "timeout" after a move event */, initialTime: 8 /* time it will wait before first adding the "timeout" class */, - exceptionAreas: [] /* IDs of elements that, if the mouse is over them, will reset the timer */, + exceptionAreas: + [] /* IDs of elements that, if the mouse is over them, will reset the timer */, }; return this.each(function () { var $this = $(this), @@ -156,43 +157,6 @@ $.extend($.expr[":"], { }, }); -// Make Visible in scroll -$.fn.makeVisibleInScroll = function (parent_id) { - var absoluteParent = null; - if (typeof parent_id === "string") { - absoluteParent = $("#" + parent_id); - } else if (parent_id) { - absoluteParent = $(parent_id); - } - - return this.each(function () { - var $this = $(this), - parent; - if (!absoluteParent) { - parent = $this.parents(":scrollable"); - if (parent.length > 0) { - parent = $(parent[0]); - } else { - parent = $(window); - } - } else { - parent = absoluteParent; - } - - var elemTop = $this.position().top; - var elemBottom = $this.height() + elemTop; - - var viewTop = parent.scrollTop(); - var viewBottom = parent.height() + viewTop; - - if (elemTop < viewTop) { - parent.scrollTop(elemTop); - } else if (elemBottom > viewBottom - parent.height() / 2) { - parent.scrollTop(elemTop - (parent.height() - $this.height()) / 2); - } - }); -}; - //Work around warning for jQuery 3.x: //JQMIGRATE: jQuery.fn.offset() requires an element connected to a document $.fn.safeOffset = function () { @@ -212,67 +176,6 @@ $.fn.safeOffset = function () { return $.fn.offset.apply(this, arguments); }; -//Make absolute location -$.fn.setPositionAbsolute = function (element, offsettop, offsetleft) { - return this.each(function () { - // set absolute location for based on the element passed - // dynamically since every browser has different settings - var $this = $(this); - var thiswidth = $(this).width(); - var pos = element.safeOffset(); - var width = element.width(); - var height = element.height(); - var setleft = pos.left + width - thiswidth + offsetleft; - var settop = pos.top + height + offsettop; - $this.css({ - "z-index": 1, - "position": "absolute", - "marginLeft": 0, - "marginTop": 0, - "left": setleft + "px", - "top": settop + "px", - "width": thiswidth, - }); - $this.remove().appendTo("body").show(); - }); -}; - -$.fn.positionAncestor = function (selector) { - var left = 0; - var top = 0; - this.each(function () { - // check if current element has an ancestor matching a selector - // and that ancestor is positioned - var $ancestor = $(this).closest(selector); - if ($ancestor.length && $ancestor.css("position") !== "static") { - var $child = $(this); - var childMarginEdgeLeft = - $child.safeOffset().left - parseInt($child.css("marginLeft"), 10); - var childMarginEdgeTop = - $child.safeOffset().top - parseInt($child.css("marginTop"), 10); - var ancestorPaddingEdgeLeft = - $ancestor.safeOffset().left + - parseInt($ancestor.css("borderLeftWidth"), 10); - var ancestorPaddingEdgeTop = - $ancestor.safeOffset().top + - parseInt($ancestor.css("borderTopWidth"), 10); - left = childMarginEdgeLeft - ancestorPaddingEdgeLeft; - top = childMarginEdgeTop - ancestorPaddingEdgeTop; - // we have found the ancestor and computed the position - // stop iterating - return false; - } - }); - return { - left: left, - top: top, - }; -}; - -$.fn.findInclusive = function (selector) { - return this.find("*").addBack().filter(selector); -}; - $.fn.slideIn = function (speed, easing, callback) { return this.animate({ width: "show" }, speed, easing, callback); }; @@ -281,20 +184,4 @@ $.fn.slideOut = function (speed, easing, callback) { return this.animate({ width: "hide" }, speed, easing, callback); }; -// case-insensitive :contains -$.expr[":"].Contains = function (a, i, m) { - return $(a).text().toUpperCase().indexOf(m[3].toUpperCase()) >= 0; -}; - -$.fn.scopedFind = function (selector) { - /* If the selector starts with an object id do a global search, - * otherwise do a local search. - */ - if (selector.indexOf("#") === 0) { - return $(selector); - } else { - return this.find(selector); - } -}; - export default undefined; diff --git a/src/core/jquery-ext.test.js b/src/core/jquery-ext.test.js deleted file mode 100644 index 73e298d07..000000000 --- a/src/core/jquery-ext.test.js +++ /dev/null @@ -1,17 +0,0 @@ -import "./jquery-ext"; -import $ from "jquery"; - -describe("Find including top-level elements", function () { - it("Top-level elements are included", function () { - var $col = $( - "
" + - "

" + - "" + - "
" + - "
" + - "

" - ), - $match = $col.findInclusive("div p"); - expect($match.length).toBe(1); - }); -});