2023-09-23 20:02:04 +02:00
|
|
|
|
import type { NavItem } from '@nuxt/content/dist/runtime/types';
|
2024-02-09 13:15:12 +01:00
|
|
|
|
import type { LocaleObject } from '@nuxtjs/i18n';
|
2023-09-26 14:57:26 +02:00
|
|
|
|
import { parseURL } from 'ufo';
|
2023-09-23 20:02:04 +02:00
|
|
|
|
|
2023-09-26 14:57:26 +02:00
|
|
|
|
/**
|
|
|
|
|
* オブジェクトのパス文字列からオブジェクトの内部を参照
|
|
|
|
|
* @param o オブジェクト
|
|
|
|
|
* @param s パス
|
|
|
|
|
* @returns パスの先にあるもの
|
|
|
|
|
*/
|
2024-02-09 13:15:12 +01:00
|
|
|
|
export function resolveObjPath(o: Record<string, any>, s: string): any {
|
2023-07-09 20:09:50 +02:00
|
|
|
|
s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
|
|
|
|
|
s = s.replace(/^\./, ''); // strip a leading dot
|
|
|
|
|
var a = s.split('.');
|
|
|
|
|
for (var i = 0, n = a.length; i < n; ++i) {
|
|
|
|
|
var k = a[i];
|
|
|
|
|
if (k in o) {
|
|
|
|
|
o = o[k];
|
|
|
|
|
} else {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return o;
|
2023-07-15 10:32:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-26 14:57:26 +02:00
|
|
|
|
/**
|
|
|
|
|
* URLがドメイン内部かどうかを判別
|
|
|
|
|
* @param link 判別したいURL
|
|
|
|
|
* @param base ローカルの基準となるドメイン
|
|
|
|
|
*/
|
2023-07-15 10:32:48 +02:00
|
|
|
|
export function isLocalPath(link: string, base?: string): boolean {
|
|
|
|
|
let baseUrl;
|
2023-09-26 14:57:26 +02:00
|
|
|
|
|
2023-07-15 10:32:48 +02:00
|
|
|
|
if (base) {
|
|
|
|
|
baseUrl = base;
|
|
|
|
|
} else {
|
|
|
|
|
const runtimeConfig = useRuntimeConfig();
|
|
|
|
|
baseUrl = runtimeConfig.public.baseUrl;
|
|
|
|
|
}
|
2023-09-26 14:57:26 +02:00
|
|
|
|
|
|
|
|
|
const rootDomain = parseURL(base);
|
|
|
|
|
const url = parseURL(link);
|
|
|
|
|
|
|
|
|
|
return (!url.host || rootDomain.host === url.host);
|
2023-07-18 11:14:54 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-11-25 17:48:16 +01:00
|
|
|
|
export function sanitizeInternalPath(path: string): string {
|
|
|
|
|
const runtimeConfig = useRuntimeConfig();
|
2023-12-01 16:33:58 +01:00
|
|
|
|
return path
|
|
|
|
|
.replace(/^(\/((?!ja)[a-z]{2}))?\/blog\/(.+)/g, '/ja/blog/$3')
|
|
|
|
|
.replace(new RegExp(`^(\/(${(runtimeConfig.public.locales as LocaleObject[]).map((l) => l.code).join('|')})\/?){2,}(.*)$`, 'g'), '$1$2');
|
2023-11-25 17:48:16 +01:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-26 14:57:26 +02:00
|
|
|
|
/**
|
|
|
|
|
* ナビゲーションObjectを合致する条件まで深掘り
|
|
|
|
|
* @param obj ナビゲーションObject
|
|
|
|
|
* @param condition 深掘りを停止する条件
|
|
|
|
|
* @returns 深掘りしたナビゲーションObject
|
|
|
|
|
*/
|
2023-09-23 20:02:04 +02:00
|
|
|
|
export const findDeepObject = (obj: NavItem, condition: (v: NavItem) => boolean): NavItem | null => {
|
2023-07-18 11:14:54 +02:00
|
|
|
|
if (condition(obj)) {
|
|
|
|
|
return obj;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (obj?.children && obj.children.length > 0) {
|
|
|
|
|
for (let i = 0; i < obj.children.length; i++) {
|
|
|
|
|
const result = findDeepObject(obj.children[i], condition);
|
|
|
|
|
if (result) {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
};
|
2023-12-04 14:47:06 +01:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Clipboardに値をコピー(TODO: 文字列以外も対応)
|
|
|
|
|
*/
|
|
|
|
|
export function copyText(val: string) {
|
|
|
|
|
if (!process.client) return;
|
|
|
|
|
|
|
|
|
|
// 空div 生成
|
|
|
|
|
const tmp = document.createElement('div');
|
|
|
|
|
// 選択用のタグ生成
|
|
|
|
|
const pre = document.createElement('pre');
|
|
|
|
|
|
|
|
|
|
// 親要素のCSSで user-select: none だとコピーできないので書き換える
|
|
|
|
|
pre.style.webkitUserSelect = 'auto';
|
|
|
|
|
pre.style.userSelect = 'auto';
|
|
|
|
|
|
|
|
|
|
tmp.appendChild(pre).textContent = val;
|
|
|
|
|
|
|
|
|
|
// 要素を画面外へ
|
|
|
|
|
const s = tmp.style;
|
|
|
|
|
s.position = 'fixed';
|
|
|
|
|
s.right = '200%';
|
|
|
|
|
|
|
|
|
|
// body に追加
|
|
|
|
|
document.body.appendChild(tmp);
|
|
|
|
|
// 要素を選択
|
|
|
|
|
document.getSelection()?.selectAllChildren(tmp);
|
|
|
|
|
|
|
|
|
|
// クリップボードにコピー
|
|
|
|
|
const result = document.execCommand('copy');
|
|
|
|
|
|
|
|
|
|
// 要素削除
|
|
|
|
|
document.body.removeChild(tmp);
|
|
|
|
|
|
|
|
|
|
return result;
|
2024-02-02 02:39:09 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Converts half-width Katakana characters to full-width Katakana characters.
|
|
|
|
|
* @param str - The string to convert.
|
|
|
|
|
* @returns The converted string with full-width Katakana characters.
|
|
|
|
|
*/
|
|
|
|
|
export function kanaHalfToFull(str: string): string {
|
|
|
|
|
const kanaMap: Record<string, string> = {
|
|
|
|
|
'ガ': 'ガ', 'ギ': 'ギ', 'グ': 'グ', 'ゲ': 'ゲ', 'ゴ': 'ゴ',
|
|
|
|
|
'ザ': 'ザ', 'ジ': 'ジ', 'ズ': 'ズ', 'ゼ': 'ゼ', 'ゾ': 'ゾ',
|
|
|
|
|
'ダ': 'ダ', 'ヂ': 'ヂ', 'ヅ': 'ヅ', 'デ': 'デ', 'ド': 'ド',
|
|
|
|
|
'バ': 'バ', 'ビ': 'ビ', 'ブ': 'ブ', 'ベ': 'ベ', 'ボ': 'ボ',
|
|
|
|
|
'パ': 'パ', 'ピ': 'ピ', 'プ': 'プ', 'ペ': 'ペ', 'ポ': 'ポ',
|
|
|
|
|
'ヴ': 'ヴ', 'ヷ': 'ヷ', 'ヺ': 'ヺ',
|
|
|
|
|
'ア': 'ア', 'イ': 'イ', 'ウ': 'ウ', 'エ': 'エ', 'オ': 'オ',
|
|
|
|
|
'カ': 'カ', 'キ': 'キ', 'ク': 'ク', 'ケ': 'ケ', 'コ': 'コ',
|
|
|
|
|
'サ': 'サ', 'シ': 'シ', 'ス': 'ス', 'セ': 'セ', 'ソ': 'ソ',
|
|
|
|
|
'タ': 'タ', 'チ': 'チ', 'ツ': 'ツ', 'テ': 'テ', 'ト': 'ト',
|
|
|
|
|
'ナ': 'ナ', 'ニ': 'ニ', 'ヌ': 'ヌ', 'ネ': 'ネ', 'ノ': 'ノ',
|
|
|
|
|
'ハ': 'ハ', 'ヒ': 'ヒ', 'フ': 'フ', 'ヘ': 'ヘ', 'ホ': 'ホ',
|
|
|
|
|
'マ': 'マ', 'ミ': 'ミ', 'ム': 'ム', 'メ': 'メ', 'モ': 'モ',
|
|
|
|
|
'ヤ': 'ヤ', 'ユ': 'ユ', 'ヨ': 'ヨ',
|
|
|
|
|
'ラ': 'ラ', 'リ': 'リ', 'ル': 'ル', 'レ': 'レ', 'ロ': 'ロ',
|
|
|
|
|
'ワ': 'ワ', 'ヲ': 'ヲ', 'ン': 'ン',
|
|
|
|
|
'ァ': 'ァ', 'ィ': 'ィ', 'ゥ': 'ゥ', 'ェ': 'ェ', 'ォ': 'ォ',
|
|
|
|
|
'ッ': 'ッ', 'ャ': 'ャ', 'ュ': 'ュ', 'ョ': 'ョ',
|
|
|
|
|
'。': '。', '、': '、', 'ー': 'ー', '「': '「', '」': '」', '・': '・'
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var reg = new RegExp('(' + Object.keys(kanaMap).join('|') + ')', 'g');
|
|
|
|
|
return str.replace(reg, (m) => kanaMap[m]).replace(/゙/g, '゛').replace(/゚/g, '゜');
|
|
|
|
|
};
|