diff --git a/core/modules/background-actions.js b/core/modules/background-actions.js new file mode 100644 index 000000000..95b4a611e --- /dev/null +++ b/core/modules/background-actions.js @@ -0,0 +1,117 @@ +/*\ +title: $:/core/modules/background-actions.js +type: application/javascript +module-type: global + +Class to dispatch actions when filters change + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +function BackgroundActionDispatcher(filterTracker,wiki) { + var self = this; + this.filterTracker = filterTracker; + this.wiki = wiki; + this.nextTrackedFilterId = 1; + this.trackedFilters = Object.create(null); // Hashmap by id + // Track the filter for the background actions + this.filterTracker.track({ + filterString: "[all[tiddlers+shadows]tag[$:/tags/BackgroundAction]!is[draft]]", + fnEnter: function fnEnter(title) { + return self.trackFilter(title); + }, + fnLeave: function fnLeave(title,enterValue) { + self.untrackFilter(enterValue); + }, + fnChange: function fnChange(title,enterValue) { + self.untrackFilter(enterValue); + return self.trackFilter(title); + }, + fnProcess: function fnProcess(changes) { + self.process(changes); + } + }); +} + +BackgroundActionDispatcher.prototype.trackFilter = function(title) { + var tiddler = this.wiki.getTiddler(title), + id = this.nextTrackedFilterId++, + tracker = new BackgroundActionTracker({ + wiki: this.wiki, + title: title, + trackFilter: tiddler.fields["track-filter"], + actions: tiddler.fields.text + }); + this.trackedFilters[id] = tracker; + return id; +}; + +BackgroundActionDispatcher.prototype.untrackFilter = function(enterValue) { + var tracker = this.trackedFilters[enterValue]; + if(tracker) { + tracker.destroy(); + } + delete this.trackedFilters[enterValue]; +}; + +BackgroundActionDispatcher.prototype.process = function(changes) { + for(var id in this.trackedFilters) { + this.trackedFilters[id].process(changes); + } +}; + +/* +Represents an individual tracked filter. Options include: +wiki: wiki to use +title: title of the tiddler being tracked +trackFilter: filter string to track changes +actions: actions to be executed when the filter changes +*/ +function BackgroundActionTracker(options) { + var self = this; + this.wiki = options.wiki; + this.title = options.title; + this.trackFilter = options.trackFilter; + this.actions = options.actions + this.filterTracker = new $tw.FilterTracker(this.wiki); + this.hasChanged = false; + this.trackerID = this.filterTracker.track({ + filterString: this.trackFilter, + fnEnter: function(title) { + self.hasChanged = true; + }, + fnLeave: function(title,enterValue) { + self.hasChanged = true; + }, + fnProcess: function(changes) { + if(self.hasChanged) { + self.hasChanged = false; + self.wiki.invokeActionString( + self.actions, + null, + { + title: self.title + },{ + parentWidget: $tw.rootWidget + } + ); + } + } + }); +} + +BackgroundActionTracker.prototype.process = function(changes) { + this.filterTracker.handleChangeEvent(changes); +}; + +BackgroundActionTracker.prototype.destroy = function() { + this.filterTracker.untrack(this.trackerID); +}; + +exports.BackgroundActionDispatcher = BackgroundActionDispatcher; + +})(); diff --git a/core/modules/filter-tracker.js b/core/modules/filter-tracker.js new file mode 100644 index 000000000..36744b2cf --- /dev/null +++ b/core/modules/filter-tracker.js @@ -0,0 +1,108 @@ +/*\ +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.nextTrackerId = 1; +} + +FilterTracker.prototype.handleChangeEvent = function(changes) { + this.processTrackers(); + this.processChanges(changes); +}; + +/* +Add a tracker to the filter tracker. Returns null if any of the parameters are invalid, or a tracker id if the tracker was added successfully. Options include: +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 +fnProcess: function to call each time the tracker is processed, after any enter, leave or change functions are called. Called as (changes) +*/ +FilterTracker.prototype.track = function(options) { + // Add the tracker details + var tracker = { + id: this.nextTrackerId++, + filterString: options.filterString, + fnEnter: options.fnEnter, + fnLeave: options.fnLeave, + fnChange: options.fnChange, + fnProcess: options.fnProcess, + previousResults: [], // Results from the previous time the tracker was processed + resultValues: {} // Map by title to the value returned by fnEnter + }; + this.trackers.push(tracker); + // Process the tracker + this.processTracker(this.trackers.length - 1); + return tracker.id; +}; + +FilterTracker.prototype.untrack = function(id) { + for(var t=0; t +<$action-setfield $tiddler="$:/palette" text="$:/palettes/TwentyTwenties"/> +<%else%> +<$action-setfield $tiddler="$:/palette" text="$:/palettes/TwentyTwenties/Dark"/> +<%endif%> \ No newline at end of file diff --git a/core/wiki/SampleBackgroundAction Story Change.tid b/core/wiki/SampleBackgroundAction Story Change.tid new file mode 100644 index 000000000..6066650b4 --- /dev/null +++ b/core/wiki/SampleBackgroundAction Story Change.tid @@ -0,0 +1,15 @@ +title: SampleBackgroundAction: Story Change +tags: $:/tags/BackgroundAction +track-filter: [list[$:/StoryList]] + +<$action-sendmessage $message="tm-notify" $param="SampleBackgroundAction: Story Change" list={{$:/StoryList!!list}}/> + +Story List: + +
    +<$list filter="[enlist]"> +
  1. +<$text text=<>/> +
  2. + +
diff --git a/core/wiki/config/MediaQueryTrackers/DarkLightPreferred.tid b/core/wiki/config/MediaQueryTrackers/DarkLightPreferred.tid new file mode 100644 index 000000000..6b5e9f26c --- /dev/null +++ b/core/wiki/config/MediaQueryTrackers/DarkLightPreferred.tid @@ -0,0 +1,5 @@ +title: $:/core/wiki/config/MediaQueryTrackers/DarkLightPreferred +tags: $:/tags/MediaQueryTracker +media-query: (prefers-color-scheme: dark) +info-tiddler: $:/info/browser/darkmode +info-tiddler-alt: $:/info/darkmode diff --git a/editions/tw5.com/tiddlers/mechanisms/InfoMechanism.tid b/editions/tw5.com/tiddlers/mechanisms/InfoMechanism.tid index 040dd7bd2..1cfeebb6e 100644 --- a/editions/tw5.com/tiddlers/mechanisms/InfoMechanism.tid +++ b/editions/tw5.com/tiddlers/mechanisms/InfoMechanism.tid @@ -4,8 +4,8 @@ tags: Mechanisms title: InfoMechanism type: text/vnd.tiddlywiki -\define example(name) -<$transclude tiddler="""$:/info/url/$name$""" mode="inline"/> +\procedure example(name) +<$text text={{{ [[$:/info/url/]addsuffixget[text]] }}} /> \end System tiddlers in the namespace `$:/info/` are used to expose information about the system (including the current browser) so that WikiText applications can adapt themselves to available features. @@ -19,6 +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.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, ''<>'') | |[[$:/info/url/host]] |<<.from-version "5.1.14">> Host portion of URL of wiki (eg, ''<>'') | @@ -28,4 +30,3 @@ System tiddlers in the namespace `$:/info/` are used to expose information about |[[$:/info/url/port]] |<<.from-version "5.1.14">> Port portion of URL of wiki (eg, ''<>'') | |[[$:/info/url/protocol]] |<<.from-version "5.1.14">> Protocol portion of URL of wiki (eg, ''<>'') | |[[$:/info/url/search]] |<<.from-version "5.1.14">> Search portion of URL of wiki (eg, ''<>'') | -|[[$:/info/darkmode]] |<<.from-version "5.1.23">> Is dark mode enabled? ("yes" or "no") | diff --git a/editions/tw5.com/tiddlers/mechanisms/MediaQueryTrackerMechanism.tid b/editions/tw5.com/tiddlers/mechanisms/MediaQueryTrackerMechanism.tid new file mode 100644 index 000000000..e050da078 --- /dev/null +++ b/editions/tw5.com/tiddlers/mechanisms/MediaQueryTrackerMechanism.tid @@ -0,0 +1,8 @@ +title: MediaQueryTrackerMechanism +tags: Mechanisms + +<<.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 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.