all files / src/ daylight.js

75.76% Statements 125/165
65.85% Branches 54/82
71.43% Functions 10/14
59.7% Lines 40/67
47 statements, 28 branches Ignored     
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170                                     80665×     80665×     80665×   160628× 160628×     160628× 160628× 481884× 481884× 481884× 481884×       160628×               160628×       80665× 160628× 160628× 160628× 160628×       80665× 160628× 160628× 160628×   80665×                 80665×   80665× 80665× 80665× 80665× 80665×   80665×                                                                                                                                                              
import { Config } from "./config";
const core = require("hcv-core");
 
/**
 * 日の満ち欠けをページに反映する処理を提供します。
 */
class Daylight {
    /**
     * インスタンスを初期化します。
     */
    constructor() {
        this._creator = core.creator;
        this._detector = core.detector;
        this._extractor = core.extractor;
    }
 
    /**
     * 日の満ち欠けを反映した色を取得します。
     * @param {String} expression 色表現。
     * @param {Config} config 設定情報。
     * @returns {String} 日の満ち欠けを反映した色を返します。色表現は expression と同種類になります。例えば、expression がヘックス表現の場
     *                   合は、返却される色表現もヘックス表現です。expression が色を含む表現の場合は、その色表現のみを置換した内容を返します。
     */
    getReflectionColor (expression, config) {
        // デフォルト設定を補完
        config = this._createConfig(config);
 
        // 色表現を抽出
        const detected = this._detector.detect(expression);
 
        // 単一の色表現の調整
        const getSingle = e => {
            // 色表現を抽出
            const source = this._extractor.extract(e.expression);
            if (I!source) return null;
 
            // 日の満ち欠けを反映
            const themeColor = config.getThemeColor();
            const reflect = (value1, value2) => {
                const gap = value2 - value1;
                let value = value1 + Math.round(gap * config.impact);
                value = value < 0 ? 0 : value > 255 ? 255 : value;
                return value;
            }
 
            // 反映結果を作成
            const reflected = new core.color(
                reflect(source.r, themeColor[0]),
                reflect(source.g, themeColor[1]),
                reflect(source.b, themeColor[2]),
                source.a
            );
 
            // 変換結果を返す
            return this._creator.create(reflected, e.type);
        };
 
        // 調整した色を取得
        const replacement = {};
        for (const result of detected) {
            const modified = getSingle(result);
            if (Imodified == null) continue;
            replacement[result.expression] = modified;
        }
 
        // 色情報を置換
        let newExpression = expression;
        for (const key of Object.keys(replacement)) {
            const value = replacement[key];
            newExpression = newExpression.replaceAll(key, value);
        }
        return newExpression;
    }
 
    /**
     * 完全な設定を作成します。
     * @param {Config} source 元となる設定。 
     * @returns {Config} 設定を返します。未設定の項目は補完されます。
     */
    _createConfig(source) {
        if (!source) source = {};
 
        const config = new Config();
        config.now = source.now || config.now;
        config.impact = source.impact || config.impact;
        config.numberOfLimitReflection = source.numberOfLimitReflection || config.numberOfLimitReflection;
        config.brightness = source.brightness || config.brightness;
 
        return config;
    }
    
    /**
     * 要素に色を反映します。
     * @param {HTMLElement} element 要素。
     * @param {Array<String>} properties 色を反映するプロパティ。
     * @param {Config} config 設定情報。
     */
    reflectToElement(element, properties=[], config) {
        // デフォルト設定を補完
        config = this._createConfig(config);
 
        // 最大適用回数を超えていれば処理終了
        const reflectionCountKey = "numberOfLimitDaylightReflection";
        const reflectionCount = element.dataset[reflectionCountKey] ? Number(element.dataset[reflectionCountKey]) : 0;
        if (reflectionCount >= config.numberOfLimitReflection) {
            return;
        }
 
        // 各種プロパティ毎に反映
        for (const property of properties) {
            // プロパティの設定がなければ処理を終了
            let style = window.getComputedStyle(element, "");
            if (!style[property]) {
                return;
            }
 
            // 色表現を抽出
            let target = style[property];
            const detected = this._detector.detect(target);
 
            // 調整した色を取得
            const replacement = {};
            for (const result of detected) {
                const modified = this.getReflectionColor(result.expression, config);
                if (modified == null) continue;
                replacement[result.expression] = modified;
            }
 
            // 色情報を置換
            for (const key of Object.keys(replacement)) {
                const value = replacement[key];
                target = target.replaceAll(key, value);
            }
            element.style[property] = target;
        }
 
        // 反映結果を記録
        element.dataset[reflectionCountKey] = reflectionCount + 1;
    }
 
    /**
     * 全ての要素に色を反映します。
     * @param {Array<String>} properties 色を反映するプロパティ。
     * @param {Config} config 設定情報。
     * @param {Array<HTMLElement>} ignore 無視対象。
     */
    reflectToPage(properties=[], config, ignore=[]) {
        // デフォルト設定を補完
        config = this._createConfig(config);
 
        // 画面の要素を変更
        const all = document.getElementsByTagName("*");
        for (let i = 0; i < all.length; i++) {
            // 無視対象であればスキップ
            if (ignore.some(x => x.isEqualNode(all[i]))) {
                continue;
            }
 
            // 変更を適用
            this.reflectToElement(all[i], properties, config);
        }
    }
}
 
// シングルトンとして提供
const instance = new Daylight();
export { instance as Daylight };