Greasy Fork is available in English.

ac-predictor

コンテスト中にAtCoderのパフォーマンスを予測します

2019/05/25時点のページです。最新版はこちら。

作者のサイトでサポートを受ける。または、このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
// ==UserScript==
// @name        ac-predictor
// @namespace   http://ac-predictor.azurewebsites.net/
// @version     1.1.0
// @description コンテスト中にAtCoderのパフォーマンスを予測します
// @author      keymoon
// @license     MIT
// @supportURL  https://github.com/key-moon/ac-predictor.user.js/issues
// @match       https://atcoder.jp/*
// @exclude     https://atcoder.jp/*/json
// ==/UserScript==

/******/ (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 = 10);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {

module.exports = jQuery;

/***/ }),
/* 1 */
/***/ (function(module, exports) {

module.exports = moment;

/***/ }),
/* 2 */
/***/ (function(module, exports) {

module.exports = "<div id=\"menu_wrap\">\r\n    <div id=\"sidemenu\" class=\"container\"></div>\r\n    <div id=\"sidemenu-key\" class=\"glyphicon glyphicon-menu-left\"></div>\r\n</div>";

/***/ }),
/* 3 */
/***/ (function(module, exports) {

module.exports = "<div id=\"predictor-alert\" class=\"row\"><h5 class='sidemenu-txt'>順位表読み込み中…</h5></div>\r\n<div id=\"predictor-data\" class=\"row\">\r\n    <div class=\"input-group col-xs-offset-1 col-xs-10\">\r\n        <span class=\"input-group-addon\">順位<span class=\"glyphicon glyphicon-question-sign\" aria-hidden=\"true\" data-html=\"true\" data-toggle=\"tooltip\" data-placement=\"right\" title=\"\" data-original-title=\"Rated内の順位です。複数人同順位の際は人数を加味します(5位が4人居たら6.5位として計算)\"></span></span>\r\n        <input class=\"form-control\" id=\"predictor-input-rank\">\r\n        <span class=\"input-group-addon\">位</span>\r\n    </div>\r\n        \r\n    <div class=\"input-group col-xs-offset-1 col-xs-10\">\r\n        <span class=\"input-group-addon\">パフォーマンス</span>\r\n        <input class=\"form-control\" id=\"predictor-input-perf\">\r\n    </div>\r\n\r\n    <div class=\"input-group col-xs-offset-1 col-xs-10\">\r\n        <span class=\"input-group-addon\">レーティング</span>\r\n        <input class=\"form-control\" id=\"predictor-input-rate\">\r\n    </div>\r\n</div>\r\n<div class=\"row\">\r\n    <div class=\"btn-group col-xs-offset-1\">\r\n        <button class=\"btn btn-default\" id=\"predictor-current\">現在の順位</button>\r\n        <button type=\"button\" class=\"btn btn-primary\" id=\"predictor-reload\" data-loading-text=\"更新中…\">更新</button>\r\n        <a class=\"btn btn-default\" rel=\"nofollow\" onClick=\"window.open(encodeURI(decodeURI(this.href)),'twwindow','width=550, height=450, personalbar=0, toolbar=0, scrollbars=1'); return false;\" id='predictor-tweet'>ツイート</a>\r\n        <!--<button class=\"btn btn-default\" id=\"predictor-solved\" disabled>現問題AC後</button>-->\r\n    </div>\r\n</div>";

/***/ }),
/* 4 */
/***/ (function(module, exports) {

module.exports = "<div id=\"estimator-alert\"></div>\r\n<div class=\"row\">\r\n\t<div class=\"input-group\">\r\n\t\t<span class=\"input-group-addon\" id=\"estimator-input-desc\"></span>\r\n\t\t<input type=\"number\" class=\"form-control\" id=\"estimator-input\">\r\n\t</div>\r\n</div>\r\n<div class=\"row\">\r\n\t<div class=\"input-group\">\r\n\t\t<span class=\"input-group-addon\" id=\"estimator-res-desc\"></span>\r\n\t\t<input class=\"form-control\" id=\"estimator-res\" disabled=\"disabled\">\r\n\t\t<span class=\"input-group-btn\">\r\n\t\t\t<button class=\"btn btn-default\" id=\"estimator-toggle\">入替</button>\r\n\t\t</span>\r\n\t</div>\r\n</div>\r\n<div class=\"row\" style=\"margin: 10px 0px;\">\r\n\t<a class=\"btn btn-default col-xs-offset-8 col-xs-4\" rel=\"nofollow\" onClick=\"window.open(encodeURI(decodeURI(this.href)),'twwindow','width=550, height=450, personalbar=0, toolbar=0, scrollbars=1'); return false;\" id='estimator-tweet'>ツイート</a>\r\n</div>";

/***/ }),
/* 5 */
/***/ (function(module, exports, __webpack_require__) {


var content = __webpack_require__(6);

if(typeof content === 'string') content = [[module.i, content, '']];

var transform;
var insertInto;



var options = {"hmr":true}

options.transform = transform
options.insertInto = undefined;

var update = __webpack_require__(8)(content, options);

if(content.locals) module.exports = content.locals;

if(false) {}

/***/ }),
/* 6 */
/***/ (function(module, exports, __webpack_require__) {

exports = module.exports = __webpack_require__(7)(false);
// Module
exports.push([module.i, "@charset \"UTF-8\";\n#menu_wrap {\n  display: block;\n  position: fixed;\n  top: 0;\n  z-index: 20;\n  width: 400px;\n  right: -350px;\n  transition: all 150ms 0ms ease;\n  margin-top: 50px; }\n\n#sidemenu {\n  background: #000;\n  opacity: 0.85; }\n\n#sidemenu-key {\n  border-radius: 5px 0px 0px 5px;\n  background: #000;\n  opacity: 0.85;\n  color: #FFF;\n  padding: 30px 0;\n  cursor: pointer;\n  margin-top: 100px;\n  text-align: center; }\n\n#sidemenu {\n  display: inline-block;\n  width: 350px;\n  float: right; }\n\n#sidemenu-key {\n  display: inline-block;\n  width: 50px;\n  float: right; }\n\n.sidemenu-active {\n  transform: translateX(-350px); }\n\n.sidemenu-txt {\n  color: #DDD; }\n\n/*アコーディオンメニュー*/\n.menu-wrapper {\n  border-bottom: 1px solid #FFF; }\n\n.menu-header {\n  margin: 10px 20px 10px 20px;\n  user-select: none; }\n\n.menu-box {\n  overflow: hidden;\n  transition: all 300ms 0s ease; }\n\n.menu-box-collapse {\n  height: 0px !important; }\n  .menu-box-collapse .menu-content {\n    transform: translateY(-100%); }\n\n.menu-content {\n  padding: 10px 20px 10px 20px;\n  transition: all 300ms 0s ease; }\n", ""]);



/***/ }),
/* 7 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/*
  MIT License http://www.opensource.org/licenses/mit-license.php
  Author Tobias Koppers @sokra
*/
// css base code, injected by the css-loader
module.exports = function (useSourceMap) {
  var list = []; // return the list of modules as css string

  list.toString = function toString() {
    return this.map(function (item) {
      var content = cssWithMappingToString(item, useSourceMap);

      if (item[2]) {
        return '@media ' + item[2] + '{' + content + '}';
      } else {
        return content;
      }
    }).join('');
  }; // import a list of modules into the list


  list.i = function (modules, mediaQuery) {
    if (typeof modules === 'string') {
      modules = [[null, modules, '']];
    }

    var alreadyImportedModules = {};

    for (var i = 0; i < this.length; i++) {
      var id = this[i][0];

      if (id != null) {
        alreadyImportedModules[id] = true;
      }
    }

    for (i = 0; i < modules.length; i++) {
      var item = modules[i]; // skip already imported module
      // this implementation is not 100% perfect for weird media query combinations
      // when a module is imported multiple times with different media queries.
      // I hope this will never occur (Hey this way we have smaller bundles)

      if (item[0] == null || !alreadyImportedModules[item[0]]) {
        if (mediaQuery && !item[2]) {
          item[2] = mediaQuery;
        } else if (mediaQuery) {
          item[2] = '(' + item[2] + ') and (' + mediaQuery + ')';
        }

        list.push(item);
      }
    }
  };

  return list;
};

function cssWithMappingToString(item, useSourceMap) {
  var content = item[1] || '';
  var cssMapping = item[3];

  if (!cssMapping) {
    return content;
  }

  if (useSourceMap && typeof btoa === 'function') {
    var sourceMapping = toComment(cssMapping);
    var sourceURLs = cssMapping.sources.map(function (source) {
      return '/*# sourceURL=' + cssMapping.sourceRoot + source + ' */';
    });
    return [content].concat(sourceURLs).concat([sourceMapping]).join('\n');
  }

  return [content].join('\n');
} // Adapted from convert-source-map (MIT)


function toComment(sourceMap) {
  // eslint-disable-next-line no-undef
  var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))));
  var data = 'sourceMappingURL=data:application/json;charset=utf-8;base64,' + base64;
  return '/*# ' + data + ' */';
}

/***/ }),
/* 8 */
/***/ (function(module, exports, __webpack_require__) {

/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/

var stylesInDom = {};

var	memoize = function (fn) {
	var memo;

	return function () {
		if (typeof memo === "undefined") memo = fn.apply(this, arguments);
		return memo;
	};
};

var isOldIE = memoize(function () {
	// Test for IE <= 9 as proposed by Browserhacks
	// @see http://browserhacks.com/#hack-e71d8692f65334173fee715c222cb805
	// Tests for existence of standard globals is to allow style-loader
	// to operate correctly into non-standard environments
	// @see https://github.com/webpack-contrib/style-loader/issues/177
	return window && document && document.all && !window.atob;
});

var getTarget = function (target, parent) {
  if (parent){
    return parent.querySelector(target);
  }
  return document.querySelector(target);
};

var getElement = (function (fn) {
	var memo = {};

	return function(target, parent) {
                // If passing function in options, then use it for resolve "head" element.
                // Useful for Shadow Root style i.e
                // {
                //   insertInto: function () { return document.querySelector("#foo").shadowRoot }
                // }
                if (typeof target === 'function') {
                        return target();
                }
                if (typeof memo[target] === "undefined") {
			var styleTarget = getTarget.call(this, target, parent);
			// Special case to return head of iframe instead of iframe itself
			if (window.HTMLIFrameElement && styleTarget instanceof window.HTMLIFrameElement) {
				try {
					// This will throw an exception if access to iframe is blocked
					// due to cross-origin restrictions
					styleTarget = styleTarget.contentDocument.head;
				} catch(e) {
					styleTarget = null;
				}
			}
			memo[target] = styleTarget;
		}
		return memo[target]
	};
})();

var singleton = null;
var	singletonCounter = 0;
var	stylesInsertedAtTop = [];

var	fixUrls = __webpack_require__(9);

module.exports = function(list, options) {
	if (typeof DEBUG !== "undefined" && DEBUG) {
		if (typeof document !== "object") throw new Error("The style-loader cannot be used in a non-browser environment");
	}

	options = options || {};

	options.attrs = typeof options.attrs === "object" ? options.attrs : {};

	// Force single-tag solution on IE6-9, which has a hard limit on the # of <style>
	// tags it will allow on a page
	if (!options.singleton && typeof options.singleton !== "boolean") options.singleton = isOldIE();

	// By default, add <style> tags to the <head> element
        if (!options.insertInto) options.insertInto = "head";

	// By default, add <style> tags to the bottom of the target
	if (!options.insertAt) options.insertAt = "bottom";

	var styles = listToStyles(list, options);

	addStylesToDom(styles, options);

	return function update (newList) {
		var mayRemove = [];

		for (var i = 0; i < styles.length; i++) {
			var item = styles[i];
			var domStyle = stylesInDom[item.id];

			domStyle.refs--;
			mayRemove.push(domStyle);
		}

		if(newList) {
			var newStyles = listToStyles(newList, options);
			addStylesToDom(newStyles, options);
		}

		for (var i = 0; i < mayRemove.length; i++) {
			var domStyle = mayRemove[i];

			if(domStyle.refs === 0) {
				for (var j = 0; j < domStyle.parts.length; j++) domStyle.parts[j]();

				delete stylesInDom[domStyle.id];
			}
		}
	};
};

function addStylesToDom (styles, options) {
	for (var i = 0; i < styles.length; i++) {
		var item = styles[i];
		var domStyle = stylesInDom[item.id];

		if(domStyle) {
			domStyle.refs++;

			for(var j = 0; j < domStyle.parts.length; j++) {
				domStyle.parts[j](item.parts[j]);
			}

			for(; j < item.parts.length; j++) {
				domStyle.parts.push(addStyle(item.parts[j], options));
			}
		} else {
			var parts = [];

			for(var j = 0; j < item.parts.length; j++) {
				parts.push(addStyle(item.parts[j], options));
			}

			stylesInDom[item.id] = {id: item.id, refs: 1, parts: parts};
		}
	}
}

function listToStyles (list, options) {
	var styles = [];
	var newStyles = {};

	for (var i = 0; i < list.length; i++) {
		var item = list[i];
		var id = options.base ? item[0] + options.base : item[0];
		var css = item[1];
		var media = item[2];
		var sourceMap = item[3];
		var part = {css: css, media: media, sourceMap: sourceMap};

		if(!newStyles[id]) styles.push(newStyles[id] = {id: id, parts: [part]});
		else newStyles[id].parts.push(part);
	}

	return styles;
}

function insertStyleElement (options, style) {
	var target = getElement(options.insertInto)

	if (!target) {
		throw new Error("Couldn't find a style target. This probably means that the value for the 'insertInto' parameter is invalid.");
	}

	var lastStyleElementInsertedAtTop = stylesInsertedAtTop[stylesInsertedAtTop.length - 1];

	if (options.insertAt === "top") {
		if (!lastStyleElementInsertedAtTop) {
			target.insertBefore(style, target.firstChild);
		} else if (lastStyleElementInsertedAtTop.nextSibling) {
			target.insertBefore(style, lastStyleElementInsertedAtTop.nextSibling);
		} else {
			target.appendChild(style);
		}
		stylesInsertedAtTop.push(style);
	} else if (options.insertAt === "bottom") {
		target.appendChild(style);
	} else if (typeof options.insertAt === "object" && options.insertAt.before) {
		var nextSibling = getElement(options.insertAt.before, target);
		target.insertBefore(style, nextSibling);
	} else {
		throw new Error("[Style Loader]\n\n Invalid value for parameter 'insertAt' ('options.insertAt') found.\n Must be 'top', 'bottom', or Object.\n (https://github.com/webpack-contrib/style-loader#insertat)\n");
	}
}

function removeStyleElement (style) {
	if (style.parentNode === null) return false;
	style.parentNode.removeChild(style);

	var idx = stylesInsertedAtTop.indexOf(style);
	if(idx >= 0) {
		stylesInsertedAtTop.splice(idx, 1);
	}
}

function createStyleElement (options) {
	var style = document.createElement("style");

	if(options.attrs.type === undefined) {
		options.attrs.type = "text/css";
	}

	if(options.attrs.nonce === undefined) {
		var nonce = getNonce();
		if (nonce) {
			options.attrs.nonce = nonce;
		}
	}

	addAttrs(style, options.attrs);
	insertStyleElement(options, style);

	return style;
}

function createLinkElement (options) {
	var link = document.createElement("link");

	if(options.attrs.type === undefined) {
		options.attrs.type = "text/css";
	}
	options.attrs.rel = "stylesheet";

	addAttrs(link, options.attrs);
	insertStyleElement(options, link);

	return link;
}

function addAttrs (el, attrs) {
	Object.keys(attrs).forEach(function (key) {
		el.setAttribute(key, attrs[key]);
	});
}

function getNonce() {
	if (false) {}

	return __webpack_require__.nc;
}

function addStyle (obj, options) {
	var style, update, remove, result;

	// If a transform function was defined, run it on the css
	if (options.transform && obj.css) {
	    result = typeof options.transform === 'function'
		 ? options.transform(obj.css) 
		 : options.transform.default(obj.css);

	    if (result) {
	    	// If transform returns a value, use that instead of the original css.
	    	// This allows running runtime transformations on the css.
	    	obj.css = result;
	    } else {
	    	// If the transform function returns a falsy value, don't add this css.
	    	// This allows conditional loading of css
	    	return function() {
	    		// noop
	    	};
	    }
	}

	if (options.singleton) {
		var styleIndex = singletonCounter++;

		style = singleton || (singleton = createStyleElement(options));

		update = applyToSingletonTag.bind(null, style, styleIndex, false);
		remove = applyToSingletonTag.bind(null, style, styleIndex, true);

	} else if (
		obj.sourceMap &&
		typeof URL === "function" &&
		typeof URL.createObjectURL === "function" &&
		typeof URL.revokeObjectURL === "function" &&
		typeof Blob === "function" &&
		typeof btoa === "function"
	) {
		style = createLinkElement(options);
		update = updateLink.bind(null, style, options);
		remove = function () {
			removeStyleElement(style);

			if(style.href) URL.revokeObjectURL(style.href);
		};
	} else {
		style = createStyleElement(options);
		update = applyToTag.bind(null, style);
		remove = function () {
			removeStyleElement(style);
		};
	}

	update(obj);

	return function updateStyle (newObj) {
		if (newObj) {
			if (
				newObj.css === obj.css &&
				newObj.media === obj.media &&
				newObj.sourceMap === obj.sourceMap
			) {
				return;
			}

			update(obj = newObj);
		} else {
			remove();
		}
	};
}

var replaceText = (function () {
	var textStore = [];

	return function (index, replacement) {
		textStore[index] = replacement;

		return textStore.filter(Boolean).join('\n');
	};
})();

function applyToSingletonTag (style, index, remove, obj) {
	var css = remove ? "" : obj.css;

	if (style.styleSheet) {
		style.styleSheet.cssText = replaceText(index, css);
	} else {
		var cssNode = document.createTextNode(css);
		var childNodes = style.childNodes;

		if (childNodes[index]) style.removeChild(childNodes[index]);

		if (childNodes.length) {
			style.insertBefore(cssNode, childNodes[index]);
		} else {
			style.appendChild(cssNode);
		}
	}
}

function applyToTag (style, obj) {
	var css = obj.css;
	var media = obj.media;

	if(media) {
		style.setAttribute("media", media)
	}

	if(style.styleSheet) {
		style.styleSheet.cssText = css;
	} else {
		while(style.firstChild) {
			style.removeChild(style.firstChild);
		}

		style.appendChild(document.createTextNode(css));
	}
}

function updateLink (link, options, obj) {
	var css = obj.css;
	var sourceMap = obj.sourceMap;

	/*
		If convertToAbsoluteUrls isn't defined, but sourcemaps are enabled
		and there is no publicPath defined then lets turn convertToAbsoluteUrls
		on by default.  Otherwise default to the convertToAbsoluteUrls option
		directly
	*/
	var autoFixUrls = options.convertToAbsoluteUrls === undefined && sourceMap;

	if (options.convertToAbsoluteUrls || autoFixUrls) {
		css = fixUrls(css);
	}

	if (sourceMap) {
		// http://stackoverflow.com/a/26603875
		css += "\n/*# sourceMappingURL=data:application/json;base64," + btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))) + " */";
	}

	var blob = new Blob([css], { type: "text/css" });

	var oldSrc = link.href;

	link.href = URL.createObjectURL(blob);

	if(oldSrc) URL.revokeObjectURL(oldSrc);
}


/***/ }),
/* 9 */
/***/ (function(module, exports) {


/**
 * When source maps are enabled, `style-loader` uses a link element with a data-uri to
 * embed the css on the page. This breaks all relative urls because now they are relative to a
 * bundle instead of the current page.
 *
 * One solution is to only use full urls, but that may be impossible.
 *
 * Instead, this function "fixes" the relative urls to be absolute according to the current page location.
 *
 * A rudimentary test suite is located at `test/fixUrls.js` and can be run via the `npm test` command.
 *
 */

module.exports = function (css) {
  // get current location
  var location = typeof window !== "undefined" && window.location;

  if (!location) {
    throw new Error("fixUrls requires window.location");
  }

	// blank or null?
	if (!css || typeof css !== "string") {
	  return css;
  }

  var baseUrl = location.protocol + "//" + location.host;
  var currentDir = baseUrl + location.pathname.replace(/\/[^\/]*$/, "/");

	// convert each url(...)
	/*
	This regular expression is just a way to recursively match brackets within
	a string.

	 /url\s*\(  = Match on the word "url" with any whitespace after it and then a parens
	   (  = Start a capturing group
	     (?:  = Start a non-capturing group
	         [^)(]  = Match anything that isn't a parentheses
	         |  = OR
	         \(  = Match a start parentheses
	             (?:  = Start another non-capturing groups
	                 [^)(]+  = Match anything that isn't a parentheses
	                 |  = OR
	                 \(  = Match a start parentheses
	                     [^)(]*  = Match anything that isn't a parentheses
	                 \)  = Match a end parentheses
	             )  = End Group
              *\) = Match anything and then a close parens
          )  = Close non-capturing group
          *  = Match anything
       )  = Close capturing group
	 \)  = Match a close parens

	 /gi  = Get all matches, not the first.  Be case insensitive.
	 */
	var fixedCss = css.replace(/url\s*\(((?:[^)(]|\((?:[^)(]+|\([^)(]*\))*\))*)\)/gi, function(fullMatch, origUrl) {
		// strip quotes (if they exist)
		var unquotedOrigUrl = origUrl
			.trim()
			.replace(/^"(.*)"$/, function(o, $1){ return $1; })
			.replace(/^'(.*)'$/, function(o, $1){ return $1; });

		// already a full url? no change
		if (/^(#|data:|http:\/\/|https:\/\/|file:\/\/\/|\s*$)/i.test(unquotedOrigUrl)) {
		  return fullMatch;
		}

		// convert the url to a full url
		var newUrl;

		if (unquotedOrigUrl.indexOf("//") === 0) {
		  	//TODO: should we add protocol?
			newUrl = unquotedOrigUrl;
		} else if (unquotedOrigUrl.indexOf("/") === 0) {
			// path should be relative to the base url
			newUrl = baseUrl + unquotedOrigUrl; // already starts with '/'
		} else {
			// path should be relative to current directory
			newUrl = currentDir + unquotedOrigUrl.replace(/^\.\//, ""); // Strip leading './'
		}

		// send back the fixed url(...)
		return "url(" + JSON.stringify(newUrl) + ")";
	});

	// send back the fixed css
	return fixedCss;
};


/***/ }),
/* 10 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);

// CONCATENATED MODULE: ./src/libs/tabID.js
let created;
let id;

function initializeID() {
    created = Date.now();
    id = Date.now() * 1024 + Math.floor(Math.random() * 1024);
}


// EXTERNAL MODULE: ./src/libs/sidemenu/sidemenu.scss
var sidemenu = __webpack_require__(5);

// EXTERNAL MODULE: external "jQuery"
var external_jQuery_ = __webpack_require__(0);

// EXTERNAL MODULE: ./src/libs/sidemenu/sidemenu.html
var sidemenu_sidemenu = __webpack_require__(2);
var sidemenu_sidemenu_default = /*#__PURE__*/__webpack_require__.n(sidemenu_sidemenu);

// CONCATENATED MODULE: ./src/libs/sidemenu/element.js
/**
 * サイドメニューに追加される要素のクラスです。
 * @property {RegExp} [match]
 */
class SideMenuElement {

    /**
     * オブジェクト生成用のコンストラクタです
     * @param {string} [id] 要素のコンテナに付加されるIDです。
     * @param {string} [title] 要素に表示されるタイトルです。
     * @param {RegExp} [match] 表示されるページを指定するための正規表現です。
     * @param {string} [document] 要素のHTMLです。
     * @param {Function} [afterAppend] 要素が追加された後に実行される処理用の関数です。
     */
    constructor(id, title, match, document, afterAppend) {
        this.id = id;
        this.title = title;
        this.match = match;
        this.document = document;
        this.afterAppend = afterAppend;
    }

    ElementShouldDisplayed(url) {
		return this.match.test(url);
    }

    GetHTML() {
        return `<div class="menu-wrapper">
    <div class="menu-header">
        <h4 class="sidemenu-txt">${this.title}<span class="glyphicon glyphicon-menu-up" style="float: right"></span></h4>
    </div>
    <div class="menu-box"><div class="menu-content" id="${this.id}">${this.document}</div></div>
</div>`;
    }
}

// CONCATENATED MODULE: ./src/libs/sidemenu/sidemenu.js





class sidemenu_SideMenu {
    constructor() {
        this.Generate();
    }

    Generate() {
        external_jQuery_('#main-div').append(sidemenu_sidemenu_default.a);
        resizeSidemenuHeight();
        external_jQuery_('#sidemenu-key').click(function () {
            external_jQuery_('#sidemenu-key').toggleClass('glyphicon-menu-left glyphicon-menu-right');
            external_jQuery_('#menu_wrap').toggleClass('sidemenu-active');
        });
        external_jQuery_(window).resize(resizeSidemenuHeight);
        external_jQuery_('#sidemenu').on('click', '.menu-header', (event) => {
            external_jQuery_(event.target).parents('.menu-wrapper').find('.menu-box').toggleClass('menu-box-collapse');
            external_jQuery_(event.target).find('.glyphicon').toggleClass('glyphicon-menu-down glyphicon-menu-up');
        });

        function resizeSidemenuHeight() {
            external_jQuery_('#sidemenu').height(external_jQuery_(window).height());
        }
    }

    /**
     * サイドメニューに要素を追加します
     * @param {SideMenuElement} [element] 追加する要素
     */
    AddElement(element) {
        const elementHtml = external_jQuery_(element.GetHTML());
        external_jQuery_('#sidemenu').append(elementHtml);
        elementHtml.ready(() => {
            const content = external_jQuery_('.menu-content', elementHtml);
            content.parents('.menu-box').css('height', content.outerHeight(true));
            element.afterAppend();
        })
    }
}

// EXTERNAL MODULE: ./src/elements/predictor/dom.html
var dom = __webpack_require__(3);
var dom_default = /*#__PURE__*/__webpack_require__.n(dom);

// EXTERNAL MODULE: external "moment"
var external_moment_ = __webpack_require__(1);
var external_moment_default = /*#__PURE__*/__webpack_require__.n(external_moment_);

// CONCATENATED MODULE: ./src/libs/datas/data.js
/**
 * データを保存/更新するためのクラスです。
 */
class Data {
    /**
     * オブジェクト生成用のコンストラクタです
     * @param {Function} [getNewData] 更新の際に新たなデータオブジェクトを返す関数です。
     */
    constructor(getNewData) {
        this.getNewData = getNewData;
    }

    /**
     * データのアップデートをする関数です。
     */
    async update() {
        this.data = await this.getNewData();
        return this.data;
    }
}

/**
 * GETでデータを取得します。
 */
class WebData extends Data {
    /**
     * オブジェクト生成用のコンストラクタです
     * @param {string} [dataURL] データ取得先のURLです。
     */
    constructor(dataURL) {
        super(async () => {
            return await $.ajax(dataURL);
        });
    }
}

// CONCATENATED MODULE: ./src/libs/datas/history.js


/**
 * ユーザのパフォーマンス履歴を取得し、他のタブと同期的にデータを扱います。
 */
class history_HistoryData extends WebData {
    constructor(userScreenName) {
        super(
            `https://atcoder.jp/users/${userScreenName}/history/json`
        );
    }
}

// CONCATENATED MODULE: ./src/libs/datas/standings.js


/**
 * コンテストの順位表データを取得し、他のタブと同期的にデータを扱います。
 */
class standings_StandingsData extends WebData {
    constructor(contestScreenName) {
        super(
            `https://atcoder.jp/contests/${contestScreenName}/standings/json`
        );
    }
}

// CONCATENATED MODULE: ./src/libs/datas/aperfs.js


class aperfs_APerfsData extends WebData {
    constructor(contestScreenName) {
        super(
            `https://ac-predictor.azurewebsites.net/api/aperfs/${contestScreenName}`
        );
    }
}

// CONCATENATED MODULE: ./src/libs/utils/ratingColor.js
const colorBounds = {
    "gray": 0,
    "brown": 400,
    "green": 800,
    "cyan": 1200,
    "blue": 1600,
    "yellow": 2000,
    "orange": 2400,
    "red": 2800
};

const colorNames = ["unrated", "gray", "brown", "green", "cyan", "blue", "yellow", "orange", "red"];

function getColor(rating) {
    let colorIndex = rating > 0 ? Math.min(Math.floor(rating / 400) + 1, 8) : 0;
    return colorNames[colorIndex];
}
// CONCATENATED MODULE: ./src/libs/utils/atcoderRating.js
//Copyright © 2017 koba-e964.
//from : https://github.com/koba-e964/atcoder-rating-estimator

const finf = bigf(400);

function bigf(n) {
    let numerator = 1.0;
    let denominator = 1.0;
    for (let i = 0; i < n; ++i) {
        numerator *= 0.81;
        denominator *= 0.9;
    }
    numerator = (1 - numerator) * 0.81 / 0.19;
    denominator = (1 - denominator) * 0.9 / 0.1;
    return Math.sqrt(numerator) / denominator;

}

function f(n) {
    return (bigf(n) - finf) / (bigf(1) - finf) * 1200.0;
}

/**
 * calculate unpositivized rating from performance history
 * @param {Number[]} [history] performance history
 * @returns {Number} unpositivized rating
 */
function calcRatingFromHistory(history) {
    let n = history.length;
    let numerator = 0.0;
    let denominator = 0.0;
    for (let i = n - 1; i >= 0; --i) {
        numerator *= 0.9;
        numerator += 0.9 * Math.pow(2, history[i] / 800.0);
        denominator *= 0.9;
        denominator += 0.9;
    }
    return Math.log2(numerator / denominator) * 800.0 - f(n);
}

/**
 * calculate unpositivized rating from last state
 * @param {Number} [last] last unpositivized rating
 * @param {Number} [perf] performance
 * @param {Number} [ratedMatches] count of participated rated contest
 * @returns {number} estimated unpositivized rating
 */
function calcRatingFromLast(last, perf, ratedMatches) {
    if (ratedMatches === 0) return perf - 1200;
    last += f(ratedMatches);
    const weight = 9 - 9 * 0.9 ** ratedMatches;
    const numerator = weight * (2 ** (last / 800.0)) + 2 ** (perf / 800.0);
    const denominator = 1 + weight;
    return Math.log2(numerator / denominator) * 800.0 - f(ratedMatches + 1);
}

/**
 * (-inf, inf) -> (0, inf)
 * @param {Number} [rating] unpositivized rating
 * @returns {number} positivized rating
 */
function positivizeRating(rating) {
    if (rating >= 400.0) {
        return rating;
    }
    return 400.0 * Math.exp((rating - 400.0) / 400.0);
}

/**
 * (0, inf) -> (-inf, inf)
 * @param {Number} [rating] positivized rating
 * @returns {number} unpositivized rating
 */
function unpositivizeRating(rating) {
    if (rating >= 400.0) {
        return rating;
    }
    return 400.0 + 400.0 * Math.log(rating / 400.0);
}

/**
 * calculate the performance required to reach a target rate
 * @param {Number} [targetRating] targeted unpositivized rating
 * @param {Number[]} [history] performance history
 * @returns {number} performance
 */
function calcRequiredPerformance(targetRating, history) {
    let upper = 10000.0;
    let lower = -10000.0;
    for (let i = 0; i < 100; ++i) {
        const mid = (lower + upper) / 2;
        const rating = calcRatingFromHistory([mid].concat(history));
        if (targetRating <= rating) upper = mid;
        else lower = mid;
    }
    return lower;
}
// CONCATENATED MODULE: ./src/libs/contest/fetchContestInformation.js


class ContestInformation{
    /**
     * @param {number[]} [participatableRange]
     * @param {number[]} [ratedRange]
     * @param {number} [penalty]
     */
    constructor(participatableRange, ratedRange, penalty){
        this.ParticipatableRange = participatableRange;
        this.RatedRange = ratedRange;
        this.Penalty = penalty;
    }

    /**
     * @param {object} object
     * @return {ContestInformation}
     */
    static GenerateFromObject(object){
        return new ContestInformation(object.ParticipatableRange, object.RatedRange, object.Penalty);
    }
}

/**
 * @param contestScreenName
 * @return {Promise<ContestInformation>}
 */
async function fetchContestInformation(contestScreenName) {
    return new Promise(async (resolve) => {
        const topPageDom = await $.ajax(`https://atcoder.jp/contests/${contestScreenName}`).then(x => new DOMParser().parseFromString(x, "text/html"));
        const dataParagraph = topPageDom.getElementsByClassName("small")[0];
        const data = Array.from(dataParagraph.children).map(x => x.innerText.split(':')[1].trim());
        resolve(new ContestInformation(parseRangeString(data[0]), parseRangeString(data[1]), parseDurationString(data[2])));
    });

    /**
     * @param {string} [s]
     * @return {number[]}
     */
    function parseRangeString(s){
        if (s === 'All') return [0, Infinity];
        if (s.indexOf('~') === -1) return [0, -1];
        let res = s.split('~').map(x => parseInt(x.trim()));
        if (isNaN(res[0])) res[0] = 0;
        if (isNaN(res[1])) res[1] = Infinity;
        return res;
    }
    /**
     * parse duration string and return a millisecond
     * @param {string} [s]
     * @return {number}
     */
    function parseDurationString(s) {
        const dic = {ヶ月: "month", 日: "day", 時間: "hour", 分: "minute", 秒: "second"};
        let res = {};
        s.match(/(\d+[^\d]+)/g).forEach(x => {
            const trimmed = x.trim(' ','s');
            const num = trimmed.match(/\d+/)[0];
            const unit = trimmed.match(/[^\d]+/)[0];
            const convertedUnit = dic[unit]||unit;
            res[convertedUnit] = num;
        });
        return external_moment_["duration"](res).asMilliseconds();
    }
}

// CONCATENATED MODULE: ./src/libs/contest/results/results.js
class Results{
    constructor(){}
    /**
     * @param {string} userScreenName
     * @return {Result}
     */
    getUserResult(userScreenName){}
}
// CONCATENATED MODULE: ./src/libs/utils/twitter.js
/**
 *
 * @param {string} [content]
 * @param {string} [url]
 * @return {string}
 */
function GetEmbedTweetLink(content, url){
   return `https://twitter.com/share?text=${encodeURI(content)}&url=${encodeURI(url)}`
}
// CONCATENATED MODULE: ./src/libs/database/database.js

/**
 * オブジェクト生成用のコンストラクタです
 * @param {Function} [getNewData] 更新の際に新たなデータオブジェクトを返す関数です。
 * @param {string} [lsKey] 保存に用いるローカルストレージのkeyです。
 * @param {Function} [onUpdate] 更新の際に呼ばれる関数です。
 */
class DataBase {

    /**
     * オブジェクト生成用のコンストラクタです
     * @param {string} [name] indexedDBにアクセスする際に用いる名前です。 
     * @param {Number} [version] indexedDBにアクセスする際に用いるバージョンです。 
     */
    constructor(name, version, update) {
        this.name = name;
        this.version = version;
        indexedDB.open(name, version).onupgradeneeded = update;
    }

    /**
     * データをデータベースに追加/更新します。
     * @param {string} [storeName] indexedDBからストアを取得する際の名前です。
     * @param {string} [key] ストアにセットする際に用いるkeyです。
     * @param {Object} [value] ストアにセットする値です。 
     * @returns {Promise} 非同期のpromiseです。
     */
    async setData(storeName, key, value) {
        return new Promise((resolve, reject) => {
            try {
                indexedDB.open(this.name).onsuccess = (e) => {
                    const db = e.target.result;
                    const trans = db.transaction(storeName, 'readwrite');
                    const objStore = trans.objectStore(storeName);
                    const data = {id: key, data: value};
                    const putReq = objStore.put(data);
                    putReq.onsuccess = () => {
                        db.close();
                        resolve();
                    };
                };
            } catch (e) {
                reject(e);
            }
        });
    }

    /**
     * データをデータベースから取得します。存在しなかった場合はrejectされます。
     * @param {string} [storeName] indexedDBからストアを取得する際の名前です。
     * @param {string} [key] ストアにセットする際に用いるkeyです。
     * @returns {Promise} 非同期のpromiseです。
     */
    async getData(storeName, key) {
        return new Promise((resolve, reject) => {
            try {
                indexedDB.open(this.name).onsuccess = (openEvent) => {
                    const db = openEvent.target.result;
                    const trans = db.transaction(storeName, 'readwrite');
                    const objStore = trans.objectStore(storeName);
                    objStore.get(key).onsuccess = (getEvent) => {
                        const result = getEvent.target.result;
                        db.close();
                        if (!result) reject(`key '${key}' not found in store '${storeName}'`);
                        else resolve(result);
                    };
                };
            } catch (e) {
                reject(e);
            }
        });
    }
}

// CONCATENATED MODULE: ./src/libs/database/predictorDB.js


const StoreKeys = { aperfs: "APerfs", standings: "Standings" };
class predictorDB_PredictorDB extends DataBase {
    constructor() {
        super("PredictorDB", 1, (event) => {
            const db = event.target.result;
            const storeNames = ["APerfs", "Standings"];
            storeNames.forEach(store => {
                db.createObjectStore(store, { keyPath: "id" });
            });
        });
    }
}

// CONCATENATED MODULE: ./src/libs/contest/results/result.js
class Result{
    /***
     * @param {boolean} isRated
     * @param {boolean} isSubmitted
     * @param {string} userScreenName
     * @param {number} performance
     * @param {number} place
     * @param {number} ratedRank
     * @param {number} competitions
     * @param {number} innerPerformance
     * @param {number} oldRating
     * @param {number} newRating
     */
    constructor(isRated, isSubmitted, userScreenName, place, ratedRank, oldRating, newRating, competitions, performance, innerPerformance){
        this.IsRated = isRated;
        this.IsSubmitted = isSubmitted;
        this.UserScreenName = userScreenName;
        this.Place = place;
        this.RatedRank = ratedRank;
        this.OldRating = oldRating;
        this.NewRating = newRating;
        this.Competitions = competitions;
        this.Performance = performance;
        this.InnerPerformance = innerPerformance;
    }
}
// CONCATENATED MODULE: ./src/libs/contest/results/standingsResults.js




class standingsResults_OnDemandResults extends Results{
    /**
     * @param {Contest} contest
     */
    constructor(contest, templateResults){
        super();
        this.Contest = contest;
        this.TemplateResults = templateResults;
    }
    /**
     * @param {string} userScreenName
     * @return {Result}
     */
    getUserResult(userScreenName){
        const baseResults = this.TemplateResults[userScreenName];
        if (!baseResults) return null;
        if (!baseResults.Performance) {
            baseResults.InnerPerformance = this.Contest.getInnerPerf(baseResults.RatedRank);
            baseResults.Performance = Math.min(baseResults.InnerPerformance, this.Contest.perfLimit);
            baseResults.NewRating = Math.round(positivizeRating(calcRatingFromLast(unpositivizeRating(baseResults.OldRating), baseResults.Performance, baseResults.Competitions)));
        }
        return baseResults;
    }
}
// CONCATENATED MODULE: ./src/libs/contest/contest.js




class Tied{}

class contest_Contest{
    constructor(contestScreenName, contestInformation, standings, aPerfs){
        this.ratedLimit = contestInformation.RatedRange[1] + 1;
        this.perfLimit = this.ratedLimit + 400;
        this.standings = standings;
        this.rankMemo = {};

        const hoge = getHoge(standings.StandingsData, aPerfs, {2000: 800, 2800: 1000, Infinity: 1200}[this.ratedLimit] || 1200, this.ratedLimit);
        this.contestantAPerf = hoge.contestantAPerf;
        this.templateResults = hoge.templateResults;

        /** @return {{contestantAPerf: number[], templateResults: Object<string, Result>}} */
        function getHoge(standingsData, aPerfs, defaultAPerf, ratedLimit) {
            let data = getData((data) => data.IsRated && data.TotalResult.Count !== 0);
            console.log(data);
            if (data.contestantAPerf.length === 0) data = getData((data) => data.OldRating < ratedLimit && data.TotalResult.Count !== 0);
            console.log(data);
            return data;

            /** @return {{contestantAPerf: number[], templateResults: Object.<string, Result>}}*/
            function getData(isUserRated) {
                let contestantAPerf = [];
                let templateResults = {};

                let currentRatedRank = 1;

                let lastRank = 0;
                let tiedUsers = [];
                let ratedInTiedUsers = 0;
                function applyTiedUsers(){
                    tiedUsers.forEach((data) => {
                        if (isUserRated(data)){
                            contestantAPerf.push(aPerfs[data.UserScreenName] || defaultAPerf);
                            ratedInTiedUsers++;
                        }
                    });

                    let ratedRank = currentRatedRank + Math.max(0, ratedInTiedUsers - 1) / 2;
                    tiedUsers.forEach((data) => {
                        templateResults[data.UserScreenName] =
                            new Result(
                                isUserRated(data),
                                data.TotalResult.Count !== 0,
                                data.UserScreenName,
                                data.Rank,
                                ratedRank,
                                data.OldRating,
                                null,
                                data.Competitions,
                                null,
                                null
                            );
                    });
                    currentRatedRank += ratedInTiedUsers;
                    tiedUsers.length = 0;
                    ratedInTiedUsers = 0;
                }

                standingsData.forEach((data) => {
                    if (lastRank !== data.Rank) applyTiedUsers();
                    tiedUsers.push(data);
                });
                applyTiedUsers();

                return {contestantAPerf: contestantAPerf, templateResults: templateResults};
            }
        }
    }

    getRatedRank(X) {
        if (this.rankMemo[X]) return this.rankMemo[X];
        return this.rankMemo[X] = this.contestantAPerf.reduce((val, APerf) => val + (1.0 / (1.0 + Math.pow(6.0, ((X - APerf) / 400.0)))), 0);
    }

    getPerf(ratedRank){
        return Math.min(this.getInnerPerf(ratedRank), this.perfLimit);
    }

    getInnerPerf(ratedRank){
        let upper = 6144;
        let lower = -2048;
        while (upper - lower > 0.5) {
            const mid = (upper + lower) / 2;
            if (ratedRank - 0.5 > this.getRatedRank(mid)) upper = mid;
            else lower = mid;
        }
        return Math.round((upper + lower) / 2);
    }
}
// CONCATENATED MODULE: ./src/elements/predictor/script.js















let predictor = new SideMenuElement('predictor','Predictor',/atcoder.jp\/contests\/.+/, dom_default.a, afterAppend);

const firstContestDate = external_moment_("2016-07-16 21:00");
const predictorElements = ['predictor-input-rank', 'predictor-input-perf', 'predictor-input-rate', 'predictor-current', 'predictor-reload', 'predictor-tweet'];

//クソの上にクソを塗り固めたようなゴミ実装 やめたら?
async function afterAppend() {
    const isStandingsPage = /standings([^\/]*)?$/.test(document.location.href);
    const predictorDB = new predictorDB_PredictorDB();
    const historyData = new history_HistoryData(userScreenName);
    const standingsData = new standings_StandingsData(contestScreenName);
    const aperfsData = new aperfs_APerfsData(contestScreenName);

    await historyData.update();

    const contestInformation = await fetchContestInformation(contestScreenName);

    if (isStandingsPage && contestInformation.RatedRange[0] <= contestInformation.RatedRange[1]){
        initStandings();
    }

    /** @type Results */
    let results;
    /** @type Contest */
    let contest;

    $('[data-toggle="tooltip"]').tooltip();
    $('#predictor-reload').click(function () {
        UpdatePredictorsData();
    });
    $('#predictor-current').click(function () {
        //自分の順位を確認
        let myRank = 0;
        let ratedCount = 0;
        let lastRank = 0;
        let rank = 1;
        let isContainedMe = false;
        //全員回して自分が出てきたら順位更新フラグを立てる
        standingsData.data.StandingsData.forEach(function (element) {
            if (lastRank !== element.Rank) {
                if (isContainedMe) {
                    myRank = rank + Math.max(0, ratedCount - 1) / 2;
                    isContainedMe = false;
                }
                rank += ratedCount;
                ratedCount = 0;
            }
            if (userScreenName === element.UserScreenName) isContainedMe = true;
            if (element.IsRated && element.TotalResult.Count !== 0) ratedCount++;
            lastRank = element.Rank;
        })
        if (isContainedMe) {
            myRank = rank + ratedCount / 2;
        }

        if (myRank === 0) return;
        $('#predictor-input-rank').val(myRank);
        lastUpdated = 0;
        drawPredictor();
    });
    $('#predictor-input-rank').keyup(function (event) {
        lastUpdated = 0;
        drawPredictor();
    });
    $('#predictor-input-perf').keyup(function (event) {
        lastUpdated = 1;
        drawPredictor();
    });
    $('#predictor-input-rate').keyup(function (event) {
        lastUpdated = 2;
        drawPredictor();
    });

    let lastUpdated = 0;

    Promise.all(
        [predictorDB.getData("APerfs", contestScreenName),
            predictorDB.getData("Standings", contestScreenName)]
    ).then((result) => {
        aperfsData.data = result[0].data;
        standingsData.data = result[1].data;
        CalcActivePerf();
        drawPredictor();
        enabled();
        AddAlert('ローカルストレージから取得されました。');
        if (isStandingsPage) {
            updateResultsData();
            addPerfToStandings();
        }
    }).catch(() => {
        UpdatePredictorsData();
    });

    //データを更新して描画する
    function UpdatePredictorsData() {
        if (!startTime.isBefore()) {
            disabled();
            AddAlert('コンテストは始まっていません');
            return;
        }
        if (external_moment_(startTime) < firstContestDate) {
            disabled();
            AddAlert('現行レートシステム以前のコンテストです');
            return;
        }
        if (contestInformation.RatedRange[0] > contestInformation.RatedRange[1]) {
            disabled();
            AddAlert('ratedなコンテストではありません');
            return;
        }
        $('#predictor-reload').button('loading');
        AddAlert('順位表読み込み中…');
        Promise.all(
            [aperfsData.update(),
            standingsData.update()]
        ).then(() => {
            if (Object.keys(aperfsData.data).length === 0) {
                disabled();
                AddAlert('APerfのデータが提供されていません');
                return;
            }
            if (standingsData.data.Fixed) {
                predictorDB.setData('APerfs', contestScreenName, aperfsData.data);
                predictorDB.setData('Standings', contestScreenName, standingsData.data);
            }
            CalcActivePerf();
            if (isStandingsPage) {
                updateResultsData();
                addPerfToStandings();
            }
            drawPredictor();
            enabled();
            AddAlert(`最終更新 : ${external_moment_().format('HH:mm:ss')}`);
        }).catch(() => {
            disabled();
            AddAlert('データの読み込みに失敗しました。');
        });
    }

    //ActivePerfの再計算
    function CalcActivePerf() {
        contest = new contest_Contest(contestScreenName, contestInformation, standingsData.data, aperfsData.data);
    }

    //フォームを更新
    function drawPredictor() {
        switch (lastUpdated) {
            case 0:
                UpdatePredictorFromRank();
                break;
            case 1:
                UpdatePredictorFromPerf();
                break;
            case 2:
                UpdatePredictorFromRate();
                break;
        }
        function UpdatePredictorFromRank() {
            let rank = $("#predictor-input-rank").val();
            lastUpdated = 0;
            let perf = contest.getPerf(rank);
            let rate = getRate(perf);
            UpdatePredictor(rank, perf, rate);
        }
        function UpdatePredictorFromPerf() {
            let perf = $("#predictor-input-perf").val();
            lastUpdated = 1;
            let rank = contest.getRatedRank(perf);
            let rate = getRate(perf);
            UpdatePredictor(rank, perf, rate)
        }
        function UpdatePredictorFromRate() {
            let rate = $("#predictor-input-rate").val();
            lastUpdated = 2;
            let upper = 10000;
            let lower = -10000;
            while (upper - lower > 0.125) {
                const mid = (upper + lower) / 2;
                if (rate < getRate(mid)) upper = mid;
                else lower = mid;
            }
            let perf = (upper + lower) / 2;
            let rank = contest.getRatedRank(perf);
            UpdatePredictor(rank, perf, rate);
        }
        function UpdatePredictor(rank, perf, rate) {
            $("#predictor-input-rank").val(round(rank));
            $("#predictor-input-perf").val(round(perf));
            $("#predictor-input-rate").val(round(rate));
            updatePredictorTweetBtn();
            function round(val) {
                return Math.round(val * 100) / 100;
            }
        }
        function getRate(perf) {
            return positivizeRating(calcRatingFromHistory(historyData.data.filter(x => x.IsRated).map(x => x.Performance).concat(perf).reverse()));
        }
        //ツイートボタンを更新する
        function updatePredictorTweetBtn() {
            let tweetStr = `Rated内順位: ${$("#predictor-input-rank").val()}位%\nパフォーマンス: ${$("#predictor-input-perf").val()}\nレート: ${$("#predictor-input-rate").val()}\n`;
            $('#predictor-tweet').attr("href", GetEmbedTweetLink(tweetStr, "https://greasyfork.org/ja/scripts/369954-ac-predictor"));
        }
    }

    //最終更新などの要素を追加する
    function AddAlert(content) {
        $("#predictor-alert").html(`<h5 class='sidemenu-txt'>${content}</h5>`);
    }

    //要素のDisabledを外す
    function enabled() {
        $('#predictor-reload').button('reset');
        predictorElements.forEach(element => {
            $(`#${element}`).removeAttr("disabled");
        });
    }

    //要素にDisabledをつける
    function disabled() {
        $('#predictor-reload').button('reset');
        predictorElements.forEach(element => {
            $(`#${element}`).attr("disabled", true);
        });
    }

    //全員の結果データを更新する
    function updateResultsData() {
        results = new standingsResults_OnDemandResults(contest, contest.templateResults);
    }

    function initStandings() {
        (new MutationObserver(() => { addPerfToStandings(); })).observe(document.getElementById('standings-tbody'), { childList: true });
        $('thead > tr').append('<th class="standings-result-th" style="width:84px;min-width:84px;">perf</th><th class="standings-result-th" style="width:168px;min-width:168px;">レート変化</th>');
    }

    //結果データを順位表に追加する
    function addPerfToStandings() {
        $('.standings-perf , .standings-rate').remove();

        $('#standings-tbody > tr').each((index, elem) => {
            if (elem.childNodes.length <= 3) {
                let unparticipatedResultCell = elem.getElementsByClassName("standings-result")[0];
                unparticipatedResultCell.setAttribute("colspan", parseInt(unparticipatedResultCell.getAttribute("colspan")) + 2);
                return;
            }
            const userName = $('.standings-username .username', elem).text();
            const result = results.getUserResult(userName);
            const perfElem = !result || !result.IsSubmitted ? '-' : getRatingSpan(result.Performance);
            const rateElem = !result ? '-' : result.IsRated ? getRatingChangeElem(result.OldRating, result.NewRating) : getUnratedElem(result.OldRating);
            $(elem).append(`<td class="standings-result standings-perf">${perfElem}</td>`);
            $(elem).append(`<td class="standings-result standings-rate">${rateElem}</td>`);
            function getRatingChangeElem(oldRate, newRate) {
                return `<span class="bold">${getRatingSpan(oldRate)}</span> → <span class="bold">${getRatingSpan(newRate)}</span> <span class="grey">(${(newRate >= oldRate ? '+' : '')}${newRate - oldRate})</span>`;
            }
            function getUnratedElem(rate) {
                return `<span class="bold">${getRatingSpan(rate)}</span> <span class="grey">(unrated)</span>`;
            }
            function getRatingSpan(rate) {
                return `<span class="user-${getColor(rate)}">${rate}</span>`;
            }
        });
    }
}

// EXTERNAL MODULE: ./src/elements/estimator/dom.html
var estimator_dom = __webpack_require__(4);
var estimator_dom_default = /*#__PURE__*/__webpack_require__.n(estimator_dom);

// CONCATENATED MODULE: ./src/elements/estimator/state/EstimatorModel.js
class EstimatorModel{
    constructor(inputValue, perfHistory){
        this.inputDesc = "";
        this.resultDesc = "";
        this.perfHistory = perfHistory;
        this.updateInput(inputValue);
    }

    updateInput(value){
        this.inputValue = value;
        this.resultValue = this.calcResult(value);
    }

    toggle(){}

    /**
     * @param {Number} [input]
     * @return {Number}
     */
    calcResult(input){ return input }
}

// CONCATENATED MODULE: ./src/elements/estimator/state/CalcRatingModel.js




class CalcRatingModel_CalcRatingModel extends EstimatorModel{
    constructor(inputValue, perfHistory){
        super(inputValue, perfHistory);
        this.inputDesc = "パフォーマンス";
        this.resultDesc = "到達レーティング";
    }

    toggle(){
        return new CalcPerfModel_CalcPerfModel(this.resultValue, this.perfHistory);
    }
    calcResult(input){
        return positivizeRating(calcRatingFromHistory([input].concat(this.perfHistory)));
    }
}
// CONCATENATED MODULE: ./src/elements/estimator/state/CalcPerfModel.js




class CalcPerfModel_CalcPerfModel extends EstimatorModel{
    constructor(inputValue, perfHistory){
        super(inputValue, perfHistory);
        this.inputDesc = "目標レーティング";
        this.resultDesc = "必要パフォーマンス";
    }

    toggle(){
        return new CalcRatingModel_CalcRatingModel(this.resultValue, this.perfHistory);
    }
    calcResult(input){
        return calcRequiredPerformance(unpositivizeRating(input), this.perfHistory);
    }
}
// CONCATENATED MODULE: ./src/atcoder-lib/utils.js

// format
if (typeof String.prototype.format === 'undefined') {
  String.prototype.format = function(arg) {
    var rep_fn = undefined;
    if (typeof arg == "object") {
      rep_fn = function(m, k) { return arg[k];};
    } else {
      var args = arguments;
      rep_fn = function(m, k) { return args[parseInt(k)];};
    }
    return this.replace(/\{(\w+)\}/g, rep_fn);
  }
}

// array search
function has(a, val) {
  return a.indexOf(val) != -1;
}

// other util
function arrayToSet(a) {
  var s = new Set();
  for (var i in a) s.add(a[i]);
    return s;
}
function setToArray(s) {
  var a = [];
  s.forEach(function(val) { a.push(val);});
  return a;
}


// cookie
var COOKIE_EXPIRES = 10000;
function setCookie(key, val, expires) {
  Cookies.set(key, val, { expires: expires || COOKIE_EXPIRES});
}
function getCookie(key) {
  return Cookies.getJSON(key);
}
function getCookieBool(key) {
  return (Cookies.get(key) === 'true');
}
function delCookie(key) {
  Cookies.remove(key);
}

// local storage
function setLS(key, val) {
  try {
    localStorage.setItem(key, JSON.stringify(val));
  } catch(error) {
    console.log(error);
  }
}
function getLS(key) {
  var val = localStorage.getItem(key);
  return val?JSON.parse(val):val;
}
function delLS(key) {
  localStorage.removeItem(key);
}

// migrate Cookie to LocalStorage
{
  var val;
  if (val = getCookie('fav')) {
    for (var i in val) favSet.add(val[i]);
    storeFavs();
    delCookie('fav');
  }
  var keys = ['plain_editor','auto_height','defaultLang',
    'show_affiliation','show_fav_btn','show_fav_only','show_rated_only'];
  for (var i = 0; i < keys.length; i++) {
    if (val = Cookies.get(keys[i])) {
      setLS(keys[i], val);
      delCookie(keys[i]);
    }
  }
}


// server time
var timeDelta = getCookie('timeDelta');
if (typeof timeDelta === 'undefined') {
  timeDelta = 0;
  setCookie('timeDelta', 0, 1/24.0);
  $.ajax('/servertime?ts={}'.format(moment().unix())).done(function(serverTime) {
    serverTime = moment(serverTime);
    if (!serverTime.isValid()) return;
    timeDelta = serverTime.diff(moment());
    setCookie('timeDelta', timeDelta, 1/24.0);
  });
}
function getServerTime() { return moment().add(timeDelta);}

// escape
function E(str) {
  return str
  .replace(/&/g, '&amp;')
  .replace(/</g, '&lt;')
  .replace(/>/g, '&gt;')
  .replace(/"/g, '&quot;')
  .replace(/'/g, '&#39;');
}

// toRegExp
function toRegExp(pattern) {
  pattern = pattern
  .replace(/[-\/\\^+.()|[\]{}]/g, '\\$&')
  .replace(/\?/g, '.')
  .replace(/\*/g, '.*');
  return new RegExp('^'+pattern);
}

// randint
function rand(range) {
  return Math.floor(Math.random()*(range[1]-range[0]))+range[0];
}

// clipboard
function copy(textVal){
  var copyFrom = document.createElement("textarea");
  copyFrom.textContent = textVal;
  var bodyElm = document.getElementsByTagName("body")[0];
  bodyElm.appendChild(copyFrom);
  copyFrom.select();
  var retVal = document.execCommand('copy');
  bodyElm.removeChild(copyFrom);
  return retVal;
}

// fit font size
(function($) {
  $.fn.fitFontSize = function(width, len, max) {
    $(this).css('font-size', Math.min(width/len, max)+'px');
  };
})(jQuery);

// user favs
var favSet;
function storeFavs() {
  setLS('fav', setToArray(favSet));
}
function reloadFavs() {
  favSet = arrayToSet(getLS('fav') || []);
}
function toggleFav(val) {
  reloadFavs();
  var res;
  if (favSet.has(val)) {
    favSet.delete(val);
    res = false;
  } else {
    favSet.add(val);
    res = true;
  }
  storeFavs();
  return res; // has val now
}




// CONCATENATED MODULE: ./src/elements/estimator/script.js









let estimator = new SideMenuElement('estimator','Estimator',/atcoder.jp/, estimator_dom_default.a, script_afterAppend);

async function script_afterAppend() {
    const estimatorInputSelector = $("#estimator-input");
    const estimatorResultSelector = $("#estimator-res");
    let model = GetModelFromStateCode(getLS("sidemenu_estimator_state"), getLS("sidemenu_estimator_value"), await GetHistory());
    updateView();

    $("#estimator-toggle").click(function () {
        model = model.toggle();
        updateLocalStorage();
        updateView();
    });
    estimatorInputSelector.keyup(() => {
        updateModel();
        updateLocalStorage();
        updateView();
    });

    /** modelをinputの値に応じて更新 */
    function updateModel() {
        const inputString = estimatorInputSelector.val();
        if (!isFinite(inputString)) return;
        const inputNumber = parseInt(inputString);
        model.updateInput(inputNumber);
    }

    /** modelの状態をLSに保存 */
    function updateLocalStorage() {
        setLS("sidemenu_estimator_value", model.inputValue);
        setLS("sidemenu_estimator_state", model.constructor.name);
    }

    /** modelを元にviewを更新 */
    function updateView() {
        const roundedInput = roundVal(model.inputValue, 2);
        const roundedResult = roundVal(model.resultValue, 2);

        $("#estimator-input-desc").text(model.inputDesc);
        $("#estimator-res-desc").text(model.resultDesc);
        estimatorInputSelector.val(roundedInput);
        estimatorResultSelector.val(roundedResult);

        const tweetStr = `AtCoderのハンドルネーム: ${userScreenName}\n${model.inputDesc}: ${roundedInput}\n${model.resultDesc}: ${roundedResult}\n`;
        $('#estimator-tweet').attr("href", GetEmbedTweetLink(tweetStr, "https://greasyfork.org/ja/scripts/369954-ac-predictor"));

        function roundVal(value, digit) {
            return Math.round(value * (10 ** digit)) / (10 ** digit);
        }
    }
}

/**
 * パフォーマンス履歴を取得し、いい感じで整形して返します
 * @return {Promise<Number[]>} パフォーマンス履歴を返すpromise
 */
async function GetHistory(){
    return new Promise((resolve) => {
        new history_HistoryData(userScreenName).update().then((data) => {
            resolve(data.filter(x => x.IsRated)
                .sort((a, b) => external_moment_default()(b.EndTime) - external_moment_default()(a.EndTime))
                .map(x => x.Performance));
        })
    });
}

const models = [CalcPerfModel_CalcPerfModel, CalcRatingModel_CalcRatingModel];

/**
 * LocalStorageに保存されたステートコードから状態を復元します
 * @param {string} [state] ステートを示す文字列(型名)
 * @param {number} [value] 最初に入る値
 * @param {number[]} [history] パフォーマンス履歴(時間降順)
 * @return {EstimatorModel} 構築されたモデル
 */
function GetModelFromStateCode(state, value, history) {
    let model = models.find(model => model.name === state);
    if (!model) model = CalcPerfModel_CalcPerfModel;
    return new model(value, history);
}
// CONCATENATED MODULE: ./src/main.js


initializeID();


let main_sidemenu = new sidemenu_SideMenu();

if (predictor.ElementShouldDisplayed(document.location.href)) main_sidemenu.AddElement(predictor);
if (estimator.ElementShouldDisplayed(document.location.href)) main_sidemenu.AddElement(estimator);


/***/ })
/******/ ]);