<!-- webview only -->
<template>
    <section class="zIndexByJs"
             :data-zIndex="zIndex"
             :class="['b-textarea', {'auto-height': autoHeight && isNative}]"
             :style="{height: autoHeight && !isNative ? `${contentHeight}px` : ''}"
             @click.capture="$emit('click', $event)"
             @touchstart.capture="$emit('touchstart', $event)"
             @touchmove.capture="$emit('touchmove', $event)"
             @touchend.capture="$emit('touchend', $event)"
             @touchcancel.capture="$emit('touchcancel', $event)">
        <template v-if="isNative">
            <section class="b-textarea-wrapper" v-if="autoHeight" :style="{height: `${contentHeight}px`}"></section>
            <span ref="placeholder" :class="placeholderClass" :style="placeholderStyle" v-show="false"></span>
        </template>
        <template v-else>
            <span class="placeholder" ref="placeholder" :class="placeholderClass" :style="placeholderStyleFilter" v-show="nativeValue.length == 0">{{placeholder}}</span>
            <textarea :disabled="disabled" v-model="nativeValue" @input="onH5Input"></textarea>
        </template>
    </section>
</template>
<script>
const listeners = {};
const observer = new MutationObserver(() => {
    for (const key in listeners) {
        if (listeners.hasOwnProperty(key)) {
            const listener = listeners[key];
            if (typeof listener === 'function') {
                listener();
            }
        }
    }
});
observer.observe(document.documentElement, {
    attributes: true,
    characterData: true,
    childList: true,
    subtree: true,
});

const styleKeys = ['x', 'y', 'color', 'fontFamily', 'fontSize', 'fontWeight', 'lineHeight',
    'opacity', 'height', 'width', 'borderBottomWidth', 'borderLeftWidth', 'borderRightWidth', 'borderTopWidth',
    'paddingBottom', 'paddingLeft', 'paddingRight', 'paddingTop', 'display', 'textAlign'];
const getComputed = (element, keys) => {
    const computed = window.getComputedStyle(element, null);

    // 这个对象用来计算最终native input的样式和位置
    const useful = {
        color: '',
        fontFamily: '',
        fontSize: 0,
        fontWeight: '',
        fontStyle: '',
        lineHeight: 0,
        opacity: 0,
        height: 0,
        width: 0,
        borderBottomWidth: 0,
        borderLeftWidth: 0,
        borderRightWidth: 0,
        borderTopWidth: 0,
        paddingBottom: 0,
        paddingLeft: 0,
        paddingRight: 0,
        paddingTop: 0,
        display: '',
        textAlign: '',
    };

    for (const key in useful) {
        if (useful.hasOwnProperty(key) && (!keys || keys.indexOf(key) >= 0)) {
            const value = useful[key];
            if (value === 0) {
                // 0 代表数值型 去掉px单位
                useful[key] = parseFloat(computed[key]) || 0;
            } else {
                useful[key] = computed[key];
            }
        }
    }

    // let offsetParent = element.offsetParent;
    // // computed里面有x,y不能通过循环赋值
    // useful.x = (!keys || keys.indexOf('x') >= 0) ? element.offsetLeft : 0;
    // useful.y = (!keys || keys.indexOf('y') >= 0) ? element.offsetTop : 0;
    // useful.fixed = true;
    //
    // while (offsetParent) {
    //     if (offsetParent === document.body) {
    //         useful.fixed = false;
    //     }
    //
    //     if ((!keys || keys.indexOf('x') >= 0)) {
    //         useful.x += offsetParent.offsetLeft;
    //     }
    //
    //     if ((!keys || keys.indexOf('y') >= 0)) {
    //         useful.y += offsetParent.offsetTop;
    //     }
    //
    //     offsetParent = offsetParent.offsetParent;
    // }
    // // computed里面有x,y不能通过循环赋值
    // // useful.x = (!keys || keys.indexOf('x') >= 0) ? element.offsetLeft || boundingClientRect.x || boundingClientRect.left : 0;
    // // useful.y = (!keys || keys.indexOf('y') >= 0) ? element.offsetTop || boundingClientRect.y || boundingClientRect.top : 0;
    // let parent = element.parentNode;
    // while (parent && parent !== document) {
    //     if (window.getComputedStyle(parent, null).display === 'none') {
    //         useful.display = 'none';
    //         break;
    //     }
    //     parent = parent.parentNode;
    // }

    let positionInfo = element.getBoundingClientRect();
    let isFixed = computed.getPropertyValue('position') === 'fixed';
    useful.x = isFixed ? positionInfo.left : positionInfo.left + SmallApp.getScrollData('left');
    useful.y = isFixed ? positionInfo.top : positionInfo.top + SmallApp.getScrollData('top');
    useful.fixed = isFixed;

    // const newStyle = {
    //     "x": isFixed ? positionInfo.left : positionInfo.left + SmallApp.getScrollData('left'),
    //     "y": isFixed ? positionInfo.top : positionInfo.top + SmallApp.getScrollData('top'),
    //     "width": positionInfo.width,
    //     "height": positionInfo.height,
    //     "disableScroll": this.disableScroll,
    //     "fixed": isFixed,
    //     "hidden": styleInfo.getPropertyValue('display') === 'none'
    // };


    return useful;
};

const computedStyleChanged = (oldStyle, newStyle, autoHeight) => {
    let changed = false;
    for (let index = 0; index < styleKeys.length; index++) {
        const key = styleKeys[index];
        if (oldStyle[key] !== newStyle[key] && (!autoHeight || key !== 'height')) {
            changed = true;
            break;
        }
    }
    return changed;
};

const getOptionStyles = (source) => {
    const styles = {};
    styles.fixed = source.fixed;
    styles.x = source.x;
    styles.y = source.y;
    styles.width = source.width;
    styles.height = source.height;
    styles.left = source.borderLeftWidth + source.paddingLeft;
    styles.top = source.borderTopWidth + source.paddingTop;
    styles.right = source.borderRightWidth + source.paddingRight;
    styles.bottom = source.borderBottomWidth + source.paddingBottom;
    styles.color = toHEXColor(source.color);
    styles.fontFamily = source.fontFamily;
    styles.fontSize = source.fontSize;
    styles.fontWeight = source.fontWeight;
    styles.fontStyle = source.fontStyle;
    styles.lineHeight = source.lineHeight;
    styles.textAlign = source.textAlign;
    styles.opacity = source.opacity;
    styles.hidden = source.display === 'none';
    return styles;
};

const isRGBReg = /^rgb/i;
const rgbReg = /\d+/gi;
const toHEXColor = (color) => {
    if (isRGBReg.test(color)) {
        const [r, g, b] = color.match(rgbReg);
        return '#' + toHEX(r) + toHEX(g) + toHEX(b);
    }

    return color;
};
const toHEX = (number) => {
    const str = '0123456789ABCDEF';
    let temp = '';
    let cur = number % 16;
    temp = str.substr(cur, 1);
    cur = (number - cur) / 16;
    return str.substr(cur, 1) + temp;
};

const isiOS = /iPad|iPhone|iPod/i.test(navigator.userAgent);
const isAndroid = !isiOS;

export default {
    name: 'Bili_Textarea',
    props: {
        value: {
            type: String,
            default: '',
        },
        disabled: {
            type: Boolean,
            default: false,
        },
        maxlength: {
            type: Number,
            default: 140,
        },
        "zIndex": {
            type: Number,
            default: 0
        },
        focus: {
            type: Boolean,
            default: false,
        },
        autoHeight: {
            type: Boolean,
            default: false,
        },
        adjustPosition: {
            type: Boolean,
            default: true,
        },
        cursorSpacing: {
            type: Number,
            default: 0,
        },
        showConfirmBar: {
            type: Boolean,
            default: true,
        },
        cursor: {
            type: Number,
            default: -1,
        },
        selectionStart: {
            type: Number,
            default: -1,
        },
        selectionEnd: {
            type: Number,
            default: -1,
        },
        placeholder: {
            type: String,
            default: '',
        },
        placeholderStyle: {
            type: String,
            default: '',
        },
        placeholderClass: {
            type: String,
            default: '',
        },
        isNative: {
            type: Boolean,
            default: true
        }
    },
    data() {
        return {
            nativeValue: '',
            lastStyle: {},
            lastPlaceholderStyle: {},
            isAndroid: isAndroid,
            showContent: false,
            contentHeight: 0,
            hasBlured: false,
            systemInfo: {}
        };
    },
    computed: {
        focusStatus() {
            return this.focus && !this.hasBlured;
        },
        placeholderStyleFilter() {
            let colorStyle = '';
            const arr = this.placeholderStyle.split(';');

            arr.some(style => {
                if (style.trim().indexOf('color') === 0) {
                    colorStyle = style;
                    return true;
                }
            });
            return colorStyle;
        }
    },
    watch: {
        focus(value) {
            if (value) {
                this.hasBlured = false;
            }
        }
    },
    mounted() {
        // 引入层级算法，自动保持层级
        SmallApp.computeIndex();
        this.systemInfo =  window.bl.getSystemInfoSync();
        // 设置初始值
        this.nativeValue = (this.maxlength >= 0 && this.value) ? this.value.substr(0, this.maxlength) : (this.value || '');
        // 初始化不是focus，永远不是focus
        this.hasBlured = !this.focus;

        // 需要监视变更并通知NA变更的属性
        const PROPS_NEED_WATCH = [
            'value',
            'nativeValue',
            'disabled',
            'maxlength',
            'autoHeight',
            'adjustPosition',
            'cursorSpacing',
            'showConfirmBar',
            'placeholder',
            'focusStatus',
            'isNative'
        ];

        // options列表
        const options = {
            name: 'textarea',
            id: this._uid,
            destroy: false,
            value: this.nativeValue,
            disabled: this.disabled,
            maxlength: this.maxlength,
            focus: this.focusStatus,
            autoHeight: this.autoHeight,
            adjustPosition: this.adjustPosition,
            cursorSpacing: this.cursorSpacing,
            showConfirmBar: this.showConfirmBar,
            cursor: this.cursor,
            selectionStart: this.selectionStart,
            selectionEnd: this.selectionEnd,
            placeholder: this.placeholder,
            styles: {},
            zIndex: Number(this.$el.getAttribute('B-zIndex'))
        };

        // 添加watcher
        PROPS_NEED_WATCH.forEach(prop => {
            this.$watch(prop, (value) => {
                options.value = this.nativeValue;

                if (prop === 'value' && this.value === this.nativeValue) return;

                switch (prop) {
                    case 'nativeValue': {
                        return;
                    }

                    case 'focusStatus': {
                        options.show = value;
                        options.focus = value;
                        break;
                    }

                    case 'value': {
                        if (!this.value) {
                            this.nativeValue = '';
                            return;
                        }
                    }

                    case 'maxlength': {
                        this.nativeValue = (this.maxlength >= 0 && this.value) ? this.value.substr(0, this.maxlength) : this.value;
                        options.value = this.nativeValue;
                        break;
                    }

                    case 'isNative': {
                        options.styles.hidden = !value;
                        break;
                    }

                    default: {
                        options[prop] = value;
                    }
                }

                if (prop === 'isNative' || this.isNative) {
                    SmallApp.nativeComponent(options);
                }
            });
        });

        // 样式变更处理方法
        const helper = () => {
            if (this.isNative) {
                SmallApp.waitStyleOver().then(()=>{
                    setTimeout(() => {
                        const newStyle = getComputed(this.$el);
                        const newPlaceholderStyle = getComputed(this.$refs.placeholder);
                        if (computedStyleChanged(this.lastStyle, newStyle, this.autoHeight) || computedStyleChanged(this.lastPlaceholderStyle, newPlaceholderStyle)) {
                            options.value = this.nativeValue;

                            this.lastStyle = newStyle;
                            options.styles = getOptionStyles(newStyle);

                            this.lastPlaceholderStyle = newPlaceholderStyle;
                            const { color, fontSize, fontWeight, fontFamily, fontStyle } = getOptionStyles(newPlaceholderStyle);
                            options.placeholderStyle = {
                                color,
                                fontSize: newStyle.fontSize
                            };

                            this.contentHeight = Math.max(options.styles.lineHeight, this.contentHeight);
                            SmallApp.nativeComponent(options);
                        }
                    }, 80);
                });
            }
        };

        listeners[this._uid] = helper;

        helper();

        window.addEventListener('resize', () => {
            helper();
        });
    },
    destroyed() {
        const options = {
            name: 'textarea',
            id: this._uid,
            destroy: true,
            styles: {},
        };
        delete listeners[this._uid];
        SmallApp.nativeComponent(options);
    },
    methods: {
        handleMessage(message) {
            if (message.type === 'native-component') {
                this.$emit(message.event.type, message.event);

                // 处理安卓的blur/focus状态是否显示内容
                switch (message.event.type) {
                    case 'blur': {
                        this.hasBlured = true;
                        break;
                    }

                    case 'linechange': {
                        this.contentHeight = message.event.detail.height;
                        break;
                    }

                    case 'input': {
                        this.nativeValue = message.event.detail.value;
                        break;
                    }
                }
            }
        },
        onH5Input() {
            this.$emit('input', {
                detail: {
                    value: this.nativeValue
                }
            });
        }
    }
};
</script>
<style>
.b-textarea {
    position: relative;
    z-index: -10;
    display: inline-block;
    font-family: sans-serif;
}
.b-textarea.auto-height {
    height: auto !important;
}
.b-textarea .placeholder {
    position: absolute;
    color: #808080;
}
textarea {
    display: block;
    width: 100%;
    height: 100%;
    font-family: inherit;
    font-size: inherit;
    line-height: inherit;
    text-align: inherit;
    color: inherit;
    background-color: inherit;
    opacity: inherit;
}
</style>
