Greasy Fork is available in English.

pixiv 検索オプションを追加

This is a search option add-on script for pixiv. Enables you to select search mode by radio buttons (legacy feature).

ของเมื่อวันที่ 30-07-2015 ดู เวอร์ชันล่าสุด

// ==UserScript==
// @name        pixiv 検索オプションを追加
// @name:ja     pixiv 検索オプションを追加
// @namespace   http://loda.jp/script/
// @id          pixiv-search-box-347021
// @version     2.6.0
// @description This is a search option add-on script for pixiv. Enables you to select search mode by radio buttons (legacy feature).
// @description:ja 検索オプションを、以前のようにラジオボタンで選択出来るようにする。
// @match       http://www.pixiv.net/*
// @exclude     http://www.pixiv.net/member_illust.php?*mode=manga*
// @exclude     http://www.pixiv.net/member_illust.php?*mode=big*
// @exclude     http://www.pixiv.net/apps.php*
// @domain      www.pixiv.net
// @match       https://booth.pm/*
// @exclude     https://booth.pm/users/*
// @domain      booth.pm
// @run-at      document-start
// @grant       dummy
// @icon        data:image/vnd.microsoft.icon;base64,AAABAAEAMDAAAAEAIADnDAAAFgAAAIlQTkcNChoKAAAADUlIRFIAAAAwAAAAMAgGAAAAVwL5hwAADK5JREFUaIHFmXuwVdV9xz9rrf0459z3hXsFUUAhIBQBUTQVJT5iCKbR2Gga22qatnl1apM2M4ZmGitpp7F5TB7TmiY6JpOZmDEmxhjUpEZrjVoeopjyEigI8r4P7vOcvfd69Y997rn3wgUutsDvzDp7zzlrr/V7/77rtwXAih9t4L7bF/Klxzfytx+YB7w3+uZj904wVk/YuKd7nhRCc4ZIgHfeq/cuPG/dL9fvqjx0962HAOb//fPityuv8UJIvHfD8z/14Bq+/edXsPwLj0ZP/8Nt2S0rH33XjYsvuLu9teXGaZNbQSqEEGeKf/D5V5qmbN6130mrv37n577zIK8/sPX3vvgzteqeW+xRAsN1K34UPnff7foP73v8nz66bMHd9U3NCiG8ybQXQuRrnlFyCCRhHMusUmbfwY4D//z9Jz7+2vc/vypuniSmLvsLv/2Re3IBPv3Qy3zzT6/kPSt++NnPfXjJV01QtNJbhcngLLA+mgREJQSWXbv39a782neX7332u+sBjZAe73IL8K7Pz/jJP96xNigUm1RYUCS9gDyrrA+TwxdbKZHw5AuvPPf1u2758Owb7ux745kfpAABwMqPLfujJDOtYakIvZ3knmVPtOqZJXOESlRnFs59x3WFqZddMj04vG5b25zMd2zxAZd9sqFvMLnZq4BypQJJBlKBP9vuUyUhAINzoayPI+Zff9MnnvnePRumLHp3uLdjSxYsmDOpBH5mWZSw5UHQjtz3T0UAAXi8B+89rvqor64hqp4qZX53akktn+wVAhxTJk+ZuxbqfdJXBrKgXvnYexrLmcVWEtB63DtIIXDOkRqLFILGUkxDKaalLkZKQRwGCAGDSYb30NFbppIa+pOMQEqiQGG9z3V1ki19kAlrtSuWSq1Ag0kGewCCtFKWqTaUU41NNFgzLsZzxjRtjSUWzWyjpb7A9PYm4lARBYowkIRK4YFKqvFAqg1JZti+/wg7D/awfV839cUI5/3JPVYZjNNoYwVQ1GkSAARZlohUWyqpwWkD5sQCeO9RUtJUF7P8qhnMmNxCMQrwgLEO5zzGOXYf6KW3nBErxfRJTUSBohiH1BciJjaVWDzrXPYc7uXpV3ZyqGew5l7H3VcZjDdk2gAoZ60ARACgrSMzFptZcMdmHwFY5/HeM629iWsXTGPBBe1Y57HO09lXZtu+bjbv6eTNQ72kxuL9kF/k1ziUzJvWziUzzmHWlFaUFMyY3MKn3reIZ17bxW82vYVzHiGOE33a4LzJXQ6ExwvAB8Y6Mm1JM51bwI4WQElBaiwTG4osnT+VK+dMoRAFJNqQZpbHX97GzkM97OvsJwwUgZIjQsjXrpXU8vLmvazeuo+FM87h96+cTX0xBODGxRcy49xmHnh6A8a4MaGLDzTOGjJtPSB81ecC53OTp9rgjB0VA0IIstQy5/wJfGL5JQRSIoUg05YoUPx605u8sGkPgZIoJXDekRl3zOYjyTrP+u0H2LG/m7+6aTFtTSW0ccyc3MJHb5jPvz31GtaOUYMyi/MWa/P1h2JGGpNbINOaTJvq/ZBVDO+7dCaf+cDlRIFCW4d1wwxKKTDW1+aPd2jj6OpL+NdV60kyXc1mnllTWlk0YxJJls8Z5iVXcJoZtM2T9JAFpPcebRxJqkkyS6Itmcmvf7ZsAR+8ejbWOg4eGeTbT75Kd39SS3nGOJLM5NlF23GPSmaopJqdB3p46pWdOYauKuSmd76DUhzkCtSWRJuqu2qSzGCd9SOdMzDOkVnr00wLrw0CS+9gyhfvWMqimZMoZ4au3jIrH/4NUaBQqoqRBBjnyIxBSsnbAX7GelZv2ce186fRWBejraO9ucS86W088+ougkDWXMVLQ+BzDxnTApk2ZCY310Al48JzmskyC87ztcfW0tVbwVqPdx58rjLnPKkebepTGc55Nuw6xOu7DiM8CA8DFc3tS+dSzgx6pGtmmjSzODdaUdJVU6E2llRrUm2JwoCH/3MzDaWIJ9buYOPujty1rK3q2dcCMjM2F9zYtzEc1noOHhmopU/vPfWliJmTW+hPMlJj0dbhnENbKwaTzAE1QYLuvrJXnX22XZaChtCBd4RK8sgLm7l4WhvFMCDVFikgMw7vPUMf6xyptih54sxzMnpx015uXXIRzjuEFBjruGb+VNZtP0BTKaa3nNJnUt/Z3U9vR5cFRJoXNIJykvndh3rt7l4flKShsRjSWCrQVBfx5Z+upqW+gDYWIUAbW4NnILA2t0Ag3/6pzXs40N2PdW4UBBtMDFt2dyKEYCDJICiAM2BGZ6EAked7vKOSGsqVlINd/YCgEIfEkaIUh8SBoqEY56arPmycy/0yyANbiGHkWWNwCKVWNx1Cq955nM/d1zmHdR4hBAKPl3C4Z4DBjj5EU6la2FwN3Y6kgCF+nAU8CIGQuUMm2pBkmt7+Crj88czYnAE83f0J2/d3E4WqBpPliDO0gBpQ8x6sczjvyeMuZ9qmmisunoaSMq8xVaDY2VeBIM9uOX9uzDNKMFJXI81aIyGqYxin5OhGoLWlXE4pK3kKByAx6kJiuO2q2bnwUlTxkGTrW10QqBHLjh1nwZi/jpfyUMgh29ttvcQB505oqDEqBHT0DLLq+U0EDUWMPXGCOLsnd+tYcskFXLdgOpC7VnNdgZUPvwTF6KicP7aC5OjbM9TA8hAFCqTgrvcvorkuRghBMQ5Zv/0Aj720FaUUbqRbCjnmSVHWVhRnzhhKCbJM841P3sCHls4l1XmaTjLDvT98kb7BDOeOcp3juOgw1/LUBbDOQTnDO48UonrUFNV0Wt2X4ewkBJDk6PP+u5bz6ZsX09VXAQGlOOT+VetZtXpbDdwdy+qxQgwHsZD58OOrqpmxLLv0Qrr++Goefn4zh97sACUgDvP0J+TwcsbhKxmEktuXLeRjyxdy9bzz6eyrUFcIqaSGzz7wLN/4yWoI1NgbSpljsGMEEOQpUKpTCgFjHRedP5GVdyzlM7dcTpIZVm/dz1PrdrCno48jAwnlRFOMQ6a2NfKRd1/M4lmTaW8uESjJYKKpK4T8dtdh7vzqL9i2r/v4zAMIBeLY8/oIC1Rz4jgpUJL12w+w80AP1yyYRl0h5NarZnPn9fPGnJ9VQdkQzn9l20GeWLON+3+6BlmI8hpzolJyHP6qAniQp1YS4lDx1LqdfOl7/8HFc8+jvbmOyy+azKIZk1BS0lAMkTI/H1vnKaeaTFt+9eou3tjbxeot+9ADCWF9AWPdybGUVGMGcpBXIjE84UQFacR/3nsa62IoRfz3zkPgPc9u2EUUhUgpiJTMMYzI4YQxDmMdOtXgPIQKUYrRzo+vkSZVNU6PEkDkuwivgnzSCRtbRwO1/DcRqJoGtfVg84POseQhCI56fpykcgGEUMKDHxI6EFLkEa7CXAA5diB55yhndoSfClLjIHPIuhA3Tm5OGXEMzVchqAAh5SjBg1AFqCiUvhhDFoFyo1VThdtxoPjL919KUynOzwXes/R3zuODN8zn52t2UArVMce9/xcakjiOgQwqIRWqfVkgiEJFFEfOxwGUYkjcKDVJIahkhr+++TK+8AdXMFDJgFzGJXOmMOvcVv5r68Fqy+U0tuTrIiDCB4oK1JpfQeQzUyxEfU6YiaKuDoQ9KlghLoR85eevce8jawnUcMUewiptTSVCpYhOG5QSUIjARsKKrB8QwufaClTf3qQUssMrP1E0NoOtHBMHHk9DqYCSY7T8PCeFvP9X3kHgowCRSQYHDu4EhHCZBQgOb36pp+13P/RcLMw7aW2FSvdxA/nskMgzV33JSz0odq/72YthXYs35V4NeSFzAxv//enWaz/ycan7JjJlKnQfBnUG4fWJyFloPw850Cu79254PRvo7lRhodc7lwEEE6bPY/eaJ19tnjr3X6Ze9p57fX1sfHyOEoMD4mTvCk47FQr4Qp1X0otKf8fg9l89+Avv3GGTlo8AuQW63tzoJs5cxOuPfuUHQpcLc66/bUU4oQ3d0miQUoqTvXk4DeSHWhk6c5GQQc+OrYOrH1rxnXLX/jeAt4Aeqq9Rh1iTCNGI9xc0T5q+eOmf/N3fTJk9f/Z43l2dLhJCkKap2/jsj19c8+Nv/VoqdcBZuxnYAXQDpiaACiI8PhBCNMX1zVPLRw5PBGYtWH7Hkrrmia31Le1Ntd7I6SYpfMfuHZ0DXft7tq/+5QagEhXre7LKwP8Ae6rMa6rl9mj9KqmCUrGhpS0tD0w0WaUeKAEhecCfCXsM2V1HxTrjrOk2WXoYRCf4fnLXGQFojiVBDvIKCFkECkAEfigtnU4zVF844wDtvUuBSnUYxmgO/S9jF1Zlift4fQAAAABJRU5ErkJggg==
// @author      100の人
// @homepage    https://greasyfork.org/scripts/265
// @license     Creative Commons Attribution 4.0 International Public License; http://creativecommons.org/licenses/by/4.0/
// ==/UserScript==

(function () {
'use strict';

polyfill();

// L10N
Gettext.setLocalizedTexts({
	'en': {
		'完全一致': 'Exact match',
		'部分一致': 'Partial match',
		'タイトル・キャプション': 'Title/Description',
		'小説': 'Novels',
		'タグ': 'Tags',
		'キーワード': 'Keyword',
		'本文': 'Content',
		'ユーザー': 'User',
		'グループ': 'Groups',
		'BOOTH': '',
		'すべて': 'All',
		'検索': 'Search',
		'小説検索': 'Search novel',
		'ユーザー検索': 'Search user',
		'グループ検索': 'Search group',
		'商品検索...': 'Search',
		'https://booth.pm/ja/search/': 'https://booth.pm/en/search/',
	},
	'fr': {
		'完全一致': 'Concordance parfaite',
		'部分一致': 'Concordance partielle',
		'タイトル・キャプション': 'Titre, Légende',
		'小説': 'Roman',
		'タグ': 'Mots-clés',
		'キーワード': 'Mots-clés',
		'本文': 'Contenu',
		'ユーザー': 'Utilisateur',
		'グループ': 'Groups',
		'BOOTH': '',
		'すべて': 'Tout',
		'検索': 'Rechercher',
		'小説検索': 'Rechercher un roman',
		'ユーザー検索': '',
		'グループ検索': 'Rechercher un groupe',
		'商品検索...': '',
		'https://booth.pm/ja/search/': '',
	},
	'ko': {
		'完全一致': '완전 일치',
		'部分一致': '부분 일치',
		'タイトル・キャプション': '제목・캡션',
		'小説': '소설',
		'タグ': '태그',
		'キーワード': '키워드',
		'本文': '본문',
		'ユーザー': '유저',
		'BOOTH': '',
		'グループ': '그룹',
		'すべて': '전체',
		'検索': '검색',
		'小説検索': '소설 검색',
		'ユーザー検索': '유저 검색',
		'グループ検索': '그룹 검색',
		'商品検索...': '',
		'https://booth.pm/ja/search/': '',
	},
	'ru': {
		'完全一致': 'Полное совпадение',
		'部分一致': 'Частичное совпадение',
		'タイトル・キャプション': 'Заголовок',
		'小説': 'Рассказы',
		'タグ': 'Метка',
		'キーワード': 'Ключевые слова',
		'本文': 'Текст',
		'ユーザー': 'Пользователь',
		'グループ': 'Группа',
		'BOOTH': '',
		'すべて': 'Все',
		'検索': 'Поиск',
		'小説検索': 'Искать рассказ',
		'ユーザー検索': 'Искать пользователя',
		'グループ検索': 'Искать группу',
		'商品検索...': '',
		'https://booth.pm/ja/search/': '',
	},
	'th': {
		'完全一致': '',
		'部分一致': '',
		'タイトル・キャプション': 'ชื่อและคำบรรยาย',
		'小説': 'นิยาย',
		'タグ': 'แท็ก',
		'キーワード': 'คีย์เวิร์ด',
		'本文': '',
		'ユーザー': 'ผู้ใช้',
		'グループ': '',
		'BOOTH': '',
		'すべて': 'ทั้งหมด',
		'検索': 'ค้นหา',
		'小説検索': '',
		'ユーザー検索': '',
		'グループ検索': '',
		'商品検索...': '',
		'https://booth.pm/ja/search/': '',
	},
	'zh': {
		'完全一致': '完全相同',
		'部分一致': '部分相同',
		'タイトル・キャプション': '题目/简述',
		'小説': '小说',
		'タグ': '标签',
		'キーワード': '关键词',
		'本文': '内容',
		'ユーザー': '用户',
		'グループ': '群组',
		'BOOTH': '',
		'すべて': '全部',
		'検索': '搜索',
		'小説検索': '搜索小说',
		'ユーザー検索': '搜索用户',
		'グループ検索': '搜索群组',
		'商品検索...': '',
		'https://booth.pm/ja/search/': '',
	},
	'zh-tw': {
		'完全一致': '完全相同',
		'部分一致': '部分相同',
		'タイトル・キャプション': '題目/簡述',
		'小説': '小說',
		'タグ': '標籤',
		'キーワード': '關鍵詞',
		'本文': '內容',
		'ユーザー': '用戶',
		'グループ': '群組',
		'BOOTH': '',
		'すべて': '全部',
		'検索': '搜索',
		'小説検索': '搜索小說',
		'ユーザー検索': '搜索用戶',
		'グループ検索': '搜索群組',
		'商品検索...': '',
		'https://booth.pm/ja/search/': '',
	},
	'es': {
		'完全一致': 'Coincidencia exacta',
		'部分一致': 'Coincidencia parcial',
		'タイトル・キャプション': 'Título/Descripción',
		'小説': 'Novelas',
		'タグ': 'Etiquetas',
		'キーワード': 'Palabra clave',
		'本文': 'Mensaje',
		'ユーザー': 'Usuarios',
		'グループ': 'Grupo',
		'BOOTH': '',
		'すべて': 'Todos',
		'検索': 'Buscar',
		'小説検索': 'Buscar novela',
		'ユーザー検索': 'Buscar usuario',
		'グループ検索': 'Buscar grupo',
		'商品検索...': '',
		'https://booth.pm/ja/search/': '',
	},
});



if (window.location.host === 'booth.pm') {
	startScript(main,
			function (parent) { return parent.localName === 'nav'; },
			function (target) { return target.classList.contains('ctrl-nav'); },
			function () { return document.getElementsByClassName('ctrl-nav')[0]; }, {
				isTargetParent: function (parent) { return parent.localName === 'html'; },
				isTarget: function (target) { return target.localName === 'body'; },
			});
} else {
	startScript(main,
			function (parent) { return parent.localName === 'body'; },
			function (target) { return target.id === 'wrapper'; },
			function () { return document.getElementById('wrapper'); });
}

function main() {
	var loggedin = !document.body.classList.contains('not-logged-in'), booth = window.location.host === 'booth.pm',
			form, submitButton, word = document.getElementById(booth ? 'query' : 'suggest-input'), mode, subMode, tabs,
			input, searchParams;

	// 言語の設定
	Gettext.setLocale(booth ? window.navigator.language : document.documentElement.lang);

	// スタイルシートの設定
	document.head.insertAdjacentHTML('beforeend', '<style> \
		.ui-search, \
		.global-nav .item-search-box .item-search { \
			display: flex; \
			align-items: center; \
			width: initial; \
			/* BOOTH */ \
			max-width: initial; \
		} \
		.ui-search { \
			background: #FFFFFF; \
			padding: 0.4em 2em; \
			border-left: 1px solid #D6DEE5; \
			border-right: 1px solid #D6DEE5; \
			border-bottom: 1px solid #D6DEE5; \
			border-radius: 0 0 5px 5px; \
			justify-content: center; \
			/* 通知が検索窓に隠れる問題の修正 */ \
			z-index: 98; \
			/* トップページ・pixivについて */ \
			margin-bottom: 10px; \
		} \
		.not-logged-in .ui-search { \
			/* ログイン前 */ \
			border-top: initial; \
			border-top-left-radius: initial; \
			border-top-right-radius: initial; \
		} \
		\
		/*------------------------------------ \
			検索窓 \
		*/ \
		.ui-search .container, \
		.global-nav .item-search-box .twitter-typeahead { \
			flex-grow: 1; \
			flex-shrink: 0; \
			width: initial; \
			max-width: 250px; \
		} \
		.ui-search div.container { \
			border-radius: 0; \
		} \
		.global-nav .item-search-box .twitter-typeahead { \
			max-width: 356px; \
		} \
		#suggest-input { \
			width: 100%; \
		} \
		\
		/*------------------------------------ \
			送信ボタン \
		*/ \
		.ui-search input.submit { \
			position: static; \
			margin-right: 1em; \
		} \
		\
		/*------------------------------------ \
			検索オプション \
		*/ \
		.ui-search label, \
		.item-search label { \
			padding: 0 0.7em; \
			display: flex; \
			align-items: center; \
			white-space: nowrap; \
		} \
		.ui-search label input { \
			margin-right: 0.3em; \
			width: initial; \
		} \
		.item-search label, \
		.item-search .sub-options { \
			/* BOOTH */ \
			font-size: 12px; \
			font-weight: normal; \
		} \
		\
		/*------------------------------------ \
			副検索モード \
		*/ \
		.sub-options label:not(:first-of-type) { \
			display: none; \
			position: absolute; \
			z-index: 1; \
			width: 13em; \
			height: 2em; \
			border: solid 1px #D6DEE5; \
			border-top: none; \
			border-bottom: none; \
			background: #FFFFFF; \
		} \
		.sub-options::after { \
			content: ""; \
			padding: 0 0.7em; \
			display: none; \
			position: absolute; \
			z-index: 1; \
			width: 13em; \
			height: 17px; \
			border: solid 1px #FFFFFF; \
			border-top: none; \
			border-bottom: none; \
			bottom: 0; \
		} \
		.item-search .sub-options::after { \
			/* BOOTH */ \
			top: 23px; \
			bottom: initial; \
		} \
		.sub-options label:nth-of-type(3) { \
			margin-top: 2em; \
		} \
		.sub-options label:nth-of-type(4) { \
			margin-top: 4em; \
		} \
		.sub-options label:nth-of-type(5) { \
			margin-top: 6em; \
		} \
		.sub-options label:last-of-type { \
			border-bottom: 1px solid #D6DEE5; \
			border-radius: 0 0 5px 5px; \
		} \
		.sub-options:hover label { \
			display: flex; \
		} \
		.sub-options:hover::after { \
			display: block; \
		} \
		\
		/*------------------------------------ \
			副検索モードが右側にはみ出す問題に対処 \
		*/ \
		.ctrl-nav { \
			/* お買い物ガイド */ \
			position: relative; \
			z-index: 1; \
		} \
		.ctrl-nav::after { \
			content: ""; \
			position: absolute; \
			width: 100%; \
			border-bottom: solid 1px #D3D3D3; \
			left: -1px; \
			top: 48px; \
		} \
		\
		/*------------------------------------ \
			ページ本体 \
		*/ \
		#wrapper { \
			margin-top: initial; \
		} \
		\
		/*==================================== \
			移動元のスペースを詰める \
		*/ \
		/*------------------------------------ \
			ヘッダ \
		*/ \
		.header .layout-wrapper { \
			height: 82px; \
		} \
		/*------------------------------------ \
			サイト名 \
		*/ \
		.header-logo { \
			display: none; \
		} \
		/*------------------------------------ \
			ヘッダ内広告 \
		*/ \
		#header-banner { \
			margin-left: 990px; \
			width: calc(50% - 515px); \
		} \
		#header-banner .multi-ads-area, \
		#header-banner .multi-ads-area > div, \
		#header-banner .multi-ads-area > div > div iframe, \
		#header-banner .multi-ads-area > iframe { \
			width: 100% !important; \
		} \
		\
		/*==================================== \
			BOOTHについて、表示領域の幅が狭い場合 \
		*/ \
		@media screen and (max-width: 1250px) { \
			/*------------------------------------ \
				検索フォーム \
			*/ \
			.global-nav .item-search-box .item-search { \
				position: absolute; \
				top: 48px; \
				left: 0; \
				right: -421px; \
				height: 30px; \
				margin-top: 1px; \
			} \
			\
			/*------------------------------------ \
				検索窓\
			*/ \
			.global-nav .item-search-box .twitter-typeahead { \
				position: absolute !important; \
				width: 356px; \
				top: -40px; \
				left: 150px; \
			} \
			\
			/*------------------------------------ \
				副検索モード \
			*/ \
			.item-search :nth-last-child(-n+2).sub-options label:not(:first-of-type) { \
				right: initial; \
			} \
			.item-search :nth-last-child(-n+2).sub-options::after { \
				right: initial; \
				border-right-color: #FFFFFF; \
			} \
			\
			/*------------------------------------ \
				ページ本体 \
			*/ \
			.page-wrap { \
				padding-top: 78px; \
			} \
			\
			/*------------------------------------ \
				副検索モード \
			*/ \
			.item-search .sub-options::after { \
				height: 6px; \
				top: initial; \
				bottom: 1px; \
			} \
		} \
	</style>');

	/**
	 * 検索オプションを設定するラジオボタンのHTML文字列。
	 * @type {string}
	 */
	var optionsHTML = ' \
		<label> \
			<input data-placeholder="' + h(_('検索')) + '" data-word-name="word" data-form-action="http://www.pixiv.net/search.php" value="s_tag_full" name="s_mode" type="radio" />'
			+ h(_('完全一致')) +
		'</label> \
		<label> \
			<input data-placeholder="' + h(_('検索')) + '" data-word-name="word" data-form-action="http://www.pixiv.net/search.php" value="s_tag" name="s_mode" type="radio" />'
			+ h(_('部分一致')) +
		'</label> \
		<label> \
			<input data-placeholder="' + h(_('検索')) + '" data-word-name="word" data-form-action="http://www.pixiv.net/search.php" value="s_tc" name="s_mode" type="radio" />'
			+ h(_('タイトル・キャプション')) +
		'</label> \
		<div id="s_novel" class="sub-options" title="' + h(_('小説')) + '"> \
			<label> \
				<input data-placeholder="' + h(_('小説検索')) + '" data-word-name="word" data-form-action="http://www.pixiv.net/novel/search.php" value="s_novel" name="s_mode" type="radio" /> \
				<img alt="' + h(_('小説')) + '" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAARBAMAAADNtor0AAAAD1BMVEVldC2Spb6susrCzdr9//waY6iPAAAAAXRSTlMAQObYZgAAAEFJREFUCNdjYBAUZIABERdDBNNZAM6ECQsCmY6CQCDAIAJiugABJtPZ2NiEfFFDIROYqIuzMkzUxcXZCMIUhAEBACaMGgfSfm2+AAAAAElFTkSuQmCC" /> \
			</label> \
			<label> \
				<input data-mode-name="" data-word-name="tag" data-form-action="/novel/tags.php" value="s_tag_full" name="s_sub_mode" type="radio" /> \
				' + h(_('タグ')) + ' \
			</label> \
			<label> \
				<input value="s_tag" name="s_sub_mode" type="radio" /> \
				' + h(_('キーワード')) + ' \
			</label> \
			<label> \
				<input value="s_tc" name="s_sub_mode" type="radio" /> \
				' + h(_('本文')) + ' \
			</label> \
		</div>' +
		(loggedin ? '<label title="' + h(_('ユーザー')) + '">\
			<input data-placeholder="' + h(_('ユーザー検索')) + '" data-word-name="nick" data-form-action="http://www.pixiv.net/search_user.php" value="s_usr" name="s_mode" type="radio" /> \
				<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2048 2048" width="16px" height="16px"> \
					<title>' + h(_('ユーザー')) + '</title> \
					<path fill="#809DB8" d="M848,348 l98,-20 q28,0 54,22 q38,32 48,44 q22,26 34,26 q16,0 26,-4 q14,-6 52,2 q32,6 48,24 q18,20 23,42 t5,50 q0,32 2,48 q-4,24 -2,38 q6,36 6,62 t-6,44 t12,28 t38,10 h22 q28,0 40,46 q8,30 8,60 q0,14 -2,30 l-6,46 q-2,16 -6,22 q-6,10 -10,16 t-14,14 l-18,12 l-24,16 q-18,10 -30,18 q-22,62 -50,102 q-34,48 -58,70 l-22,22 q-10,96 -10,118 v6 q0,8 2,16 q0,8 4,22 q4,18 12,28 l20,28 q10,14 30,28 q8,6 46,26 q26,14 62,24 q46,12 82,18 q88,12 138,28 q60,18 102,44 t62,40 t50,46 q28,30 30,32 q20,22 40,98 q20,74 20,140 v66 h-1768 v-72 q0,-42 14,-126 q16,-92 34,-106 q8,-6 32,-34 t46,-48 t62,-46 q36,-24 100,-44 q66,-22 140,-32 q60,-8 102,-22 q40,-12 74,-32 t50,-34 q26,-24 34,-36 q10,-16 16,-30 q12,-18 10,-20 q-4,-6 -2,-6 v-132 q-4,-2 -12,-8 q-8,-4 -28,-22 q-14,-10 -40,-36 q-18,-18 -38,-50 t-32,-64 l-36,-18 q-20,-10 -24,-14 q-4,-2 -12,-18 q-8,-14 -8,-16 q0,-4 -2,-18 t-4,-22 q0,-4 2,-10 t5,-26 t3,-38 q0,-20 8,-38 q10,-32 12,-36 q8,-14 18,-24 t22,-10 h14 q8,-2 18,-12 q12,-12 8,-30 q2,-64 2,-134 q0,-22 8,-50 q10,-38 18,-58 q12,-32 38,-56 t60,-32 l52,-12 h-2 q2,0 6,-2 q10,-2 12,-2 v-6 v-4 q-2,-4 0,-6 q0,-2 8,-4 t22,-2 z" /> \
				</svg> \
			</label>' : '') +
		'<div id="s_group" class="sub-options" title="' + h(_('グループ')) + '"> \
			<label> \
				<input data-placeholder="' + h(_('グループ検索')) + '" data-mode-name="mode" data-word-name="word" data-form-action="http://www.pixiv.net/group/search_group.php" value="s_group" name="s_mode" type="radio" /> \
				<img alt="' + h(_('グループ')) + '" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAgMAAABinRfyAAAADFBMVEWLpryswdDV3ubr8POkfP3KAAAAT0lEQVQI12NYBQQMYGLd7/e/GFYtX/6LYfX27fsYVqU/i2JYwX4hjGEJk4MYw0Y2hSiGLUwNcgzrVr9aBxLjY1jCDOJGP93FsPrf/ldgUwDsuCQfQnDv1AAAAABJRU5ErkJggg==" /> \
			</label> \
			<label> \
				<input value="keyword_all" name="s_sub_mode" type="radio" />'
				+ h(_('すべて')) +
			'</label> \
			<label> \
				<input value="tag_full" name="s_sub_mode" type="radio" />'
				+ h(_('完全一致')) +
			'</label> \
			<label> \
				<input value="tag" name="s_sub_mode" type="radio" />'
				+ h(_('部分一致')) +
			'</label> \
			<label> \
				<input value="keyword" name="s_sub_mode" type="radio" />'
				+ h(_('タイトル・キャプション')) +
			'</label> \
		</div> \
		<label title="' + h(_('BOOTH')) + '"> \
			<input data-placeholder="' + h(_('商品検索...')) + '" data-form-action="' + h(_('https://booth.pm/ja/search/')) + '" value="s_booth" name="s_mode" type="radio"> \
			<img src="https://booth.pm/favicon.ico" width="16" alt="' + h(_('BOOTH')) + '" /> \
		</label>';

	var wrapper = document.getElementById('wrapper');
	if (loggedin) {
		// ログイン後、またはBOOTH
		form = word.form;
		submitButton = form.querySelector('[type="submit"]');

		word.required = true;

		if (booth) {
			// 送信ボタンの移動
			var observer;
			var containers = document.getElementsByClassName('twitter-typeahead');
			if (containers[0]) {
				containers[0].appendChild(submitButton);
			} else {
				observer = new MutationObserver(function (mutations, observer) {
					if (mutations.some(function (mutation) {
						return Array.prototype.some.call(mutation.addedNodes, function (node) {
							return node.nodeType === Node.ELEMENT_NODE && node.classList.contains('twitter-typeahead');
						});
					})) {
						observer.disconnect();
						containers[0].appendChild(submitButton);
					}
				});
				observer.observe(form, {
					childList: true,
				});
			}

			// ページが書き換えられたとき
			observer = new MutationObserver(function (mutations, observer) {
				if (mutations.some(function (mutation) { return mutation.target.id === 'mini-carts'; })) {
					var newForm = document.getElementsByClassName('item-search')[0];
					form.dataset.searchParams = newForm.dataset.searchParams;
					newForm.parentElement.replaceChild(form, newForm);
				}
			});
			observer.observe(document, {
				childList: true,
				subtree: true,
			});
		} else {
			// デフォルトの検索オプションを取得・削除
			input = form.s_mode || form.mode;
			subMode = mode = input ? input.value : 's_tag';
			if (input) {
				input.remove();
			}

			// フォームの移動
			wrapper.insertBefore(form, wrapper.firstChild);
		}

		// 検索オプションを設定するラジオボタンの追加
		form.insertAdjacentHTML('beforeend', optionsHTML);
	} else {
		// ログイン前
		// 検索語句の取得
		var searchWord;
		searchParams = new URLSearchParams(window.location.search.replace('?', ''));
		var smallForm = document.getElementsByClassName('search-small')[0];
		if (smallForm) {
			searchWord = smallForm.word.value;
			smallForm.remove();
			// デフォルトの検索オプションを取得
			input = smallForm.s_mode || smallForm.mode;
		} else {
			searchWord = searchParams.get('word') || '';
		}
		subMode = mode = input ? input.value : 's_tag';

		// 検索フォームを構築する
		wrapper.insertAdjacentHTML('afterbegin', '<form class="ui-search"> \
			<div class="container"> \
				<input required="" id="suggest-input" name="word" value="' + h(searchWord) + '" placeholder="' + h(_('検索')) + '"> \
			</div> \
			<input class="submit sprites-search-old" type="submit">'
			+ optionsHTML +
		'</form>');

		form = wrapper.firstElementChild;
		word = form.word;
		submitButton = form.querySelector('[type="submit"]');
	}

	if (booth) {
		mode = 's_booth';
	} else {
		var pathname = window.location.pathname;
		switch (pathname.split('/')[1]) {
			case 'search.php':
				if (!loggedin) {
					mode = subMode = searchParams.get('s_mode');
				}
				break;
			case 'novel':
				mode = 's_novel';
				if (!loggedin) {
					word.placeholder = _('小説検索');
					subMode = searchParams.get('s_mode') || 's_tag';
				}
				if (pathname === '/novel/tags.php') {
					subMode = 's_tag_full';
				}
				break;
			case 'group':
				mode = 's_group';
				if (!loggedin) {
					word.placeholder = _('グループ検索');
					subMode = searchParams.get('mode') || 'keyword_all';
				}
				break;
		}
	}

	form.addEventListener('submit', function (event) {
		var searchMode, subOption, hiddenParams, i, l;

		if (!/\S/.test(word.value)) {
			// 空白以外の文字が入力されていなければ
			word.value = '';
			return;
		}

		searchMode = form.querySelector('[name="s_mode"]:checked');
		if (booth) {
			if (searchMode.value === 's_booth') {
				// BOOTHからBOOTHに移動するとき
				return;
			} else {
				// BOOTHからpixivに移動するとき
				event.stopPropagation();
				event.preventDefault();
				submitButton.removeAttribute('name');
			}
		} else {
			if (searchMode.value === 's_booth') {
				// pixivからBOOTHに移動するとき
				event.preventDefault();
				window.location.assign(searchMode.dataset.formAction + encodeURIComponent(word.value));
				return;
			}
		}

		subOption = form.querySelector('[name="s_sub_mode"]:checked');

		if ((subOption || searchMode).value.endsWith('tag_full') && /[^  ][  ]+[^  ]/.test(word.value)) {
			// 完全一致タグ検索で、先頭・末尾以外に半角スペース・全角スペースが含まれていれば
			if (subOption) {
				searchMode.checked = false;
				searchMode.click();
			} else {
				// イラスト
				searchMode.value = 's_tag';
			}
		}

		form.action = searchMode.dataset.formAction;
		if (searchMode.dataset.wordName) {
			word.name = searchMode.dataset.wordName;
		}
		if (searchMode.dataset.modeName) {
			searchMode.name = searchMode.dataset.modeName;
		} else if (searchMode.dataset.modeName === '') {
			searchMode.removeAttribute('name');
		}

		if (subOption) {
			// 副検索モードが存在すれば
			searchMode.value = subOption.value;
			subOption.removeAttribute('name');
			if (subOption.dataset.formAction) {
				form.action = subOption.dataset.formAction;
			}
			if (subOption.dataset.wordName) {
				word.name = subOption.dataset.wordName;
			}
			if (subOption.dataset.modeName) {
				searchMode.name = subOption.dataset.modeName;
			} else if (subOption.dataset.modeName === '') {
				searchMode.removeAttribute('name');
			}
		}

		if (form.action !== window.location.origin + window.location.pathname) {
			// サービス間をまたぐ場合、不要なパラメーターは送信しない
			Array.prototype.forEach.call(form.querySelectorAll('[type="hidden"]'), function (hiddenParam) {
				hiddenParam.disabled = true;
			});
		}

		if (event.defaultPrevented) {
			// httpsからhttpに移動する際のセキュリティ警告を回避
			var searchParams = new URLSearchParams();
			Array.prototype.forEach.call(form, function (element) {
				if (element.name && !element.disabled && (element.type !== 'checkbox' && element.type !== 'radio' || element.checked)) {
					searchParams.append(element.name, element.value);
				}
			});
			window.location.assign(form.action + '?' + searchParams);
		}
	});

	// 副検索モードの選択
	form.addEventListener('change', function (event) {
		var subOptions = event.target.parentElement.parentElement, subOption;
		if (event.target.name === 's_mode') {
			// 検索モードの選択なら
			if (subOptions.classList.contains('sub-options')) {
				// 副検索モードが存在すれば
				subOptions.querySelector('[value="s_tag"], [value="keyword_all"]').checked = true;
			} else {
				subOption = form.querySelector('[name="s_sub_mode"]:checked');
				if (subOption) {
					subOption.checked = false;
				}
			}
			word.placeholder = event.target.dataset.placeholder;
		} else if (event.target.name === 's_sub_mode') {
			// 副検索モードの選択なら
			subOption = subOptions.firstElementChild.firstElementChild;
			subOption.checked = true;
			word.placeholder = subOption.dataset.placeholder;
		}
	});

	// ラベルをダブルクリックで検索
	form.addEventListener('dblclick', function (event) {
		var target = event.target;
		if (target['matches' in target ? 'matches' : /* Firefox */ 'mozMatchesSelector']('label, label *')) { // Bug 886308 – Implement Element.matches() <https://bugzilla.mozilla.org/show_bug.cgi?id=886308>
			submitButton.click();
		}
	});

	// 現在の検索モードを設定
	form.querySelector(mode === subMode ? '[value="' + mode + '"]' : '#' + mode + ' [value="' + subMode + '"]').click();

	// 副検索モードが右側にはみ出す問題に対処
	if (!booth && document.getElementById('s_group').offsetLeft > 795) {
		document.head.insertAdjacentHTML('beforeend', '<style> \
			#s_group label:not(:first-of-type) { \
				right: -1px; \
			} \
			#s_group::after { \
				right: -1px; \
				border-right-color: transparent; \
			} \
		</style>');
	}
}



/**
 * 挿入された節の親節が、目印となる節の親節か否かを返すコールバック関数。
 * @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]
 * @param {isTargetParent} [callbacksForFirefox.isTargetParent] - Firefoxにおける{@link isTargetParent}。
 * @param {isTarget} [callbacksForFirefox.isTarget] - Firefoxにおける{@link isTarget}。
 * @param {number} [timeoutSinceStopParsingDocument=0] - DOM構築完了後に監視を続けるミリ秒数。
 * @version 2014-11-25
 * @global
 */
function startScript(main, isTargetParent, isTarget, existsTarget) {
	/**
	 * {@link checkExistingTarget}で{@link startMain}を実行する間隔(ミリ秒)。
	 * @constant {number}
	 */
	var INTERVAL = 10;
	/**
	 * {@link checkExistingTarget}で{@link startMain}を実行する回数。
	 * @constant {number}
	 */
	var LIMIT = 500;

	/**
	 * 実行済みなら真。
	 * @type {boolean}
	 */
	var alreadyCalled = false;

	// 指定した節が既に存在していれば、即実行
	startMain();
	if (alreadyCalled) {
		return;
	}

	// FirefoxのMutationObserverは、HTMLのDOM構築に関して要素をまとめて挿入したと見なすため、isTargetParent、isTargetを変更
	var callbacksForFirefox = arguments[4];
	if (callbacksForFirefox && typeof MozSettingsEvent !== 'undefined') {
		isTargetParent = callbacksForFirefox.isTargetParent || isTargetParent;
		isTarget = callbacksForFirefox.isTarget || isTarget;
	}

	var observer = new MutationObserver(mutationCallback);
	observer.observe(document, {
		childList: true,
		subtree: true,
	});

	var timeoutSinceStopParsingDocument = arguments[5] || 0;
	if (document.readyState === 'complete') {
		// DOMの構築が完了していれば
		onDOMContentLoaded();
	} else {
		document.addEventListener('DOMContentLoaded', onDOMContentLoaded);
	}

	/**
	 * {@link startMain}を実行し、スクリプトが開始されていなければさらに{@link timeoutSinceStopParsingDocument}ミリ秒待機し、
	 * スクリプトが開始されていなければ{@link stopObserving}を実行する。
	 */
	function onDOMContentLoaded() {
		startMain();
		if (timeoutSinceStopParsingDocument === 0) {
			if (!alreadyCalled) {
				stopObserving();
			}
		} else {
			window.setTimeout(function () {
				if (!alreadyCalled) {
					stopObserving();
				}
			}, timeoutSinceStopParsingDocument);
		}
	}

	/**
	 * 目印となる節が挿入されたら、監視を停止し、{@link checkExistingTarget}を実行する。
	 * @param {MutationRecord[]} mutations - A list of MutationRecord objects.
	 * @param {MutationObserver} observer - The constructed MutationObserver object.
	 */
	function mutationCallback(mutations, observer) {
		for (var mutation of mutations) {
			var target = mutation.target;
			if (target.nodeType === Node.ELEMENT_NODE && isTargetParent(target)) {
				// 子が追加された節が要素節で、かつその節についてisTargetParentが真を返せば
				for (var addedNode of mutation.addedNodes) {
					if (addedNode.nodeType === Node.ELEMENT_NODE && isTarget(addedNode)) {
						// 追加された子が要素節で、かつその節についてisTargetが真を返せば
						observer.disconnect();
						checkExistingTarget(0);
						return;
					}
				}
			}
		}
	}

	/**
	 * {@link startMain}を実行し、スクリプトが開始されていなければ再度実行。
	 * @param {number} count - {@link startMain}を実行した回数。
	 */
	function checkExistingTarget(count) {
		startMain();
		if (!alreadyCalled && count < LIMIT) {
			window.setTimeout(checkExistingTarget, INTERVAL, count + 1);
		}
	}

	/**
	 * 指定した節が存在するか確認し、存在すれば{@link stopObserving}を実行しスクリプトを開始。
	 */
	function startMain() {
		if (!alreadyCalled && existsTarget()) {
			stopObserving();
			main();
		}
	}

	/**
	 * 監視を停止する。
	 */
	function stopObserving() {
		alreadyCalled = true;
		if (observer) {
			observer.disconnect();
		}
		document.removeEventListener('DOMContentLoaded', onDOMContentLoaded);
	}
}

/**
 * 国際化・地域化関数の読み込み、ECMAScriptとWHATWG仕様のPolyfill。
 */
function polyfill() {
	/**
	 * DOM関連のメソッド。
	 */
	var DOMUtils = {
		/**
		 * XMLの特殊文字を文字参照に置換する。
		 * @param {string} str - プレーンな文字列。
		 * @returns {string} HTMLとして扱われる文字列。
		 */
		convertSpecialCharactersToCharacterReferences: function (str) {
			return str.replace(/[&<>"']/g, function (specialCharcter) {
				return '&#x' + specialCharcter.charCodeAt(0).toString(16) + ';'
			});
		},
	};
	window.h = DOMUtils.convertSpecialCharactersToCharacterReferences;

	/**
	 * 以下のような形式の翻訳リソース。すべての言語について、msgidは欠けていないものとする。
	 * {@link Gettext.DEFAULT_LOCALE}のリソースを必ず含む。{@link Gettext.ORIGINAL_LOCALE}のリソースは無視される。
	 * {
	 *     'IETF言語タグ': {
	 *         '翻訳前 (msgid)': '翻訳後 (msgstr)',
	 *         ……
	 *     },
	 *     ……
	 * }
	 * @typedef {Object} LocalizedTexts
	 */

	/**
	 * i18n。
	 * @version 2014-07-10
	 */
	window.Gettext = {
		/**
		 * 翻訳対象文字列 (msgid) の言語。IETF言語タグの「language」サブタグ。
		 * @constant {string}
		 */
		ORIGINAL_LOCALE: 'ja',

		/**
		 * クライアントの言語の翻訳リソースが存在しないとき、どの言語に翻訳するか。IETF言語タグの「language」サブタグ。
		 * @constant {string}
		 */
		DEFAULT_LOCALE: 'en',

		/**
		 * 翻訳リソースを追加する。
		 * @param {LocalizedTexts} localizedTexts
		 */
		setLocalizedTexts: function (localizedTexts) {
			this.multilingualLocalizedTexts = localizedTexts;
		},

		/**
		 * クライアントの言語を設定する。
		 * @param {string} clientLang - IETF言語タグ(「language」と「language-REGION」にのみ対応)。
		 */
		setLocale: function (clientLang) {
			var splitedClientLang = clientLang.split('-', 2);
			this.language = splitedClientLang[0].toLowerCase();
			this.langtag = this.language + (splitedClientLang[1] ? '-' + splitedClientLang[1].toUpperCase() : '');
			if (this.language === 'ja') {
				// ja-JPをjaと同一視
				this.langtag = this.language;
			}
		},

		/**
		 * テキストをクライアントの言語に変換する。
		 * @param {string} message - 翻訳前。
		 * @returns {string} 翻訳後。
		 */
		gettext: function (message) {
			// クライアントの言語が翻訳元の言語なら、そのまま返す
			return this.langtag === this.ORIGINAL_LOCALE && message
					// クライアントの言語の翻訳リソースが存在すれば、それを返す
					|| this.langtag in this.multilingualLocalizedTexts && this.multilingualLocalizedTexts[this.langtag][message]
					// 地域下位タグを取り除いた言語タグの翻訳リソースが存在すれば、それを返す
					|| this.language in this.multilingualLocalizedTexts && this.multilingualLocalizedTexts[this.language][message]
					// 既定言語の翻訳リソースが存在すれば、それを返す
					|| this.DEFAULT_LOCALE in this.multilingualLocalizedTexts && this.multilingualLocalizedTexts[this.DEFAULT_LOCALE][message]
					// そのまま返す
					|| message;
		},

		/**
		 * クライアントの言語。{@link Gettext.setLocale}から変更される。
		 * @type {string}
		 * @access private
		 */
		langtag: 'ja',

		/**
		 * クライアントの言語のlanguage部分。{@link Gettext.setLocale}から変更される。
		 * @type {string}
		 * @access private
		 */
		language: 'ja',

		/**
		 * 翻訳リソース。{@link Gettext.setLocalizedTexts}から変更される。
		 * @type {LocalizedTexts}
		 * @access private
		 */
		multilingualLocalizedTexts: {},
	};
	window._ = Gettext.gettext.bind(Gettext);

	// Polyfill for Opera and Google Chrome
	if (typeof URLSearchParams === 'undefined') {
		/** @namespace URL */
		/**
		 * @param {string} input
		 * @returns {Array.<Array.<string>>} List of name-value pairs where both name and value hold a string.
		 * @see [application/x-www-form-urlencoded – URL Standard]{@link https://url.spec.whatwg.org/#concept-urlencoded-string-parser}
		 * @memberof URL
		 * @function
		 * @private
		 */
		var parseXWWWFormUrlencoded = function (input) {
			var output = [];
			for (var bytes of input.split('&')) {
				if (bytes !== '') {
					bytes = bytes.replace(/\+/g, ' ');
					var index = bytes.indexOf('=');
					var name, value;
					if (index !== -1) {
						name = bytes.slice(0, index);
						value = bytes.slice(index + 1);
					} else {
						name = bytes;
						value = '';
					}
					output.push([decodeURIComponent(name), decodeURIComponent(value)]);
				}
			}
			return output;
		};
		/**
		 * @param {string} input - A byte sequence.
		 * @returns {string}
		 * @see [application/x-www-form-urlencoded – URL Standard]{@link https://url.spec.whatwg.org/#concept-urlencoded-byte-serializer}
		 * @memberof URL
		 * @function
		 * @private
		 */
		var serializeXWWWFormUrlencodedByte = function (input) {
			return encodeURIComponent(input).replace(/%20/g, '+').replace(/[!~'()]+/g, escape);
		};
		/**
		 * @param {Array.<Array.string>} pairs - List of name-value pairs pairs.
		 * @returns {string}
		 * @see [application/x-www-form-urlencoded – URL Standard]{@link https://url.spec.whatwg.org/#concept-urlencoded-serializer}
		 * @memberof URL
		 * @function
		 * @private
		 */
		var serializeXWWWFormUrlencodedString = function (pairs) {
			return pairs.map(function (pair) {
				return serializeXWWWFormUrlencodedByte(pair[0]) + '=' + serializeXWWWFormUrlencodedByte(pair[1]);
			}).join('&');
		};

		var _URLSearchParams_list = new WeakMap();
		var _URLSearchParams_urlObjects = new WeakMap();

		/**
		 * A URLSearchParams object has an associated list of name-value pairs, which is initially empty.
		 * @param {(string|URLSearchParams)} [init=""]
		 * @see [URLSearchParams Interface – URL Standard]{@link https://url.spec.whatwg.org/#interface-urlsearchparams}
		 * @version polyfill-2015-07-30
		 * @license CC-BY-4.0
		 * @constructor URLSearchParams
		 */
		Object.defineProperty(window, 'URLSearchParams', {
			writable: true,
			enumerable: false,
			configurable: true,
			value: function (init) {
				if (init === undefined) {
					init = '';
				} else if (!(init instanceof URLSearchParams) && typeof init !== 'string') {
					init = String(init);
				}

				/**
				 * A URLSearchParams object has an associated list of name-value pairs, which is initially empty.
				 * @type {Array.<Array.<string, string>>}
				 * @private
				 * @memberof URLSearchParams#
				 */
				var list = [];
				if (init instanceof URLSearchParams) {
					for (var pair of _URLSearchParams_list.get(init)) {
						list.push(pair[0], pair[1]);
					}
				} else if (init !== '') {
					list = parseXWWWFormUrlencoded(init);
				}
				_URLSearchParams_list.set(this, list);

				/**
				 * A URLSearchParams object has an associated list of zero or more url objects, which is initially empty. 
				 * @type {Set.<Array.<string, string>>}
				 * @private
				 * @member URLSearchParams#urlObjects
				 */
				_URLSearchParams_urlObjects.set(this, new Set());
			}
		});

		/**
		 * @see [URLSearchParams Interface – URL Standard]{@link https://url.spec.whatwg.org/#concept-URLSearchParams-update}
		 * @param {(URL|HTMLAnchorElement|HTMLAreaElement|Location)} [excluded]
		 * @function
		 * @private
		 * @function URLSearchParams#runQueryUpdateSteps
		 */
		var _URLSearchParams_runQueryUpdateSteps = function (excluded) {
			var list = _URLSearchParams_list.get(this);
			var search = excluded
					? excluded.search
					: (list[0] ? '?' + serializeXWWWFormUrlencodedString(list) : '');
			for (var urlObject of _URLSearchParams_urlObjects.get(this)) {
				if (urlObject !== excluded) {
					urlObject.search = search;
				}
			}
		};

		Object.defineProperties(URLSearchParams.prototype, /** @lends URLSearchParams# */ {
			/**
			 * Append a new name-value pair whose name is name and value is value, to the list of name-value pairs.
			 * @param {string} name
			 * @param {string} value
			 * @function
			 */
			append: {
				writable: true,
				enumerable: false,
				configurable: true,
				value: function (name, value) {
					if (arguments.length < 2) {
						throw new TypeError('Failed to execute \'append\' on \'URLSearchParams\': 2 argument required, but only ' + arguments.length + ' present.');
					}
					_URLSearchParams_list.get(this).push([String(name), String(value)]);
					_URLSearchParams_runQueryUpdateSteps.call(this);
				}
			},
			/**
			 * Remove all name-value pairs whose name is name.
			 * @param {string} name
			 * @function
			 */
			'delete': {
				writable: true,
				enumerable: false,
				configurable: true,
				value: function (name) {
					if (arguments.length < 1) {
						throw new TypeError('Failed to execute \'delete\' on \'URLSearchParams\': 1 argument required, but only ' + arguments.length + ' present.');
					}
					var list = _URLSearchParams_list.get(this);
					for (var i = 0, l = list.length; i < l; i++) {
						if (list[i][0] === name) {
							list.splice(i, 1);
							i--;
							l--;
						}
					}
					_URLSearchParams_runQueryUpdateSteps.call(this);
				}
			},
			/**
			 * Return the value of the first name-value pair whose name is name, and null if there is no such pair.
			 * @param {string} name
			 * @returns {?string}
			 * @function
			 */
			get: {
				writable: true,
				enumerable: false,
				configurable: true,
				value: function (name) {
					if (arguments.length < 1) {
						throw new TypeError('Failed to execute \'get\' on \'URLSearchParams\': 1 argument required, but only ' + arguments.length + ' present.');
					}
					for (var pair of _URLSearchParams_list.get(this)) {
						if (pair[0] === name) {
							return pair[1];
						}
					}
					return null;
				}
			},
			/**
			 * Return the values of all name-value pairs whose name is name, in list order, and the empty sequence otherwise.
			 * @param {string} name
			 * @returns {string[]}
			 * @function
			 */
			getAll: {
				writable: true,
				enumerable: false,
				configurable: true,
				value: function (name) {
					if (arguments.length < 1) {
						throw new TypeError('Failed to execute \'getAll\' on \'URLSearchParams\': 1 argument required, but only ' + arguments.length + ' present.');
					}
					var values = [];
					for (var pair of _URLSearchParams_list.get(this)) {
						if (pair[0] === name) {
							values.push(pair[1]);
						}
					}
					return values;
				}
			},
			/**
			 * If there are any name-value pairs whose name is name, set the value of the first such name-value pair to value and remove the others.
			 * Otherwise, append a new name-value pair whose name is name and value is value, to the list of name-value pairs.
			 * @param {string} name
			 * @param {string} value
			 * @function
			 */
			set: {
				writable: true,
				enumerable: false,
				configurable: true,
				value: function (name, value) {
					if (arguments.length < 2) {
						throw new TypeError('Failed to execute \'set\' on \'URLSearchParams\': 2 argument required, but only ' + arguments.length + ' present.');
					}
					var list = _URLSearchParams_list.get(this);
					var flag;
					for (var i = 0, l = list.length; i < l; i++) {
						if (list[i][0] === name) {
							if (flag) {
								list.splice(i, 1);
								i--;
								l--;
							} else {
								list[i][1] = String(value);
								flag = true;
							}
						}
					}
					if (!flag) {
						list.push([String(name), String(value)]);
					}
					_URLSearchParams_runQueryUpdateSteps.call(this);
				}
			},
			/**
			 * Return true if there is a name-value pair whose name is name, and false otherwise.
			 * @param {string} name
			 * @returns {boolean}
			 * @function
			 */
			has: {
				writable: true,
				enumerable: false,
				configurable: true,
				value: function (name) {
					if (arguments.length < 1) {
						throw new TypeError('Failed to execute \'has\' on \'URLSearchParams\': 1 argument required, but only ' + arguments.length + ' present.');
					}
					for (var pair of _URLSearchParams_list.get(this)) {
						if (pair[0] === name) {
							return true;
						}
					}
					return false;
				}
			},
			/**
			 * Return the serialization of the URLSearchParams object's associated list of name-value pairs.
			 * @returns {string}
			 * @function
			 */
			toString: {
				writable: true,
				enumerable: false,
				configurable: true,
				value: function () {
					return serializeXWWWFormUrlencodedString(_URLSearchParams_list.get(this));
				}
			},
			/**
			 * @returns {Iterator.<Array.<string, string>>}
			 * @function
			 */
			entries: {
				writable: true,
				enumerable: false,
				configurable: true,
				value: function* () {
					for (var pair of _URLSearchParams_list.get(this)) {
						yield [pair[0], pair[1]];
					}
				}
			},
			/**
			 * @returns {Iterator.<string>}
			 * @function
			 */
			keys: {
				writable: true,
				enumerable: false,
				configurable: true,
				value: function* () {
					for (var pair of _URLSearchParams_list.get(this)) {
						yield pair[0];
					}
				}
			},
			/**
			 * @returns {Iterator.<string>}
			 * @function
			 */
			values: {
				writable: true,
				enumerable: false,
				configurable: true,
				value: function* () {
					for (var pair of _URLSearchParams_list.get(this)) {
						yield pair[1];
					}
				}
			},
		});
		/**
		 * The value pairs to iterate over are the list name-value pairs with the key being the name and the value the value. 
		 * @returns {Iterator.<Array.<string, string>>}
		 * @function URLSearchParams#@@iterator
		 */
		Object.defineProperty(URLSearchParams.prototype, Symbol.iterator, {
			writable: true,
			enumerable: false,
			configurable: true,
			value: function* () {
				for (var pair of _URLSearchParams_list.get(this)) {
					yield [pair[0], pair[1]];
				}
			}
		});

		/** @namespace URLUtils */
		var _URLUtils_queryObject = new WeakMap();
		/**
		 * @member {URLSearchParams} URLUtilsSearchParams#searchParams
		 */
		for (var interfaceObject of [URL, HTMLAnchorElement, HTMLAreaElement]) {
			Object.defineProperty(interfaceObject.prototype, 'searchParams', {
				enumerable: false,
				configurable: true,
				get: function () {
					var queryObject = _URLUtils_queryObject.get(this);
					if (!queryObject) {
						// If query object is null,
						// set query object to a new URLSearchParams object using query, 
						queryObject = new URLSearchParams(this.search.replace('?', ''));
						_URLUtils_queryObject.set(this, queryObject);
						// and then append the context object to query object’s list of url objects.
						_URLSearchParams_urlObjects.get(queryObject).add(this);
					}
					return queryObject;
				},
				set: function (object) {
					if (!(object instanceof Object)) {
						throw new TypeError('Value being assigned to ' + Object.prototype.toString.call(this).match(/ (.+)\]/)[1] + '.searchParams is not an object.');
					} else if (!(object instanceof URLSearchParams)) {
						throw new TypeError('Value being assigned to ' + Object.prototype.toString.call(this).match(/ (.+)\]/)[1] + '.searchParams does not implement interface URLSearchParams.');
					}
					var queryObject = _URLUtils_queryObject.get(this);
					// Remove the context object from query object’s list of url objects.
					if (queryObject) {
						_URLSearchParams_urlObjects.get(queryObject).delete(this);
					}
					// Append the context object to object’s list of url objects.
					_URLSearchParams_urlObjects.get(object).add(this);
					// Set query object to object.
					_URLUtils_queryObject.set(this, object);
				}
			});
		}
		/**
		 * @private
		 * @function URLUtils#updateSearchParams
		 */
		var _URLUtils_updateSearchParams = function () {
			var searchParams = this.searchParams;
			var search = this.search;
			_URLSearchParams_list.set(searchParams, search ? parseXWWWFormUrlencoded(search.replace('?', '')) : []);
			_URLSearchParams_runQueryUpdateSteps.call(searchParams, this);
		};
		var _pushState = History.prototype.pushState;
		History.prototype.pushState = function () {
			_pushState.apply(this, arguments);
			_URLUtils_updateSearchParams.call(window.location);
		};
		var _replaceState = History.prototype.replaceState;
		History.prototype.replaceState = function () {
			_replaceState.apply(this, arguments);
			_URLUtils_updateSearchParams.call(window.location);
		};
		window.addEventListener('popstate', function (event) {
			_URLUtils_updateSearchParams.call(window.location);
		});
		new MutationObserver(function (mutations) {
			for (var record of mutations) {
				var target = record.target;
				var name = target.localName;
				if (name === 'a' || name === 'area') {
					_URLUtils_updateSearchParams.call(target);
				}
			}
		}).observe(document, {
			attributeFilter: ['href'],
			subtree: true,
		});
	}
}

})();