all files / directives/duplex/ share.js

100% Statements 121/121
89.47% Branches 51/57
100% Functions 14/14
100% Lines 121/121
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 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203                36× 36×   36× 36×     36×   33× 33× 33× 33×   33× 33× 33× 16×   16×   33× 33×     33× 33×   32× 27×         33×       33× 19× 19× 14× 14×     33× 33×       79× 20× 20× 20×     59× 59× 49×   59× 58× 58×           33× 33× 33× 33× 33×   33×     86× 86× 86× 32×                           69× 26× 26× 26×     69×       30× 30× 30×     36× 36×       18× 18× 18× 18× 10×     18× 18×     18× 18× 18× 18× 18× 29×     18× 18×     10×   10×                  
import { avalon, createFragment } from '../../seed/core'
import { rcheckedType } from '../../dom/rcheckedType'
import { lookupOption } from './option'
import { addScope, makeHandle } from '../../parser/index'
import { fromString } from '../../vtree/fromString'
import { updateModel } from './updateDataHandle'
 
 
var rchangeFilter = /\|\s*change\b/
var rdebounceFilter = /\|\s*debounce(?:\(([^)]+)\))?/
export function duplexBeforeInit() {
    var expr = this.expr
    if (rchangeFilter.test(expr)) {
        this.isChanged = true
        expr = expr.replace(rchangeFilter, '')
    }
    var match = expr.match(rdebounceFilter)
    if (match) {
        expr = expr.replace(rdebounceFilter, '')
        Eif (!this.isChanged) {
            this.debounceTime = parseInt(match[1], 10) || 300
        }
    }
    this.expr = expr
}
export function duplexInit() {
    var expr = this.expr
    var node = this.node
    var etype = node.props.type
    this.parseValue = parseValue
    //处理数据转换器
    var parsers = this.param, dtype
    var isChecked = false
    parsers = parsers ? parsers.split('-').map(function (a) {
        if (a === 'checked') {
            isChecked = true
        }
        return a
    }) : []
    node.duplex = this
    if (rcheckedType.test(etype) && isChecked) {
        //如果是radio, checkbox,判定用户使用了checked格式函数没有
        parsers = []
        dtype = 'radio'
        this.isChecked = isChecked
    }
    this.parsers = parsers
    if (!/input|textarea|select/.test(node.nodeName)) {
        Eif ('contenteditable' in node.props) {
            dtype = 'contenteditable'
        }
    } else if (!dtype) {
        dtype = node.nodeName === 'select' ? 'select' :
            etype === 'checkbox' ? 'checkbox' :
                etype === 'radio' ? 'radio' :
                    'input'
    }
    this.dtype = dtype
 
    //判定是否使用了 change debounce 过滤器
    // this.isChecked = /boolean/.test(parsers)
    if (dtype !== 'input' && dtype !== 'contenteditable') {
        delete this.isChanged
        delete this.debounceTime
    } else Eif (!this.isChecked) {
        this.isString = true
    }
 
    var cb = node.props['data-duplex-changed']
    if (cb) {
        var arr = addScope(cb, 'xx')
        var body = makeHandle(arr[0])
        this.userCb = new Function('$event', 'var __vmodel__ = this\nreturn ' + body)
    }
 
}
export function duplexDiff(newVal, oldVal) {
    if (Array.isArray(newVal)) {
        Eif (newVal + '' !== this.compareVal) {
            this.compareVal = newVal + ''
            return true
        }
    } else {
        newVal = this.parseValue(newVal)
        if (!this.isChecked) {
            this.value = newVal += ''
        }
        if (newVal !== this.compareVal) {
            this.compareVal = newVal
            return true
        }
    }
 
}
 
export function duplexBind(vdom, addEvent){
    var dom = vdom.dom
    this.dom = dom
    this.vdom = vdom
    this.duplexCb = updateModel
    dom._ms_duplex_ = this
    //绑定事件
    addEvent(dom, this)
}
 
export var valueHijack = true
try { //#272 IE9-IE11, firefox
    var setters = {}
    var aproto = HTMLInputElement.prototype
    var bproto = HTMLTextAreaElement.prototype
    var newSetter = function (value) { // jshint ignore:line
        setters[this.tagName].call(this, value)
        var data = this._ms_duplex_
        if (!this.caret && data && data.isString) {
            data.duplexCb.call(this, { type: 'setter' })
        }
    }
    var inputProto = HTMLInputElement.prototype
    Object.getOwnPropertyNames(inputProto) //故意引发IE6-8等浏览器报错
    setters['INPUT'] = Object.getOwnPropertyDescriptor(aproto, 'value').set
 
    Object.defineProperty(aproto, 'value', {
        set: newSetter
    })
    setters['TEXTAREA'] = Object.getOwnPropertyDescriptor(bproto, 'value').set
    Object.defineProperty(bproto, 'value', {
        set: newSetter
    })
    valueHijack = false
} catch (e) {
    //在chrome 43中 ms-duplex终于不需要使用定时器实现双向绑定了
    // http://updates.html5rocks.com/2015/04/DOM-attributes-now-on-the-prototype
    // https://docs.google.com/document/d/1jwA8mtClwxI-QJuHT7872Z0pxpZz8PBkf2bGAbsUtqs/edit?pli=1
}
 
function parseValue(val) {
    for (var i = 0, k; k = this.parsers[i++];) {
        var fn = avalon.parsers[k]
        Eif (fn) {
            val = fn.call(this, val)
        }
    }
    return val
}
 
export var updateView = {
    input: function () {//处理单个value值处理
        var vdom = this.node
        var value = this.value + ''
        vdom.dom.value =   vdom.props.value = value
    },
    updateChecked: function (vdom, checked) {
        Eif (vdom.dom) {
           vdom.dom.defaultChecked =  vdom.dom.checked = checked
        }
    },
    radio: function () {//处理单个checked属性
        var node = this.node
        var nodeValue = node.props.value
        var checked
        if (this.isChecked) {
            checked = !!this.value
        } else {
            checked = this.value + '' === nodeValue
        }
        node.props.checked = checked
        updateView.updateChecked(node, checked)
    },
    checkbox: function () {//处理多个checked属性
        var node = this.node
        var props = node.props
        var value = props.value+''
        var values = [].concat(this.value)
        var checked = values.some(function (el) {
            return el + ''=== value
        })
        
        props.defaultChecked = props.checked = checked
        updateView.updateChecked(node, checked)
    },
    select: function () {//处理子级的selected属性
        var a = Array.isArray(this.value) ?
            this.value.map(String) : this.value + ''
        lookupOption(this.node, a)
    },
    contenteditable: function () {//处理单个innerHTML 
 
        var vnodes = fromString(this.value)
        var fragment = createFragment()
        for (var i = 0, el; el = vnodes[i++];) {
            var child = avalon.vdom(el, 'toDOM')
            fragment.appendChild(child)
        }
        avalon.clearHTML(this.dom).appendChild(fragment)
        var list = this.node.children
        list.length = 0
        Array.prototype.push.apply(list, vnodes)
 
        this.duplexCb.call(this.dom)
    }
}