1 简介

  感谢大佬提供:https://github.com/shiguang0116/sg-utils/tree/master

 

2 js工具类

/**
 * @description: js通用工具类
 * @author: guang.shi <https://blog.csdn.net/guang_s>
 * @date: 2018-12-13 15:38:27
 */
'use strict'

;(function(window) {
    var u = {}

    /** ******************************************* cookie 缓存 ***************************************************/

    u.cookie = {}

    /**
     * @description 设置缓存
     * @param {String} name 缓存数据的名字
     * @param {*} value 缓存数据的值
     * @param {Number} expiredays 缓存数据的时间(天),默认关闭浏览器时失效。1/24 表示一个小时,1/24/60 表示一分钟
     */
    u.cookie.set = function(name, value, expiredays) {
        var expires = ''
        if (expiredays) {
            var exdate = new Date()
            exdate.setTime(exdate.getTime() + expiredays * (24 * 3600 * 1000))
            expires = 'expires=' + exdate.toUTCString()
        }
        document.cookie = name + '=' + escape(value) + ';' + expires
    }

    /**
     * @description 获取缓存的数据
     * @param {String} name 要获取的数据对应的名字
     * @return {*}
     */
    u.cookie.get = function(name) {
        var arr = document.cookie.split('; ')
        for (var i = 0, len = arr.length; i < len; i++) {
            var temp = arr[i].split('=')
            if (temp[0] === name) return unescape(temp[1])
        }
        return null
    }

    /**
     * @description 删除缓存中某些数据
     * @param {String} name 要删除的数据对应的名字
     */
    u.cookie.remove = function(name) {
        u.cookie.set(name, '', -1)
    }

    /** ****************************************** localStorage 本地储存 **************************************************/

    u.storage = {}

    /**
     * @description 获取localStorage对象,兼容android(android原生系统老系统不支持localstorage)
     * @return localStorage对象
     */
    function uzStorage() {
        var ls = window.localStorage
        var isAndroid = (/android/gi).test(window.navigator.appVersion)
        if (isAndroid) ls = os.localStorage()
        return ls
    }

    /**
     * @description 设置本地储存
     * @param {String} key 储存的名字
     * @param {*} value 储存的值
     */
    u.storage.set = function(key, value) {
        var v = value
        var ls = uzStorage()
        if (typeof v === 'object') {
            v = JSON.stringify(v)
            v = 'obj-' + v
        }
        if (ls) ls.setItem(key, v)
    }

    /**
     * @description 获取本地储存的数据
     * @param {String} key 要获取的数据对应的名字
     * @return {*}
     */
    u.storage.get = function(key) {
        var ls = uzStorage()
        if (ls) {
            var v = ls.getItem(key)
            if (!v) return
            if (v.indexOf('obj-') === 0) return JSON.parse(v.slice(4))
            else return v
        }
    }

    /**
     * @description 删除本地储存中某些数据
     * @param {String} key 要删除的数据对应的名字
     */
    u.storage.remove = function(key) {
        var ls = uzStorage()
        if (ls && key) ls.removeItem(key)
    }

    /**
     * @description 清空本地储存的所有数据
     */
    u.storage.clear = function() {
        var ls = uzStorage()
        if (ls) ls.clear()
    }

    /** ****************************************** 数据类型 ***************************************************/

    /**
     * @description 判断元素是否为字符串
     * @param {*} source
     * @return {Boolen}
     */
    u.isString = function(source) {
        return typeof source === 'string'
    }

    /**
     * @description 判断元素是否为数组
     * @param {*} source
     * @return {Boolen}
     */
    u.isArray = function(source) {
        if (Array.isArray) return Array.isArray(source)
        else return source instanceof Array
    }

    /**
     * @description 判断元素是否为对象
     * @param {*} source
     * @return {Boolen}
     */
    u.isObject = function(source) {
        return Object.prototype.toString.call(source) === '[object Object]'
    }

    /**
     * @description 判断元素是否为函数
     * @param {*} source
     * @return {Boolen}
     */
    u.isFunction = function(source) {
        return typeof source === 'function'
    }

    /**
     * @description 判断元素是否为空
     * @param {*} source
     * @return {Boolen}
     */
    u.isEmpty = function(source) {
        if (source === undefined || source === null) return true
        if (u.isString(source) || u.isArray(source)) return source.length === 0
        if (u.isObject(source)) return JSON.stringify(source) === '{}'
        else return source.toString().length === 0
    }

    /**
     * @description 遍历数组、对象
     * @param {*} source 对象或数组
     * @param {Function} func 执行函数,function(i, item) 或 function(key, value)。执行函数返回 false 时,循环终止。
     */
    u.forEach = function(source, func) {
        if (source === undefined || source === null) return
        if (typeof (func) !== 'function') return

        var flag
        if (u.isObject(source)) {
            for (var key in source) {
                if (Object.prototype.hasOwnProperty.call(source, key)) {
                    flag = func.apply(window, [key, source[key]])
                    if (flag === false) break
                }
            }
        }
        else {
            for (var i = 0, len = source.length; i < len; i++) {
                flag = func.apply(window, [i, source[i]])
                if (flag === false) break
            }
        }
    }

    /**
     * @description 判断两个元素是否相等
     * @param {*} source1
     * @param {*} source2
     * @param {Boolean} ignoreCase 是否忽略掉大小写,不传则为false
     * @param {Boolean} ignoreSort 是否忽略排序,不传则为false
     * @return {Boolean} 是否相等
     */
    u.equal = function(source1, source2, ignoreCase, ignoreSort) {
        u.prop_equal = true
        // 同为数组或同为对象
        if ((u.isArray(source1) && u.isArray(source2)) || (u.isObject(source1) && u.isObject(source2))) {
            if (u.isArray(source1)) {
                if (source1.length !== source2.length) {
                    u.prop_equal = false
                    return false
                }
                if (ignoreSort) {
                    source1.sort()
                    source2.sort()
                }
            }
            else {
                if (u.length(source1) !== u.length(source2)) {
                    u.prop_equal = false
                    return false
                }
            }

            u.forEach(source1, function(ikey, item) {
                return u.equal(item, source2[ikey], ignoreCase, ignoreSort)
            })
            return u.prop_equal
        }
        // 字符串
        else {
            if (ignoreCase) {
                source1 = String.prototype.toLowerCase.call(source1.toString())
                source2 = String.prototype.toLowerCase.call(source2.toString())
            }
            if (source1 !== source2) u.prop_equal = false
            return u.prop_equal
        }
    }

    /**
     * @description 复制对象或数组(深拷贝)
     * @param {*} source 源数据
     * @return {*}
     */
    u.copy = function(source) {
        var ret
        if (u.isObject(source)) {
            ret = {}
            u.forEach(source, function(key, value) {
                if (u.isObject(value) || u.isArray(value)) {
                    value = u.copy(value)
                }
                ret[key] = value
            })
        }
        else if (u.isArray(source)) {
            ret = []
            u.forEach(source, function(i, item) {
                if (u.isObject(item) || u.isArray(item)) {
                    item = u.copy(item)
                }
                ret.push(item)
            })
        }
        else return source
        return ret
    }

    /**
     * @description 扩展对象(深拷贝)
     * @param {Object} target 目标对象
     * @param arguments 后面的属性会覆盖掉前面的
     */
    u.extend = function(target) {
        if (!u.isObject(target)) return

        for (var i = 1, len = arguments.length; i < len; i++) {
            var nextObj = arguments[i]
            if (u.isObject(nextObj)) {
                u.forEach(nextObj, function(key, value) {
                    if (u.isObject(value) || u.isArray(value)) {
                        value = u.copy(value)
                    }
                    target[key] = value
                })
            }
        }
        return target
    }

    /**
     * @description 判断元素的长度
     * @param {*} source
     * @return {Number}
     */
    u.length = function(source) {
        if (source === undefined || source === null) return 0
        if (u.isString(source) || u.isArray(source)) return source.length
        if (u.isObject(source)) {
            var len = 0
            u.forEach(source, function(key, value) {
                len++
            })
            return len
        }
    }

    /** ****************************************** string 字符串 ***************************************************/

    u.string = {}

    /**
     * @description 去除字符串前后空格
     * @param {String} str
     * @return {String} 去除空格之后的字符串
     */
    u.trim = function(str) {
        return str.replace(/(^\s*)|(\s*$)/g, '')
    }

    /**
     * @description 去除字符串所有空格
     * @param {String} str
     * @return {String} 去除空格之后的字符串
     */
    u.trimAll = function(str) {
        return str.replace(/\s*/g, '')
    }

    /**
    * @description 格式化字符串(文本替换)
    * @param {String} str 源字符串。如:'确定要{0}单据【{1}】吗?'
    * @param {*} args 要替换的参数。如:'删除', 'QZYDYJZB201901300002'
    * @return {String} 如:'确定要删除单据【QZYDYJZB201901300002】吗?'
    */
    u.string.format = function(str, args) {
        for (var i = 1, len = arguments.length; i < len; i++) {
            var reg = new RegExp('\\{' + (i - 1) + '\\}', 'gm')
            arguments[0] = arguments[0].replace(reg, arguments[i])
        }
        return arguments[0]
    }

    /**
     * @description 判断字符串是否以指定字符串开头
     * @param {String} str 源字符串
     * @param {String} searchString 要查询的字符串
     * @param {Boolean} ignoreCase 是否忽略大小写,默认false
     * @return {Boolean}
     */
    u.string.isStartWith = function(str, searchString, ignoreCase) {
        if (str === null || str === undefined) return false
        var preSubStr = str.substring(0, searchString.length) + ''
        if (ignoreCase) {
            preSubStr = preSubStr.toLowerCase()
            searchString = (searchString + '').toLowerCase()
        }
        return preSubStr === searchString
    }

    /**
     * @description 判断字符串是否以指定字符串结束
     * @param {String} str 源字符串
     * @param {String} searchString 要查询的字符串
     * @param {Boolean} ignoreCase 是否忽略大小写,默认false
     * @return {Boolean}
     */
    u.string.isEndWith = function(str, searchString, ignoreCase) {
        if (str === null || str === undefined) return false
        var lastSubStr = str.substring(str.length - searchString.length, str.length) + ''
        if (ignoreCase) {
            lastSubStr = lastSubStr.toLowerCase()
            searchString = (searchString + '').toLowerCase()
        }
        return lastSubStr === searchString
    }

    /**
     * @description 首字母小写
     * @param {String} str 源字符串
     * @return {String}
     */
    u.string.firstLowerCase = function(str) {
        if (u.isEmpty(str)) return ''
        return str.replace(/^\S/, function(s) { return s.toLowerCase() })
    }

    /**
     * @description 首字母大写
     * @param {String} str 源字符串
     * @return {String}
     */
    u.string.firstUpperCase = function(str) {
        if (u.isEmpty(str)) return ''
        return str.replace(/^\S/, function(s) { return s.toUpperCase() })
    }

    /**
     * @description 反转字符串的元素顺序
     * @param {String} str 源字符串
     * @return {String}
     */
    u.string.reverse = function(str) {
        if (u.isEmpty(str)) return ''

        var newStr = ''
        for (var i = str.length - 1; i >= 0; i--) {
            newStr += str[i]
        }
        return newStr
    }

    /**
     * @description 复制文本到剪切板
     * @param text {String} 需要复制的文本内容
     */
    u.string.copy = function(text) {
        var input = window.document.createElement('input')
        input.value = text
        input.select()
        window.document.execCommand('copy')
    }

    /**
     * @description 字母和数字混合的编号自加1(以数字结尾)
     * @param {String} code 编号。例:'XM0001'
     * @return {String} 编号+1。例:'XM0002'
     */
    u.string.getNext = function(code) {
        var part1, part2
        if (/[a-z]/i.test(code)) {
            var x = code.match(/[a-z]/ig)
            part1 = x.join('')
            part2 = code.substring(x.length)
        }
        else {
            part1 = ''
            part2 = code
        }
        var int = parseInt(part2)
        var zero = (part2 + '.').split(int + '.')[0]
        var newPart2 = zero + (int + 1).toString()
        return part1 + newPart2
    }

    /** ******************************************* number 数字 ***************************************************/

    u.number = {}

    /**
     * @description 转换成int类型
     * @param {String Number} input 输入的数
     * @param {Number} defaultValue 转换失败时的默认值
     * @return {int}
     */
    u.number.parseInt = function(input, defaultValue) {
        var value = parseInt(input)
        if (isNaN(value) || Infinity === value) {
            defaultValue = defaultValue || 0
            return defaultValue
        }
        return value
    }

    /**
     * @description 转换成float类型
     * @param {String Number} input 输入的数
     * @param {Number} defaultValue 转换失败时的默认值
     * @return {float}
     */
    u.number.parseFloat = function(input, defaultValue) {
        var value = parseFloat(input)
        if (isNaN(value) || Infinity === value) {
            defaultValue = defaultValue || 0
            return defaultValue
        }
        return value
    }

    /**
     * @description 保留几位小数(四舍五入法)
     * @param {String Number} input 输入的数
     * @param {String Number} digits 小数位数,默认 2
     * @return {String}
     */
    u.number.toFixed = function(input, digits) {
        digits = digits || 2
        input = u.number.parseFloat(input, 0)
        if (input === 0) return 0
        return input.toFixed(digits)
    }

    /**
     * @description 保留几位小数(进一法)
     * @param {String Number} input 输入的数
     * @param {String Number} digits 小数位数,默认 2
     * @return {String}
     */
    u.number.ceil = function(input, digits) {
        digits = digits || 2
        var num = Math.ceil(input * Math.pow(10, digits)) / Math.pow(10, digits)
        return u.number.toFixed(num, digits)
    }

    /**
     * @description 保留几位小数(舍一法)
     * @param {String Number} input 输入的数
     * @param {String Number} digits 小数位数,默认 2
     * @return {String}
     */
    u.number.floor = function(input, digits) {
        digits = digits || 2
        var num = Math.floor(input * Math.pow(10, digits)) / Math.pow(10, digits)
        return u.number.toFixed(num, digits)
    }

    /**
     * @description 获取两个数之间的随机数
     * @param {Number} min
     * @param {Number} max
     * @return {Number}
     */
    u.number.random = function(min, max) {
        return Math.floor(Math.random() * (max - min + 1)) + min
    }

    /**
     * @description 获取随机字符
     * @param {Number} len
     * @return {String}
     */
    u.number.getRandomStr = function(len) {
        var source = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
            source += 'abcdefghijklmnopqrstuvwxyz'
            source += '1234567890_$'
        var ret = '';
        for (var i = 0; i < len; i++) {
            var randomIndex = Math.floor(Math.random() * source.length);
            ret += source[randomIndex];
        }
        return ret
    }

    /**
     * @description 多个数相加
     * @param {Number String} args 加数
     * @return {Number} 和
     */
    u.number.add = function(args) {
        var m = 0
        var ret = 0
        for (var i = 0, len = arguments.length; i < len; i++) {
            arguments[i] = arguments[i].toString()
            try {
                m += arguments[i].split('.')[1].length
            }
            catch (e) {
                m += 0
            }
        }
        for (var j = 0, leng = arguments.length; j < leng; j++) {
            ret = arguments[j] * Math.pow(10, m) + ret
        }
        ret = ret / Math.pow(10, m)
        return ret
    }

    /**
     * @description 多个数相乘
     * @param {Number String} args 乘数
     * @return {Number} 积
     */
    u.number.mul = function(args) {
        var m = 0
        var ret = 1
        for (var i = 0, len = arguments.length; i < len; i++) {
            arguments[i] = arguments[i].toString()
            try {
                m += arguments[i].split('.')[1].length
            }
            catch (e) {
                m += 0
            }
            ret = arguments[i].replace('.', '') * ret
        }
        ret = ret / Math.pow(10, m)
        return ret
    }

    /**
     * @description 转化成货币格式(千分位)
     * @param {Number String} num 源数据
     * @param {String Number} digits 小数位数,默认不处理
     * @return {String}
     */
    u.number.toCurrency = function(input, digits) {
        input = input + ''
        if (digits) input = u.number.toFixed(input, digits)
        if (input.indexOf('.') === -1) input += '.'

        return input.replace(/(\d)(?=(\d{3})+\.)/g, function($1) {
            return $1 + ','
        }).replace(/\.$/, '')
    }

    /** ******************************************* array 数组 ***************************************************/

    u.array = {}

    /**
     * @description 检索数组(子元素为数组、对象、字符串等)
     * @param {Array} source [''] [[]] [{}]
     * @param {*} searchElement 当子元素为对象时,只用匹配该对象的某一个(几个)属性即可
     * @return {Number} 索引 或 -1
     */
    u.array.indexOf = function(source, searchElement) {
        var index = -1
        // 子元素为对象
        if (u.isObject(searchElement)) {
            u.forEach(source, function(i, item) {
                var isHas = true
                u.forEach(searchElement, function(searchKey, searchValue) {
                    if (item[searchKey]) {
                        if (!u.equal(item[searchKey], searchValue)) {
                            isHas = false
                            return false
                        }
                    }
                    else {
                        isHas = false
                    }
                })
                if (isHas) {
                    index = i
                    return false
                }
            })
            return index
        }
        // 子元素为数组
        if (u.isArray(searchElement)) {
            u.forEach(source, function(i, item) {
                if (u.equal(item, searchElement)) {
                    index = i
                    return false
                }
            })
            return index
        }
        // 子元素为字符串
        else {
            return source.indexOf(searchElement)
        }
    }

    /**
     * @description 向数组的末尾添加一个或多个元素,并返回新的长度
     * @param {Array} target 目标数组
     * @param {Array} array 要添加的数组
     * @return {Number} 新数组的长度
     */
    u.array.push = function(target, array) {
        if (u.isEmpty(array)) return target
        if (!u.isArray(array)) array = [array]
        return Array.prototype.push.apply(target, array)
    }

    /**
     * @description 对数组排序
     * @param {Array} array 源数组 [{}]
     * @param {String} sortKey 排序字段
     * @param {String} order 排序方式,asc升序,desc降序,默认为升序
     * @return {Array} 排序后的新数组
     */
    u.array.sort = function(array, sortKey, order) {
        if (u.isEmpty(array)) return []
        var ret = array.concat([])
        order = order || 'asc'
        ret.sort(function(a, b) {
            var aVal = a[sortKey]
            var bVal = b[sortKey]
            if (aVal > bVal) return order === 'asc' ? 1 : -1
            else if (aVal < bVal) return order === 'asc' ? -1 : 1
            return 0
        })
        return ret
    }

    /**
     * @description 数组去重(子元素为数组、对象、字符串等)
     * @param {Array} array [''] [[]] [{}]
     * @param {String Array} keys 根据属性去重(针对子元素是对象时)
     * @param {Boolean} ignoreSort 是否忽略排序(针对子元素是数组时)
     * @return {Array}
     */
    u.array.unique = function(array, keys, ignoreSort) {
        var ret = []
        u.forEach(array, function(i, item) {
            if (keys && u.isObject(item)) { // 根据属性去重,去掉排在末位的对象
                if (!u.isArray(keys)) keys = [keys]
                var searchObj = {}
                u.forEach(keys, function(i, selectKey) {
                    searchObj[selectKey] = item[selectKey]
                })
                if (u.array.indexOf(ret, searchObj) === -1) ret.push(item)
            }
            else if (ignoreSort && u.isArray(item)) {
                if (u.array.indexOf(ret, item.sort()) === -1) ret.push(item)
            }
            else {
                if (u.array.indexOf(ret, item) === -1) ret.push(item)
            }
        })
        return ret
    }

    /**
     * @description 筛选出符合条件的数组,生成新的数组
     * @param {Array} source 原数组 [{}]
     * @param {Object} filterProperty 条件对象 { status: ['1','2'] }
     * @param {Boolean} getDeleteData 是否返回被过滤掉的数组,默认false
     * @return {Array} 符合条件的数据 或 不符合条件的数据
     */
    u.array.filter = function(source, filterProperty, getDeleteData) {
        if (u.isEmpty(source) || u.isEmpty(filterProperty)) return []

        var ret = []
        var retByDelete = []
        u.forEach(source, function(i, item) {
            var equal = true
            u.forEach(filterProperty, function(filterKey, filterValue) {
                var itemValue = item[filterKey]
                if (!u.isArray(filterValue)) filterValue = [filterValue]
                if (filterValue.indexOf(itemValue) === -1) {
                    equal = false
                    return false
                }
            })
            if (equal) ret.push(item)
            else retByDelete.push(item)
        })
        if (getDeleteData) return retByDelete
        return ret
    }

    /**
     * @description 选择数组的子元素(对象)的一个(多个)属性
     * @param {Array} source 源数组 [{}]
     * @param {String Array} keys 属性(集合)
     * @return {Array} 新数组 [''] 或 [{}]
     */
    u.array.select = function(source, keys) {
        if (u.isEmpty(source) || u.isEmpty(keys)) return source

        var ret = []
        u.forEach(source, function(i, item) {
            if (u.isArray(keys)) {
                var obj = {}
                u.forEach(keys, function(j, key) {
                    obj[key] = u.object.getValue(item, key)
                })
                ret.push(obj)
            }
            else {
                ret.push(u.object.getValue(item, keys))
            }
        })
        return ret
    }

    /**
     * @description 合并两个数组,生成新的数组
     * @param {Array} source 原数组
     * @param {Array} array 待合并的数组
     * @param {String Array} keys 根据属性去重(针对子元素是对象时)
     * @param {Boolean} ignoreSort 是否忽略排序(针对子元素是数组时)
     * @return {Object}
     */
    u.array.concat = function(source, array, keys, ignoreSort) {
        if (u.isEmpty(source)) return array
        if (u.isEmpty(array)) return source

        var ret = []
        ret = source.concat(array)
        ret = u.array.unique(ret, keys, ignoreSort)
        return ret
    }

    /**
     * @description 对数组中的元素进行分组
     * @param {Array} array 数组
     * @param {Array} fields 分组的依据字段
     * @return {Array} 分组后的新数组
     */
    u.array.group = function(array, fields) {
        if (u.isEmpty(array) || u.isEmpty(fields)) return null

        var ret = []
        u.forEach(array, function(i, item) {
            if (!u.isArray(fields)) fields = [fields]

            var itemGroup = {}
            u.forEach(fields, function(i, field) {
                itemGroup[field] = item[field]
            })
            var index = u.array.indexOf(ret, itemGroup)
            if (index === -1) {
                itemGroup.group = []
                itemGroup.group.push(item)
                ret.push(itemGroup)
            }
            else {
                ret[index].group.push(item)
            }
        })
        return ret
    }

    /**
     * @description arraybuffer转string
     * @param source 原数据
     * @returns {string}
     */
     u.array.arraybufferToString = function(source) {
      const enc = new TextDecoder("utf-8");
      const uint8_msg = new Uint8Array(source);
      const decodedString = enc.decode(uint8_msg)
      return decodedString
    }

    /**
     * @description 删除数组中指定的元素或不合法的值(undefined, null, '')
     * @param {Array} source 原数组
     * @param {*} values 被删除的元素集合,不传则删除不合法的值
     */
    u.array.remove = function(array, values) {
        if (u.isEmpty(array)) return []
        // 删除不合法的值
        if (u.isEmpty(values)) {
            var i = array.length
            while (i--) {
                var item = array[i]
                if (item === null || item === undefined || item === '') {
                    array.splice(i, 1)
                }
            }
        }
        // 删除指定的元素
        else {
            if (!u.isArray(values)) values = [values]
            u.forEach(values, function(i, value) {
                var index = u.array.indexOf(array, value)
                if (index > -1) array.splice(index, 1)
            })
        }
    }

    /**
     * @description 清空数组中的元素
     * @param {Array} array 源数组
     */
    u.array.clear = function(array) {
        if (u.isEmpty(array)) return

        array.splice(0, array.length)
    }

    /** ******************************************* object 对象 ***************************************************/

    u.object = {}

    /**
     * @description 获取对象的属性集合
     * @param {Object} obj 源对象
     * @return {Array} 属性名的数组
     */
    u.object.keys = function(obj) {
        if (u.isEmpty(obj)) return []

        return Object.keys(obj)
    }

    /**
     * @description 获取对象属性的值 的集合
     * @param {Object} obj 源对象
     * @return {Array} 属性值的数组
     */
    u.object.values = function(obj) {
        if (u.isEmpty(obj)) return []

        var ret = []
        try {
            ret = Object.values(obj)
        }
        catch (e) {
            u.forEach(obj, function(key, value) {
                ret.push(value)
            })
        }
        return ret
    }

    /**
     * @description 合并对象( Object.assign() 拷贝的是属性值,不属于深拷贝。深拷贝请参考 u.extend() )
     * @param {Object} target 目标对象
     * @param arguments 后面的属性会覆盖掉前面的
     */
    u.object.assign = function(target) {
        if (!u.isObject(target)) return

        try {
            Object.assign.apply(window, arguments)
        }
        catch (e) {
            for (var i = 1, len = arguments.length; i < len; i++) {
                var nextObj = arguments[i]
                if (u.isObject(nextObj)) {
                    u.forEach(nextObj, function(key, value) {
                        target[key] = value
                    })
                }
            }
        }
    }

    /**
     * @description 选择对象中的一个(多个)属性
     * @param {Object} obj 源对象
     * @param {String Array} keys 属性名集合
     * @return {Object} 新对象
     */
    u.object.select = function(obj, keys) {
        if (u.isEmpty(obj) || u.isEmpty(keys)) return {}

        var ret = {}
        if (!u.isArray(keys)) keys = [keys]
        u.forEach(keys, function(i, key) {
            ret[key] = obj[key]
        })
        return ret
    }

    /**
     * @description 修改属性名
     * @param {Object} obj 要修改的对象
     * @param {String} oldKey 原来的属性名
     * @param {String} newKey 新的属性名
     * @param {Boolean} keepOld 是否保留旧的属性,默认为false
     */
    u.object.rename = function(obj, oldKey, newKey, keepOld) {
        if (u.isEmpty(obj)) return

        if (obj[oldKey]) {
            obj[newKey] = obj[oldKey]
            if (!keepOld) u.object.remove(obj, oldKey)
        }
    }

    /**
     * @description 获取对象的属性值(支持多层数据)
     * @param {Object} obj 对象
     * @param {String} propertyName 属性名 'data.child.name'
     * @param {Boolean} ignoreCase 忽略属性名大小写,默认false
     */
    u.object.getValue = function(obj, propertyName, ignoreCase) {
        var propertyValue = null
        if (!obj) return propertyValue
        if (u.isEmpty(propertyName)) return propertyValue

        var pointIndex = propertyName.indexOf('.')
        if (pointIndex > -1) {
            obj = obj[propertyName.substring(0, pointIndex)]
            return u.object.getValue(obj, propertyName.substring(pointIndex + 1), ignoreCase)
        }
        else {
            u.forEach(obj, function(key, value) {
                if (u.equal(key, propertyName, ignoreCase)) {
                    propertyValue = value
                    return false
                }
            })
        }
        return propertyValue
    }

    /**
     * @description 序列化对象
     * @param {Object} paramObj 源对象
     * @return {String}
     */
    u.object.serialize = function(paramObj) {
        var name, value, fullSubName, subName, subValue, innerObj
        var ret = ''
        for (name in paramObj) {
            value = paramObj[name]
            if (value instanceof Array) {
                for (var i = 0; i < value.length; ++i) {
                    subValue = value[i]
                    fullSubName = name + '[' + i + ']'
                    innerObj = {}
                    innerObj[fullSubName] = subValue
                    ret += u.object.serialize(innerObj) + '&'
                }
            }
            else if (value instanceof Object) {
                for (subName in value) {
                    subValue = value[subName]
                    fullSubName = name + '[' + subName + ']'
                    innerObj = {}
                    innerObj[fullSubName] = subValue
                    ret += u.object.serialize(innerObj) + '&'
                }
            }
            else if (value !== undefined && value !== null) { ret += encodeURIComponent(name) + '=' + encodeURIComponent(value) + '&' }
        }
        ret = ret.substring(0, ret.length - 1)
        return ret
    }

    /**
     * @description 删除对象中指定的属性 或 值为空的属性(undefined, null, '')
     * @param {Object} obj 源对象
     * @param {String Array} keys 属性名集合,不传则删除为空的属性
     * @return {Object}
     */
    u.object.remove = function(obj, keys) {
        if (u.isEmpty(obj)) return obj

        var ret = {}
        var es6 = true
        if (!u.isEmpty(keys)) {
            if (!u.isArray(keys)) keys = [keys]
            u.forEach(keys, function(i, key) {
                try {
                    delete obj[key]
                }
                catch (e) {
                    es6 = false
                    return false
                }
            })
            if (es6) return obj
            else {
                u.forEach(obj, function(key, value) {
                    if (keys.indexOf(key) === -1) {
                        ret[key] = value
                    }
                })
                return ret
            }
        }
        else {
            u.forEach(obj, function(key, value) {
                var wrongful = (value === null || value === undefined || value === '')
                try {
                    if (wrongful) delete obj[key]
                }
                catch (e) {
                    es6 = false
                    if (!wrongful) {
                        ret[key] = value
                    }
                }
            })
            if (es6) return obj
            else return ret
        }
    }

    /**
     * @description 清空对象
     * @param {Object} obj 源对象
     * @param {Array} keys 属性名集合,不传则清空全部属性
     * @return {Object} 清空后的对象
     */
    u.object.clear = function(obj, keys) {
        if (u.isEmpty(obj)) return {}

        if (keys) {
            if (!u.isArray(keys)) keys = [keys]
            u.forEach(keys, function(i, key) {
                obj[key] = ''
            })
        }
        else {
            u.forEach(obj, function(key, value) {
                obj[key] = ''
            })
        }
        return obj
    }

    /** ****************************************** JSON **************************************************/

    u.json = {}

    /**
     * @description json格式转树状结构
     * @param {JSON} data json数组 [{},{}]
     * @param {String} id id 的字段名
     * @param {String} pid 父id 的字段名
     * @param {String} child child 的字段名
     * @return {Array}
     */
    u.json.toTreeData = function(data, id, pid, child) {
        var i
        var ret = []
        var hash = {}
        var len = (data || []).length
        for (i = 0; i < len; i++) {
            hash[data[i][id]] = data[i]
        }
        for (i = 0; i < len; i++) {
            var pidO = hash[data[i][pid]]
            if (pidO) {
                pidO[child] = pidO[child] || []
                pidO[child].push(data[i])
            }
            else {
                ret.push(data[i])
            }
        }
        return ret
    }

    /** ****************************************** date 时间 **************************************************/

    u.date = {}

    /**
     * @description 获取需要的时间格式
     * @param {Date} time 时间、时间字符串、时间戳
     * @param {String} format 时间格式,默认'YYYY-MM-DD'。如果是'星期WW',则返回(如:'星期日')
     * @return {String} 格式化后的时间
     */
    u.date.format = function(time, format) {
        time = time ? new Date(time) : new Date()
        format = format || 'YYYY-MM-DD'
        function tf(i) { return (i < 10 ? '0' : '') + i }
        return format.replace(/YYYY|MM|DD|hh|mm|ss|WW/g, function(a) {
            switch (a) {
                    case 'YYYY':
                        return tf(time.getFullYear())
                    case 'MM':
                        return tf(time.getMonth() + 1)
                    case 'DD':
                        return tf(time.getDate())
                    case 'mm':
                        return tf(time.getMinutes())
                    case 'hh':
                        return tf(time.getHours())
                    case 'ss':
                        return tf(time.getSeconds())
                    case 'WW':
                        return ['', '', '', '', '', '', ''][time.getDay()]
            }
        })
    }

    /**
     * @description 获取前后几月的日期
     * @param {Number} MM 前后几月(正数代表后几个月,负数代表前几个月),默认上个月(-1)
     * @param {Date} time 时间、时间字符串、时间戳
     * @param {String} format 时间格式,默认'YYYY-MM-DD'
     * @return {String} 格式化后的时间
     */
    u.date.otherMonth = function(MM, time, format) {
        MM = !isNaN(parseInt(MM)) ? parseInt(MM) : -1
        time = time ? new Date(time) : new Date()

        var oldDate = time.getDate()
        time.setMonth(time.getMonth() + MM)
        var newDate = time.getDate()
        if (newDate < oldDate) {
            time.setMonth(time.getMonth(), 0)
        }
        return u.date.format(time, format)
    }

    /**
     * @description 某一月的第一天
     * @param {Number} MM 前后几月(正数代表后几个月,负数代表前几个月),默认本月(0)
     * @param {Date} time 时间、时间字符串、时间戳
     * @param {String} format 时间格式,默认'YYYY-MM-DD'
     * @return {String} 格式化后的时间
     */
    u.date.startOfMonth = function(MM, time, format) {
        MM = !isNaN(parseInt(MM)) ? parseInt(MM) : 0
        time = time ? new Date(time) : new Date()

        time.setMonth(time.getMonth() + MM, 1)
        return u.date.format(time, format)
    }

    /**
     * @description 某一月的最后一天
     * @param {Number} MM 前后几月(正数代表后几个月,负数代表前几个月),默认本月(0)
     * @param {Date} time 时间、时间字符串、时间戳
     * @param {String} format 时间格式,默认'YYYY-MM-DD'
     * @return {String} 格式化后的时间
     */
    u.date.endOfMonth = function(MM, time, format) {
        MM = !isNaN(parseInt(MM)) ? parseInt(MM) : 0
        time = time ? new Date(time) : new Date()

        time.setMonth(time.getMonth() + MM + 1, 0)
        return u.date.format(time, format)
    }

    /**
     * @description 某一周的第一天(默认星期一)
     * @param {Number} WW 前后几周(正数代表后几周,负数代表前几周),默认本周(0)
     * @param {Date} time 时间、时间字符串、时间戳
     * @param {String} format 时间格式,默认'YYYY-MM-DD'
     * @return {String}
     */
    u.date.startOfWeek = function(WW, time, format) {
        WW = !isNaN(parseInt(WW)) ? parseInt(WW) : 0
        time = time ? new Date(time) : new Date()

        var curWW = time.getDay()
        curWW = curWW === 0 ? 7 : curWW
        time.setDate(time.getDate() + 7 * WW - (curWW - 1))
        return u.date.format(time, format)
    }

    /**
     * @description 某一周的最后一天(默认星期日)
     * @param {Number} WW 前后几周(正数代表后几周,负数代表前几周),默认本周(0)
     * @param {Date} time 时间、时间字符串、时间戳
     * @param {String} format 时间格式,默认'YYYY-MM-DD'
     * @return {String}
     */
    u.date.endOfWeek = function(WW, time, format) {
        WW = !isNaN(parseInt(WW)) ? parseInt(WW) : 0
        time = time ? new Date(time) : new Date()

        var curWW = time.getDay()
        curWW = curWW === 0 ? 7 : curWW
        time.setDate(time.getDate() + 7 * WW - (curWW - 1) + 6)
        return u.date.format(time, format)
    }

    /**
     * @description 前后几天的日期(几小时、几分钟均可)
     * @param {Number} DD 前后几天(正数代表后几天,负数代表前几天),默认过去一周的日期(-6)
     * @param {Date} time 时间、时间字符串、时间戳
     * @param {String} format 时间格式,默认'YYYY-MM-DD'
     * @return {String} 格式化后的时间
     */
    u.date.otherDate = function(DD, time, format) {
        DD = !isNaN(parseFloat(DD)) ? parseFloat(DD) : -6
        time = time ? new Date(time) : new Date()

        time.setTime(time.getTime() + DD * (24 * 3600 * 1000))
        return u.date.format(time, format)
    }

    /**
     * @description 两个日期之间相差多少天
     * @param {Date} date1
     * @param {Date} date2
     * @return {Number}
     */
    u.date.howManyDays = function(date1, date2) {
        var ret = ''
        var timestamp1 = Date.parse(date1)
        var timestamp2 = Date.parse(date2)
        var dateSpan = Math.abs(timestamp2 - timestamp1)
        ret = Math.floor(dateSpan / (24 * 3600 * 1000))
        return ret
    }

    /**
     * @description 两个日期之间相差多少月
     * @param {Date} date1
     * @param {Date} date2
     * @return {Number}
     */
    u.date.howManyMonths = function(date1, date2) {
        var months1, months2, ret
        date1 = new Date(date1)
        date2 = new Date(date2)
        months1 = date1.getFullYear() * 12 + date1.getMonth() + 1
        months2 = date2.getFullYear() * 12 + date2.getMonth() + 1
        ret = Math.abs(months1 - months2)
        return ret
    }

    /**
     * @description 查询两个日期之间的所有日期
     * @param {Date} date1
     * @param {Date} date2
     * @return {Array}
     */
    u.date.getDatesBetween = function(date1, date2, format) {
        format = format || 'YYYY-MM-DD'

        var start, len
        var ret = []
        start = Date.parse(date1) < Date.parse(date2) ? date1 : date2
        // 所有天
        if (format.indexOf('DD') > -1) {
            len = u.date.howManyDays(date1, date2)
            for (var i = 0; i <= len; i++) {
                ret.push(u.date.otherDate(i, start, format))
            }
        }
        // 所有月
        else {
            len = u.date.howManyMonths(date1, date2)
            for (var j = 0; j <= len; j++) {
                ret.push(u.date.otherMonth(j, start, format))
            }
        }
        return ret
    }

    /** ******************************************* browser 浏览器/手机端 ***************************************************/

    u.browser = {}

    var userAgent = window.navigator.userAgent // 获取浏览器的userAgent字符串

    /**
     * @description 判断当前浏览类型
     * @return {String} ie7
     * @return {String} ie8
     * @return {String} ie9
     * @return {String} ie10
     * @return {String} ie11
     * @return {String} edge
     * @return {String} chrome
     * @return {String} safari
     * @return {String} firefox
     * @return {String} opera
     */
    u.browser.type = function() {
        if (u.browser.isIE()) {
            var reIE = new RegExp('MSIE (\\d+\\.\\d+);')
            reIE.test(userAgent)
            var fIEVersion = parseFloat(RegExp['$1'])
            if (fIEVersion === 7) return 'ie7'
            if (fIEVersion === 8) return 'ie8'
            if (fIEVersion === 9) return 'ie9'
            if (fIEVersion === 10) return 'ie10'
            if (fIEVersion === 11) return 'ie11'
            else return false // IE版本过低
        }

        if (u.browser.isFirefox()) return 'firefox'
        if (u.browser.isOpera()) return 'opera'
        if (u.browser.isSafari()) return 'safari'
        if (u.browser.isChrome()) return 'chrome'
        if (u.browser.isEdge()) return 'edge'
    }
    /**
     * @description 判断是否是IE浏览器
     */
    u.browser.isIE = function() {
        return userAgent.indexOf('compatible') > -1 && userAgent.indexOf('MSIE') > -1 && !u.browser.isOpera()
    }
    /**
     * @description 判断是否IE的Edge浏览器
     */
    u.browser.isEdge = function() {
        return userAgent.indexOf('Windows NT 6.1; Trident/7.0;') > -1 && !u.browser.isIE()
    }
    /**
     * @description 判断是否Safari浏览器
     */
    u.browser.isSafari = function() {
        return userAgent.indexOf('Safari') > -1 && userAgent.indexOf('Chrome') === -1
    }
    /**
     * @description 判断是否是Chrome浏览器
     */
    u.browser.isChrome = function() {
        return userAgent.indexOf('Chrome') > -1 && userAgent.indexOf('Safari') > -1
    }
    /**
     * @description 判断是否Firefox浏览器
     */
    u.browser.isFirefox = function() {
        return userAgent.indexOf('Firefox') > -1
    }
    /**
     * @description 判断是否Opera浏览器
     */
    u.browser.isOpera = function() {
        return userAgent.indexOf('Opera') > -1
    }
    /**
     * @description 判断是否是微信浏览器
     */
    u.browser.isWechat = function() {
        var ua = userAgent.toLowerCase()
        if (ua.match(/MicroMessenger/i) === 'micromessenger') return true
        else return false
    }
    /**
     * @description 判断是否是android
     */
    u.browser.isAndroid = function() {
        return (/android/gi).test(navigator.appVersion)
    }
    /**
     * @description 判断是否是ios
     */
    u.browser.isIOS = function() {
        if (userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)) return true
        else return false
    }
    /**
     * @description 判断是否是移动端
     */
    u.browser.isMobile = function() {
        if (userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i)) {
            return true
        }
        else return false
    }
    /**
     * @description 根据手机设备调取相机
     * <input type="file" capture="camera" accept="image/*" multiple="multiple">
     * Android:加上 capture 属性,可以同时调用相册和相机,否则只能调用相册;
     * IOS:    加上 capture 属性,只能调相机,反之可以同时调用相册和相机。二者在 capture="camera" 上是相反的
     */
    u.browser.callCamera = function() {
        if (u.browser.isIOS()) {
            var file = window.document.querySelectorAll('input[capture=camera]')
            for (var i = 0; i < file.length; i++) {
                file[i].removeAttribute('capture')
            }
        }
    }

    /** ****************************************** base64 **************************************************/

    u.base64 = {}

    // base64 前缀
    u.base64.prefix = 'data:image/png;base64,'

    /**
     * @description base64 编码
     * @param {input}
     */
    u.base64.encrypt = function(input) {
        var str = CryptoJS.enc.Utf8.parse(input)
        var base64 = CryptoJS.enc.Base64.stringify(str)
        return base64
    }

    /**
     * @description base64 解码
     * @param {input}
     */
    u.base64.decrypt = function(input) {
        return CryptoJS.enc.Base64.parse(input).toString(CryptoJS.enc.Utf8)
    }

    /** ******************************************* url 路径处理 ***************************************************/

    u.url = {}

    /**
     * @description 获取url参数
     * @param {String} name 参数名,不传则返回所有参数的对象
     * @return {String Object}
     */
    u.url.getParams = function(name) {
        var search = window.location.search.substring(1)
        if (!search) {
            search = window.location.hash.split('?')[1]
        }
        if (search) {
            var obj = JSON.parse('{"' + decodeURIComponent(search).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g, '":"') + '"}')
            return name ? obj[name] : obj
        }
    }

    /**
     * @description 页面跳转(参数传递)
     * @param {String} url 目标地址
     * @return {Object} param 参数对象
     */
    u.url.jump = function(url, param) {
        if (param) {
            url = url + '?' + u.object.serialize(param)
        }
        window.location.href = url
    }

    /**
     * @description 页面跳转(需跳回)
     * @param {String} url 目标地址
     * @param {String} referrerURL 源地址,默认当前页面的地址
     */
    u.url.jumpFromReferrer = function(url, referrerURL) {
        referrerURL = referrerURL || window.location.href
        window.location.href = url + '?' + encodeURIComponent('referrer=' + referrerURL)
    }

    /**
     * @description 跳回到之前的页面
     */
    u.url.jumpToReferrer = function() {
        var search = decodeURIComponent(window.location.search)
        var url = search.split('referrer=')[1]
        window.location.href = url
    }

    /** ******************************************* validate 验证 ***************************************************/

    u.validate = {}

    /**
     * @description 验证是否必填
     * @param {String} input
     */
    u.validate.required = function(input) {
        return !u.isEmpty(input)
    }
    // 验证手机号码
    u.validate.mobile = function(input) {
        return /^1[34578][0-9]{9}$/.test(input)
    }
    // 验证座机号码
    u.validate.fixedPhone = function(input) {
        return /^((0\d{2,3})-)(\d{7,8})(-(\d{3,}))?$/.test(input)
    }
    // 验证手机或者座机号码
    u.validate.phone = function(input) {
        return u.validate.mobile(input) || u.validate.fixedPhone(input)
    }
    // 验证邮箱号码
    u.validate.email = function(input) {
        // ^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$
        return /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((\.[a-zA-Z0-9_-]{2,3}){1,2})$/.test(input)
    }
    // 验证身份证号码
    u.validate.IDcard = function(input) {
        return /^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$|^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(input)
    }
    // 验证 url
    u.validate.url = function(input) {
        return /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/.test(input)
    }
    // 验证数字
    u.validate.number = function(input) {
        return /^(\+|-)?((0)|([1-9][0-9]*))$|^(\+|-)?((0)|([1-9][0-9]*)).[0-9]+$/.test(input)
    }
    // 验证整数(不包括0)
    u.validate.integer = function(input) {
        return /^(\+|-)?[1-9][0-9]*$/.test(input)
    }
    // 验证正整数(不包括0)
    u.validate.positiveInteger = function(input) {
        return /^\+?[1-9][0-9]*$/.test(input)
    }
    // 验证正数(不包括0)
    u.validate.positive = function(input) {
        return /^[1-9][0-9]*$|^((0)|([1-9][0-9]*)).[0-9]+$/.test(input)
    }
    // 验证精确到几位小数的正数(不包括0)
    u.validate.positiveToFixed = function(input, digits) {
        digits = digits || 2
        var reg = new RegExp('^[1-9][0-9]*$|^((0)|([1-9][0-9]*)).[0-9]{' + digits + '}$')
        return reg.test(input)
    }
    // 验证是否相等
    u.validate.equal = function(input1, input2) {
        return u.equal(input1, input2)
    }

    /** ******************************************* event 事件工具集 ***************************************************/

    u.event = {} // event(事件)工具集,来源:github.com/markyun

    /**
     * @description 页面加载完成后
     * @param {Function} fn
     */
    u.event.ready = function(fn) {
        if (fn == null) {
            fn = document
        }
        var oldonload = window.onload
        if (typeof window.onload !== 'function') {
            window.onload = fn
        }
        else {
            window.onload = function() {
                oldonload()
                fn()
            }
        }
    }

    /**
     * @description 添加事件
     * @param {Object} element 必需。描述事件名称的字符串。注意:不要使用 "on" 前缀。例如,使用 "click" 来取代 "onclick"。
     * @param {String} type 必需。描述事件名称的字符串。注意:不要使用 "on" 前缀。例如,使用 "click" 来取代 "onclick"。
     * @param {Function} handler 必需。描述了事件触发后执行的函数。
     * @param {Boolean} useCapture 可选。true: 事件在捕获阶段执行; false(默认): 事件在冒泡阶段执行
     */
    u.event.add = function(element, type, handler, useCapture) {
        if (element.addEventListener) {
            element.addEventListener(type, handler, useCapture)
        }
        else if (element.attachEvent) {
            element.attachEvent('on' + type, function() {
                handler.call(element)
            })
        }
        else {
            element['on' + type] = handler
        }
    }

    /**
     * @description 移除事件
     * @param {Object} element 必需。描述事件名称的字符串。注意:不要使用 "on" 前缀。例如,使用 "click" 来取代 "onclick"。
     * @param {String} type 必需。描述事件名称的字符串。注意:不要使用 "on" 前缀。例如,使用 "click" 来取代 "onclick"。
     * @param {Function} handler 必需。描述了事件触发后执行的函数。
     * @param {Boolean} useCapture 可选。true: 事件在捕获阶段执行; false(默认): 事件在冒泡阶段执行
     */
    u.event.remove = function(element, type, handler, useCapture) {
        if (element.removeEventListener) {
            element.removeEventListener(type, handler, useCapture)
        }
        else if (element.datachEvent) {
            element.detachEvent('on' + type, handler)
        }
        else {
            element['on' + type] = null
        }
    }

    /**
     * @description 阻止事件 (主要是事件冒泡,因为IE不支持事件捕获)
     * @param {Object} ev 事件对象
     */
    u.event.stop = function(ev) {
        if (ev.stopPropagation) {
            ev.stopPropagation()
        }
        else {
            ev.cancelBubble = true
        }
    }

    /**
     * @description 取消事件的默认行为
     * @param {Object} ev 事件对象
     */
    u.event.prevent = function(ev) {
        if (ev.preventDefault) {
            ev.preventDefault()
        }
        else {
            ev.returnValue = false
        }
    }

    /**
     * @description 获取事件目标
     * @param {Object} ev 事件对象
     */
    u.event.target = function(ev) {
        return ev.target || ev.srcElement
    }

    /**
     * @description 获取event对象的引用,取到事件的所有信息,确保随时能使用event
     * @param {Object} ev 事件对象
     */
    u.event.get = function(ev) {
        ev = ev || window.event
        if (!ev) {
            var c = u.event.get.caller
            while (c) {
                ev = c.arguments[0]
                if (ev && Event === ev.constructor) {
                    break
                }
                c = c.caller
            }
        }
        return ev
    }

    /** ******************************************* layout 布局 ***************************************************/

    u.layout = {}

    /**
     * @description 固定页脚。页面内容的高度小于浏览器高度时,将页脚定位在底部;否则,不定位。(适用于任何一个div盒子里的页脚固定)
     * @param {Dom Object} oBody  被定位元素的父级元素 的dom对象
     * @param {Dom Object} oFooter  被定位元素的dom对象
     */
    u.layout.fixedFooter = function(oBody, oFooter) {
        var sh, fh
        var h = 0
        sh = document.documentElement.clientHeight // 页面对象高度(即BODY对象高度加上Margin高)
        fh = oFooter.offsetHeight
        if (u.browser.isWechat()) h = 64 // 微信浏览器要去除状态栏的高度
        // 处理父元素
        oBody.style.position = 'relative'
        oBody.style.minHeight = (sh - h) + 'px'
        oBody.style.paddingBottom = fh + 'px'
        oBody.style.boxSizing = 'border-box'
        // 处理页脚元素
        oFooter.style.position = 'absolute'
        oFooter.style.bottom = '0' + 'px'
    }

    window._util = u
})(window)

// module.exports = u