참고: 설정을 저장한 후에 바뀐 점을 확인하기 위해서는 브라우저의 캐시를 새로 고쳐야 합니다.
- 파이어폭스 / 사파리: Shift 키를 누르면서 새로 고침을 클릭하거나, Ctrl-F5 또는 Ctrl-R을 입력 (Mac에서는 ⌘-R)
- 구글 크롬: Ctrl-Shift-R키를 입력 (Mac에서는 ⌘-Shift-R)
- 인터넷 익스플로러 / 엣지: Ctrl 키를 누르면서 새로 고침을 클릭하거나, Ctrl-F5를 입력.
- 오페라: Ctrl-F5를 입력.
/* =========================================
FrameGeometry
대문 프레임 공통 canvas 픽셀 렌더러
========================================= */
(function (w, document) {
'use strict';
var FRAME = {
BORDER:1,
BEVEL:1,
SLANT_RISE:2,
SLANT_RUN:1,
BUTTON_FACE:20,
FRAME_FACE:7,
GAP:2,
COLORS:{
border:'#050505',
surface:'#1d1d1d',
well:'#080808',
highlight:'#555555',
shadow:'#101010',
label:'#e2e2e2'
}
};
var DIMENSIONS = {
FRAME_SURFACE_TOP:1,
FRAME_SURFACE_BOTTOM:8,
WELL_TOP:8,
WELL_BOTTOM:10,
BUTTON_EDGE_TOP:10,
BUTTON_SURFACE_TOP:11,
BUTTON_SURFACE_BOTTOM:31,
BUTTON_EDGE_BOTTOM:32,
WELL2_TOP:32,
WELL2_BOTTOM:34,
FRAME2_SURFACE_TOP:34,
FRAME2_SURFACE_BOTTOM:41,
ROW_HEIGHT:42,
OVERLAP:9,
BUTTON_TOTAL:22,
BOTTOM_START_ABS_Y:33
};
var COL = {
B:[5,5,5],
S:[29,29,29],
H:[85,85,85],
D:[16,16,16],
BG:[8,8,8],
T:[226,226,226],
TW:[255,255,255]
};
var seamListeners = [];
var scaleListeners = [];
var shellScale = 1;
DIMENSIONS.TOP_FRAME = DIMENSIONS.FRAME_SURFACE_BOTTOM;
DIMENSIONS.BUTTON_TOP = DIMENSIONS.BUTTON_EDGE_TOP;
DIMENSIONS.BUTTON_BOTTOM = DIMENSIONS.BUTTON_EDGE_BOTTOM;
DIMENSIONS.BOTTOM_FRAME = DIMENSIONS.ROW_HEIGHT - DIMENSIONS.FRAME2_SURFACE_TOP;
function integer(value) {
return Math.round(Number(value) || 0);
}
function snapWidth(raw) {
return Math.max(0, Math.floor(Number(raw) || 0));
}
function readWidth(node) {
if (!node) return 0;
return snapWidth(node.clientWidth || node.offsetWidth || 0);
}
function readCssShellScale() {
var raw = '';
var value;
try {
raw = w.getComputedStyle(document.documentElement)
.getPropertyValue('--clbi-shell-scale');
} catch (error) {}
value = parseFloat(raw);
return isFinite(value) && value > 0 ? value : 1;
}
function getShellScale() {
var current = readCssShellScale();
if (Math.abs(current - shellScale) > 0.0001) shellScale = current;
return shellScale;
}
function setShellScale(value) {
var next = Number(value);
var previous = shellScale;
if (!isFinite(next) || next <= 0) next = 1;
shellScale = next;
document.documentElement.style.setProperty(
'--portal-frame-counter-scale',
String(1 / shellScale)
);
if (Math.abs(previous - shellScale) <= 0.0001) return shellScale;
scaleListeners.slice().forEach(function (listener) {
try { listener(shellScale, previous); } catch (error) {}
});
try {
document.dispatchEvent(new CustomEvent('framegeometryscale', {
detail:{ scale:shellScale, previous:previous }
}));
} catch (error) {}
return shellScale;
}
function onScale(listener) {
if (typeof listener !== 'function') return function () {};
scaleListeners.push(listener);
return function () {
var index = scaleListeners.indexOf(listener);
if (index !== -1) scaleListeners.splice(index, 1);
};
}
function readRenderWidth(node) {
var layoutWidth = readWidth(node);
if (!layoutWidth) return 0;
return Math.max(1, Math.round(layoutWidth * getShellScale()));
}
function applyCounterScale(hosts, inner, logicalHeight, overlap) {
var scale = getShellScale();
var inverse = 1 / scale;
var hostHeight = logicalHeight / scale;
var hostList = Array.isArray(hosts) ? hosts : [hosts];
hostList.forEach(function (host) {
if (!host || !host.style) return;
host.classList.add('portal-frame-counter-host');
host.style.setProperty('height', hostHeight + 'px', 'important');
host.style.setProperty('min-height', hostHeight + 'px', 'important');
host.style.setProperty('max-height', hostHeight + 'px', 'important');
host.style.setProperty('flex-basis', hostHeight + 'px', 'important');
});
if (hosts && !Array.isArray(hosts) && typeof overlap === 'number') {
hosts.style.setProperty('margin-top', (-overlap / scale) + 'px', 'important');
} else if (Array.isArray(hosts) && hosts[0] && typeof overlap === 'number') {
hosts[0].style.setProperty('margin-top', (-overlap / scale) + 'px', 'important');
}
if (inner && inner.style) {
inner.classList.add('portal-frame-counter-inner');
inner.style.setProperty('height', logicalHeight + 'px');
inner.style.setProperty('transform-origin', 'top left');
inner.style.setProperty('transform', 'scale(' + inverse + ')');
inner.setAttribute('data-frame-shell-scale', String(scale));
}
return { scale:scale, inverse:inverse, hostHeight:hostHeight };
}
/*
* 검증본의 버튼 마스크. phase는 2단 접합의 홀짝 위상만 더한다.
* slant: right | left | both
*/
function buttonMask(BTN_W, ys, slant, phase) {
var step = Math.floor((ys + (phase || 0)) / 2);
var left;
var right;
if (slant === 'right') {
left = 0;
right = BTN_W - step;
} else if (slant === 'left') {
left = step;
right = BTN_W;
} else {
left = step;
right = BTN_W - step;
}
return { left:left, right:right };
}
function buildButtonPixels(BTN_W, SURFACE_H, slant, phase) {
var H = 1 + SURFACE_H + 1;
var W = BTN_W + 2;
function io(x, y) {
if (y < 0 || y >= H) return false;
var ys = y - 1;
var ysc = ys < 0 ? 0 : (ys >= SURFACE_H ? SURFACE_H - 1 : ys);
var m = buttonMask(BTN_W, ysc, slant, phase || 0);
return x >= m.left && x <= m.right;
}
var g = [];
var src;
var y;
var x;
for (y = 0; y < H; y++) {
g[y] = [];
for (x = 0; x < W; x++) {
if (!io(x, y)) {
g[y][x] = 'BG';
continue;
}
var edge = !(io(x, y - 1) && io(x, y + 1) && io(x - 1, y) && io(x + 1, y));
g[y][x] = edge ? 'B' : 'S';
}
}
src = g.map(function (row) { return row.slice(); });
for (y = 0; y < H; y++) {
for (x = 0; x < W; x++) {
if (src[y][x] !== 'S') continue;
var tb = y > 0 && src[y - 1][x] === 'B';
var rb = x < W - 1 && src[y][x + 1] === 'B';
var bb = y < H - 1 && src[y + 1][x] === 'B';
var lb = x > 0 && src[y][x - 1] === 'B';
if (tb || rb) g[y][x] = 'H';
else if (bb || lb) g[y][x] = 'D';
}
}
return g;
}
function buildReverseButtonPixels(BTN_W, SURFACE_H, slant, phase) {
var H = 1 + SURFACE_H + 1;
var W = BTN_W + 2;
function io(x, y) {
if (y < 0 || y >= H) return false;
var ys = y - 1;
var ysc = ys < 0 ? 0 : (ys >= SURFACE_H ? SURFACE_H - 1 : ys);
var reversed = SURFACE_H - 1 - ysc;
var m = buttonMask(BTN_W, reversed, slant, phase || 0);
return x >= m.left && x <= m.right;
}
var g = [];
var src;
var y;
var x;
for (y = 0; y < H; y++) {
g[y] = [];
for (x = 0; x < W; x++) {
if (!io(x, y)) {
g[y][x] = 'BG';
continue;
}
var edge = !(io(x, y - 1) && io(x, y + 1) && io(x - 1, y) && io(x + 1, y));
g[y][x] = edge ? 'B' : 'S';
}
}
src = g.map(function (row) { return row.slice(); });
for (y = 0; y < H; y++) {
for (x = 0; x < W; x++) {
if (src[y][x] !== 'S') continue;
var tb = y > 0 && src[y - 1][x] === 'B';
var rb = x < W - 1 && src[y][x + 1] === 'B';
var bb = y < H - 1 && src[y + 1][x] === 'B';
var lb = x > 0 && src[y][x - 1] === 'B';
if (tb || rb) g[y][x] = 'H';
else if (bb || lb) g[y][x] = 'D';
}
}
return g;
}
function createImage(ctx, width, height, fillCode) {
var image = ctx.createImageData(width, height);
var color = COL[fillCode || 'BG'];
var i;
for (i = 0; i < image.data.length; i += 4) {
image.data[i] = color[0];
image.data[i + 1] = color[1];
image.data[i + 2] = color[2];
image.data[i + 3] = 255;
}
return image;
}
function setPixel(image, width, height, x, y, code) {
var color;
var index;
x = integer(x);
y = integer(y);
if (x < 0 || x >= width || y < 0 || y >= height) return;
color = COL[code] || COL.BG;
index = (y * width + x) * 4;
image.data[index] = color[0];
image.data[index + 1] = color[1];
image.data[index + 2] = color[2];
image.data[index + 3] = 255;
}
function fillRect(image, width, height, x, y, rectWidth, rectHeight, code) {
var endX = Math.min(width, integer(x + rectWidth));
var endY = Math.min(height, integer(y + rectHeight));
var yy;
var xx;
x = Math.max(0, integer(x));
y = Math.max(0, integer(y));
for (yy = y; yy < endY; yy++) {
for (xx = x; xx < endX; xx++) setPixel(image, width, height, xx, yy, code);
}
}
function paintGrid(image, width, height, grid, offsetX, offsetY, skipBackground) {
var y;
var x;
offsetX = integer(offsetX);
offsetY = integer(offsetY);
for (y = 0; y < grid.length; y++) {
for (x = 0; x < grid[y].length; x++) {
if (skipBackground && grid[y][x] === 'BG') continue;
setPixel(image, width, height, offsetX + x, offsetY + y, grid[y][x]);
}
}
}
function paintRaisedRect(image, width, height, x, y, rectWidth, rectHeight) {
var right = x + rectWidth - 1;
var bottom = y + rectHeight - 1;
var i;
fillRect(image, width, height, x, y, rectWidth, rectHeight, 'B');
fillRect(image, width, height, x + 1, y + 1, rectWidth - 2, rectHeight - 2, 'S');
for (i = x + 1; i <= right - 1; i++) setPixel(image, width, height, i, bottom - 1, 'D');
for (i = y + 1; i <= bottom - 1; i++) setPixel(image, width, height, x + 1, i, 'D');
for (i = x + 1; i <= right - 1; i++) setPixel(image, width, height, i, y + 1, 'H');
for (i = y + 1; i <= bottom - 1; i++) setPixel(image, width, height, right - 1, i, 'H');
}
function paintStandardFrame(image, width, height) {
paintRaisedRect(image, width, height, 0, 0, width, height);
fillRect(
image,
width,
height,
DIMENSIONS.WELL_TOP,
DIMENSIONS.WELL_TOP,
Math.max(0, width - DIMENSIONS.WELL_TOP * 2),
DIMENSIONS.WELL2_BOTTOM - DIMENSIONS.WELL_TOP,
'BG'
);
}
function createCanvas(className, width, height) {
var canvas = document.createElement('canvas');
canvas.className = className || 'portal-frame-canvas';
canvas.width = Math.max(1, integer(width));
canvas.height = Math.max(1, integer(height));
canvas.style.width = canvas.width + 'px';
canvas.style.height = canvas.height + 'px';
canvas.setAttribute('aria-hidden', 'true');
return canvas;
}
function drawLabel(ctx, text, x, y, hover, fontSize) {
var family = 'Galmuri11, sans-serif';
try {
family = w.getComputedStyle(ctx.canvas).fontFamily || family;
} catch (error) {}
ctx.save();
ctx.font = '700 ' + integer(fontSize || 12) + 'px ' + family;
ctx.fillStyle = hover ? FRAME.COLORS.label.replace('e2e2e2', 'ffffff') : FRAME.COLORS.label;
if (hover) ctx.fillStyle = '#ffffff';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(String(text || ''), integer(x), integer(y));
ctx.restore();
}
function setSeams(next) {
var frame = w.CLBI.frame;
var copy = {};
Object.keys(next || {}).forEach(function (key) { copy[key] = next[key]; });
copy.revision = integer((frame.seams && frame.seams.revision) || 0) + 1;
frame.seams = copy;
seamListeners.slice().forEach(function (listener) {
try { listener(copy); } catch (error) {}
});
try {
document.dispatchEvent(new CustomEvent('framegeometryseams', { detail:copy }));
} catch (error) {}
if (w.mw && w.mw.hook) w.mw.hook('frame.geometry.seams').fire(copy);
return copy;
}
function onSeams(listener) {
if (typeof listener !== 'function') return function () {};
seamListeners.push(listener);
if (w.CLBI.frame.seams && w.CLBI.frame.seams.width) listener(w.CLBI.frame.seams);
return function () {
var index = seamListeners.indexOf(listener);
if (index !== -1) seamListeners.splice(index, 1);
};
}
w.CLBI = w.CLBI || {};
w.CLBI.frame = {
FRAME:FRAME,
DIMENSIONS:DIMENSIONS,
COL:COL,
integer:integer,
snapWidth:snapWidth,
readWidth:readWidth,
readRenderWidth:readRenderWidth,
getShellScale:getShellScale,
setShellScale:setShellScale,
onScale:onScale,
applyCounterScale:applyCounterScale,
buttonMask:buttonMask,
buildButtonPixels:buildButtonPixels,
buildReverseButtonPixels:buildReverseButtonPixels,
createImage:createImage,
setPixel:setPixel,
fillRect:fillRect,
paintGrid:paintGrid,
paintRaisedRect:paintRaisedRect,
paintStandardFrame:paintStandardFrame,
createCanvas:createCanvas,
drawLabel:drawLabel,
setSeams:setSeams,
onSeams:onSeams,
seams:{}
};
setShellScale(readCssShellScale());
}(window, document));