Javascript plugin to set the current window name

[Edited] State of play Javascript plugin to set the current window name - #11 by TW_Tones

Folks,

I can start my journey to write a javascript plugin but I thought I would first ask here;

  • If possible a TiddlyWIki javascript dev could please write it for me
  • or point to me to a Javascript tiddler with at least one parameter I could use as a template.

I would like to call a “VERY SIMPLE javascript macro” with one parameter “windowname” and have the javascript set the current window/tab using
window.name = '$windowname$' $windowname$ to indicate the parameter value

Desired functionality;

  • I understand this is the same as following a href with a target of $windowname$ but sets the current tab/window to that name, so following a subsequent link with a target="$windowname$" it will open in the same window/tab

Why?;
When using single file wikis, or local storage on a read only hosted wiki it, is desirable not to open the same file or site in multiple windows or tabs.

  • It is a little awkward to ask a user to follow a link to a new window with a target set then get them to close the initial tab/window.
  • It is possible to set the target in a bookmark in the local browser that honors the target that was set.

Futures;

  • I wish to use both the window.name and target along with some cookies that will allow us to set a path to a local copy (or copies) of that site and be prompted to navigate to it from the internet published site.
  • If at all possible if a macro could retrieve the existing window.name this would be helpful (I do not know if this is possible)
  • I may also be able to make a smart bookmarklet (javascript) to smooth any workflows.

Thanks in advance for anything you can contribute.

I assume you mean just the macro… I would start by copying a simple macro, such as TiddlyWiki5/now.js at master · Jermolene/TiddlyWiki5 · GitHub

Create a tiddler with the type “application/javascript” and the field “module-type: macro”.

Copy this code to the tiddler, save and refresh.

(function(){

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

exports.name = "set-window-name";

exports.params = [
	{name: "windowname"}
];

exports.run = function(windowname) {
	window.name = windowname;
	return ""; // Macros are expected to return output
};

})();

Try it with <<set-window-name "Foobar!">>.

I have no idea if this will work or not.

2 Likes

@cdaven you were spot on thank you.

I expected it to be simple but knew just one character wrong and It would fail. Thank you.

set-window-name.json (818 Bytes)

Apparently the same term window.name can be used to retrieve the value if already set.

I am now looking to learn how to return a value as well.

Thanks this small piece of info is unleashing the script kiddy in me :nerd_face:

I have successfully created a get-window-name macro as follows;

(function(){

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

exports.name = "get-window-name";

exports.params = [
	{name: "windowname"}
];

exports.run = function() {
	return window.name; // Macros are expected to return output
};

})();
  • This is working and I can use it in a filter
  • The following is “redundant” because I do not need a parameter for this
    • but if I remove it the macro throws an error;
exports.params = [
	{name: "windowname"}
];

Once this is tidied up I can see creating simple templates from these to create javascript macro to get or set any available value, or other simple javascript “functions” which will prove very useful.

Using set-window-name

The set-window-name macro works as in the package above, but simply happens if the macro <<set-windows-name name>> is rendered.

  • There will be cases where this is helpful like always forcing the target name
  • However I wonder how I may be able to use it behind a trigger like a button so it only occurs as a result of a selected trigger?
    • Is it as simple as hiding it an only revealing it when we want the window.name to be set
    • OR is there another way similar to other macros or widgets?

Just stick it in the actions= for your button.

You might also consider modifying the title of your document – then the window/tab title will visibly change.

document.title = "Tones"

image

1 Like

I meant to add, congrats Tony, you just expanded your comfort zone :sunglasses:

1 Like

YES! Congrats. That was a really good experiment. I have notes (NOT negative).

  • The get-window-name macro will function fine for all your use cases. It only returns text data, which is the purpose of macros.
  • The set-window-name macro will cause problems. It will attempt to run the code inside the function every time the macro is onscreen and refreshed. Probably not noticeable at first, but you will see errors when the tiddler you are rendering into another window has this function displayed/wikified. It will over-write the window.name if the are passed different windowname values.

You will want to setup the set-window-name as an action-widget. Let me see if I can prototype it here. Ok, this will set a passed in $name= value, OR get the variable windowName from the surrounding context. [Edit] If neither are valid (or both are blank/empty), it uses the current window.name.

/*\
title: $:/plugins/windowname/modules/widgets/action-setwindowname.js
type: application/javascript
module-type: widget
Action widget to set the current window.name.
\*/
(function(){

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

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

var SetWindowNameWidget = function(parseTreeNode,options) {
	this.initialise(parseTreeNode,options);
};

/*
Inherit from the base widget class
*/
SetWindowNameWidget.prototype = new Widget();

/*
Render this widget into the DOM
*/
SetWindowNameWidget.prototype.render = function(parent,nextSibling) {
	this.computeAttributes();
	this.execute();
};

/*
Compute the internal state of the widget
*/
SetWindowNameWidget.prototype.execute = function() {
	this.windowName= this.getAttribute("$name") || (!this.hasParseTreeNodeAttribute("$name") && this.getVariable("windowName")) || window.name;
};

/*
Refresh the widget by ensuring our attributes are up to date
*/
SetWindowNameWidget.prototype.refresh = function(changedTiddlers) {
	// Nothing to refresh
	return this.refreshChildren(changedTiddlers);
};

/*
Invoke the action associated with this widget
*/
SetWindowNameWidget.prototype.invokeAction = function(triggeringWidget,event) {
	var self = this,
		options = {};
	if(typeof this.windowName == "string" && this.windowName !== window.name) {
	    window.name = windowName;
	}
	return true; // Action was invoked
};

exports["action-setwindowname"] = SetWindowNameWidget;

})();

:dark_sunglasses:

(I landed a very good job recently, and can pivot back to TW. :slight_smile: )

1 Like

Just edited to update a bug with both values being missing/blank.

1 Like

Thanks all for your help

Yes - this may prove useful at some point, I was trawling the variables/functions available for opportunities after writing my first JS macros. Of course I have already achieved this using the $:/core/wiki/title constructed from $:/SiteTitle and $:/SiteSubtitle.

  • Actually this demonstrates another way to handle the window name and that is to use a config tiddler (Still above my pay grade in Javascript)
  • Yes, and helpful when I see a gap that needs Javascript, however I don’t know how well it is known but I have tried to maintain a super user (non dev) position with tiddlywiki so I could represent a perspective that is often missing, a position between users and developers.
  • However this has proven to be a “Rod for my own back” as I struggle to communicate with the experts and the patience and empathy is not always as strong, as I would use towards others outside my areas of expertise.
  • Thanks @joshuafontany - me personally I love constructive criticism of “ideas”, especially my own ideas, having come from the “egoless programing” approach to technology.
  • I will look at this transition to an action widget ASAP but today I have to deal with Malware locking out access on someones computer, someone close to me, who was defrauded in the $1,000’s of dollars yesterday.
  • I am very happy to hear that, congratulations, I need to do that myself.

Background

These plugins and the functionality they give me is part of a “workflow chain” that helps simplify the “make this your own” process. I am working to make the user process of acquiring a tiddlywiki edition / application almost “one touch”, I think I have most of the “validated” components now. Allowing;

  • Make this your own process
  • A transparent way for a single user to handle single file wikis across multiple devices
  • Its only a small step further for multiple users in a team to use a single file wiki with serial editing.
  • Some “secret stretch goals” that would blow your mind :nerd_face:

Do reach out if you would be happy to collaborate, perhaps PM me here in talk.tiddlywiki

I have returned to work on this since I now have the wiki auto-detecting the current window.name if available and updating the $:/info/startup-window-name or to “unnamed” if not available.

The difficulty I have is making a working action tiddler widget. action-setwindowname.json (1.8 KB)

<$button >
<$action-setwindowname $name="New name"/>
New name
</$button>

when the button is clicked I get

Internal JavaScript Error

Well, this is embarrassing. It is recommended that you restart TiddlyWiki by refreshing your browser

ReferenceError: windowName is not defined

Once I have the action widget working;

  • I will save a custom window name in $:/config/window.name
  • Create a startup action to see if the window.name in $:/info/startup-window-name matches $:/config/window.name
    • if not set the window name to the value in $:/config/window.name
    • Provide an optional prompt before changing the window name.

Any developer support appreciated.

Since all of the actual processing occurs in the invokeAction() method, you don’t need render(), execute(), or refresh() methods. Instead, your invokeAction() can just do this:

SetWindowNameWidget.prototype.invokeAction = function(triggeringWidget,event) {
	this.computeAttributes();
	var windowName= this.getAttribute("$name")
		|| (!this.hasParseTreeNodeAttribute("$name") && this.getVariable("windowName"))
		|| window.name;
	window.name = windowName;
	return true; // Action was invoked
};

Note also that getAttribute("...") always returns a string value, so there is no need to check for typeof windowName == "string". You also can just set the window.name unconditionally, without checking if (windowName != window.name), since they ARE equal it would just set the same name, which is effectively a no-op.

I will try and edit it correctly, thanks @EricShulman