Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce background actions and media query tracker mechanisms #8555

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from
87 changes: 87 additions & 0 deletions core/modules/filter-tracker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*\
title: $:/core/modules/filter-tracker.js
type: application/javascript
module-type: global

Class to track the results of a filter string

\*/
(function(){

/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";

function FilterTracker(wiki) {
this.wiki = wiki;
this.trackers = [];
this.wiki.addEventListener("change",this.handleChangeEvent.bind(this));
}

FilterTracker.prototype.handleChangeEvent = function(changes) {
this.processTrackers();
this.processChanges(changes);
};

/*
Add a tracker to the filter tracker
filterString: the filter string to track
fnEnter: function to call when a title enters the filter results. Called even if the tiddler does not actually exist. Called as (title), and should return a truthy value that is stored in the tracker as the "enterValue"
fnLeave: function to call when a title leaves the filter results. Called as (title,enterValue)
fnChange: function to call when a tiddler changes in the filter results. Only called for filter results that identify a tiddler or shadow tiddler. Called as (title,enterValue), and may optionally return a replacement enterValue
*/
FilterTracker.prototype.track = function(filterString,fnEnter,fnLeave,fnChange) {
// Add the tracker details
var index = this.trackers.length;
this.trackers.push({
filterString: filterString,
fnEnter: fnEnter,
fnLeave: fnLeave,
fnChange: fnChange,
Copy link
Member

@pmario pmario Jan 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not 100% sure, but there seem to be no tests if the fn* parameters are undefined of if they actually are function()

previousResults: [], // Results from the previous time the tracker was processed
resultValues: {} // Map by title to the value returned by fnEnter
});
// Process the tracker
this.processTracker(index);
};

FilterTracker.prototype.processTrackers = function() {
for(var t=0; t<this.trackers.length; t++) {
this.processTracker(t);
}
};

FilterTracker.prototype.processTracker = function(index) {
var tracker = this.trackers[index],
results = this.wiki.filterTiddlers(tracker.filterString);
// Process the results
$tw.utils.each(results,function(title) {
if(tracker.previousResults.indexOf(title) === -1 && !tracker.resultValues[title]) {
tracker.resultValues[title] = tracker.fnEnter(title) || true;
}
});
$tw.utils.each(tracker.previousResults,function(title) {
if(results.indexOf(title) === -1 && tracker.resultValues[title]) {
tracker.fnLeave(title,tracker.resultValues[title]);
delete tracker.resultValues[title];
}
});
// Update the previous results
tracker.previousResults = results;
};

FilterTracker.prototype.processChanges = function(changes) {
for(var t=0; t<this.trackers.length; t++) {
var tracker = this.trackers[t];
$tw.utils.each(changes,function(change,title) {
if(tracker.previousResults.indexOf(title) !== -1) {
// Call the change function and if it doesn't return a value then keep the old value
tracker.resultValues[title] = tracker.fnChange(title,tracker.resultValues[title]) || tracker.resultValues[title];
}
});
}
};

exports.FilterTracker = FilterTracker;

})();
45 changes: 32 additions & 13 deletions core/modules/info/mediaquerytracker.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,36 +13,55 @@ Initialise $:/info/ tiddlers derived from media queries via
"use strict";

exports.getInfoTiddlerFields = function(updateInfoTiddlersCallback) {
var infoTiddlerFields = [];
if($tw.browser) {
// Get the media query tracker tiddlers
var trackers = $tw.wiki.getTiddlersWithTag("$:/tags/MediaQueryTracker");
$tw.utils.each(trackers,function(title) {
var tiddler = $tw.wiki.getTiddler(title);
// Functions to start and stop tracking a particular media query tracker tiddler
function track(title) {
var result = {},
tiddler = $tw.wiki.getTiddler(title);
if(tiddler) {
var mediaQuery = tiddler.fields["media-query"],
infoTiddler = tiddler.fields["info-tiddler"],
infoTiddlerAlt = tiddler.fields["info-tiddler-alt"];
if(mediaQuery && infoTiddler) {
// Evaluate and track the media query
var mqList = window.matchMedia(mediaQuery);
result.mqList = window.matchMedia(mediaQuery);
function getResultTiddlers() {
var value = mqList.matches ? "yes" : "no",
tiddlers = [{title: infoTiddler, text: value}];
var value = result.mqList.matches ? "yes" : "no",
tiddlers = [];
tiddlers.push({title: infoTiddler, text: value});
if(infoTiddlerAlt) {
tiddlers.push({title: infoTiddlerAlt, text: value})
}
return tiddlers;
};
infoTiddlerFields.push.apply(infoTiddlerFields,getResultTiddlers());
mqList.addEventListener("change",function(event) {
updateInfoTiddlersCallback(getResultTiddlers());
result.handler = function(event) {
updateInfoTiddlersCallback(getResultTiddlers());
});
};
result.mqList.addEventListener("change",result.handler);
}
}
});
return result;
}
function untrack(enterValue) {
if(enterValue.mqList && enterValue.handler) {
enterValue.mqList.removeEventListener("change",enterValue.handler);
}
}
// Track media query tracker tiddlers
function fnEnter(title) {
return track(title);
}
function fnLeave(title,enterValue) {
untrack(enterValue);
}
function fnChange(title,enterValue) {
untrack(enterValue);
return track(title);
}
$tw.filterTracker.track("[all[tiddlers+shadows]tag[$:/tags/MediaQueryTracker]!is[draft]]",fnEnter,fnLeave,fnChange);
}
return infoTiddlerFields;
return [];
};

})();
11 changes: 11 additions & 0 deletions core/modules/startup/load-modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ Load core modules
exports.name = "load-modules";
exports.synchronous = true;

// Set to `true` to enable performance instrumentation
var PERFORMANCE_INSTRUMENTATION_CONFIG_TITLE = "$:/config/Performance/Instrumentation";

exports.startup = function() {
// Load modules
$tw.modules.applyMethods("utils",$tw.utils);
Expand All @@ -35,6 +38,14 @@ exports.startup = function() {
$tw.macros = $tw.modules.getModulesByTypeAsHashmap("macro");
$tw.wiki.initParsers();
$tw.Commander.initCommands();
// --------------------------
// The rest of the startup process here is not strictly to do with loading modules, but are needed before other startup
// modules are executed. It is easier to put them here than to introduce a new startup module
// --------------------------
// Set up the performance framework
$tw.perf = new $tw.Performance($tw.wiki.getTiddlerText(PERFORMANCE_INSTRUMENTATION_CONFIG_TITLE,"no") === "yes");
// Kick off the filter tracker
$tw.filterTracker = new $tw.FilterTracker($tw.wiki);
};

})();
5 changes: 0 additions & 5 deletions core/modules/startup/startup.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@ exports.name = "startup";
exports.after = ["load-modules"];
exports.synchronous = true;

// Set to `true` to enable performance instrumentation
var PERFORMANCE_INSTRUMENTATION_CONFIG_TITLE = "$:/config/Performance/Instrumentation";

var widget = require("$:/core/modules/widgets/widget.js");

exports.startup = function() {
Expand Down Expand Up @@ -57,8 +54,6 @@ exports.startup = function() {
}
// Initialise version
$tw.version = $tw.utils.extractVersionInfo();
// Set up the performance framework
$tw.perf = new $tw.Performance($tw.wiki.getTiddlerText(PERFORMANCE_INSTRUMENTATION_CONFIG_TITLE,"no") === "yes");
// Create a root widget for attaching event handlers. By using it as the parentWidget for another widget tree, one can reuse the event handlers
$tw.rootWidget = new widget.widget({
type: "widget",
Expand Down
4 changes: 2 additions & 2 deletions editions/tw5.com/tiddlers/mechanisms/InfoMechanism.tid
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ System tiddlers in the namespace `$:/info/` are used to expose information about
|[[$:/info/browser/language]] |<<.from-version "5.1.20">> Language as reported by browser (note that some browsers report two character codes such as `en` while others report full codes such as `en-GB`) |
|[[$:/info/browser/screen/width]] |Screen width in pixels |
|[[$:/info/browser/screen/height]] |Screen height in pixels |
|[[$:/info/browser/darkmode]] |<<.from-version "5.3.6">> Is dark mode preferred? ("yes" or "no") |
|[[$:/info/darkmode]] |<<.deprecated-since "5.3.6">> Alias for $:/info/browser/darkmode |
|[[$:/info/browser/darkmode]] |<<.from-version "5.3.7">> Is dark mode preferred? ("yes" or "no") |
|[[$:/info/darkmode]] |<<.deprecated-since "5.3.7">> Alias for $:/info/browser/darkmode |
|[[$:/info/node]] |Running under [[Node.js]]? ("yes" or "no") |
|[[$:/info/url/full]] |<<.from-version "5.1.14">> Full URL of wiki (eg, ''<<example full>>'') |
|[[$:/info/url/host]] |<<.from-version "5.1.14">> Host portion of URL of wiki (eg, ''<<example host>>'') |
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
title: MediaQueryTrackerMechanism
tags: Mechanisms

The media query tracker mechanism allows you to define [[custom CSS media queries|https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries/Using_media_queries]] to be bound to a specified [[info|InfoMechanism]] tiddler. The info tiddler will be dynamically update to reflect the current state of the media query.
<<.from-version "5.3.7">> The media query tracker mechanism allows you to define [[custom CSS media queries|https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries/Using_media_queries]] to be bound to a specified [[info|InfoMechanism]] tiddler. The info tiddler will be dynamically update to reflect the current state of the media query.

Adding or modifying a tiddler tagged $:/tags/MediaQueryTracker will only take effect when the wiki is reloaded
Adding or modifying a tiddler tagged $:/tags/MediaQueryTracker takes effect immediately.

The media queries are always applied against the main window. This is relevant for viewport related media queries such as `min-width` which will always respect the main window and ignore the sizes of any external windows.
Loading