diff --git a/docs/index.html b/docs/index.html
index a61832d5..986aff5b 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -79,6 +79,7 @@
+
format
+
nico:waku:(color|color code)
+
example
+
+
nico:waku:red
+nico:waku:#f0f
+nico:waku:#fff5
+
+
コメント単位で枠を表示するできます
色の指定は色コマンドまたはカラーコードで行ってください
カラーコードは透明度も指定可能です
diff --git a/package.json b/package.json
index 43b02da1..8220fb41 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@xpadev-net/niconicomments",
- "version": "0.2.45",
+ "version": "0.2.46",
"description": "NiconiComments is a comment drawing library that is somewhat compatible with the official Nico Nico Douga player.",
"main": "dist/bundle.js",
"types": "dist/bundle.d.ts",
diff --git a/src/@types/format.v1.d.ts b/src/@types/format.v1.d.ts
index fb681988..2ab170f8 100644
--- a/src/@types/format.v1.d.ts
+++ b/src/@types/format.v1.d.ts
@@ -2,7 +2,7 @@ export type v1Thread = {
id: string;
fork: string;
commentCount: number;
- comments: { [key: string]: v1Comment };
+ comments: v1Comment[];
};
type v1Comment = {
id: string;
diff --git a/src/@types/types.d.ts b/src/@types/types.d.ts
index 297226d3..01aebba6 100644
--- a/src/@types/types.d.ts
+++ b/src/@types/types.d.ts
@@ -16,6 +16,7 @@ type formattedCommentWithFont = {
font: commentFont;
color: string;
strokeColor?: string;
+ wakuColor?: string;
full: boolean;
ender: boolean;
_live: boolean;
@@ -117,6 +118,7 @@ type parsedCommand = {
fontSize: number | undefined;
color: string | undefined;
strokeColor?: string;
+ wakuColor?: string;
font: commentFont | undefined;
full: boolean;
ender: boolean;
diff --git a/src/comments/FlashComment.ts b/src/comments/FlashComment.ts
index 6081fa80..58a76f96 100644
--- a/src/comments/FlashComment.ts
+++ b/src/comments/FlashComment.ts
@@ -1,25 +1,24 @@
import {
getConfig,
+ getFlashFontIndex,
+ getFlashFontName,
getPosX,
getStrokeColor,
+ nativeSort,
parseCommandAndNicoScript,
parseFont,
} from "@/util";
-import typeGuard from "@/typeGuard";
import { config, options } from "@/definition/config";
import { nicoScripts } from "@/contexts/nicoscript";
import { imageCache } from "@/contexts/cache";
import type { IComment } from "@/@types/IComment";
import type {
- commentContentIndex,
commentContentItem,
- commentFlashFont,
commentMeasuredContentItem,
formattedCommentWithFont,
formattedCommentWithSize,
measureTextInput,
measureTextResult,
- parsedCommand,
} from "@/@types/types";
import type { formattedComment } from "@/@types/format.formatted";
@@ -75,59 +74,6 @@ class FlashComment implements IComment {
return this.comment.lineCount;
}
- /**
- * コメントに含まれるコマンドを解釈する
- * @param comment- 独自フォーマットのコメントデータ
- * @returns {{loc: string|undefined, size: string|undefined, color: string|undefined, fontSize: number|undefined, ender: boolean, font: string|undefined, full: boolean, _live: boolean, invisible: boolean, long:number|undefined}}
- */
- parseCommand(comment: formattedComment): parsedCommand {
- const metadata = comment.mail;
- const result: parsedCommand = {
- loc: undefined,
- size: undefined,
- fontSize: undefined,
- color: undefined,
- font: undefined,
- full: false,
- ender: false,
- _live: false,
- invisible: false,
- long: undefined,
- };
- for (let command of metadata) {
- command = command.toLowerCase();
- const match = command.match(/^@([0-9.]+)/);
- if (match && match[1]) {
- result.long = Number(match[1]);
- } else if (result.loc === undefined && typeGuard.comment.loc(command)) {
- result.loc = command;
- } else if (result.size === undefined && typeGuard.comment.size(command)) {
- result.size = command;
- result.fontSize = getConfig(config.fontSize, true)[command].default;
- } else {
- if (result.color === undefined) {
- const color = config.colors[command];
- if (color) {
- result.color = color;
- continue;
- } else {
- const match = command.match(/#[0-9a-z]{3,6}/);
- if (match && match[0] && comment.premium) {
- result.color = match[0].toUpperCase();
- continue;
- }
- }
- }
- if (result.font === undefined && typeGuard.comment.font(command)) {
- result.font = command;
- } else if (typeGuard.comment.command.key(command)) {
- result[command] = true;
- }
- }
- }
- return result;
- }
-
/**
* コメントに含まれるニコスクリプトを処理する
* @param comment
@@ -140,18 +86,6 @@ class FlashComment implements IComment {
const parts = (comment.content.match(/\n|[^\n]+/g) || []).map((val) =>
Array.from(val.match(/[ -~。-゚]+|[^ -~。-゚]+/g) || [])
);
- const regex = {
- simsunStrong: new RegExp(config.flashChar.simsunStrong),
- simsunWeak: new RegExp(config.flashChar.simsunWeak),
- gulim: new RegExp(config.flashChar.gulim),
- gothic: new RegExp(config.flashChar.gothic),
- };
- const getFontName = (font: string) =>
- font.match("^simsun.+")
- ? "simsun"
- : font === "gothic"
- ? "defont"
- : (font as commentFlashFont);
for (const line of parts) {
const lineContent: commentContentItem[] = [];
for (const part of line) {
@@ -159,34 +93,16 @@ class FlashComment implements IComment {
lineContent.push({ content: part });
continue;
}
- const index: commentContentIndex[] = [];
- let match;
- if ((match = regex.simsunStrong.exec(part)) !== null) {
- index.push({ font: "simsunStrong", index: match.index });
- }
- if ((match = regex.simsunWeak.exec(part)) !== null) {
- index.push({ font: "simsunWeak", index: match.index });
- }
- if ((match = regex.gulim.exec(part)) !== null) {
- index.push({ font: "gulim", index: match.index });
- }
- if ((match = regex.gothic.exec(part)) !== null) {
- index.push({ font: "gothic", index: match.index });
- }
+ const index = getFlashFontIndex(part);
if (index.length === 0) {
lineContent.push({ content: part });
} else if (index.length === 1 && index[0]) {
- lineContent.push({ content: part, font: getFontName(index[0].font) });
- } else {
- index.sort((a, b) => {
- if (a.index > b.index) {
- return 1;
- } else if (a.index < b.index) {
- return -1;
- } else {
- return 0;
- }
+ lineContent.push({
+ content: part,
+ font: getFlashFontName(index[0].font),
});
+ } else {
+ index.sort(nativeSort((val) => val.index));
if (config.flashMode === "xp") {
let offset = 0;
for (let i = 1; i < index.length; i++) {
@@ -195,7 +111,7 @@ class FlashComment implements IComment {
if (currentVal === undefined || lastVal === undefined) continue;
lineContent.push({
content: part.slice(offset, currentVal.index),
- font: getFontName(lastVal.font),
+ font: getFlashFontName(lastVal.font),
});
offset = currentVal.index;
}
@@ -203,7 +119,7 @@ class FlashComment implements IComment {
if (val)
lineContent.push({
content: part.slice(offset),
- font: getFontName(val.font),
+ font: getFlashFontName(val.font),
});
} else {
const firstVal = index[0],
@@ -215,16 +131,16 @@ class FlashComment implements IComment {
if (firstVal.font !== "gothic") {
lineContent.push({
content: part,
- font: getFontName(firstVal.font),
+ font: getFlashFontName(firstVal.font),
});
} else {
lineContent.push({
content: part.slice(0, secondVal.index),
- font: getFontName(firstVal.font),
+ font: getFlashFontName(firstVal.font),
});
lineContent.push({
content: part.slice(secondVal.index),
- font: getFontName(secondVal.font),
+ font: getFlashFontName(secondVal.font),
});
}
}
@@ -296,9 +212,7 @@ class FlashComment implements IComment {
spacedWidth_arr = [];
let currentWidth = 0,
spacedWidth = 0;
- for (let i = 0; i < comment.content.length; i++) {
- const item = comment.content[i];
- if (item === undefined) continue;
+ for (const item of comment.content) {
const lines = item.content.split("\n");
const widths = [];
@@ -458,6 +372,15 @@ class FlashComment implements IComment {
}
this.context.drawImage(this.image, posX, posY);
}
+ if (this.comment.wakuColor) {
+ this.context.strokeStyle = this.comment.wakuColor;
+ this.context.strokeRect(
+ posX,
+ posY,
+ this.comment.width,
+ this.comment.height
+ );
+ }
if (showCollision) {
this.context.strokeStyle = "rgba(255,0,255,1)";
this.context.strokeRect(
@@ -549,9 +472,7 @@ class FlashComment implements IComment {
let lastFont = this.comment.font,
leftOffset = 0,
lineCount = 0;
- for (let i = 0; i < this.comment.content.length; i++) {
- const item = this.comment.content[i];
- if (!item) continue;
+ for (const item of this.comment.content) {
if (lastFont !== (item.font || this.comment.font)) {
lastFont = item.font || this.comment.font;
context.font = parseFont(lastFont, this.comment.fontSize);
diff --git a/src/comments/HTML5Comment.ts b/src/comments/HTML5Comment.ts
index 74350cbf..9a74eb48 100644
--- a/src/comments/HTML5Comment.ts
+++ b/src/comments/HTML5Comment.ts
@@ -290,6 +290,15 @@ class HTML5Comment implements IComment {
}
this.context.drawImage(this.image, posX, posY);
}
+ if (this.comment.wakuColor) {
+ this.context.strokeStyle = this.comment.wakuColor;
+ this.context.strokeRect(
+ posX,
+ posY,
+ this.comment.width,
+ this.comment.height
+ );
+ }
if (showCollision) {
const scale = getConfig(config.commentScale, false);
this.context.strokeStyle = "rgba(0,255,255,1)";
@@ -378,9 +387,7 @@ class HTML5Comment implements IComment {
const paddingTop =
(10 - scale * 10) *
(this.comment.lineCount / config.hiResCommentCorrection);
- for (let i = 0; i < this.comment.content.length; i++) {
- const item = this.comment.content[i];
- if (!item) continue;
+ for (const item of this.comment.content) {
const lines = item.content.split("\n");
for (let j = 0; j < lines.length; j++) {
const line = lines[j];
diff --git a/src/inputParser.ts b/src/inputParser.ts
index 8a71131e..28fb3d6b 100644
--- a/src/inputParser.ts
+++ b/src/inputParser.ts
@@ -112,9 +112,8 @@ const fromFormatted = (
const fromLegacy = (data: rawApiResponse[]): formattedComment[] => {
const data_: formattedComment[] = [],
userList: string[] = [];
- for (let i = 0; i < data.length; i++) {
- const val = data[i];
- if (!val || !typeGuard.legacy.apiChat(val?.chat)) continue;
+ for (const val of data) {
+ if (!typeGuard.legacy.apiChat(val.chat)) continue;
const value = val.chat;
if (value.deleted !== 1) {
const tmpParam: formattedComment = {
@@ -233,9 +232,7 @@ const fromV1 = (data: v1Thread[]): formattedComment[] => {
for (const item of data) {
const val = item.comments,
forkName = item.fork;
- for (const key of Object.keys(val)) {
- const value = val[key];
- if (!value) continue;
+ for (const value of val) {
const tmpParam: formattedComment = {
id: value.no,
vpos: Math.floor(value.vposMs / 10),
diff --git a/src/nico.ts b/src/nico.ts
index 7b4d08d6..d4ef18be 100644
--- a/src/nico.ts
+++ b/src/nico.ts
@@ -50,9 +50,7 @@ const measureWidth = (
itemWidth = [];
context.font = parseFont(comment.font, fontSize);
let currentWidth = 0;
- for (let i = 0; i < comment.content.length; i++) {
- const item = comment.content[i];
- if (item === undefined) continue;
+ for (const item of comment.content) {
const lines = item.content.split("\n");
context.font = parseFont(item.font || comment.font, fontSize);
const width = [];
diff --git a/src/typeGuard.ts b/src/typeGuard.ts
index 8eebcc86..8eaf5d7a 100644
--- a/src/typeGuard.ts
+++ b/src/typeGuard.ts
@@ -114,14 +114,11 @@ const typeGuard = {
)
return false;
if (!(i as XMLDocument).documentElement.children) return false;
- for (
- let index = 0;
- index < (i as XMLDocument).documentElement.children.length;
- index++
- ) {
- const value = (i as XMLDocument).documentElement.children[index];
- if (!value || value.nodeName !== "chat") continue;
- if (!typeAttributeVerify(value, ["vpos", "date"])) return false;
+ for (const element of Array.from(
+ (i as XMLDocument).documentElement.children
+ )) {
+ if (!element || element.nodeName !== "chat") continue;
+ if (!typeAttributeVerify(element, ["vpos", "date"])) return false;
}
return true;
},
@@ -168,8 +165,8 @@ const typeGuard = {
thread: (i: unknown): i is v1Thread => {
if (!objectVerify(i, ["id", "fork", "commentCount", "comments"]))
return false;
- for (const item of Object.keys((i as v1Thread).comments)) {
- if (!typeGuard.v1.comment((i as v1Thread).comments[item])) return false;
+ for (const value of (i as v1Thread).comments) {
+ if (!typeGuard.v1.comment(value)) return false;
}
return true;
},
diff --git a/src/util.ts b/src/util.ts
index cc91a6bf..f589425c 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -5,12 +5,14 @@ import { colors } from "@/definition/colors";
import type { configItem } from "@/@types/config";
import type { IComment } from "@/@types/IComment";
import type {
+ commentContentIndex,
commentFont,
formattedCommentWithFont,
formattedCommentWithSize,
parsedCommand,
} from "@/@types/types";
import type { formattedComment } from "@/@types/format.formatted";
+import { commentFlashFont } from "@/@types/types";
/**
* 当たり判定からコメントを配置できる場所を探す
* @param {number} currentPos
@@ -454,6 +456,7 @@ const parseCommand = (comment: formattedComment): parsedCommand => {
fontSize: undefined,
color: undefined,
strokeColor: undefined,
+ wakuColor: undefined,
font: undefined,
full: false,
ender: false,
@@ -475,6 +478,15 @@ const parseCommand = (comment: formattedComment): parsedCommand => {
} else if (typeGuard.comment.colorCode(match[1])) {
result.strokeColor = match[1].slice(1);
}
+ } else if (
+ result.wakuColor === undefined &&
+ (match = command.match(/^nico:waku:(.+)$/))
+ ) {
+ if (typeGuard.comment.color(match[1])) {
+ result.wakuColor = colors[match[1]];
+ } else if (typeGuard.comment.colorCode(match[1])) {
+ result.wakuColor = match[1].slice(1);
+ }
} else if (result.loc === undefined && typeGuard.comment.loc(command)) {
result.loc = command;
} else if (result.size === undefined && typeGuard.comment.size(command)) {
@@ -533,6 +545,52 @@ const ArrayEqual = (a: unknown[], b: unknown[]) => {
return true;
};
+const getFlashFontIndex = (part: string): commentContentIndex[] => {
+ const regex = {
+ simsunStrong: new RegExp(config.flashChar.simsunStrong),
+ simsunWeak: new RegExp(config.flashChar.simsunWeak),
+ gulim: new RegExp(config.flashChar.gulim),
+ gothic: new RegExp(config.flashChar.gothic),
+ };
+ const index: commentContentIndex[] = [];
+ let match;
+ if ((match = regex.simsunStrong.exec(part)) !== null) {
+ index.push({ font: "simsunStrong", index: match.index });
+ }
+ if ((match = regex.simsunWeak.exec(part)) !== null) {
+ index.push({ font: "simsunWeak", index: match.index });
+ }
+ if ((match = regex.gulim.exec(part)) !== null) {
+ index.push({ font: "gulim", index: match.index });
+ }
+ if ((match = regex.gothic.exec(part)) !== null) {
+ index.push({ font: "gothic", index: match.index });
+ }
+ return index;
+};
+
+const getFlashFontName = (font: string): commentFlashFont => {
+ if (font.match("^simsun.+")) return "simsun";
+ if (font === "gothic") return "defont";
+ return font as commentFlashFont;
+};
+
+const getValue =
(value: T | undefined | null, alternative: T): T => {
+ return value ?? alternative;
+};
+
+const nativeSort = (getter: (input: T) => number) => {
+ return (a: T, b: T) => {
+ if (getter(a) > getter(b)) {
+ return 1;
+ } else if (getter(a) < getter(b)) {
+ return -1;
+ } else {
+ return 0;
+ }
+ };
+};
+
export {
getPosY,
getPosX,
@@ -546,4 +604,8 @@ export {
isFlashComment,
parseCommandAndNicoScript,
ArrayEqual,
+ getFlashFontIndex,
+ getFlashFontName,
+ getValue,
+ nativeSort,
};