您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
『niconico』の検索窓にタグ検索タブを追加 / Adds "tag" search tab above search box in "niconico"
当前为
- // ==UserScript==
- // @name Append Tag Searching Tub
- // @namespace http://loda.jp/script/
- // @id niconico-adds-search-tab-347021
- // @version 3.0.1
- // @description 『niconico』の検索窓にタグ検索タブを追加 / Adds "tag" search tab above search box in "niconico"
- // @match http://www.nicovideo.jp/*
- // @match http://seiga.nicovideo.jp/search/*
- // @match http://live.nicovideo.jp/*
- // @match http://watch.live.nicovideo.jp/*
- // @match http://com.nicovideo.jp/*
- // @match http://blog.nicovideo.jp/en_info/*
- // @match http://tw.blog.nicovideo.jp/*
- // @match http://info.nicovideo.jp/psvita/en/*
- // @grant GM_xmlhttpRequest
- // @domain www.nicovideo.jp
- // @domain seiga.nicovideo.jp
- // @domain live.nicovideo.jp
- // @domain watch.live.nicovideo.jp
- // @domain com.nicovideo.jp
- // @domain blog.nicovideo.jp
- // @domain tw.blog.nicovideo.jp
- // @domain info.nicovideo.jp
- // @run-at document-start
- // @icon data:image/vnd.microsoft.icon;base64,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
- // @author 100の人 https://userscripts.org/users/347021
- // @license Creative Commons Attribution 3.0 Unported License
- // ==/UserScript==
- (function () {
- 'use strict';
- polyfill();
- // L10N
- setLocalizedTexts({
- 'en': {
- '静画を検索': 'Search Image',
- '静画': 'Images',
- '生放送を検索': 'Search Live Program',
- '生放送': 'Live',
- 'タグ': 'Tags',
- 'マンガ': 'Comics',
- },
- 'zh': {
- '静画を検索': '搜尋靜畫',
- '静画': '靜畫',
- '生放送を検索': '搜尋生放送',
- '生放送': '生放送',
- 'タグ': '標籤',
- 'マンガ': '漫畫',
- },
- });
- /**
- * 検索窓の最大幅
- * @constant {string}
- */
- var MAX_SEARCH_BOX_WIDTH = '268px';
- var host = window.location.host, pathname = window.location.pathname, pageType,
- targetParentIdFirefox, isTargetParentFirefox, isTargetFirefox;
- // 検索ページの種類を取得
- switch (host) {
- case 'www.nicovideo.jp':
- if (window.location.pathname === '/') {
- pageType = 'top';
- } else if (/^\/(?:search\/|mylist_search(?:\/|$))/.test(pathname)) {
- pageType = 'video';
- } else if (/^\/(?:(?:tag|related_tag|watch|mylist)\/|(?:recent|newarrival|hotlist|video_top|openlist|playlist|recommendations)(?:\/|$))/.test(pathname)) {
- pageType = 'tag';
- }
- break;
- case 'seiga.nicovideo.jp':
- pageType = 'image';
- break;
- case 'live.nicovideo.jp':
- case 'watch.live.nicovideo.jp':
- if (pathname.startsWith('/search')) {
- pageType = 'live';
- }
- break;
- case 'info.nicovideo.jp':
- if (pathname.startsWith('/psvita/en/')) {
- // 英語版PS Vita紹介ページ
- startScript(prepare,
- function (parent) { return parent.localName === 'body'; },
- function (target) { return target.id === 'header'; },
- function () { return document.getElementById('header'); },
- {
- isTargetParent: function (parent) { return parent.localName === 'html'; },
- isTarget: function (target) { return target.localName === 'body'; },
- });
- }
- return;
- }
- // 上部メニューが追加されるまで待機
- switch (host) {
- case 'seiga.nicovideo.jp':
- // 静画
- isTargetFirefox = function (target) { return target.id === 'wrapper'; };
- break;
- case 'live.nicovideo.jp':
- // 生放送
- targetParentIdFirefox = 'body_header';
- break;
- case 'blog.nicovideo.jp':
- // 英語版ニコニコインフォ
- targetParentIdFirefox = 'container-inner';
- break;
- case 'tw.blog.nicovideo.jp':
- // 台湾版ニコニコインフォ
- targetParentIdFirefox = 'header';
- break;
- case 'info.nicovideo.jp':
- break;
- }
- if (!isTargetParentFirefox) {
- if (targetParentIdFirefox) {
- isTargetParentFirefox = function (parent) { return parent.id === targetParentIdFirefox; };
- } else {
- isTargetParentFirefox = function (parent) { return parent.localName === 'body'; };
- }
- }
- startScript(prepare,
- function (parent) { return parent.classList.contains('siteHeaderGlovalNavigation'); },
- function (target) { return target.id === 'siteHeaderLeftMenu'; },
- function () { return document.getElementById('siteHeaderLeftMenu'); },
- {
- isTargetParent: isTargetParentFirefox,
- isTarget: isTargetFirefox || function (target) { return target.id === 'siteHeader'; },
- });
- function prepare () {
- var parentId, parentIdFirefox, targetId, targetIdFirefox, isTargetParent, isTargetParentFirefox,
- textVideo, harajuku, itemLive, item;
- // ニコニコ生放送ではlang属性値が常にja-JPのため、ニコニコ動画へのリンク文字によって、ページの言語を判定する
- textVideo = document.querySelector('[href^="http://www.nicovideo.jp/video_top"]').textContent;
- if (textVideo.contains('Video')) {
- setlang('en');
- } else if (textVideo.contains('動畫')) {
- setlang('zh');
- }
- if (!document.querySelector(pageType === 'image' ? '#siteHeader [href="/?header"], #siteHeader [href="/"]' : '#siteHeader [href^="http://seiga.nicovideo.jp/"], #globalNav [href^="http://seiga.nicovideo.jp/"]')) {
- // ヘッダに静画へのリンクが無ければ
- // 生放送へのリンクを取得
- itemLive = document.querySelector('#siteHeader [href^="http://live.nicovideo.jp/"], #globalNav [href^="http://live.nicovideo.jp/"]').parentNode;
- // 生放送リンクの複製
- item = itemLive.cloneNode(true);
- // リンク文字を変更
- (item.getElementsByTagName('span')[0] || item.getElementsByTagName('a')[0]).textContent = _('静画');
- // アドレスを変更
- item.getElementsByTagName('a')[0].host = 'seiga.nicovideo.jp';
- // ヘッダに静画へのリンクを追加
- itemLive.parentNode.insertBefore(item, itemLive);
- }
- // スクリプトを起動
- if (!pageType) {
- return;
- }
- harajuku = document.doctype.publicId;
- switch (pageType) {
- case 'video':
- if (harajuku) {
- // マイリスト検索、キーワード検索
- parentId = 'form_search';
- targetId = 'search_united_form';
- parentIdFirefox = 'PAGEMAIN';
- targetIdFirefox = 'PAGEBODY';
- } else {
- // GINZAバージョンのキーワード検索
- startScript(main,
- function (parent) { return parent.classList.contains('formSearch'); },
- function (target) { return target.id === 'search_united_form'; },
- function () { return document.getElementById('search_united_form'); },
- {
- isTargetParent: function (parent) { return parent.localName === 'body'; },
- isTarget: function (target) { return target.localName === 'section'; },
- });
- return;
- }
- break;
- case 'top':
- // トップページ
- main = mainTop;
- parentId = 'searchFormInner';
- targetId = 'searchForm';
- isTargetParentFirefox = function (parent) {
- return parent.id === 'main_container' || parent.localName === 'body';
- };
- targetIdFirefox = 'searchFormWrap';
- break;
- case 'image':
- // 静画
- parentId = 'usearch_form';
- targetId = 'usearch_form_input';
- parentIdFirefox = 'wrapper';
- targetIdFirefox = 'main';
- break;
- case 'live':
- // 生放送
- isTargetParentFirefox = isTargetParent = function (target) {
- return target.classList.contains('container');
- };
- targetIdFirefox = targetId = 'form_frm_btm';
- break;
- case 'tag':
- if (harajuku) {
- // タグ検索等
- main = mainTag;
- parentId = 'search_tab';
- targetId = 'target_m';
- parentIdFirefox = 'PAGEMAIN';
- targetIdFirefox = 'PAGEBODY';
- } else {
- // GINZAバージョンのタグ検索
- startScript(mainTag,
- function (parent) { return parent.classList.contains('videoSearchOption'); },
- function (target) { return target.classList.contains('optMylist'); },
- function () { return document.getElementsByClassName('optMylist')[0]; },
- {
- isTargetParent: function (parent) { return parent.localName === 'body'; },
- isTarget: function (target) { return target.localName === 'header'; },
- });
- return;
- }
- }
- startScript(main,
- isTargetParent || function (parent) { return parent.id === parentId; },
- function (target) { return target.id === targetId; },
- function () { return document.getElementById(targetId); },
- {
- isTargetParent: isTargetParentFirefox || function (parent) { return parent.id === parentIdFirefox; },
- isTarget: function (target) { return target.id === targetIdFirefox; },
- });
- }
- // タグ検索
- function mainTag () {
- var mylistTab, tabList, styleSheet, cssRules, script;
- // スタイルの設定
- styleSheet = document.head.appendChild(document.createElement('style')).sheet;
- cssRules = styleSheet.cssRules;
- [
- '#PAGEHEADER > div {'
- + 'display: flex;'
- + '}',
- '#head_search {'
- + 'max-width: ' + MAX_SEARCH_BOX_WIDTH + ';'
- + 'flex-grow: 1;'
- + '}',
- '#search_input {'
- + 'width: 100%;'
- + 'display: flex;'
- + '}',
- '#search_input .typeText {'
- + 'flex-grow: 1;'
- + '}',
- '#head_ads {'
- + 'margin-right: -26px;'
- + '}',
- '#search_input #bar_search {'
- + '-moz-box-sizing: border-box;'
- + 'box-sizing: border-box;'
- + 'width: 100% !important;'
- + '}',
- // GINZAバージョン
- '.siteHeader > .inner {'
- + 'display: flex;'
- + '}',
- '.videoSearch {'
- + 'max-width: ' + MAX_SEARCH_BOX_WIDTH + ';'
- + 'flex-grow: 1;'
- + 'padding-left: 4px;'
- + 'padding-right: 4px;'
- + '}',
- '.videoSearch form {'
- + 'display: flex;'
- + '}',
- '.videoSearch form .inputText {'
- + 'flex-grow: 1;'
- + '}',
- ].forEach(function (rule) {
- styleSheet.insertRule(rule, cssRules.length);
- });
- // タブリストの取得
- mylistTab = document.querySelector('#target_m, .optMylist');
- tabList = mylistTab.parentNode;
- // タブの複製・追加
- [
- {
- type: 'image',
- title: _('静画を検索'),
- uri: 'http://seiga.nicovideo.jp/search',
- text: _('静画'),
- },
- {
- type: 'live',
- title: _('生放送を検索'),
- uri: 'http://live.nicovideo.jp/search',
- text: _('生放送'),
- },
- ].forEach(function (option) {
- var tab = mylistTab.cloneNode(true);
- if (mylistTab.classList.contains('optMylist')) {
- // GINZAバージョン
- tab.classList.remove('optMylist');
- tab.classList.add('opt' + option.type[0].toUpperCase() + option.type.slice(1));
- tab.dataset.type = option.type;
- tab.getElementsByTagName('a')[0].textContent = option.text;
- } else {
- // 原宿バージョン
- tab.id = 'target_' + option.type[0];
- tab.title = option.title;
- tab.setAttribute('onclick', tab.getAttribute('onclick').replace(/'.+?'/, '\'' + option.uri + '\''));
- tab.textContent = option.text;
- }
- tabList.appendChild(tab);
- });
- if (mylistTab.classList.contains('optMylist')) {
- // GINZAバージョン
- script = document.createElement('script');
- script.text = '(' + (function () {
- eval('Nico.Navigation.HeaderSearch.Controller.search = ' + Nico.Navigation.HeaderSearch.Controller.search.toString().replace(/(switch.+?{.+?)(})/, '$1; break;'
- + 'case "image":'
- + 'd = "http://seiga.nicovideo.jp/search/" + e; break;'
- + 'case "live":'
- + 'd = "http://live.nicovideo.jp/search/" + e; break;'
- + '$2'));
- }).toString() + ')();';
- document.head.appendChild(script);
- }
- }
- // トップページ
- function mainTop() {
- var styleSheet, cssRules, refItem, item, anchor;
- fixPrototypeJavaScriptFramework();
- // スタイルの設定
- styleSheet = document.head.appendChild(document.createElement('style')).sheet;
- cssRules = styleSheet.cssRules;
- [
- '#searchFormInner {'
- + 'width: auto;'
- + 'margin-left: 136px;'
- + '}',
- ].forEach(function (rule) {
- styleSheet.insertRule(rule, cssRules.length);
- });
- // マイリスト検索ボタンの取得
- refItem = document.getElementsByClassName('sMylist')[0].parentNode;
- // マイリスト検索ボタンの複製
- item = refItem.cloneNode(true);
- // ボタン名を変更
- anchor = item.getElementsByTagName('a')[0];
- anchor.textContent = _('タグ');
- // クラス名を変更
- anchor.className = 'sVideo';
- // アドレスを変更
- anchor.href = 'http://www.nicovideo.jp/tag/';
- // タグ検索ボタンを追加
- refItem.parentNode.insertBefore(item, refItem);
- if (!document.getElementsByClassName('sSeiga')[0]) {
- // 静画検索ボタンが存在しなければ
- // 生放送検索の取得
- refItem = document.getElementsByClassName('sLive')[0].parentNode;
- // 生放送検索の複製
- item = refItem.cloneNode(true);
- // ボタン名を変更
- anchor = item.getElementsByTagName('a')[0];
- anchor.textContent = _('静画');
- // クラス名を変更
- anchor.className = 'sSeiga';
- // アドレスを変更
- anchor.href = 'http://seiga.nicovideo.jp/search/';
- // 静画検索を追加
- refItem.parentNode.insertBefore(item, refItem);
- startScript(function () {
- var list, item, anchor;
- // メニューの生放送リンクの取得
- list = document.querySelector('.service_main .live').parentNode.parentNode;
- // 生放送リンクの複製
- item = list.cloneNode(true);
- // リンク文字を変更
- anchor = item.getElementsByTagName('a')[0];
- anchor.title = anchor.textContent = _('静画');
- // クラス名を変更
- anchor.classList.remove('live');
- anchor.classList.add('seiga');
- // アドレスを変更
- item.getElementsByTagName('a')[0].href = 'http://seiga.nicovideo.jp/';
- // メニューに静画へのリンクを追加
- list.parentNode.insertBefore(item, list);
- // サブメニューの複製
- item = document.getElementsByClassName('service_sub')[0].cloneNode(true);
- // 2つ目以降の要素を削除
- Array.prototype.forEach.call(item.querySelectorAll('li:first-child ~ li'), function (item) {
- item.parentNode.removeChild(item);
- });
- // リンク文字を変更
- anchor = item.getElementsByTagName('a')[0];
- anchor.title = anchor.textContent = _('マンガ');
- // アドレスを変更
- item.getElementsByTagName('a')[0].href = 'http://seiga.nicovideo.jp/manga/';
- // メニューに静画のサブメニューへのリンクを追加
- list.parentNode.insertBefore(item, list);
- },
- function (parent) { return parent.id === 'sideNav'; },
- function (target) { return target.id === 'trendyTags'; },
- function () { return document.querySelector('#menuService [href="http://live.nicovideo.jp/timetable/"]'); },
- {
- isTarget: function (target) { return target.id === 'NewServiceList'; },
- });
- }
- }
- // キーワード検索、マイリスト検索、静画検索、生放送検索
- function main() {
- var inactiveTab, mylistTab, tagTab, tabNameNode, searchCount, anchor, searchWords = '', searchWordsPattern;
- // マイリスト検索タブの取得
- mylistTab = document.querySelector('.tab_table td:nth-of-type(2), #search_frm_a a:nth-of-type(2), .seachFormA a:nth-of-type(2)');
- // マイリスト検索タブの複製
- tagTab = mylistTab.cloneNode(true);
- // タブ名を変更
- anchor = tagTab.tagName.toLowerCase() === 'a' ? tagTab : tagTab.getElementsByTagName('a')[0];
- tabNameNode = anchor.getElementsByTagName('div');
- tabNameNode = (tabNameNode.length > 0 ? tabNameNode[0].firstChild : anchor.firstChild);
- tabNameNode.data = _('タグ') + (pageType === 'live' ? '(' : ' ( ');
- // クラス名を変更・動画件数をリセット
- searchCount = tagTab.querySelector('strong, span');
- if (pageType === 'image') {
- searchCount.classList.remove('search_value_em');
- searchCount.classList.add('search_value');
- } else if (pageType === 'live') {
- searchCount.classList.remove('Redtxt');
- } else{
- searchCount.style.removeProperty('color');
- }
- searchCount.textContent = '-';
- if (searchCount.id) {
- // 生放送
- searchCount.id = 'search_count_tag';
- }
- // 検索語句を取得
- searchWordsPattern = /(?:\/(?:search|tag|mylist_search)\/|[?&]keyword=)([^?&#]+)/g;
- if (searchWords = window.location.href.match(searchWordsPattern)) {
- searchWords = searchWordsPattern.exec(searchWords[pageType === 'live' ? searchWords.length - 1 : 0])[1];
- }
- // タグが付いた動画件数を取得・表示
- if (searchWords) {
- GM_xmlhttpRequest({
- method: 'GET',
- url: 'http://www.nicovideo.jp/tag/' + searchWords,
- onload: function (response) {
- var responseDocument, total, trimmedThousandsSep;
- responseDocument = new DOMParser().parseFromString(response.responseText, 'text/html');
- if (!responseDocument) {
- // Blink
- // Issue 265379: DOMParser + text/html does not work <http://code.google.com/p/chromium/issues/detail?id=265379>
- responseDocument = document.implementation.createHTMLDocument();
- responseDocument.documentElement.innerHTML = response.responseText;
- }
- total = responseDocument.title.contains('(原宿)')
- // 原宿バージョン
- ? /[,0-9]+/.exec(responseDocument.getElementsByClassName('searchTagTotal')[0].textContent)[0]
- // GINZAバージョン
- : responseDocument.querySelector('.tagCaption .dataValue .num').textContent;
- trimmedThousandsSep = total.replace(/,/g, '');
- if (trimmedThousandsSep >= 100) {
- if (pageType === 'image') {
- searchCount.classList.remove('search_value');
- searchCount.classList.add('search_value_em');
- } else if (pageType === 'live') {
- searchCount.classList.add('Redtxt');
- } else {
- searchCount.style.color = '#CC0000';
- }
- }
- searchCount.textContent = pageType === 'live' ? trimmedThousandsSep : (pageType === 'image' ? total : ' ' + total + ' ');
- }
- });
- }
- // 非アクティブタブを取得
- inactiveTab = document.querySelector('.tab_0, .tab1');
- // クラス名を変更
- anchor.className = inactiveTab.className;
- // アドレスを変更
- anchor.href = 'http://www.nicovideo.jp/tag/' + searchWords + inactiveTab.search;
- // タグ検索タブを追加
- mylistTab.parentNode.insertBefore(tagTab, mylistTab);
- if (inactiveTab.classList.contains('tab1')) {
- // GINZAバージョン
- mylistTab.parentNode.insertBefore(tagTab.previousSibling.cloneNode(true), mylistTab);
- }
- }
- /**
- * 挿入された節の親節が、目印となる節の親節か否かを返すコールバック関数
- * @callback isTargetParent
- * @param {(Document|Element)} parent
- * @returns {boolean}
- */
- /**
- * 挿入された節が、目印となる節か否かを返すコールバック関数
- * @callback isTarget
- * @param {(DocumentType|Element)} target
- * @returns {boolean}
- */
- /**
- * 目印となる節が文書に存在するか否かを返すコールバック関数
- * @callback existsTarget
- * @returns {boolean}
- */
- /**
- * 目印となる節が挿入された直後に関数を実行する
- * @param {Function} main - 実行する関数
- * @param {isTargetParent} isTargetParent
- * @param {isTarget} isTarget
- * @param {existsTarget} existsTarget
- * @param {Object} [callbacksForFirefox] - DOMContentLoaded前のタイミングで1回だけスクリプトを起動させる場合に設定
- * @param {isTargetParent} [callbacksForFirefox.isTargetParent] - FirefoxにおけるisTargetParent
- * @param {isTarget} [callbacksForFirefox.isTarget] - FirefoxにおけるisTarget
- * @version 2013-09-23
- */
- function startScript(main, isTargetParent, isTarget, existsTarget, callbacksForFirefox) {
- var observer, flag;
- // FirefoxのDOMContentLoaded前のMutationObserverは、要素をまとめて挿入したと見なすため、isTargetParent、isTargetを変更
- if (callbacksForFirefox && window.navigator.userAgent.contains(' Firefox/')) {
- if (callbacksForFirefox.isTargetParent) {
- isTargetParent = callbacksForFirefox.isTargetParent;
- }
- if (callbacksForFirefox.isTarget) {
- isTarget = callbacksForFirefox.isTarget;
- }
- }
- // 指定した節が既に存在していれば、即実行
- startMain();
- if (flag) {
- return;
- }
- observer = new MutationObserver(mutationCallback);
- observer.observe(document, {
- childList: true,
- subtree: true,
- });
- if (callbacksForFirefox) {
- // DOMContentLoadedまでにスクリプトを実行できなかった場合、監視を停止(指定した節が存在するか確認し、存在すれば実行)
- document.addEventListener('DOMContentLoaded', function stopScript(event) {
- event.target.removeEventListener('DOMContentLoaded', stopScript);
- if (observer) {
- observer.disconnect();
- }
- startMain();
- flag = true;
- });
- }
- /**
- * 目印となる節が挿入されたら、監視を停止し、{@link checkExistingTarget}を実行する
- * @param {MutationRecord[]} mutations - a list of MutationRecord objects
- * @param {MutationObserver} observer - the constructed MutationObserver object
- */
- function mutationCallback(mutations, observer) {
- var mutation, target, nodeType, addedNodes, addedNode, i, j, l, l2;
- for (i = 0, l = mutations.length; i < l; i++) {
- mutation = mutations[i];
- target = mutation.target;
- nodeType = target.nodeType;
- if ((nodeType === Node.ELEMENT_NODE || nodeType === Node.DOCUMENT_NODE) && isTargetParent(target)) {
- // 子が追加された節が要素節か文書節で、かつそのノードについてisTargetParentが真を返せば
- addedNodes = Array.prototype.slice.call(mutation.addedNodes);
- for (j = 0, l2 = addedNodes.length; j < l2; j++) {
- addedNode = addedNodes[j];
- nodeType = addedNode.nodeType;
- if ((nodeType === Node.ELEMENT_NODE || nodeType === Node.DOCUMENT_TYPE_NODE) && isTarget(addedNode)) {
- // 追加された子が要素節か文書型節で、かつそのノードについてisTargetが真を返せば
- observer.disconnect();
- checkExistingTarget(0);
- return;
- }
- }
- }
- }
- }
- /**
- * {@link startMain}を実行し、スクリプトが開始されていなければ再度実行
- * @param {number} count - {@link startMain}を実行した回数
- */
- function checkExistingTarget(count) {
- var LIMIT = 500, INTERVAL = 10;
- startMain();
- if (!flag && count < LIMIT) {
- window.setTimeout(checkExistingTarget, INTERVAL, count + 1);
- }
- }
- /**
- * 指定した節が存在するか確認し、存在すれば監視を停止しスクリプトを実行
- */
- function startMain() {
- if (!flag && existsTarget()) {
- flag = true;
- main();
- }
- }
- }
- /**
- * prototype汚染が行われる Prototype JavaScript Framework (prototype.js) 1.5.1.1 のバグを修正(Tampermonkey用)
- */
- function fixPrototypeJavaScriptFramework() {
- [
- [document, 'getElementsByClassName'],
- ].forEach(function (objectProperty) {
- delete objectProperty[0][objectProperty[1]];
- });
- }
- /**
- * 国際化・地域化関数の読み込み、ECMAScript仕様のPolyfill
- */
- function polyfill() {
- // i18n
- (function () {
- /**
- * 翻訳対象文字列 (msgid) の言語
- * @constant {string}
- */
- var ORIGINAL_LOCALE = 'ja';
- /**
- * クライアントの言語の翻訳リソースが存在しないとき、どの言語に翻訳するか
- * @constant {string}
- */
- var DEFAULT_LOCALE = 'en';
- /**
- * 以下のような形式の翻訳リソース
- * {
- * 'IETF言語タグ': {
- * '翻訳前 (msgid)': '翻訳後 (msgstr)',
- * ……
- * },
- * ……
- * }
- * @typedef {Object} LocalizedTexts
- */
- /**
- * クライアントの言語。{@link setlang}から変更される
- * @type {string}
- * @access private
- */
- var langtag = 'ja';
- /**
- * クライアントの言語のlanguage部分。{@link setlang}から変更される
- * @type {string}
- * @access private
- */
- var language = 'ja';
- /**
- * 翻訳リソース。{@link setLocalizedTexts}から変更される
- * @type {LocalizedTexts}
- * @access private
- */
- var multilingualLocalizedTexts = {};
- multilingualLocalizedTexts[ORIGINAL_LOCALE] = {};
- /**
- * テキストをクライアントの言語に変換する
- * @param {string} message - 翻訳前
- * @returns {string} 翻訳後
- */
- window._ = window.gettext = function (message) {
- // クライアントの言語の翻訳リソースが存在すれば、それを返す
- return langtag in multilingualLocalizedTexts && multilingualLocalizedTexts[langtag][message]
- // 地域下位タグを取り除いた言語タグの翻訳リソースが存在すれば、それを返す
- || language in multilingualLocalizedTexts && multilingualLocalizedTexts[language][message]
- // デフォルト言語の翻訳リソースが存在すれば、それを返す
- || DEFAULT_LOCALE in multilingualLocalizedTexts && multilingualLocalizedTexts[DEFAULT_LOCALE][message]
- // そのまま返す
- || message;
- };
- /**
- * {@link gettext}から参照されるクライアントの言語を設定する
- * @param {string} lang - IETF言語タグ(「language」と「language-REGION」にのみ対応)
- */
- window.setlang = function (lang) {
- lang = lang.split('-', 2);
- language = lang[0].toLowerCase();
- langtag = language + (lang[1] ? '-' + lang[1].toUpperCase() : '');
- };
- /**
- * {@link gettext}から参照される翻訳リソースを追加する
- * @param {LocalizedTexts} localizedTexts
- */
- window.setLocalizedTexts = function (localizedTexts) {
- var localizedText, lang, language, langtag, msgid;
- for (lang in localizedTexts) {
- localizedText = localizedTexts[lang];
- lang = lang.split('-');
- language = lang[0].toLowerCase();
- langtag = language + (lang[1] ? '-' + lang[1].toUpperCase() : '');
- if (langtag in multilingualLocalizedTexts) {
- // すでに該当言語の翻訳リソースが存在すれば、統合する(同じmsgidがあれば上書き)
- for (msgid in localizedText) {
- multilingualLocalizedTexts[langtag][msgid] = localizedText[msgid];
- }
- } else {
- multilingualLocalizedTexts[langtag] = localizedText;
- }
- if (language !== langtag) {
- // 言語タグに地域下位タグが含まれていれば
- // 地域下位タグを取り除いた言語タグも翻訳リソースとして追加する
- if (language in multilingualLocalizedTexts) {
- // すでに該当言語の翻訳リソースが存在すれば、統合する(同じmsgidがあれば無視)
- for (msgid in localizedText) {
- if (!(msgid in multilingualLocalizedTexts[language])) {
- multilingualLocalizedTexts[language][msgid] = localizedText[msgid];
- }
- }
- } else {
- multilingualLocalizedTexts[language] = localizedText;
- }
- }
- // msgidの言語の翻訳リソースを生成
- for (msgid in localizedText) {
- multilingualLocalizedTexts[ORIGINAL_LOCALE][msgid] = msgid;
- }
- }
- };
- })();
- // Polyfill for Blink
- if (!String.prototype.hasOwnProperty('startsWith')) {
- /**
- * Determines whether a string begins with the characters of another string, returning true or false as appropriate.
- * @param {string} searchString - The characters to be searched for at the start of this string.
- * @param {number} [position=0] - The position in this string at which to begin searching for searchString.
- * @returns {boolean}
- * @see {@link http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.startswith 21.1.3.18 String.prototype.startsWith (searchString [, position ] )}
- * @see {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith String.startsWith - JavaScript | MDN}
- * @version polyfill-2013-11-05
- * @name String.prototype.startsWith
- */
- Object.defineProperty(String.prototype, 'startsWith', {
- writable: true,
- enumerable: false,
- configurable: true,
- value: function (searchString) {
- var position = arguments[1];
- return this.indexOf(searchString, position) === Math.max(Math.floor(position) || 0, 0);
- },
- });
- }
- if (!String.prototype.hasOwnProperty('contains')) {
- /**
- * Determines whether one string may be found within another string, returning true or false as appropriate.
- * @param {string} searchString - A string to be searched for within this string.
- * @param {number} [position=0] - The position in this string at which to begin searching for searchString.
- * @returns {boolean}
- * @see {@link http://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.contains 21.1.3.6 String.prototype.contains (searchString, position = 0 )}
- * @see {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/contains String.contains - JavaScript | MDN}
- * @version polyfill-2013-11-05
- * @name String.prototype.contains
- */
- Object.defineProperty(String.prototype, 'contains', {
- writable: true,
- enumerable: false,
- configurable: true,
- value: function (searchString) {
- return this.indexOf(searchString, arguments[1]) !== -1;
- },
- });
- }
- }
- })();