parent
							
								
									9d405b4581
								
							
						
					
					
						commit
						31aa008566
					
				
					 8 changed files with 193 additions and 433 deletions
				
			
		|  | @ -77,9 +77,57 @@ export default defineComponent({ | |||
| 					}, genEl(token.children)); | ||||
| 				} | ||||
| 
 | ||||
| 				case 'big': { | ||||
| 					return h('strong', { | ||||
| 						style: `display: inline-block; font-size: 150%;` + (this.$store.state.device.animatedMfm ? 'animation: anime-tada 1s linear infinite both;' : ''), | ||||
| 				case 'fn': { | ||||
| 					// TODO: CSSを文字列で組み立てていくと token.node.props.args.~~~ 経由でCSSインジェクションできるのでよしなにやる
 | ||||
| 					let style; | ||||
| 					switch (token.node.props.name) { | ||||
| 						case 'tada': { | ||||
| 							style = `font-size: 150%;` + (this.$store.state.device.animatedMfm ? 'animation: tada 1s linear infinite both;' : ''); | ||||
| 							break; | ||||
| 						} | ||||
| 						case 'jelly': { | ||||
| 							const speed = token.node.props.args.speed || '1s'; | ||||
| 							style = (this.$store.state.device.animatedMfm ? `animation: mfm-rubberBand ${speed} linear infinite both;` : ''); | ||||
| 							break; | ||||
| 						} | ||||
| 						case 'twitch': { | ||||
| 							const speed = token.node.props.args.speed || '0.5s'; | ||||
| 							style = this.$store.state.device.animatedMfm ? `animation: mfm-twitch ${speed} ease infinite;` : ''; | ||||
| 							break; | ||||
| 						} | ||||
| 						case 'shake': { | ||||
| 							const speed = token.node.props.args.speed || '0.5s'; | ||||
| 							style = this.$store.state.device.animatedMfm ? `animation: mfm-shake ${speed} ease infinite;` : ''; | ||||
| 							break; | ||||
| 						} | ||||
| 						case 'spin': { | ||||
| 							const direction = | ||||
| 								token.node.props.args.left ? 'reverse' : | ||||
| 								token.node.props.args.alternate ? 'alternate' : | ||||
| 								'normal'; | ||||
| 							const anime = | ||||
| 								token.node.props.args.x ? 'mfm-spinX' : | ||||
| 								token.node.props.args.y ? 'mfm-spinY' : | ||||
| 								'mfm-spin'; | ||||
| 							const speed = token.node.props.args.speed || '1.5s'; | ||||
| 							style = this.$store.state.device.animatedMfm ? `animation: ${anime} ${speed} linear infinite; animation-direction: ${direction};` : ''; | ||||
| 							break; | ||||
| 						} | ||||
| 						case 'jump': { | ||||
| 							style = this.$store.state.device.animatedMfm ? 'animation: mfm-jump 0.75s linear infinite;' : ''; | ||||
| 							break; | ||||
| 						} | ||||
| 						case 'flip': { | ||||
| 							const transform = | ||||
| 								(token.node.props.args.h && token.node.props.args.v) ? 'scale(-1, -1)' : | ||||
| 								token.node.props.args.v ? 'scaleY(-1)' : | ||||
| 								'scaleX(-1)'; | ||||
| 							style = `transform: ${transform};`; | ||||
| 							break; | ||||
| 						} | ||||
| 					} | ||||
| 					return h('span', { | ||||
| 						style: 'display: inline-block;' + style, | ||||
| 					}, genEl(token.children)); | ||||
| 				} | ||||
| 
 | ||||
|  | @ -95,48 +143,6 @@ export default defineComponent({ | |||
| 					}, genEl(token.children))]; | ||||
| 				} | ||||
| 
 | ||||
| 				case 'motion': { | ||||
| 					return h('span', { | ||||
| 						style: 'display: inline-block;' + (this.$store.state.device.animatedMfm ? 'animation: anime-rubberBand 1s linear infinite both;' : ''), | ||||
| 					}, genEl(token.children)); | ||||
| 				} | ||||
| 
 | ||||
| 				case 'spin': { | ||||
| 					const direction = | ||||
| 						token.node.props.attr == 'left' ? 'reverse' : | ||||
| 						token.node.props.attr == 'alternate' ? 'alternate' : | ||||
| 						'normal'; | ||||
| 					const style = this.$store.state.device.animatedMfm | ||||
| 						? `animation: anime-spin 1.5s linear infinite; animation-direction: ${direction};` : ''; | ||||
| 					return h('span', { | ||||
| 						style: 'display: inline-block;' + style | ||||
| 					}, genEl(token.children)); | ||||
| 				} | ||||
| 
 | ||||
| 				case 'jump': { | ||||
| 					return h('span', { | ||||
| 						style: this.$store.state.device.animatedMfm ? 'display: inline-block; animation: anime-jump 0.75s linear infinite;' : 'display: inline-block;' | ||||
| 					}, genEl(token.children)); | ||||
| 				} | ||||
| 
 | ||||
| 				case 'flip': { | ||||
| 					return h('span', { | ||||
| 						style: 'display: inline-block; transform: scaleX(-1);' | ||||
| 					}, genEl(token.children)); | ||||
| 				} | ||||
| 
 | ||||
| 				case 'twitch': { | ||||
| 					return h('span', { | ||||
| 						style: this.$store.state.device.animatedMfm ? 'display: inline-block; animation: anime-twitch 0.5s ease infinite;' : 'display: inline-block;' | ||||
| 					}, genEl(token.children)); | ||||
| 				} | ||||
| 
 | ||||
| 				case 'shake': { | ||||
| 					return h('span', { | ||||
| 						style: this.$store.state.device.animatedMfm ? 'display: inline-block; animation: anime-shake 0.5s ease infinite;' : 'display: inline-block;' | ||||
| 					}, genEl(token.children)); | ||||
| 				} | ||||
| 
 | ||||
| 				case 'url': { | ||||
| 					return [h(MkUrl, { | ||||
| 						key: Math.random(), | ||||
|  | @ -198,12 +204,6 @@ export default defineComponent({ | |||
| 					} | ||||
| 				} | ||||
| 
 | ||||
| 				case 'title': { | ||||
| 					return [h('div', { | ||||
| 						class: 'title' | ||||
| 					}, genEl(token.children))]; | ||||
| 				} | ||||
| 
 | ||||
| 				case 'emoji': { | ||||
| 					return [h(MkEmoji, { | ||||
| 						key: Math.random(), | ||||
|  |  | |||
|  | @ -13,6 +13,115 @@ export default defineComponent({ | |||
| }); | ||||
| </script> | ||||
| 
 | ||||
| <style lang="scss"> | ||||
| @keyframes mfm-spin { | ||||
| 	0% { transform: rotate(0deg); } | ||||
| 	100% { transform: rotate(360deg); } | ||||
| } | ||||
| 
 | ||||
| @keyframes mfm-spinX { | ||||
| 	0% { transform: perspective(128px) rotateX(0deg); } | ||||
| 	100% { transform: perspective(128px) rotateX(360deg); } | ||||
| } | ||||
| 
 | ||||
| @keyframes mfm-spinY { | ||||
| 	0% { transform: perspective(128px) rotateY(0deg); } | ||||
| 	100% { transform: perspective(128px) rotateY(360deg); } | ||||
| } | ||||
| 
 | ||||
| @keyframes mfm-jump { | ||||
| 	0% { transform: translateY(0); } | ||||
| 	25% { transform: translateY(-16px); } | ||||
| 	50% { transform: translateY(0); } | ||||
| 	75% { transform: translateY(-8px); } | ||||
| 	100% { transform: translateY(0); } | ||||
| } | ||||
| 
 | ||||
| // const val = () => `translate(${Math.floor(Math.random() * 20) - 10}px, ${Math.floor(Math.random() * 20) - 10}px)`; | ||||
| // let css = ''; | ||||
| // for (let i = 0; i <= 100; i += 5) { css += `${i}% { transform: ${val()} }\n`; } | ||||
| @keyframes mfm-twitch { | ||||
| 	0% { transform: translate(7px, -2px) } | ||||
| 	5% { transform: translate(-3px, 1px) } | ||||
| 	10% { transform: translate(-7px, -1px) } | ||||
| 	15% { transform: translate(0px, -1px) } | ||||
| 	20% { transform: translate(-8px, 6px) } | ||||
| 	25% { transform: translate(-4px, -3px) } | ||||
| 	30% { transform: translate(-4px, -6px) } | ||||
| 	35% { transform: translate(-8px, -8px) } | ||||
| 	40% { transform: translate(4px, 6px) } | ||||
| 	45% { transform: translate(-3px, 1px) } | ||||
| 	50% { transform: translate(2px, -10px) } | ||||
| 	55% { transform: translate(-7px, 0px) } | ||||
| 	60% { transform: translate(-2px, 4px) } | ||||
| 	65% { transform: translate(3px, -8px) } | ||||
| 	70% { transform: translate(6px, 7px) } | ||||
| 	75% { transform: translate(-7px, -2px) } | ||||
| 	80% { transform: translate(-7px, -8px) } | ||||
| 	85% { transform: translate(9px, 3px) } | ||||
| 	90% { transform: translate(-3px, -2px) } | ||||
| 	95% { transform: translate(-10px, 2px) } | ||||
| 	100% { transform: translate(-2px, -6px) } | ||||
| } | ||||
| 
 | ||||
| // const val = () => `translate(${Math.floor(Math.random() * 6) - 3}px, ${Math.floor(Math.random() * 6) - 3}px) rotate(${Math.floor(Math.random() * 24) - 12}deg)`; | ||||
| // let css = ''; | ||||
| // for (let i = 0; i <= 100; i += 5) { css += `${i}% { transform: ${val()} }\n`; } | ||||
| @keyframes mfm-shake { | ||||
| 	0% { transform: translate(-3px, -1px) rotate(-8deg) } | ||||
| 	5% { transform: translate(0px, -1px) rotate(-10deg) } | ||||
| 	10% { transform: translate(1px, -3px) rotate(0deg) } | ||||
| 	15% { transform: translate(1px, 1px) rotate(11deg) } | ||||
| 	20% { transform: translate(-2px, 1px) rotate(1deg) } | ||||
| 	25% { transform: translate(-1px, -2px) rotate(-2deg) } | ||||
| 	30% { transform: translate(-1px, 2px) rotate(-3deg) } | ||||
| 	35% { transform: translate(2px, 1px) rotate(6deg) } | ||||
| 	40% { transform: translate(-2px, -3px) rotate(-9deg) } | ||||
| 	45% { transform: translate(0px, -1px) rotate(-12deg) } | ||||
| 	50% { transform: translate(1px, 2px) rotate(10deg) } | ||||
| 	55% { transform: translate(0px, -3px) rotate(8deg) } | ||||
| 	60% { transform: translate(1px, -1px) rotate(8deg) } | ||||
| 	65% { transform: translate(0px, -1px) rotate(-7deg) } | ||||
| 	70% { transform: translate(-1px, -3px) rotate(6deg) } | ||||
| 	75% { transform: translate(0px, -2px) rotate(4deg) } | ||||
| 	80% { transform: translate(-2px, -1px) rotate(3deg) } | ||||
| 	85% { transform: translate(1px, -3px) rotate(-10deg) } | ||||
| 	90% { transform: translate(1px, 0px) rotate(3deg) } | ||||
| 	95% { transform: translate(-2px, 0px) rotate(-3deg) } | ||||
| 	100% { transform: translate(2px, 1px) rotate(2deg) } | ||||
| } | ||||
| 
 | ||||
| @keyframes mfm-rubberBand { | ||||
|   from { | ||||
|     transform: scale3d(1, 1, 1); | ||||
|   } | ||||
| 
 | ||||
|   30% { | ||||
|     transform: scale3d(1.25, 0.75, 1); | ||||
|   } | ||||
| 
 | ||||
|   40% { | ||||
|     transform: scale3d(0.75, 1.25, 1); | ||||
|   } | ||||
| 
 | ||||
|   50% { | ||||
|     transform: scale3d(1.15, 0.85, 1); | ||||
|   } | ||||
| 
 | ||||
|   65% { | ||||
|     transform: scale3d(0.95, 1.05, 1); | ||||
|   } | ||||
| 
 | ||||
|   75% { | ||||
|     transform: scale3d(1.05, 0.95, 1); | ||||
|   } | ||||
| 
 | ||||
|   to { | ||||
|     transform: scale3d(1, 1, 1); | ||||
|   } | ||||
| } | ||||
| </style> | ||||
| 
 | ||||
| <style lang="scss" scoped> | ||||
| .havbbuyv { | ||||
| 	white-space: pre-wrap; | ||||
|  | @ -42,10 +151,5 @@ export default defineComponent({ | |||
| 		word-break: break-all; | ||||
| 		padding: 4px 6px; | ||||
| 	} | ||||
| 
 | ||||
| 	::v-deep(.title) { | ||||
| 		text-align: center; | ||||
| 		border-bottom: solid 1px var(--divider); | ||||
| 	} | ||||
| } | ||||
| </style> | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ | |||
| 			<Mfm :key="'past-turn-of:' + turnUser().name" :text="$t('_reversi.pastTurnOf', { name: turnUser().name })" :plain="true" :custom-emojis="turnUser().emojis"/> | ||||
| 		</p> | ||||
| 		<p class="turn1" v-if="iAmPlayer && !game.isEnded && !isMyTurn()">{{ $t('_reversi.opponentTurn') }}<MkEllipsis/></p> | ||||
| 		<p class="turn2" v-if="iAmPlayer && !game.isEnded && isMyTurn()" style="animation: anime-tada 1s linear infinite both;">{{ $t('_reversi.myTurn') }}</p> | ||||
| 		<p class="turn2" v-if="iAmPlayer && !game.isEnded && isMyTurn()" style="animation: tada 1s linear infinite both;">{{ $t('_reversi.myTurn') }}</p> | ||||
| 		<p class="result" v-if="game.isEnded && logPos == logs.length"> | ||||
| 			<template v-if="game.winner"> | ||||
| 				<Mfm :key="'won'" :text="$t('_reversi.won', { name: game.winner.name })" :plain="true" :custom-emojis="game.winner.emojis"/> | ||||
|  |  | |||
|  | @ -486,74 +486,7 @@ hr { | |||
| 	90% { opacity: 0; transform: scale(0.5); } | ||||
| } | ||||
| 
 | ||||
| @keyframes anime-spin { | ||||
| 	0% { transform: rotate(0deg); } | ||||
| 	100% { transform: rotate(360deg); } | ||||
| } | ||||
| 
 | ||||
| @keyframes anime-jump { | ||||
| 	0% { transform: translateY(0); } | ||||
| 	25% { transform: translateY(-16px); } | ||||
| 	50% { transform: translateY(0); } | ||||
| 	75% { transform: translateY(-8px); } | ||||
| 	100% { transform: translateY(0); } | ||||
| } | ||||
| 
 | ||||
| // const val = () => `translate(${Math.floor(Math.random() * 20) - 10}px, ${Math.floor(Math.random() * 20) - 10}px)`; | ||||
| // let css = ''; | ||||
| // for (let i = 0; i <= 100; i += 5) { css += `${i}% { transform: ${val()} }\n`; } | ||||
| @keyframes anime-twitch { | ||||
| 	0% { transform: translate(7px, -2px) } | ||||
| 	5% { transform: translate(-3px, 1px) } | ||||
| 	10% { transform: translate(-7px, -1px) } | ||||
| 	15% { transform: translate(0px, -1px) } | ||||
| 	20% { transform: translate(-8px, 6px) } | ||||
| 	25% { transform: translate(-4px, -3px) } | ||||
| 	30% { transform: translate(-4px, -6px) } | ||||
| 	35% { transform: translate(-8px, -8px) } | ||||
| 	40% { transform: translate(4px, 6px) } | ||||
| 	45% { transform: translate(-3px, 1px) } | ||||
| 	50% { transform: translate(2px, -10px) } | ||||
| 	55% { transform: translate(-7px, 0px) } | ||||
| 	60% { transform: translate(-2px, 4px) } | ||||
| 	65% { transform: translate(3px, -8px) } | ||||
| 	70% { transform: translate(6px, 7px) } | ||||
| 	75% { transform: translate(-7px, -2px) } | ||||
| 	80% { transform: translate(-7px, -8px) } | ||||
| 	85% { transform: translate(9px, 3px) } | ||||
| 	90% { transform: translate(-3px, -2px) } | ||||
| 	95% { transform: translate(-10px, 2px) } | ||||
| 	100% { transform: translate(-2px, -6px) } | ||||
| } | ||||
| 
 | ||||
| // const val = () => `translate(${Math.floor(Math.random() * 6) - 3}px, ${Math.floor(Math.random() * 6) - 3}px) rotate(${Math.floor(Math.random() * 24) - 12}deg)`; | ||||
| // let css = ''; | ||||
| // for (let i = 0; i <= 100; i += 5) { css += `${i}% { transform: ${val()} }\n`; } | ||||
| @keyframes anime-shake { | ||||
| 	0% { transform: translate(-3px, -1px) rotate(-8deg) } | ||||
| 	5% { transform: translate(0px, -1px) rotate(-10deg) } | ||||
| 	10% { transform: translate(1px, -3px) rotate(0deg) } | ||||
| 	15% { transform: translate(1px, 1px) rotate(11deg) } | ||||
| 	20% { transform: translate(-2px, 1px) rotate(1deg) } | ||||
| 	25% { transform: translate(-1px, -2px) rotate(-2deg) } | ||||
| 	30% { transform: translate(-1px, 2px) rotate(-3deg) } | ||||
| 	35% { transform: translate(2px, 1px) rotate(6deg) } | ||||
| 	40% { transform: translate(-2px, -3px) rotate(-9deg) } | ||||
| 	45% { transform: translate(0px, -1px) rotate(-12deg) } | ||||
| 	50% { transform: translate(1px, 2px) rotate(10deg) } | ||||
| 	55% { transform: translate(0px, -3px) rotate(8deg) } | ||||
| 	60% { transform: translate(1px, -1px) rotate(8deg) } | ||||
| 	65% { transform: translate(0px, -1px) rotate(-7deg) } | ||||
| 	70% { transform: translate(-1px, -3px) rotate(6deg) } | ||||
| 	75% { transform: translate(0px, -2px) rotate(4deg) } | ||||
| 	80% { transform: translate(-2px, -1px) rotate(3deg) } | ||||
| 	85% { transform: translate(1px, -3px) rotate(-10deg) } | ||||
| 	90% { transform: translate(1px, 0px) rotate(3deg) } | ||||
| 	95% { transform: translate(-2px, 0px) rotate(-3deg) } | ||||
| 	100% { transform: translate(2px, 1px) rotate(2deg) } | ||||
| } | ||||
| 
 | ||||
| @keyframes anime-tada { | ||||
| @keyframes tada { | ||||
| 	from { | ||||
| 		transform: scale3d(1, 1, 1); | ||||
| 	} | ||||
|  | @ -580,33 +513,3 @@ hr { | |||
| 		transform: scale3d(1, 1, 1); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @keyframes anime-rubberBand { | ||||
|   from { | ||||
|     transform: scale3d(1, 1, 1); | ||||
|   } | ||||
| 
 | ||||
|   30% { | ||||
|     transform: scale3d(1.25, 0.75, 1); | ||||
|   } | ||||
| 
 | ||||
|   40% { | ||||
|     transform: scale3d(0.75, 1.25, 1); | ||||
|   } | ||||
| 
 | ||||
|   50% { | ||||
|     transform: scale3d(1.15, 0.85, 1); | ||||
|   } | ||||
| 
 | ||||
|   65% { | ||||
|     transform: scale3d(0.95, 1.05, 1); | ||||
|   } | ||||
| 
 | ||||
|   75% { | ||||
|     transform: scale3d(1.05, 0.95, 1); | ||||
|   } | ||||
| 
 | ||||
|   to { | ||||
|     transform: scale3d(1, 1, 1); | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -23,7 +23,6 @@ export const mfmLanguage = P.createLanguage({ | |||
| 	root: r => P.alt(r.block, r.inline).atLeast(1), | ||||
| 	plain: r => P.alt(r.emoji, r.text).atLeast(1), | ||||
| 	block: r => P.alt( | ||||
| 		r.title, | ||||
| 		r.quote, | ||||
| 		r.search, | ||||
| 		r.blockCode, | ||||
|  | @ -37,14 +36,6 @@ export const mfmLanguage = P.createLanguage({ | |||
| 			return P.makeFailure(i, 'not newline'); | ||||
| 		} | ||||
| 	}), | ||||
| 	title: r => r.startOfLine.then(P((input, i) => { | ||||
| 		const text = input.substr(i); | ||||
| 		const match = text.match(/^([【\[]([^【\[】\]\n]+?)[】\]])(\n|$)/); | ||||
| 		if (!match) return P.makeFailure(i, 'not a title'); | ||||
| 		const q = match[2].trim(); | ||||
| 		const contents = r.inline.atLeast(1).tryParse(q); | ||||
| 		return P.makeSuccess(i + match[0].length, createTree('title', contents, {})); | ||||
| 	})), | ||||
| 	quote: r => r.startOfLine.then(P((input, i) => { | ||||
| 		const text = input.substr(i); | ||||
| 		if (!text.match(/^>[\s\S]+?/)) return P.makeFailure(i, 'not a quote'); | ||||
|  | @ -67,17 +58,10 @@ export const mfmLanguage = P.createLanguage({ | |||
| 		return P.makeSuccess(i + match[0].length, createLeaf('blockCode', { code: match[2], lang: match[1] ? match[1].trim() : null })); | ||||
| 	})), | ||||
| 	inline: r => P.alt( | ||||
| 		r.big, | ||||
| 		r.bold, | ||||
| 		r.small, | ||||
| 		r.italic, | ||||
| 		r.strike, | ||||
| 		r.motion, | ||||
| 		r.spin, | ||||
| 		r.jump, | ||||
| 		r.flip, | ||||
| 		r.twitch, | ||||
| 		r.shake, | ||||
| 		r.inlineCode, | ||||
| 		r.mathInline, | ||||
| 		r.mention, | ||||
|  | @ -85,9 +69,9 @@ export const mfmLanguage = P.createLanguage({ | |||
| 		r.url, | ||||
| 		r.link, | ||||
| 		r.emoji, | ||||
| 		r.fn, | ||||
| 		r.text | ||||
| 	), | ||||
| 	big: r => P.regexp(/^\*\*\*([\s\S]+?)\*\*\*/, 1).map(x => createTree('big', r.inline.atLeast(1).tryParse(x), {})), | ||||
| 	bold: r => { | ||||
| 		const asterisk = P.regexp(/\*\*([\s\S]+?)\*\*/, 1); | ||||
| 		const underscore = P.regexp(/__([a-zA-Z0-9\s]+?)__/, 1); | ||||
|  | @ -107,25 +91,6 @@ export const mfmLanguage = P.createLanguage({ | |||
| 		return P.alt(xml, underscore).map(x => createTree('italic', r.inline.atLeast(1).tryParse(x), {})); | ||||
| 	}, | ||||
| 	strike: r => P.regexp(/~~([^\n~]+?)~~/, 1).map(x => createTree('strike', r.inline.atLeast(1).tryParse(x), {})), | ||||
| 	motion: r => { | ||||
| 		const paren = P.regexp(/\(\(\(([\s\S]+?)\)\)\)/, 1); | ||||
| 		const xml = P.regexp(/<motion>(.+?)<\/motion>/, 1); | ||||
| 		return P.alt(paren, xml).map(x => createTree('motion', r.inline.atLeast(1).tryParse(x), {})); | ||||
| 	}, | ||||
| 	spin: r => { | ||||
| 		return P((input, i) => { | ||||
| 			const text = input.substr(i); | ||||
| 			const match = text.match(/^<spin(\s[a-z]+?)?>(.+?)<\/spin>/i); | ||||
| 			if (!match) return P.makeFailure(i, 'not a spin'); | ||||
| 			return P.makeSuccess(i + match[0].length, { | ||||
| 				content: match[2], attr: match[1] ? match[1].trim() : null | ||||
| 			}); | ||||
| 		}).map(x => createTree('spin', r.inline.atLeast(1).tryParse(x.content), { attr: x.attr })); | ||||
| 	}, | ||||
| 	jump: r => P.regexp(/<jump>(.+?)<\/jump>/, 1).map(x => createTree('jump', r.inline.atLeast(1).tryParse(x), {})), | ||||
| 	flip: r => P.regexp(/<flip>(.+?)<\/flip>/, 1).map(x => createTree('flip', r.inline.atLeast(1).tryParse(x), {})), | ||||
| 	twitch: r => P.regexp(/<twitch>(.+?)<\/twitch>/, 1).map(x => createTree('twitch', r.inline.atLeast(1).tryParse(x), {})), | ||||
| 	shake: r => P.regexp(/<shake>(.+?)<\/shake>/, 1).map(x => createTree('shake', r.inline.atLeast(1).tryParse(x), {})), | ||||
| 	center: r => r.startOfLine.then(P.regexp(/<center>([\s\S]+?)<\/center>/, 1).map(x => createTree('center', r.inline.atLeast(1).tryParse(x), {}))), | ||||
| 	inlineCode: () => P.regexp(/`([^´\n]+?)`/, 1).map(x => createLeaf('inlineCode', { code: x })), | ||||
| 	mathBlock: r => r.startOfLine.then(P.regexp(/\\\[([\s\S]+?)\\\]/, 1).map(x => createLeaf('mathBlock', { formula: x.trim() }))), | ||||
|  | @ -192,5 +157,29 @@ export const mfmLanguage = P.createLanguage({ | |||
| 		const code = P.regexp(emojiRegex).map(x => createLeaf('emoji', { emoji: x })); | ||||
| 		return P.alt(name, code); | ||||
| 	}, | ||||
| 	fn: r => { | ||||
| 		return P.seqObj( | ||||
| 			P.string('['), ['fn', P.regexp(/[^\s\n\[\]]+/)] as any, P.string(' '), P.optWhitespace, ['text', P.regexp(/[^\n\[\]]+/)] as any, P.string(']'), | ||||
| 		).map((x: any) => { | ||||
| 			let name = x.fn; | ||||
| 			const args = {}; | ||||
| 			const separator = x.fn.indexOf('.'); | ||||
| 			if (separator > -1) { | ||||
| 				name = x.fn.substr(0, separator); | ||||
| 				for (const arg of x.fn.substr(separator + 1).split(',')) { | ||||
| 					const kv = arg.split('='); | ||||
| 					if (kv.length === 1) { | ||||
| 						args[kv[0]] = true; | ||||
| 					} else { | ||||
| 						args[kv[0]] = kv[1]; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			return createTree('fn', r.inline.atLeast(1).tryParse(x.text), { | ||||
| 				name, | ||||
| 				args | ||||
| 			}); | ||||
| 		}); | ||||
| 	}, | ||||
| 	text: () => P.any.map(x => createLeaf('text', { text: x })) | ||||
| }); | ||||
|  |  | |||
|  | @ -25,12 +25,6 @@ export function toHtml(tokens: MfmForest | null, mentionedRemoteUsers: IMentione | |||
| 			return el; | ||||
| 		}, | ||||
| 
 | ||||
| 		big(token) { | ||||
| 			const el = doc.createElement('strong'); | ||||
| 			appendChildren(token.children, el); | ||||
| 			return el; | ||||
| 		}, | ||||
| 
 | ||||
| 		small(token) { | ||||
| 			const el = doc.createElement('small'); | ||||
| 			appendChildren(token.children, el); | ||||
|  | @ -49,42 +43,12 @@ export function toHtml(tokens: MfmForest | null, mentionedRemoteUsers: IMentione | |||
| 			return el; | ||||
| 		}, | ||||
| 
 | ||||
| 		motion(token) { | ||||
| 		fn(token) { | ||||
| 			const el = doc.createElement('i'); | ||||
| 			appendChildren(token.children, el); | ||||
| 			return el; | ||||
| 		}, | ||||
| 
 | ||||
| 		spin(token) { | ||||
| 			const el = doc.createElement('i'); | ||||
| 			appendChildren(token.children, el); | ||||
| 			return el; | ||||
| 		}, | ||||
| 
 | ||||
| 		jump(token) { | ||||
| 			const el = doc.createElement('i'); | ||||
| 			appendChildren(token.children, el); | ||||
| 			return el; | ||||
| 		}, | ||||
| 
 | ||||
| 		twitch(token) { | ||||
| 			const el = doc.createElement('i'); | ||||
| 			appendChildren(token.children, el); | ||||
| 			return el; | ||||
| 		}, | ||||
| 
 | ||||
| 		shake(token) { | ||||
| 			const el = doc.createElement('i'); | ||||
| 			appendChildren(token.children, el); | ||||
| 			return el; | ||||
| 		}, | ||||
| 
 | ||||
| 		flip(token) { | ||||
| 			const el = doc.createElement('span'); | ||||
| 			appendChildren(token.children, el); | ||||
| 			return el; | ||||
| 		}, | ||||
| 
 | ||||
| 		blockCode(token) { | ||||
| 			const pre = doc.createElement('pre'); | ||||
| 			const inner = doc.createElement('code'); | ||||
|  | @ -157,12 +121,6 @@ export function toHtml(tokens: MfmForest | null, mentionedRemoteUsers: IMentione | |||
| 			return el; | ||||
| 		}, | ||||
| 
 | ||||
| 		title(token) { | ||||
| 			const el = doc.createElement('h1'); | ||||
| 			appendChildren(token.children, el); | ||||
| 			return el; | ||||
| 		}, | ||||
| 
 | ||||
| 		text(token) { | ||||
| 			const el = doc.createElement('span'); | ||||
| 			const nodes = (token.node.props.text as string).split(/\r\n|\r|\n/).map(x => doc.createTextNode(x) as Node); | ||||
|  |  | |||
|  | @ -18,10 +18,6 @@ export function toString(tokens: MfmForest | null, opts?: RestoreOptions): strin | |||
| 			return `**${appendChildren(token.children, opts)}**`; | ||||
| 		}, | ||||
| 
 | ||||
| 		big(token, opts) { | ||||
| 			return `***${appendChildren(token.children, opts)}***`; | ||||
| 		}, | ||||
| 
 | ||||
| 		small(token, opts) { | ||||
| 			return `<small>${appendChildren(token.children, opts)}</small>`; | ||||
| 		}, | ||||
|  | @ -34,30 +30,11 @@ export function toString(tokens: MfmForest | null, opts?: RestoreOptions): strin | |||
| 			return `<i>${appendChildren(token.children, opts)}</i>`; | ||||
| 		}, | ||||
| 
 | ||||
| 		motion(token, opts) { | ||||
| 			return `<motion>${appendChildren(token.children, opts)}</motion>`; | ||||
| 		}, | ||||
| 
 | ||||
| 		spin(token, opts) { | ||||
| 			const attr = token.node.props?.attr; | ||||
| 			const post = attr ? ` ${attr}` : ''; | ||||
| 			return `<spin${post}>${appendChildren(token.children, opts)}</spin>`; | ||||
| 		}, | ||||
| 
 | ||||
| 		jump(token, opts) { | ||||
| 			return `<jump>${appendChildren(token.children, opts)}</jump>`; | ||||
| 		}, | ||||
| 
 | ||||
| 		twitch(token, opts) { | ||||
| 			return `<twitch>${appendChildren(token.children, opts)}</twitch>`; | ||||
| 		}, | ||||
| 
 | ||||
| 		shake(token, opts) { | ||||
| 			return `<shake>${appendChildren(token.children, opts)}</shake>`; | ||||
| 		}, | ||||
| 
 | ||||
| 		flip(token, opts) { | ||||
| 			return `<flip>${appendChildren(token.children, opts)}</flip>`; | ||||
| 		fn(token, opts) { | ||||
| 			const name = token.node.props?.name; | ||||
| 			const args = token.node.props?.args || {}; | ||||
| 			const argsStr = Object.entries(args).map(([k, v]) => v === true ? k : `${k}=${v}`).join(','); | ||||
| 			return `[${name}${argsStr !== '' ? '.' + argsStr : ''} ${appendChildren(token.children, opts)}]`; | ||||
| 		}, | ||||
| 
 | ||||
| 		blockCode(token) { | ||||
|  | @ -104,10 +81,6 @@ export function toString(tokens: MfmForest | null, opts?: RestoreOptions): strin | |||
| 			return `${appendChildren(token.children, {doNyaize: false}).replace(/^/gm,'>').trim()}\n`; | ||||
| 		}, | ||||
| 
 | ||||
| 		title(token, opts) { | ||||
| 			return `[${appendChildren(token.children, opts)}]\n`; | ||||
| 		}, | ||||
| 
 | ||||
| 		text(token, opts) { | ||||
| 			return (opts && opts.doNyaize) ? nyaize(token.node.props.text) : token.node.props.text; | ||||
| 		}, | ||||
|  |  | |||
							
								
								
									
										167
									
								
								test/mfm.ts
									
										
									
									
									
								
							
							
						
						
									
										167
									
								
								test/mfm.ts
									
										
									
									
									
								
							|  | @ -227,16 +227,6 @@ describe('MFM', () => { | |||
| 			}); | ||||
| 		}); | ||||
| 
 | ||||
| 		it('big', () => { | ||||
| 			const tokens = parse('***Strawberry*** Pasta'); | ||||
| 			assert.deepStrictEqual(tokens, [ | ||||
| 				tree('big', [ | ||||
| 					text('Strawberry') | ||||
| 				], {}), | ||||
| 				text(' Pasta'), | ||||
| 			]); | ||||
| 		}); | ||||
| 
 | ||||
| 		it('small', () => { | ||||
| 			const tokens = parse('<small>smaller</small>'); | ||||
| 			assert.deepStrictEqual(tokens, [ | ||||
|  | @ -246,133 +236,6 @@ describe('MFM', () => { | |||
| 			]); | ||||
| 		}); | ||||
| 
 | ||||
| 		it('flip', () => { | ||||
| 			const tokens = parse('<flip>foo</flip>'); | ||||
| 			assert.deepStrictEqual(tokens, [ | ||||
| 				tree('flip', [ | ||||
| 					text('foo') | ||||
| 				], {}), | ||||
| 			]); | ||||
| 		}); | ||||
| 
 | ||||
| 		describe('spin', () => { | ||||
| 			it('text', () => { | ||||
| 				const tokens = parse('<spin>foo</spin>'); | ||||
| 				assert.deepStrictEqual(tokens, [ | ||||
| 					tree('spin', [ | ||||
| 						text('foo') | ||||
| 					], { | ||||
| 						attr: null | ||||
| 					}), | ||||
| 				]); | ||||
| 			}); | ||||
| 
 | ||||
| 			it('emoji', () => { | ||||
| 				const tokens = parse('<spin>:foo:</spin>'); | ||||
| 				assert.deepStrictEqual(tokens, [ | ||||
| 					tree('spin', [ | ||||
| 						leaf('emoji', { name: 'foo' }) | ||||
| 					], { | ||||
| 						attr: null | ||||
| 					}), | ||||
| 				]); | ||||
| 			}); | ||||
| 
 | ||||
| 			it('with attr', () => { | ||||
| 				const tokens = parse('<spin left>:foo:</spin>'); | ||||
| 				assert.deepStrictEqual(tokens, [ | ||||
| 					tree('spin', [ | ||||
| 						leaf('emoji', { name: 'foo' }) | ||||
| 					], { | ||||
| 						attr: 'left' | ||||
| 					}), | ||||
| 				]); | ||||
| 			}); | ||||
| /* | ||||
| 			it('multi', () => { | ||||
| 				const tokens = parse('<spin>:foo:</spin><spin>:foo:</spin>'); | ||||
| 				assert.deepStrictEqual(tokens, [ | ||||
| 					tree('spin', [ | ||||
| 						leaf('emoji', { name: 'foo' }) | ||||
| 					], { | ||||
| 						attr: null | ||||
| 					}), | ||||
| 					tree('spin', [ | ||||
| 						leaf('emoji', { name: 'foo' }) | ||||
| 					], { | ||||
| 						attr: null | ||||
| 					}), | ||||
| 				]); | ||||
| 			}); | ||||
| 
 | ||||
| 			it('nested', () => { | ||||
| 				const tokens = parse('<spin><spin>:foo:</spin></spin>'); | ||||
| 				assert.deepStrictEqual(tokens, [ | ||||
| 					tree('spin', [ | ||||
| 						tree('spin', [ | ||||
| 							leaf('emoji', { name: 'foo' }) | ||||
| 						], { | ||||
| 							attr: null | ||||
| 						}), | ||||
| 					], { | ||||
| 						attr: null | ||||
| 					}), | ||||
| 				]); | ||||
| 			}); | ||||
| */ | ||||
| 		}); | ||||
| 
 | ||||
| 		it('jump', () => { | ||||
| 			const tokens = parse('<jump>:foo:</jump>'); | ||||
| 			assert.deepStrictEqual(tokens, [ | ||||
| 				tree('jump', [ | ||||
| 					leaf('emoji', { name: 'foo' }) | ||||
| 				], {}), | ||||
| 			]); | ||||
| 		}); | ||||
| 
 | ||||
| 		describe('motion', () => { | ||||
| 			it('by triple brackets', () => { | ||||
| 				const tokens = parse('(((foo)))'); | ||||
| 				assert.deepStrictEqual(tokens, [ | ||||
| 					tree('motion', [ | ||||
| 						text('foo') | ||||
| 					], {}), | ||||
| 				]); | ||||
| 			}); | ||||
| 
 | ||||
| 			it('by triple brackets (with other texts)', () => { | ||||
| 				const tokens = parse('bar(((foo)))bar'); | ||||
| 				assert.deepStrictEqual(tokens, [ | ||||
| 					text('bar'), | ||||
| 					tree('motion', [ | ||||
| 						text('foo') | ||||
| 					], {}), | ||||
| 					text('bar'), | ||||
| 				]); | ||||
| 			}); | ||||
| 
 | ||||
| 			it('by <motion> tag', () => { | ||||
| 				const tokens = parse('<motion>foo</motion>'); | ||||
| 				assert.deepStrictEqual(tokens, [ | ||||
| 					tree('motion', [ | ||||
| 						text('foo') | ||||
| 					], {}), | ||||
| 				]); | ||||
| 			}); | ||||
| 
 | ||||
| 			it('by <motion> tag (with other texts)', () => { | ||||
| 				const tokens = parse('bar<motion>foo</motion>bar'); | ||||
| 				assert.deepStrictEqual(tokens, [ | ||||
| 					text('bar'), | ||||
| 					tree('motion', [ | ||||
| 						text('foo') | ||||
| 					], {}), | ||||
| 					text('bar'), | ||||
| 				]); | ||||
| 			}); | ||||
| 		}); | ||||
| 
 | ||||
| 		describe('mention', () => { | ||||
| 			it('local', () => { | ||||
| 				const tokens = parse('@himawari foo'); | ||||
|  | @ -1310,30 +1173,6 @@ describe('MFM', () => { | |||
| 		it('小さい字', () => { | ||||
| 			assert.deepStrictEqual(toString(parse('<small>小さい字</small>')), '<small>小さい字</small>'); | ||||
| 		}); | ||||
| 		it('モーション', () => { | ||||
| 			assert.deepStrictEqual(toString(parse('<motion>モーション</motion>')), '<motion>モーション</motion>'); | ||||
| 		}); | ||||
| 		it('モーション2', () => { | ||||
| 			assert.deepStrictEqual(toString(parse('(((モーション)))')), '<motion>モーション</motion>'); | ||||
| 		}); | ||||
| 		it('ビッグ+', () => { | ||||
| 			assert.deepStrictEqual(toString(parse('*** ビッグ+ ***')), '*** ビッグ+ ***'); | ||||
| 		}); | ||||
| 		it('回転', () => { | ||||
| 			assert.deepStrictEqual(toString(parse('<spin>回転</spin>')), '<spin>回転</spin>'); | ||||
| 		}); | ||||
| 		it('右回転', () => { | ||||
| 			assert.deepStrictEqual(toString(parse('<spin right>右回転</spin>')), '<spin right>右回転</spin>'); | ||||
| 		}); | ||||
| 		it('左回転', () => { | ||||
| 			assert.deepStrictEqual(toString(parse('<spin left>左回転</spin>')), '<spin left>左回転</spin>'); | ||||
| 		}); | ||||
| 		it('往復回転', () => { | ||||
| 			assert.deepStrictEqual(toString(parse('<spin alternate>往復回転</spin>')), '<spin alternate>往復回転</spin>'); | ||||
| 		}); | ||||
| 		it('ジャンプ', () => { | ||||
| 			assert.deepStrictEqual(toString(parse('<jump>ジャンプ</jump>')), '<jump>ジャンプ</jump>'); | ||||
| 		}); | ||||
| 		it('コードブロック', () => { | ||||
| 			assert.deepStrictEqual(toString(parse('```\nコードブロック\n```')), '```\nコードブロック\n```'); | ||||
| 		}); | ||||
|  | @ -1352,12 +1191,6 @@ describe('MFM', () => { | |||
| 		it('詳細なしリンク', () => { | ||||
| 			assert.deepStrictEqual(toString(parse('?[詳細なしリンク](http://example.com)')), '?[詳細なしリンク](http://example.com)'); | ||||
| 		}); | ||||
| 		it('【タイトル】', () => { | ||||
| 			assert.deepStrictEqual(toString(parse('【タイトル】')), '[タイトル]'); | ||||
| 		}); | ||||
| 		it('[タイトル]', () => { | ||||
| 			assert.deepStrictEqual(toString(parse('[タイトル]')), '[タイトル]'); | ||||
| 		}); | ||||
| 		it('インライン数式', () => { | ||||
| 			assert.deepStrictEqual(toString(parse('\\(インライン数式\\)')), '\\(インライン数式\\)'); | ||||
| 		}); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue