UNPKG

coupdoeil

Version:

Javascript for Ruby on Rails Coupdoeil gem

143 lines (122 loc) 4.6 kB
import {getOptions} from "./attributes"; export const OPTIONS = { animation: { getter: getAnimation }, cache: { getter: getCache }, loading: { getter: getLoading }, offset: { getter: getOffset }, openingDelay: { getter: getOpeningDelay }, placement: { getter: getPlacement }, trigger: { getter: getTrigger }, } const ORDERED_OPTIONS = [ "trigger", // bit size: 1 shift: 0 "loading", // bit size: 2 shift: 1 "cache", // bit size: 1 shift: 3 "openingDelay", // bit size: 1 shift: 4 "animation", // bit size: 3 shift: 5 "placement", // bit size: 16 shift: 8 "offset" // bit size: 20 shift: 24 ] const TRIGGERS = ["hover", "click"] const ANIMATIONS = [false, "slide-in", "fade-in", "slide-out", "custom"] const PLACEMENTS = [ 'auto', 'top', 'top-start', 'top-end', 'right', 'right-start', 'right-end', 'bottom', 'bottom-start', 'bottom-end', 'left', 'left-start', 'left-end' ] const LOADINGS = ["async", "preload", "lazy"] function parseCSSSize(value) { if (typeof value === 'number') { return value } else if ((/^(-?\d+\.?\d+)px$/).test(value)) { return parseFloat(value) } else if ((/^(-?\d*\.?\d+)rem$/).test(value)) { return parseFloat(value) * parseFloat(getComputedStyle(document.documentElement).fontSize) } return 0 } // Offset option is a three part number. // integer (8 bits) + decimals (10 bits) + config (2 bits) // config bits are for sign (positive (0) or negative (1)) and offset unit (px (0) or rem (1)) // Examples: // - "1.25rem" is: 00000001 . 0000011001 . 10 // - "-16px" is: 00010000 . 00000000 . 01 // 10 bits are reserved for decimals so values like 0.875rem are possible. function getOffset(optionsInt) { // shift is BigInt(16 + 3 + 1 + 1 + 2 + 1) const offsetBits = Number(BigInt(optionsInt) >> BigInt(24)) if (offsetBits === 0) return 0 const isNegative = (offsetBits & 1) === 1 const isREM = (offsetBits & 2) === 2 const decimals = (offsetBits >> 2 /* config bits */) & 1023 // (2 ** 10) - 1 const integer = (offsetBits >> (12 /* config (2) + decimals (10) bits */)) const CSSSize = `${isNegative ? '-' : ''}${integer}.${decimals}${isREM ? 'rem' : 'px'}` return parseCSSSize(CSSSize) } // Placement option can have up to 4 placement values: a main one and 3 fallbacks. // There are 13 possible values for one placement, stored as an array in the PLACEMENTS const. // The max index of this array is 12, so a placement value can be stored as an index of this array, // and this index value fits in 4 bits. // Option for placements then consists of 4 times (main and fallbacks) this index value: // So from 0.0.0.0 up to 15.15.15.15, or from 0 up to 65535 (0b1111111111111111) function getPlacement(optionsInt) { // shift is 3 + 1 + 1 + 2 + 1, mask is 2 ** 16 - 1 or 0b1111111111111111 const placementBits = (optionsInt >> 8) & 65535 let shift = 0 let lastPlacement = null const placements = [] while (lastPlacement !== "auto" && shift < 16) { // mask is 2 ** 4 - 1 lastPlacement = PLACEMENTS[(placementBits >> shift) & 15] placements.push(lastPlacement) shift += 4 } return placements } function getAnimation(optionsInt) { // return ANIMATIONS[(optionsInt & 56) >> 5] return ANIMATIONS[(optionsInt >> 5) & 7] } function getOpeningDelay(optionsInt) { return ((optionsInt >> 4) & 1) === 1 } function getCache(optionsInt) { return (optionsInt & 8) === 8 } function getLoading(optionsInt) { // Shift right 1 time to remove trigger bit, mask with 3 (0b11). return LOADINGS[(optionsInt >> 1) & 3] } function getTrigger(optionsInt) { return TRIGGERS[optionsInt & 1] } const popoverOptions = { animation: undefined, cache: undefined, loading: undefined, offset: undefined, openingDelay: undefined, placement: undefined, trigger: undefined, } export function extractOptionsFromElement(coupdoeilElement) { const controller = coupdoeilElement.popoverController const optionsInt = controller.optionsInt ||= parseOptionsInt((controller)) const options = Object.create(popoverOptions) for (const option of ORDERED_OPTIONS) { options[option] = OPTIONS[option].getter(optionsInt) } return options } function parseOptionsInt(controller) { const optionsString = getOptions(controller) return parseInt(optionsString, 36) } export function extractOptionFromElement(coupdoeilElement, optionName) { const controller = coupdoeilElement.popoverController const optionsInt = controller.optionsInt ||= parseOptionsInt((controller)) return OPTIONS[optionName].getter(optionsInt) }