diff --git a/Readme.md b/Readme.md
index 1a30815..5e98335 100644
--- a/Readme.md
+++ b/Readme.md
@@ -15,5 +15,8 @@
npm start
# 生产环境
-npm run build
+npm run prod
+
+# 不编译,只压缩
+npm run next
```
diff --git a/build.dev.js b/build.dev.js
index e72453b..a9a0be5 100644
--- a/build.dev.js
+++ b/build.dev.js
@@ -40,7 +40,8 @@ const compileJs = (entry, output) => {
fs.cp(entry, output)
} else {
try {
- const { code } = babel.transformFileSync(entry, jsOpt)
+ let { code } = babel.transformFileSync(entry, jsOpt)
+ code = code.replace(/\.scss/g, '.css')
fs.echo(code, output)
} catch (err) {
return log(err)
diff --git a/build.next.js b/build.next.js
new file mode 100644
index 0000000..af0f47e
--- /dev/null
+++ b/build.next.js
@@ -0,0 +1,115 @@
+#! /usr/bin/env node
+
+const log = console.log
+const fs = require('iofs')
+const path = require('path')
+const scss = require('node-sass')
+const postcss = require('postcss')
+const autoprefixer = require('autoprefixer')
+const chalk = require('chalk')
+const uglify = require('uglify-es')
+
+const sourceDir = path.resolve(__dirname, 'src')
+const buildDir = path.resolve(__dirname, 'dist')
+const prefixer = postcss().use(
+ autoprefixer({
+ browsers: ['ie > 9', 'iOS > 8', 'Android >= 4.4', 'ff > 38', 'Chrome > 38']
+ })
+)
+const cssOpt = {
+ includePaths: ['src/css/'],
+ outputStyle: 'compressed'
+}
+
+const compileJs = (entry, output) => {
+ if (/touch\.patch/.test(entry)) {
+ return
+ }
+ let t1 = Date.now()
+ let buf = fs.cat(entry).toString()
+ let { code } = uglify.minify(buf)
+ code = code.replace(/\.scss/g, '.css')
+ log(
+ '编译JS: %s, 耗时 %s ms',
+ chalk.green(entry),
+ chalk.yellow(Date.now() - t1)
+ )
+ fs.echo(code, output)
+}
+
+const compileCss = (entry, output) => {
+ let t1 = Date.now()
+ const { css } = scss.renderSync({ ...cssOpt, file: entry })
+ prefixer.process(css, { from: '', to: '' }).then(result => {
+ log(
+ '编译scss: %s, 耗时 %s ms',
+ chalk.green(entry),
+ chalk.yellow(Date.now() - t1)
+ )
+ fs.echo(result.css, output)
+ })
+}
+
+const compileHtm = (entry, output) => {
+ let t1 = Date.now()
+ let htm = fs.cat(entry).toString('utf8')
+ htm = htm.replace(/[\r\n\t]+/g, ' ').replace(/\s{2,}/g, ' ')
+ log(
+ '压缩HTML: %s, 耗时 %s ms',
+ chalk.green(entry),
+ chalk.yellow(Date.now() - t1)
+ )
+ fs.echo(htm, output)
+}
+
+/*=======================================================*/
+/*===== ===*/
+/*=======================================================*/
+
+const fontFiles = fs.ls('./src/font/', true)
+const jsFiles = fs.ls('./src/js/', true)
+const cssFiles = fs.ls('./src/css/', true)
+
+if (fs.isdir(buildDir)) {
+ fs.rm(buildDir, true)
+ log(chalk.cyan('清除旧目录 dist/'))
+}
+
+// 字体文件直接复制
+fontFiles.forEach(file => {
+ fs.cp('./src/font/' + file, './dist/font/' + file)
+})
+
+// css目录
+cssFiles.forEach(file => {
+ if (/\.scss$/.test(file)) {
+ let entry = path.resolve(sourceDir, 'css/', file)
+ let output = path.resolve(buildDir, 'css/', file.replace(/scss$/, 'css'))
+
+ compileCss(entry, output)
+ }
+})
+
+// js目录的处理要复杂一点
+jsFiles.forEach(file => {
+ let entry = path.resolve(sourceDir, 'js', file)
+ let output = path.resolve(buildDir, 'js', file)
+ let ext = file.slice(file.lastIndexOf('.') + 1)
+
+ switch (ext) {
+ case 'js':
+ compileJs(entry, output)
+ break
+ case 'scss':
+ output = output.replace(/scss$/, 'css')
+ compileCss(entry, output)
+ break
+ case 'htm':
+ compileHtm(entry, output)
+ break
+ default:
+ if (!fs.isdir(entry)) {
+ fs.cp(entry, output)
+ }
+ }
+})
diff --git a/build.prod.js b/build.prod.js
index a130299..6d9527e 100644
--- a/build.prod.js
+++ b/build.prod.js
@@ -8,7 +8,7 @@ const scss = require('node-sass')
const postcss = require('postcss')
const autoprefixer = require('autoprefixer')
const chalk = require('chalk')
-const uglify = require('uglify-js')
+const uglify = require('uglify-es')
const sourceDir = path.resolve(__dirname, 'src')
const buildDir = path.resolve(__dirname, 'dist')
@@ -41,7 +41,7 @@ const compileJs = (entry, output) => {
tmpOpt = Object.assign({}, jsOpt, { plugins: [] })
}
let { code } = babel.transformFileSync(entry, tmpOpt)
- code = uglify.minify(code).code
+ code = uglify.minify(code).code.replace(/\.scss/g, '.css')
log(
'编译JS: %s, 耗时 %s ms',
chalk.green(entry),
@@ -84,7 +84,7 @@ const jsFiles = fs.ls('./src/js/', true)
const cssFiles = fs.ls('./src/css/', true)
if (fs.isdir(buildDir)) {
- // fs.rm(buildDir, true)
+ fs.rm(buildDir, true)
log(chalk.cyan('清除旧目录 dist/'))
}
diff --git a/package-lock.json b/package-lock.json
index 2fdc6a1..d20aba2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3672,16 +3672,22 @@
"dev": true,
"optional": true
},
- "uglify-js": {
- "version": "3.3.15",
- "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.3.15.tgz",
- "integrity": "sha512-bqtBCAINYXX/OkdnqMGpbXr+OPWc00hsozRpk+dAtfnbdk2jjKiLmyOkQ7zamg648lVMnzATL8JrSN6LmaVpYA==",
+ "uglify-es": {
+ "version": "3.3.9",
+ "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz",
+ "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==",
"dev": true,
"requires": {
- "commander": "2.15.0",
+ "commander": "2.13.0",
"source-map": "0.6.1"
},
"dependencies": {
+ "commander": {
+ "version": "2.13.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz",
+ "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==",
+ "dev": true
+ },
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
diff --git a/package.json b/package.json
index 23fe8e7..99ab780 100644
--- a/package.json
+++ b/package.json
@@ -5,16 +5,14 @@
"main": "index.js",
"scripts": {
"start": "node ./build.dev.js",
- "build": "node ./build.prod.js"
+ "prod": "node ./build.prod.js",
+ "next": "node ./build.next.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/yutent/doui.git"
},
- "keywords": [
- "doui",
- "Anot"
- ],
+ "keywords": ["doui", "Anot"],
"author": "yutent",
"license": "MIT",
"devDependencies": {
@@ -29,7 +27,7 @@
"iofs": "^1.0.3",
"node-sass": "^4.8.2",
"postcss": "^6.0.19",
- "uglify-js": "^3.3.15"
+ "uglify-es": "^3.3.9"
},
"dependencies": {}
}
diff --git a/src/js/anot.js b/src/js/anot.js
index d1baee2..68a7593 100644
--- a/src/js/anot.js
+++ b/src/js/anot.js
@@ -5,22 +5,7 @@
* support IE10+ and other browsers
*
==================================================*/
-;(function(global, factory) {
- if (typeof module === 'object' && typeof module.exports === 'object') {
- module.exports = global.document
- ? factory(global, true)
- : function(w) {
- if (!w.document) {
- throw new Error('Anot.js只能运行在浏览器环境')
- }
- return factory(w)
- }
- } else {
- factory(global)
- }
-
- // Pass this if window is not defined yet
-})(typeof window !== 'undefined' ? window : this, function(window, noGlobal) {
+const _Anot = (function() {
/*********************************************************************
* 全局变量及方法 *
**********************************************************************/
@@ -35,7 +20,7 @@
var head = DOC.head //HEAD元素
head.insertAdjacentHTML(
'afterBegin',
- ''
+ ''
)
var ifGroup = head.firstChild
@@ -141,7 +126,7 @@
}
if (tickObserver) {
- var node = document.createTextNode('any')
+ var node = document.createTextNode('anot')
new tickObserver(callback).observe(node, { characterData: true }) // jshint ignore:line
var bool = false
return function(fn) {
@@ -181,7 +166,11 @@
return ''
},
check: function(val) {
- return Anot.type(val) === this.checkType
+ this.result = Anot.type(val)
+ return this.result === this.checkType
+ },
+ call: function() {
+ return this.toString()
}
}
@@ -235,13 +224,13 @@
if ($elem === DOC.body) {
scanTag($elem, [])
} else {
- var $parent = null
- while (($parent = $elem.parentNode)) {
+ var $parent = $elem
+ while (($parent = $parent.parentNode)) {
if ($parent.anotctrl) {
- scanTag($elem.parentNode, [VMODELS[$parent.anotctrl]])
break
}
}
+ scanTag($elem.parentNode, $parent ? [VMODELS[$parent.anotctrl]] : [])
}
}
return vm
@@ -460,7 +449,7 @@
value: function() {
var thisYear = this.getFullYear(),
that = new Date(thisYear, 0, 1),
- firstDay = that.getDay(),
+ firstDay = that.getDay() || 1,
numsOfToday = (this - that) / 86400000
return Math.ceil((numsOfToday + firstDay) / 7)
},
@@ -1373,6 +1362,7 @@
var methods = source.methods
var props = source.props
var watches = source.watch
+ var mounted = source.mounted
delete source.state
delete source.computed
@@ -1488,6 +1478,8 @@
hideProperty($vmodel, '$active', false)
hideProperty($vmodel, '$pathname', old ? old.$pathname : '')
hideProperty($vmodel, '$accessors', accessors)
+ hideProperty($vmodel, '$refs', {})
+ hideProperty($vmodel, '$children', [])
hideProperty($vmodel, 'hasOwnProperty', trackBy)
if (options.watch) {
hideProperty($vmodel, '$watch', function() {
@@ -1500,6 +1492,12 @@
var v = Anot.vmodels[i]
v.$fire && v.$fire.apply(v, [ee, a])
}
+ } else if (path.indexOf('child!') === 0) {
+ var ee = 'props.' + path.slice(6)
+ for (var i in $vmodel.$children) {
+ var v = $vmodel.$children[i]
+ v.$fire && v.$fire.apply(v, [ee, a])
+ }
} else {
$emit.call($vmodel, path, [a])
}
@@ -1527,6 +1525,11 @@
}
$vmodel.$active = true
+ $vmodel.mounted = mounted
+
+ if (old && old.$up) {
+ old.$up.$children.push($vmodel)
+ }
return $vmodel
}
@@ -2912,7 +2915,7 @@
binding.observers.forEach(function(it) {
if (it.type === 'function') {
// log(it, expr)
- let reg = new RegExp(it.p + '\\(([^)]*)\\)', 'g')
+ var reg = new RegExp(it.p + '\\(([^)]*)\\)', 'g')
expr = expr.replace(reg, function(s, m) {
m = m.trim()
return (
@@ -2933,7 +2936,7 @@
"'use strict';\ntry{\nvar " +
assigns.join(',\n') +
expr +
- '\n}catch(e){log(e)}'
+ '\n}catch(e){console.log(e)}'
)
)
/* jshint ignore:end */
@@ -3082,17 +3085,19 @@
}
var rnoCollect = /^(:\S+|data-\S+|on[a-z]+|id|style|class)$/
- var ronattr = /^on\-[\w-]+$/
+ var ronattr = '__fn__'
function getOptionsFromTag(elem, vmodels) {
var attributes = elem.attributes
var ret = {}
for (var i = 0, attr; (attr = attributes[i++]); ) {
var name = attr.name
if (attr.specified && !rnoCollect.test(name)) {
- var camelizeName = camelize(attr.name)
- if (/^on\-[\w-]+$/.test(name)) {
- ret[camelizeName] = getBindingCallback(elem, name, vmodels)
+ if (name.indexOf(ronattr) === 0) {
+ name = attr.value.slice(6)
+ ret[name] = elem[attr.value]
+ delete elem[attr.value]
} else {
+ var camelizeName = camelize(name)
ret[camelizeName] = parseData(attr.value)
}
}
@@ -3224,6 +3229,12 @@
//确保所有:attr-name扫描完再处理
_delay_component(widget)
}
+ } else {
+ // 非组件才检查 ref属性
+ var ref = isRef(elem)
+ if (ref) {
+ vmodels[0].$refs[ref] = elem
+ }
}
}
@@ -3267,34 +3278,19 @@
createSignalTower(elem, newVmodel)
hideProperty(newVmodel, '$elem', elem)
if (vmodels.length) {
- attrs.forEach(function(attr, i) {
+ var props = {}
+ attrs.forEach(function(attr) {
if (/^:/.test(attr.name)) {
var name = attr.name.match(rmsAttr)[1]
var value = null
- if (!name || Anot.directives[name]) {
+ if (!name || Anot.directives[name] || events[name]) {
return
}
try {
value = parseExpr(attr.value, vmodels, {}).apply(0, vmodels)
+ value = toJson(value)
elem.removeAttribute(attr.name)
- if (!value) {
- return
- }
- if (
- newVmodel.props[name] &&
- newVmodel.props[name].type === 'PropsTypes'
- ) {
- if (newVmodel.props[name].check(value)) {
- newVmodel.props[name] = value
- } else {
- Anot.error(
- 'props「' + name + '」类型错误!' + value,
- TypeError
- )
- }
- } else {
- newVmodel.props[name] = value
- }
+ props[name] = value
} catch (error) {
log(
'Props parse faild on (%s[class=%s]),',
@@ -3306,13 +3302,38 @@
}
}
})
+ // 一旦设定了 props的类型, 就必须传入正确的值
+ for (var k in newVmodel.props) {
+ if (newVmodel.props[k] && newVmodel.props[k].type === 'PropsTypes') {
+ if (newVmodel.props[k].check(props[k])) {
+ newVmodel.props[k] = props[k]
+ delete props[k]
+ } else {
+ Anot.error(
+ 'props.' +
+ k +
+ ' needs [' +
+ newVmodel.props[k].checkType +
+ '], but [' +
+ newVmodel.props[k].result +
+ '] given.',
+ TypeError
+ )
+ }
+ }
+ }
+ Object.assign(newVmodel.props, props)
+ props = undefined
}
}
scanAttr(elem, vm) //扫描特性节点
if (newVmodel) {
setTimeout(function() {
- newVmodel.$fire(':scan-end', elem)
+ if (typeof newVmodel.mounted === 'function') {
+ newVmodel.mounted()
+ }
+ delete newVmodel.mounted
})
}
}
@@ -3436,15 +3457,13 @@
var componentQueue = []
var widgetList = []
var componentHooks = {
- construct: function(props, next) {
- next(props)
- },
+ construct: noop,
componentWillMount: noop,
componentDidMount: noop,
childComponentDidMount: noop,
componentWillUnmount: noop,
- render: function(str) {
- return str
+ render: function() {
+ return null
}
}
@@ -3474,21 +3493,19 @@
return
}
var props = getOptionsFromTag(elem, host.vmodels)
- var vmOpts = getOptionsFromVM(host.vmodels, props.config)
var $id = props.uuid || generateID(widget)
- var componentDefinition = {}
- props = Object.assign({}, vmOpts, props)
- vmOpts = void 0
- delete props.config
delete props.uuid
- hooks.construct.call(elem, props, function next(val) {
- Object.assign(hooks.props, val)
- Object.assign(componentDefinition, hooks)
- })
+ delete props.name
- componentDefinition.$refs = {}
- componentDefinition.$id = $id
+ hooks.props = hooks.props || {}
+ hooks.state = hooks.state || {}
+
+ Object.assign(hooks.props, props)
+
+ hooks.construct.call(elem, hooks.props, hooks.state)
+
+ hooks.$id = $id
//==========构建VM=========
var {
@@ -3497,15 +3514,17 @@
childComponentDidMount,
componentWillUnmount,
render
- } = componentDefinition
+ } = hooks
- delete componentDefinition.construct
- delete componentDefinition.componentWillMount
- delete componentDefinition.componentDidMount
- delete componentDefinition.childComponentDidMount
- delete componentDefinition.componentWillUnmount
+ delete hooks.construct
+ delete hooks.componentWillMount
+ delete hooks.componentDidMount
+ delete hooks.childComponentDidMount
+ delete hooks.componentWillUnmount
- var vmodel = Anot(componentDefinition)
+ var vmodel = Anot(hooks)
+ delete vmodel.mounted
+ host.vmodels[0].$children.push(vmodel)
elem.msResolved = 1 //防止二进扫描此元素
@@ -3514,18 +3533,30 @@
if (!elem.content.firstElementChild) {
Anot.clearHTML(elem)
- elem.innerHTML = render()
+ var html = render.call(vmodel) || ''
+
+ html = html.replace(/<\w+[^>]*>/g, function(m, s) {
+ return m.replace(/[\n\t\s]{1,}/g, ' ')
+ })
+
+ elem.innerHTML = html
}
// 组件所使用的标签是temlate,所以必须要要用子元素替换掉
var child = elem.content.firstElementChild
- elem.parentNode.replaceChild(child, elem)
+ var nullComponent = DOC.createComment('empty component')
+ elem.parentNode.replaceChild(child || nullComponent, elem)
+
+ // 空组件直接跳出
+ if (!child) {
+ return
+ }
child.msResolved = 1
var cssText = elem.style.cssText
var className = elem.className
elem = host.element = child
- elem.style.cssText += ';' + cssText
+ elem.style && (elem.style.cssText += ';' + cssText)
if (className) {
Anot(elem).addClass(className)
@@ -3542,10 +3573,11 @@
if (ev.childReady) {
dependencies += ev.childReady
if (vmodel !== ev.vm) {
- vmodel.$refs[ev.vm.$id] = ev.vm
+ vmodel.$children.push(ev.vm)
+ ev.vm.$up = vmodel
if (ev.childReady === -1) {
children++
- childComponentDidMount.call(vmodel, elem, ev)
+ childComponentDidMount.call(vmodel, ev.vm)
}
ev.stopPropagation()
}
@@ -3590,24 +3622,11 @@
})
}, 17)
}
- })(obj, Anot.components[name], obj.element, obj.name) // jshint ignore:line
+ })(obj, toJson(Anot.components[name]), obj.element, obj.name) // jshint ignore:line
}
}
}
- function getOptionsFromVM(vmodels, pre) {
- if (pre) {
- for (var i = 0, v; (v = vmodels[i++]); ) {
- if (v.hasOwnProperty(pre) && typeof v[pre] === 'object') {
- var vmOptions = v[pre]
- return vmOptions.$model || vmOptions
- break
- }
- }
- }
- return {}
- }
-
function isWidget(el) {
//如果是组件,则返回组件的名字
var name = el.nodeName.toLowerCase()
@@ -3617,6 +3636,10 @@
return null
}
+ function isRef(el) {
+ return el.hasAttribute('ref') ? el.getAttribute('ref') : null
+ }
+
var bools = [
'autofocus,autoplay,async,allowTransparency,checked,controls',
'declare,disabled,defer,defaultChecked,defaultSelected',
@@ -3687,11 +3710,16 @@
update: function(val) {
var elem = this.element
var obj = val
+ var vm = this.vmodels[0]
if (typeof obj === 'object' && obj !== null) {
- if (!Anot.isPlainObject(obj)) obj = obj.$model
+ if (!Anot.isPlainObject(obj)) {
+ obj = obj.$model
+ }
} else {
- if (!this.param) return
+ if (!this.param) {
+ return
+ }
obj = {}
obj[this.param] = val
@@ -3711,24 +3739,32 @@
//chrome v37- 下embed标签动态设置的src,无法发起请求
if (window.chrome && elem.tagName === 'EMBED') {
var parent = elem.parentNode
- var com = document.createComment(':src')
+ var com = DOC.createComment(':src')
parent.replaceChild(com, elem)
parent.replaceChild(elem, com)
}
} else {
var k = i
//古董IE下,部分属性名字要进行映射
- if (!W3C && propMap[k]) k = propMap[k]
+ if (!W3C && propMap[k]) {
+ k = propMap[k]
+ }
+ if (obj[i] === false || obj[i] === null || obj[i] === undefined) {
+ obj[i] = ''
+ }
if (typeof elem[boolMap[k]] === 'boolean') {
//布尔属性必须使用el.xxx = true|false方式设值
elem[boolMap[k]] = !!obj[i]
//如果为false, IE全系列下相当于setAttribute(xxx, ''),会影响到样式,需要进一步处理
- if (!obj[i]) obj[i] = !!obj[i]
+ if (!obj[i]) {
+ obj[i] = !!obj[i]
+ }
+ if (obj[i] === false) {
+ return elem.removeAttribute(k)
+ }
}
- if (obj[i] === false || obj[i] === null || obj[i] === undefined)
- return elem.removeAttribute(k)
//SVG只能使用setAttribute(xxx, yyy), VML只能使用elem.xxx = yyy ,HTML的固有属性必须elem.xxx = yyy
var isInnate = rsvg.test(elem)
@@ -3737,6 +3773,13 @@
if (isInnate) {
elem[k] = obj[i]
} else {
+ if (typeof obj[i] === 'object') {
+ obj[i] = JSON.stringify(obj[i])
+ } else if (typeof obj[i] === 'function') {
+ k = '__fn__' + camelize(k)
+ elem[k] = obj[i].bind(vm)
+ obj[i] = k
+ }
elem.setAttribute(k, obj[i])
}
}
@@ -3881,115 +3924,121 @@
/*------ 表单验证 -------*/
var __rules = {}
- Anot.validate = function(key) {
- return (
- !__rules[key] ||
- __rules[key].every(function(it) {
- return it.checked
- })
- )
+ Anot.validate = function(key, cb) {
+ if (!__rules[key]) {
+ throw new Error('validate [' + key + '] not exists.')
+ }
+ if (typeof cb === 'function') {
+ __rules[key].event = cb
+ }
+ var result = __rules[key].result
+ for (var k in result) {
+ if (!result[k].passed) {
+ return result[k]
+ }
+ }
+ return true
}
+
Anot.directive('rule', {
priority: 2010,
init: function(binding) {
if (binding.param && !__rules[binding.param]) {
- __rules[binding.param] = []
+ __rules[binding.param] = {
+ event: noop,
+ result: {}
+ }
}
binding.target = __rules[binding.param]
},
- update: function(obj) {
- var _this = this,
- elem = this.element,
- ruleID = -1
-
- if (!['INPUT', 'TEXTAREA'].includes(elem.nodeName)) return
-
+ update: function(opt) {
+ var _this = this
+ var elem = this.element
+ if (!['INPUT', 'TEXTAREA'].includes(elem.nodeName)) {
+ return
+ }
if (this.target) {
- ruleID = this.target.length
- this.target.push({ checked: true })
+ this.target.result[elem.expr] = { key: elem.expr }
}
+ var target = this.target
- //如果父级元素没有定位属性,则加上相对定位
- if (getComputedStyle(elem.parentNode).position === 'static') {
- elem.parentNode.style.position = 'relative'
- }
-
- var $elem = Anot(elem),
- ol = elem.offsetLeft + elem.offsetWidth - 50,
- ot = elem.offsetTop + elem.offsetHeight + 8,
- tips = document.createElement('div')
-
- tips.className = 'do-rule-tips'
- tips.style.left = ol + 'px'
- tips.style.bottom = ot + 'px'
-
+ // 0: 验证通过
+ // 10001: 不能为空
+ // 10002: 必须为合法数字
+ // 10003: Email格式错误
+ // 10004: 手机格式错误
+ // 10005: 必须为纯中文
+ // 10006: 格式匹配错误(正则)
+ // 10011: 输入值超过指定最大长度
+ // 10012: 输入值短于指定最小长度
+ // 10021: 输入值大于指定最大数值
+ // 10022: 输入值小于指定最小数值
+ // 10031: 与指定的表单的值不一致
function checked(ev) {
- var txt = '',
- val = elem.value
+ var val = elem.value
+ var code = 0
- if (obj.require && (val === '' || val === null)) txt = '必填项'
-
- if (!txt && obj.isNumeric) txt = !isFinite(val) ? '必须为合法数字' : ''
-
- if (!txt && obj.isEmail)
- txt = !/^[\w\.\-]+@\w+([\.\-]\w+)*\.\w+$/.test(val)
- ? 'Email格式错误'
- : ''
-
- if (!txt && obj.isPhone)
- txt = !/^1[34578]\d{9}$/.test(val) ? '手机格式错误' : ''
-
- if (!txt && obj.isCN)
- txt = !/^[\u4e00-\u9fa5]+$/.test(val) ? '必须为纯中文' : ''
-
- if (!txt && obj.exp)
- txt = !obj.exp.test(val) ? obj.msg || '格式错误' : ''
-
- if (!txt && obj.maxLen)
- txt =
- val.length > obj.maxLen ? '长度不得超过' + obj.maxLen + '位' : ''
-
- if (!txt && obj.minLen)
- txt =
- val.length < obj.minLen ? '长度不得小于' + obj.minLen + '位' : ''
-
- if (!txt && obj.hasOwnProperty('max'))
- txt = val > obj.max ? '输入值不能大于' + obj.max : ''
-
- if (!txt && obj.hasOwnProperty('min'))
- txt = val < obj.min ? '输入值不能小于' + obj.min : ''
-
- if (!txt && obj.eq) {
- var eqEl = document.querySelector('#' + obj.eq)
- txt = val !== eqEl.value ? obj.msg || '2次值不一致' : ''
+ if (opt.require && (val === '' || val === null)) {
+ code = 10001
}
- if (txt) {
- if (ev) {
- tips.textContent = txt
- elem.parentNode.appendChild(tips)
+ if (code === 0 && opt.isNumeric) {
+ code = !isFinite(val) ? 10002 : 0
+ }
+
+ if (code === 0 && opt.isEmail)
+ code = !/^[\w\.\-]+@\w+([\.\-]\w+)*\.\w+$/.test(val) ? 10003 : 0
+
+ if (code === 0 && opt.isPhone) {
+ code = !/^1[34578]\d{9}$/.test(val) ? 10004 : 0
+ }
+
+ if (code === 0 && opt.isCN) {
+ code = !/^[\u4e00-\u9fa5]+$/.test(val) ? 10005 : 0
+ }
+
+ if (code === 0 && opt.exp) {
+ code = !opt.exp.test(val) ? 10006 : 0
+ }
+
+ if (code === 0 && opt.maxLen) {
+ code = val.length > opt.maxLen ? 10011 : 0
+ }
+
+ if (code === 0 && opt.minLen) {
+ code = val.length < opt.minLen ? 10012 : 0
+ }
+
+ if (code === 0 && opt.hasOwnProperty('max')) {
+ code = val > opt.max ? 10021 : 0
+ }
+
+ if (code === 0 && opt.hasOwnProperty('min')) {
+ code = val < opt.min ? 10022 : 0
+ }
+
+ if (code === 0 && opt.eq) {
+ var eqEl = document.querySelector('#' + opt.eq)
+ txt = val !== eqEl.value ? 10031 : 0
+ }
+
+ target.result[elem.expr].code = code
+ target.result[elem.expr].passed = opt.require ? code === 0 : true
+
+ var done
+ for (var k in target.result) {
+ if (!target.result[k].passed) {
+ done = true
+ target.event(target.result[k])
+ break
}
- //必须是"必填项"才会更新验证状态
- if (_this.target && obj.require) {
- _this.target[ruleID].checked = false
- }
- } else {
- if (_this.target) {
- _this.target[ruleID].checked = true
- }
- try {
- elem.parentNode.removeChild(tips)
- } catch (err) {}
+ }
+ if (!done) {
+ target.event(true)
}
}
- $elem.bind('change,blur', checked)
- $elem.bind('focus', function(ev) {
- try {
- elem.parentNode.removeChild(tips)
- } catch (err) {}
- })
-
+ Anot(elem).bind('blur', checked)
checked()
}
})
@@ -4039,12 +4088,13 @@
? 'change'
: 'input'
}
+ elem.expr = binding.expr
//===================绑定事件======================
var bound = (binding.bound = function(type, callback) {
elem.addEventListener(type, callback, false)
var old = binding.rollback
binding.rollback = function() {
- elem.anotStter = null
+ elem.anotSetter = null
Anot.unbind(elem, type, callback)
old && old()
}
@@ -4148,7 +4198,7 @@
elem.msFocus = false
})
}
- elem.anotStter = updateVModel //#765
+ elem.anotSetter = updateVModel //#765
watchValueInTimer(function() {
if (root.contains(elem)) {
if (!elem.msFocus) {
@@ -4167,7 +4217,7 @@
if (!this.init) {
for (var i in Anot.vmodels) {
var v = Anot.vmodels[i]
- v.$fire('any-duplex-init', binding)
+ v.$fire('anot-duplex-init', binding)
}
var cpipe = binding.pipe || (binding.pipe = pipe)
cpipe(null, binding, 'init')
@@ -4250,17 +4300,7 @@
if (-val === -number) {
return number
}
- var arr = /strong|medium|weak/.exec(
- binding.element.getAttribute('data-duplex-number')
- ) || ['medium']
- switch (arr[0]) {
- case 'strong':
- return 0
- case 'medium':
- return val === '' ? '' : 0
- case 'weak':
- return val
- }
+ return 0
},
set: fixNull
}
@@ -4308,8 +4348,8 @@
function newSetter(value) {
// jshint ignore:line
setters[this.tagName].call(this, value)
- if (!this.msFocus && this.anotStter) {
- this.anotStter()
+ if (!this.msFocus && this.anotSetter) {
+ this.anotSetter()
}
}
var inputProto = HTMLInputElement.prototype
@@ -5676,7 +5716,9 @@
time: function(str) {
str = str >> 0
var s = str % 60
- ;(m = Math.floor(str / 60)), (h = Math.floor(m / 60)), (m = m % 60)
+ var m = Math.floor(str / 60)
+ var h = Math.floor(m / 60)
+ m = m % 60
m = m < 10 ? '0' + m : m
s = s < 10 ? '0' + s : s
@@ -5763,14 +5805,38 @@
}
})
- // Map over Anot in case of overwrite
- var _Anot = window.Anot
- Anot.noConflict = function(deep) {
- if (deep && window.Anot === Anot) {
- window.Anot = _Anot
+ /*********************************************************************
+ * DOMReady *
+ **********************************************************************/
+
+ var readyList = [],
+ isReady
+ var fireReady = function(fn) {
+ isReady = true
+ var require = Anot.require
+ if (require && require.checkDeps) {
+ modules['domReady!'].state = 4
+ require.checkDeps()
+ }
+ while ((fn = readyList.shift())) {
+ fn(Anot)
}
- return Anot
}
- window.Anot = Anot
-})
+ if (DOC.readyState === 'complete') {
+ setTimeout(fireReady) //如果在domReady之外加载
+ } else {
+ DOC.addEventListener('DOMContentLoaded', fireReady)
+ }
+ window.addEventListener('load', fireReady)
+ Anot.ready = function(fn) {
+ if (!isReady) {
+ readyList.push(fn)
+ } else {
+ fn(Anot)
+ }
+ }
+
+ return Anot
+})()
+export default _Anot
diff --git a/src/js/anot.shim.js b/src/js/anot.shim.js
index cb752c7..32fd7f8 100644
--- a/src/js/anot.shim.js
+++ b/src/js/anot.shim.js
@@ -35,7 +35,7 @@
var head = DOC.head //HEAD元素
head.insertAdjacentHTML(
'afterBegin',
- ''
+ ''
)
var ifGroup = head.firstChild
@@ -3939,115 +3939,120 @@
/*------ 表单验证 -------*/
var __rules = {}
- Anot.validate = function(key) {
- return (
- !__rules[key] ||
- __rules[key].every(function(it) {
- return it.checked
- })
- )
+ Anot.validate = function(key, cb) {
+ if (!__rules[key]) {
+ throw new Error('validate [' + key + '] not exists.')
+ }
+ if (typeof cb === 'function') {
+ __rules[key].event = cb
+ }
+ var result = __rules[key].result
+ for (var k in result) {
+ if (!result[k].passed) {
+ return result[k]
+ }
+ }
+ return true
}
Anot.directive('rule', {
priority: 2010,
init: function(binding) {
if (binding.param && !__rules[binding.param]) {
- __rules[binding.param] = []
+ __rules[binding.param] = {
+ event: noop,
+ result: {}
+ }
}
binding.target = __rules[binding.param]
},
- update: function(obj) {
- var _this = this,
- elem = this.element,
- ruleID = -1
-
- if (!['INPUT', 'TEXTAREA'].includes(elem.nodeName)) return
-
+ update: function(opt) {
+ var _this = this
+ var elem = this.element
+ if (!['INPUT', 'TEXTAREA'].includes(elem.nodeName)) {
+ return
+ }
if (this.target) {
- ruleID = this.target.length
- this.target.push({ checked: true })
+ this.target.result[elem.expr] = { key: elem.expr }
}
+ var target = this.target
- //如果父级元素没有定位属性,则加上相对定位
- if (getComputedStyle(elem.parentNode).position === 'static') {
- elem.parentNode.style.position = 'relative'
- }
-
- var $elem = Anot(elem),
- ol = elem.offsetLeft + elem.offsetWidth - 50,
- ot = elem.offsetTop + elem.offsetHeight + 8,
- tips = document.createElement('div')
-
- tips.className = 'do-rule-tips'
- tips.style.left = ol + 'px'
- tips.style.bottom = ot + 'px'
-
+ // 0: 验证通过
+ // 10001: 不能为空
+ // 10002: 必须为合法数字
+ // 10003: Email格式错误
+ // 10004: 手机格式错误
+ // 10005: 必须为纯中文
+ // 10006: 格式匹配错误(正则)
+ // 10011: 输入值超过指定最大长度
+ // 10012: 输入值短于指定最小长度
+ // 10021: 输入值大于指定最大数值
+ // 10022: 输入值小于指定最小数值
+ // 10031: 与指定的表单的值不一致
function checked(ev) {
- var txt = '',
- val = elem.value
+ var val = elem.value
+ var code = 0
- if (obj.require && (val === '' || val === null)) txt = '必填项'
-
- if (!txt && obj.isNumeric) txt = !isFinite(val) ? '必须为合法数字' : ''
-
- if (!txt && obj.isEmail)
- txt = !/^[\w\.\-]+@\w+([\.\-]\w+)*\.\w+$/.test(val)
- ? 'Email格式错误'
- : ''
-
- if (!txt && obj.isPhone)
- txt = !/^1[34578]\d{9}$/.test(val) ? '手机格式错误' : ''
-
- if (!txt && obj.isCN)
- txt = !/^[\u4e00-\u9fa5]+$/.test(val) ? '必须为纯中文' : ''
-
- if (!txt && obj.exp)
- txt = !obj.exp.test(val) ? obj.msg || '格式错误' : ''
-
- if (!txt && obj.maxLen)
- txt =
- val.length > obj.maxLen ? '长度不得超过' + obj.maxLen + '位' : ''
-
- if (!txt && obj.minLen)
- txt =
- val.length < obj.minLen ? '长度不得小于' + obj.minLen + '位' : ''
-
- if (!txt && obj.hasOwnProperty('max'))
- txt = val > obj.max ? '输入值不能大于' + obj.max : ''
-
- if (!txt && obj.hasOwnProperty('min'))
- txt = val < obj.min ? '输入值不能小于' + obj.min : ''
-
- if (!txt && obj.eq) {
- var eqEl = document.querySelector('#' + obj.eq)
- txt = val !== eqEl.value ? obj.msg || '2次值不一致' : ''
+ if (opt.require && (val === '' || val === null)) {
+ code = 10001
}
- if (txt) {
- if (ev) {
- tips.textContent = txt
- elem.parentNode.appendChild(tips)
+ if (code === 0 && opt.isNumeric) {
+ code = !isFinite(val) ? 10002 : 0
+ }
+
+ if (code === 0 && opt.isEmail)
+ code = !/^[\w\.\-]+@\w+([\.\-]\w+)*\.\w+$/.test(val) ? 10003 : 0
+
+ if (code === 0 && opt.isPhone) {
+ code = !/^1[34578]\d{9}$/.test(val) ? 10004 : 0
+ }
+
+ if (code === 0 && opt.isCN) {
+ code = !/^[\u4e00-\u9fa5]+$/.test(val) ? 10005 : 0
+ }
+
+ if (code === 0 && opt.exp) {
+ code = !opt.exp.test(val) ? 10006 : 0
+ }
+
+ if (code === 0 && opt.maxLen) {
+ code = val.length > opt.maxLen ? 10011 : 0
+ }
+
+ if (code === 0 && opt.minLen) {
+ code = val.length < opt.minLen ? 10012 : 0
+ }
+
+ if (code === 0 && opt.hasOwnProperty('max')) {
+ code = val > opt.max ? 10021 : 0
+ }
+
+ if (code === 0 && opt.hasOwnProperty('min')) {
+ code = val < opt.min ? 10022 : 0
+ }
+
+ if (code === 0 && opt.eq) {
+ var eqEl = document.querySelector('#' + opt.eq)
+ txt = val !== eqEl.value ? 10031 : 0
+ }
+
+ target.result[elem.expr].code = code
+ target.result[elem.expr].passed = opt.require ? code === 0 : true
+
+ var done
+ for (var k in target.result) {
+ if (!target.result[k].passed) {
+ done = true
+ target.event(target.result[k])
+ break
}
- //必须是"必填项"才会更新验证状态
- if (_this.target && obj.require) {
- _this.target[ruleID].checked = false
- }
- } else {
- if (_this.target) {
- _this.target[ruleID].checked = true
- }
- try {
- elem.parentNode.removeChild(tips)
- } catch (err) {}
+ }
+ if (!done) {
+ target.event(true)
}
}
- $elem.bind('change,blur', checked)
- $elem.bind('focus', function(ev) {
- try {
- elem.parentNode.removeChild(tips)
- } catch (err) {}
- })
-
+ Anot(elem).bind('blur', checked)
checked()
}
})
@@ -4097,6 +4102,7 @@
? 'change'
: 'input'
}
+ elem.expr = binding.expr
//===================绑定事件======================
var bound = (binding.bound = function(type, callback) {
elem.addEventListener(type, callback, false)