Last active
December 13, 2022 07:58
-
-
Save sounisi5011/bd4a431e31e73ff7286fbd09a154f57a to your computer and use it in GitHub Desktop.
Show Unicode Code Point List
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function(global) { | |
'use strict'; | |
var ATTR = 'ref'; | |
/** | |
* @param {*} origCallback | |
* @param {!function} addCallback | |
* @return {!function} | |
*/ | |
function overridePropCallback(origCallback, addCallback) { | |
if (typeof origCallback === 'function') { | |
return function() { | |
var args = Array.prototype.slice.call(arguments); | |
origCallback.apply(null, args); | |
addCallback.apply(null, args); | |
}; | |
} else { | |
return addCallback; | |
} | |
} | |
/** | |
* Actionsの各関数の引数にgetRef関数を追加する | |
* @param {!Object} origActions | |
* @param {function} getRef | |
* @return {!Object} | |
*/ | |
function addActionsRefsParam(origActions, getRef) { | |
var newActions = {}; | |
for (var key in origActions) { | |
var origAction = origActions[key]; | |
if (typeof origAction === 'function') { | |
newActions[key] = (function(origAction) { | |
return function(data) { | |
var result = origAction(data); | |
if (typeof result === 'function') { | |
return function(actionInState, actionInActions) { | |
return result(actionInState, actionInActions, getRef); | |
}; | |
} | |
return result; | |
}; | |
})(origAction); | |
} else { | |
newActions[key] = addActionsRefsParam(origAction, getRef); | |
} | |
} | |
return newActions; | |
} | |
/** | |
* hyperappにref/refsを追加する | |
* @param {{h: !function, app: !function}} hyperapp | |
* @return {{h: !function, app: !function}} | |
*/ | |
function hyperappRefs(hyperapp) { | |
var origH = hyperapp.h; | |
var origApp = hyperapp.app; | |
return { | |
h: function h(name, attributes) { | |
var args = [ name ]; | |
/* | |
* 第二引数が配列ではないオブジェクトの場合に、属性として設定する | |
*/ | |
if (typeof attributes === 'object' && !Array.isArray(attributes)) { | |
args.push(attributes); | |
} else { | |
args.push({}, attributes); | |
} | |
/* | |
* 第三引数以降を設定する | |
*/ | |
for (var i = 2; i < arguments.length; i++) { | |
args.push(arguments[i]); | |
} | |
/* | |
* 元のh関数を実行する | |
*/ | |
return origH.apply(null, args); | |
}, | |
app: function app(state, actions, view, container) { | |
/** | |
* @type {Object<string, ?Element>} | |
*/ | |
var globalRefs = {}; | |
/** | |
* @param {string} refName | |
* @return {!Element|null} | |
*/ | |
function getRef(refName) { | |
return globalRefs[refName] || null; | |
} | |
/* | |
* Actionsの第三引数にgetRef関数を追加 | |
*/ | |
var overridedActions = addActionsRefsParam(actions, getRef); | |
var overrideView = function(viewInState, viewInActions) { | |
/* | |
* 元のview関数を実行し、仮想DOMを取得する | |
*/ | |
var node = view(viewInState, viewInActions, getRef); | |
/* | |
* 生成された仮想DOMのref属性を、oncreateとondestroyへ上書きする | |
*/ | |
(function defRefUpdater(node) { | |
if (node && typeof node === 'object') { | |
var attributes = node.attributes; | |
if (attributes) { | |
var refName = attributes[ATTR]; | |
if (refName && typeof refName === 'string') { | |
delete attributes[ATTR]; | |
attributes.oncreate = overridePropCallback(attributes.oncreate, function(elem) { | |
globalRefs[refName] = elem; | |
}); | |
attributes.ondestroy = overridePropCallback(attributes.ondestroy, function(elem) { | |
if (globalRefs[refName] === elem) { | |
globalRefs[refName] = null; | |
} | |
}); | |
} | |
} | |
node.children.forEach(defRefUpdater); | |
} | |
})(node); | |
/* | |
* 変更を加えた仮想DOMを返す | |
*/ | |
return node; | |
}; | |
/* | |
* 元のapp関数を実行する | |
*/ | |
return origApp(state, overridedActions, overrideView, container); | |
} | |
}; | |
} | |
global.hyperappRefs = hyperappRefs; | |
})(Function('return this')()); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang=en> | |
<meta charset=utf-8> | |
<meta name=viewport content="width=device-width,initial-scale=1"> | |
<meta name=format-detection content="telephone=no,email=no,address=no"> | |
<title>Show Unicode Code Point List</title> | |
<link rel=stylesheet href="main.css"> | |
<h1>Show Unicode Code Point List</h1> | |
<main id=main><p>JavaScript has been disabled. Please enable JavaScript.</main> | |
<footer> | |
<h2>Gist</h2> | |
<p><a href="https://gist.github.com/sounisi5011/bd4a431e31e73ff7286fbd09a154f57a">gist.github.com<wbr>/sounisi5011<wbr>/bd4a431e31e73ff7286fbd09a154f57a</a> | |
</footer> | |
<script src="https://polyfill.io/v3/polyfill.min.js?features=es5,String.prototype.padStart&flags=always,gated" crossorigin=anonymous></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/hyperapp/1.2.0/hyperapp.js" integrity="sha256-nGvSTSqpX5gJdp7Jv5+4FCU1IbclUByINJqDQIbfwNc=" crossorigin=anonymous></script> | |
<script src="https://unpkg.com/grapheme-splitter@1.0.4/index.js" integrity="sha256-ApHozg+22P2OAAiCbUVy12oWNZxACKDhlKOdnZHK7JU=" crossorigin=anonymous></script> | |
<script src="hyperappRefs.js"></script> | |
<script src="main.js"></script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.input-area, | |
.check-button { | |
width: 100%; | |
} | |
.input-area { | |
min-height: 5em; | |
resize: vertical; | |
} | |
.table-wrap { | |
overflow-x: scroll; | |
} | |
.table-wrap table { | |
text-align: center; | |
} | |
.table-wrap .full-string { | |
text-align: left; | |
} | |
.table-wrap tr + .full-string td { | |
padding-top: 3em; | |
} | |
.char { | |
padding: .1em .4em; | |
border: solid 1px #ccc; | |
border-radius: 3px; | |
white-space: nowrap; | |
} | |
.char[data-char-type~="ascii-printable"] { | |
background-color: #dff7ff; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function(global) { | |
'use strict'; | |
var document = global.document; | |
var location = global.location; | |
var hyperapp = global.hyperappRefs(global.hyperapp); | |
var h = hyperapp.h; | |
var app = hyperapp.app; | |
var splitter = new global.GraphemeSplitter(); | |
/** | |
* @param {number} code | |
* @return {string} | |
*/ | |
function unicodeCode2str(code) { | |
return 'U+' + code.toString(16).toUpperCase().padStart(4, '0'); | |
} | |
/** | |
* @param {Array<string>} graphemes | |
* @return {Array<Array<string>>} | |
* @see https://en.wikipedia.org/wiki/Newline#Unicode | |
*/ | |
function graphemes2LineBreakList(graphemes) { | |
/** @type {Array<Array<string>>} */ | |
var out = []; | |
/** @type {number} */ | |
var start = 0; | |
for (var index = 0; index < graphemes.length; index++) { | |
/** @type {string} */ | |
var graphemeChar = graphemes[index]; | |
/** @type {number} */ | |
var nextIndex = index + 1; | |
if (nextIndex === graphemes.length || /^(?:\u000D\u000A|[\u000A\u000B\u000C\u000D\u0085\u2028\u2029])$/.test(graphemeChar)) { | |
out.push(graphemes.slice(start, nextIndex)); | |
start = nextIndex; | |
} | |
} | |
return out; | |
} | |
/** | |
* @param {string} str | |
* @return {{code: Array<number>, char: Array<string>, data: Array<{code: number, char: string}>}} | |
* @see https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/codePointAt#%E4%BA%92%E6%8F%9B%E6%80%A7 | |
*/ | |
function getCodePointList(str) { | |
/** @type {Array<string>} */ | |
var charList = str.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]?|[^\uD800-\uDFFF]|./g) || []; | |
/** @type {Array<{code: number, char: string}>} */ | |
var dataList = charList.map(function(/** @type {string} */ char) { | |
return { | |
/** @type {number} */ | |
code: ( | |
(char.length === 2) ? | |
((char.charCodeAt(0) - 0xD800) * 0x400 + char.charCodeAt(1) - 0xDC00 + 0x10000) : | |
char.charCodeAt(0) | |
), | |
/** @type {string} */ | |
char: char | |
}; | |
}); | |
return { | |
/** @type {Array<number>} */ | |
code: dataList.map(function(/** @type {{code: number, char: string}} */ data) { | |
return data.code; | |
}), | |
/** @type {Array<string>} */ | |
char: charList, | |
/** @type {Array<{code: number, char: string}>} */ | |
data: dataList | |
}; | |
} | |
/** | |
* @param {string} char | |
* @return {Array<string>} | |
*/ | |
function getCharTypeValue(char) { | |
var out = []; | |
if (/^[\u0020-\u007E]$/.test(char)) { | |
out.push('ascii-printable'); | |
} | |
return out; | |
} | |
/** | |
* @param {string} [fragment=location.hash] | |
* @return {string} | |
*/ | |
function getUrlFragmentValue(fragment) { | |
if (typeof fragment !== 'string') { | |
fragment = location.hash; | |
} | |
if (fragment.charAt(0) === '#') { | |
fragment = fragment.substr(1); | |
} | |
return decodeURIComponent(fragment); | |
} | |
/** | |
* @param {string} value | |
* @param {boolean} [force=false] | |
*/ | |
function setUrlFragmentValue(value, force) { | |
if (force || getUrlFragmentValue() !== value) { | |
location.hash = '#' + ( | |
global.encodeURIComponent(value) | |
.replace(/[!\'()*]/g, function(char) { | |
return '%' + char.charCodeAt(0).toString(16).toUpperCase(); | |
}) | |
); | |
} | |
} | |
/** | |
* @param {string} value | |
*/ | |
var updateTitleValue = (function(defaultTitle) { | |
return function(value) { | |
document.title = ( | |
value !== '' ? | |
(defaultTitle + ' #' + value) : | |
defaultTitle | |
); | |
}; | |
})(document.title); | |
var state = { | |
/** @type {string} */ | |
value: '' | |
}; | |
var actions = { | |
updateValue: function(value) { | |
return function(state) { | |
if (state.value !== value) { | |
return { value: value }; | |
} | |
}; | |
}, | |
inputChange: function(inputValue) { | |
return function(state, actions) { | |
setUrlFragmentValue(inputValue); | |
actions.updateValue(inputValue); | |
}; | |
}, | |
fragmentChange: function(fragmentValue) { | |
return function(state, actions, refs) { | |
var value = getUrlFragmentValue(fragmentValue); | |
var inputElem = refs('input'); | |
if (inputElem && inputElem.value !== value) { | |
inputElem.value = value; | |
} | |
updateTitleValue(value); | |
actions.updateValue(value); | |
}; | |
} | |
}; | |
var view = function(state, actions, refs) { | |
var inputListener = function(e) { | |
return actions.inputChange(e.target.value); | |
}; | |
/** @type {Array<string>} */ | |
var graphemes = splitter.splitGraphemes(state.value); | |
/** @type {Array<Array<string>>} */ | |
var graphemesLineList = graphemes2LineBreakList(graphemes); | |
return h('div', [ | |
h('textarea', { | |
class: 'input-area', | |
oninput: inputListener, | |
onchange: inputListener, | |
onblur: inputListener, | |
onkeyup: inputListener, | |
onmouseup: inputListener, | |
onpaste: inputListener, | |
oncreate: function(elem) { | |
elem.value = state.value; | |
}, | |
ref: 'input' | |
}), | |
h('br'), | |
h('input', { | |
type: 'button', | |
value: 'Check!', | |
class: 'check-button', | |
onclick: function(e) { | |
if (refs('input')) { | |
actions.inputChange(refs('input').value); | |
} | |
} | |
}), | |
h('div', { class: 'table-wrap' }, [ | |
h('table', [ | |
graphemesLineList.map(function(/** @type {Array<string>} */ graphemes) { | |
/** @type {string} */ | |
var fullStr = graphemes.join(''); | |
/** @type {Array<{code: number, char: string, graphemeCharLength: number}>} */ | |
var charCodeList = graphemes.reduce(function(array, /** @type {string} */ graphemeChar) { | |
/** @type {Array<{code: number, char: string}>} */ | |
var dataList = getCodePointList(graphemeChar).data; | |
/** @type {number} */ | |
var graphemeCharLength = dataList.length; | |
return array.concat(dataList.map(function(/** @type {{code: number, char: string}} */ data) { | |
data.graphemeCharLength = graphemeCharLength; | |
return data; | |
})); | |
}, []); | |
return ( | |
(0 < graphemes.length) ? | |
[ | |
h('tr', { class: 'full-string' }, [ | |
h('td', { colSpan: fullStr.length }, [ | |
h('span', { class: 'char' }, fullStr) | |
]) | |
]), | |
h('tr', graphemes.map(function(/** @type {string} */ graphemeChar) { | |
return h('td', { colSpan: graphemeChar.length }, [ | |
h('span', { class: 'char', 'data-char-type': getCharTypeValue(graphemeChar).join(' ') }, graphemeChar) | |
]); | |
})), | |
h('tr', charCodeList.map(function(/** @type {{code: number, char: string, graphemeCharLength: number}} */ data) { | |
/** @type {string} */ | |
var char = data.char; | |
/** @type {number} */ | |
var graphemeCharLength = data.graphemeCharLength; | |
return h('td', { colSpan: char.length }, ( | |
(1 < graphemeCharLength) ? | |
h('span', { class: 'char', 'data-char-type': getCharTypeValue(char).join(' ') }, char) : | |
null | |
)); | |
})), | |
h('tr', charCodeList.map(function(/** @type {{code: number, char: string, graphemeCharLength: number}} */ data) { | |
/** @type {string} */ | |
var char = data.char; | |
/** @type {number} */ | |
var code = data.code; | |
return h('td', { colSpan: char.length }, [ | |
h('span', { class: 'char', 'data-char-type': getCharTypeValue(char).join(' ') }, unicodeCode2str(code)) | |
]); | |
})) | |
] : | |
null | |
); | |
}) | |
]) | |
]) | |
]); | |
}; | |
var main = app(state, actions, view, document.getElementById('main')); | |
global.onhashchange = function() { | |
main.fragmentChange(location.hash); | |
}; | |
main.fragmentChange(location.hash); | |
})(Function('return this')()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment