├── .gitignore ├── README.md ├── co.js ├── code ├── 1.whatIsCallback.js ├── 2.callbackHell.js ├── 3.callbackToPromise.js ├── 4.whyChoosePromise.js ├── 5.generator.js ├── 6.autoActuator.js ├── 7.asyncFn.js ├── 8.asyncTest.js ├── 9.objectToPromise.js ├── node_modules │ └── q │ │ ├── CHANGES.md │ │ ├── LICENSE │ │ ├── README.md │ │ ├── package.json │ │ ├── q.js │ │ └── queue.js ├── package-lock.json └── package.json ├── index.html ├── ppt ├── assets │ ├── arrow.svg │ ├── github.svg │ └── 方正兰亭超细黑简体.ttf ├── component │ ├── directory │ │ ├── index.grs │ │ └── style.css │ ├── oneParts │ │ ├── asyncFunction.grs │ │ ├── asyncTest.grs │ │ ├── autoActuator.grs │ │ ├── autoActuatorTest.grs │ │ ├── callback.grs │ │ ├── callbackChange.grs │ │ ├── callbackHell.grs │ │ ├── callbackToPromise.grs │ │ ├── generator.grs │ │ ├── history.grs │ │ ├── index.grs │ │ ├── promise.grs │ │ ├── promiseChangeToDeferred.grs │ │ ├── summary.grs │ │ └── whyChoosePromise.grs │ ├── root │ │ ├── index.grs │ │ └── style.css │ ├── style.css │ ├── summary │ │ ├── index.grs │ │ └── style.css │ └── twoParts │ │ ├── arrayToPromise.grs │ │ ├── coWrap.grs │ │ ├── index.grs │ │ ├── objectToPromise.grs │ │ ├── summary.grs │ │ ├── theable.grs │ │ └── toPromise.grs ├── global.css ├── index.js └── lib │ ├── css-module.js │ ├── flexible.js │ ├── highlight │ ├── highlight.css │ └── highlight.pack.js │ ├── impress.js │ ├── liberty-plugin.js │ └── liberty.js └── 思路.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .vscode 3 | 4 | # Editor directories and files 5 | .idea 6 | *.suo 7 | *.ntvs* 8 | *.njsproj 9 | *.sln -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 这是分享 Js 异步发展历史和介绍 co 源码的 ppt 2 | 3 | 如果对你有帮助,欢迎点个 star,[预览链接](https://imtaotao.github.io/co-share/) 😁 4 | 5 | 6 | ``` 7 | 使用静态服务器打开此项目就可以查看 ppt 8 | 9 | +-- 10 | +-- ppt (ppt 源码) 11 | +-- code (演示源码,通过 node 运行看效果) 12 | ``` -------------------------------------------------------------------------------- /co.js: -------------------------------------------------------------------------------- 1 | var slice = Array.prototype.slice; 2 | // module.exports = co['default'] = co.co = co; 3 | 4 | // const wrap = co.wrap(function * () {}) 5 | // const gen = wrap() 6 | 7 | co.wrap = function (fn) { 8 | createPromise.__generatorFunction__ = fn; 9 | return createPromise; 10 | function createPromise() { 11 | return co.call(this, fn.apply(this, arguments)); 12 | } 13 | }; 14 | 15 | function co(gen) { 16 | // 保证正确的 this 指向 17 | var ctx = this; 18 | var args = slice.call(arguments, 1); 19 | 20 | // we wrap everything in a promise to avoid promise chaining, 21 | // which leads to memory leak errors. 22 | // see https://github.com/tj/co/issues/180 23 | return new Promise(function(resolve, reject) { 24 | // 调用传入进来的函数,gen 也有可能本身就是一个 generator 对象 25 | if (typeof gen === 'function') gen = gen.apply(ctx, args); 26 | // 如果是个普通对象就返回 27 | if (!gen || typeof gen.next !== 'function') return resolve(gen); 28 | 29 | onFulfilled(); 30 | 31 | // 调用 next 32 | function onFulfilled(res) { 33 | var ret; 34 | try { 35 | ret = gen.next(res); 36 | } catch (e) { 37 | return reject(e); 38 | } 39 | next(ret); 40 | return null; 41 | } 42 | 43 | // 出现错误后调用 throw 方法,以便以让 generator 函数中的 try catch 捕获到 44 | function onRejected(err) { 45 | var ret; 46 | try { 47 | ret = gen.throw(err); 48 | } catch (e) { 49 | // 如果连 generator 函数重点 catch 中都报错了就需要传到最外面去了 50 | return reject(e); 51 | } 52 | next(ret); 53 | } 54 | 55 | function next(ret) { 56 | // done 了直接结束整个执行器 57 | if (ret.done) return resolve(ret.value); 58 | 59 | // 转换成 promise 60 | var value = toPromise.call(ctx, ret.value); 61 | if (value && isPromise(value)) return value.then(onFulfilled, onRejected); 62 | 63 | // co 只能支持 function promise generator array object 这几种类型,这和 async 函数有点不一样 64 | return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, ' 65 | + 'but the following object was passed: "' + String(ret.value) + '"')); 66 | } 67 | }); 68 | } 69 | 70 | function toPromise(obj) { 71 | // 这条语句会让上面的判断报 TypeError 错误 72 | if (!obj) return obj; 73 | 74 | // 判断是否是一个 promise(包括鸭子类型) 75 | if (isPromise(obj)) return obj; 76 | 77 | // 如果是一个 generator 调用 co, co 会返回一个 promise 78 | if (isGeneratorFunction(obj) || isGenerator(obj)) return co.call(this, obj); 79 | 80 | if ('function' == typeof obj) return thunkToPromise.call(this, obj); 81 | 82 | // 进行包装 83 | if (Array.isArray(obj)) return arrayToPromise.call(this, obj); 84 | 85 | // 类似 array 的处理 86 | if (isObject(obj)) return objectToPromise.call(this, obj); 87 | return obj; 88 | } 89 | 90 | function thunkToPromise(fn) { 91 | var ctx = this; 92 | return new Promise(function (resolve, reject) { 93 | fn.call(ctx, function (err, res) { 94 | if (err) return reject(err); 95 | if (arguments.length > 2) res = slice.call(arguments, 1); 96 | resolve(res); 97 | }); 98 | }); 99 | } 100 | 101 | function arrayToPromise(obj) { 102 | // 对每个元素包装成 promise 103 | return Promise.all(obj.map(toPromise, this)); 104 | } 105 | 106 | // 对 对象的每个元素进行包装 107 | function objectToPromise(obj){ 108 | var results = new obj.constructor(); 109 | var keys = Object.keys(obj); 110 | var promises = []; 111 | 112 | for (var i = 0; i < keys.length; i++) { 113 | var key = keys[i]; 114 | var promise = toPromise.call(this, obj[key]); 115 | 116 | // 如果是 promise,就放到 promise 数组中去,等待 promise.all 调用 117 | if (promise && isPromise(promise)) defer(promise, key); 118 | else results[key] = obj[key]; 119 | } 120 | // 对所有包装的 item 调用 promise.all 121 | return Promise.all(promises).then(function () { 122 | return results; 123 | }); 124 | 125 | function defer(promise, key) { 126 | // 预定义结果中的键,js 引擎喜欢稳定的对象 127 | results[key] = undefined; 128 | // 往数组种添加 promise 129 | // [1, 2, 3, promise, promise] 130 | promises.push(promise.then(function (res) { 131 | results[key] = res; 132 | })); 133 | } 134 | } 135 | 136 | // promise theable 所以只需要判读是否有 then 函数(鸭子类型) 137 | function isPromise(obj) { 138 | return 'function' == typeof obj.then; 139 | } 140 | 141 | // 只要你部署了遍历器接口,需要 throw 是因为要 try/catch 捕捉到 142 | function isGenerator(obj) { 143 | return 'function' == typeof obj.next && 'function' == typeof obj.throw; 144 | } 145 | 146 | 147 | function isGeneratorFunction(obj) { 148 | var constructor = obj.constructor; 149 | if (!constructor) return false; 150 | if ('GeneratorFunction' === constructor.name || 'GeneratorFunction' === constructor.displayName) return true; 151 | return isGenerator(constructor.prototype); 152 | } 153 | 154 | // 只处理 {} 155 | function isObject(val) { 156 | return Object == val.constructor; 157 | } -------------------------------------------------------------------------------- /code/1.whatIsCallback.js: -------------------------------------------------------------------------------- 1 | // 以下 demo 演示为什么需要回调 2 | function ordinary () { 3 | let i = 1 4 | // ... 5 | return i 6 | } 7 | 8 | function callback () { 9 | let i = 1 10 | setTimeout(() => { 11 | return i 12 | }) 13 | } 14 | 15 | // demo 16 | const result = ordinary() 17 | console.log(result) // 1 18 | 19 | const resultTwo = callback() 20 | console.log(resultTwo) // undefined 21 | 22 | // 人们尝试理解异步函数的时候,最大的问题是程序执行的顺序,这取决异步行为,而这是一个无法预测的行为 23 | // 也被函数式编程称呼为副作用,因为不可预测的行为,所以是大部分 bug 的来源。是需要花费很大力气解决的事情 24 | 25 | // 疑问:回调函数带来什么样的问题 -------------------------------------------------------------------------------- /code/2.callbackHell.js: -------------------------------------------------------------------------------- 1 | getData(function (a) { 2 | getMoreData(a, function (b) { 3 | getMoreData(b, function (c) { 4 | getMoreData(c, function (d) { 5 | // ... 6 | }) 7 | }) 8 | }) 9 | }) 10 | 11 | // 需要合理的封装和简化,这需要开发人员自身的水平和认知决定 12 | function fn (a, cb) { 13 | getMoreData(a, function (b) { 14 | getMoreData(b, function (c) { 15 | getMoreData(c, function (d) { 16 | cb(d) 17 | }) 18 | }) 19 | }) 20 | } 21 | 22 | // 通过层层封装,抽象出模块和通用的类来保证代码是浅层 23 | getData(function (a) { 24 | fn(a, function (d) { 25 | // ... 26 | }) 27 | }) 28 | 29 | // 疑问:有什么办法不依靠开发人员的水平来维护 callback hell -------------------------------------------------------------------------------- /code/3.callbackToPromise.js: -------------------------------------------------------------------------------- 1 | getData(function (a) { 2 | getMoreData(a, function (b) { 3 | getMoreData(b, function (c) { 4 | getMoreData(c, function (d) { 5 | // ... 6 | }) 7 | }) 8 | }) 9 | }) 10 | 11 | // 如果是 promise,这将 callback 变成了一种扁平化的结构。相对于 callback 更加友好 12 | getData() 13 | .then(getMoreData) 14 | .then(getMoreData) 15 | .then(getMoreData) 16 | .then(function (d) { 17 | // ... 18 | }) 19 | 20 | // 疑问:promise 和 其他方案的竞争,为什么是 promise 胜出了 -------------------------------------------------------------------------------- /code/4.whyChoosePromise.js: -------------------------------------------------------------------------------- 1 | // 社区不止有 promise 一个概念 2 | // 在 jQuery1.5 中就有了 deferred 的概念 3 | // 为什么最后选择了 promise 4 | // https://groups.google.com/forum/#!topic/bluebird-js/mUiX2-vXW2s 5 | // es6 提出了 new Promise() 这种构建 promise 的方式,以下是俩 demo 6 | // 1. promise 和 deferred 使用方式不同 7 | // 2. deferred 的局限在哪里 8 | 9 | const create = () => require('q').defer() 10 | 11 | // 1. deferred 和 promise 效果是一样的 12 | function demoOne () { 13 | // deferred promise 可以理解为订阅发布模式实现 14 | // 我们可以在 A 模块注册,在 B 模块触发 15 | const deferred = create() 16 | deferred.promise.then(v => console.log(v)) 17 | setTimeout(() => deferred.resolve('tao'), 500) 18 | 19 | // promise 在构建时就立即进入到触发阶段,不够自由,但是可以把 resolve 拿出来 20 | // 你即使可以在其他模块引用 p 模块,但是此时的 promise 状态可能已经更改了 21 | new Promise(resolve => { 22 | setTimeout(() => resolve('tao'), 500) 23 | }) 24 | .then(v => console.log(v)) 25 | } 26 | 27 | // 2. 为什么最终还是选择了 promise 28 | function demoTwo () { 29 | const deferred = create() 30 | deferred.promise.catch(reason => console.log(reason))// 不触发 31 | setTimeout(() => { 32 | // throw 'err' 33 | // 必须用 try catch 然后通过 deferrd.reject 触发 34 | try { 35 | throw 'errDeferred' 36 | } catch (err) { 37 | deferred.reject(err) 38 | } 39 | }) 40 | 41 | // promise 由于是自执行,自动捕捉异常 42 | new Promise(() => { throw 'errPromise' }) 43 | .catch(reason => console.log(reason)) 44 | } 45 | 46 | // demoOne() 47 | demoTwo() 48 | 49 | // 通过 promise 创建一个 deferred,可以替换上面的 q 创建的 50 | function createDeferred () { 51 | let resolve, reject 52 | 53 | const promise = new Promise((_resolve, _reject) => { 54 | resolve = _resolve 55 | reject = _reject 56 | }) 57 | return { promise, resolve, reject } 58 | } 59 | 60 | // 结论 61 | // promise 首先应该是一个异步流程控制的解决方案,流程控制包括了正常的数据流和异常流程处理。(捕捉异常,线程安全) 62 | // 而 deferred 的方式存在一个致命的缺陷 63 | // 就是 promise 链的第一个promise(deferred.promise)的触发阶段抛出的异常是不交由 promise 自动处理的 64 | 65 | // 疑问:但是 promise 的写法看起来还是不够同步,有什么办法呢?幸好我们有了 generator 函数 -------------------------------------------------------------------------------- /code/5.generator.js: -------------------------------------------------------------------------------- 1 | // 代码演变历程 2 | // callback -> promise -> generator 3 | // 不同演变进程下的代码写法,我们可以看到是以越来越同步的写法进行 4 | 5 | const getData = cb => { 6 | return new Promise(resolve => { 7 | cb && cb(1) 8 | resolve(1) 9 | }) 10 | } 11 | const getMoreData = (v, cb) => { 12 | cb && cb(v + 1) 13 | return v + 1 14 | } 15 | 16 | // 1. callback 17 | getData(function (a) { 18 | getMoreData(a, function (b) { 19 | getMoreData(b, function (c) { 20 | getMoreData(c, function (d) { 21 | console.log('callback', d) 22 | }) 23 | }) 24 | }) 25 | }) 26 | 27 | // 2. promise 28 | getData() 29 | .then(getMoreData) 30 | .then(getMoreData) 31 | .then(getMoreData) 32 | .then(function (d) { 33 | console.log('promise', d) 34 | }) 35 | 36 | // 3. generator 37 | const gen = (function * () { 38 | const a = yield 1 39 | const b = yield a + 1 40 | const c = yield b + 1 41 | const d = yield c + 1 42 | return d 43 | })() 44 | 45 | const a = gen.next() 46 | const b = gen.next(a.value) 47 | const c = gen.next(b.value) 48 | const d = gen.next(c.value) 49 | console.log('generator', d.value) 50 | 51 | // 可以看到使用 generato 函数已经做到语法上的同步了,但是 generator 函数需要手动调用 next 52 | // 疑问:我们可不可以把手动调用的过程封装为一个函数,让他自动执行 -------------------------------------------------------------------------------- /code/6.autoActuator.js: -------------------------------------------------------------------------------- 1 | // 一个 generator 自执行器 2 | // 我们把这个自执行函数封装为 co 3 | 4 | function co (fn, ...args) { 5 | return new Promise((resolve, reject) => { 6 | // 处理 generator 函数 7 | const gen = fn(...args) 8 | 9 | // 自动执行下一步 10 | const next = result => { 11 | let value = result.value 12 | if (result.done) return resolve(value) 13 | 14 | // 如果是 generator 函数 15 | if (value && value.constructor && value.constructor.name === 'GeneratorFunction') { 16 | value = co(value) 17 | } 18 | 19 | // 就算 value 是 promise,也包裹起来 20 | Promise.resolve(value).then(onFulfilled, onRejected) 21 | } 22 | 23 | const onFulfilled = res => { 24 | let result 25 | try { 26 | result = gen.next(res) 27 | next(result) 28 | } catch (err) { 29 | return reject(err) 30 | } 31 | } 32 | 33 | const onRejected = err => { 34 | let result 35 | try { 36 | result = gen.throw(err) 37 | next(result) 38 | } catch (e) { 39 | return reject(e) 40 | } 41 | } 42 | 43 | onFulfilled() 44 | }) 45 | } 46 | 47 | // ---- test ---- 48 | const ret = co(function * () { 49 | const a = yield 1 50 | const b = yield a + 1 51 | const c = yield b + 1 52 | const d = yield c + 1 53 | return d 54 | }) 55 | ret.then(v => console.log(v)) 56 | 57 | // demo 58 | function demo () { 59 | const fn = v => { 60 | return new Promise(resolve => { 61 | setTimeout(() => resolve(v), 200) 62 | }) 63 | } 64 | 65 | const ret = co(function * () { 66 | const a = yield fn(1) 67 | const b = yield fn(a + 1) 68 | const c = yield fn(b + 1) 69 | const d = yield fn(c + 1) 70 | return d 71 | }) 72 | ret.then(v => console.log(v)) 73 | } 74 | 75 | function demoTwo () { 76 | const ret = co(function * () { 77 | try { 78 | throw 'err' 79 | } catch (err) { 80 | console.log(err) 81 | throw 'err' 82 | } 83 | }) 84 | 85 | ret.catch(err => console.log(err)) 86 | } 87 | 88 | demo() 89 | demoTwo() 90 | 91 | // 疑问:generator 函数和 promise 能不能封装一下,做成一个新的语法糖(肯定是可以的) -------------------------------------------------------------------------------- /code/7.asyncFn.js: -------------------------------------------------------------------------------- 1 | const ret = (async function () { 2 | const a = await 1 3 | const b = await a + 1 4 | const c = await b + 1 5 | const d = await c + 1 6 | return d 7 | })() 8 | ret.then(v => console.log(v)) 9 | 10 | // demo 11 | function demo () { 12 | const fn = v => { 13 | return new Promise(resolve => { 14 | setTimeout(() => resolve(v), 200) 15 | }) 16 | } 17 | 18 | const ret = (async function () { 19 | const a = await fn(1) 20 | const b = await fn(a + 1) 21 | const c = await fn(b + 1) 22 | const d = await fn(c + 1) 23 | return d 24 | })() 25 | ret.then(v => console.log(v)) 26 | } 27 | 28 | function demoTwo () { 29 | const ret = (async function () { 30 | try { 31 | throw 'err' 32 | } catch (err) { 33 | console.log(err) 34 | throw 'err' 35 | } 36 | })() 37 | 38 | ret.catch(err => console.log(err)) 39 | } 40 | 41 | demo() 42 | demoTwo() 43 | 44 | // 疑问:原生的 async 函数是否真的用到了 promise(真的用到了,看下面的 demo) -------------------------------------------------------------------------------- /code/8.asyncTest.js: -------------------------------------------------------------------------------- 1 | // 测试原生的 async 函数是否使用了 promise 2 | // 鸭子模型,不要判断是不是鸭子,只要判断像不像鸭子,这里就是判断像不像 promise 3 | const p = { 4 | then (resolve, reject) { 5 | console.log('into') 6 | setTimeout(() => resolve('tao'), 1000) 7 | 8 | // reject 会被 async 的 try catch 捕获 9 | // reject('err') 10 | } 11 | }; 12 | 13 | (async function () { 14 | try { 15 | const v = await p 16 | console.log(v) 17 | } catch (err) { 18 | console.log(err) 19 | } 20 | })() 21 | 22 | 23 | // 看看 co 是在整个异步问题的解决历史上的哪个关键节点上。我们要看 co 源码了 -------------------------------------------------------------------------------- /code/9.objectToPromise.js: -------------------------------------------------------------------------------- 1 | function isPromise(obj) { 2 | return 'function' == typeof obj.then; 3 | } 4 | function isGenerator(obj) { 5 | return 'function' == typeof obj.next && 'function' == typeof obj.throw; 6 | } 7 | function isGeneratorFunction(obj) { 8 | var constructor = obj.constructor; 9 | if (!constructor) return false; 10 | if ('GeneratorFunction' === constructor.name || 'GeneratorFunction' === constructor.displayName) return true; 11 | return isGenerator(constructor.prototype); 12 | } 13 | function isObject(val) { 14 | return Object == val.constructor; 15 | } 16 | function toPromise(obj) { 17 | if (!obj) return obj; 18 | if (isPromise(obj)) return obj; 19 | if (isGeneratorFunction(obj) || isGenerator(obj)) return co.call(this, obj); 20 | if ('function' == typeof obj) return thunkToPromise.call(this, obj); 21 | if (Array.isArray(obj)) return arrayToPromise.call(this, obj); 22 | if (isObject(obj)) return objectToPromise.call(this, obj); 23 | return obj; 24 | } 25 | 26 | function arrayToPromise(obj) { 27 | return Promise.all(obj.map(toPromise, this)); 28 | } 29 | 30 | function objectToPromise(obj){ 31 | var results = new obj.constructor(); 32 | var keys = Object.keys(obj); 33 | var promises = []; 34 | 35 | for (var i = 0; i < keys.length; i++) { 36 | var key = keys[i]; 37 | var promise = toPromise.call(this, obj[key]); 38 | 39 | // 如果是 promise 40 | if (promise && isPromise(promise)) defer(promise, key); 41 | else results[key] = obj[key]; 42 | } 43 | // 对所有包装的 item 调用 promise.all 44 | return Promise.all(promises).then(function () { 45 | return results; 46 | }); 47 | 48 | function defer(promise, key) { 49 | // 预定义结果中的键,js 引擎喜欢稳定的对象 50 | results[key] = undefined; 51 | // 往数组种添加 promise 52 | // [1, 2, 3, promise, promise] 53 | promises.push(promise.then(function (res) { 54 | results[key] = res; 55 | })); 56 | } 57 | } 58 | 59 | objectToPromise({ 60 | a: 1, 61 | b: [2, 3], 62 | c: new Promise(resolve => { 63 | setTimeout(() => resolve(1), 500) 64 | }) 65 | }).then(res => { 66 | console.log(res); 67 | }) -------------------------------------------------------------------------------- /code/node_modules/q/CHANGES.md: -------------------------------------------------------------------------------- 1 | 2 | ## 1.5.1 3 | 4 | - Q.any now annotates its error message to clarify that Q.any was involved and 5 | includes only the last error emitted. (Ivan Etchart) 6 | - Avoid domain.dispose during tests in preparation for Node.js 9. (Anna 7 | Henningsen) 8 | 9 | ## 1.5.0 10 | 11 | - Q.any gives an error message from the last rejected promise 12 | - Throw if callback supplied to "finally" is invalid (@grahamrhay) 13 | - Long stack trace improvements, can now construct long stack traces 14 | across rethrows. 15 | 16 | ## 1.4.1 17 | 18 | - Address an issue that prevented Q from being used as a ` 35 | 36 | 37 | 38 | 39 | 75 | 76 | -------------------------------------------------------------------------------- /ppt/assets/arrow.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ppt/assets/github.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ppt/assets/方正兰亭超细黑简体.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imtaotao/co-share/1874028fa383018abd8efd86dc617a2550f6e005/ppt/assets/方正兰亭超细黑简体.ttf -------------------------------------------------------------------------------- /ppt/component/directory/index.grs: -------------------------------------------------------------------------------- 1 | 25 | 26 | -------------------------------------------------------------------------------- /ppt/component/directory/style.css: -------------------------------------------------------------------------------- 1 | .sub-title { 2 | font-size: 0.45rem; 3 | } 4 | 5 | .direactory ul { 6 | padding: 0 0.8rem; 7 | font-size: 0.3rem; 8 | } 9 | 10 | .direactory ul li { 11 | margin-bottom: 0.2rem; 12 | } 13 | 14 | @media (max-width: 500px) { 15 | .sub-title { 16 | font-size: 0.9rem; 17 | } 18 | 19 | .direactory ul { 20 | padding: 0 0.8rem; 21 | font-size: 0.6rem; 22 | } 23 | 24 | .direactory ul li { 25 | margin-bottom: 0.5rem; 26 | } 27 | } -------------------------------------------------------------------------------- /ppt/component/oneParts/asyncFunction.grs: -------------------------------------------------------------------------------- 1 | 68 | 69 | -------------------------------------------------------------------------------- /ppt/component/oneParts/asyncTest.grs: -------------------------------------------------------------------------------- 1 | 64 | 65 | -------------------------------------------------------------------------------- /ppt/component/oneParts/autoActuator.grs: -------------------------------------------------------------------------------- 1 | 150 | 151 | -------------------------------------------------------------------------------- /ppt/component/oneParts/autoActuatorTest.grs: -------------------------------------------------------------------------------- 1 | 132 | 133 | -------------------------------------------------------------------------------- /ppt/component/oneParts/callback.grs: -------------------------------------------------------------------------------- 1 | 98 | 99 | -------------------------------------------------------------------------------- /ppt/component/oneParts/callbackChange.grs: -------------------------------------------------------------------------------- 1 | 51 | 52 | -------------------------------------------------------------------------------- /ppt/component/oneParts/callbackHell.grs: -------------------------------------------------------------------------------- 1 | 44 | 45 | -------------------------------------------------------------------------------- /ppt/component/oneParts/callbackToPromise.grs: -------------------------------------------------------------------------------- 1 | 36 | 37 | -------------------------------------------------------------------------------- /ppt/component/oneParts/generator.grs: -------------------------------------------------------------------------------- 1 | 80 | 81 | -------------------------------------------------------------------------------- /ppt/component/oneParts/history.grs: -------------------------------------------------------------------------------- 1 | 31 | 32 | -------------------------------------------------------------------------------- /ppt/component/oneParts/index.grs: -------------------------------------------------------------------------------- 1 | 19 | 20 | -------------------------------------------------------------------------------- /ppt/component/oneParts/promise.grs: -------------------------------------------------------------------------------- 1 | 13 | 14 | -------------------------------------------------------------------------------- /ppt/component/oneParts/promiseChangeToDeferred.grs: -------------------------------------------------------------------------------- 1 | 45 | 46 | -------------------------------------------------------------------------------- /ppt/component/oneParts/summary.grs: -------------------------------------------------------------------------------- 1 | 68 | 69 | -------------------------------------------------------------------------------- /ppt/component/oneParts/whyChoosePromise.grs: -------------------------------------------------------------------------------- 1 | 146 | 147 | -------------------------------------------------------------------------------- /ppt/component/root/index.grs: -------------------------------------------------------------------------------- 1 | 83 | 84 | -------------------------------------------------------------------------------- /ppt/component/root/style.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: xihei; 3 | src: url('./ppt/assets/方正兰亭超细黑简体.ttf'); 4 | } 5 | 6 | .mask { 7 | position: fixed; 8 | left: 0; 9 | bottom: 0; 10 | z-index: 99; 11 | width: 100vw; 12 | height: 25vh; 13 | } 14 | 15 | .github-icon { 16 | top: 0.2rem; 17 | left: 0.2rem; 18 | width: 0.35rem; 19 | height: 0.35rem; 20 | display: flex; 21 | position: fixed; 22 | border-radius: 50%; 23 | align-items: center; 24 | justify-content: center; 25 | z-index: 999; 26 | } 27 | 28 | .github-icon img { 29 | width: 100%; 30 | height: 100%; 31 | } 32 | 33 | .title { 34 | font-size: 0.4rem; 35 | color: #cdcdcd; 36 | margin-top: -1rem; 37 | } 38 | 39 | .author { 40 | font-size: 0.3rem; 41 | margin-top: 0.5rem; 42 | color: #d0d0d0; 43 | margin-left: 7rem; 44 | font-family: xihei; 45 | } 46 | 47 | .full-screen { 48 | width: 0.6rem; 49 | height: 0.6rem; 50 | line-height: 0.6rem; 51 | font-size: 0.22rem; 52 | text-align: center; 53 | right: 0; 54 | top: 0; 55 | background: #444859; 56 | color: #6b7d8f; 57 | position: fixed; 58 | cursor: pointer; 59 | border-radius: 50%; 60 | z-index: 9999; 61 | } 62 | 63 | .more-text { 64 | font-size: 0.32rem; 65 | line-height: 0.9rem; 66 | text-align: center; 67 | } 68 | 69 | .history-title { 70 | font-size: 0.45rem; 71 | text-align: center; 72 | } 73 | 74 | /* 移动端才需要 */ 75 | @media (min-width: 700px) { 76 | .mask { 77 | display: none; 78 | } 79 | } 80 | 81 | @media (max-width: 500px) { 82 | .github-icon { 83 | top: 0.3rem; 84 | left: 0.3rem; 85 | width: 0.45rem; 86 | height: 0.45rem; 87 | } 88 | 89 | .title { 90 | font-size: 0.6rem; 91 | margin-top: -1rem; 92 | } 93 | 94 | .author { 95 | font-size: 0.45rem; 96 | margin-top: 0.5rem; 97 | margin-left: 7rem; 98 | } 99 | 100 | .full-screen { 101 | width: 1.5rem; 102 | height: 1.5rem; 103 | line-height: 1.5rem; 104 | font-size: 0.5rem; 105 | margin-top: -1rem; 106 | } 107 | 108 | .more-text { 109 | font-size: 0.7rem; 110 | line-height: 1.8rem; 111 | } 112 | 113 | .history-title { 114 | line-height: 1.5rem; 115 | font-size: 0.8rem; 116 | } 117 | } -------------------------------------------------------------------------------- /ppt/component/style.css: -------------------------------------------------------------------------------- 1 | .small { 2 | margin-top: 0.4rem; 3 | font-size: 0.25rem; 4 | } 5 | 6 | .title { 7 | font-size: 0.45rem; 8 | text-align: center; 9 | } 10 | 11 | .title div { 12 | margin: 0.2rem 0; 13 | } 14 | 15 | .title p { 16 | font-size: 0.32rem; 17 | } 18 | 19 | .history-title { 20 | font-size: 0.5rem; 21 | left: 0; 22 | } 23 | 24 | .history-item { 25 | margin: 0.15rem 0; 26 | display: flex; 27 | align-items: center; 28 | justify-content: center; 29 | flex-direction: column; 30 | } 31 | 32 | .history-item :global(.pure-text) { 33 | font-size: 0.4rem !important; 34 | } 35 | 36 | .history-item div { 37 | margin-bottom: 0.25rem; 38 | border-radius: 0.08rem; 39 | border: 1px solid #fff; 40 | padding: 0.15rem; 41 | box-sizing: border-box; 42 | } 43 | 44 | .history-item img { 45 | transform: rotate(-90deg); 46 | } 47 | 48 | .left-title { 49 | text-align: left; 50 | } 51 | 52 | .conclusion { 53 | font-size: 0.3rem; 54 | text-align: left; 55 | line-height: 0.6rem; 56 | } 57 | 58 | .conclusion li { 59 | margin: 0.4rem 0; 60 | } 61 | 62 | .sub-conclusion { 63 | font-size: 0.2rem; 64 | line-height: 0.4rem; 65 | margin-left: 0.5rem !important; 66 | } 67 | 68 | .more-text { 69 | font-size: 0.32rem; 70 | line-height: 0.9rem; 71 | text-align: center; 72 | } 73 | 74 | .top-title { 75 | margin-bottom: 1rem; 76 | } 77 | 78 | .small-code code { 79 | font-size: 0.28rem !important; 80 | } 81 | 82 | .bottom-letter { 83 | margin: 1.2rem 0 -0.7rem 0 !important; 84 | } 85 | 86 | .portal { 87 | width: 0.6rem; 88 | height: 0.6rem; 89 | line-height: 0.6rem; 90 | font-size: 0.22rem; 91 | text-align: center; 92 | right: 0; 93 | top: 0; 94 | background: #444859; 95 | color: #6b7d8f; 96 | position: fixed; 97 | cursor: pointer; 98 | border-radius: 50%; 99 | } 100 | 101 | @media (max-width: 500px) { 102 | .small { 103 | margin-top: 0.4rem; 104 | font-size: 0.25rem; 105 | } 106 | 107 | .title { 108 | font-size: 0.8rem; 109 | } 110 | 111 | .title div { 112 | margin: 0.2rem 0; 113 | } 114 | 115 | .title p { 116 | font-size: 0.6rem; 117 | } 118 | 119 | .history-title { 120 | font-size: 1rem; 121 | } 122 | 123 | .history-item { 124 | margin: 0.6rem 0; 125 | } 126 | 127 | .history-item :global(.pure-text) { 128 | font-size: 0.8rem !important; 129 | } 130 | 131 | .history-item div { 132 | margin-bottom: 0.5rem; 133 | border-radius: 0.1rem; 134 | padding: 0.15rem; 135 | border: none; 136 | } 137 | 138 | .conclusion { 139 | font-size: 0.6rem; 140 | line-height: 1.5rem; 141 | } 142 | 143 | .conclusion li { 144 | margin: 1rem 0; 145 | } 146 | 147 | .sub-conclusion { 148 | font-size: 0.45rem; 149 | line-height: 0.7rem; 150 | margin-left: 0.8rem !important; 151 | } 152 | 153 | .more-text { 154 | font-size: 0.6rem; 155 | line-height: 1.3rem; 156 | } 157 | 158 | .top-title { 159 | margin-bottom: 1rem; 160 | } 161 | 162 | .small-code code { 163 | font-size: 0.5rem !important; 164 | } 165 | 166 | .bottom-letter { 167 | margin: 1.2rem 0 -0.7rem 0 !important; 168 | } 169 | 170 | .portal { 171 | width: 0.6rem; 172 | height: 0.6rem; 173 | line-height: 0.6rem; 174 | font-size: 0.22rem; 175 | } 176 | 177 | :global(.step) pre { 178 | padding: 0; 179 | margin: 0; 180 | } 181 | } -------------------------------------------------------------------------------- /ppt/component/summary/index.grs: -------------------------------------------------------------------------------- 1 | 25 | 26 | -------------------------------------------------------------------------------- /ppt/component/summary/style.css: -------------------------------------------------------------------------------- 1 | .qa { 2 | font-size: 0.8rem; 3 | text-align: center; 4 | font-family: 微软雅黑; 5 | } 6 | 7 | .end-word { 8 | font-size: 0.45rem; 9 | line-height: 9rem; 10 | text-align: center; 11 | } 12 | 13 | .end-tag { 14 | font-size: 0.3rem; 15 | text-align: right; 16 | margin-top: 1rem; 17 | margin-right: 0.3rem; 18 | } 19 | 20 | @media (max-width: 500px) { 21 | .end-word { 22 | font-size: 0.8rem; 23 | line-height: 9rem; 24 | } 25 | 26 | .end-tag { 27 | font-size: 0.6rem; 28 | } 29 | } -------------------------------------------------------------------------------- /ppt/component/twoParts/arrayToPromise.grs: -------------------------------------------------------------------------------- 1 | 26 | 27 | -------------------------------------------------------------------------------- /ppt/component/twoParts/coWrap.grs: -------------------------------------------------------------------------------- 1 | 57 | 58 | -------------------------------------------------------------------------------- /ppt/component/twoParts/index.grs: -------------------------------------------------------------------------------- 1 | 51 | 52 | -------------------------------------------------------------------------------- /ppt/component/twoParts/objectToPromise.grs: -------------------------------------------------------------------------------- 1 | 49 | 50 | -------------------------------------------------------------------------------- /ppt/component/twoParts/summary.grs: -------------------------------------------------------------------------------- 1 | 76 | 77 | 106 | `, 107 | 108 | codeTwo: ` 109 | 110 |
111 |
112 | 113 | 132 | `, 133 | } 134 | } 135 | } 136 | ) 137 | -------------------------------------------------------------------------------- /ppt/component/twoParts/theable.grs: -------------------------------------------------------------------------------- 1 | 109 | 110 | -------------------------------------------------------------------------------- /ppt/component/twoParts/toPromise.grs: -------------------------------------------------------------------------------- 1 | 75 | 76 | -------------------------------------------------------------------------------- /ppt/global.css: -------------------------------------------------------------------------------- 1 | /* 该类添加在还未展示的步骤结点,当该元素被导航时会自动删除future类,显示页面 */ 2 | .future { 3 | pointer-events: none; 4 | } 5 | 6 | /* 该类会在步骤元素被导航过后自动添加 */ 7 | .past { 8 | pointer-events: none; 9 | } 10 | 11 | /* 该类会被自动添加到目前位于屏幕中央的步骤结点,这对幻灯片展示时的动画效果非常有用 */ 12 | /* .present .rotating { 13 | transform: rotate(-10deg); 14 | } */ 15 | 16 | .step { 17 | opacity: 0; 18 | width: 85vw; 19 | font-size: 0.3rem; 20 | transition: all 0.5s; 21 | } 22 | 23 | /* 该类会被自动添加到当前的步骤元素,与presentclass类似 */ 24 | .step.active { 25 | opacity: 1; 26 | transition: all 0.5s; 27 | } 28 | 29 | /* impress-on-* */ 30 | /* 用于定义每个步骤结点的样式,步骤结点的id可以在URL中看到,如#/step-1 */ 31 | .impress-on-overview .step { 32 | opacity: 1; 33 | cursor: pointer; 34 | } 35 | .impress-on-step-1, 36 | .impress-on-step-2, 37 | .impress-on-step-3 { 38 | background: rgb(49, 54, 64); 39 | } 40 | 41 | ::-webkit-scrollbar { 42 | width: 0.18rem; 43 | height: 0.18rem; 44 | } 45 | 46 | ::-webkit-scrollbar-track-piece { 47 | background-color: rgba(0, 0, 0, 0.2); 48 | } 49 | 50 | ::-webkit-scrollbar-thumb:vertical { 51 | height: 0.18rem; 52 | background: -webkit-gradient(linear,left top, right bottom, from(#485563), to(#29323c)); 53 | -webkit-border-radius: 0.08rem; 54 | } 55 | 56 | ::-webkit-scrollbar-thumb:horizontal { 57 | width: 0.18rem; 58 | background-color: rgba(125, 125, 125, 0.7); 59 | -webkit-border-radius: 0.08rem; 60 | } 61 | 62 | .center { 63 | display: flex; 64 | align-items: center; 65 | justify-content: center; 66 | } 67 | 68 | .pure-text { 69 | font-size: 0.6rem; 70 | color: #e7edf1; 71 | font-family: xihei, 微软雅黑; 72 | font-weight: bold; 73 | } 74 | 75 | .pure-code code { 76 | font-size: 0.35rem; 77 | text-align: left; 78 | font-weight: bold; 79 | } 80 | 81 | .half { 82 | width: 50%; 83 | float: left; 84 | box-sizing: border-box; 85 | } 86 | 87 | .link { 88 | color: #94d6e9; 89 | } 90 | 91 | @media (max-width: 500px) { 92 | .step { 93 | width: 220vw; 94 | } 95 | .pure-text { 96 | font-size: 1.8rem; 97 | } 98 | 99 | .pure-code code { 100 | font-size: 0.6rem; 101 | } 102 | 103 | .half { 104 | width: 100%; 105 | padding-left: 0.8rem !important; 106 | } 107 | } -------------------------------------------------------------------------------- /ppt/index.js: -------------------------------------------------------------------------------- 1 | require('./global.css') 2 | require('./lib/flexible.js') 3 | const Grass = require('@Grass') 4 | const Root = require('@comp/root/index') 5 | 6 | // 语法高亮指令 7 | Grass.directive('lighlight', dom => { 8 | dom.querySelectorAll('code').forEach(block => { 9 | hljs.highlightBlock(block) 10 | }) 11 | }) 12 | 13 | // 添加过滤的 code 14 | Grass.mixin({ 15 | filterCode (code) { 16 | let firstLine = null 17 | codes = code.split('\n').map(line => { 18 | if (!line) return '' 19 | for (let i = 0, j = 0; i < line.length; i++) { 20 | if (line[i] !== ' ') { 21 | if (typeof firstLine === 'number') { 22 | j = j > firstLine ? firstLine : j 23 | } else { 24 | firstLine = j || 0 25 | } 26 | return line.slice(j, line.length).replace('template.', 'template') || '' 27 | } 28 | j++ 29 | } 30 | }) 31 | return codes.join('\n') 32 | } 33 | }) 34 | 35 | // 初始化组件 36 | Root.$mount(document.getElementById('root')) 37 | 38 | // 初始化 ppt 39 | const Impress = impress() 40 | Impress.init() 41 | 42 | // 如果 liberty 没有语法报错,删除 loading 信息 43 | const loading = document.getElementById('loading') 44 | if (loading) { 45 | loading.parentNode.removeChild(loading) 46 | } 47 | 48 | module.exports = Impress -------------------------------------------------------------------------------- /ppt/lib/css-module.js: -------------------------------------------------------------------------------- 1 | window.CssModule = (function () { 2 | const SINGLE_QUOTE = '\''.charCodeAt(0) 3 | const DOUBLE_QUOTE = '"'.charCodeAt(0) 4 | const BACKSLASH = '\\'.charCodeAt(0) 5 | const SLASH = '/'.charCodeAt(0) 6 | const NEWLINE = '\n'.charCodeAt(0) 7 | const SPACE = ' '.charCodeAt(0) 8 | const FEED = '\f'.charCodeAt(0) 9 | const TAB = '\t'.charCodeAt(0) 10 | const CR = '\r'.charCodeAt(0) 11 | const OPEN_SQUARE = '['.charCodeAt(0) 12 | const CLOSE_SQUARE = ']'.charCodeAt(0) 13 | const OPEN_PARENTHESES = '('.charCodeAt(0) 14 | const CLOSE_PARENTHESES = ')'.charCodeAt(0) 15 | const OPEN_CURLY = '{'.charCodeAt(0) 16 | const CLOSE_CURLY = '}'.charCodeAt(0) 17 | const SEMICOLON = ';'.charCodeAt(0) 18 | const ASTERISK = '*'.charCodeAt(0) 19 | const COLON = ':'.charCodeAt(0) 20 | const AT = '@'.charCodeAt(0) 21 | 22 | const CLASS_REG = /\.([^\.),"'\[\]]+)/g 23 | const RE_AT_END = /[ \n\t\r\f{}()'"\\;/[\]#]/g 24 | const RE_WORD_END = /[ \n\t\r\f(){}:;@!'"\\\][#]|\/(?=\*)/g 25 | const RE_BAD_BRACKET = /.[\\/("'\n]/ 26 | const RE_HEX_ESCAPE = /[a-f0-9]/i 27 | 28 | function tokenizer (cssText) { 29 | let css = cssText.valueOf() 30 | let code, next, quote, lines, last, content, escape 31 | let nextLine, nextOffset, escaped, escapePos, prev, n, currentToken 32 | 33 | let length = css.length 34 | let offset = -1 35 | let line = 1 36 | let pos = 0 37 | let buffer = [] 38 | let returned = [] 39 | 40 | function position () { 41 | return pos 42 | } 43 | 44 | function unclosed (what) { 45 | throw new Error(`Unclosed ${what}\n\n line:${line} column:${pos - offset}`) 46 | } 47 | 48 | function endOfFile () { 49 | return returned.length === 0 && pos >= length 50 | } 51 | 52 | function nextToken () { 53 | if (returned.length) return returned.pop() 54 | if (pos >= length) return 55 | 56 | code = css.charCodeAt(pos) 57 | if ( 58 | code === NEWLINE || code === FEED || 59 | (code === CR && css.charCodeAt(pos + 1) !== NEWLINE) 60 | ) { 61 | offset = pos 62 | line += 1 63 | } 64 | 65 | switch (code) { 66 | case NEWLINE: 67 | case SPACE: 68 | case TAB: 69 | case CR: 70 | case FEED: 71 | next = pos 72 | do { 73 | next += 1 74 | code = css.charCodeAt(next) 75 | if (code === NEWLINE) { 76 | offset = next 77 | line += 1 78 | } 79 | } while ( 80 | code === SPACE || 81 | code === NEWLINE || 82 | code === TAB || 83 | code === CR || 84 | code === FEED 85 | ) 86 | 87 | currentToken = ['space', css.slice(pos, next)] 88 | pos = next - 1 89 | break 90 | 91 | case OPEN_SQUARE: 92 | case CLOSE_SQUARE: 93 | case OPEN_CURLY: 94 | case CLOSE_CURLY: 95 | case COLON: 96 | case SEMICOLON: 97 | case CLOSE_PARENTHESES: 98 | let controlChar = String.fromCharCode(code) 99 | currentToken = [controlChar, controlChar, line, pos - offset] 100 | break 101 | 102 | case OPEN_PARENTHESES: 103 | prev = buffer.length ? buffer.pop()[1] : '' 104 | n = css.charCodeAt(pos + 1) 105 | if ( 106 | prev === 'url' && 107 | n !== SINGLE_QUOTE && n !== DOUBLE_QUOTE && 108 | n !== SPACE && n !== NEWLINE && n !== TAB && 109 | n !== FEED && n !== CR 110 | ) { 111 | next = pos 112 | do { 113 | escaped = false 114 | next = css.indexOf(')', next + 1) 115 | if (next === -1) { 116 | unclosed('bracket') 117 | } 118 | escapePos = next 119 | while (css.charCodeAt(escapePos - 1) === BACKSLASH) { 120 | escapePos -= 1 121 | escaped = !escaped 122 | } 123 | } while (escaped) 124 | 125 | currentToken = ['brackets', css.slice(pos, next + 1), 126 | line, pos - offset, 127 | line, next - offset 128 | ] 129 | 130 | pos = next 131 | } else { 132 | next = css.indexOf(')', pos + 1) 133 | content = css.slice(pos, next + 1) 134 | 135 | if (next === -1 || RE_BAD_BRACKET.test(content)) { 136 | currentToken = ['(', '(', line, pos - offset] 137 | } else { 138 | currentToken = ['brackets', content, 139 | line, pos - offset, 140 | line, next - offset 141 | ] 142 | pos = next 143 | } 144 | } 145 | 146 | break 147 | 148 | case SINGLE_QUOTE: 149 | case DOUBLE_QUOTE: 150 | quote = code === SINGLE_QUOTE ? '\'' : '"' 151 | next = pos 152 | do { 153 | escaped = false 154 | next = css.indexOf(quote, next + 1) 155 | if (next === -1) { 156 | unclosed('string') 157 | } 158 | escapePos = next 159 | while (css.charCodeAt(escapePos - 1) === BACKSLASH) { 160 | escapePos -= 1 161 | escaped = !escaped 162 | } 163 | } while (escaped) 164 | 165 | content = css.slice(pos, next + 1) 166 | lines = content.split('\n') 167 | last = lines.length - 1 168 | 169 | if (last > 0) { 170 | nextLine = line + last 171 | nextOffset = next - lines[last].length 172 | } else { 173 | nextLine = line 174 | nextOffset = offset 175 | } 176 | 177 | currentToken = ['string', css.slice(pos, next + 1), 178 | line, pos - offset, 179 | nextLine, next - nextOffset 180 | ] 181 | 182 | offset = nextOffset 183 | line = nextLine 184 | pos = next 185 | break 186 | 187 | case AT: 188 | RE_AT_END.lastIndex = pos + 1 189 | RE_AT_END.test(css) 190 | if (RE_AT_END.lastIndex === 0) { 191 | next = css.length - 1 192 | } else { 193 | next = RE_AT_END.lastIndex - 2 194 | } 195 | 196 | currentToken = ['at-word', css.slice(pos, next + 1), 197 | line, pos - offset, 198 | line, next - offset 199 | ] 200 | 201 | pos = next 202 | break 203 | 204 | case BACKSLASH: 205 | next = pos 206 | escape = true 207 | while (css.charCodeAt(next + 1) === BACKSLASH) { 208 | next += 1 209 | escape = !escape 210 | } 211 | code = css.charCodeAt(next + 1) 212 | if ( 213 | escape && 214 | code !== SLASH && 215 | code !== SPACE && 216 | code !== NEWLINE && 217 | code !== TAB && 218 | code !== CR && 219 | code !== FEED 220 | ) { 221 | next += 1 222 | if (RE_HEX_ESCAPE.test(css.charAt(next))) { 223 | while (RE_HEX_ESCAPE.test(css.charAt(next + 1))) { 224 | next += 1 225 | } 226 | if (css.charCodeAt(next + 1) === SPACE) { 227 | next += 1 228 | } 229 | } 230 | } 231 | 232 | currentToken = ['word', css.slice(pos, next + 1), 233 | line, pos - offset, 234 | line, next - offset 235 | ] 236 | 237 | pos = next 238 | break 239 | 240 | default: 241 | if (code === SLASH && css.charCodeAt(pos + 1) === ASTERISK) { 242 | next = css.indexOf('*/', pos + 2) + 1 243 | if (next === 0) { 244 | unclosed('comment') 245 | } 246 | 247 | content = css.slice(pos, next + 1) 248 | lines = content.split('\n') 249 | last = lines.length - 1 250 | 251 | if (last > 0) { 252 | nextLine = line + last 253 | nextOffset = next - lines[last].length 254 | } else { 255 | nextLine = line 256 | nextOffset = offset 257 | } 258 | 259 | currentToken = ['comment', content, 260 | line, pos - offset, 261 | nextLine, next - nextOffset 262 | ] 263 | 264 | offset = nextOffset 265 | line = nextLine 266 | pos = next 267 | } else { 268 | RE_WORD_END.lastIndex = pos + 1 269 | RE_WORD_END.test(css) 270 | if (RE_WORD_END.lastIndex === 0) { 271 | next = css.length - 1 272 | } else { 273 | next = RE_WORD_END.lastIndex - 2 274 | } 275 | 276 | currentToken = ['word', css.slice(pos, next + 1), 277 | line, pos - offset, 278 | line, next - offset 279 | ] 280 | 281 | buffer.push(currentToken) 282 | 283 | pos = next 284 | } 285 | 286 | break 287 | } 288 | 289 | pos++ 290 | return currentToken 291 | } 292 | 293 | function back (token) { 294 | returned.push(token) 295 | } 296 | 297 | return { 298 | back, 299 | nextToken, 300 | endOfFile, 301 | position 302 | } 303 | } 304 | 305 | // Tranfer function 306 | function genHash(str) { 307 | let hash = 5381 308 | let i = str.length 309 | 310 | while(i) { 311 | hash = (hash * 33) ^ str.charCodeAt(--i) 312 | } 313 | return hash >>> 0 314 | } 315 | 316 | function isGlobalToken (token, preToken = []) { 317 | const preIsGlobalToken = preToken[0] === 'word' && preToken[1] === 'global' 318 | return preIsGlobalToken && token[0] === 'brackets' 319 | } 320 | 321 | function genModuleStyleName (token, preToken, preFilterToken, path) { 322 | const styles = {} 323 | const isGlobal = isGlobalToken(token, preToken) 324 | 325 | // replace class name 326 | token[1] = token[1].replace(CLASS_REG, (k1, k2) => { 327 | const hash = '_' + genHash(path + k2) 328 | if (styles[k2] === undefined) { 329 | styles[k2] = isGlobal 330 | ? k2 331 | : k2 + hash 332 | } 333 | return isGlobal ? k1 : k1 + hash 334 | }) 335 | 336 | // replace keyframe 337 | if ( 338 | token[0] === 'word' && 339 | token[1] !== 'global' && 340 | preFilterToken[0] === 'at-word' && 341 | preFilterToken[1] === '@keyframes' 342 | ) { token[1] += '_' + genHash(token[1]) } 343 | 344 | return [token, styles] 345 | } 346 | 347 | function splicingCssText (tokens) { 348 | let text = '' 349 | const isPre = t => t[0] === ':' && t[1] === ':' 350 | const isGlobal = t => t[0] === 'word' && t[1] === 'global' 351 | 352 | for (let i = 0; i < tokens.length; i++) { 353 | let j = 0 354 | let token = tokens[i] 355 | const nextToken = tokens[i + ++j] 356 | 357 | // If a global word (animate) 358 | if (isPre(token) && isGlobal(nextToken)) { 359 | do { 360 | token = tokens[i + ++j] 361 | } while (token[0] === 'space') 362 | 363 | const keyword = token[1].match(/[^\(\)]+/) 364 | if (!keyword) { 365 | throw new Error(`Global syntax error\n\n line:${token[2]} column:${token[3]}`) 366 | } 367 | 368 | text += keyword[0] 369 | i += j 370 | } else { 371 | text += token[1] 372 | } 373 | } 374 | return text 375 | } 376 | 377 | function transferCssText (cssText, path) { 378 | let scope = [] 379 | let preFilterToken = [] 380 | const result = [] 381 | const styles = {} 382 | const tokens = tokenizer(cssText) 383 | 384 | while (!tokens.endOfFile()) { 385 | const token = tokens.nextToken() 386 | const type = token[0] 387 | 388 | if (type === 'at-word' && token[1] === '@media') { 389 | scope.push('media') 390 | } 391 | 392 | if (type === '{') { 393 | if (scope[scope.length - 1] === 'media') { 394 | scope.pop() 395 | scope.push('mediaRule') 396 | } else { 397 | scope.push('normalRule') 398 | } 399 | } 400 | 401 | if (type === '}') { 402 | scope.pop() 403 | } 404 | 405 | const isAllow = () => { 406 | const typeIsNormal = type === 'word' || type === 'brackets' || type === 'at-word' 407 | // 如果是 media 需要允许编译 408 | return typeIsNormal && (scope.length === 0 || scope[scope.length - 1] === 'mediaRule') 409 | } 410 | 411 | if (isAllow()) { 412 | const [newToken, style] = genModuleStyleName(token, result[result.length - 1], preFilterToken, path) 413 | preFilterToken = token 414 | result.push(newToken) 415 | Object.assign(styles, style) 416 | } else { 417 | result.push(token) 418 | } 419 | } 420 | 421 | return [splicingCssText(result), styles] 422 | } 423 | 424 | function cssModule (cssText, path) { 425 | const [newCssText, styles] = transferCssText(cssText, path) 426 | 427 | // Append to dom tree 428 | const styleNode = document.createElement('style') 429 | styleNode.textContent = newCssText 430 | document.head.appendChild(styleNode) 431 | 432 | return styles 433 | } 434 | 435 | return res => cssModule(res.resource, res.path) 436 | })() -------------------------------------------------------------------------------- /ppt/lib/flexible.js: -------------------------------------------------------------------------------- 1 | (function(win, lib) { 2 | var doc = win.document; 3 | var docEl = doc.documentElement; 4 | var metaEl = doc.querySelector('meta[name="viewport"]'); 5 | var flexibleEl = doc.querySelector('meta[name="flexible"]'); 6 | var dpr = 0; 7 | var scale = 0; 8 | var tid; 9 | var flexible = lib.flexible || (lib.flexible = {}); 10 | 11 | if (metaEl) { 12 | console.warn('将根据已有的meta标签来设置缩放比例'); 13 | var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/); 14 | if (match) { 15 | scale = parseFloat(match[1]); 16 | dpr = parseInt(1 / scale); 17 | } 18 | } else if (flexibleEl) { 19 | var content = flexibleEl.getAttribute('content'); 20 | if (content) { 21 | var initialDpr = content.match(/initial\-dpr=([\d\.]+)/); 22 | var maximumDpr = content.match(/maximum\-dpr=([\d\.]+)/); 23 | if (initialDpr) { 24 | dpr = parseFloat(initialDpr[1]); 25 | scale = parseFloat((1 / dpr).toFixed(2)); 26 | } 27 | if (maximumDpr) { 28 | dpr = parseFloat(maximumDpr[1]); 29 | scale = parseFloat((1 / dpr).toFixed(2)); 30 | } 31 | } 32 | } 33 | 34 | if (!dpr && !scale) { 35 | var isAndroid = win.navigator.appVersion.match(/android/gi); 36 | var isIPhone = win.navigator.appVersion.match(/iphone/gi); 37 | var devicePixelRatio = win.devicePixelRatio; 38 | if (isIPhone) { 39 | // iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案 40 | if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) { 41 | dpr = 3; 42 | } else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){ 43 | dpr = 2; 44 | } else { 45 | dpr = 1; 46 | } 47 | } else { 48 | // 其他设备下,仍旧使用1倍的方案 49 | dpr = 1; 50 | } 51 | scale = 1 / dpr; 52 | } 53 | 54 | docEl.setAttribute('data-dpr', dpr); 55 | if (!metaEl) { 56 | metaEl = doc.createElement('meta'); 57 | metaEl.setAttribute('name', 'viewport'); 58 | metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no'); 59 | if (docEl.firstElementChild) { 60 | docEl.firstElementChild.appendChild(metaEl); 61 | } else { 62 | var wrap = doc.createElement('div'); 63 | wrap.appendChild(metaEl); 64 | doc.write(wrap.innerHTML); 65 | } 66 | } 67 | 68 | function refreshRem(){ 69 | var width = docEl.getBoundingClientRect().width; 70 | if (width / dpr > 540) { 71 | width = 540 * dpr; 72 | } 73 | var rem = width / 10 / 0.54; 74 | docEl.style.fontSize = rem + 'px'; 75 | flexible.rem = win.rem = rem; 76 | } 77 | 78 | win.addEventListener('resize', function() { 79 | clearTimeout(tid); 80 | tid = setTimeout(refreshRem, 300); 81 | }, false); 82 | win.addEventListener('pageshow', function(e) { 83 | if (e.persisted) { 84 | clearTimeout(tid); 85 | tid = setTimeout(refreshRem, 300); 86 | } 87 | }, false); 88 | 89 | if (doc.readyState === 'complete') { 90 | doc.body.style.fontSize = 12 * dpr + 'px'; 91 | } else { 92 | doc.addEventListener('DOMContentLoaded', function(e) { 93 | doc.body.style.fontSize = 12 * dpr + 'px'; 94 | }, false); 95 | } 96 | 97 | 98 | refreshRem(); 99 | 100 | flexible.dpr = win.dpr = dpr; 101 | flexible.refreshRem = refreshRem; 102 | flexible.rem2px = function(d) { 103 | var val = parseFloat(d) * this.rem; 104 | if (typeof d === 'string' && d.match(/rem$/)) { 105 | val += 'px'; 106 | } 107 | return val; 108 | } 109 | flexible.px2rem = function(d) { 110 | var val = parseFloat(d) / this.rem; 111 | if (typeof d === 'string' && d.match(/px$/)) { 112 | val += 'rem'; 113 | } 114 | return val; 115 | } 116 | 117 | })(window, window['lib'] || (window['lib'] = {})); -------------------------------------------------------------------------------- /ppt/lib/highlight/highlight.css: -------------------------------------------------------------------------------- 1 | /* 2 | Atom One Dark by Daniel Gamage 3 | Original One Dark Syntax theme from https://github.com/atom/one-dark-syntax 4 | base: #282c34 5 | mono-1: #abb2bf 6 | mono-2: #818896 7 | mono-3: #5c6370 8 | hue-1: #56b6c2 9 | hue-2: #61aeee 10 | hue-3: #c678dd 11 | hue-4: #98c379 12 | hue-5: #e06c75 13 | hue-5-2: #be5046 14 | hue-6: #d19a66 15 | hue-6-2: #e6c07b 16 | */ 17 | 18 | .hljs { 19 | display: block; 20 | overflow-x: auto; 21 | padding: 0.5em; 22 | color: #abb2bf; 23 | /* background: #282c34; */ 24 | } 25 | 26 | .hljs-comment, 27 | .hljs-quote { 28 | color: #5c6370; 29 | font-style: italic; 30 | } 31 | 32 | .hljs-doctag, 33 | .hljs-keyword, 34 | .hljs-formula { 35 | color: #c678dd; 36 | } 37 | 38 | .hljs-section, 39 | .hljs-name, 40 | .hljs-selector-tag, 41 | .hljs-deletion, 42 | .hljs-subst { 43 | color: #e06c75; 44 | } 45 | 46 | .hljs-literal { 47 | color: #56b6c2; 48 | } 49 | 50 | .hljs-string, 51 | .hljs-regexp, 52 | .hljs-addition, 53 | .hljs-attribute, 54 | .hljs-meta-string { 55 | color: #98c379; 56 | } 57 | 58 | .hljs-built_in, 59 | .hljs-class .hljs-title { 60 | color: #e6c07b; 61 | } 62 | 63 | .hljs-attr, 64 | .hljs-variable, 65 | .hljs-template-variable, 66 | .hljs-type, 67 | .hljs-selector-class, 68 | .hljs-selector-attr, 69 | .hljs-selector-pseudo, 70 | .hljs-number { 71 | color: #d19a66; 72 | } 73 | 74 | .hljs-symbol, 75 | .hljs-bullet, 76 | .hljs-link, 77 | .hljs-meta, 78 | .hljs-selector-id, 79 | .hljs-title { 80 | color: #61aeee; 81 | } 82 | 83 | .hljs-emphasis { 84 | font-style: italic; 85 | } 86 | 87 | .hljs-strong { 88 | font-weight: bold; 89 | } 90 | 91 | .hljs-link { 92 | text-decoration: underline; 93 | } -------------------------------------------------------------------------------- /ppt/lib/highlight/highlight.pack.js: -------------------------------------------------------------------------------- 1 | /*! highlight.js v9.15.10 | BSD3 License | git.io/hljslicense */ 2 | !function(e){var n="object"==typeof window&&window||"object"==typeof self&&self;"undefined"==typeof exports||exports.nodeType?n&&(n.hljs=e({}),"function"==typeof define&&define.amd&&define([],function(){return n.hljs})):e(exports)}(function(a){var f=[],u=Object.keys,N={},c={},n=/^(no-?highlight|plain|text)$/i,s=/\blang(?:uage)?-([\w-]+)\b/i,t=/((^(<[^>]+>|\t|)+|(?:\n)))/gm,r={case_insensitive:"cI",lexemes:"l",contains:"c",keywords:"k",subLanguage:"sL",className:"cN",begin:"b",beginKeywords:"bK",end:"e",endsWithParent:"eW",illegal:"i",excludeBegin:"eB",excludeEnd:"eE",returnBegin:"rB",returnEnd:"rE",relevance:"r",variants:"v",IDENT_RE:"IR",UNDERSCORE_IDENT_RE:"UIR",NUMBER_RE:"NR",C_NUMBER_RE:"CNR",BINARY_NUMBER_RE:"BNR",RE_STARTERS_RE:"RSR",BACKSLASH_ESCAPE:"BE",APOS_STRING_MODE:"ASM",QUOTE_STRING_MODE:"QSM",PHRASAL_WORDS_MODE:"PWM",C_LINE_COMMENT_MODE:"CLCM",C_BLOCK_COMMENT_MODE:"CBCM",HASH_COMMENT_MODE:"HCM",NUMBER_MODE:"NM",C_NUMBER_MODE:"CNM",BINARY_NUMBER_MODE:"BNM",CSS_NUMBER_MODE:"CSSNM",REGEXP_MODE:"RM",TITLE_MODE:"TM",UNDERSCORE_TITLE_MODE:"UTM",COMMENT:"C",beginRe:"bR",endRe:"eR",illegalRe:"iR",lexemesRe:"lR",terminators:"t",terminator_end:"tE"},b="",h={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0};function _(e){return e.replace(/&/g,"&").replace(//g,">")}function E(e){return e.nodeName.toLowerCase()}function v(e,n){var t=e&&e.exec(n);return t&&0===t.index}function l(e){return n.test(e)}function g(e){var n,t={},r=Array.prototype.slice.call(arguments,1);for(n in e)t[n]=e[n];return r.forEach(function(e){for(n in e)t[n]=e[n]}),t}function R(e){var a=[];return function e(n,t){for(var r=n.firstChild;r;r=r.nextSibling)3===r.nodeType?t+=r.nodeValue.length:1===r.nodeType&&(a.push({event:"start",offset:t,node:r}),t=e(r,t),E(r).match(/br|hr|img|input/)||a.push({event:"stop",offset:t,node:r}));return t}(e,0),a}function i(e){if(r&&!e.langApiRestored){for(var n in e.langApiRestored=!0,r)e[n]&&(e[r[n]]=e[n]);(e.c||[]).concat(e.v||[]).forEach(i)}}function m(o){function s(e){return e&&e.source||e}function c(e,n){return new RegExp(s(e),"m"+(o.cI?"i":"")+(n?"g":""))}!function n(t,e){if(!t.compiled){if(t.compiled=!0,t.k=t.k||t.bK,t.k){function r(t,e){o.cI&&(e=e.toLowerCase()),e.split(" ").forEach(function(e){var n=e.split("|");a[n[0]]=[t,n[1]?Number(n[1]):1]})}var a={};"string"==typeof t.k?r("keyword",t.k):u(t.k).forEach(function(e){r(e,t.k[e])}),t.k=a}t.lR=c(t.l||/\w+/,!0),e&&(t.bK&&(t.b="\\b("+t.bK.split(" ").join("|")+")\\b"),t.b||(t.b=/\B|\b/),t.bR=c(t.b),t.endSameAsBegin&&(t.e=t.b),t.e||t.eW||(t.e=/\B|\b/),t.e&&(t.eR=c(t.e)),t.tE=s(t.e)||"",t.eW&&e.tE&&(t.tE+=(t.e?"|":"")+e.tE)),t.i&&(t.iR=c(t.i)),null==t.r&&(t.r=1),t.c||(t.c=[]),t.c=Array.prototype.concat.apply([],t.c.map(function(e){return function(n){return n.v&&!n.cached_variants&&(n.cached_variants=n.v.map(function(e){return g(n,{v:null},e)})),n.cached_variants||n.eW&&[g(n)]||[n]}("self"===e?t:e)})),t.c.forEach(function(e){n(e,t)}),t.starts&&n(t.starts,e);var i=t.c.map(function(e){return e.bK?"\\.?(?:"+e.b+")\\.?":e.b}).concat([t.tE,t.i]).map(s).filter(Boolean);t.t=i.length?c(function(e,n){for(var t=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./,r=0,a="",i=0;i')+n+(t?"":b):n}function o(){E+=null!=l.sL?function(){var e="string"==typeof l.sL;if(e&&!N[l.sL])return _(g);var n=e?C(l.sL,g,!0,f[l.sL]):O(g,l.sL.length?l.sL:void 0);return 0")+'"');return g+=n,n.length||1}var s=B(e);if(!s)throw new Error('Unknown language: "'+e+'"');m(s);var a,l=t||s,f={},E="";for(a=l;a!==s;a=a.parent)a.cN&&(E=c(a.cN,"",!0)+E);var g="",R=0;try{for(var d,p,M=0;l.t.lastIndex=M,d=l.t.exec(n);)p=r(n.substring(M,d.index),d[0]),M=d.index+p;for(r(n.substr(M)),a=l;a.parent;a=a.parent)a.cN&&(E+=b);return{r:R,value:E,language:e,top:l}}catch(e){if(e.message&&-1!==e.message.indexOf("Illegal"))return{r:0,value:_(n)};throw e}}function O(t,e){e=e||h.languages||u(N);var r={r:0,value:_(t)},a=r;return e.filter(B).filter(M).forEach(function(e){var n=C(e,t,!1);n.language=e,n.r>a.r&&(a=n),n.r>r.r&&(a=r,r=n)}),a.language&&(r.second_best=a),r}function d(e){return h.tabReplace||h.useBR?e.replace(t,function(e,n){return h.useBR&&"\n"===e?"
":h.tabReplace?n.replace(/\t/g,h.tabReplace):""}):e}function o(e){var n,t,r,a,i,o=function(e){var n,t,r,a,i=e.className+" ";if(i+=e.parentNode?e.parentNode.className:"",t=s.exec(i))return B(t[1])?t[1]:"no-highlight";for(n=0,r=(i=i.split(/\s+/)).length;n/g,"\n"):n=e,i=n.textContent,r=o?C(o,i,!0):O(i),(t=R(n)).length&&((a=document.createElementNS("http://www.w3.org/1999/xhtml","div")).innerHTML=r.value,r.value=function(e,n,t){var r=0,a="",i=[];function o(){return e.length&&n.length?e[0].offset!==n[0].offset?e[0].offset"}function u(e){a+=""}function s(e){("start"===e.event?c:u)(e.node)}for(;e.length||n.length;){var l=o();if(a+=_(t.substring(r,l[0].offset)),r=l[0].offset,l===e){for(i.reverse().forEach(u);s(l.splice(0,1)[0]),(l=o())===e&&l.length&&l[0].offset===r;);i.reverse().forEach(c)}else"start"===l[0].event?i.push(l[0].node):i.pop(),s(l.splice(0,1)[0])}return a+_(t.substr(r))}(t,R(a),i)),r.value=d(r.value),e.innerHTML=r.value,e.className=function(e,n,t){var r=n?c[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),-1===e.indexOf(r)&&a.push(r),a.join(" ").trim()}(e.className,o,r.language),e.result={language:r.language,re:r.r},r.second_best&&(e.second_best={language:r.second_best.language,re:r.second_best.r}))}function p(){if(!p.called){p.called=!0;var e=document.querySelectorAll("pre code");f.forEach.call(e,o)}}function B(e){return e=(e||"").toLowerCase(),N[e]||N[c[e]]}function M(e){var n=B(e);return n&&!n.disableAutodetect}return a.highlight=C,a.highlightAuto=O,a.fixMarkup=d,a.highlightBlock=o,a.configure=function(e){h=g(h,e)},a.initHighlighting=p,a.initHighlightingOnLoad=function(){addEventListener("DOMContentLoaded",p,!1),addEventListener("load",p,!1)},a.registerLanguage=function(n,e){var t=N[n]=e(a);i(t),t.aliases&&t.aliases.forEach(function(e){c[e]=n})},a.listLanguages=function(){return u(N)},a.getLanguage=B,a.autoDetection=M,a.inherit=g,a.IR=a.IDENT_RE="[a-zA-Z]\\w*",a.UIR=a.UNDERSCORE_IDENT_RE="[a-zA-Z_]\\w*",a.NR=a.NUMBER_RE="\\b\\d+(\\.\\d+)?",a.CNR=a.C_NUMBER_RE="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",a.BNR=a.BINARY_NUMBER_RE="\\b(0b[01]+)",a.RSR=a.RE_STARTERS_RE="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",a.BE=a.BACKSLASH_ESCAPE={b:"\\\\[\\s\\S]",r:0},a.ASM=a.APOS_STRING_MODE={cN:"string",b:"'",e:"'",i:"\\n",c:[a.BE]},a.QSM=a.QUOTE_STRING_MODE={cN:"string",b:'"',e:'"',i:"\\n",c:[a.BE]},a.PWM=a.PHRASAL_WORDS_MODE={b:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},a.C=a.COMMENT=function(e,n,t){var r=a.inherit({cN:"comment",b:e,e:n,c:[]},t||{});return r.c.push(a.PWM),r.c.push({cN:"doctag",b:"(?:TODO|FIXME|NOTE|BUG|XXX):",r:0}),r},a.CLCM=a.C_LINE_COMMENT_MODE=a.C("//","$"),a.CBCM=a.C_BLOCK_COMMENT_MODE=a.C("/\\*","\\*/"),a.HCM=a.HASH_COMMENT_MODE=a.C("#","$"),a.NM=a.NUMBER_MODE={cN:"number",b:a.NR,r:0},a.CNM=a.C_NUMBER_MODE={cN:"number",b:a.CNR,r:0},a.BNM=a.BINARY_NUMBER_MODE={cN:"number",b:a.BNR,r:0},a.CSSNM=a.CSS_NUMBER_MODE={cN:"number",b:a.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},a.RM=a.REGEXP_MODE={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[a.BE,{b:/\[/,e:/\]/,r:0,c:[a.BE]}]},a.TM=a.TITLE_MODE={cN:"title",b:a.IR,r:0},a.UTM=a.UNDERSCORE_TITLE_MODE={cN:"title",b:a.UIR,r:0},a.METHOD_GUARD={b:"\\.\\s*"+a.UIR,r:0},a});hljs.registerLanguage("json",function(e){var i={literal:"true false null"},n=[e.QSM,e.CNM],r={e:",",eW:!0,eE:!0,c:n,k:i},t={b:"{",e:"}",c:[{cN:"attr",b:/"/,e:/"/,c:[e.BE],i:"\\n"},e.inherit(r,{b:/:/})],i:"\\S"},c={b:"\\[",e:"\\]",c:[e.inherit(r)],i:"\\S"};return n.splice(n.length,0,t,c),{c:n,k:i,i:"\\S"}});hljs.registerLanguage("javascript",function(e){var r="[A-Za-z$_][0-9A-Za-z$_]*",t={keyword:"in of if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const export super debugger as async await static import from as",literal:"true false null undefined NaN Infinity",built_in:"eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Error EvalError InternalError RangeError ReferenceError StopIteration SyntaxError TypeError URIError Number Math Date String RegExp Array Float32Array Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require module console window document Symbol Set Map WeakSet WeakMap Proxy Reflect Promise"},a={cN:"number",v:[{b:"\\b(0[bB][01]+)"},{b:"\\b(0[oO][0-7]+)"},{b:e.CNR}],r:0},s={cN:"subst",b:"\\$\\{",e:"\\}",k:t,c:[]},c={b:"html`",e:"",starts:{e:"`",rE:!1,c:[e.BE,s],sL:"xml"}},n={b:"css`",e:"",starts:{e:"`",rE:!1,c:[e.BE,s],sL:"css"}},o={cN:"string",b:"`",e:"`",c:[e.BE,s]};s.c=[e.ASM,e.QSM,c,n,o,a,e.RM];var i=s.c.concat([e.CBCM,e.CLCM]);return{aliases:["js","jsx"],k:t,c:[{cN:"meta",r:10,b:/^\s*['"]use (strict|asm)['"]/},{cN:"meta",b:/^#!/,e:/$/},e.ASM,e.QSM,c,n,o,e.CLCM,e.CBCM,a,{b:/[{,]\s*/,r:0,c:[{b:r+"\\s*:",rB:!0,r:0,c:[{cN:"attr",b:r,r:0}]}]},{b:"("+e.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[e.CLCM,e.CBCM,e.RM,{cN:"function",b:"(\\(.*?\\)|"+r+")\\s*=>",rB:!0,e:"\\s*=>",c:[{cN:"params",v:[{b:r},{b:/\(\s*\)/},{b:/\(/,e:/\)/,eB:!0,eE:!0,k:t,c:i}]}]},{cN:"",b:/\s/,e:/\s*/,skip:!0},{b://,sL:"xml",c:[{b:/<[A-Za-z0-9\\._:-]+\s*\/>/,skip:!0},{b:/<[A-Za-z0-9\\._:-]+/,e:/(\/[A-Za-z0-9\\._:-]+|[A-Za-z0-9\\._:-]+\/)>/,skip:!0,c:[{b:/<[A-Za-z0-9\\._:-]+\s*\/>/,skip:!0},"self"]}]}],r:0},{cN:"function",bK:"function",e:/\{/,eE:!0,c:[e.inherit(e.TM,{b:r}),{cN:"params",b:/\(/,e:/\)/,eB:!0,eE:!0,c:i}],i:/\[|%/},{b:/\$[(.]/},e.METHOD_GUARD,{cN:"class",bK:"class",e:/[{;=]/,eE:!0,i:/[:"\[\]]/,c:[{bK:"extends"},e.UTM]},{bK:"constructor get set",e:/\{/,eE:!0}],i:/#(?!!)/}});hljs.registerLanguage("xml",function(s){var e={eW:!0,i:/`]+/}]}]}]};return{aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf"],cI:!0,c:[{cN:"meta",b:"",r:10,c:[{b:"\\[",e:"\\]"}]},s.C("\x3c!--","--\x3e",{r:10}),{b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"meta",b:/<\?xml/,e:/\?>/,r:10},{b:/<\?(php)?/,e:/\?>/,sL:"php",c:[{b:"/\\*",e:"\\*/",skip:!0},{b:'b"',e:'"',skip:!0},{b:"b'",e:"'",skip:!0},s.inherit(s.ASM,{i:null,cN:null,c:null,skip:!0}),s.inherit(s.QSM,{i:null,cN:null,c:null,skip:!0})]},{cN:"tag",b:"|$)",e:">",k:{name:"style"},c:[e],starts:{e:"",rE:!0,sL:["css","xml"]}},{cN:"tag",b:"|$)",e:">",k:{name:"script"},c:[e],starts:{e:"<\/script>",rE:!0,sL:["actionscript","javascript","handlebars","xml","vbscript"]}},{cN:"tag",b:"",c:[{cN:"name",b:/[^\/><\s]+/,r:0},e]}]}});hljs.registerLanguage("css",function(e){var c={b:/(?:[A-Z\_\.\-]+|--[a-zA-Z0-9_-]+)\s*:/,rB:!0,e:";",eW:!0,c:[{cN:"attribute",b:/\S/,e:":",eE:!0,starts:{eW:!0,eE:!0,c:[{b:/[\w-]+\(/,rB:!0,c:[{cN:"built_in",b:/[\w-]+/},{b:/\(/,e:/\)/,c:[e.ASM,e.QSM]}]},e.CSSNM,e.QSM,e.ASM,e.CBCM,{cN:"number",b:"#[0-9A-Fa-f]+"},{cN:"meta",b:"!important"}]}}]};return{cI:!0,i:/[=\/|'\$]/,c:[e.CBCM,{cN:"selector-id",b:/#[A-Za-z0-9_-]+/},{cN:"selector-class",b:/\.[A-Za-z0-9_-]+/},{cN:"selector-attr",b:/\[/,e:/\]/,i:"$"},{cN:"selector-pseudo",b:/:(:)?[a-zA-Z0-9\_\-\+\(\)"'.]+/},{b:"@(font-face|page)",l:"[a-z-]+",k:"font-face page"},{b:"@",e:"[{;]",i:/:/,c:[{cN:"keyword",b:/\w+/},{b:/\s/,eW:!0,eE:!0,r:0,c:[e.ASM,e.QSM,e.CSSNM]}]},{cN:"selector-tag",b:"[a-zA-Z-][a-zA-Z0-9_-]*",r:0},{b:"{",e:"}",i:/\S/,c:[e.CBCM,c]}]}}); -------------------------------------------------------------------------------- /ppt/lib/liberty-plugin.js: -------------------------------------------------------------------------------- 1 | var GrassPlugin = (function () { 2 | function loader (source) { 3 | source = source.trim() 4 | if (/^\/\/\s*#no\s+compile\s*\n/.test(source)) { 5 | return source 6 | } 7 | 8 | const result = handleSource(source) 9 | // if no template and script,it's not grass file 10 | if (!result || (!result.script && !result.template)) { 11 | return source 12 | } 13 | return conversion(result) || source 14 | } 15 | 16 | function handleSource (source) { 17 | if (!source) return null 18 | 19 | const template = getTagContent(/<\s*template(\s*[^<>]*|\s*)>([\s\S]+)<\/\s*template\s*>/, source) 20 | const script = getTagContent(/<\s*script(\s*[^<>]*|\s*)>([\s\S]+)<\/\s*script\s*>/, source) 21 | 22 | return { 23 | script, 24 | template, 25 | options: null, 26 | } 27 | } 28 | 29 | function getTagContent (reg, source) { 30 | const res = source.match(reg) 31 | if (res) { 32 | return { 33 | content: res[2].trim(), 34 | options: getTagOptions(res[1].trim()) 35 | } 36 | } 37 | return null 38 | } 39 | 40 | function getTagOptions (str) { 41 | const options = Object.create(null) 42 | if (!str) return options 43 | 44 | let result, i = 0 45 | const reg = /\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))/g 46 | 47 | while (result = reg.exec(str)) { 48 | const name = result[1] 49 | const val = result[3] || result[4] || result[5] 50 | 51 | if (name && val !== undefined) { 52 | options[name] = val 53 | } 54 | if (++i > 2) break 55 | } 56 | 57 | return options 58 | } 59 | 60 | function conversion (file) { 61 | return !file.template 62 | ? onlyScript(file) 63 | : !file.script 64 | ? onlyTemplate(file) 65 | : completeFormat(file) 66 | } 67 | 68 | function completeFormat (file) { 69 | let newCode 70 | const scrCode = file.script.content 71 | const tempStr = file.template.content.replace(/`/g, '\\`') 72 | const string = `\`${tempStr}\`\n` 73 | const method = `template () { return \`${tempStr}\`; }\n` 74 | 75 | newCode = replaceMethod(scrCode, method) 76 | newCode = replaceString(newCode, string) 77 | 78 | return newCode 79 | } 80 | 81 | function onlyTemplate (file) { 82 | const options = file.template.options 83 | const tempStr = file.template.content.replace(/`/g, '\\`') 84 | 85 | let method = options.name 86 | ? `function ${options.name}()` 87 | : 'function ()' 88 | 89 | method += ` { return \`${tempStr}\`; }` 90 | return `module.exports = ${method}` 91 | } 92 | 93 | function onlyScript (file) { 94 | return file.script.content 95 | } 96 | 97 | function replaceMethod (code, method) { 98 | // "//#temp method" or "//#temp" 99 | return code.replace(/\/\/\s*#temp(\s+method)?\s*\n/g, method) 100 | } 101 | 102 | function replaceString(code, string) { 103 | // "//#temp string" or "/*#temp string*/" 104 | const newCode = code.replace(/\/\/\s*#temp\s+string?\s*\n/g, string) 105 | return newCode.replace(/\/\*\s*#temp\s+string?\s*\*\//g, string) 106 | } 107 | 108 | return res => loader(res.resource) 109 | })() 110 | -------------------------------------------------------------------------------- /ppt/lib/liberty.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 3 | typeof define === 'function' && define.amd ? define(factory) : 4 | (global = global || self, global.Liberty = factory()); 5 | }(this, function () { 'use strict'; 6 | 7 | function assertPath(path) { 8 | if (typeof path !== 'string') { 9 | throw new TypeError('Path must be a string. Received ' + JSON.stringify(path)); 10 | } 11 | } 12 | function normalizeStringPosix(path, allowAboveRoot) { 13 | var res = ''; 14 | var lastSegmentLength = 0; 15 | var lastSlash = -1; 16 | var dots = 0; 17 | var code; 18 | for (var i = 0; i <= path.length; ++i) { 19 | if (i < path.length) { 20 | code = path.charCodeAt(i); 21 | } else if (code === 47 ) 22 | break; 23 | else 24 | code = 47 ; 25 | if (code === 47 ) { 26 | if (lastSlash === i - 1 || dots === 1) ; else if (lastSlash !== i - 1 && dots === 2) { 27 | if (res.length < 2 || lastSegmentLength !== 2 || res.charCodeAt(res.length - 1) !== 46 || res.charCodeAt(res.length - 2) !== 46 ) { 28 | if (res.length > 2) { 29 | var lastSlashIndex = res.lastIndexOf('/'); 30 | if (lastSlashIndex !== res.length - 1) { 31 | if (lastSlashIndex === -1) { 32 | res = ''; 33 | lastSegmentLength = 0; 34 | } else { 35 | res = res.slice(0, lastSlashIndex); 36 | lastSegmentLength = res.length - 1 - res.lastIndexOf('/'); 37 | } 38 | lastSlash = i; 39 | dots = 0; 40 | continue; 41 | } 42 | } else if (res.length === 2 || res.length === 1) { 43 | res = ''; 44 | lastSegmentLength = 0; 45 | lastSlash = i; 46 | dots = 0; 47 | continue; 48 | } 49 | } 50 | if (allowAboveRoot) { 51 | if (res.length > 0) 52 | res += '/..'; 53 | else 54 | res = '..'; 55 | lastSegmentLength = 2; 56 | } 57 | } else { 58 | if (res.length > 0) 59 | res += '/' + path.slice(lastSlash + 1, i); 60 | else 61 | res = path.slice(lastSlash + 1, i); 62 | lastSegmentLength = i - lastSlash - 1; 63 | } 64 | lastSlash = i; 65 | dots = 0; 66 | } else if (code === 46 && dots !== -1) { 67 | ++dots; 68 | } else { 69 | dots = -1; 70 | } 71 | } 72 | return res; 73 | } 74 | var posix = { 75 | normalize: function normalize(path) { 76 | assertPath(path); 77 | if (path.length === 0) return '.'; 78 | var isAbsolute = path.charCodeAt(0) === 47 ; 79 | var trailingSeparator = path.charCodeAt(path.length - 1) === 47 ; 80 | path = normalizeStringPosix(path, !isAbsolute); 81 | if (path.length === 0 && !isAbsolute) path = '.'; 82 | if (path.length > 0 && trailingSeparator) path += '/'; 83 | if (isAbsolute) return '/' + path; 84 | return path; 85 | }, 86 | isAbsolute: function isAbsolute(path) { 87 | assertPath(path); 88 | return path.length > 0 && path.charCodeAt(0) === 47 ; 89 | }, 90 | join: function join() { 91 | if (arguments.length === 0) 92 | return '.'; 93 | var joined; 94 | for (var i = 0; i < arguments.length; ++i) { 95 | var arg = arguments[i]; 96 | assertPath(arg); 97 | if (arg.length > 0) { 98 | if (joined === undefined) 99 | joined = arg; 100 | else 101 | joined += '/' + arg; 102 | } 103 | } 104 | if (joined === undefined) 105 | return '.'; 106 | return posix.normalize(joined); 107 | }, 108 | dirname: function dirname(path) { 109 | assertPath(path); 110 | if (path.length === 0) return '.'; 111 | var code = path.charCodeAt(0); 112 | var hasRoot = code === 47 ; 113 | var end = -1; 114 | var matchedSlash = true; 115 | for (var i = path.length - 1; i >= 1; --i) { 116 | code = path.charCodeAt(i); 117 | if (code === 47 ) { 118 | if (!matchedSlash) { 119 | end = i; 120 | break; 121 | } 122 | } else { 123 | matchedSlash = false; 124 | } 125 | } 126 | if (end === -1) return hasRoot ? '/' : '.'; 127 | if (hasRoot && end === 1) return '//'; 128 | return path.slice(0, end); 129 | }, 130 | extname: function extname(path) { 131 | assertPath(path); 132 | var startDot = -1; 133 | var startPart = 0; 134 | var end = -1; 135 | var matchedSlash = true; 136 | var preDotState = 0; 137 | for (var i = path.length - 1; i >= 0; --i) { 138 | var code = path.charCodeAt(i); 139 | if (code === 47 ) { 140 | if (!matchedSlash) { 141 | startPart = i + 1; 142 | break; 143 | } 144 | continue; 145 | } 146 | if (end === -1) { 147 | matchedSlash = false; 148 | end = i + 1; 149 | } 150 | if (code === 46 ) { 151 | if (startDot === -1) 152 | startDot = i; 153 | else if (preDotState !== 1) 154 | preDotState = 1; 155 | } else if (startDot !== -1) { 156 | preDotState = -1; 157 | } 158 | } 159 | if (startDot === -1 || end === -1 || 160 | preDotState === 0 || 161 | preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) { 162 | return ''; 163 | } 164 | return path.slice(startDot, end); 165 | }, 166 | sep: '/', 167 | delimiter: ':', 168 | win32: null, 169 | posix: null 170 | }; 171 | 172 | function genMappings (source) { 173 | const lines = source.split('\n'); 174 | const code = l => `AA${l}A`; 175 | return code('D') + ';' + lines.map(() => code('C')).join(';') 176 | } 177 | function sourcemap (resource, responseURL) { 178 | const content = JSON.stringify({ 179 | version: 3, 180 | sources: [responseURL], 181 | mappings: genMappings(resource), 182 | }); 183 | return `//@ sourceMappingURL=data:application/json;base64,${btoa(content)}` 184 | } 185 | 186 | var config = { 187 | alias: {}, 188 | hooks: {}, 189 | init: false, 190 | exname: '.js', 191 | sourcemap: true, 192 | staticOptimize: true, 193 | }; 194 | 195 | class Cache { 196 | constructor () { 197 | this.Modules = new Map(); 198 | } 199 | cache (path, Module, update) { 200 | if (update || !this.has(path)) { 201 | this.Modules.set(path, Module); 202 | } 203 | } 204 | has (path) { 205 | return this.Modules.has(path) 206 | } 207 | get (path) { 208 | return this.Modules.get(path) || null 209 | } 210 | clear (path) { 211 | return this.Modules.delete(path) 212 | } 213 | clearAll () { 214 | return this.Modules.clear() 215 | } 216 | } 217 | var cacheModule = new Cache(); 218 | const resourceCache = new Cache(); 219 | const responseURLModules = new Cache(); 220 | 221 | function request (url, envPath, isAsync) { 222 | const getCache = xhr => { 223 | const responseURL = xhr.responseURL; 224 | if (responseURLModules.has(responseURL)) { 225 | xhr.abort(); 226 | return { 227 | responseURL, 228 | resource: null, 229 | haveCache: true, 230 | } 231 | } 232 | if (!isAsync) { 233 | console.warn(`The module [${url}] is requested by synchronization, please avoid using this method\n\n --> from [${envPath}]\n`); 234 | } 235 | return null 236 | }; 237 | const xhr = new XMLHttpRequest(); 238 | xhr.open('GET', url, isAsync); 239 | xhr.send(); 240 | if (isAsync) { 241 | return new Promise((resolve, reject) => { 242 | xhr.onreadystatechange = () => { 243 | const cache = getCache(xhr); 244 | cache && resolve({ target: cache }); 245 | }; 246 | xhr.onload = resolve; 247 | xhr.onerror = reject; 248 | }) 249 | } 250 | return getCache(xhr) || xhr 251 | } 252 | function dealWithResponse (url, xhr, envPath) { 253 | if (xhr.haveCache) return xhr 254 | if (xhr.readyState === 4) { 255 | if (xhr.status === 200) { 256 | if (typeof xhr.response === 'string') { 257 | return { 258 | resource: xhr.response, 259 | responseURL: xhr.responseURL, 260 | } 261 | } 262 | } else if (xhr.status === 404) { 263 | throw Error(`Module [${url}] not found.\n\n --> from [${envPath}]\n`) 264 | } 265 | } 266 | } 267 | async function asyncRequest (url, envPath) { 268 | const { target: xhr } = await request(url, envPath, true); 269 | return dealWithResponse(url, xhr, envPath) 270 | } 271 | function syncRequest (url, envPath) { 272 | const xhr = request(url, envPath, false); 273 | return dealWithResponse(url, xhr, envPath) 274 | } 275 | 276 | const PROTOCOL = /\w+:\/\/?/; 277 | const readOnly = (obj, key, value) => { 278 | Object.defineProperty(obj, key, { 279 | value: value, 280 | writable: false, 281 | }); 282 | }; 283 | const readOnlyMap = obj => { 284 | const newObj = {}; 285 | for (const key in obj) { 286 | if (obj.hasOwnProperty(key)) { 287 | const val = obj[key]; 288 | val && typeof val === 'object' 289 | ? readOnly(newObj, key, readOnlyMap(val)) 290 | : readOnly(newObj, key, val); 291 | } 292 | } 293 | return newObj 294 | }; 295 | const getLegalName = name => { 296 | return name in window 297 | ? getLegalName(name + '1') 298 | : name 299 | }; 300 | const PREFIX_RE = /(@[^\/]+)(\/.+)*/; 301 | const applyAlias = (path, alias, envPath) => { 302 | return path.replace(PREFIX_RE, ($1, $2, $3 = '') => { 303 | const prefix = $2.slice(1, $2.length); 304 | const aliasStr = alias[prefix]; 305 | if (typeof aliasStr !== 'string') { 306 | throw Error(`Alias [${prefix}] does not exist.\n\n ---> from ${envPath} \n` ) 307 | } 308 | return PROTOCOL.test(aliasStr) 309 | ? aliasStr + $3 310 | : posix.join(aliasStr, $3) 311 | }) 312 | }; 313 | const getParentConfig = (envPath, responseURL) => { 314 | const dirname = posix.dirname(responseURL); 315 | const envDir = (new URL(dirname)).pathname; 316 | return { envDir, envPath, dirname } 317 | }; 318 | const realPath = (path, {envPath, envDir}, config) => { 319 | const alias = config.alias; 320 | if (alias && path[0] === '@') { 321 | path = applyAlias(path, alias, envPath); 322 | } 323 | if (path === '.' || path === './') path = envPath; 324 | let exname = posix.extname(path); 325 | if (!exname) { 326 | path += config.exname; 327 | exname = config.exname; 328 | } 329 | if (!posix.isAbsolute(path) && !PROTOCOL.test(path)) { 330 | path = posix.join(envDir, path); 331 | } 332 | return { path, exname } 333 | }; 334 | 335 | function getFilePaths (codeStr, set, processPath) { 336 | let res; 337 | const paths = []; 338 | codeStr = ' ' + codeStr.replace(/[^:]\/\/.*|\/\*[\w\W]*?\*\//g, ''); 339 | const REG = /[^\w\.](require[\n\s]*)\(\s*\n*['"]([^'"]+)['"]\n*\s*\);*/g; 340 | while (res = REG.exec(codeStr)) { 341 | if (res[2]) { 342 | const path = processPath(res[2]).path; 343 | if (!paths.includes(path) && !set.has(path)) { 344 | paths.push(path); 345 | } 346 | } 347 | } 348 | return paths 349 | } 350 | function getFileResult (envPath, paths) { 351 | return Promise.all(paths.map(async path => { 352 | if (!cacheModule.has(path) && !resourceCache.has(path)){ 353 | const content = await asyncRequest(path, envPath); 354 | if (!content.haveCache) { 355 | return { path, content } 356 | } 357 | } 358 | })) 359 | } 360 | async function deepTraversal (paths, envPath, config, set = new Set()) { 361 | paths.forEach(v => set.add(v)); 362 | const files = await getFileResult(envPath, paths); 363 | const children = files.map((fileInfo) => { 364 | if (!fileInfo) return null 365 | const { path, content } = fileInfo; 366 | const parentConfig = getParentConfig(path, content.responseURL); 367 | resourceCache.cache(path, content); 368 | const paths = getFilePaths(content.resource, set, 369 | childPath => realPath(childPath, parentConfig, config)); 370 | return paths.length > 0 371 | ? deepTraversal(paths, parentConfig.envPath, config, set) 372 | : null 373 | }); 374 | return Promise.all(children).then(() => set) 375 | } 376 | function staticOptimize (entrance, parentConfig, config) { 377 | const paths = realPath(entrance, parentConfig, config); 378 | return deepTraversal([paths.path], parentConfig.envPath, config) 379 | } 380 | 381 | class Plugins { 382 | constructor (type) { 383 | this.type = type; 384 | this.plugins = new Set(); 385 | } 386 | add (fn) { 387 | this.plugins.add(fn); 388 | } 389 | forEach (params) { 390 | let res = params; 391 | for (const plugin of this.plugins.values()) { 392 | res.resource = plugin(res); 393 | } 394 | return res 395 | } 396 | } 397 | const map = { 398 | allPlugins: new Map(), 399 | add (type, fn) { 400 | if (typeof type === 'string' && typeof fn === 'function') { 401 | if (!this.allPlugins.has(type)) { 402 | const pluginClass = new Plugins(type); 403 | pluginClass.add(fn); 404 | this.allPlugins.set(type, pluginClass); 405 | } else { 406 | this.allPlugins.get(type).add(fn); 407 | } 408 | } else { 409 | throw TypeError('The "parameter" does not meet the requirements') 410 | } 411 | }, 412 | get (type = '*') { 413 | return this.allPlugins.get(type) 414 | }, 415 | run (type, params) { 416 | const plugins = this.allPlugins.get(type); 417 | if (plugins) { 418 | return plugins.forEach(params) 419 | } 420 | return params 421 | } 422 | }; 423 | function addDefaultPlugins () { 424 | map.add('*', opts => opts.resource); 425 | map.add('.js', jsPlugin); 426 | } 427 | 428 | let isStart = false; 429 | function init (opts = {}) { 430 | if (this.config && this.config.init) { 431 | throw new Error('Can\'t repeat init.') 432 | } 433 | opts.init = true; 434 | readOnly(this, 'config', 435 | readOnlyMap(Object.assign(config, opts)) 436 | ); 437 | return entrance => { 438 | if (isStart) throw Error('Can\'t repeat start.') 439 | if (!entrance || (!posix.isAbsolute(entrance) && !PROTOCOL.test(entrance))) { 440 | throw Error('The startup path must be an absolute path.') 441 | } 442 | const parentConfig = { 443 | envPath: entrance, 444 | envDir: posix.dirname(entrance) || '/', 445 | }; 446 | const start = () => { 447 | if (isStart) throw Error('Can\'t repeat start.') 448 | isStart = true; 449 | importModule(entrance, parentConfig, this.config, true); 450 | }; 451 | readOnly(this.config, 'entrance', entrance); 452 | addDefaultPlugins(); 453 | if (this.config.staticOptimize) { 454 | staticOptimize(entrance, parentConfig, this.config) 455 | .then(set => { 456 | typeof this.config.hooks.ready === 'function' 457 | ? this.config.hooks.ready(set, start) 458 | : start(); 459 | }); 460 | } else { 461 | start(); 462 | } 463 | } 464 | } 465 | function addPlugin (exname, fn) { 466 | if (this.config && this.config.init) { 467 | throw Error('Unable to add plugin after initialization.') 468 | } else { 469 | if (typeof exname === 'string') { 470 | const types = exname.split(' '); 471 | if (types.length) { 472 | if (types.length === 1) { 473 | map.add(types[0], fn); 474 | } else { 475 | for (const type of types) { 476 | map.add(type, fn); 477 | } 478 | } 479 | } 480 | } 481 | } 482 | } 483 | async function ready (paths = [], entrance) { 484 | const config = this.config; 485 | if (!config || !config.init) { 486 | throw Error('This method must be called after initialization.') 487 | } 488 | if (isStart) { 489 | throw Error('Static resources must be loaded before the module is loaded.') 490 | } 491 | await Promise.all(paths.map(p => { 492 | const isProtocolUrl = PROTOCOL.test(p); 493 | if (!isProtocolUrl) p = posix.normalize(p); 494 | if (!posix.isAbsolute(p) && !isProtocolUrl) { 495 | throw Error(`The path [${p}] must be an absolute path.\n\n ---> from [ready method]\n`) 496 | } 497 | return resourceCache.has(p) 498 | ? null 499 | : asyncRequest(p, 'ready method').then(resource => { 500 | resourceCache.cache(p, resource); 501 | }) 502 | })); 503 | return entrance 504 | } 505 | function importAll (paths, parentInfo, config) { 506 | if (Array.isArray(paths)) { 507 | return paths.length === 0 508 | ? Promise.resolve([]) 509 | : Promise.all(paths.map(path => importModule(path, parentInfo, config, true))) 510 | } 511 | throw Error(`Paths [${paths}] must be an array.\n\n ---> from [${parentInfo.envPath}]\n`) 512 | } 513 | function importModule (path, parentInfo, config, isAsync) { 514 | const envPath = parentInfo.envPath; 515 | if (!path || typeof path !== 'string') { 516 | throw TypeError(`Require path [${path}] must be a string. \n\n ---> from [${envPath}]\n`) 517 | } 518 | const pathOpts = realPath(path, parentInfo, config); 519 | if (cacheModule.has(pathOpts.path)) { 520 | const Module = cacheModule.get(pathOpts.path); 521 | const result = getModuleResult(Module); 522 | return !isAsync 523 | ? result 524 | : Promise.resolve(result) 525 | } 526 | return isAsync 527 | ? getModuleForAsync(pathOpts, config, parentInfo) 528 | : getModuleForSync(pathOpts, config, envPath) 529 | } 530 | async function getModuleForAsync ({path, exname}, config, parentInfo) { 531 | let staticFile = null; 532 | if (resourceCache.has(path)) { 533 | staticFile = resourceCache.get(path); 534 | } else { 535 | await staticOptimize(path, parentInfo, config); 536 | staticFile = resourceCache.get(path); 537 | } 538 | return genModule(path, exname, config, staticFile) 539 | } 540 | function getModuleForSync ({path, exname}, config, envPath) { 541 | const staticFile = resourceCache.has(path) 542 | ? resourceCache.get(path) 543 | : syncRequest(path, envPath); 544 | return genModule(path, exname, config, staticFile) 545 | } 546 | function getModuleResult (Module) { 547 | return Module && typeof Module === 'object' && Module.__rustleModule 548 | ? Module.exports 549 | : Module 550 | } 551 | function genModule (path, exname, config, staticFile) { 552 | const Module = processResource(path, exname, config, staticFile); 553 | resourceCache.clear(path); 554 | return Module 555 | } 556 | function processResource (path, exname, config, {resource, responseURL}) { 557 | const Module = responseURLModules.has(responseURL) 558 | ? responseURLModules.get(responseURL) 559 | : runPlugins(exname, { 560 | path, 561 | exname, 562 | config, 563 | resource, 564 | responseURL, 565 | }); 566 | cacheModule.cache(path, Module); 567 | responseURLModules.cache(responseURL, Module); 568 | return getModuleResult(Module) 569 | } 570 | function runPlugins (type, opts) { 571 | opts = map.run('*', opts); 572 | return map.run(type, opts).resource 573 | } 574 | 575 | function run (scriptCode, rigisterObject, windowModuleName) { 576 | const node = document.createElement('script'); 577 | node.text = scriptCode; 578 | node.style.display = 'none'; 579 | window[windowModuleName] = rigisterObject; 580 | document.body.append(node); 581 | document.body.removeChild(node); 582 | delete window[windowModuleName]; 583 | } 584 | function getRegisterParams (config, path, responseURL) { 585 | const Module = { exports: {} }; 586 | const parentInfo = getParentConfig(path, responseURL); 587 | readOnly(Module, '__rustleModule', true); 588 | const require = path => importModule(path, parentInfo, config, false); 589 | require.async = path => importModule(path, parentInfo, config, true); 590 | require.all = paths => importAll(paths, parentInfo, config); 591 | return { 592 | Module, 593 | require, 594 | dirname: parentInfo.dirname, 595 | } 596 | } 597 | function generateObject (config, path, responseURL) { 598 | const { dirname, Module, require } = getRegisterParams(config, path, responseURL); 599 | return { 600 | require, 601 | module: Module, 602 | __dirname: dirname, 603 | exports: Module.exports, 604 | __filename: responseURL, 605 | } 606 | } 607 | function generateScriptCode (basecode, path, responseURL, parmas, config) { 608 | const randomId = Math.floor(Math.random() * 10000); 609 | const moduleName = getLegalName('__rustleModuleObject') + randomId; 610 | let scriptCode = 611 | `(function ${getLegalName(path.replace(/[@#\/\.:-]/g, '_'))} (${parmas.join(',')}) {` + 612 | `\n${basecode}` + 613 | `\n}).call(undefined, window.${moduleName}.${parmas.join(`,window.${moduleName}.`)});`; 614 | if (config.sourcemap) { 615 | scriptCode += `\n${sourcemap(scriptCode, responseURL)}`; 616 | } 617 | return { moduleName, scriptCode } 618 | } 619 | function runInThisContext (code, path, responseURL, config) { 620 | const rigisterObject = generateObject(config, path, responseURL); 621 | const Module = rigisterObject.module; 622 | const parmas = Object.keys(rigisterObject); 623 | const { moduleName, scriptCode } = generateScriptCode(code, path, responseURL, parmas, config); 624 | cacheModule.cache(path, Module); 625 | responseURLModules.cache(responseURL, Module); 626 | run(scriptCode, rigisterObject, moduleName); 627 | cacheModule.clear(path); 628 | responseURLModules.clear(responseURL); 629 | return Module 630 | } 631 | function jsPlugin ({resource, path, config, responseURL}) { 632 | return runInThisContext(resource, path, responseURL, config) 633 | } 634 | 635 | var index = { 636 | init, 637 | path: posix, 638 | ready, 639 | addPlugin, 640 | plugins: { 641 | jsPlugin, 642 | } 643 | }; 644 | 645 | return index; 646 | 647 | })); 648 | -------------------------------------------------------------------------------- /思路.md: -------------------------------------------------------------------------------- 1 | ## js 的异步发展历史 2 | callback -> jq.deferred -> promise -> (generator + promise = co) -> async/await 3 | + callback --> callback hell + demo 4 | + deferred -> promise 社区 5 | 6 | + promise 的缺点(不够同步的写法) 7 | + co -> async/await 8 | 9 | 1. 讲 generator 函数的分步执行,得手动调用 --> 如何像 async 一样 10 | 2. 像 async 之后如果想 await 处理异步 --> 结合 promise 11 | 3. 讲 promise 的几种的异步处理 --> 实现 generator 与 promise 的结合 12 | 13 | ## 什么是回调 14 | + http://callbackhell.com/ 15 | + https://stackoverflow.com/questions/25098066/what-is-callback-hell-and-how-and-why-does-rx-solve-it 16 | 17 | ## 如何解决 http://callbackhell.com/ 18 | 2. 保证代码的浅 19 | 1. 封装、抽象 -> 模块 20 | 21 | ## callback -> deferred -> promise 22 | ### 社区为什么选择 promise 23 | 1. https://groups.google.com/forum/#!topic/commonjs/6T9z75fohDk 24 | 2. https://www.cnblogs.com/tarol/p/9042407.html 25 | 3. Q 这个库 Promise 的集大成者 26 | 27 | ### promise 的缺点 28 | 1. 还是有嵌套的代码,不够同步(代码量多很多) 29 | 2. 堆栈信息过深 30 | 31 | ## generator 的出现 32 | 1. generator 函数的使用(自行 mdn) 33 | 34 | ## 如何使用 generator 解决 promise 带来的问题 35 | 1. 写个简单的自执行器 demo 36 | 37 | ## 介绍 co 源码 38 | 39 | ## async/await(语法糖) 演示 --> 也结合了 promise 40 | 41 | ## 结论 42 | 1. promsie 和 generator 为我们带来了类似同步的异步流程体验 43 | 2. 然后对比开头的什么是 callback 的 demo 44 | 3. es6 的很多新规范都是处理异步(副作用)的东西,包括 react (下面第三步) 45 | 4. 副作用与函数式是不契合的(副作用的不可控和函数式的纯) 46 | 5. 更好的异步体验,更好的结合函数式编程 47 | 48 | ## es6 49 | 1. 更多的数据结构 50 | 2. 更多的扩展和语法改善 51 | 3. 更好的异步处理 52 | 4. 更好的 typeArray 支持 53 | --------------------------------------------------------------------------------