Skip to content

Latest commit

 

History

History
687 lines (610 loc) · 17.2 KB

cards_ui.sfile.md

File metadata and controls

687 lines (610 loc) · 17.2 KB

An extension to cards.sfile that provides graphical ui versions of shortcuts.

This shortcut-file has a tutorial video available: Using the "cards" shortcut-file to use virtual cards (runtime 12:43)

__

^sfile setup$

__

const confirmObjectPath = _inlineScripts.inlineScripts.HelperFncs.confirmObjectPath;

// Helper function - Turn relative path into absolute path based in the vault's root
function getAbsolutePath(path)
{
	if (path.startsWith("data:image")) { return path; }
	path = app.vault.fileMap[path];
	if (!path) { return ""; }
	return app.vault.getResourcePath(path);
}

// Helper function - Get the current back-image, url
function getBackImage()
{
	return _inlineScripts.state.sessionState.cards.backImage ||
	       _inlineScripts.cards.defaultBackImage;
}

// Custom popop definition for various card manipulations
confirmObjectPath("_inlineScripts.cards_ui.manipulateCardsPopup",
{
	// Setup function
	onOpen: async (data, parent, firstButton, SettingType) =>
	{
		data.pileNames = Object.keys(_inlineScripts.state.sessionState.cards.piles);

		// Source pile dropdown
		new SettingType(parent)
			.setName(
				data.defaults?.dst === undefined ? "Card-pile" : "Source card-pile")
			.setDesc(data.descriptions?.src)
			.addDropdown(dropdown =>
			{
				dropdown.addOptions(data.pileNames);
				dropdown.setValue(
					data.defaults?.src ?
					data.pileNames.indexOf(data.defaults?.src) :
					0);
				data.src = dropdown;
				dropdown.selectEl.addEventListener("keypress", e =>
				{
					if (e.key === "Enter") { firstButton.click(); }
				});
				return dropdown;
			});

		// Destination pile dropdown
		if (data.defaults?.dst !== undefined)
		{
			new SettingType(parent)
				.setName("Destination Card-pile")
				.setDesc(data.descriptions?.dst)
				.addDropdown(dropdown =>
				{
					dropdown.addOptions(data.pileNames);
					dropdown.setValue(
						data.defaults?.dst ?
						data.pileNames.indexOf(data.defaults?.dst) :
						0);
					data.dst = dropdown;
					dropdown.selectEl.addEventListener("keypress", e =>
				    {
						if (e.key === "Enter") { firstButton.click(); }
				    });
					return dropdown;
				});
		}

		// Count textbox
		if (data.defaults?.count !== undefined)
		{
			new SettingType(parent)
				.setName("Count")
				.setDesc(data.descriptions?.count)
				.addText(text =>
				{
					text.setValue(data.defaults?.count + "");
					data.count = text;
					text.inputEl.addEventListener("keypress", e =>
				    {
						if (e.key === "Enter") { firstButton.click(); }
						else if (e.keyCode < 48 || e.keyCode > 57)
						{
							window.event.returnValue = null;
						}
				    });
					text.inputEl.addEventListener("blur", e =>
				    {
					    if (Number(e.target.value) < 1)
					    {
						    e.target.value = "1";
					    }
				    });
					return text;
				});
		}

		// Topbottom dropdown
		if (data.defaults?.topBottom !== undefined)
		{
			new SettingType(parent)
				.setName("Top or bottom")
				.setDesc(data.descriptions?.topBottom)
				.addDropdown(dropdown =>
				{
					dropdown.addOptions([ "Top", "Bottom" ]);
					dropdown.setValue(data.defaults?.topBottom);
					data.topBottom = dropdown;
					dropdown.selectEl.addEventListener("keypress", e =>
				    {
						if (e.key === "Enter") { firstButton.click(); }
				    });
					return dropdown;
				});
		}

		// Rotate toggle button
		if (data.defaults?.rotate !== undefined)
		{
			new SettingType(parent)
				.setName("Rotate cards")
				.setDesc(data.descriptions?.rotate)
				.addToggle(toggle =>
				{
					toggle.setValue(data.defaults?.rotate);
					data.rotate = toggle;
					toggle.toggleEl.addEventListener("keypress", e =>
				    {
						if (e.key === "Enter") { firstButton.click(); }
				    });
					return toggle;
				});
		}
	},

	// Shutown function
	onClose: async (data, resolveFnc, buttonId) =>
	{
		if (buttonId !== "Ok") { return; }
		let result = { src: data.pileNames[data.src.getValue()] };
		if (data.dst) { result.dst = data.pileNames[data.dst.getValue()]; }
		if (data.count) { result.count = data.count.getValue(); }
		if (data.topBottom) { result.topBottom = data.topBottom.getValue(); }
		if (data.rotate) { result.rotate = data.rotate.getValue(); }
		resolveFnc(result);
	}
});

// Custom popop definition for altering the settings of the cards system
confirmObjectPath("_inlineScripts.cards_ui.settingsPopup",
{
	// Setup function
	onOpen: async (data, parent, firstButton, SettingType) =>
	{
		// The current card size (before we change it)
		const currentSize = _inlineScripts.state.sessionState.cards.size;
		const currentBackImage = getBackImage();

		// A parent div to center the card visualization
		let sampleContainerUi = document.createElement("div");
		sampleContainerUi.style["text-align"] = "center";
		parent.append(sampleContainerUi);

		// The card visualization
		data.sampleUi = document.createElement("img");
		data.sampleUi.src = getAbsolutePath(currentBackImage);
		data.sampleUi.style.width = currentSize + "px";
		sampleContainerUi.append(data.sampleUi);

		// A slider to change the card size
		let sizeUi = new SettingType(parent)
			.setName("Size (" + currentSize + ")")
			.setDesc("Pick the size for all cards (in pixels).")
			.addSlider((slider) =>
			{
				slider
					.setLimits(10, 500, 10)
					.setValue(currentSize)
					.onChange(value =>
					{
						data.sampleUi.style.width = value + "px";
						data.titleUi.innerText = "Size (" + value + ")";
					});
				data.sliderUi = slider;
				slider.sliderEl.addEventListener("keypress", e =>
				{
					if (e.key === "Enter") { firstButton.click(); }
				});
				return slider;
			});
		// Keep track of the slider label - to change its text when the size changes
		data.titleUi = sizeUi.nameEl;

		// A dropdown to select the card back
		let backImageUi = new SettingType(parent)
			.setName("Back image")
			.setDesc("Choose the image to represent the back of all cards.")
			.addDropdown(dropdown =>
			{
				data.backImageOptions =
					Object.keys(app.vault.fileMap).filter(
						v => { return v.match(/.(?:png|bmp|gif|jpg|jpeg)$/); }
					);
				data.backImageOptions.unshift("");
				dropdown.addOptions(data.backImageOptions);
				dropdown.setValue(data.backImageOptions.indexOf(currentBackImage));
				dropdown.onChange(value =>
				{
					data.sampleUi.src = getAbsolutePath(
						data.backImageOptions[value] ||
						_inlineScripts.cards.defaultBackImage);
				});
				data.backImageUi = dropdown;
				dropdown.selectEl.addEventListener("keypress", e =>
				{
					if (e.key === "Enter") { firstButton.click(); }
				});
				return dropdown;
			});
	},

	// Shutown function
	onClose: async (data, resolveFnc, buttonId) =>
	{
		if (buttonId !== "Ok") { return; }
		resolveFnc({
			size: data.sliderUi.getValue(),
			backImage: data.backImageOptions[data.backImageUi.getValue()] || "default"
		});
	}
});

__ Sets up this shortcut-file

__ __

// User chooses an existing pile
userChoosesPileId = function(requestMessage, failMessage, pileToBlock)
{
	// Get names of the piles
	const pileNames =
		Object.keys(_inlineScripts.state.sessionState.cards.piles).
		filter(v => v !== pileToBlock);
	if (!pileNames.length)
	{
		return [ null, expFormat(failMessage + "No card-piles available.") ];
	}

	// Choose a pile
	const pileIndex =
		popups.pick("Choose a card-pile " + requestMessage, pileNames, 0, "adaptive");
	if (pileIndex === null) { return [ null, null ]; }

	// Return the chosen pile name
	return [ pileNames[pileIndex], null ];
}

// Return true if at least one pile exists, return false otherwise
function doPilesExist()
{
	return !!( Object.keys(_inlineScripts.state.sessionState.cards.piles).length );
}

__ Helper scripts

__

^ui cards? reset$

__

return expand("cards reset");

__ ui cards reset - Clears all card-piles.

__

^ui cards? settings$

__

// Choose settings
const newSettings = popups.custom(
	"Choose settings for all cards", _inlineScripts.cards_ui.settingsPopup);
if (newSettings === null) { return null; }

return expand("cards settings " + newSettings.size + " " + newSettings.backImage);

__ ui cards settings - User chooses card size and card back image.


__

^ui cards? pile$

__

// Get the data-string to import
const toImport = popups.input("Enter a name for the new pile");
if (toImport === null) { return null; }
if (_inlineScripts.state.sessionState.cards.piles[toImport])
{
	return expFormat("Card-pile not created.  __" + toImport + "__ already exists.");
}

// User decides whether to rotate cards
const isFaceUp = popups.pick(
	"How should cards face in the card-pile?", [ "Face-down", "Face-up" ], 0, 2);
if (isFaceUp === null) { return null; }

// User decides whether to rotate cards
const hideMoved = popups.pick(
	"Should cards be printed when drawn or picked into the card-pile",
	[ "Yes", "No" ], _inlineScripts.state.sessionState.cards.priorShowMoved ? 0 : 1,
	2);
if (hideMoved === null) { return null; }

// Pile creation shortcut is called
return expand(
	"cards pile " + toImport + " " + (isFaceUp ? "up" : "down") + " " +
	(hideMoved ? "n" : "y"));

__ ui cards pile - User enters a name, card-facing and show-moved flag for the new card-pile. Card-pile is created from the choices.

__

^ui cards? remove$

__

// User chooses the pile to remove
const pile = await userChoosesPileId("to remove", "Card-pile not removed.");
if (pile[0] == null) { return pile[1]; }

// User decides whether to rotate cards
const doRecall = popups.pick(
	"Try recalling card-pile's cards before removing it?", [ "Yes", "No" ], 0, 2);
if (doRecall === null) { return null; }

// Shuffle shortcut is called
return expand("cards remove " + pile[0] + " " + (doRecall ? "n" : "y"));

__ ui cards remove - User choses a card-pile to remove, then removes that card-pile.

__

^ui cards? pilesettings$

__

// User chooses the pile to change settings for
const pileName =
	await userChoosesPileId("to change settings for", "Card-pile not changed.");
if (pileName[0] == null) { return pileName[1]; }

// Get the pile to use it's current settings for defaults
const pile = _inlineScripts.state.sessionState.cards.piles[pileName[0]];

// User decides whether to rotate cards
const isFaceUp = popups.pick(
	"How should cards face in the card-pile?", [ "Face-down", "Face-up" ],
	pile.isFaceUp ? 1 : 0, 2);
if (isFaceUp === null) { return null; }

// User decides whether to rotate cards
const hideMoved = popups.pick(
	"Should cards be printed when drawn or picked into the card-pile",
	[ "Yes", "No" ], pile.showMoved ? 0 : 1, 2);
if (hideMoved === null) { return null; }

// Pile creation shortcut is called
return expand(
	"cards pilesettings " + pileName[0] + " " + (isFaceUp ? "up" : "down") + " " +
	(hideMoved ? "n" : "y"));

__ ui cards pilesettings - User chooses a card-pile, then choses its card-facing and show-moved flag.


__

^ui cards? fromfolder$

__

// Make a list of viable folders to choose from
const folders = Object.keys(app.vault.fileMap)
	.filter(v => app.vault.fileMap[v].children?.length)
	// Choose folders with non-markdown files
	.filter(v =>
	{
		for (const child of app.vault.fileMap[v].children)
		{
			if (child.extension && child.extension !== "md")
			{
				return true;
			}
		}
		return false;
	});

// Choose a folder to create cards from
let folder =
	popups.pick("Choose a folder to take card images from", folders, 0, "adaptive");
if (!folder) { return null; }
folder = folders[folder];

// Choose a pile to put the cards into
const pileId =
	await userChoosesPileId("to hold new cards", "Card images not loaded.");
if (pileId === null) { return pile[1]; }

return expand("cards fromfolder " + pileId[0] + " " + folder);

__ ui cards fromfolder - User choses a folder to create new cards from, then choses a card-pile to put them into. Cards are created and added to the chosen card-pile.


__

^ui cards? list$

__

return expand("cards list");

__ ui cards list - Lists all card-piles.

__

^ui cards? peek$

__

// Show a custom popup 
const data =
{
	defaults:
	{
		src: _inlineScripts.state.sessionState.cards.priorPeekPile || "",
		count: 1,
		topBottom: 0
	},
	descriptions:
	{
		src: "Card-pile to peek into",
		count: "How many cards to peek",
		topBottom: "Peek at top or bottom of card-pile"
	}
};
const result = popups.custom(
	"Choose peek options", _inlineScripts.cards_ui.manipulateCardsPopup, data);
if (result === null) { return null; }

// Run the non-ui shortcut
return expand(
	"cards peek " + result.count + " " + result.src + " " +
	(result.topBottom === "1" ? "y" : "n"));

__ ui cards peek - User choses a card-pile, how many cards to peek and what side to peek from (top or bottom). The peeked cards are printed to the note.


__

^ui cards? draw$

__

// Show a custom popup 
const data =
{
	defaults:
	{
		src: _inlineScripts.state.sessionState.cards.priorDrawSrc || "",
		dst: _inlineScripts.state.sessionState.cards.priorDrawDst || "",
		count: 1,
	},
	descriptions:
	{
		src: "Card-pile to draw from",
		dst: "Card-pile to add to",
		count: "How many cards to draw"
	}
};
const result = popups.custom(
	"Choose draw options", _inlineScripts.cards_ui.manipulateCardsPopup, data);
if (result === null) { return null; }

// Run the non-ui shortcut
return expand("cards draw " + result.count + " " + result.dst + " " + result.src);

__ ui cards draw - User choses one card-pile to draw from, one to add to and how many cards to draw. Cards are moved from the top of one card-pile to the top of the other.

__

^ui cards? pick$

__

// Show a custom popup 
const data =
{
	defaults:
	{
		src: _inlineScripts.state.sessionState.cards.priorPickSrc || "",
		dst: _inlineScripts.state.sessionState.cards.priorPickDst || ""
	},
	descriptions:
	{
		src: "Card-pile to pick from",
		dst: "Card-pile to add to"
	}
};
const result = popups.custom(
	"Choose pick options", _inlineScripts.cards_ui.manipulateCardsPopup, data);
if (result === null) { return null; }

// Run the non-ui shortcut
return expand("cards pick " + result.dst + " " + result.src);

__ ui cards pick - User choses one card-pile to pick from and one to add to, then the user picks the cards to move from the top of one to the top of the other.


__

^ui cards? shuffle$

__

// Show a custom popup 
const data =
{
	defaults:
	{
		src: 0,
		rotate: false
	},
	descriptions:
	{
		src: "Card-pile to shuffle",
		rotate: "Rotate cards while shuffling?"
	}
};
const result = popups.custom(
	"Choose pick options", _inlineScripts.cards_ui.manipulateCardsPopup, data);
if (result === null) { return null; }

// Run the non-ui shortcut
return expand("cards shuffle " + result.src + " " + (result.rotate ? "y" : "n"));

__ ui cards shuffle - User choses a card-pile, and whether to rotate the cards while shuffling. The card-pile is then shuffled.

__

^ui cards? unrotate$

__

const pile = await userChoosesPileId("to un-rotate", "Cards not un-rotated.");
if (pile[0] == null) { return pile[1]; }
return expand("cards unrotate " + pile[0]);

__ ui cards unrotate - User choses a card-pile, then all the card-pile's cards are turned right-side-up.

__

^ui cards? reverse$

__

const pile = await userChoosesPileId("to reverse", "Card-pile not reversed.");
if (pile[0] == null) { return pile[1]; }
return expand("cards reverse " + pile[0]);

__ ui cards reverse - User choses a card-pile, then the card-pile's order is reversed.

__

^ui cards? recall$

__

const pile = await userChoosesPileId("to recall", "Cards not recalled.");
if (pile[0] == null) { return pile[1]; }
return expand("cards recall " + pile[0]);

__ ui cards recall - User choses a card-pile, then the card-pile is recalled. Recalling means finding all cards with the card-pile as their origin, then moving them to the card-pile.

__

^ui cards? reorigin$

__

const pile = await userChoosesPileId("to re-origin", "Cards not re-origined.");
if (pile[0] == null) { return pile[1]; }
return expand("cards reorigin " + pile[0]);

__ ui cards reorigin - Asks the user to choose a card-pile, then the card-pile is re-origined.. Re-origining means setting the origin of all cards in a card-pile to that card-pile.


__

^ui cards? import$

__

// Get the pile to import into
let pile =
	await userChoosesPileId("to import into", "Card-pile not imported.");
if (pile[0] == null) { return pile[1]; }
pile = pile[0];

// Get the pile's data-string for the default value
let defaultDataString =
	JSON.stringify(_inlineScripts.state.sessionState.cards.piles[pile]);

// Get the data-string to import
const toImport = popups.input("Enter the data-string to import", defaultDataString);
if (toImport === null) { return null; }
if (toImport === defaultDataString) { return null; }

// Run the import shortcut
return expand("cards reorigin " + pile + " " + toImport);

__ ui cards import - User chooses a card-pile and enters a data-string representation of a card-pile. The data-string is turned into a card-pile and assigned to the card-pile the user chose.

__

^ui cards? export$

__

const pile = await userChoosesPileId("to export", "Card-pile not exported.");
if (pile[0] == null) { return pile[1]; }
return expand("cards export " + pile[0]);

__ ui cards export - User chooses a card-pile, then the card-pile is exported to a data-string, which is printed to the note.