AtCoderのお気に入りの管理を行います。
当前为
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 3);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {
module.exports = jQuery;
/***/ }),
/* 1 */
/***/ (function(module, exports) {
module.exports = "<li><a id=\"fav-manager-dropdown-button\" data-toggle=\"modal\" data-target=\"#modal-fav-manager\" style=\"cursor : pointer;\"><span class=\"glyphicon glyphicon-star\" aria-hidden=\"true\"></span> お気に入り管理</a></li>";
/***/ }),
/* 2 */
/***/ (function(module, exports) {
module.exports = "<div id=\"modal-fav-manager\" class=\"modal fade\" tabindex=\"-1\" role=\"dialog\">\r\n\t<div class=\"modal-dialog\" role=\"document\">\r\n\t\t<div class=\"modal-content\">\r\n\t\t\t<div class=\"modal-header\">\r\n\t\t\t\t<button type=\"button\" class=\"close\" data-dismiss=\"modal\" aria-label=\"Close\"><span aria-hidden=\"true\">×</span></button>\r\n\t\t\t\t<h4 class=\"modal-title\">お気に入り管理</h4>\r\n\t\t\t</div>\r\n\t\t\t<div class=\"modal-body\">\r\n <div class=\"container-fluid\">\r\n <div class=\"row\">\r\n <div class=\"form-inline\">\r\n <div class=\"col-sm-12 form-group\">\r\n <label for=\"fav-manager-set-select\">セット: </label>\r\n <select id=\"fav-manager-set-select\" class=\"form-control\" data-placeholder=\"default\" data-allow-clear=\"false\" style=\"width: 30%;\">\r\n <option value=\"default\">default</option>\r\n <option value=\"blacklist\">blacklist</option>\r\n </select>\r\n <button id=\"fav-manager-toggle-set-activeness-button\" type=\"button\" class=\"btn btn-default\"></button>\r\n <button id=\"fav-manager-set-export-button\" type=\"button\" class=\"btn btn-default\">エクスポート</button>\r\n <button id=\"fav-manager-set-delete-button\" type=\"button\" class=\"btn btn-danger pull-right\">削除</button>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"row\">\r\n <table class=\"table\">\r\n <tbody id=\"fav-manager-users-table\">\r\n </tbody>\r\n </table>\r\n </div>\r\n <div class=\"row\">\r\n <div class=\"col-sm-6\">\r\n <div class=\"input-group\">\r\n <span class=\"input-group-addon\">セット名</span>\r\n <input id=\"fav-manager-add-set-input\" type=\"text\" class=\"form-control\" placeholder=\"サークル\">\r\n <span class=\"input-group-btn\">\r\n <button id=\"fav-manager-add-set-button\" type=\"button\" class=\"btn btn-default\">作成</button>\r\n </span>\r\n </div>\r\n </div>\r\n <div class=\"col-sm-6\">\r\n <div class=\"input-group\">\r\n <span class=\"input-group-addon\">ユーザ名</span>\r\n <input id=\"fav-manager-add-user-input\" type=\"text\" class=\"form-control\" placeholder=\"tourist\">\r\n <span class=\"input-group-btn\">\r\n <button id=\"fav-manager-add-user-button\" type=\"button\" class=\"btn btn-default\">追加</button>\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n <p></p>\r\n <div class=\"row\">\r\n <label class=\"col-sm-6\">\r\n <span class=\"btn btn-primary col-sm-12\">ファイルからインポート<input id=\"fav-manager-select-import-file-button\" type=\"file\" style=\"display:none\" accept=\".json\" multiple=\"\"></span>\r\n </label>\r\n <label class=\"col-sm-6\">\r\n <button id=\"fav-manager-export-all\" type=\"button\" class=\"btn btn-default col-sm-12\">全てエクスポート</button>\r\n </label>\r\n </div>\r\n </div>\r\n\t\t\t</div>\r\n\t\t\t<div class=\"modal-footer\">\r\n\t\t\t\t<button type=\"button\" class=\"btn btn-default\" data-dismiss=\"modal\">閉じる</button>\r\n\t\t\t</div>\r\n\t\t</div>\r\n\t</div>\r\n</div>";
/***/ }),
/* 3 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
// CONCATENATED MODULE: ./src/favs.js
class favSets{
constructor(){
this.sets = {};
this.isActive = {};
}
initialize(){
this.sets = {default:new Set(),blacklist:new Set()};
this.isActive = {default:true,blacklist:true};
}
createNewSet(key){
if (typeof(key) !== "string") throw new Error(`set名 ${JSON.stringify(key)} は文字列型ではありません`);
if (this.sets[key]) throw new Error(`set名 ${key} は既に存在しています`);
this.sets[key] = new Set();
this.isActive[key] = true;
}
setActive(key, activeness){
if (typeof(key) !== "string") throw new Error(`set名 ${JSON.stringify(key)} は文字列型ではありません`);
if (key === "blacklist") throw Error(`set ${key} の有効値は変更できません`);
if (!this.isActive.hasOwnProperty(key)) throw Error(`set名 ${key} は存在していません`);
this.isActive[key] = !!(activeness);
}
mergeWith(newSets){
for (const key in newSets.sets){
if (this.sets.hasOwnProperty(key)) {
newSets.sets[key].forEach((user) => {
this.sets[key].add(user);
});
this.isActive[key] |= newSets.isActive[key];
}
else{
this.sets[key] = newSets.sets[key];
this.isActive[key] = newSets.isActive[key];
}
}
}
//defaultは常に有効、blacklistは常に無効
get favSet() {
let a = [];
for (const key in this.isActive){
if (!this.isActive[key]) continue;
a.push(...this.sets[key]);
}
let set = new Set(a);
this.sets["blacklist"].forEach((user) => {
set.delete(user);
});
return set;
}
/**
* favSetsをJSON化する
* @param {favSets} favSets
* @param {boolean} containActivenessData
* @return {string}
*/
static stringify(favSets, containActivenessData = true){
let res = [];
for (const key in favSets.isActive){
let data = {name: key, users: [...favSets.sets[key]]};
if (containActivenessData) data.isActive = favSets.isActive[key];
res.push(data);
}
return JSON.stringify(res);
}
/**
* JSONからfavSetsを復元する
* @param {string} json
* @return {favSets}
*/
static parse(json){
let sets = new favSets();
JSON.parse(json).forEach((elem) => {
if (!elem.hasOwnProperty("name")) throw new Error(`key "name" がオブジェクト ${JSON.stringify(elem)} に存在しません`);
if (!elem.hasOwnProperty("users")) throw new Error(`key "users" がオブジェクト ${JSON.stringify(elem)} に存在しません`);
if (typeof(elem.name) !== "string") throw new Error(`key "name" の値 (${JSON.stringify(elem.name)}) は文字列型でありません`);
if (!Array.isArray(elem.users)) throw new Error(`key "users" の値 (${JSON.stringify(elem.users)}) は配列ではありません`);
sets.sets[elem.name] = arrayToSet(elem.users);
sets.isActive[elem.name] = elem.hasOwnProperty("isActive") ? !!(elem.isActive) : true;
});
return sets;
}
static isSpecialSet(key){
return key === "default" || key === "blacklist";
}
}
// CONCATENATED MODULE: ./src/globalFavSets.js
let globalFavSets = new favSets();
globalFavSets.initialize();
/* harmony default export */ var src_globalFavSets = (globalFavSets);
// CONCATENATED MODULE: ./src/injectFavHandler.js
/* harmony default export */ var injectFavHandler = (function () {
storeFavs = () => {
setLS('favmanager-favSets', favSets.stringify(src_globalFavSets));
setLS('fav', setToArray(favSet = src_globalFavSets.favSet));
};
reloadFavs = () => {
src_globalFavSets.initialize();
src_globalFavSets.mergeWith(favSets.parse(getLS('favmanager-favSets') || "[]"));
favSet = src_globalFavSets.favSet;
};
toggleFav = (val) => {
reloadFavs();
let res;
if (favSet.has(val)) {
src_globalFavSets.sets.default.delete(val);
src_globalFavSets.sets.blacklist.add(val);
res = false;
} else {
src_globalFavSets.sets.default.add(val);
src_globalFavSets.sets.blacklist.delete(val);
res = true;
}
favSet = src_globalFavSets.favSet;
storeFavs();
return res; // has val now
};
//migration
if (!getLS('favmanager-favSets')) {
getLS('fav').forEach((user) => {
src_globalFavSets.sets.default.add(user);
});
storeFavs();
}
else{
reloadFavs();
}
});
// EXTERNAL MODULE: ./src/html/dropdownElement.html
var dropdownElement = __webpack_require__(1);
var dropdownElement_default = /*#__PURE__*/__webpack_require__.n(dropdownElement);
// EXTERNAL MODULE: ./src/html/modal.html
var modal = __webpack_require__(2);
var modal_default = /*#__PURE__*/__webpack_require__.n(modal);
// CONCATENATED MODULE: ./src/files.js
/**
* ファイルに保存する
* @param {string} content
* @param {string} name
*/
function saveFile(content, name) {
var bom = new Uint8Array([0xef, 0xbb, 0xbf]);
var blob = new Blob([bom, content], { type: "text/plain" });
var a = document.createElement("a");
a.download = name;
a.target = "_blank";
a.href = window.URL.createObjectURL(blob);
// for Firefox
if (window.URL && window.URL.createObjectURL) {
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
// for Chrome
else if (window.webkitURL && window.webkitURL.createObject) {
a.href = window.webkitURL.createObjectURL(blob);
a.click();
}
}
// EXTERNAL MODULE: external "jQuery"
var external_jQuery_ = __webpack_require__(0);
// CONCATENATED MODULE: ./src/getTimeStamp.js
/* harmony default export */ var getTimeStamp = (function(){
const now = new Date();
return `${now.getFullYear()}${now.getMonth()}${now.getDate()}_${now.getHours()}${now.getMinutes()}${now.getSeconds()}`;
});
// CONCATENATED MODULE: ./src/generateElement.js
const modalNode = external_jQuery_(modal_default.a);
const dropdownNode = external_jQuery_(dropdownElement_default.a);
const setSelectSelector = external_jQuery_("#fav-manager-set-select", modalNode);
const usersTableSelector = external_jQuery_("#fav-manager-users-table", modalNode);
const setNameInputSelector = external_jQuery_("#fav-manager-add-set-input", modalNode);
const userNameInputSelector = external_jQuery_("#fav-manager-add-user-input", modalNode);
const setDeleteButtonSelector = external_jQuery_("#fav-manager-set-delete-button", modalNode);
const selectImportFileButtonSelector = external_jQuery_("#fav-manager-select-import-file-button", modalNode);
const toggleSetActivenessButtonSelector = external_jQuery_("#fav-manager-toggle-set-activeness-button", modalNode);
function getSelectedSet() {
return setSelectSelector.val();
}
function setSelectedSet(value) {
setSelectSelector.val(value);
}
function updateSelector() {
const selected = getSelectedSet();
setSelectSelector.empty();
for (const key in src_globalFavSets.isActive){
setSelectSelector.append(`<option value="${E(key)}">${E(key)}${src_globalFavSets.isActive[key] ? "" : "(無効)"}</option>`);
}
setSelectedSet(selected);
}
function updateTable() {
usersTableSelector.empty();
appendRow();
src_globalFavSets.sets[getSelectedSet()].forEach((user) => {
if (usersTableSelector[0].lastElementChild.children.length === 3)
appendRow();
external_jQuery_(usersTableSelector[0].lastElementChild).append(
`<td class="col-sm-4"><span>${E(user)}</span><a class="fav-manager-user-delete-button pull-right" name="${E(user)}" style="cursor : pointer; user-select: none;">×</a></td>`
);
});
while (usersTableSelector[0].lastElementChild.children.length < 3){
external_jQuery_(usersTableSelector[0].lastElementChild).append('<td class="col-sm-4"></td>');
}
function appendRow() {
usersTableSelector.append("<tr></tr>");
}
}
function updateView() {
console.log(src_globalFavSets);
const selectedSet = getSelectedSet();
toggleSetActivenessButtonSelector.text(src_globalFavSets.isActive[selectedSet] ? "無効にする" : "有効にする");
setDeleteButtonSelector.text(favSets.isSpecialSet(selectedSet) ? "クリア" : "セット削除");
toggleSetActivenessButtonSelector.prop("disabled", selectedSet === "blacklist");
updateSelector();
updateTable();
}
window.addEventListener("storage", event => {
if (event.key !== 'favmanager-favSets') return;
reloadFavs();
updateView();
});
/* harmony default export */ var generateElement = (function(){
external_jQuery_("body").prepend(modalNode);
external_jQuery_(".navbar-right .dropdown-menu .divider:nth-last-child(2)").before(dropdownNode);
external_jQuery_("#fav-manager-export-all", modalNode).click(() => {
saveFile(favSets.stringify(src_globalFavSets), `all-favsets-${getTimeStamp()}.json`);
});
external_jQuery_("#fav-manager-set-export-button", modalNode).click(() => {
const key = getSelectedSet();
let exportSets = new favSets();
exportSets.sets[key] = src_globalFavSets.sets[key];
exportSets.isActive[key] = true;
saveFile(favSets.stringify(exportSets, false), `favset-${key}-${getTimeStamp()}.json`);
});
setSelectSelector.change(updateView);
setDeleteButtonSelector.click(() => {
const key = getSelectedSet();
if (favSets.isSpecialSet(key)) {
src_globalFavSets.sets[key] = new Set();
}
else{
delete src_globalFavSets.sets[key];
delete src_globalFavSets.isActive[key];
updateSelector();
setSelectedSet("default");
}
storeFavs();
updateView();
});
usersTableSelector.on("click", ".fav-manager-user-delete-button", (event) => {
const key = getSelectedSet();
const userName = event.target.getAttribute("name");
src_globalFavSets.sets[key].delete(userName);
storeFavs();
updateView();
});
external_jQuery_("#fav-manager-add-set-button", modalNode).click(() => {
const newSetName = setNameInputSelector.val();
if (newSetName) {
src_globalFavSets.createNewSet(newSetName);
updateSelector();
setSelectedSet(newSetName);
}
setNameInputSelector.val("");
storeFavs();
updateView();
});
external_jQuery_("#fav-manager-add-user-button", modalNode).click(() => {
src_globalFavSets.sets[getSelectedSet()].add(userNameInputSelector.val());
userNameInputSelector.val("");
storeFavs();
updateView();
});
toggleSetActivenessButtonSelector.click(() => {
const selectedSet = getSelectedSet();
src_globalFavSets.setActive(selectedSet, !src_globalFavSets.isActive[selectedSet]);
storeFavs();
updateView();
});
selectImportFileButtonSelector.change((event) => {
let files = event.target.files;
let reader = new FileReader();
reader.onload = (readerEvent) => {
try{
let parsedSets = favSets.parse(readerEvent.target.result);
src_globalFavSets.mergeWith(parsedSets);
storeFavs();
updateView();
}
catch (e){
console.log("failed to load");
}
};
for (let i = 0; i < files.length; i++){
reader.readAsText(files[i]);
}
});
external_jQuery_("#fav-manager-dropdown-button", dropdownNode).click(updateView);
modalNode.ready(updateView);
});
// CONCATENATED MODULE: ./src/main.js
// ==UserScript==
// @name ac-favorite-manager
// @namespace https://atcoder.jp/
// @version 1.0.1
// @description AtCoderのお気に入りの管理を行います。
// @author keymoon
// @license MIT
// @match https://atcoder.jp/*
// @exclude https://atcoder.jp/*/json
// @match http://atcoder-circles.com/circles/*
// @exclude http://atcoder-circles.com/circles/
// ==/UserScript==
if (location.hostname === "atcoder.jp"){
injectFavHandler();
generateElement();
}
else{
let elem = $("<div style=\"text-align: center;\"><a style=\"color: gray;\" href=\"#\" onclick=\"return false;\">お気に入り用のファイルをダウンロード</a></div>");
$("table").before(elem);
$("a", elem).click(() => {
const circleName = location.pathname.split('/')[2];
$.get(`/circles/${circleName}/api`).then(members => {
let exportSets = new favSets();
exportSets.sets[circleName] = new Set();
exportSets.isActive[circleName] = true;
members.forEach((member) => {
exportSets.sets[circleName].add(member);
});
saveFile(favSets.stringify(exportSets, false), `circles-${circleName}-${getTimeStamp()}.json`);
});
});
}
/***/ })
/******/ ]);