ganariya
https://gyazo.com/6082d7221db83bdad5758c02cef24bf3
About
ガナリヤが競技プログラミングの問題をまとめているサイトです。
code:script.js
import '/api/code/daiiz/omakase-links/script.js'
scrapbox.TimeStamp.addFormat('YYYY/MM/DD')
const __appliedProject__ = scrapbox.Project.name
const __charCounterSetup__ = setInterval(function() {
// ページが準備できるのを待ちたいので、スクリプトがロードされてから3秒くらいしたら処理開始↓↓
if (document.getElementById('editor') && scrapbox.Page.lines)
clearInterval(__charCounterSetup__)
else
return // ページの準備ができてないときはまた3秒待つ
// 下準備
const $id = id => document.getElementById(id)
const $query = q => document.querySelector(q)
const fmt = n => new Intl.NumberFormat('en-US').format(n).padStart(6)
// 文字数カウンター表示用のエレメントを作ってく
const linesText = $query('.lines').innerText.trim()
const chars = linesText.split(/\s+/).join('').length
var counterWrapper = document.createElement('div')
counterWrapper.id = '__charCounter__'
counterWrapper.innerHTML = <span>${fmt(chars)} chars</span> +
'<pre id="__charCounterPopup__" style="opacity:0"></pre>'
$id('editor').appendChild(counterWrapper)
const counter = $query('#__charCounter__ span')
const popup = $id('__charCounterPopup__')
// 文字数カウンターにマウスカーソルを乗せたときに詳細をポップアップする
counter.addEventListener('mouseover',
function() {
const linesText = $query('.lines').innerText.trim()
const chars = linesText.split(/\s+/).join('').length
const words = linesText.split(/\s+/).length
popup.innerHTML = ${fmt(chars)} chars\n +
${fmt(words)} words\n +
${fmt(scrapbox.Page.lines.length)} lines
popup.style.opacity = 1
})
// 文字数カウンターからマウスカーソルが離れたら詳細ポップアップを見えなくする
counter.addEventListener('mouseout', function() { popup.style.opacity = 0 })
// 文字数のみを数え直す関数
const updateCounter = function() {
if ($query('.presentation')
|| scrapbox.Project.name !== __appliedProject__) {
// プレゼンモードになってたり、よそのプロジェクトを表示してたら文字数カウンターを非表示にする
counterWrapper.style.display = 'none'
} else if (scrapbox.Page.lines) {
// ここで数え直ししてます
const linesText = $query('.lines').innerText.trim()
const chars = linesText.split(/\s+/).join('').length
counter.innerText = ${fmt(chars)} chars
counterWrapper.style.display = 'block'
}
}
// 数え直すタイミングは、テキスト入力時とペースト時
$id('text-input').addEventListener('input', updateCounter)
$id('text-input').addEventListener('paste', updateCounter)
// 何もしなくても3秒ごとに数え直す
setInterval(updateCounter, 3000)
}, 3000);
$('body').attr('data-daiiz-rel-bubble', 'n')
// リンク先ページのテキストを表示する
$('body').attr('data-daiiz-text-bubble', 'on')
// GyazoでScrapboxのページを読む
$('body').attr('data-daiiz-gyazo-text-bubble', 'teamj')
$('body').attr('data-daiiz-paste-url-title', 'ctrl')
scrapbox.PageMenu.addItem({
title: () => 'Tweet',
onClick: () =>(function(){
let href = location.href;
window.open(url);
})()
});
const Asearch = (function() {
var INITPAT, INITSTATE, MAXCHAR;
INITPAT = 0x80000000;
MAXCHAR = 0x100;
Asearch.prototype.isupper = function(c) {
return (c >= 0x41) && (c <= 0x5a);
};
Asearch.prototype.islower = function(c) {
return (c >= 0x61) && (c <= 0x7a);
};
Asearch.prototype.tolower = function(c) {
if (this.isupper(c)) {
return c + 0x20;
} else {
return c;
}
};
Asearch.prototype.toupper = function(c) {
if (this.islower(c)) {
return c - 0x20;
} else {
return c;
}
};
function Asearch(source) {
var c, i, j, len, mask, ref, ref1;
this.source = source;
this.shiftpat = [];
this.epsilon = 0;
this.acceptpat = 0;
mask = INITPAT;
for (c = i = 0, ref = MAXCHAR; 0 <= ref ? i < ref : i > ref; c = 0 <= ref ? ++i : --i) {
}
ref1 = this.unpack(this.source);
for (j = 0, len = ref1.length; j < len; j++) {
if (c === 0x20) {
this.epsilon |= mask;
} else {
mask >>>= 1;
}
}
this.acceptpat = mask;
return this;
}
Asearch.prototype.state = function(state, str) {
var c, i, i0, i1, i2, i3, len, mask, ref;
if (state == null) {
state = INITSTATE;
}
if (str == null) {
str = '';
}
ref = this.unpack(str);
for (i = 0, len = ref.length; i < len; i++) {
i3 = (i3 & this.epsilon) | ((i3 & mask) >>> 1) | (i2 >>> 1) | i2;
i2 = (i2 & this.epsilon) | ((i2 & mask) >>> 1) | (i1 >>> 1) | i1;
i1 = (i1 & this.epsilon) | ((i1 & mask) >>> 1) | (i0 >>> 1) | i0;
i0 = (i0 & this.epsilon) | ((i0 & mask) >>> 1);
i1 |= i0 >>> 1;
i2 |= i1 >>> 1;
i3 |= i2 >>> 1;
}
};
Asearch.prototype.match = function(str, ambig) {
var s;
if (ambig == null) {
ambig = 0;
}
s = this.state(INITSTATE, str);
if (!(ambig < INITSTATE.length)) {
ambig = INITSTATE.length - 1;
}
return (sambig & this.acceptpat) !== 0; };
Asearch.prototype.unpack = function(str) {
var bytes, c, code, i, len, ref;
bytes = [];
ref = str.split('');
for (i = 0, len = ref.length; i < len; i++) {
code = c.charCodeAt(0);
if (code > 0xFF) {
bytes.push((code & 0xFF00) >>> 8);
}
bytes.push(code & 0xFF);
}
return bytes;
};
return Asearch;
})();
const projectName = scrapbox.Project.name;
let emojis = [];
const box = $('<div>').addClass('form-group').css("position", "absolute");
const container = $('<div>').addClass('dropdown');
box.append(container);
let items = $('<ul>').addClass('dropdown-menu');
container.append(items);
$('#editor').append(box);
fetch(/api/pages/${projectName}?limit=10000, { credentials: 'same-origin'})
.then( res => res.text())
.then( text => {
const data = JSON.parse( text );
const pages = data.pages;
pages.filter( page => (page.image !== null && page.title.match(/^\w\s\-\++$/))) .forEach( page => {
emojis.push({
name: page.title,
path: page.title,
icon: /api/pages/${projectName}/${page.title}/icon,
})
})
})
scrapbox.PageMenu.addMenu({
title: 'emoji',
})
scrapbox.PageMenu('emoji').addItem({
title: "load emojis from /emoji",
onClick: () => {
fetch('/api/pages/emoji?limit=10000')
.then( res => res.text())
.then( text => {
const data = JSON.parse( text );
const pages = data.pages;
pages.filter( page => (page.image !== null && page.title.match(/^\w\s\-\++$/))) .forEach( page => {
for( let emoji of emojis ) {
if( emoji.name === page.title )return;
}
emojis.push({
name: page.title,
path: '/emoji/' + page.title,
icon: /api/pages/emoji/${page.title}/icon,
})
})
})
}
})
// TODO: 様々な文字列が来る場合を考慮する
const taberareloo = ( word, list ) => {
const targetWord = word.replace(':', '');
const regStr = targetWord.split('').reduce( (pre, cur) => pre + cur + '.*' ).replace('+', '\\+');
const reg = RegExp(regStr,'i');
return list.filter( item => item.name.match(reg));
}
const asearched = ( word, list ) => {
const targetWord = word.replace(':', '');
const a = new Asearch( targetWord );
const limitCount = Math.floor( targetWord.length/ 4 ) + 1;
let result = [];
for(let i = 0; i <= limitCount; i++){
let matched = list.filter( item => a.match( item.name, i));
let notExisted = matched.filter( item => {
for( let r of result){
if(r.name === item.name){
return false;
}
}
return true;
})
}
return result;
}
const fizzSearch = ( word, list ) => {
const a = asearched( word, list );
const b = taberareloo( word, list );
const c = b.filter( item => {
for( let r of a ){
if( r.name == item.name){
return false;
}
}
return true;
})
}
let stack = "";
const editor = $('#editor');
const open = () => container.addClass("open");
const close = () => {
stack = "";
container.removeClass("open");
}
const replaceText = (text, cursor, emojiPath) => {
cursor.focus();
setTimeout(()=>{
for(let i = 0; i < text.length; i++){
var ke1 = document.createEvent("Events");
ke1.initEvent("keydown", true, true);
ke1.keyCode = ke1.which = 8; // Backspace
cursor.dispatchEvent(ke1);
}
document.execCommand('insertText',null, [${emojiPath}.icon] );
close();
}, 50)
}
editor.keydown( e => {
const key = e.key;
if(key === undefined ) return;
if( stack === "" && key !== ":"){
close();
return
};
if ($('.cursor-line').text().trim() == 'code:'
|| $('.cursor-line .code-block').length == 1) {
close()
return;
}
if( key === ':' && stack.length !== 0){
let name = stack.replace(':', '');
for(let emoji of emojis){
if( emoji.name === name ){
let cursor = $('#text-input')0; replaceText(stack + ":", cursor, emoji.path);
return;
}
}
close()
return;
}
const cursor = $('#text-input')0; stack += e.key;
let focused = $(':focus');
if(focused.is(items.find('li > a'))){
cursor.focus();
}
}
if( stack.length === 2 ){
if( key === " " ){
stack = "";
return;
}
open();
}
switch(key){
case 'Backspace':
stack = stack.slice(0, stack.length - 1);
if(stack.length === 0){
close();
return;
}
break;
case 'ArrowUp':
let focusedUp = $(':focus');
if( focusedUp.is(items.find('li > a').eq(0)) ){
e.stopPropagation();
cursor.focus();
}else if( !focusedUp.is(items.find('li > a')) ){
close();
return;
}
break;
case 'ArrowDown':
let focusedDown = $(':focus');
if( !focusedDown.is(items.find('li > a'))) {
e.stopPropagation();
e.preventDefault();
items.find("li > a").eq(0).focus();
}
break;
case 'Escape':
case 'ArrowLeft':
case 'ArrowRight':
case 'Home':
case 'End':
case 'PageUp':
case 'PageDown':
close();
break;
case 'Enter':
if( stack.length === 1 ){
close();
break;
}
let focused = $(':focus');
if(!focused.is(items.find('li > a'))){
e.stopPropagation();
e.preventDefault();
items.find('li > a').eq(0).click();
}
break;
}
if( stack.length <= 1 || !key.match(/^\w\s\:\-\+$|Backspace/)) return; const matchedEmoji = fizzSearch(stack, emojis)
if( matchedEmoji.length === 0){
close();
return;
}
const newItems = $('<ul>').addClass('dropdown-menu');
matchedEmoji.forEach( ( emoji, index) => {
if( index > 30 ) return;
const li = $('<li>').addClass('dropdown-item');
const a = $('<a>').attr("tabindex", "0");
const img = $('<img>').attr("src", emoji.icon)
.addClass("icon").css({ height: "17px", float: "left"});
const nameTag = $('<div>').text(" :" + emoji.name + ":");
a.append(img);
a.append(nameTag);
li.append(a);
newItems.append(li);
a.on('click', () => {
cursor.focus();
replaceText(stack, cursor, emoji.path);
})
a.on('keypress', ev => {
if(ev.key === "Enter"){
ev.preventDefault();
ev.stopPropagation();
replaceText(stack, cursor, emoji.path);
}
})
})
items.replaceWith(newItems);
items = newItems;
let css = {};
cursor.style.cssText.split(';').filter( text => text !== '' )
.forEach( text => {
const props = text.split(':').map( text => text.replace(' ', '').replace('px', ''));
});
box.css({
top: ${parseInt(css.top) + parseInt(css.height) + 3}px,
left: ${css.left}px,
});
})
for(let e of document.getElementsByClassName("hover")){
e.onclick = () =>{
let id = Math.floor(Math.random() * (10 - 1)) + 1;
let voice = new Audio(url);
voice.volume = 0.3;
voice.play()
}
}
document.querySelector('.app')
.insertAdjacentHTML('beforeend', '<div id="presentation-progress" style="display:none"></div>')
document.querySelector('.app')
.insertAdjacentHTML('beforeend', `<style media="screen">
display: block !important;
z-index: 100; position: fixed; bottom: 0; left: 0; width: 100vw;
text-align: center; line-height: 1;
font-size: x-small; /* 📝バーの高さを変えたいときはフォントサイズを変えてね */
--bar-color: #fc0; /* 📝プログレスバーの色 */ --bar-bgcolor: #eee; /* 📝プログレスバーの背景色 */ --complete: 0;
background: linear-gradient(to right,
var(--bar-color) var(--complete), var(--bar-bgcolor) var(--complete), var(--bar-bgcolor))
}
</style>`);
(() => {
const updateProgress = () => {
const sections = Array.from(document.querySelectorAll('.section-title'))
const currentPos = sections.findIndex(e => {
return window.getComputedStyle(e, null).getPropertyValue('display') != 'none'
}) + 1
const complete = currentPos / sections.length * 100
const progressBarElem = document.getElementById('presentation-progress')
progressBarElem.style.setProperty('--complete', complete + '%')
progressBarElem.innerText = ${currentPos} / ${sections.length}
}
const appObserver = new MutationObserver(mutations => {
mutations.forEach(mutation => {
const addedStyles = Array.from(mutation.addedNodes).filter(node => node.tagName == 'STYLE')
if (addedStyles.length) {
// プレゼンモード開始?
// STYLEタグも MutationObserver で監視したかったけど無理ぽいのでキーイベント拾います
window.addEventListener('keydown', updateProgress)
updateProgress()
}
const removedStyles = Array.from(mutation.removedNodes).filter(node => node.tagName == 'STYLE')
if (removedStyles.length) {
// プレゼンモード終了?
window.removeEventListener('keydown', updateProgress)
}
})
})
appObserver.observe(document.querySelector('.app'), { childList: true })
})()
code:style.css
.line.formula-line .dot { display: block }
/* インデントレベルごとに行頭文字を切り替えるスタイル指定*/
/* メニューのHide Dotと連動する */
/* この例では、第5レベルまでの行頭文字を指定。第6レベルからはドットに戻る */
/* すべてのビュレット文字に共通の設定 */
.line .indent-mark .dot:before {
display: block;
position: absolute;
right: -5px;
top: -10px;
}
/* 第1レベルのインデント(c-0)の設定 */
/* ドットはバックグラウンドカラーなので透明にする */
/* セレクタとして
.line .indent-mark .dot { ... }
を使うとインデントレベルごとに
background-color: transparent;
を指定する必要はないが、指定のないレベルのドットが消える
*/
.line .indent-mark .c-0 + .dot {
background-color: transparent;
}
/* このレベルの行頭文字を表示 */
.line .indent-mark .c-0 + .dot:before {
content:"■";
}
/* 以下同様 */
.line .indent-mark .c-1 + .dot {
background-color: transparent;
}
.line .indent-mark .c-1 + .dot:before {
content:"□";
}
.line .indent-mark .c-2 + .dot {
background-color: transparent;
}
.line .indent-mark .c-2 + .dot:before {
content:"▶";
}
.line .indent-mark .c-3 + .dot {
background-color: transparent;
}
.line .indent-mark .c-3 + .dot:before {
content:"●";
}
.line .indent-mark .c-4 + .dot {
background-color: transparent;
}
.line .indent-mark .c-4 + .dot:before {
content:"○";
}
display: inline-block;
padding: 2px 8px;
margin: 0 8px 10px 0;
font-size: 0.8em;
border-radius: 3px;
transition: .3s;
-webkit-transform: scale(1);
transform: scale(1);
}
-webkit-transform: scale(1.1);
transform: scale(1.1);
}
.line img.image{
display: block;
margin: 0 auto;
}
/* 行間調整 */
.line .section-0{
line-height: 1.2em !important;
}
.grid li.page-list-item a .pin{
width: 14px;
height: 14px;
}
.level-1{
background: linear-gradient(transparent 80%, #738598 60%); }
.line strong.level-3{
display: block;
position: relative;
padding-left: 16px;
}
.line strong.level-3:before{
content: '';
position: absolute;
width: 5px;
height: 100%;
top: 0;
left: 0;
}
.line strong.level-2{
display: block;
position: relative;
font-size: 200%;
line-height: 160%;
text-align: center;
margin: 12px auto 18px;
font-weight: 100;
}
.line strong.level-2:after{
position: absolute;
content: '';
width: 100px;
top: 0;
bottom: -0.5em;
left: 0;
right: 0;
margin: 0 auto;
}
body,
.editor{
color: #373942 !important; /* 全体のフォントカラーを変える */ }
cursor: pointer; font: 88%/1 monospace;
opacity: .35; /* ←マウスを乗せてないときの濃さ 35% */
transition: opacity .2s ease-out }
#__charCounter__ { z-index: 30; position: sticky; bottom: 0; text-align: right } /* ポップアップのスタイル */
z-index: 30; position: absolute; bottom: 2em; right: -1em;
border-radius: .25em; border: 1px solid #ddd; box-shadow: 0 0 8px 1px rgba(8,8,8,.1); padding: .8em; background-color: azure; color: #5F9EA0; font: 13.5px/1.4 monospace; transition: opacity .3s ease-out }
/* プレゼンモードのときは非表示にする */
.cursor-line {
background-color: rgba(0, 0, 0, .006);
box-shadow: inset 0 -5px 5px -5px rgba(0, 0, 0, .006);
}
.table-block table { background-color: transparent; border-collapse: separate; border-spacing: 2px }
.table-block table tr td:nth-child(odd) { padding: .1em; background-color: rgba(0,0,0,0.04) }
.table-block table tr:nth-child(even) td { background-color: rgba(0,0,0,0.06) } /* 偶数行を濃くする */
.table-block table tr:first-child td { font-weight: bolder; text-align: center } /* 1行目だけ太字&中央揃え */
.table-block table tr td:first-child { padding: 0; background-color: transparent; border-width: 0 }
.deco-\{, .deco-\} {
font-weight: bold;
padding: 1.2rem;
border-radius: .5rem;
margin: auto 1rem;
display: inline-block;
max-width: calc(100% - 100px); /* 長いセリフの折り返しをいい感じにする */
vertical-align: top;
}
/* 吹き出し内のリンク色 */
.deco-\{ a,
.deco-\} a {
}
/* スマホ画面 */
@media screen and (max-width: 768px) {
.deco-\{, .deco-\} {
padding: 1rem;
}
}
.deco-\{:before {
position: absolute;
margin: 0;
padding: 0;
transform: translateX(-200%) translateY(calc(1em - 0%));
width: 0;
content: "";
border-width: 0 0 .6em .6em;
border-style: solid;
}
.deco-\}:after {
position: absolute;
margin: 0;
padding: 0;
transform: translateX(100%) translateY(calc(2rem - 0%));
width: 0;
content: "";
border-width: 0 .6em .6em 0;
border-style: solid;
}
.navbar-brand::before {
content: '';
width: 40px;
height: 40px;
position: absolute;
background-size: cover;
border-radius: 4px;/*角丸はお好みで*/
}