├── docs ├── .nojekyll ├── favicon.ico ├── _sidebar.md ├── AUTUMN.md ├── WINTER.md ├── SUMMER.md ├── MIXIN.md ├── index.html ├── SPRING.md ├── GUIDE.md ├── SECOND.md ├── FIRST.md ├── THIRD.md ├── demo.html └── WHY.md ├── .babelrc ├── .travis.yml ├── .gitignore ├── src ├── interface │ ├── coilConfig.ts │ └── zcoilConig.ts ├── utils.ts ├── scoil.ts ├── serialize.ts ├── vue.ts ├── watch.ts ├── coil.ts └── index.ts ├── test.js ├── tsconfig.json ├── demo ├── helloWorld.js ├── asyncData.js ├── $coil.js └── in_vue.js ├── test-storage ├── index.html ├── test-vue.js └── tests.js ├── index.html ├── package.json ├── webpack.config.js ├── README.md ├── test └── zcoil-test.js ├── LICENSE └── dist └── zcoil.js /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"] 3 | } -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/channg/zcoil/HEAD/docs/favicon.ico -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | language: node_js 3 | node_js: 4 | - "8" 5 | - "9" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | README.md.bak 3 | .idea/ 4 | package-lock.json 5 | yarn-error.log 6 | src/test.ts -------------------------------------------------------------------------------- /src/interface/coilConfig.ts: -------------------------------------------------------------------------------- 1 | export interface coilConif{ 2 | rollback?:Boolean; 3 | errorContinue?:Boolean; 4 | saveWithExec?:Boolean; 5 | } -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | var z8 = new zcoil({}); 2 | z8.init({ 3 | data:{ 4 | a:1 5 | } 6 | }) 7 | 8 | z8.$watch((from,to)=>{ 9 | debugger 10 | }) 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/interface/zcoilConig.ts: -------------------------------------------------------------------------------- 1 | export interface zcoilConif{ 2 | name?:String; 3 | localStorage?:boolean; 4 | deadline?:number; 5 | cover?:boolean; 6 | mixin?:any; 7 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "sourceMap": true, 5 | "noImplicitAny": true, 6 | "module": "commonjs", 7 | "target": "es2015", 8 | "allowJs": true, 9 | "allowSyntheticDefaultImports":true 10 | } 11 | } -------------------------------------------------------------------------------- /demo/helloWorld.js: -------------------------------------------------------------------------------- 1 | var z = new zcoil() 2 | z.init({ 3 | data(){ 4 | return { 5 | message: "hello world " 6 | } 7 | }, 8 | name(yourName){ 9 | this.message+=yourName 10 | } 11 | }) 12 | z.name("channg") 13 | 14 | console.log(z.message) -------------------------------------------------------------------------------- /docs/_sidebar.md: -------------------------------------------------------------------------------- 1 | * **醉里挑灯看剑** 2 | * [为什么要创造这个库](WHY.md) 3 | * [前言](GUIDE.md) 4 | * [入门](FIRST.md) 5 | * [监听数据变化](SECOND.md) 6 | * [队列执行函数](THIRD.md) 7 | * **梦回吹角连营** 8 | * [浏览器存储](SPRING.md) 9 | * [合并实例对象](SUMMER.md) 10 | * [强制更新](AUTUMN.md) 11 | * **马作的卢飞快** 12 | * [与其他框架混用(vue)](MIXIN.md) -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | export function isPromise(obj:any) { 2 | return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function'; 3 | } 4 | 5 | export function getTimestamp(){ 6 | return new Date().getTime() 7 | } 8 | 9 | export function isFunction(functionToCheck:any) { 10 | return functionToCheck && {}.toString.call(functionToCheck) === '[object Function]'; 11 | } -------------------------------------------------------------------------------- /demo/asyncData.js: -------------------------------------------------------------------------------- 1 | var z = new zcoil() 2 | z.init({ 3 | data() { 4 | return { 5 | message: "hello world " 6 | } 7 | }, 8 | asyncGetName() { 9 | return new Promise((resolve, reject) => { 10 | setTimeout(() => { 11 | resolve('channg') 12 | }, 2000) 13 | }) 14 | }, 15 | name() { 16 | this.asyncGetName().then((name) => { 17 | this.message += name 18 | }) 19 | } 20 | }) 21 | 22 | z.name() 23 | z.$watch((from,to)=>{ 24 | debugger 25 | }) -------------------------------------------------------------------------------- /src/scoil.ts: -------------------------------------------------------------------------------- 1 | import forIn = require('lodash/forIn') 2 | import has = require( 'lodash/has') 3 | export default class scoil { 4 | model: any = {}; 5 | constructor(_model: any, zcoil: any, data: any) { 6 | forIn(_model, (value, key) => { 7 | if (has(data, key)) { 8 | this.model[key] = data[key] 9 | } else { 10 | this.model[key] = value 11 | } 12 | }) 13 | this.model['_call'] = zcoil._call 14 | this.model.$zcoil = zcoil 15 | }; 16 | } -------------------------------------------------------------------------------- /docs/AUTUMN.md: -------------------------------------------------------------------------------- 1 | # 追究 2 | 3 | 如果一个数据在方法运行外部被改变,`zcoil`应该如何得知呢? 4 | 5 | `zcoil`没有使用`definePrototype`,所有当数据在方法外部被改变的时候,`zcoil`也就无法知晓,从而产生`$watch`无法得知数据变动的后果。 6 | 7 | ``` 8 | var z = new zcoil() 9 | z.init({ 10 | data(){ 11 | return { 12 | message: "hello world " 13 | } 14 | } 15 | }) 16 | z.messsage="abc" //不会触发$watch 17 | ``` 18 | 19 | 如果我们必须要在程序外部使用数据并进行赋值操作,我们必须要主动通知。 20 | 21 | 使用`z.$commit()`主动通知程序。 22 | 23 | ``` 24 | var z = new zcoil() 25 | z.init({ 26 | data(){ 27 | return { 28 | message: "hello world " 29 | } 30 | } 31 | }) 32 | z.messsage="abc" //不会触发$watch 33 | z.$commit() 34 | ``` 35 | 调用了`$commit()`之后,`$watch`便可以得知数据的状态是否被更新了。 -------------------------------------------------------------------------------- /docs/WINTER.md: -------------------------------------------------------------------------------- 1 | # 寒冬 2 | 3 | 很高兴,你能看到这里。 4 | 5 | 但是接下来我只想说一些废话,吐槽一下。 6 | 7 | --- 8 | 9 | 冬至来了,春天还会远吗。 10 | 11 | 还是很远,有很多人,并不能熬过这个冬天。 12 | 13 | 从这个框架初到至今,一个月都在反复的琢磨,疲惫、焦虑、遇到困难的时候,并没有一个人能交流,焦头烂额的时候只能自己抓头发。 14 | 15 | 但那时候还是**春天**,每解决一个问题,就到了花开的时候,芳香四溢。 16 | 17 | 那时候我相信,我的作品可以帮助很多人解决问题,或者通过我的源代码学习到一点点新的知识,我就心满意足了。因为这就是**开源**的力量 18 | 19 | 直到现在,我想寻求一些伙伴,或者说推广一下我这一个月的努力成果的时候。**冬天**来了。 20 | 21 | 我遇到了很多人,向他们展示我的成果。而他们不会看一眼你的努力,却以挑刺为彰显自己能力的手段;被讽刺,被嘲笑,我只能默不作声。 22 | 23 | 因为没有**成绩** 24 | 25 | 我不想向他们去解释,这可能我内心还是有自己的骄傲。 26 | 27 | star = 1 1 = `我` 28 | 29 | 我不在意 star 的数量,我只想得到一些客观的评价。 30 | 31 | --- 32 | 33 | 如果你看过`zocil`的使用方法,应该发现和`vue`相同,将数据集成在了内部。 34 | 35 | 当然这很不利于在现有项目中使用。 36 | 37 | 我还是这么设计了。 38 | 39 | 原因呢? 40 | 41 | `人总要有理想的不是吗`,未来我会自己去实现`view`层。 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/serialize.ts: -------------------------------------------------------------------------------- 1 | import {setItem,getItem,removeItem} from 'localforage' 2 | import {getTimestamp} from './utils' 3 | 4 | export function serializeData(id:string,data:any){ 5 | return setItem(id,data) 6 | } 7 | 8 | export function getData(id:string){ 9 | return new Promise((resolve,reject)=>{ 10 | getItem(`_${id}_deadline`).then((time:number)=>{ 11 | if(time&&time>getTimestamp()){ 12 | getItem(id).then((data)=>{ 13 | resolve(data) 14 | }).catch(()=>{ 15 | resolve() 16 | }) 17 | }else{ 18 | resolve() 19 | removeItem(id) 20 | removeItem(`_${id}_deadline`) 21 | } 22 | }) 23 | }) 24 | } -------------------------------------------------------------------------------- /demo/$coil.js: -------------------------------------------------------------------------------- 1 | var z = new zcoil() 2 | z.init({ 3 | data() { 4 | return { 5 | message: "hello world " 6 | } 7 | }, 8 | asyncGetSaySomething(param) { 9 | return new Promise((resolve, reject) => { 10 | setTimeout(() => { 11 | resolve(param) 12 | }, 1000) 13 | }) 14 | }, 15 | say(param) { 16 | this.asyncGetSaySomething(param).then((say) => { 17 | this.message += "," + say 18 | }) 19 | }, 20 | endToSay(){ 21 | this.message += ",come on " 22 | } 23 | }) 24 | 25 | z.$watch((from,to)=>{ 26 | console.log(from.message) 27 | console.log(to.message) 28 | }) 29 | 30 | 31 | var hl = z.$coil().say("Thank your star this project") 32 | 33 | hl = hl.endToSay() 34 | 35 | hl = hl.say("It works really well") 36 | 37 | hl.exec((data)=>{ 38 | debugger 39 | }) 40 | 41 | -------------------------------------------------------------------------------- /docs/SUMMER.md: -------------------------------------------------------------------------------- 1 | # 穿针引线 2 | 3 | `zcoil`提供了一个方法,用于合并两个`zocil`实例对象。 4 | 5 | ```javascript 6 | var z3 = new zcoil() 7 | z3.init({ 8 | data() { 9 | return { 10 | index: 2, 11 | type:'last' 12 | } 13 | }, 14 | assignment() { 15 | this.index = 200 16 | } 17 | } 18 | ) 19 | 20 | var z4 = new zcoil() 21 | z4.init({ 22 | data() { 23 | return { 24 | index: 4 25 | } 26 | }, 27 | assignment() { 28 | this.index = 300 29 | } 30 | } 31 | ) 32 | ``` 33 | 34 | 使用`zcoil.$assign(z3,z4)` 35 | 36 | ``` 37 | var zassign = zcoil.$assign(z3,z4) 38 | ``` 39 | 前者相同的方法,属性,将会被后者覆盖。 40 | 41 | ```javascript 42 | zassign.index // 4 43 | zassign.assignment() 44 | zassign.index //300 45 | ``` 46 | >**tips** 47 | > 48 | >`zcoil.$assign` 相当于新生成了一个zcoli实例对象,之前的对象如果发生改变,生成的对象不会同步改变。 49 | 50 | -------------------------------------------------------------------------------- /docs/MIXIN.md: -------------------------------------------------------------------------------- 1 | # 与其他框架混用 2 | 3 | 当我们并不需要将数据存储在`zcoil`中时。我们可以不去传入`data方法`。我们需要在`new zcoil`传入我们存储数据的变量。 4 | 5 | ```javascript 6 | new zcoil({ 7 | mixin:xxx 8 | }) 9 | ``` 10 | 11 | 这样我们就能在程序中 使用`this.$mixin`去获取传入的数据用于赋值操作了。 12 | 13 | ## 与vue混用 14 | 15 | `zcoil`实例本身就是一个vue插件 16 | ```javascript 17 | var z = new zcoil() 18 | z.init(...) 19 | ``` 20 | 当实例话了一个`zcoil`对象之后,直接可以使用 21 | ``` 22 | Vue.use(z) 23 | ``` 24 | 25 | 这时候,在你的vue组件中的就可以使用`this.$zcoil`获取你使用`Vue.use(z)`的`z`变量。 26 | 27 | 这时候,你可以在`created`中使用 28 | ``` 29 | created(){ 30 | this.$zcoil.$invoke(this) 31 | } 32 | ``` 33 | 这个方法是将`z`内部属性名与`this`相同的变量同步到`this`中。 34 | ```javascript 35 | z.init({ 36 | data:{ 37 | message:10 38 | } 39 | }) 40 | 41 | new Vue({ 42 | data(){ 43 | reuturn { 44 | message:0 45 | } 46 | }, 47 | created(){ 48 | this.$zcoil.$invoke(this) 49 | //这时候 this.message的值将会变为10 50 | } 51 | }) 52 | ``` 53 | 54 | 接下来你就可以在`vue`中使用`zcoil`了 55 | -------------------------------------------------------------------------------- /demo/in_vue.js: -------------------------------------------------------------------------------- 1 | var z = new zcoil() 2 | z.init({ 3 | data() { 4 | return { 5 | message: "hello world " 6 | } 7 | }, 8 | asyncGetName() { 9 | return new Promise((resolve, reject) => { 10 | setTimeout(() => { 11 | resolve('my friend') 12 | }, 200) 13 | }) 14 | }, 15 | asyncGetSaySomething(param) { 16 | return new Promise((resolve, reject) => { 17 | setTimeout(() => { 18 | resolve(param) 19 | }, 100) 20 | }) 21 | }, 22 | name() { 23 | this.asyncGetName().then((name) => { 24 | this.message += name 25 | }) 26 | }, 27 | say(param) { 28 | this.asyncGetSaySomething(param).then((say) => { 29 | this.message += "," + say 30 | }) 31 | } 32 | }) 33 | 34 | Vue.use(z) 35 | var vm = new Vue({ 36 | el:"#vm", 37 | data(){ 38 | return{ 39 | z:this.$zcoil, 40 | message:"" 41 | } 42 | }, 43 | store:z, 44 | created(){ 45 | this.z.$invoke(this) 46 | this.z.$coil().say("enjoy this ,").name().exec() 47 | } 48 | }) -------------------------------------------------------------------------------- /test-storage/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Mocha 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/vue.ts: -------------------------------------------------------------------------------- 1 | import forIn = require('lodash/forIn'); 2 | /** 3 | * vue install function 4 | * @param Vue 5 | * @param options 6 | */ 7 | export function install(Vue:any, options:any) { 8 | Vue.prototype.$zcoil = this 9 | /** 10 | * $use to watch the data and save in vm 11 | * @param vm 12 | * @param options 13 | */ 14 | this.$invoke = (vm:any, options:any)=> { 15 | this.$watch((from:any, to:any)=> { 16 | if (!!options) { 17 | if(Array.isArray(options)){ 18 | options.forEach((key)=>{ 19 | vm[key] = to[key] 20 | }) 21 | }else{ 22 | forIn(options,(value:any,key:any)=>{ 23 | vm[key] = to[value] 24 | }) 25 | } 26 | }else{ 27 | forIn(to, (value:any, key:any)=> { 28 | if (vm[key] !== undefined) { 29 | vm[key] = value 30 | } 31 | }) 32 | } 33 | }) 34 | } 35 | } -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | zcoil 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 19 | 20 | 21 |
22 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Getting Started 4 | 37 | 38 | 39 |
40 |
41 |
42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /docs/SPRING.md: -------------------------------------------------------------------------------- 1 | # 更多 2 | 3 | `zcoil`内集成了浏览器数据存储,可以快捷的将`data`存入浏览器中。 4 | 5 | ```javascript 6 | new zcoil({ 7 | name:"channg", 8 | localStorage:true 9 | }) 10 | ``` 11 | 12 | 初始化`zcoil`时,传入的对象含有`name`和`localStorage:true`属性。当`data`内的数据被更新时,会被同步到浏览器的存储中。 13 | 14 | >zcoil 中浏览器存储使用的 localForage ,由于 localForage 机制原因, 存储为异步函数,所以不能保证数据被更新时,立刻能被存储到浏览器中。 15 | 16 | ### 获取 17 | 18 | 存储在浏览器的数据 可以使用 `new zcoil().$deserialize()`方法同步到`data`中。 19 | 20 | 如果你并不想使用`zcoil`自动同步数据,加入`cover:false`,`$deserialize()`被调用的时候不会自动同步数据,需要手动注入方法同步 21 | ```javascript 22 | var z = new zcoil({ 23 | name:"channg", 24 | localStorage:true, 25 | cover:false 26 | }) 27 | z.init({ 28 | data(){ 29 | ... 30 | }, 31 | some(){ 32 | this.$deserialize((data)=>{ 33 | this.some = data.some 34 | .... 35 | }) 36 | } 37 | }) 38 | ``` 39 | 40 | ### 进阶 41 | 42 | 使用`$coil`链式同步 43 | 44 | ```javascript 45 | var coil = z.$coil().$deserialize() 46 | 47 | coil.dosomething() 48 | ``` 49 | 50 | 使用`$coil`调用`$deserialize`之后,剩下的所有方法都会在同步数据之后被执行,非常简单易用。 51 | 52 | 53 | ### 超时时间 54 | 55 | ```javascript 56 | var z = new zcoil({ 57 | name:"channg", 58 | localStorage:true, 59 | cover:false 60 | deadline:60 * 60 *24 61 | }) 62 | ``` 63 | 64 | `deadline` 是有效时间,单位为秒,超时后同步数据将失效。默认` deadline: 30 * 24 * 3600` 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /docs/GUIDE.md: -------------------------------------------------------------------------------- 1 | # 楔子 2 | 3 | 任何事物的诞生都有理由,一个框架也是。 4 | 5 | `zcoil`的诞生,便是为了解决更复杂的数据操作场景的一个`model`层框架。当程序中数据获取、更新的方式随着产品迭代逐步提升时,对阅读代码,以及增加需求的时间成本会高度增加。 6 | 7 | `zcoil`的初衷便是为了解决如何在复杂数据交互场景下进行`更快捷、优雅`生产出更优美、直观的code,并含有许多很酷的api。 8 | 9 | ## 为什么使用 10 | 使用`zocil` 可以生成多个,方法的消息队列,当你的数据发生异步请求时,你不需要再等待数据返回再执行下一个方法。直接可以按照顺序的将他们塞入消息队列中,方法的消息队列将顺序的消费他们。 11 | 举个例子,一个点击事件会发出一个请求,但依赖于初始化的时候去请求`userinfo`,这时候你只需要在初始化的时候创建消息队列,init的时候塞入请求`userinfo`的方法,接下来的点击事件,你不需要关心`userinfo`是否完成,只需要继续向消息队列塞入方法。 12 | 13 | 使用`zocil`可以使用高性能的`$watch`,`zocil`的`$watch`模型,基于`AOP`,完全基于用户行为,不会消耗任何性能。 14 | 15 | 使用`zocil`可以超级快速的将数据存入本地浏览器内,内置了`localforage`兼容包括ie8及以上的现代浏览器,可以在第二次进入页面的时候快速取出数据。 16 | 17 | `zocil`也是一个很好入手去实践的框架,学习`zcoil`你可以理解框架的原理。在创作的时候,我遇到了很多包括:`this`指向;`浅复制`;`深复制`的问题。 18 | 在框架搭建过程中,也解决了很多问题:`lodash`按需加载;`travis ci `配合`chrome headless`与`mocha`自动化测试;`webpack`导出实例。 19 | 学习一个框架确实收获很多。 20 | 21 | ## 前言 22 | 使用zcoil的时候,你可能需要知晓的知识点 23 | 24 | ### Promise 25 | 26 | 使用**new Promise()**创建一个`Promise`对象Promise 27 | ``` 28 | new Promise( function(resolve, reject) {...} /* executor */ ); 29 | ``` 30 | 31 | ### EventLoop 32 | 33 | JavaScript 的并发模型基于"事件循环"。这个模型与像 C 或者 Java 这种其它语言中的模型截然不同。 34 | 35 | ## 接下来 36 | 那么,我们可以开始新的旅程了。 37 | ``` 38 | npm install zcoil 39 | ``` 40 | ## [ -> 入门 <-](FIRST.md) 41 | -------------------------------------------------------------------------------- /src/watch.ts: -------------------------------------------------------------------------------- 1 | import isEqual =require('lodash/isEqual'); 2 | import cloneDeep =require('lodash/cloneDeep'); 3 | import get = require( 'lodash/get') 4 | 5 | /** 6 | * watch 每调用一次$watch 就会初始化一个 watch 对象i 7 | */ 8 | export class watch { 9 | _expression: string; 10 | _expression_is_array: Boolean 11 | _init_data: any 12 | _callback: Function 13 | 14 | _on_data_change(to: any) { 15 | // expression 为string 16 | if (this._expression && !this._expression_is_array) { 17 | var isEq = this._expressionEquals(this._expression, to, this._init_data) 18 | if (!isEq) { 19 | this._callback(get(this._init_data, this._expression), get(to, this._expression)) 20 | this._init_data = cloneDeep(to) 21 | } 22 | } else { 23 | if (!isEqual(to, this._init_data)) { 24 | let deep_to = cloneDeep(to) 25 | this._callback(this._init_data, deep_to) 26 | this._init_data = deep_to 27 | } 28 | } 29 | } 30 | 31 | _expressionEquals(expression: any, to: any, from: any) { 32 | return isEqual(get(to, expression), get(from, expression)) 33 | } 34 | 35 | constructor(expression: any, callback: Function, _data: any) { 36 | this._callback = callback 37 | this._init_data = _data 38 | if (typeof expression == 'string') { 39 | this._expression = expression 40 | this._expression_is_array = false 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /docs/SECOND.md: -------------------------------------------------------------------------------- 1 | # 窥镜 2 | 3 | 对于一个数据的改变,我们可能有几种方式得知呢? 4 | * 当一个数据更新的时候。 5 | * 当数据有可能发生改变的时候进行判断。 6 | * 定时获取数据状态,定时判断。 7 | 8 | `zcoil`使用的是第二种,在数据有可能发生改变的时候进行判断。下面将要介绍,如何在`zcoil`中获取数据改变的时机。 9 | 10 | ## $watch 11 | >使用$watch来监听数据的变化。 12 | 13 | `z.$watch(param?,callback)` 14 | params:`String` 需要监听数据的key,若不填,将检测所有数据变化。 15 | callback:`Function`回调函数,返回参数携带`from`,`to`为数据变化前与数据变化后的值,若填写了`param`,将会针对性的返回`from`与`to`,不会讲整个数据全部返回。 16 | 17 | 18 | 19 | ### example 20 | ```javascript 21 | var z = new zcoil() 22 | z.init({ 23 | data() { 24 | return { 25 | message: "hello world " 26 | } 27 | }, 28 | asyncGetName() { 29 | return new Promise((resolve, reject) => { 30 | setTimeout(() => { 31 | resolve('channg') 32 | }, 2000) 33 | }) 34 | }, 35 | name() { 36 | this.asyncGetName().then((name) => { 37 | this.message += name 38 | }) 39 | } 40 | }) 41 | z.$watch((from,to)=>{ 42 | from.message //hello world 43 | to.message //hello world channg 44 | }) 45 | 46 | z.$watch('message',(from,to)=>{ 47 | from //hello world 48 | to //hello world channg 49 | }) 50 | 51 | z.name() 52 | ``` 53 | 54 | ## 探索 55 | 56 | `$watch` 是如何做到检测数据变动的呢? 57 | 58 | 在`zcoil`中,所有方法都会在执行前执行后被标记。对于`async**`方法,会提前解析返回的`promise`,同样在执行前后进行标记,并返回一个新的被包装的`promise`对象。 59 | 60 | 很显然`$watch`是惰性的,只有当特定条件下被触发的时候才会被执行。也正是这一点,使得它性能相当优越,可以作为常规性手段进行使用。 61 | 62 | 也正是使用了这种惰性检测的方法,使得`zcoil`的拥有更好的兼容性,因为不依赖于ES5的`Object.defineProperty()`。 63 | 64 | ### 接下来 65 | 66 | `$watch`只是`zocil`中最普通的武器,接下来你讲面对的是`zcoil`的杀手锏。 67 | 68 | 69 | ## [ -> 队列执行函数 <-](THIRD.md) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zcoil", 3 | "version": "0.0.5", 4 | "description": "zcoil", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "webpack --mode development", 8 | "build": "cross-env webpack --mode production && mocha", 9 | "server": "webpack-dev-server --mode development --port 8088", 10 | "serve": "cross-env npm run opn && npm run server", 11 | "testnode": "mocha", 12 | "test": "cross-env npm run testnode && npm run teststorage", 13 | "opn": "opn http://localhost:8088/", 14 | "teststorage": "mocha-chrome ./test-storage/index.html", 15 | "doc": "docsify serve ./docs" 16 | }, 17 | "keywords": [ 18 | "webpack" 19 | ], 20 | "repository": "https://github.com/channg/zcoil", 21 | "author": "channg", 22 | "license": "MIT", 23 | "devDependencies": { 24 | "@types/jquery": "^3.3.0", 25 | "@types/lodash": "^4.14.109", 26 | "babel-loader": "^7.1.4", 27 | "babel-plugin-lodash": "^3.3.4", 28 | "babel-preset-es2015": "^6.24.1", 29 | "cross-env": "^5.2.0", 30 | "css-loader": "^0.28.10", 31 | "less": "^3.0.1", 32 | "less-loader": "^4.0.6", 33 | "lodash-webpack-plugin": "^0.11.5", 34 | "mocha": "^5.2.0", 35 | "mocha-chrome": "^1.1.0", 36 | "opn-cli": "^3.1.0", 37 | "style-loader": "^0.20.2", 38 | "ts-loader": "^4.0.1", 39 | "typescript": "^2.7.2", 40 | "uglifyjs-webpack-plugin": "^1.2.6", 41 | "webpack": "^4.0.1", 42 | "webpack-cli": "^2.0.4", 43 | "webpack-dev-server": "^3.1.0" 44 | }, 45 | "dependencies": { 46 | "localforage": "^1.7.2", 47 | "lodash": "^4.17.10" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /test-storage/test-vue.js: -------------------------------------------------------------------------------- 1 | var expect = chai.expect; 2 | 3 | var z = new zcoil() 4 | z.init({ 5 | data() { 6 | return { 7 | message: "hello world " 8 | } 9 | }, 10 | asyncGetName() { 11 | return new Promise((resolve, reject) => { 12 | setTimeout(() => { 13 | resolve('my friend') 14 | }, 100) 15 | }) 16 | }, 17 | asyncGetSaySomething(param) { 18 | return new Promise((resolve, reject) => { 19 | setTimeout(() => { 20 | resolve(param) 21 | }, 200) 22 | }) 23 | }, 24 | name() { 25 | this.asyncGetName().then((name) => { 26 | this.message += name 27 | }) 28 | }, 29 | say(param) { 30 | this.asyncGetSaySomething(param).then((say) => { 31 | this.message += "," + say 32 | }) 33 | } 34 | }) 35 | Vue.use(z) 36 | describe('zcoil with vue', function() { 37 | it('vue use $coil', function(done) { 38 | var vm = new Vue({ 39 | el:"#vm", 40 | data(){ 41 | return{ 42 | z:this.$zcoil, 43 | message:"" 44 | } 45 | }, 46 | watch:{ 47 | message(some){ 48 | if(some==='hello world ,enjoy this ,'){ 49 | expect(z.message).to.be.equal(this.message); 50 | 51 | } 52 | if(some==='hello world ,enjoy this ,my friend'){ 53 | expect(z.message).to.be.equal(this.message); 54 | done() 55 | } 56 | } 57 | }, 58 | store:z, 59 | created(){ 60 | this.z.$invoke(this) 61 | this.z.$coil().say("enjoy this ,").name().exec() 62 | } 63 | }) 64 | }); 65 | }) 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin') 2 | const LodashModuleReplacementPlugin = require('lodash-webpack-plugin'); 3 | module.exports = (env, argv) => { 4 | const config = { 5 | entry: { 6 | zcoil: "./src/index.ts" 7 | }, 8 | 9 | module: { 10 | rules: [{ 11 | test: /\.less$/, 12 | use: [{ 13 | loader: "style-loader" // creates style nodes from JS strings 14 | }, { 15 | loader: "css-loader" // translates CSS into CommonJS 16 | }, { 17 | loader: "less-loader" // compiles Less to CSS 18 | }] 19 | }, { 20 | test: /\.tsx?$/, 21 | use: [ 22 | { 23 | loader: 'babel-loader', 24 | options: { 25 | plugins: ['lodash'] 26 | } 27 | }, { 28 | loader: 'ts-loader' 29 | }], 30 | exclude: /node_modules/, 31 | } 32 | ] 33 | }, 34 | optimization: { 35 | splitChunks: {} 36 | }, 37 | resolve: { 38 | extensions: ['.tsx', '.ts', '.js'] 39 | }, 40 | output: { 41 | globalObject: 'this', 42 | libraryTarget: 'umd', 43 | library: "zcoil", 44 | filename: "[name].js", 45 | libraryExport: 'default' 46 | }, 47 | plugins: [ 48 | new LodashModuleReplacementPlugin, 49 | ] 50 | } 51 | if (argv.mode === 'development') { 52 | config.devtool = 'inline-source-map' 53 | } else { 54 | config.plugins = [ 55 | new UglifyJsPlugin({ 56 | uglifyOptions: { 57 | compress: { 58 | warnings: false 59 | }, 60 | output: { 61 | comments: false 62 | } 63 | } 64 | }) 65 | ] 66 | } 67 | return config 68 | } -------------------------------------------------------------------------------- /docs/FIRST.md: -------------------------------------------------------------------------------- 1 | # 入门 2 | 3 | ## 引入 4 | 5 | 6 | ``` 7 | import zcoil from 'zcoil' 8 | ``` 9 | `or` 10 | 11 | ``` 12 | var zocil = require('zocil') 13 | ``` 14 | `or` 15 | 16 | **直接引用dist目录下的 zcoil.js文件** 17 | 18 | ## 初始化 19 | 20 | 使用new 创建一个zcoil实例对象,并调用init 方法 进行初始化 21 | 22 | ```javascript 23 | var z = new zcoil() 24 | z.init({ 25 | data(){ 26 | return { 27 | message: "hello world" 28 | } 29 | } 30 | }) 31 | ``` 32 | 使用`init`方法对实例化的zocil对象进行初始化,`init`方法有且只有一个参数,一个包含着函数键值对的对象。 33 | 这个对象必须有一个`data`方法,并`return`了初始化时的参数。 34 | 35 | 当我们完成了这些,我们就可以开始使用`zcoil`了。 36 | ``` 37 | z.message //hello world 38 | ``` 39 | 40 | ## 尝试 41 | 42 | `init`方法参数对象中,可以添加更多方法,并可以直接调用。 43 | ```javascript 44 | var z = new zcoil() 45 | z.init({ 46 | data(){ 47 | return { 48 | message: "hello world " 49 | } 50 | }, 51 | name(yourName){ 52 | this.message+=yourName 53 | } 54 | }) 55 | 56 | z.name("channg") 57 | console.log(z.message) //hello world channg 58 | ``` 59 | 看起来是不是很不错,你可以将所有数据操作全部作为方法来调用,语义化的方法名,可以更好的让人理解程序。 60 | 61 | ## 异步数据 62 | 63 | 在zcoil中使用异步,需要多一个步骤。 64 | 65 | ```javascript 66 | var z = new zcoil() 67 | z.init({ 68 | data() { 69 | return { 70 | message: "hello world " 71 | } 72 | }, 73 | asyncGetName() { 74 | return new Promise((resolve, reject) => { 75 | setTimeout(() => { 76 | resolve('channg') 77 | }, 2000) 78 | }) 79 | }, 80 | name() { 81 | this.asyncGetName().then((name) => { 82 | this.message += name 83 | }) 84 | } 85 | }) 86 | 87 | z.name() 88 | ``` 89 | `asyncGetName` 是一个返回Promise的异步方法。`asyncGetName`返回的Promise对象,在`name`方法中被执行,改变了`message`的值,两者只有调用关系,互相没有交集,从而做到数据隔离, 90 | 91 | 但是这样会有一个问题。 92 | `z.name()`被调用的时候,`message`的值并没有发生改变,那么我们需要在什么时候获取更新后的message的值呢。 93 | 94 | 当然,我们需要下回分解。 95 | 96 | ## [ -> 监听数据变化 <-](SECOND.md) 97 | 98 | -------------------------------------------------------------------------------- /test-storage/tests.js: -------------------------------------------------------------------------------- 1 | var expect = chai.expect; 2 | 3 | 4 | 5 | 6 | var z6 = new zcoil({ 7 | name:"test01", 8 | localStorage:true 9 | }) 10 | z6.init({ 11 | data() { 12 | return { 13 | index: 4 14 | } 15 | }, 16 | assignment() { 17 | this.index = 300 18 | } 19 | } 20 | ) 21 | z6.assignment() 22 | describe('zcoil serialize', function() { 23 | it('The value is correct when not serialized.', function() { 24 | expect(z6.index).to.be.equal(300); 25 | }); 26 | it('Check the value after serialization(Will succeed on the second).', function(done) { 27 | localforage.setItem('test01',{index:400}).then(()=>{ 28 | localforage.setItem('_test01_deadline',new Date().getTime()+1000000).then(()=>{ 29 | z6.$deserialize().then((data)=>{ 30 | expect(z6.index).to.be.equal(400); 31 | expect(data.index).to.be.equal(400); 32 | done() 33 | }) 34 | }) 35 | }) 36 | }); 37 | it('Test zcoil in vue with $mixin.', function(done) { 38 | var vm = new Vue({ 39 | data() { 40 | return { 41 | z: {}, 42 | coil: {}, 43 | message: "hello world ", 44 | } 45 | }, 46 | created() { 47 | this.z = new zcoil({ 48 | mixin:this 49 | }) 50 | this.z.init({ 51 | asyncGetSaySomething(param) { 52 | return new Promise((resolve, reject) => { 53 | setTimeout(() => { 54 | resolve(param) 55 | }, 200) 56 | }) 57 | }, 58 | say(param) { 59 | this.asyncGetSaySomething(param).then((say) => { 60 | this.$mixin.message += "," + say 61 | }) 62 | }, 63 | endToSay() { 64 | this.$mixin.message += ",come on " 65 | } 66 | }) 67 | }, 68 | methods: { 69 | dosome() { 70 | this.coil = this.z.$coil().say("lll") 71 | this.coil = this.coil.endToSay() 72 | this.coil = this.coil.say("It works really well") 73 | this.coil = this.coil.exec((data)=>{ 74 | expect(data.$mixin.message).to.be.equal('hello world ,lll,come on ,It works really well'); 75 | expect(this.message).to.be.equal('hello world ,lll,come on ,It works really well'); 76 | done() 77 | }) 78 | } 79 | } 80 | }).$mount("#vm") 81 | vm.dosome() 82 | }); 83 | 84 | }); 85 | 86 | 87 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

zcoil

2 | 3 |

4 | Travis Status 5 | npm 6 | license 7 |

8 | 9 | 10 | 11 | 12 | > `Zcoil` is a model layer framework for more convenient and elegant data manipulation 13 | 14 |

document

15 | 16 | >Currently only Chinese documents are available 17 | 18 | [中文文档](https://channg.github.io/zcoil/) 19 | 20 |

Let's start with the demo

21 | 22 | > The demo uses queues to execute methods 23 | 24 | https://channg.github.io/zcoil/demo.html 25 | 26 |

use

27 |

install zcoil with npm

28 | 29 | ``` 30 | npm i zcoil 31 | ``` 32 |

easy to use in Hello world

33 | 34 | **Instantiated object** 35 | 36 | ```javascript 37 | var z = new zcoil() 38 | ``` 39 | 40 | **initialization with param** 41 | 42 | ```javascript 43 | z.init({ 44 | data() { 45 | return { 46 | message: "hello world " 47 | } 48 | }, 49 | asyncGetSaySomething(param) { 50 | return new Promise((resolve, reject) => { 51 | setTimeout(() => { 52 | resolve(param) 53 | }, 1000) 54 | }) 55 | }, 56 | say(param) { 57 | this.asyncGetSaySomething(param).then((say) => { 58 | this.message += "," + say 59 | }) 60 | }, 61 | endToSay(){ 62 | this.message += ",come on " 63 | } 64 | }) 65 | ``` 66 | **Magical method call process with**`$coil()` 67 | 68 | ```javascript 69 | var hl = z.$coil().say("Thank your star this project") 70 | 71 | hl = hl.endToSay() 72 | 73 | hl = hl.say("It works really well") 74 | 75 | hl.exec((data)=>{ 76 | data.message //"hello world ,Thank your star this project,come on ,It works really well" 77 | z.message //"hello world ,Thank your star this project,come on ,It works really well" 78 | }) 79 | ``` 80 | 81 | **You can use the `$watch` method to get the data before the `$coil` method is executed** 82 | 83 | ```javascript 84 | z.$watch((from,to)=>{ 85 | console.log(from.message) 86 | console.log(to.message) 87 | }) 88 | ``` 89 | 90 |

test it online

91 | 92 | try it in[ jsfiddle](https://jsfiddle.net/channg/uhfxqsj4/11/) 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /docs/THIRD.md: -------------------------------------------------------------------------------- 1 | # 探索 2 | 3 | 对于`javascript`而言,异步方法的调用,算得上是一个比较令人头痛的问题了。单线程的特性,也从始至终贯彻在我们的code之中。从嵌套的`callback`函数,接着是更优雅的`promise`,再接着变为更趋近于同步写法的`async\await`,或是更优秀的响应式编程库`rxjs`,`javascript`开发者都在进步着。 4 | 5 | `zcoil`也会带来一种全新的异步开发体验。 6 | 7 | ```javascript 8 | var z = new zcoil() 9 | z.init({ 10 | data() { 11 | return { 12 | message: "hello world " 13 | } 14 | }, 15 | asyncGetName() { 16 | return new Promise((resolve, reject) => { 17 | setTimeout(() => { 18 | resolve('my friend') 19 | }, 2000) 20 | }) 21 | }, 22 | asyncGetSaySomething(param) { 23 | return new Promise((resolve, reject) => { 24 | setTimeout(() => { 25 | resolve(param) 26 | }, 1000) 27 | }) 28 | }, 29 | name() { 30 | this.asyncGetName().then((name) => { 31 | this.message += name 32 | }) 33 | }, 34 | say(param) { 35 | this.asyncGetSaySomething(param).then((say) => { 36 | this.message += "," + say 37 | }) 38 | } 39 | }) 40 | 41 | z.name() 42 | z.say("how are you ?") 43 | ``` 44 | 45 | 这段代码是中最后调用了两个异步方法,我们希望最后的`z.message`的结果是`hello world my friend,how are you ?`。但很明显,因为执行时间的不同,这段代码最后`z.message`的结果是`hello world ,how are you ?my friend`。 46 | 47 | 如何让执行顺序符合我们的预期呢。 48 | 49 | ## $coil 50 | 51 | >$coil 创造方法可以被链式调用的生成器。 52 | 53 | 现在可以对上面的代码进行包装。 54 | 55 | ```javascript 56 | z.$coil().name().say("how are you ?").exec((data)=>{ 57 | data.message //hello world my friend,how are you ? 58 | z.message //hello world my friend,how are you ? 59 | }) 60 | ``` 61 | 62 | 这时候,当链式调用结束的时候调用`exec`,并可以从回调函数中获得执行结束的数据。这时候`z.message`的结果就是`hello world my friend,how are you ?`。 63 | 64 | ### exec 65 | `exec`函数表示执行当前顺序执行之前所有的函数。 66 | 67 | ```javascript 68 | z.$coil().some().exec((data,error)=>{ 69 | }) 70 | ``` 71 | `exec`会在执行钩子的时候传入两个参数。 72 | 73 | `data`与`z.data`内的数据相同,表示执行队列已经完成最终的到的数据。 74 | 75 | `error`表示在之前的函数调用中出现过`reject`,如果没有出现`reject`,`error`将返回`null`。 76 | 77 | 如果再之前的函数调用只返回一个错误,`error`将等于`reject`传递的数据,如果再调用过程中出现了大于一个的错误,`error`会作为一个包含`Error`的数组被返回。 78 | 可以在方法中统一处理`error` 79 | 80 | >tips 81 | > 82 | >不管同步方法,还是异步方法,都可以被链式调用执行,但如果方法含有`return`将会被忽略掉。 83 | 84 | ### 出鞘 85 | 86 | 使用`$coil`确实可以解决一些异步的问题,但是显然是这段代码被运行在同一时间点。如果需要在不同时间执行这些代码,例如`点击事件`之后异步调用,但有部分需要提前获取。 87 | 88 | 所以在`$coil()`之后被链式调用的所以方法,全部会返回一个`$coil`对象,这个对象可以再任何时候被调用。使用`$coil`可以完成全新的异步方法体验。 89 | 90 | ```javascript 91 | var messageGet = z.$coil().name().say("^_^").exec((data)=>{ 92 | console.log(data.message) //hello world my friend,^_^ 93 | }) 94 | messageGet.say("lululu").exec((data)=>{ 95 | console.log(data.message) //hello world my friend,^_^,lululu 96 | }) 97 | ``` 98 | 99 | >tips 100 | > 101 | >`$coil`链式调用执行的方法,只会在`exec`执行的时候被执行。链式调用只是将方法加入到`执行队列中`当执行队列被塞入了`exec`,这个队列将会被顺序调用。 102 | 103 | ### 参数 104 | 105 | `$coil`方法含有几个参数需要注意一下。 106 | 107 | ```javascript 108 | export interface coilConif{ 109 | rollback?:Boolean; 110 | errorContinue?:Boolean; 111 | saveWithExec?:Boolean; 112 | } 113 | ``` 114 | `rollback` 115 | * 执行链调用中出现异常(`promise.reject`)时是否闪回。默认 `false` 116 | 117 | `errorContinue` 118 | * 执行链调用中出现异常(`promise.reject`)时是否继续执行。默认`true` 119 | 120 | `saveWithExec` 121 | * 执行`exec`时,是否替换闪回数据,此参数需要配合`rollback`使用,默认`true` 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /docs/demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Getting Started 4 | 5 | 6 | 12 | 21 | 53 | 54 | 55 |
56 |

zcoil github document

The asynchronous method terminates the solution, using queues

57 |
58 |
59 |
60 |
100
61 |
200
62 |
300
63 |
1000
64 |
2000
65 |
3000
66 |
5000
67 |
68 |
69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /docs/WHY.md: -------------------------------------------------------------------------------- 1 | # 理解 2 | 在学习zcoil之前,现在看一看为什么要使用`zcoil`。 3 | 4 | 来看这段普通的代码 5 | ```javascript 6 | function findFood(){ 7 | //asyncToFindFood 8 | ... 9 | } 10 | 11 | function eat(food){ 12 | ... 13 | } 14 | ``` 15 | 16 | 有一个function `findFood`,还有一个function `eat`。在程序执行的某个时间点,我会调用`findeFood`,这时候我派出了一个机器人`async`异步去寻找食物,毕竟寻找食物是要耗时的。 17 | 18 | 如果想要让`findFood`找到食物之后,吃掉这个食物。那`eat`方法就必须要在`asyncToFindFood`之后被调用。有如下代码 19 | ```javascript 20 | function findFood(){ 21 | asyncToFindFood(function(food){ 22 | eat(food) 23 | }) 24 | } 25 | ``` 26 | 很显然,这种嵌套代码不完美,有什么问题呢。 27 | * 美观性差 28 | * 后期维护复杂 29 | * 对异常处理很困难 30 | 31 | 所以说我们现在,需要一个更优秀的写法,来规避这些问题。 32 | 很开心的是,我们拥有了`async/await` 33 | 34 | 代码就就如下: 35 | ```javascript 36 | async function findFoodAndEat(){ 37 | var food = await asyncToFindFood() 38 | eat(food) 39 | } 40 | ``` 41 | 使用了`async/awiait`之后,程序美观了许多,如果发生异常,也可以直接使用`try catch`去捕获。 42 | 43 | 继续阅读这段代码,会发现一个问题,通过`asyncToFindFood`找到了多少食物,就必须要`eat`多少。 44 | 45 | 这时候的需求来了,在`findFood`的时候,我们找到的不只是只够一次吃的食物,找到的食物是无穷的,也就是说只要`findFood`成功一次,以后就不需要再进行`findFood`了,那么我们就需要在寻找的到食物的时候要存储起来,下次也可以去`eat` 46 | 这个时候,我们就需要调整代码内部的逻辑。 47 | 48 | 所以代码如下: 49 | ```javascript 50 | var food 51 | async function findFood(){ 52 | if(!food) 53 | food = await asyncToFindFood() 54 | eat(food) 55 | } 56 | ``` 57 | 上面的代码,表示,找到了食物要存储起来,如果下次再调用方法,我就去查看一下是不是有食物,如果有食物,我就不再`findFood`了,直接`eat` 58 | 59 | 这时候,新的需求又来了,我现在有了一个闹铃,闹铃响了的时候代表我饿了,必须要`eat`,程序这时候增加了一个闹铃方法`alarmGoOff`,当闹铃响起的时候`alarmGoOff`会被调用,之后我们就需要调用`eat()`,但是呢我们要保证,在闹钟响了的时候`eat`是能吃到食物的。 60 | 61 | ```javascript 62 | function alarmGoOff(){ 63 | eat(food) 64 | } 65 | ``` 66 | 67 | 那么我们要怎么保证在`alarmGoOff()`闹铃响了的时候一定能吃到食物呢? 68 | 69 | 想了一下,决定在页面加载的时候调用`findFood`。只要程序一开始运行我就先去找食物,这样之后我都会有食物,是不是可行呢? 70 | 71 | ```javascript 72 | function init(){ 73 | findFood() 74 | } 75 | init() 76 | ``` 77 | 78 | 但总有事情不凑巧,虽然在程序一开始执行就调用了`findFood`,但因为`findFood`是异步的,闹铃正好在你`findFood`期间响起来了,由于`javascript`单线程的原因,又因为`EventLoop`模型。 79 | 80 | 这时候你无法等待`findFood`结束了,直接触发了`alarmGoOff`里面的方法`eat`。 81 | 但是很明显这个时候`food`是`null` 82 | 你吃掉了一口`null` 83 | 就这样,嘎嘣一声程序就挂了。 84 | 85 | 在实际的场景中,我们经常性的会遇到这种问题,比如说一个点击事件依赖一个异步数据,这个异步数据在程序初始化的时候被执行,但有时候因为网速的原因啊,用户进行点击事件的时候异步数据还并没有返回,这时候就造成了异常。 86 | 87 | 通常我们用最简单的解决办法。**在闹铃响起来的时候,也先去寻找食物**,然后当程序触发`findFood`的时候,也去寻找食物,两者谁先进行,谁就先保存食物。 88 | 89 | ```javascript 90 | var food 91 | async function alarmGoOff(){ 92 | if(!food) 93 | food = await asyncToFindFood() 94 | eat(food) 95 | } 96 | 97 | async function findFood(){ 98 | if(!food) 99 | food = await asyncToFindFood() 100 | eat(food) 101 | } 102 | ``` 103 | 😭但是,总感觉,这两个代码好像一毛一样。一个是普通的程序执行过程中的`findFood`,另外一个是`alarmGoOff`因为闹铃响了要去吃东西。两者的代码有很大的冗余。但是这样,确实是能够保证程序没有异常 104 | 105 | 但就算如此,现在代码没有问题吗? 106 | 虽然在两个函数中都判断了`food`是否存在,但如果两者被调用的时候,两个函数都没有被返回,那么就会触发两次`asyncToFindFood()`,因为`async/awaut`本质还是异步的。虽然这样不会对程序造成异常。但貌似还是不完美。那么我们应该怎么样解决呢? 107 | 108 | 当某一个函数,在调用`asyncToFindFood`的时候,设置一个变量表示正在请求,你就不用请求了等着就行了。 109 | 110 | 看下面的代码 111 | 112 | ```javascript 113 | var food 114 | var wait 115 | async function alarmGoOff(){ 116 | if(!food&&!wait){ 117 | wait = new Promise(()=>{ 118 | food = await asyncToFindFood() 119 | resolve() 120 | }) 121 | }else if(wait&&!food){ 122 | awiat wait 123 | } 124 | 125 | eat(food) 126 | } 127 | ``` 128 | 129 | 这段代码表示 130 | * 如果没有`food`也没有`wait`,那么我们就生成一个`Promise`表示:后面的你等着吧,前面的机器人正在找食物呢,如果你也要找食物,就等我找完了再说。 131 | * 如果没有`food`但是有`wait`,很明显就是前面的机器人已经去找食物了,但是还没有回来,我们等着找完了回来直接`eat`就行了,不用再去找一遍。 132 | * 最后一种就是已经有`food`了 直接开吃 133 | 134 | 终于完美了,但是看这段代码像什么? 135 | 一个标志表示事件是否结束,如果没有结束,就等待,如果结束了,就立刻执行。 136 | 是不是,像是一个消息队列。哈哈哈,其实是因为`zcoil`就是实现了一个这样的消息队列。 137 | 138 | 使用`zcoil`超级简单的处理这种问题。 139 | 140 | 使用`zcoil`生成一个消息队列 141 | 142 | ```javascript 143 | z.$coil() 144 | ``` 145 | 146 | 在初始化的时候在方法消息队列里插入一个`findFood`方法 147 | 148 | ```javascript 149 | z.$coil().findFood() 150 | ``` 151 | 152 | 不用担心,这个时候,`findFood`不会被执行,队列只有在调用`exec`方法的时候被执行。 153 | 154 | ok,这时候闹钟响了怎么办,很简单。只需要将队列保存下来,在闹钟响了的时候,在队列里插入`eat()`,并调用`exec()`执行队列就可以了。当然,返回值还是一个队列,可以继续被调用。 155 | 156 | ```javascript 157 | var some = z.$coil().findFood() 158 | 159 | function alarmGoOff(){ 160 | var some = some.eat().exec() 161 | } 162 | 163 | ``` 164 | 165 | 使用`zcoil`生成队列添加的函数,永远都在前一个方法结束的时候被执行,后面的方法总会等待前面的方法结束,而且使用起来非常方便,可以说是前端异步请求的终极解决方案。是不是很酷? 166 | 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /src/coil.ts: -------------------------------------------------------------------------------- 1 | import forIn =require('lodash/forIn'); 2 | import assign = require('lodash/assign'); 3 | import {coilConif} from './interface/CoilConfig' 4 | 5 | export class coil { 6 | pArray: any[] = []; 7 | [key: string]: any; 8 | _call_index: any = 0; 9 | _callback: Function; 10 | _model: any; 11 | _call_stack: any[] = []; 12 | _zcoil: any; 13 | _error: any 14 | _default_config: coilConif = { 15 | rollback: false, 16 | errorContinue: true, 17 | saveWithExec: true 18 | } 19 | _ncoil: any 20 | _rollback_data_: any = {} 21 | _wait: any = null 22 | _next_exec: Function 23 | 24 | constructor(zcoil: any, config?: coilConif, wait?: any) { 25 | if (wait) { 26 | this._wait = wait 27 | } 28 | assign(this._default_config, config) 29 | // 注意 rollback 的数据 将为 $coil 被调用时的数据,如果调用过程中有并行的数据改变,rollback不会记录。 30 | if (this._default_config.rollback) { 31 | this._save_data(zcoil._data) 32 | } 33 | 34 | let that = this 35 | this._zcoil = zcoil 36 | this._model = zcoil._model 37 | this._model.$mixin = zcoil.$mixin 38 | /** 39 | * 初始化调用栈 40 | */ 41 | forIn(zcoil._func, (value, key) => { 42 | this[key] = function (...args: any[]) { 43 | that.pArray.push(() => { 44 | zcoil[key].call({ 45 | _call: (key: any, type: any,error?:any) => { 46 | that._call_stack.push({[type]: key}) 47 | that._ca(key, type,error) 48 | } 49 | }, ...args) 50 | }) 51 | return that 52 | } 53 | }) 54 | this._add_deserialize(zcoil) 55 | }; 56 | 57 | exec(_callback?: Function) { 58 | let nCoil = new coil(this._zcoil, this._default_config, true) 59 | this._ncoil = nCoil 60 | this._callback = _callback 61 | // is the queue is be perform ,do not run _next 62 | if (!this._wait) { 63 | this._next() 64 | } 65 | return nCoil 66 | }; 67 | 68 | _add_deserialize(zcoil: any) { 69 | this.$deserialize = (...args: any[]) => { 70 | this._call_stack.push({push: 'deserialize'}) 71 | this._ca('deserialize', 'push') 72 | zcoil.$deserialize(...args).then((data: any) => { 73 | this._call_stack.push({pop: 'deserialize'}) 74 | this._ca('deserialize', 'pop') 75 | }) 76 | return this 77 | } 78 | } 79 | 80 | _next() { 81 | if (this.pArray.length > 0) { 82 | this.pArray.shift()() 83 | } else { 84 | this._check_call_array() 85 | } 86 | }; 87 | 88 | _ca(key: any, type: String,error?:any) { 89 | if (type === 'push') { 90 | ++this._call_index 91 | } else if (type === 'pop' || (this._default_config.errorContinue && type === 'err')) { 92 | --this._call_index 93 | if (type === 'err') { 94 | this._set_error(error) 95 | } 96 | this._check_call_array() 97 | } else if (type === 'err' && !this._default_config.errorContinue) { 98 | this._call_index = 0 99 | this.pArray = [] 100 | this._error = new Error('The call chain has reject') 101 | this._check_call_array() 102 | } 103 | }; 104 | 105 | /** 106 | * When only one error occurs in the queue, the error is returned, and when two or more errors occur, an array is returned 107 | * @param error 108 | * @private 109 | */ 110 | _set_error(error: any) { 111 | if(!error){ 112 | error = new Error('no message reject') 113 | } 114 | if (!this._error) { 115 | this._error = error 116 | } else { 117 | if (Array.isArray(this._error)) { 118 | this._error.push(error) 119 | } else { 120 | this._error = [this._error, error] 121 | } 122 | } 123 | } 124 | 125 | _check_call_array() { 126 | if (this._call_index === 0&&this.pArray.length > 0) { 127 | this._next() 128 | } 129 | //done the queue 130 | else if (this._call_index === 0 && this.pArray.length === 0) { 131 | if (this._callback) { 132 | this._zcoil._dataTransToThis() 133 | //Check whether the data is rolled back 134 | if (!!this._error && this._default_config.rollback) { 135 | this._zcoil._dataTransToThis(this._rollback_data_) 136 | } 137 | this._callback.call(this._zcoil, this._model, this._error) 138 | this._error = null 139 | this._call_stack = [] 140 | } 141 | //next exec : if this._ncoil.pArray.length >0 means the next queue is be perform 142 | if (this._ncoil.pArray.length > 0) { 143 | //check saveWithExec 144 | if (this._ncoil._default_config.rollback && this._ncoil._default_config.saveWithExec) { 145 | this._ncoil._save_data(this._zcoil._data) 146 | } 147 | this._ncoil._next() 148 | } else { 149 | this._ncoil._wait = false 150 | } 151 | } 152 | }; 153 | 154 | _save_data(data: any) { 155 | forIn(data, (value, key) => { 156 | this._rollback_data_[key] = value 157 | }) 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /test/zcoil-test.js: -------------------------------------------------------------------------------- 1 | var zcoil = require('../dist/zcoil') 2 | const assert = require('assert'); 3 | var z = new zcoil() 4 | var z0 = new zcoil() 5 | z0.init({ 6 | data:{ 7 | a:1 8 | } 9 | }) 10 | z.init({ 11 | data() { 12 | return { 13 | index: 0 14 | } 15 | }, 16 | fetch2() { 17 | return Promise.resolve(2) 18 | }, 19 | timeout() { 20 | return new Promise((resolve) => { 21 | setTimeout(() => { 22 | resolve(20) 23 | }, 500) 24 | }) 25 | }, 26 | addIndex() { 27 | this.fetch2().then((data) => { 28 | this.index += data 29 | }) 30 | }, 31 | multiply() { 32 | this.timeout().then((data) => { 33 | this.index *= data 34 | }) 35 | }, 36 | assignment() { 37 | this.index = 200 38 | } 39 | }) 40 | 41 | var z2 = new zcoil() 42 | z2.init({ 43 | data() { 44 | return { 45 | index: 0 46 | } 47 | }, 48 | assignment() { 49 | this.index = 200 50 | } 51 | } 52 | ) 53 | 54 | var z3 = new zcoil() 55 | z3.init({ 56 | data() { 57 | return { 58 | index: 2, 59 | type:'last' 60 | } 61 | }, 62 | assignment() { 63 | this.index = 200 64 | } 65 | } 66 | ) 67 | 68 | var z4 = new zcoil() 69 | z4.init({ 70 | data() { 71 | return { 72 | index: 4 73 | } 74 | }, 75 | assignment() { 76 | this.index = 300 77 | } 78 | } 79 | ) 80 | 81 | var zassign = zcoil.$assign(z3,z4) 82 | 83 | var z5 = new zcoil() 84 | z5.init({ 85 | data() { 86 | return { 87 | index: 4 88 | } 89 | }, 90 | settimeout() { 91 | var that = this 92 | setTimeout(function(){ 93 | z5.index = 100 94 | that.$commit() 95 | },200) 96 | } 97 | } 98 | ) 99 | z5.$watch(function(from,to){ 100 | }) 101 | z5.settimeout() 102 | 103 | var z6 = new zcoil() 104 | z6.init({ 105 | data() { 106 | return { 107 | index: 4 108 | } 109 | }, 110 | assignment() { 111 | this.index = 300 112 | } 113 | } 114 | ) 115 | 116 | var z7 = new zcoil({}); 117 | z7.init({ 118 | data() { 119 | return { 120 | index: 4 121 | } 122 | }, 123 | luck(){ 124 | return new Promise((resolve)=>{ 125 | setTimeout(()=>{ 126 | resolve(8) 127 | },800) 128 | }) 129 | }, 130 | some(){ 131 | this.luck().then((da)=>{ 132 | this.index+=da 133 | }) 134 | }, 135 | jj(){ 136 | this.luck().then((da)=>{ 137 | this.index*=da 138 | }) 139 | } 140 | } 141 | ); 142 | 143 | var z8 = new zcoil({}); 144 | z8.init({ 145 | data() { 146 | return { 147 | index: 4 148 | } 149 | }, 150 | reject(){ 151 | return Promise.reject() 152 | }, 153 | resolve(){ 154 | return Promise.resolve(2) 155 | }, 156 | aa(){ 157 | this.resolve().then((data)=>{ 158 | this.index+=data 159 | }) 160 | }, 161 | bb(){ 162 | this.reject().then((data)=>{ 163 | this.index+=data 164 | }).catch(()=>{ 165 | //donothing 166 | }) 167 | } 168 | } 169 | ); 170 | 171 | var z9 = new zcoil({}); 172 | z9.init({ 173 | data() { 174 | return { 175 | index: 4 176 | } 177 | }, 178 | reject(){ 179 | return Promise.reject() 180 | }, 181 | resolve(){ 182 | return Promise.resolve(2) 183 | }, 184 | aa(){ 185 | this.resolve().then((data)=>{ 186 | this.index+=data 187 | }) 188 | }, 189 | bb(){ 190 | this.reject().then((data)=>{ 191 | this.index+=data 192 | }).catch(()=>{ 193 | //donothing 194 | }) 195 | } 196 | } 197 | ); 198 | 199 | var z10 = new zcoil() 200 | z10.init({ 201 | data() { 202 | return { 203 | message: "hello world " 204 | } 205 | }, 206 | asyncGetName() { 207 | return new Promise((resolve, reject) => { 208 | setTimeout(() => { 209 | resolve('my friend') 210 | }, 100) 211 | }) 212 | }, 213 | asyncGetSaySomething(param) { 214 | return new Promise((resolve, reject) => { 215 | setTimeout(() => { 216 | reject(param) 217 | }, 200) 218 | }) 219 | }, 220 | name() { 221 | this.asyncGetName().then((name) => { 222 | this.message += name 223 | }) 224 | }, 225 | say(param) { 226 | this.asyncGetSaySomething(param).then((say) => { 227 | this.message += "," + say 228 | }).catch(()=>{}) 229 | } 230 | }) 231 | 232 | 233 | describe('|||| ZCOIL MOCHA TEST ||||', () => { 234 | it('use data as a Object', () => { 235 | assert.strictEqual(z0.a , 1); 236 | }); 237 | it('use zcoil', () => { 238 | assert.strictEqual(zcoil !== null, true); 239 | }); 240 | it('new zcoil();', () => { 241 | assert.strictEqual(z !== null, true); 242 | }); 243 | it('init z.index', () => { 244 | assert.strictEqual(z.index, 0); 245 | }); 246 | 247 | it('z.fetch()', (done) => { 248 | z.fetch2().then((data) => { 249 | assert.strictEqual(data, 2); 250 | done() 251 | }) 252 | }); 253 | it('z.$watch()', (done) => { 254 | z2.$watch((from, to) => { 255 | assert.strictEqual(z2.index, to.index); 256 | done() 257 | }) 258 | z2.assignment() 259 | }); 260 | 261 | 262 | it('z.$coil().addIndex().multiply().assignment().multiply()', (done) => { 263 | z.$coil().addIndex().multiply().assignment().multiply().exec(function(data){ 264 | assert.strictEqual(z.index, 4000); 265 | assert.strictEqual(data.index, 4000); 266 | assert.strictEqual(this.index, 4000); 267 | done() 268 | }) 269 | }); 270 | 271 | it('zcoil.$assign()', () => { 272 | assert.strictEqual(zassign.index, 4); 273 | assert.strictEqual(zassign.type, 'last'); 274 | }); 275 | 276 | it('zcoil.$assign().method()', () => { 277 | zassign.assignment() 278 | assert.strictEqual(zassign.index, 300); 279 | }); 280 | 281 | it('z.$watch() by $commit', (done) => { 282 | z6.$watch((from, to) => { 283 | assert.strictEqual(z6.index, 200); 284 | assert.strictEqual(from.index, 4); 285 | assert.strictEqual(to.index, 200); 286 | done() 287 | }) 288 | z6.index = 200 289 | z6.$commit() 290 | }); 291 | var some = null 292 | it('zcoil.$coil two exec', (done) => { 293 | some = z7.$coil().some().exec(function () { 294 | 295 | }).jj().exec(function (data) { 296 | assert.strictEqual(data.index, 96); 297 | assert.strictEqual(z7.index, 96); 298 | done() 299 | }) 300 | }); 301 | it('zcoil.$coil next exec', (done) => { 302 | some.some().exec(function(data){ 303 | assert.strictEqual(z7.index, 104); 304 | assert.strictEqual(data.index, 104); 305 | done() 306 | }) 307 | }); 308 | 309 | 310 | it('zcoil more one exec() use saveWithExec = false and rollback = true.', (done) => { 311 | z8.$coil({rollback:true,saveWithExec:false}).aa().exec(function(data){ 312 | 313 | }).bb().exec(function (data,error) { 314 | assert.strictEqual(z8.index, 4); 315 | assert.strictEqual(data.index, 4); 316 | done() 317 | }) 318 | }); 319 | it('zcoil more one exec() use rollback = true but saveWithExec = true.', (done) => { 320 | z9.$coil({rollback:true,saveWithExec:true}).aa().exec(function(data){ 321 | debugger 322 | }).bb().exec(function (data,error) { 323 | assert.strictEqual(z9.index, 6); 324 | assert.strictEqual(data.index, 6); 325 | done() 326 | }) 327 | }); 328 | it('z.$coir() exec one error ', (done) => { 329 | z10.$coil().say('err').name().exec(function (data,error) { 330 | assert.strictEqual(error,'err'); 331 | done() 332 | }) 333 | }); 334 | 335 | }) 336 | 337 | 338 | 339 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import assign = require('lodash/assign'); 2 | import cloneDeep = require('lodash/cloneDeep'); 3 | import forIn = require('lodash/forIn'); 4 | import merge =require('lodash/merge'); 5 | import {getTimestamp, isPromise,isFunction} from './utils' 6 | import {coil} from './coil' 7 | import {coilConif} from './interface/coilConfig' 8 | import {zcoilConif} from './interface/zcoilConig' 9 | import {watch} from './watch' 10 | import scoil from './scoil' 11 | import {getData, serializeData} from "./serialize"; 12 | import {install} from './vue'; 13 | class zcoil { 14 | constructor(config?: zcoilConif) { 15 | this.install = install 16 | if (config) { 17 | config = assign({}, zcoil._init_config, config) 18 | this._config = config 19 | this.$mixin = config.mixin 20 | } 21 | } 22 | [key: string]: any; 23 | private _config: any = {} 24 | private _data: any = null; 25 | private _func: any = {}; 26 | _watch_array: any[] = [] 27 | $mixin:any = {} 28 | 29 | static _init_config = { 30 | localStorage: true, 31 | deadline: 30 * 24 * 3600 , 32 | cover: true, 33 | mixin:{} 34 | } 35 | 36 | static $assign(...datas: any[]) { 37 | let _merge = merge({}, ...datas) 38 | let _merge_func = cloneDeep(_merge._func) 39 | let _config: any = { 40 | data() { 41 | return cloneDeep(_merge._data) 42 | } 43 | } 44 | forIn(_merge_func, (value: any, key: any) => { 45 | _config[key] = value 46 | }) 47 | let _assign_obj = new zcoil() 48 | _assign_obj.init(_config) 49 | return _assign_obj 50 | } 51 | 52 | $coil(args?: coilConif) { 53 | return new coil(this, args) 54 | }; 55 | 56 | $watch(callback?: Function): void; 57 | $watch(expression?: String | Array, callback?: Function): void; 58 | $watch(expression?: any, callback?: Function) { 59 | if (typeof expression === 'function') { 60 | this._watch_array.push(new watch(null, expression, cloneDeep(this._data))) 61 | } 62 | else { 63 | this._watch_array.push(new watch(expression, callback, cloneDeep(this._data))) 64 | } 65 | } 66 | 67 | $commit() { 68 | if (this.$zcoil) { 69 | this.$zcoil._dataTransToThis.call(this.$zcoil, this) 70 | } else { 71 | this._dataTransToThis.call(this, this) 72 | } 73 | } 74 | 75 | _push_dictate(model: any) { 76 | model.$commit = this.$commit 77 | } 78 | 79 | /** 80 | * init 方法初始化数据,并绑定方法,根据返回值不同,在不同时刻进行不同操作监听数据变动, 81 | * @param data 82 | * @param func 83 | */ 84 | init({data, ...func}: any) { 85 | this._model = {} 86 | this._serialize() 87 | if(data){ 88 | if(isFunction(data)){ 89 | this._data = data() 90 | }else{ 91 | this._data = cloneDeep(data) 92 | } 93 | }else{ 94 | this._data = {} 95 | } 96 | func.$deserialize = this.$deserialize 97 | this._func = func 98 | let that = this 99 | forIn(func, (value, key) => { 100 | this[key] = this._model[key] = function (...arg: any[]) { 101 | let _to_model: any = that._model 102 | if (this._call) { 103 | _to_model = new scoil(that._model, this, that._data).model 104 | } 105 | _to_model.$zcoil = that 106 | that._push_dictate(_to_model) 107 | if (this._call) { 108 | this._call(key, 'push') 109 | } 110 | that._before(key) 111 | that._dataTransToThis(_to_model) 112 | _to_model.$mixin = that._config.mixin 113 | let _mr = value.apply(_to_model, arg) 114 | if (_mr) { 115 | if (isPromise(_mr)) { 116 | return new Promise((reserve, reject) => { 117 | const _calls = this._call 118 | _mr.then((datas: any) => { 119 | reserve(datas) 120 | Promise.resolve().then(() => { 121 | that._after(key, _to_model) 122 | that._dataTransToThis(this) 123 | if (_calls) { 124 | _calls(key, 'pop') 125 | } 126 | }) 127 | }).catch((error: any) => { 128 | reject(error) 129 | Promise.resolve().then(() => { 130 | that._error(key, _to_model) 131 | if (_calls) { 132 | _calls(key, 'err',error) 133 | } 134 | }) 135 | }) 136 | }) 137 | } else { 138 | that._dataTransToThis(_to_model) 139 | if (this._call) { 140 | this._call(key, 'pop') 141 | } 142 | that._after(key, _to_model) 143 | return _mr 144 | } 145 | } else { 146 | that._dataTransToThis(_to_model) 147 | if (this._call) { 148 | this._call(key, 'pop') 149 | } 150 | that._after(key, _to_model) 151 | } 152 | } 153 | }) 154 | this._dataTransToThis() 155 | 156 | }; 157 | 158 | /** 159 | * 反序列化数据方法 160 | */ 161 | $deserialize() { 162 | let that = this.$zcoil || this 163 | return new Promise((resolve) => { 164 | if (that._config && that._config.name && that._config.localStorage) { 165 | getData(that._config.name).then((d) => { 166 | if (d && that._config.cover) { 167 | that._data = d 168 | that._dataTransToThis(d) 169 | resolve(that._data) 170 | } else if (d) { 171 | resolve(d) 172 | } else { 173 | resolve(that._data) 174 | } 175 | }) 176 | } else { 177 | resolve(that._data) 178 | } 179 | }) 180 | 181 | } 182 | 183 | private _before(key: String) { 184 | //console.log('before:' + key) 185 | }; 186 | 187 | private _after(key: any, _to_model: any) { 188 | this._dataTransToThis(_to_model) 189 | //console.log('after:' + key) 190 | }; 191 | 192 | private _error(key: String, _to_model: any) { 193 | this._dataTransToThis(_to_model) 194 | //console.log('error:' + key) 195 | }; 196 | 197 | private _watch_each_call(to: any) { 198 | if (this._watch_array.length > 0) { 199 | this._watch_array.forEach((_watch) => { 200 | _watch._on_data_change(to) 201 | }) 202 | } 203 | } 204 | 205 | private _serialize() { 206 | if (this._config && this._config.name && this._config.localStorage) { 207 | this.$watch((from: any, to: any) => { 208 | if (from) { 209 | serializeData(this._config.name, this._data).catch(() => { 210 | throw new Error('new zcoil(); serialize error'); 211 | }) 212 | serializeData(`_${this._config.name}_deadline`, getTimestamp() + this._config.deadline * 1000).catch(() => { 213 | throw new Error('new zcoil(); serialize error'); 214 | }) 215 | } 216 | }) 217 | } 218 | } 219 | 220 | private _dataTransToThis(_to_model?: any) { 221 | forIn(this._data, (value, key) => { 222 | if (!!_to_model) { 223 | this._data[key] = this[key] = value = this._model[key] = _to_model[key] 224 | } 225 | else if (!!this._model[key]) { 226 | this._data[key] = this[key] = value = this._model[key] 227 | } 228 | else { 229 | this._data[key] = this[key] = this._model[key] = value 230 | } 231 | }) 232 | if (this._watch_array.length > 0) { 233 | this._watch_each_call(this._data) 234 | } 235 | }; 236 | } 237 | 238 | export default zcoil -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /dist/zcoil.js: -------------------------------------------------------------------------------- 1 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.zcoil=e():t.zcoil=e()}(this,function(){return function(t){var e={};function n(r){if(e[r])return e[r].exports;var o=e[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{configurable:!1,enumerable:!0,get:r})},n.r=function(t){Object.defineProperty(t,"__esModule",{value:!0})},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=168)}([function(t,e,n){var r=n(61),o="object"==typeof self&&self&&self.Object===Object&&self,i=r||o||Function("return this")();t.exports=i},function(t,e){var n=Array.isArray;t.exports=n},function(t,e){t.exports=function(t){return null!=t&&"object"==typeof t}},function(t,e){t.exports=function(t){var e=typeof t;return null!=t&&("object"==e||"function"==e)}},function(t,e,n){var r=n(167),o=n(162);t.exports=function(t,e){var n=o(t,e);return r(n)?n:void 0}},function(t,e,n){var r=n(37),o=n(36);t.exports=function(t,e,n,i){var a=!n;n||(n={});for(var c=-1,u=e.length;++c-1&&t%1==0&&t-1&&t%1==0&&t<=9007199254740991}},function(t,e){t.exports=function(t){return t}},function(t,e,n){var r=n(6),o=n(3);t.exports=function(t){if(!o(t))return!1;var e=r(t);return"[object Function]"==e||"[object GeneratorFunction]"==e||"[object AsyncFunction]"==e||"[object Proxy]"==e}},function(t,e,n){var r=n(62);t.exports=function(t,e,n){"__proto__"==e&&r?r(t,e,{configurable:!0,enumerable:!0,value:n,writable:!0}):t[e]=n}},function(t,e,n){var r=n(36),o=n(9),i=Object.prototype.hasOwnProperty;t.exports=function(t,e,n){var a=t[e];i.call(t,e)&&o(a,n)&&(void 0!==n||e in t)||r(t,e,n)}},function(t,e,n){var r=n(20);t.exports=function(t){if("string"==typeof t||r(t))return t;var e=t+"";return"0"==e&&1/t==-1/0?"-0":e}},function(t,e,n){var r=n(1),o=n(77),i=n(76),a=n(73);t.exports=function(t,e){return r(t)?t:o(t,e)?[t]:i(a(t))}},function(t,e,n){var r=n(88),o=n(85),i=n(84);t.exports=function(t,e,n,a,c,u){var f=1&n,s=t.length,l=e.length;if(s!=l&&!(f&&l>s))return!1;var v=u.get(t);if(v&&u.get(e))return v==e;var h=-1,p=!0,d=2&n?new r:void 0;for(u.set(t,e),u.set(e,t);++h1?n[i-1]:void 0,c=i>2?n[2]:void 0;for(a=t.length>3&&"function"==typeof a?(i--,a):void 0,c&&o(n[0],n[1],c)&&(a=i<3?void 0:a,i=1),e=Object(e);++r=43)}}).catch(function(){return!1})}(t).then(function(t){return v=t})}function b(t){var e=h[t.name],n={};n.promise=new a(function(t,e){n.resolve=t,n.reject=e}),e.deferredOperations.push(n),e.dbReady?e.dbReady=e.dbReady.then(function(){return n.promise}):e.dbReady=n.promise}function g(t){var e=h[t.name].deferredOperations.pop();if(e)return e.resolve(),e.promise}function m(t,e){var n=h[t.name].deferredOperations.pop();if(n)return n.reject(e),n.promise}function x(t,e){return new a(function(n,r){if(h[t.name]=h[t.name]||{forages:[],db:null,dbReady:null,deferredOperations:[]},t.db){if(!e)return n(t.db);b(t),t.db.close()}var i=[t.name];e&&i.push(t.version);var a=o.open.apply(o,i);e&&(a.onupgradeneeded=function(e){var n=a.result;try{n.createObjectStore(t.storeName),e.oldVersion<=1&&n.createObjectStore(l)}catch(n){if("ConstraintError"!==n.name)throw n;console.warn('The database "'+t.name+'" has been upgraded from version '+e.oldVersion+" to version "+e.newVersion+', but the storage "'+t.storeName+'" already exists.')}}),a.onerror=function(t){t.preventDefault(),r(a.error)},a.onsuccess=function(){n(a.result),g(t)}})}function j(t){return x(t,!1)}function w(t){return x(t,!0)}function O(t,e){if(!t.db)return!0;var n=!t.db.objectStoreNames.contains(t.storeName),r=t.versiont.db.version;if(r&&(t.version!==e&&console.warn('The database "'+t.name+"\" can't be downgraded from version "+t.db.version+" to version "+t.version+"."),t.version=t.db.version),o||n){if(n){var i=t.db.version+1;i>t.version&&(t.version=i)}return!0}return!1}function S(t){return i([function(t){for(var e=t.length,n=new ArrayBuffer(e),r=new Uint8Array(n),o=0;o0&&(!t.db||"InvalidStateError"===o.name||"NotFoundError"===o.name))return a.resolve().then(function(){if(!t.db||"NotFoundError"===o.name&&!t.db.objectStoreNames.contains(t.storeName)&&t.version<=t.db.version)return t.db&&(t.version=t.db.version+1),w(t)}).then(function(){return function(t){b(t);for(var e=h[t.name],n=e.forages,r=0;r>4,s[u++]=(15&r)<<4|o>>2,s[u++]=(3&o)<<6|63&i;return f}function Q(t){var e,n=new Uint8Array(t),r="";for(e=0;e>2],r+=T[(3&n[e])<<4|n[e+1]>>4],r+=T[(15&n[e+1])<<2|n[e+2]>>6],r+=T[63&n[e+2]];return n.length%3==2?r=r.substring(0,r.length-1)+"=":n.length%3==1&&(r=r.substring(0,r.length-2)+"=="),r}var K={serialize:function(t,e){var n="";if(t&&(n=G.call(t)),t&&("[object ArrayBuffer]"===n||t.buffer&&"[object ArrayBuffer]"===G.call(t.buffer))){var r,o=N;t instanceof ArrayBuffer?(r=t,o+=D):(r=t.buffer,"[object Int8Array]"===n?o+=B:"[object Uint8Array]"===n?o+=C:"[object Uint8ClampedArray]"===n?o+=F:"[object Int16Array]"===n?o+=M:"[object Uint16Array]"===n?o+=$:"[object Int32Array]"===n?o+=L:"[object Uint32Array]"===n?o+=U:"[object Float32Array]"===n?o+=W:"[object Float64Array]"===n?o+=q:e(new Error("Failed to get type for BinaryArray"))),e(o+Q(r))}else if("[object Blob]"===n){var i=new FileReader;i.onload=function(){var n="~~local_forage_type~"+t.type+"~"+Q(this.result);e(N+R+n)},i.readAsArrayBuffer(t)}else try{e(JSON.stringify(t))}catch(n){console.error("Couldn't convert value into a JSON string: ",t),e(null,n)}},deserialize:function(t){if(t.substring(0,P)!==N)return JSON.parse(t);var e,n=t.substring(V),r=t.substring(P,V);if(r===R&&z.test(n)){var o=n.match(z);e=o[1],n=n.substring(o[0].length)}var a=H(n);switch(r){case D:return a;case R:return i([a],{type:e});case B:return new Int8Array(a);case C:return new Uint8Array(a);case F:return new Uint8ClampedArray(a);case M:return new Int16Array(a);case $:return new Uint16Array(a);case L:return new Int32Array(a);case U:return new Uint32Array(a);case W:return new Float32Array(a);case q:return new Float64Array(a);default:throw new Error("Unkown type: "+r)}},stringToBuffer:H,bufferToString:Q};function X(t,e,n,r){t.executeSql("CREATE TABLE IF NOT EXISTS "+e.storeName+" (id INTEGER PRIMARY KEY, key unique, value)",[],n,r)}function J(t,e,n,r,o,i){t.executeSql(n,r,o,function(t,a){a.code===a.SYNTAX_ERR?t.executeSql("SELECT name FROM sqlite_master WHERE type='table' AND name = ?",[e.storeName],function(t,c){c.rows.length?i(t,a):X(t,e,function(){t.executeSql(n,r,o,i)},i)},i):i(t,a)},i)}var Y={_driver:"webSQLStorage",_initStorage:function(t){var e=this,n={db:null};if(t)for(var r in t)n[r]="string"!=typeof t[r]?t[r].toString():t[r];var o=new a(function(t,r){try{n.db=openDatabase(n.name,String(n.version),n.description,n.size)}catch(t){return r(t)}n.db.transaction(function(o){X(o,n,function(){e._dbInfo=n,t()},function(t,e){r(e)})},r)});return n.serializer=K,o},_support:"function"==typeof openDatabase,iterate:function(t,e){var n=this,r=new a(function(e,r){n.ready().then(function(){var o=n._dbInfo;o.db.transaction(function(n){J(n,o,"SELECT * FROM "+o.storeName,[],function(n,r){for(var i=r.rows,a=i.length,c=0;c0)return void a(t.apply(i,[e,u,r,o-1]));c(n)}})})}).catch(c)});return c(u,r),u}.apply(this,[t,e,n,1])},removeItem:function(t,e){var n=this;t=f(t);var r=new a(function(e,r){n.ready().then(function(){var o=n._dbInfo;o.db.transaction(function(n){J(n,o,"DELETE FROM "+o.storeName+" WHERE key = ?",[t],function(){e()},function(t,e){r(e)})})}).catch(r)});return c(r,e),r},clear:function(t){var e=this,n=new a(function(t,n){e.ready().then(function(){var r=e._dbInfo;r.db.transaction(function(e){J(e,r,"DELETE FROM "+r.storeName,[],function(){t()},function(t,e){n(e)})})}).catch(n)});return c(n,t),n},length:function(t){var e=this,n=new a(function(t,n){e.ready().then(function(){var r=e._dbInfo;r.db.transaction(function(e){J(e,r,"SELECT COUNT(key) as c FROM "+r.storeName,[],function(e,n){var r=n.rows.item(0).c;t(r)},function(t,e){n(e)})})}).catch(n)});return c(n,t),n},key:function(t,e){var n=this,r=new a(function(e,r){n.ready().then(function(){var o=n._dbInfo;o.db.transaction(function(n){J(n,o,"SELECT key FROM "+o.storeName+" WHERE id = ? LIMIT 1",[t+1],function(t,n){var r=n.rows.length?n.rows.item(0).key:null;e(r)},function(t,e){r(e)})})}).catch(r)});return c(r,e),r},keys:function(t){var e=this,n=new a(function(t,n){e.ready().then(function(){var r=e._dbInfo;r.db.transaction(function(e){J(e,r,"SELECT key FROM "+r.storeName,[],function(e,n){for(var r=[],o=0;o '__WebKitDatabaseInfoTable__'",[],function(n,r){for(var o=[],i=0;i0?(this._dbInfo=e,e.serializer=K,a.resolve()):a.reject()},_support:function(){try{return"undefined"!=typeof localStorage&&"setItem"in localStorage&&!!localStorage.setItem}catch(t){return!1}}(),iterate:function(t,e){var n=this,r=n.ready().then(function(){for(var e=n._dbInfo,r=e.keyPrefix,o=r.length,i=localStorage.length,a=1,c=0;c=0;n--){var r=localStorage.key(n);0===r.indexOf(t)&&localStorage.removeItem(r)}});return c(n,t),n},length:function(t){var e=this.keys().then(function(t){return t.length});return c(e,t),e},key:function(t,e){var n=this,r=n.ready().then(function(){var e,r=n._dbInfo;try{e=localStorage.key(t)}catch(t){e=null}return e&&(e=e.substring(r.keyPrefix.length)),e});return c(r,e),r},keys:function(t){var e=this,n=e.ready().then(function(){for(var t=e._dbInfo,n=localStorage.length,r=[],o=0;o=0;e--){var n=localStorage.key(e);0===n.indexOf(t)&&localStorage.removeItem(n)}}):a.reject("Invalid arguments"),e),r}},et=function(t,e){for(var n=t.length,r=0;ro.getTimestamp()?r.getItem(t).then(function(t){e(t)}).catch(function(){e()}):(e(),r.removeItem(t),r.removeItem("_"+t+"_deadline"))})})}},function(t,e,n){var r=n(39),o=n(31),i=n(1),a=n(32),c=n(33),u=n(38);t.exports=function(t,e,n){for(var f=-1,s=(e=r(e,t)).length,l=!1;++f0?this.pArray.shift()():this._check_call_array()}},{key:"_ca",value:function(t,e,n){"push"===e?++this._call_index:"pop"===e||this._default_config.errorContinue&&"err"===e?(--this._call_index,"err"===e&&this._set_error(n),this._check_call_array()):"err"!==e||this._default_config.errorContinue||(this._call_index=0,this.pArray=[],this._error=new Error("The call chain has reject"),this._check_call_array())}},{key:"_set_error",value:function(t){t||(t=new Error("no message reject")),this._error?Array.isArray(this._error)?this._error.push(t):this._error=[this._error,t]:this._error=t}},{key:"_check_call_array",value:function(){0===this._call_index&&this.pArray.length>0?this._next():0===this._call_index&&0===this.pArray.length&&(this._callback&&(this._zcoil._dataTransToThis(),this._error&&this._default_config.rollback&&this._zcoil._dataTransToThis(this._rollback_data_),this._callback.call(this._zcoil,this._model,this._error),this._error=null,this._call_stack=[]),this._ncoil.pArray.length>0?(this._ncoil._default_config.rollback&&this._ncoil._default_config.saveWithExec&&this._ncoil._save_data(this._zcoil._data),this._ncoil._next()):this._ncoil._wait=!1)}},{key:"_save_data",value:function(t){var e=this;o(t,function(t,n){e._rollback_data_[n]=t})}}]),t}();e.coil=a},function(t,e,n){var r=n(5),o=n(7);t.exports=function(t){return r(t,o(t))}},function(t,e,n){var r=n(6),o=n(22),i=n(2),a=Function.prototype,c=Object.prototype,u=a.toString,f=c.hasOwnProperty,s=u.call(Object);t.exports=function(t){if(!i(t)||"[object Object]"!=r(t))return!1;var e=o(t);if(null===e)return!0;var n=f.call(e,"constructor")&&e.constructor;return"function"==typeof n&&n instanceof n&&u.call(n)==s}},function(t,e,n){var r=n(8),o=n(2);t.exports=function(t){return o(t)&&r(t)}},function(t,e,n){var r=n(43),o=n(54),i=n(46),a=n(53),c=n(45),u=n(31),f=n(1),s=n(96),l=n(17),v=n(35),h=n(3),p=n(95),d=n(29),y=n(42),_=n(94);t.exports=function(t,e,n,b,g,m,x){var j=y(t,n),w=y(e,n),O=x.get(w);if(O)r(t,n,O);else{var S=m?m(j,w,n+"",t,e,x):void 0,I=void 0===S;if(I){var A=f(w),E=!A&&l(w),k=!A&&!E&&d(w);S=w,A||E||k?f(j)?S=j:s(j)?S=a(j):E?(I=!1,S=o(w,!0)):k?(I=!1,S=i(w,!0)):S=[]:p(w)||u(w)?(S=j,u(j)?S=_(j):(!h(j)||b&&v(j))&&(S=c(w))):I=!1}I&&(x.set(w,S),g(S,w,b,m,x),x.delete(w)),r(t,n,S)}}},function(t,e,n){var r=n(26),o=n(43),i=n(44),a=n(97),c=n(3),u=n(7),f=n(42);t.exports=function t(e,n,s,l,v){e!==n&&i(n,function(i,u){if(c(i))v||(v=new r),a(e,n,u,s,t,l,v);else{var h=l?l(f(e,u),i,u+"",e,n,v):void 0;void 0===h&&(h=i),o(e,u,h)}},u)}},function(t,e,n){var r=n(98),o=n(58)(function(t,e,n){r(t,e,n)});t.exports=o},function(t,e,n){var r=n(34);t.exports=function(t){return"function"==typeof t?t:r}},function(t,e){t.exports=function(t){return function(e,n,r){for(var o=-1,i=Object(e),a=r(e),c=a.length;c--;){var u=a[t?c:++o];if(!1===n(i[u],u,i))break}return e}}},function(t,e,n){var r=n(12),o=n(2);t.exports=function(t){return o(t)&&"[object Set]"==r(t)}},function(t,e,n){var r=n(102),o=n(28),i=n(27),a=i&&i.isSet,c=a?o(a):r;t.exports=c},function(t,e,n){var r=n(12),o=n(2);t.exports=function(t){return o(t)&&"[object Map]"==r(t)}},function(t,e,n){var r=n(104),o=n(28),i=n(27),a=i&&i.isMap,c=a?o(a):r;t.exports=c},function(t,e,n){var r=n(3),o=Object.create,i=function(){function t(){}return function(e){if(!r(e))return{};if(o)return o(e);t.prototype=e;var n=new t;return t.prototype=void 0,n}}();t.exports=i},function(t,e,n){var r=n(10),o=r?r.prototype:void 0,i=o?o.valueOf:void 0;t.exports=function(t){return i?Object(i.call(t)):{}}},function(t,e){var n=/\w*$/;t.exports=function(t){var e=new t.constructor(t.source,n.exec(t));return e.lastIndex=t.lastIndex,e}},function(t,e,n){var r=n(21);t.exports=function(t,e){var n=e?r(t.buffer):t.buffer;return new t.constructor(n,t.byteOffset,t.byteLength)}},function(t,e,n){var r=n(21),o=n(109),i=n(108),a=n(107),c=n(46);t.exports=function(t,e,n){var u=t.constructor;switch(e){case"[object ArrayBuffer]":return r(t);case"[object Boolean]":case"[object Date]":return new u(+t);case"[object DataView]":return o(t,n);case"[object Float32Array]":case"[object Float64Array]":case"[object Int8Array]":case"[object Int16Array]":case"[object Int32Array]":case"[object Uint8Array]":case"[object Uint8ClampedArray]":case"[object Uint16Array]":case"[object Uint32Array]":return c(t,n);case"[object Map]":return new u;case"[object Number]":case"[object String]":return new u(t);case"[object RegExp]":return i(t);case"[object Set]":return new u;case"[object Symbol]":return a(t)}}},function(t,e){var n=Object.prototype.hasOwnProperty;t.exports=function(t){var e=t.length,r=new t.constructor(e);return e&&"string"==typeof t[0]&&n.call(t,"index")&&(r.index=t.index,r.input=t.input),r}},function(t,e,n){var r=n(4)(n(0),"WeakMap");t.exports=r},function(t,e,n){var r=n(4)(n(0),"Set");t.exports=r},function(t,e,n){var r=n(4)(n(0),"Promise");t.exports=r},function(t,e,n){var r=n(4)(n(0),"DataView");t.exports=r},function(t,e,n){var r=n(48),o=n(51),i=n(7);t.exports=function(t){return r(t,i,o)}},function(t,e,n){var r=n(5),o=n(51);t.exports=function(t,e){return r(t,o(t),e)}},function(t,e){t.exports=function(t,e){for(var n=-1,r=null==t?0:t.length,o=0,i=[];++n-1}},function(t,e,n){var r=n(15);t.exports=function(t){var e=this.__data__,n=r(e,t);return n<0?void 0:e[n][1]}},function(t,e,n){var r=n(15),o=Array.prototype.splice;t.exports=function(t){var e=this.__data__,n=r(e,t);return!(n<0||(n==e.length-1?e.pop():o.call(e,n,1),--this.size,0))}},function(t,e){t.exports=function(){this.__data__=[],this.size=0}},function(t,e,n){var r=n(26),o=n(124),i=n(37),a=n(123),c=n(122),u=n(54),f=n(53),s=n(119),l=n(117),v=n(49),h=n(116),p=n(12),d=n(111),y=n(110),_=n(45),b=n(1),g=n(17),m=n(105),x=n(3),j=n(103),w=n(18),O="[object Arguments]",S="[object Function]",I="[object Object]",A={};A[O]=A["[object Array]"]=A["[object ArrayBuffer]"]=A["[object DataView]"]=A["[object Boolean]"]=A["[object Date]"]=A["[object Float32Array]"]=A["[object Float64Array]"]=A["[object Int8Array]"]=A["[object Int16Array]"]=A["[object Int32Array]"]=A["[object Map]"]=A["[object Number]"]=A[I]=A["[object RegExp]"]=A["[object Set]"]=A["[object String]"]=A["[object Symbol]"]=A["[object Uint8Array]"]=A["[object Uint8ClampedArray]"]=A["[object Uint16Array]"]=A["[object Uint32Array]"]=!0,A["[object Error]"]=A[S]=A["[object WeakMap]"]=!1,t.exports=function t(e,n,E,k,T,z){var N,P=1&n,D=2&n,R=4&n;if(E&&(N=T?E(e,k,T,z):E(e)),void 0!==N)return N;if(!x(e))return e;var B=b(e);if(B){if(N=d(e),!P)return f(e,N)}else{var C=p(e),F=C==S||"[object GeneratorFunction]"==C;if(g(e))return u(e,P);if(C==I||C==O||F&&!T){if(N=D||F?{}:_(e),!P)return D?l(e,c(N,e)):s(e,a(N,e))}else{if(!A[C])return T?e:{};N=y(e,C,P)}}z||(z=new r);var M=z.get(e);if(M)return M;if(z.set(e,N),j(e))return e.forEach(function(r){N.add(t(r,n,E,r,e,z))}),N;if(m(e))return e.forEach(function(r,o){N.set(o,t(r,n,E,o,e,z))}),N;var L=R?D?h:v:D?keysIn:w,$=B?void 0:L(e);return o($||e,function(r,o){$&&(r=e[o=r]),i(N,o,t(r,n,E,o,e,z))}),N}},function(t,e,n){var r=n(56)(Object.keys,Object);t.exports=r},function(t,e,n){var r=n(19),o=n(148),i=Object.prototype.hasOwnProperty;t.exports=function(t){if(!r(t))return o(t);var e=[];for(var n in Object(t))i.call(t,n)&&"constructor"!=n&&e.push(n);return e}},function(t,e,n){var r=n(6),o=n(33),i=n(2),a={};a["[object Float32Array]"]=a["[object Float64Array]"]=a["[object Int8Array]"]=a["[object Int16Array]"]=a["[object Int32Array]"]=a["[object Uint8Array]"]=a["[object Uint8ClampedArray]"]=a["[object Uint16Array]"]=a["[object Uint32Array]"]=!0,a["[object Arguments]"]=a["[object Array]"]=a["[object ArrayBuffer]"]=a["[object Boolean]"]=a["[object DataView]"]=a["[object Date]"]=a["[object Error]"]=a["[object Function]"]=a["[object Map]"]=a["[object Number]"]=a["[object Object]"]=a["[object RegExp]"]=a["[object Set]"]=a["[object String]"]=a["[object WeakMap]"]=!1,t.exports=function(t){return i(t)&&o(t.length)&&!!a[r(t)]}},function(t,e){t.exports=function(){return!1}},function(t,e,n){var r=n(6),o=n(2);t.exports=function(t){return o(t)&&"[object Arguments]"==r(t)}},function(t,e){t.exports=function(t,e){for(var n=-1,r=Array(t);++n0){if(++e>=800)return arguments[0]}else e=0;return t.apply(void 0,arguments)}}},function(t,e){t.exports=function(t){return function(){return t}}},function(t,e,n){var r=n(156),o=n(62),i=n(34),a=o?function(t,e){return o(t,"toString",{configurable:!0,enumerable:!1,value:r(e),writable:!0})}:i;t.exports=a},function(t,e,n){var r=n(157),o=n(155)(r);t.exports=o},function(t,e){t.exports=function(t,e,n){switch(n.length){case 0:return t.call(e);case 1:return t.call(e,n[0]);case 2:return t.call(e,n[0],n[1]);case 3:return t.call(e,n[0],n[1],n[2])}return t.apply(e,n)}},function(t,e,n){var r=n(159),o=Math.max;t.exports=function(t,e,n){return e=o(void 0===e?t.length-1:e,0),function(){for(var i=arguments,a=-1,c=o(i.length-e,0),u=Array(c);++a0&&this._watch_array.forEach(function(e){e._on_data_change(t)})}},{key:"_serialize",value:function(){var t=this;this._config&&this._config.name&&this._config.localStorage&&this.$watch(function(e,n){e&&(v.serializeData(t._config.name,t._data).catch(function(){throw new Error("new zcoil(); serialize error")}),v.serializeData("_"+t._config.name+"_deadline",u.getTimestamp()+1e3*t._config.deadline).catch(function(){throw new Error("new zcoil(); serialize error")}))})}},{key:"_dataTransToThis",value:function(t){var e=this;a(this._data,function(n,r){t?e._data[r]=e[r]=n=e._model[r]=t[r]:e._model[r]?e._data[r]=e[r]=n=e._model[r]:e._data[r]=e[r]=e._model[r]=n}),this._watch_array.length>0&&this._watch_each_call(this._data)}}],[{key:"$assign",value:function(){for(var e=arguments.length,n=Array(e),r=0;r