msawady
https://pbs.twimg.com/profile_images/1146347219869507585/zFtTJc0t_400x400.jpg
code:style.css
/* カーソルのある行の背景色と下線を薄いグレーにする */
.cursor-line {
background-color: rgba(0, 0, 0, .02);
box-shadow: inset 0 -5px 5px -5px rgba(0, 0, 0, .3);
}
code:button.js
function main(){var m=function(b){b=b.replace(/&/g,"&");b=b.replace(/</g,"<");b=b.replace(/>/g,">");b=b.replace(/"/g,""");return b=b.replace(/'/g,"'")},n=function(b){b=void 0===b?0:b;for(var a="",g=1;g<b;g++)a+=" ";return a},p=function(b){b=void 0===b?"":b;var a=document.createElement("div");a.innerHTML=b;b=a.querySelectorAll("strong");for(var g=0;g<b.length;g++){for(var c=bg,e=+c.className.split("level-")1,d=c.innerHTML,e=void 0===e?1:e,e=6-e,f="",h=0;h<e;h++)f+="#";c.innerHTML= f+" "+d}return a.innerHTML},q=function(b){b=void 0===b?"":b;var a=document.createElement("div");a.innerHTML=b;b=a.querySelectorAll("a");for(var g=0;g<b.length;g++){var c=bg,e=c.innerText.trim(),d=c.href,e=""+e+"("+d+")",f=c.querySelector("img");null!==f&&(e="[!Image("+f.src+")]("+d+")");c.innerText=e}return a.innerText},f=document.querySelector(".lines"),r=f.querySelector(".line-title .text").innerText,f=f.querySelectorAll(".line");var pageTexts=[];for(var l=1;l<f.length;l++){for(var d=fl.querySelector(".text").cloneNode(!0), h=d.querySelectorAll("span.empty-char-index"),a=0;a<h.length;a++){var k=ha;k.innerText=""}h=d.querySelectorAll("span.backquote");for(a=0;a<h.length;a++)k=ha,k.innerText="`";a=d.innerHTML.replace(/<span>/g,"");a=a.replace(/<span.+?>/g,"").replace(/<\/span>/g,"");a=a.replace(/<br.+?>/g,"");a=a.replace(/\n/gi,"").replace(/\t/gi,"").trim();a=p(a);a=q(a);d=d.querySelector(".indent-mark");null!==d&&(k=+d.style.width.split("em")0/1.5,a=n(k)+"- "+a);null===d&&0<a.length&&"#"!==a0&&(a+="<br>");pageTexts.push(a)}(function(b, a){b=void 0===b?"Title":b;a=void 0===a?[]:a;for(var d="# "+b+"\n",c=0;c<a.length;c++)d+="\n"+ac;print(m(d))} )(r,pageTexts)}; const print = body => {
const url = URL.createObjectURL(new Blob(bom, body, { type: "text/plain",
}))
window.open(url)
}
main()
code:script.js
scrapbox.PopupMenu.addButton({
title: "TimeTag",
onClick: (text) => {
var m = text.match(/((\d\d)?(\d\d))-?(\d\d)-?(\d\d)/);
return ${year}-${month}-${day} #${year} #${month}-${day} #${year}-${month};
}
})
code:script.js
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,
});
})