541 lines
14 KiB
JavaScript
541 lines
14 KiB
JavaScript
let Anot = function(el) {
|
||
//创建jQuery式的无new 实例化结构
|
||
return new Anot.init(el)
|
||
}
|
||
|
||
/*视浏览器情况采用最快的异步回调*/
|
||
Anot.nextTick = new function() {
|
||
// jshint ignore:line
|
||
let tickImmediate = window.setImmediate
|
||
let tickObserver = window.MutationObserver
|
||
if (tickImmediate) {
|
||
return tickImmediate.bind(window)
|
||
}
|
||
|
||
let queue = []
|
||
function callback() {
|
||
let n = queue.length
|
||
for (let i = 0; i < n; i++) {
|
||
queue[i]()
|
||
}
|
||
queue = queue.slice(n)
|
||
}
|
||
|
||
if (tickObserver) {
|
||
let node = document.createTextNode('anot')
|
||
new tickObserver(callback).observe(node, { characterData: true }) // jshint ignore:line
|
||
let bool = false
|
||
return function(fn) {
|
||
queue.push(fn)
|
||
bool = !bool
|
||
node.data = bool
|
||
}
|
||
}
|
||
|
||
return function(fn) {
|
||
setTimeout(fn, 4)
|
||
}
|
||
}() // jshint ignore:line
|
||
|
||
/*********************************************************************
|
||
* Anot的静态方法定义区 *
|
||
**********************************************************************/
|
||
|
||
Anot.type = function(obj) {
|
||
//取得目标的类型
|
||
if (obj == null) {
|
||
return String(obj)
|
||
}
|
||
// 早期的webkit内核浏览器实现了已废弃的ecma262v4标准,可以将正则字面量当作函数使用,因此typeof在判定正则时会返回function
|
||
return typeof obj === 'object' || typeof obj === 'function'
|
||
? class2type[serialize.call(obj)] || 'object'
|
||
: typeof obj
|
||
}
|
||
|
||
Anot.PropsTypes = function(type) {
|
||
this.type = 'PropsTypes'
|
||
this.checkType = type
|
||
}
|
||
|
||
Anot.PropsTypes.prototype = {
|
||
toString: function() {
|
||
return ''
|
||
},
|
||
check: function(val) {
|
||
this.result = Anot.type(val)
|
||
return this.result === this.checkType
|
||
},
|
||
call: function() {
|
||
return this.toString()
|
||
}
|
||
}
|
||
|
||
Anot.PropsTypes.isString = function() {
|
||
return new this('string')
|
||
}
|
||
|
||
Anot.PropsTypes.isNumber = function() {
|
||
return new this('number')
|
||
}
|
||
|
||
Anot.PropsTypes.isFunction = function() {
|
||
return new this('function')
|
||
}
|
||
|
||
Anot.PropsTypes.isArray = function() {
|
||
return new this('array')
|
||
}
|
||
|
||
Anot.PropsTypes.isObject = function() {
|
||
return new this('object')
|
||
}
|
||
|
||
Anot.PropsTypes.isBoolean = function() {
|
||
return new this('boolean')
|
||
}
|
||
|
||
/*判定是否是一个朴素的javascript对象(Object),不是DOM对象,不是BOM对象,不是自定义类的实例*/
|
||
Anot.isPlainObject = function(obj) {
|
||
// 简单的 typeof obj === "object"检测,会致使用isPlainObject(window)在opera下通不过
|
||
return (
|
||
serialize.call(obj) === '[object Object]' &&
|
||
Object.getPrototypeOf(obj) === oproto
|
||
)
|
||
}
|
||
|
||
let VMODELS = (Anot.vmodels = {}) //所有vmodel都储存在这里
|
||
Anot.init = function(source) {
|
||
if (Anot.isPlainObject(source)) {
|
||
let $id = source.$id
|
||
let vm = null
|
||
if (!$id) {
|
||
log('warning: vm必须指定id')
|
||
}
|
||
vm = modelFactory(Object.assign({ props: {} }, source))
|
||
vm.$id = $id
|
||
VMODELS[$id] = vm
|
||
|
||
Anot.nextTick(function() {
|
||
let $elem = document.querySelector('[anot=' + vm.$id + ']')
|
||
if ($elem) {
|
||
if ($elem === DOC.body) {
|
||
scanTag($elem, [])
|
||
} else {
|
||
let _parent = $elem
|
||
while ((_parent = _parent.parentNode)) {
|
||
if (_parent.__VM__) {
|
||
break
|
||
}
|
||
}
|
||
scanTag($elem.parentNode, _parent ? [_parent.__VM__] : [])
|
||
}
|
||
}
|
||
})
|
||
|
||
return vm
|
||
} else {
|
||
this[0] = this.element = source
|
||
}
|
||
}
|
||
Anot.fn = Anot.prototype = Anot.init.prototype
|
||
|
||
//与jQuery.extend方法,可用于浅拷贝,深拷贝
|
||
Anot.mix = Anot.fn.mix = function() {
|
||
let options,
|
||
name,
|
||
src,
|
||
copy,
|
||
copyIsArray,
|
||
clone,
|
||
target = arguments[0] || {},
|
||
i = 1,
|
||
length = arguments.length,
|
||
deep = false
|
||
|
||
// 如果第一个参数为布尔,判定是否深拷贝
|
||
if (typeof target === 'boolean') {
|
||
deep = target
|
||
target = arguments[1] || {}
|
||
i++
|
||
}
|
||
|
||
//确保接受方为一个复杂的数据类型
|
||
if (typeof target !== 'object' && Anot.type(target) !== 'function') {
|
||
target = {}
|
||
}
|
||
|
||
//如果只有一个参数,那么新成员添加于mix所在的对象上
|
||
if (i === length) {
|
||
target = this
|
||
i--
|
||
}
|
||
|
||
for (; i < length; i++) {
|
||
//只处理非空参数
|
||
if ((options = arguments[i]) != null) {
|
||
for (name in options) {
|
||
src = target[name]
|
||
copy = options[name]
|
||
// 防止环引用
|
||
if (target === copy) {
|
||
continue
|
||
}
|
||
if (
|
||
deep &&
|
||
copy &&
|
||
(Anot.isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))
|
||
) {
|
||
if (copyIsArray) {
|
||
copyIsArray = false
|
||
clone = src && Array.isArray(src) ? src : []
|
||
} else {
|
||
clone = src && Anot.isPlainObject(src) ? src : {}
|
||
}
|
||
|
||
target[name] = Anot.mix(deep, clone, copy)
|
||
} else if (copy !== void 0) {
|
||
target[name] = copy
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return target
|
||
}
|
||
|
||
function cacheStore(tpye, key, val) {
|
||
if (this.type(key) === 'object') {
|
||
for (let i in key) {
|
||
window[tpye].setItem(i, key[i])
|
||
}
|
||
return
|
||
}
|
||
switch (arguments.length) {
|
||
case 2:
|
||
return window[tpye].getItem(key)
|
||
case 3:
|
||
if ((this.type(val) == 'string' && val.trim() === '') || val === null) {
|
||
window[tpye].removeItem(key)
|
||
return
|
||
}
|
||
if (this.type(val) !== 'object' && this.type(val) !== 'array') {
|
||
window[tpye].setItem(key, val.toString())
|
||
} else {
|
||
window[tpye].setItem(key, JSON.stringify(val))
|
||
}
|
||
break
|
||
}
|
||
}
|
||
|
||
/*判定是否类数组,如节点集合,纯数组,arguments与拥有非负整数的length属性的纯JS对象*/
|
||
function isArrayLike(obj) {
|
||
if (obj && typeof obj === 'object') {
|
||
let n = obj.length,
|
||
str = serialize.call(obj)
|
||
if (/(Array|List|Collection|Map|Arguments)\]$/.test(str)) {
|
||
return true
|
||
} else if (str === '[object Object]' && n === n >>> 0) {
|
||
return true //由于ecma262v5能修改对象属性的enumerable,因此不能用propertyIsEnumerable来判定了
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
Anot.mix({
|
||
rword: rword,
|
||
subscribers: subscribers,
|
||
version: '1.0.0',
|
||
log: log,
|
||
ui: {}, //仅用于存放组件版本信息等
|
||
slice: function(nodes, start, end) {
|
||
return aslice.call(nodes, start, end)
|
||
},
|
||
noop: noop,
|
||
/*如果不用Error对象封装一下,str在控制台下可能会乱码*/
|
||
error: function(str, e) {
|
||
throw new (e || Error)(str) // jshint ignore:line
|
||
},
|
||
/* Anot.range(10)
|
||
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||
Anot.range(1, 11)
|
||
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||
Anot.range(0, 30, 5)
|
||
=> [0, 5, 10, 15, 20, 25]
|
||
Anot.range(0, -10, -1)
|
||
=> [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
|
||
Anot.range(0)
|
||
=> []*/
|
||
range: function(start, end, step) {
|
||
// 用于生成整数数组
|
||
step || (step = 1)
|
||
if (end == null) {
|
||
end = start || 0
|
||
start = 0
|
||
}
|
||
let index = -1,
|
||
length = Math.max(0, Math.ceil((end - start) / step)),
|
||
result = new Array(length)
|
||
while (++index < length) {
|
||
result[index] = start
|
||
start += step
|
||
}
|
||
return result
|
||
},
|
||
deepCopy: toJson,
|
||
eventHooks: {},
|
||
/*绑定事件*/
|
||
bind: function(el, type, fn, phase) {
|
||
let hooks = Anot.eventHooks
|
||
type = type.split(',')
|
||
Anot.each(type, function(i, t) {
|
||
t = t.trim()
|
||
let hook = hooks[t]
|
||
if (typeof hook === 'object') {
|
||
type = hook.type || type
|
||
phase = hook.phase || !!phase
|
||
fn = hook.fix ? hook.fix(el, fn) : fn
|
||
}
|
||
el.addEventListener(t, fn, phase)
|
||
})
|
||
return fn
|
||
},
|
||
/*卸载事件*/
|
||
unbind: function(el, type, fn, phase) {
|
||
let hooks = Anot.eventHooks
|
||
type = type.split(',')
|
||
fn = fn || noop
|
||
Anot.each(type, function(i, t) {
|
||
t = t.trim()
|
||
let hook = hooks[t]
|
||
if (typeof hook === 'object') {
|
||
type = hook.type || type
|
||
phase = hook.phase || !!phase
|
||
}
|
||
el.removeEventListener(t, fn, phase)
|
||
})
|
||
},
|
||
/*读写删除元素节点的样式*/
|
||
css: function(node, name, value) {
|
||
if (node instanceof Anot) {
|
||
node = node[0]
|
||
}
|
||
var prop = /[_-]/.test(name) ? camelize(name) : name
|
||
var fn
|
||
|
||
name = Anot.cssName(prop) || prop
|
||
if (value === void 0 || typeof value === 'boolean') {
|
||
//获取样式
|
||
fn = cssHooks[prop + ':get'] || cssHooks['@:get']
|
||
if (name === 'background') {
|
||
name = 'backgroundColor'
|
||
}
|
||
var val = fn(node, name)
|
||
return value === true ? +val || 0 : val
|
||
} else if (value === '') {
|
||
//请除样式
|
||
node.style[name] = ''
|
||
} else {
|
||
//设置样式
|
||
if (value == null || value !== value) {
|
||
return
|
||
}
|
||
if (isFinite(value) && !Anot.cssNumber[prop]) {
|
||
value += 'px'
|
||
}
|
||
fn = cssHooks[prop + ':set'] || cssHooks['@:set']
|
||
fn(node, name, value)
|
||
}
|
||
},
|
||
/*遍历数组与对象,回调的第一个参数为索引或键名,第二个或元素或键值*/
|
||
each: function(obj, fn) {
|
||
if (obj) {
|
||
//排除null, undefined
|
||
let i = 0
|
||
if (isArrayLike(obj)) {
|
||
for (let n = obj.length; i < n; i++) {
|
||
if (fn(i, obj[i]) === false) break
|
||
}
|
||
} else {
|
||
for (i in obj) {
|
||
if (obj.hasOwnProperty(i) && fn(i, obj[i]) === false) {
|
||
break
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
Array: {
|
||
/*只有当前数组不存在此元素时只添加它*/
|
||
ensure: function(target, item) {
|
||
if (target.indexOf(item) === -1) {
|
||
return target.push(item)
|
||
}
|
||
},
|
||
/*移除数组中指定位置的元素,返回布尔表示成功与否*/
|
||
removeAt: function(target, index) {
|
||
return !!target.splice(index, 1).length
|
||
},
|
||
/*移除数组中第一个匹配传参的那个元素,返回布尔表示成功与否*/
|
||
remove: function(target, item) {
|
||
let index = target.indexOf(item)
|
||
if (~index) return Anot.Array.removeAt(target, index)
|
||
return false
|
||
}
|
||
},
|
||
/**
|
||
* [ls localStorage操作]
|
||
* @param {[type]} key [键名]
|
||
* @param {[type]} val [键值,为空时删除]
|
||
* @return
|
||
*/
|
||
ls: function() {
|
||
let args = aslice.call(arguments, 0)
|
||
args.unshift('localStorage')
|
||
return cacheStore.apply(this, args)
|
||
},
|
||
ss: function() {
|
||
let args = aslice.call(arguments, 0)
|
||
args.unshift('sessionStorage')
|
||
return cacheStore.apply(this, args)
|
||
},
|
||
/**
|
||
* [cookie cookie 操作 ]
|
||
* @param key [cookie名]
|
||
* @param val [cookie值]
|
||
* @param {[json]} opt [有效期,域名,路径等]
|
||
* @return {[boolean]} [读取时返回对应的值,写入时返回true]
|
||
*/
|
||
cookie: function(key, val, opt) {
|
||
if (arguments.length > 1) {
|
||
if (!key) {
|
||
return
|
||
}
|
||
|
||
//设置默认的参数
|
||
opt = opt || {}
|
||
opt = Object.assign(
|
||
{
|
||
expires: '',
|
||
path: '/',
|
||
domain: document.domain,
|
||
secure: ''
|
||
},
|
||
opt
|
||
)
|
||
|
||
if ((this.type(val) == 'string' && val.trim() === '') || val === null) {
|
||
document.cookie =
|
||
encodeURIComponent(key) +
|
||
'=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=' +
|
||
opt.domain +
|
||
'; path=' +
|
||
opt.path
|
||
return true
|
||
}
|
||
if (opt.expires) {
|
||
switch (opt.expires.constructor) {
|
||
case Number:
|
||
opt.expires =
|
||
opt.expires === Infinity
|
||
? '; expires=Fri, 31 Dec 9999 23:59:59 GMT'
|
||
: '; max-age=' + opt.expires
|
||
break
|
||
case String:
|
||
opt.expires = '; expires=' + opt.expires
|
||
break
|
||
case Date:
|
||
opt.expires = '; expires=' + opt.expires.toUTCString()
|
||
break
|
||
}
|
||
}
|
||
document.cookie =
|
||
encodeURIComponent(key) +
|
||
'=' +
|
||
encodeURIComponent(val) +
|
||
opt.expires +
|
||
'; domain=' +
|
||
opt.domain +
|
||
'; path=' +
|
||
opt.path +
|
||
'; ' +
|
||
opt.secure
|
||
return true
|
||
} else {
|
||
if (!key) {
|
||
return document.cookie
|
||
}
|
||
return (
|
||
decodeURIComponent(
|
||
document.cookie.replace(
|
||
new RegExp(
|
||
'(?:(?:^|.*;)\\s*' +
|
||
encodeURIComponent(key).replace(/[\-\.\+\*]/g, '\\$&') +
|
||
'\\s*\\=\\s*([^;]*).*$)|^.*$'
|
||
),
|
||
'$1'
|
||
)
|
||
) || null
|
||
)
|
||
}
|
||
},
|
||
//获取url的参数
|
||
search: function(key) {
|
||
key += ''
|
||
let uri = location.search
|
||
|
||
if (!key || !uri) {
|
||
return null
|
||
}
|
||
uri = decodeURIComponent(uri)
|
||
|
||
uri = uri.slice(1)
|
||
uri = uri.split('&')
|
||
|
||
let obj = {}
|
||
for (let i = 0, item; (item = uri[i++]); ) {
|
||
let tmp = item.split('=')
|
||
tmp[1] = tmp.length < 2 ? null : tmp[1]
|
||
tmp[1] = tmp[1]
|
||
if (obj.hasOwnProperty(tmp[0])) {
|
||
if (typeof obj[tmp[0]] === 'object') {
|
||
obj[tmp[0]].push(tmp[1])
|
||
} else {
|
||
obj[tmp[0]] = [obj[tmp[0]]]
|
||
obj[tmp[0]].push(tmp[1])
|
||
}
|
||
} else {
|
||
obj[tmp[0]] = tmp[1]
|
||
}
|
||
}
|
||
return obj.hasOwnProperty(key) ? obj[key] : null
|
||
},
|
||
//复制文本到粘贴板
|
||
copy: function(txt) {
|
||
if (!DOC.queryCommandSupported || !DOC.queryCommandSupported('copy')) {
|
||
return log('该浏览器不支持复制到粘贴板')
|
||
}
|
||
|
||
let ta = DOC.createElement('textarea')
|
||
ta.textContent = txt
|
||
ta.style.position = 'fixed'
|
||
ta.style.bottom = '-1000px'
|
||
DOC.body.appendChild(ta)
|
||
ta.select()
|
||
try {
|
||
DOC.execCommand('copy')
|
||
} catch (err) {
|
||
log('复制到粘贴板失败', err)
|
||
}
|
||
DOC.body.removeChild(ta)
|
||
}
|
||
})
|
||
|
||
let bindingHandlers = (Anot.bindingHandlers = {})
|
||
let bindingExecutors = (Anot.bindingExecutors = {})
|
||
|
||
let directives = (Anot.directives = {})
|
||
Anot.directive = function(name, obj) {
|
||
bindingHandlers[name] = obj.init = obj.init || noop
|
||
bindingExecutors[name] = obj.update = obj.update || noop
|
||
return (directives[name] = obj)
|
||
}
|