radix-library
Version:
CSS and Javascript library to initialize pages.
911 lines (901 loc) • 66.1 kB
JavaScript
/************************************
RADIX
- Version : 4.1.8
Copyright 2021 shoalwave and other contributors.
Released under the MIT License.
https://radix.shoalwave.net/LICENSE
************************************/
let DOMLoaded = false;
let windowLoaded = false;
window.addEventListener('DOMContentLoaded', () => { DOMLoaded = true });
window.addEventListener('load', () => { windowLoaded = true });
/**
* 本体クラス
* @typedef radix
* @type class
* @property {object} option オプション値を格納
* @property {object} modalParts モーダルウィンドウ機能で生成しているエレメントノードたち
* @property {object} events 各操作に対して発火しているイベントフックたち
* @property {object} icons SVGアイコンの全ノード
* @property {boolean} isMobile モバイル端末(タッチ操作可能)か否か
* @property {boolean} navState ナビゲーションの開閉状態
* @property {boolean} modalState モーダルウィンドウの開閉状態
* @property {boolean} initialized init() 関数の実行が完了したか
* @property {function} init radixの初期化を行う関数
* @property {function} toggleNav ナビゲーション開閉を実行する関数
* @property {function} smoothScroll スムーススクロールを実行する関数
* @property {function} replaceIcon SVGアイコンにエレメントを置換する関数
* @property {function} dragDown ドラッグスクロールのマウスダウン
* @property {function} dragMove ドラッグスクロールのマウスムーブ
* @property {function} dragUp ドラッグスクロールのマウスアップ
* @property {function} modalOpen モーダルウィンドウを開く関数
* @property {function} modalClose モーダルウィンドウを閉じる関数
* @property {function} modalResize モーダルウィンドウの拡縮をする関数
* @property {function} floatRound 浮動小数点数の文字列変換関数
* @property {function} floatCeil 浮動小数点数の文字列変換関数
* @property {function} floatFloor 浮動小数点数の文字列変換関数
* @property {function} preventScroll ページ全体の縦方向スクロール禁止を操作する関数
* @property {function} getEasing イージング関数を取得する関数
*/
class Radix {
/**
* コンストラクタ
* @constructor
*/
constructor(option, mode) {
this.version = '4.1.8'
const defOption = {
timeFrame: 10,
preload: {
active: false,
selector: '',
class: 'hide',
delay: 200,
preventScroll: false
},
smoothScroll: {
active: true,
duration: 600,
easing: 'easeInOutExpo',
header: '',
},
fixExtLink: {
active: true
},
toggleNav: {
active: false,
trigger: '',
target: '',
class: 'opened',
preventScroll: false
},
icons: {
active: true,
selector: '.radix-icon'
},
dragScroll: {
active: true,
selector: '.radix-drag',
hint: true
},
flexFix: {
active: true,
selector: '.radix-flexfix',
inherit: true,
min: 0
},
modal: {
active: true,
selector: '.radix-modal',
class: 'white',
resizeDuration: 300,
resizeEasing: 'easeInOutBack',
scaleStep: [0.2, 0.4, 0.6, 0.8, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5],
fit: true,
drag: true,
magnify: true,
wrap: false
},
scrollAppear: {
active: true,
selector: '.radix-appear',
delay: 200,
reset: true,
class: 'active'
}
}
const argFlg = this.typeJudge(option) == 'object' ? true : false;
Object.keys(defOption).forEach(key => {
if (mode === false && key != 'timeFrame') {
defOption[key].active = false;
}
if (argFlg) {
Object.assign(defOption[key], option[key]);
}
});
this.option = defOption;
// インスタンス変数
this.navState = false;
this.modalState = false;
this.initialized = false;
this.isMobile = typeof window.ontouchstart === "undefined" ? false : true;
this.DOM_ROOTS = document.querySelectorAll('html,body');
this.events = {
beforeInitialize: new CustomEvent('_radixInit'),
afterInitialize: new CustomEvent('radixInit_'),
beforeScroll: new CustomEvent('_radixScroll'),
afterScroll: new CustomEvent('radixScroll_'),
beforeNavOpen: new CustomEvent('_radixNavOpen'),
afterNavOpen: new CustomEvent('radixNavOpen_'),
beforeNavClose: new CustomEvent('_radixNavClose'),
afterNavClose: new CustomEvent('radixNavClose_'),
beforeModalOpen: new CustomEvent('_radixModalOpen'),
afterModalOpen: new CustomEvent('radixModalOpen_'),
beforeModalClose: new CustomEvent('_radixModalClose'),
afterModalClose: new CustomEvent('radixModalClose_'),
}
}
/**
* DOMロード時に実行し初期化する関数
*/
init() {
const self = this;
new Promise(resolve => {
// Speed 0
document.dispatchEvent(self.events.beforeInitialize);
if (DOMLoaded) {
resolve();
} else {
window.addEventListener('DOMContentLoaded', resolve);
}
}).then(() => {
// Speed 1
return new Promise(resolve => {
// preload display
if (self.option.preload.active) {
if (self.option.preload.preventScroll) self.preventScroll(true);
let preloader = self.option.preload.selector.length > 0 ? document.querySelectorAll(self.option.preload.selector) : [];
window.addEventListener('load', () => {
if (self.option.preload.active && preloader && self.initialized) {
setTimeout(() => {
if (self.option.preload.preventScroll) self.preventScroll(false);
preloader.forEach(pl => {
pl.classList.add(self.option.preload.class);
});
}, self.option.preload.delay);
}
});
document.addEventListener('radixInit_', () => {
self.initialized = true;
if (self.option.preload.active && preloader.length > 0 && windowLoaded) {
setTimeout(() => {
if (self.option.preload.preventScroll) self.preventScroll(false);
preloader.forEach(pl => {
pl.classList.add(self.option.preload.class);
});
}, self.option.preload.delay);
}
});
}
// SVG insert
if (self.option.icons.active) {
const iconSources = {
cross: {
viewbox: '0 0 100 100',
path: 'M 2 17 L 17 2 L 50 35 L 83 2 L 98 17 L 65 50 L 98 83 L 83 98 L 50 65 L 17 98 L 2 83 L 35 50 L 2 17 Z',
fill: true,
stroke: 0,
linejoin: 'miter',
linecap: 'butt'
},
angle_top: {
viewbox: '0 0 100 100',
path: 'M 50 19 L 97 66 L 84 79 L 50 45 L 16 79 L 3 66 L 50 19 Z',
fill: true,
stroke: 0
},
angle_right: {
viewbox: '0 0 100 100',
path: 'M 81 50 L 34 97 L 21 84 L 55 50 L 21 16 L 34 3 L 81 50 Z',
fill: true,
stroke: 0
},
angle_bottom: {
viewbox: '0 0 100 100',
path: 'M 50 81 L 97 34 L 84 21 L 50 55 L 16 21 L 3 34 L 50 81 Z',
fill: true,
stroke: 0
},
angle_left: {
viewbox: '0 0 100 100',
path: 'M 19 50 L 66 3 L 79 16 L 45 50 L 79 84 L 66 97 L 19 50 Z',
fill: true,
stroke: 0
},
arrow_lr: {
viewbox: '0 0 100 100',
path: 'M 10 50 L 35 20 L 35 42 L 65 42 L 65 20 L 90 50 L 65 80 L 65 58 L 35 58 L 35 80 L 10 50 Z',
fill: true,
stroke: 0
},
arrow_tb: {
viewbox: '0 0 100 100',
path: 'M 50 10 L 80 35 L 58 35 L 58 65 L 80 65 L 50 90 L 20 65 L 42 65 L 42 35 L 20 35 L 50 10 Z',
fill: true,
stroke: 0
},
magnifying_glass: {
viewbox: '0 0 500 500',
path: 'M 120 195 C 120 92.896 202.896 10 305 10 C 407.104 10 490 92.896 490 195 C 490 297.104 407.104 380 305 380 C 202.896 380 120 297.104 120 195 Z M 180 195 C 179.99 186.52 180.85 178.06 182.55 169.75 C 184.18 161.76 186.61 153.94 189.8 146.42 C 196.1 131.55 205.21 118.05 216.64 106.64 C 228.05 95.21 241.55 86.1 256.42 79.8 C 263.94 76.61 271.76 74.18 279.76 72.54 C 288.07 70.85 296.52 69.99 305 70 C 313.48 69.99 321.93 70.85 330.24 72.54 C 338.24 74.18 346.06 76.61 353.58 79.8 C 368.45 86.1 381.95 95.21 393.36 106.64 C 404.79 118.05 413.9 131.55 420.2 146.42 C 423.39 153.94 425.82 161.76 427.46 169.76 C 429.15 178.07 430.01 186.52 430 195 C 430.01 203.48 429.15 211.93 427.46 220.24 C 425.82 228.24 423.39 236.06 420.2 243.58 C 413.9 258.45 404.79 271.95 393.36 283.36 C 381.95 294.79 368.45 303.9 353.58 310.2 C 346.06 313.39 338.24 315.82 330.24 317.46 C 321.93 319.15 313.48 320.01 305 320 C 296.52 320.01 288.07 319.15 279.76 317.46 C 271.76 315.82 263.94 313.39 256.42 310.2 C 241.55 303.9 228.05 294.79 216.64 283.36 C 205.21 271.95 196.1 258.45 189.8 243.58 C 186.61 236.06 184.18 228.24 182.54 220.24 C 180.85 211.93 179.99 203.48 180 195 Z M 13 490 L 13 413.148 L 126.148 300 L 203 376.852 L 89.852 490 L 13 490 Z',
fill: true,
fillRule: "evenodd",
stroke: 0
},
zoom_in: {
viewbox: '0 0 500 500',
path: 'M 120 195 C 120 92.896 202.896 10 305 10 C 407.104 10 490 92.896 490 195 C 490 297.104 407.104 380 305 380 C 202.896 380 120 297.104 120 195 Z M 180 195 C 179.99 186.52 180.85 178.06 182.55 169.75 C 184.18 161.76 186.61 153.94 189.8 146.42 C 196.1 131.55 205.21 118.05 216.64 106.64 C 228.05 95.21 241.55 86.1 256.42 79.8 C 263.94 76.61 271.76 74.18 279.76 72.54 C 288.07 70.85 296.52 69.99 305 70 C 313.48 69.99 321.93 70.85 330.24 72.54 C 338.24 74.18 346.06 76.61 353.58 79.8 C 368.45 86.1 381.95 95.21 393.36 106.64 C 404.79 118.05 413.9 131.55 420.2 146.42 C 423.39 153.94 425.82 161.76 427.46 169.76 C 429.15 178.07 430.01 186.52 430 195 C 430.01 203.48 429.15 211.93 427.46 220.24 C 425.82 228.24 423.39 236.06 420.2 243.58 C 413.9 258.45 404.79 271.95 393.36 283.36 C 381.95 294.79 368.45 303.9 353.58 310.2 C 346.06 313.39 338.24 315.82 330.24 317.46 C 321.93 319.15 313.48 320.01 305 320 C 296.52 320.01 288.07 319.15 279.76 317.46 C 271.76 315.82 263.94 313.39 256.42 310.2 C 241.55 303.9 228.05 294.79 216.64 283.36 C 205.21 271.95 196.1 258.45 189.8 243.58 C 186.61 236.06 184.18 228.24 182.54 220.24 C 180.85 211.93 179.99 203.48 180 195 Z M 13 490 L 13 413.148 L 126.148 300 L 203 376.852 L 89.852 490 L 13 490 Z M 205 160 L 270 160 L 270 95 L 340 95 L 340 160 L 405 160 L 405 230 L 340 230 L 340 295 L 270 295 L 270 230 L 205 230 L 205 160 Z',
fill: true,
fillRule: "evenodd",
stroke: 0
},
zoom_out: {
viewbox: '0 0 500 500',
path: 'M 120 195 C 120 92.896 202.896 10 305 10 C 407.104 10 490 92.896 490 195 C 490 297.104 407.104 380 305 380 C 202.896 380 120 297.104 120 195 Z M 180 195 C 179.99 186.52 180.85 178.06 182.55 169.75 C 184.18 161.76 186.61 153.94 189.8 146.42 C 196.1 131.55 205.21 118.05 216.64 106.64 C 228.05 95.21 241.55 86.1 256.42 79.8 C 263.94 76.61 271.76 74.18 279.76 72.54 C 288.07 70.85 296.52 69.99 305 70 C 313.48 69.99 321.93 70.85 330.24 72.54 C 338.24 74.18 346.06 76.61 353.58 79.8 C 368.45 86.1 381.95 95.21 393.36 106.64 C 404.79 118.05 413.9 131.55 420.2 146.42 C 423.39 153.94 425.82 161.76 427.46 169.76 C 429.15 178.07 430.01 186.52 430 195 C 430.01 203.48 429.15 211.93 427.46 220.24 C 425.82 228.24 423.39 236.06 420.2 243.58 C 413.9 258.45 404.79 271.95 393.36 283.36 C 381.95 294.79 368.45 303.9 353.58 310.2 C 346.06 313.39 338.24 315.82 330.24 317.46 C 321.93 319.15 313.48 320.01 305 320 C 296.52 320.01 288.07 319.15 279.76 317.46 C 271.76 315.82 263.94 313.39 256.42 310.2 C 241.55 303.9 228.05 294.79 216.64 283.36 C 205.21 271.95 196.1 258.45 189.8 243.58 C 186.61 236.06 184.18 228.24 182.54 220.24 C 180.85 211.93 179.99 203.48 180 195 Z M 13 490 L 13 413.148 L 126.148 300 L 203 376.852 L 89.852 490 L 13 490 Z M 205 160 L 405 160 L 405 230 L 205 230 L 205 160 Z',
fill: true,
fillRule: "evenodd",
stroke: 0
}
};
self.icons = {};
Object.keys(iconSources).forEach(iconName => {
let iconData = iconSources[iconName];
let svgIcon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svgIcon.setAttribute('viewBox', iconData.viewbox);
svgIcon.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
svgIcon.classList.add('radix-icon');
svgIcon.classList.add('rdx-icon-' + iconName);
let iconPath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
iconPath.setAttribute('d', iconData.path);
if (iconData.fill) {
iconPath.setAttribute('fill', 'currentColor');
if (iconData.fillRule !== undefined) iconPath.setAttribute('fill-rule', iconData.fillRule);
} else {
iconPath.setAttribute('fill', 'none');
}
if (iconData.stroke === 0) {
iconPath.setAttribute('stroke', 'none');
} else {
iconPath.setAttribute('stroke', 'currentColor');
iconPath.setAttribute('stroke-linejoin', iconData.linejoin);
iconPath.setAttribute('stroke-linecap', iconData.linecap);
}
iconPath.setAttribute('stroke', 'none');
svgIcon.appendChild(iconPath);
self.icons[iconName] = svgIcon;
});
// hamburger Menu
let hmbgr = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
let hmbgrLine1 = document.createElementNS('http://www.w3.org/2000/svg', 'line');
let hmbgrLine2 = document.createElementNS('http://www.w3.org/2000/svg', 'line');
let hmbgrLine3 = document.createElementNS('http://www.w3.org/2000/svg', 'line');
hmbgr.setAttribute('viewBox', '0 0 50 50');
hmbgr.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
hmbgr.classList.add('radix-icon');
hmbgr.classList.add('rdx-icon-hamburger');
hmbgrLine1.setAttribute('x1', 5);
hmbgrLine1.setAttribute('y1', 13);
hmbgrLine1.setAttribute('x2', 45);
hmbgrLine1.setAttribute('y2', 13);
hmbgrLine1.classList.add('bar1');
hmbgrLine2.setAttribute('x1', 5);
hmbgrLine2.setAttribute('y1', 25);
hmbgrLine2.setAttribute('x2', 45);
hmbgrLine2.setAttribute('y2', 25);
hmbgrLine2.classList.add('bar2');
hmbgrLine3.setAttribute('x1', 5);
hmbgrLine3.setAttribute('y1', 37);
hmbgrLine3.setAttribute('x2', 45);
hmbgrLine3.setAttribute('y2', 37);
hmbgrLine3.classList.add('bar3');
hmbgr.appendChild(hmbgrLine1);
hmbgr.appendChild(hmbgrLine2);
hmbgr.appendChild(hmbgrLine3);
self.icons.hamburger = hmbgr;
// svg insert
let iconCalls = document.querySelectorAll(self.option.icons.selector);
if (iconCalls) {
iconCalls.forEach(iconCall => {
let innerText = iconCall.textContent;
self.replaceIcon(iconCall, innerText);
});
}
}
resolve();
});
}).then(() => {
// Speed 2
return new Promise(resolve => {
// Smooth scroll and Fix external links
if (self.option.smoothScroll.active || self.option.fixExtLink.active) {
const links = document.querySelectorAll('a');
let scrollFrom = null;
let scrollTo = null;
links.forEach(link => {
if (!link.hasAttribute('href') || link.getAttribute('href').length == 0) {
link.setAttribute('href', '#');
}
let linkHref = link.getAttribute('href');
// Smooth scroll
if (self.option.smoothScroll.active) {
let firstLetter = linkHref.substring(0, 1);
let hrefTarget = linkHref.substring(1);
if (firstLetter === '#') {
link.addEventListener('click', event => {
event.preventDefault();
const target = hrefTarget.length > 0 ? document.getElementById(hrefTarget) : document.body;
const uniqueEasing = link.dataset.scrollEasing;
const uniqueDuration = link.dataset.scrollDuration;
const headerElm = !self.option.smoothScroll.header ? null : document.querySelector(self.option.smoothScroll.header);
const headerFixed = headerElm ? window.getComputedStyle(headerElm).getPropertyValue('position') : null;
const headerHeight = headerFixed === 'fixed' ? headerElm.offsetHeight : 0;
scrollFrom = window.scrollY;
scrollTo = window.scrollY + target.getBoundingClientRect().top - headerHeight;
self.smoothScroll(scrollFrom, scrollTo, uniqueDuration, uniqueEasing);
});
}
}
// Auto fill target blank
if (self.option.fixExtLink.active) {
let host = null;
if (linkHref.match(/.+\.pdf$/)) {
link.setAttribute('target', '_blank');
link.setAttribute('rel', 'noopener');
link.classList.add('rdx-pdf');
} else if (linkHref.match(/^http/)) {
host = window.location.hostname;
const linkHost = linkHref.replace(/\\/g, '/').match(/\/\/([^/]*)/)
if (host === '' || !!linkHost && linkHost[1] !== host) {
link.setAttribute('target', '_blank');
link.setAttribute('rel', 'noopener');
link.classList.add('rdx-extlink');
}
}
}
});
}
// Toggle menu
if (self.option.toggleNav.active && self.option.toggleNav.trigger !== '' && self.option.toggleNav.target !== '') {
self.navState = false;
let toggleTrigger = document.querySelectorAll(self.option.toggleNav.trigger);
toggleTrigger.forEach(t => {
t.addEventListener('click', () => { self.toggleNav(); });
});
}
// drag scroll
if (self.option.dragScroll.active && self.option.dragScroll.selector !== '') {
let dragScrollWrapper = Array.from(document.querySelectorAll(self.option.dragScroll.selector));
let scrollHint = document.createElement('div');
scrollHint.classList.add('rdx-drag-hint');
let scrollHintInner = document.createElement('div');
scrollHintInner.classList.add('rdx-drag-hint-inner');
scrollHint.appendChild(scrollHintInner);
scrollHintInner.innerHTML = '<div class="rdx-drag-hint-text">DRAG</div>';
scrollHintInner.prepend(self.icons.arrow_lr.cloneNode(true));
if (dragScrollWrapper.length > 0) {
dragScrollWrapper.forEach(e => {
e.style.overflow = 'auto';
e.style.position = 'relative';
e.addEventListener('mousedown', v => { self.dragDown(e, v) }, false);
if (e.scrollWidth > e.clientWidth && self.option.dragScroll.hint) {
e.appendChild(scrollHint.cloneNode(true));
e.addEventListener('scroll', () => {
let thisHint = e.querySelector('.rdx-drag-hint');
if (thisHint !== null) {
thisHint.classList.add('hide');
}
});
}
});
}
document.addEventListener('touchmove', v => { self.dragMove(v) }, false);
document.addEventListener('mousemove', v => { self.dragMove(v) }, false);
document.addEventListener('mouseup', v => { self.dragUp(v) }, false);
}
// flex fix
if (self.option.flexFix.active) {
let flexFix = document.querySelectorAll(self.option.flexFix.selector);
if (flexFix.length > 0) {
let inherit = self.option.flexFix.inherit;
flexFix.forEach(e => {
let childArr = Array.from(e.children);
let dummyChild = childArr[0].cloneNode(inherit);
dummyChild.classList.add('rdx-dummy-item');
let count = self.option.flexFix.min > childArr.length ? self.option.flexFix.min : childArr.length;
for (let i = 0; i < count; i++) {
let dummyClone = dummyChild.cloneNode(inherit);
e.appendChild(dummyClone);
}
});
}
}
// scroll appear
if (self.option.scrollAppear.active) {
const appearItems = document.querySelectorAll(self.option.scrollAppear.selector);
if (appearItems.length > 0) {
appearItems.forEach(item => {
item.classList.add('rdx-scroll-appear');
});
window.addEventListener('scroll', () => {
let windowHeight = window.innerHeight;
appearItems.forEach(appearItem => {
let resetFlg = appearItem.hasAttribute('data-appear-reset') ? appearItem.dataset.appearReset : self.option.scrollAppear.reset;
let activeClass = appearItem.hasAttribute('data-appear-class') ? appearItem.dataset.appearClass : self.option.scrollAppear.class;
let delay = appearItem.hasAttribute('data-appear-delay') ? appearItem.dataset.appearDelay : self.option.scrollAppear.delay;
let modeNum = appearItem.hasAttribute('data-appear-fixed') ? appearItem.dataset.appearFixed : null;
if (modeNum === null) {
let itemRect = appearItem.getBoundingClientRect();
let itemHeight = appearItem.offsetHeight;
if (itemRect.top < windowHeight * .9 && itemRect.top + itemHeight > windowHeight * .1) {
setTimeout(() => {
appearItem.classList.add(activeClass);
}, delay);
} else if (resetFlg) {
setTimeout(() => {
appearItem.classList.remove(activeClass);
}, delay);
}
} else {
if (window.pageYOffset > modeNum) {
setTimeout(() => {
appearItem.classList.add(activeClass);
}, delay);
} else if (resetFlg) {
setTimeout(() => {
appearItem.classList.remove(activeClass);
}, delay);
}
}
});
});
}
}
resolve();
});
}).then(() => {
// Speed 3
return new Promise(resolve => {
// modal
if (self.option.modal.active) {
let modals = document.querySelectorAll(self.option.modal.selector);
if (modals.length > 0) {
self.modalParts = {
viewport: document.createElement('div'),
area: document.createElement('div'),
wrapper: document.createElement('div'),
frame: document.createElement('div'),
content: document.createElement('div'),
closeButton: self.icons.cross.cloneNode(true),
toggles: document.createElement('div'),
magnifier: document.createElement('div'),
magnify: self.option.modal.magnify,
enlarge: self.icons.zoom_in.cloneNode(true),
shrink: self.icons.zoom_out.cloneNode(true),
scaleDisp: document.createElement('div'),
scaleSelector: document.createElement('ul'),
scale: self.option.modal.defaultScale,
size: {
width: 0,
height: 0
},
class: self.option.modal.class,
drag: self.option.modal.drag,
dragEvent: v => { self.dragDown(self.modalParts.wrapper, v) },
fit: self.option.modal.fit,
duration: self.option.modal.resizeDuration,
easing: self.option.modal.resizeEasing
}
self.modalParts.viewport.classList.add('rdx-modal-viewport');
self.modalParts.area.classList.add('rdx-modal-area');
self.modalParts.wrapper.classList.add('rdx-modal-wrapper');
self.modalParts.content.classList.add('rdx-modal-content');
self.modalParts.frame.classList.add('rdx-modal-frame');
self.modalParts.toggles.classList.add('rdx-modal-toggles');
self.modalParts.viewport.appendChild(self.modalParts.toggles);
self.modalParts.viewport.appendChild(self.modalParts.area);
self.modalParts.area.appendChild(self.modalParts.wrapper);
self.modalParts.wrapper.appendChild(self.modalParts.frame);
self.modalParts.frame.appendChild(self.modalParts.content);
self.modalParts.magnifier.classList.add('rdx-modal-magnifier');
self.modalParts.enlarge.classList.add('rdx-modal-enlarge');
self.modalParts.shrink.classList.add('rdx-modal-shrink');
self.modalParts.scaleDisp.classList.add('rdx-modal-scale');
self.modalParts.toggles.appendChild(self.modalParts.magnifier);
self.modalParts.magnifier.appendChild(self.modalParts.scaleDisp);
self.modalParts.magnifier.appendChild(self.modalParts.enlarge);
self.modalParts.magnifier.appendChild(self.modalParts.shrink);
self.modalParts.closeButton.classList.add('rdx-modal-close');
self.modalParts.toggles.appendChild(self.modalParts.closeButton);
self.modalParts.scaleSelector.classList.add('rdx-modal-scaler');
self.modalParts.magnifier.appendChild(self.modalParts.scaleSelector);
document.body.appendChild(self.modalParts.viewport);
self.modalParts.scaleDisp.addEventListener('click', event => {
event.preventDefault();
let i = self.option.modal.scaleStep.indexOf(self.modalParts.scale);
self.modalParts.scaleSelector.scrollTop = 40 * i;
self.modalParts.scaleSelector.classList.add('active');
});
self.option.modal.scaleStep.forEach(scale => {
let scaleItem = document.createElement('li');
scaleItem.innerHTML = self.floatRound(scale, 1) + 'x';
scaleItem.dataset.scale = scale;
self.modalParts.scaleSelector.appendChild(scaleItem);
scaleItem.addEventListener('click', event => {
event.preventDefault();
self.modalResize(scaleItem.dataset.scale);
self.modalParts.scale = scale;
});
});
self.modalParts.enlarge.addEventListener('click', event => {
event.preventDefault();
let i = self.option.modal.scaleStep.indexOf(self.modalParts.scale);
let aftScale = i < self.option.modal.scaleStep.length - 1 ? self.option.modal.scaleStep[i + 1] : self.option.modal.scaleStep[i];
self.modalResize(aftScale);
self.modalParts.scale = aftScale;
});
self.modalParts.shrink.addEventListener('click', event => {
event.preventDefault();
let i = self.option.modal.scaleStep.indexOf(self.modalParts.scale);
let aftScale = i > 0 ? self.option.modal.scaleStep[i - 1] : self.option.modal.scaleStep[i];
self.modalResize(aftScale);
self.modalParts.scale = aftScale;
});
modals.forEach(modal => {
let targets = [];
if (modal.hasAttribute('data-modal-target')) {
targets = document.querySelectorAll(modal.dataset.modalTarget);
if (targets) {
targets.forEach(target => {
target.classList.add('rdx-modal-source');
});
}
} else {
targets = [modal];
modal.classList.add('rdx-modal-source');
}
modal.addEventListener('click', event => {
event.preventDefault();
let duration = modal.hasAttribute('data-modal-duration') ? modal.dataset.modalDuration : null;
let easing = modal.hasAttribute('data-modal-easing') ? modal.dataset.modalEasing : null;
let scale = modal.hasAttribute('data-modal-scale') ? modal.dataset.modalScale : null;
let drag = modal.hasAttribute('data-modal-drag') ? modal.dataset.modalDrag : null;
let magnify = modal.hasAttribute('data-modal-magnify') ? modal.dataset.modalMagnify : null;
let fit = modal.hasAttribute('data-modal-fit') ? modal.dataset.modalFit : null;
let Class = modal.hasAttribute('data-modal-class') ? modal.dataset.modalClass : null;
let wrap = modal.hasAttribute('data-modal-wrap') ? modal.dataset.modalWrap : null;
self.modalOpen(targets, Class, duration, easing, scale, drag, magnify, fit, wrap);
});
});
self.modalParts.viewport.addEventListener('mouseup', event => {
let clicked = event.target;
if (!clicked.classList.contains('rdx-modal-item')) {
if (clicked.closest('.rdx-modal-content') === null && clicked.closest('.rdx-modal-toggles') === null) {
self.modalClose();
}
}
}, false);
self.modalParts.closeButton.addEventListener('click', () => { self.modalClose() }, false);
}
}
resolve();
});
}).then(() => {
// Speed 4
document.dispatchEvent(self.events.afterInitialize);
});
};
/**
* スムーススクロール
* @param {float} scrollFrom スクロールの始点
* @param {float} scrollTo スクロールの終点
* @param {int} duration スクロールにかける時間(ミリ秒)
* @param {string} easingName スクロールアニメーションのイージング
*/
smoothScroll(scrollFrom, scrollTo, duration, easingName) {
const self = this;
scrollTo = scrollTo < 0 ? 0 : scrollTo;
const changeVal = scrollTo - scrollFrom;
duration = duration === undefined ? self.option.smoothScroll.duration : duration;
easingName = easingName === undefined ? self.option.smoothScroll.easing : easingName;
let easing = self.getEasing(easingName);
let cnt = 0;
let timer = null;
document.dispatchEvent(self.events.beforeScroll);
let moveAnimate = () => {
cnt++;
let elapsedTime = cnt * self.option.timeFrame;
let pos = easing(elapsedTime, scrollFrom, changeVal, duration);
window.scrollTo(0, pos);
if (elapsedTime > duration) {
window.scrollTo(0, scrollTo);
clearInterval(timer);
document.dispatchEvent(self.events.afterScroll);
}
}
timer = setInterval(moveAnimate, self.option.timeFrame);
};
/**
* ページスクロールのオンオフ
* @param {boolean} mode 禁止する(true)、禁止しない(false)
*/
preventScroll(mode) {
this.DOM_ROOTS.forEach(e => {
if (mode) {
e.style.overflowY = 'hidden';
} else {
e.style.overflowY = 'initial';
}
});
};
/**
* ナビゲーション開閉
* @param {boolean | string} mode 開ける(false)か閉じる(true)か
*/
toggleNav(mode) {
const self = this;
let toggleTrigger = document.querySelectorAll(self.option.toggleNav.trigger);
let toggleTarget = document.querySelectorAll(self.option.toggleNav.target);
if (mode === undefined) {
if (self.navState) {
document.dispatchEvent(self.events.beforeNavClose);
if (self.option.toggleNav.preventScroll) {
self.preventScroll(false);
}
self.navState = false;
toggleTrigger.forEach(t => {
t.classList.remove(self.option.toggleNav.class);
});
toggleTarget.forEach(t => {
t.classList.remove(self.option.toggleNav.class);
});
document.dispatchEvent(self.events.afterNavClose);
} else {
document.dispatchEvent(self.events.beforeNavOpen);
if (self.option.toggleNav.preventScroll) {
self.preventScroll(true);
}
self.navState = true;
toggleTrigger.forEach(t => {
t.classList.add(self.option.toggleNav.class);
});
toggleTarget.forEach(t => {
t.classList.add(self.option.toggleNav.class);
});
document.dispatchEvent(self.events.afterNavOpen);
}
} else {
if (mode === true || mode === 'close') {
document.dispatchEvent(self.events.beforeNavClose);
if (self.option.toggleNav.preventScroll) {
self.preventScroll(false);
}
self.navState = false;
toggleTrigger.forEach(t => {
t.classList.remove(self.option.toggleNav.class);
});
toggleTarget.forEach(t => {
t.classList.remove(self.option.toggleNav.class);
});
document.dispatchEvent(self.events.afterNavClose);
} else if (mode === false || mode === 'open') {
document.dispatchEvent(self.events.beforeNavOpen);
if (self.option.toggleNav.preventScroll) {
self.preventScroll(true);
}
self.navState = true;
toggleTrigger.forEach(t => {
t.classList.add(self.option.toggleNav.class);
});
toggleTarget.forEach(t => {
t.classList.add(self.option.toggleNav.class);
});
document.dispatchEvent(self.events.afterNavOpen);
}
}
};
/**
* SVG iconに置き換える
* @param {element} target SVGに置き換える対象
* @param {element} iconName アイコンの名前
*/
replaceIcon(target, iconName) {
const self = this;
iconName = iconName === undefined ? target.innerText : iconName;
if (Object.keys(self.icons).includes(iconName)) {
target.replaceWith(self.icons[iconName].cloneNode(true));
} else {
console.log('' + iconName + ' is not icon name.');
}
};
/**
* ドラッグスクロールのマウスダウンイベント
* @param {elemnt} e スクロール領域のエレメント
* @param {event} v マウスダウンイベント
*/
dragDown(e, v) {
if (!self.isMobile) {
e.style.cursor = 'move';
e.setAttribute('rdx-drag-on', true);
e.setAttribute('rdx-drag-scrolled-x', e.scrollLeft);
e.setAttribute('rdx-drag-scrolled-y', e.scrollTop);
e.setAttribute('rdx-drag-click-x', v.clientX);
e.setAttribute('rdx-drag-click-y', v.clientY);
}
};
/**
* ドラッグスクロールのマウスムーブイベント
* @param {event} v マウスムーブイベント
*/
dragMove(v) {
let target = document.querySelector('[rdx-drag-on="true"]');
if (target !== null) {
v.preventDefault();
target.scrollLeft = Number(target.getAttribute('rdx-drag-scrolled-x')) + Number(target.getAttribute('rdx-drag-click-x')) - v.clientX;
target.scrollTop = Number(target.getAttribute('rdx-drag-scrolled-y')) + Number(target.getAttribute('rdx-drag-click-y')) - v.clientY;
}
};
/**
* ドラッグスクロールのマウスアップイベント
* @param {event} v マウスアップイベント
*/
dragUp(v) {
v.preventDefault();
v.stopImmediatePropagation();
let target = document.querySelector('[rdx-drag-on="true"]');
if (target) {
target.style.cursor = '';
target.setAttribute('rdx-drag-on', false);
}
};
/**
* モーダルを開く
* @param {array} targets 開く対象のエレメントの配列
* @param {string} Class viewport に追加するCSSクラス
* @param {int} duration 拡縮にかける時間
* @param {string} easing 拡縮アニメーションのイージング
* @param {float} scale 開いたときの拡大率
* @param {boolean} drag ドラッグスクロールを可能にするか
* @param {boolean} magnify 拡縮ボタンを表示するか
* @param {boolean} fit 展開時にスケールを自動調整するか
* @param {boolean} wrap 折り返しモードで開くか
*/
modalOpen(targets, Class, duration, easing, scale, drag, magnify, fit, wrap) {
const self = this;
if (self.modalState) return;
new Promise(resolve => {
document.dispatchEvent(self.events.beforeModalOpen);
resolve();
}).then(() => {
return new Promise(resolve => {
self.modalParts.content.innerHTML = '';
self.modalParts.magnify = magnify !== null ? magnify : self.option.modal.magnify;
self.modalParts.drag = drag !== null ? drag : self.option.modal.drag;
self.modalParts.fit = fit !== null ? fit : self.option.modal.fit;
self.modalParts.duration = duration === undefined ? self.option.modal.resizeDuration : duration;
self.modalParts.easing = easing === undefined ? self.option.modal.resizeEasing : easing;
self.modalParts.wrap = wrap === undefined ? self.option.modal.wrap : wrap;
let classArr = Class !== null ? Class : self.option.modal.class;
classArr = String(classArr).split(' ');
self.modalParts.class = classArr;
self.preventScroll(true);
resolve();
});
}).then(() => {
return new Promise(resolve => {
targets.forEach(target => {
let modalClone = target.cloneNode(true);
modalClone.classList.remove('rdx-modal-source');
modalClone.classList.add('rdx-modal-item');
self.modalParts.content.appendChild(modalClone);
});
resolve();
});
}).then(() => {
return new Promise(resolve => {
self.modalParts.magnifier.style.display = self.str2bool(self.modalParts.magnify) && !self.str2bool(self.modalParts.wrap) ? 'flex' : 'none';
self.modalParts.size = {
width: self.modalParts.content.offsetWidth,
height: self.modalParts.content.offsetHeight
};
resolve();
});
}).then(() => {
return new Promise(resolve => {
self.modalParts.scale = 1;
if (scale !== null) {
self.modalParts.scale = self.floatRound(scale, 1);
} else if (self.str2bool(self.modalParts.fit)) {
let areaHeight = self.modalParts.area.clientHeight;
let areaWidth = self.modalParts.area.clientWidth;
self.modalParts.scale = self.option.modal.scaleStep[0];
for (let i = 0; i < self.option.modal.scaleStep.length; i++) {
if (self.modalParts.size.width * self.option.modal.scaleStep[i] > areaWidth || self.modalParts.size.height * self.option.modal.scaleStep[i] > areaHeight) break;
self.modalParts.scale = self.option.modal.scaleStep[i];
}
}
if (self.str2bool(self.modalParts.drag) && !self.str2bool(self.modalParts.wrap)) {
self.modalParts.wrapper.addEventListener('mousedown', self.modalParts.dragEvent, false);
} else {
self.modalParts.wrapper.removeEventListener('mousedown', self.modalParts.dragEvent, false);
}
resolve();
});
}).then(() => {
return new Promise(resolve => {
if (self.str2bool(self.modalParts.wrap)) {
self.modalParts.content.style.maxHeight = '100%';
self.modalParts.content.style.maxWidth = '100%';
} else {
self.modalParts.content.style.transform = 'scale(' + self.modalParts.scale + ')';
self.modalParts.wrapper.style.height = 'min(' + self.floatCeil(self.modalParts.size.height * self.modalParts.scale, 0) + 'px, 100%)';
self.modalParts.wrapper.style.width = 'min(' + self.floatCeil(self.modalParts.size.width * self.modalParts.scale, 0) + 'px, 100%)';
self.modalParts.scaleDisp.innerHTML = self.floatRound(self.modalParts.scale, 1) + 'x';
self.modalParts.frame.style.height = self.floatCeil(self.modalParts.size.height * self.modalParts.scale, 0) + 'px';
self.modalParts.frame.style.width = self.floatCeil(self.modalParts.size.width * self.modalParts.scale, 0) + 'px';
}
self.modalParts.class.forEach(c => {
self.modalParts.viewport.classList.add(c);
});
self.modalParts.viewport.classList.add('active');
self.modalState = true;
resolve();
});
}).then(() => {
document.dispatchEvent(self.events.afterModalOpen);
});
};
/**
* モーダルを閉じる
*/
modalClose() {
const self = this;
if (!self.modalState) return;
if (self.modalParts.wrapper.getAttribute('rdx-drag-on') === 'true') return;
new Promise(resolve => {
document.dispatchEvent(self.events.beforeModalClose);
resolve();
}).then(() => {
return new Promise(resolve => {
self.modalParts.scaleSelector.classList.remove('active');
self.modalParts.content.innerHTML = '';
self.preventScroll(false);
self.modalParts.content.style = '';
self.modalParts.frame.style = '';
self.modalParts.wrapper.style = '';
self.modalParts.viewport.classList.remove('active');
setTimeout(() => {
self.modalParts.class.forEach(c => {
self.modalParts.viewport.classList.remove(c);
});
}, 250);
self.modalState = false;
resolve();
});
}).then(() => {
document.dispatchEvent(self.events.afterModalClose);
});
};
/**
* モーダルウィンドウの拡縮
* @param {float} aftScale 操作後の倍率
*/
modalResize(aftScale) {
const self = this;
if (!self.modalState) return;
let duration = self.option.modal.resizeDuration;
let frame = self.option.timeFrame;
let cnt = 0;
let timer = null;
let easing = self.getEasing(self.option.modal.resizeEasing);
let befScale = self.modalParts.scale;
let befWidth = self.modalParts.size.width * befScale;
let befHeight = self.modalParts.size.height * befS