├── .gitignore ├── .jsinspectrc ├── northbrook.json ├── .travis.yml ├── .eslintrc ├── .flowconfig ├── examples ├── rollup.config.js ├── counter │ ├── index.html │ ├── index.js │ └── app.js ├── timer │ ├── index.html │ ├── index.js │ └── app.js ├── mouse-position │ ├── index.html │ ├── index.js │ └── app.js └── stopwatch │ ├── index.html │ ├── index.js │ └── app.js ├── src ├── pair.js ├── run.js ├── test │ ├── show.js │ └── index.js ├── dom.js ├── vdom.js ├── session.js ├── signalgen.js ├── event.js └── signal.js ├── dist ├── arrow.js.map ├── arrow.min.js └── arrow.js ├── LICENSE ├── README.md └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .idea/ 3 | /coverage/ 4 | -------------------------------------------------------------------------------- /.jsinspectrc: -------------------------------------------------------------------------------- 1 | { 2 | "threshold": 30, 3 | "identifiers": true 4 | } 5 | -------------------------------------------------------------------------------- /northbrook.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": ["."], 3 | "plugins": ["eslint"], 4 | "eslint": { 5 | "formatter": "table" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - "6" 5 | - "5" 6 | - "4" 7 | env: 8 | - CXX=g++-4.8 9 | branches: 10 | only: 11 | - master 12 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint-config-standard", 3 | "parser": "babel-eslint", 4 | "plugins": ["flowtype", "import"], 5 | "rules": { 6 | "no-duplicate-imports": 0 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | .*/\.git/.* 3 | .*/coverage/.* 4 | .*/node_modules/findup/.* 5 | .*/node_modules/flow-remove-types/.* 6 | .*/node_modules/khaos/.* 7 | .*/node_modules/rollup-plugin-flow/.* 8 | 9 | [include] 10 | src/ 11 | examples/ 12 | 13 | [libs] 14 | 15 | [options] 16 | -------------------------------------------------------------------------------- /examples/rollup.config.js: -------------------------------------------------------------------------------- 1 | import buble from 'rollup-plugin-buble'; 2 | import resolve from 'rollup-plugin-node-resolve' 3 | import commonjs from 'rollup-plugin-commonjs' 4 | import flow from 'rollup-plugin-flow' 5 | 6 | export default { 7 | plugins: [ 8 | flow(), 9 | buble(), 10 | resolve(), 11 | commonjs({ 12 | include: 'node_modules/**', 13 | }) 14 | ], 15 | format: 'iife' 16 | }; 17 | -------------------------------------------------------------------------------- /examples/counter/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | Counter 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /examples/timer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | Timer 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/pair.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | // Turn a single value into a pair 4 | export function dup (a: A): [A, A] { 5 | return pair(a, a) 6 | } 7 | 8 | export function pair (a: A, b: B): [A, B] { 9 | return [a, b] 10 | } 11 | 12 | // swap the contents of a pair 13 | export function swap ([a, b]: [A, B]): [B, A] { 14 | return [b, a] 15 | } 16 | 17 | export function uncurry (f: (a: A, b: B) => C): (ab: [A, B]) => C { 18 | return ([a, b]) => f(a, b) 19 | } 20 | -------------------------------------------------------------------------------- /dist/arrow.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":null,"sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"} -------------------------------------------------------------------------------- /examples/mouse-position/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | Mouse Position 9 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/run.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import type { SignalGen, ForgetSG } from './input' 3 | import type { SignalFunc } from './signal' 4 | import type { Session } from './session' 5 | 6 | export function loop (session: Session, input: SignalGen, sf: SignalFunc]>): ForgetSG { 7 | let forget = input.listen(a => { 8 | const { sample, nextSession } = session.step() 9 | const { value: [_, nextInput], next } = sf.step(sample, a) // eslint-disable-line no-unused-vars 10 | forget = switchInput(nextSession, nextInput, next, forget) 11 | }) 12 | 13 | return forget 14 | } 15 | 16 | const switchInput = (session, input, sf, forget) => { 17 | forget.forget() 18 | return loop(session, input, sf) 19 | } 20 | -------------------------------------------------------------------------------- /src/test/show.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | const color = (set, reset = 39) => s => `\u001b[${set}m${s}\u001b[${reset}m` 4 | const cyan = color(36) 5 | const green = color(32) 6 | const dim = color(2, 22) 7 | 8 | const logStdout = (t, a, b) => { 9 | process.stdout.cursorTo(0) 10 | process.stdout.write(formatStep(t, a, b)) 11 | } 12 | 13 | const logConsole = (t, a, b) => 14 | console.log(t, a, b) 15 | 16 | const isNodeTTY = typeof process !== 'undefined' && typeof process.stdout !== 'undefined' && process.stdout.isTTY 17 | const log = isNodeTTY ? logStdout : logConsole 18 | 19 | export const logSignal = (t: any, a: any, b: any): boolean => 20 | !log(t, a, b) 21 | 22 | export const formatStep = (t: any, a: any, b: any): string => 23 | `${dim(t + ':')} ${cyan(JSON.stringify(a))} ${dim('->')} ${green(JSON.stringify(b))}` 24 | -------------------------------------------------------------------------------- /src/dom.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import type { SignalGen } from './signalgen' 3 | import { stepInput } from './signalgen' 4 | 5 | /* global EventTarget, Event, requestAnimationFrame, cancelAnimationFrame */ 6 | 7 | export type DomSignalGen = (name: string) => (node: EventTarget) => SignalGen 8 | 9 | const cancelDomEvent = ({ node, name, handler }) => node.removeEventListener(name, handler, false) 10 | 11 | export const fromDomEvent: DomSignalGen = (name) => (node) => 12 | stepInput(handler => { 13 | node.addEventListener(name, handler, false) 14 | return { node, name, handler } 15 | }, cancelDomEvent) 16 | 17 | export const click = fromDomEvent('click') 18 | export const mousemove = fromDomEvent('mousemove') 19 | export const keydown = fromDomEvent('keydown') 20 | 21 | export const animationFrame = stepInput(requestAnimationFrame, cancelAnimationFrame) 22 | -------------------------------------------------------------------------------- /src/vdom.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import type { SignalFunc } from './signal' 3 | import type { SignalGen } from './input' 4 | import { first } from './signal' 5 | import { scan } from './event' 6 | 7 | export { default as events } from 'snabbdom/modules/eventlisteners' 8 | export { default as attrs } from 'snabbdom/modules/attributes' 9 | export { default as props } from 'snabbdom/modules/props' 10 | export { default as clss } from 'snabbdom/modules/class' 11 | 12 | import snabbdom from 'snabbdom' 13 | import sh from 'snabbdom/h' 14 | import hh from 'hyperscript-helpers' 15 | 16 | export const init = (modules = []) => snabbdom.init(modules) 17 | 18 | export const html = hh(sh) 19 | 20 | export type PatchVTree = (orig: VTree, updated: VTree) => VTree 21 | 22 | export function vdomPatch (patch: PatchVTree, init: VTree): SignalFunc], [VTree, SignalGen]> { 23 | return first(scan(patch, init)) 24 | } 25 | -------------------------------------------------------------------------------- /examples/mouse-position/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import { pipe, always, split, unsplit, both } from '../../src/signal' 3 | import { hold, map } from '../../src/event' 4 | import { and } from '../../src/signalgen' 5 | import { countSession } from '../../src/session' 6 | import { loop } from '../../src/run' 7 | import { mousemove, keydown } from '../../src/dom' 8 | import { html, init, vdomPatch } from '../../src/vdom' 9 | const { span } = html 10 | 11 | const container = document.getElementById('app') 12 | const patch = init() 13 | 14 | const inputs = and(mousemove(document), keydown(document)) 15 | 16 | const render = (pos, key) => span(`${pos.clientX},${pos.clientY}:${key}`) 17 | const withInputs = always(inputs) 18 | 19 | const coords = hold('-,-') 20 | const keyCode = pipe(map(e => e.keyCode), hold('-')) 21 | const mouseAndKey = pipe(both(coords, keyCode), unsplit(render)) 22 | 23 | const update = vdomPatch(patch, patch(container, span('move the mouse and press some keys'))) 24 | 25 | loop(countSession(), inputs, pipe(split(mouseAndKey, withInputs), update)) 26 | -------------------------------------------------------------------------------- /examples/counter/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import { lift, pipe } from '../../src/signal' 3 | import { or, as, scan } from '../../src/event' 4 | import { signalGen, and } from '../../src/signalgen' 5 | import { clockSession } from '../../src/session' 6 | import { loop } from '../../src/run' 7 | import { html, init, events, vdomPatch } from '../../src/vdom' 8 | const { div, p, button } = html 9 | 10 | const container = document.getElementById('app') 11 | const patch = init([events]) 12 | 13 | const [inc, incInput] = signalGen() 14 | const [dec, decInput] = signalGen() 15 | 16 | const render = value => 17 | [div('#app', [ 18 | p(value), 19 | button({ on: { click: dec } }, '-'), 20 | button({ on: { click: inc } }, '+') 21 | ]), and(incInput, decInput)] 22 | 23 | const add = (a, b) => a + b 24 | const counter = pipe(or(as(1), as(-1)), scan(add, 0)) 25 | 26 | const [vtree, inputs] = render(0) 27 | const update = vdomPatch(patch, patch(container, vtree)); 28 | const runCounter = pipe(counter, lift(render), update) 29 | 30 | loop(clockSession(), inputs, runCounter) 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Tylor Steinberger 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/session.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | // A session provides a sample of state that will be fed into 4 | // a signal function when events occur 5 | export type Session = { 6 | step: () => SessionStep 7 | } 8 | 9 | export type SessionStep = { 10 | sample: A, 11 | nextSession: Session 12 | } 13 | 14 | // Session that yields an incrementing count at each step 15 | export const countSession = (): Session => new CountSession(1) 16 | 17 | class CountSession { 18 | count: number 19 | 20 | constructor (count: number) { 21 | this.count = count 22 | } 23 | 24 | step (): SessionStep { 25 | return { sample: this.count, nextSession: new CountSession(this.count + 1) } 26 | } 27 | } 28 | 29 | // Session that yields a time delta from its start time at each step 30 | export const clockSession = (): Session => new ClockSession(Date.now()) 31 | 32 | class ClockSession { 33 | start: number; 34 | 35 | constructor (start: number) { 36 | this.start = start 37 | } 38 | 39 | step (): SessionStep { 40 | return { sample: Date.now() - this.start, nextSession: new ClockSession(this.start) } 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /src/test/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import type { SignalFunc } from '../signal' 3 | import type { Session } from '../session' 4 | import { logSignal } from './show' 5 | 6 | export function assertSF (check: (t: T, a: A, b: B) => boolean, n: number, session: Session, genInput: (t: T) => A, sf: SignalFunc): Promise { 7 | return stepAssertSF(check, n, session, genInput, sf) 8 | } 9 | 10 | export function runSF (session: Session, genInput: (t: T) => A, sf: SignalFunc): Promise { 11 | return stepAssertSF(logSignal, Infinity, session, genInput, sf) 12 | } 13 | 14 | const stepAssertSF = (check, n, session, f, sf) => { 15 | if(n <= 0) { 16 | return Promise.resolve() 17 | } 18 | const { sample, nextSession } = session.step() 19 | return Promise.resolve(sample).then(t => 20 | stepAndCheck(check, n, nextSession, f, sf, t, f(t))) 21 | } 22 | 23 | const stepAndCheck = (check, n, nextSession, f, sf, t, a) => { 24 | const { value: b, next } = sf.step(t, a) 25 | return check(t, a, b) 26 | ? stepAssertSF(check, n - 1, nextSession, f, next) 27 | : Promise.reject(new Error(`assertion failed: ${t} ${a} ${b}`)) 28 | } 29 | 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Arrow 2 | 3 | Push-pull Signal Function FRP. 4 | 5 | ## Goals 6 | 7 | - Learn about Signal Functions, Signal Vectors, Arrows, and Profunctors 8 | - See if Signal-Function / Arrowized FRP is a viable approach to interactive apps in JavaScript 9 | 10 | ## Design goals 11 | 12 | - Implement sound FRP semantics with a pure foundation (as far as possible in JS) 13 | - Avoid bugs, race conditions, and glitches 14 | - Push impurity and IO to the edges 15 | - Balance JavaScript's event-driven nature with the simplicity and predictability of programming with continuous values and total functions 16 | - Use a push-pull approach instead of either pull-only, or push-only 17 | - Favor composition as a base programming API 18 | - Provide a core set of operators with rigorous semantics as building blocks 19 | - Assemble higher level operations by composing existing ones 20 | 21 | ## References 22 | 23 | - [Edward Amsden, __Time Files: Push-Pull Signal-Function Functional Reactive Programming__](https://github.com/eamsden/pushbasedFRP/blob/master/Docs/Thesis/thesis.pdf) 24 | - [Github repo](https://github.com/eamsden/pushbasedFRP) 25 | - [Netwire](https://hackage.haskell.org/package/netwire) 26 | - [FRP.Reactive](http://hackage.haskell.org/package/reactive-0.11.5) 27 | -------------------------------------------------------------------------------- /examples/timer/index.js: -------------------------------------------------------------------------------- 1 | import { pipe, both, unsplit, time } from '../../src/signal' 2 | import { hold, eventTime } from '../../src/event' 3 | import { signalGen, and, delay } from '../../src/signalgen' 4 | import { clockSession } from '../../src/session' 5 | import { loop } from '../../src/run' 6 | import { html, init, events, vdomPatch } from '../../src/vdom' 7 | const { div, p, button } = html 8 | 9 | const container = document.getElementById('app') 10 | const patch = init([events]) 11 | 12 | // Counter component 13 | const render = (start, now) => { 14 | const elapsed = now - start 15 | const [click, reset] = signalGen() 16 | return [ 17 | div([ 18 | p(`Seconds passed: ${Math.floor(elapsed * .001)}`), 19 | button({ on: { click } }, `Reset`) 20 | ]), 21 | and(reset, delay(1000 - (elapsed % 1000))) // account for setTimeout drift 22 | ] 23 | } 24 | 25 | const reset = pipe(eventTime, hold(0)) 26 | const counter = pipe(both(reset, time), unsplit(render)) 27 | 28 | // Render initial UI and get initial inputs 29 | const [vtree, input] = render(0, 0) 30 | 31 | // Append vdom updater to counter component 32 | const update = vdomPatch(patch, patch(container, vtree)) 33 | const updateCounter = pipe(counter, update) 34 | 35 | // Run it 36 | loop(clockSession(), input, updateCounter) 37 | -------------------------------------------------------------------------------- /examples/stopwatch/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | Stopwatch 9 | 76 | 77 | 78 |
79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@arrow/core", 3 | "version": "0.0.1", 4 | "description": "Push-pull Signal Function FRP", 5 | "main": "dist/arrow.js", 6 | "jsnext:main": "src/index.js", 7 | "module": "src/index.js", 8 | "files": [ 9 | "src/*", 10 | "dist/arrow.js" 11 | ], 12 | "scripts": { 13 | "build:examples": "npm run build:counter && npm run build:mouse-position && npm run build:stopwatch && npm run build:timer", 14 | "build:counter": "rollup -c examples/rollup.config.js -o examples/counter/app.js examples/counter/index.js", 15 | "build:mouse-position": "rollup -c examples/rollup.config.js -o examples/mouse-position/app.js examples/mouse-position/index.js", 16 | "build:stopwatch": "rollup -c examples/rollup.config.js -o examples/stopwatch/app.js examples/stopwatch/index.js", 17 | "build:temperature": "rollup -c examples/rollup.config.js -o examples/temperature/app.js examples/temperature/index.js", 18 | "build:timer": "rollup -c examples/rollup.config.js -o examples/timer/app.js examples/timer/index.js", 19 | "test:unit": "istanbul cover _mocha -- -r buba/register", 20 | "test:typecheck": "flow", 21 | "test:lint": "jsinspect src && nb eslint", 22 | "test": "npm run test:lint && npm run test:typecheck && npm run test:unit", 23 | "commit": "nb commit", 24 | "release": "nb release" 25 | }, 26 | "repository": { 27 | "type": "git", 28 | "url": "git+https://github.com/briancavalier/arrow.git" 29 | }, 30 | "keywords": [ 31 | "reactive", 32 | "event" 33 | ], 34 | "author": "Brian Cavalier (github.com/briancavalier)", 35 | "license": "MIT", 36 | "bugs": { 37 | "url": "https://github.com/briancavalier/arrow/issues" 38 | }, 39 | "homepage": "https://github.com/briancavalier/arrow#readme", 40 | "devDependencies": { 41 | "@northbrook/commit-types": "^1.1.0", 42 | "assert": "^1.4.1", 43 | "babel-eslint": "^6.1.2", 44 | "buba": "^2.0.3", 45 | "eslint": "^3.4.0", 46 | "eslint-config-standard": "^6.0.0", 47 | "eslint-plugin-flowtype": "^2.11.4", 48 | "eslint-plugin-import": "^1.14.0", 49 | "eslint-plugin-promise": "^2.0.1", 50 | "eslint-plugin-standard": "^2.0.0", 51 | "flow-bin": "^0.32.0", 52 | "ghooks": "^1.3.2", 53 | "istanbul": "^1.1.0-alpha.1", 54 | "jsinspect": "^0.8.0", 55 | "mocha": "^3.0.2", 56 | "northbrook": "^2.3.0", 57 | "northbrook-eslint": "^1.1.0", 58 | "rollup": "^0.34.11", 59 | "rollup-plugin-buble": "^0.12.1", 60 | "rollup-plugin-commonjs": "^3.3.1", 61 | "rollup-plugin-flow": "^1.0.1", 62 | "rollup-plugin-node-resolve": "^2.0.0", 63 | "uglify-js": "^2.7.1", 64 | "validate-commit-msg": "^2.8.0" 65 | }, 66 | "config": { 67 | "ghooks": { 68 | "commit-msg": "node ./node_modules/.bin/validate-commit-msg" 69 | }, 70 | "validate-commit-msg": { 71 | "types": "@northbrook/commit-types" 72 | } 73 | }, 74 | "dependencies": { 75 | "hyperscript-helpers": "^3.0.1", 76 | "snabbdom": "^0.5.3" 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/signalgen.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import type { Evt } from './event' 3 | import { NoEvent } from './event' 4 | 5 | // A SignalGen generates values or events of a signal 6 | export type SignalGen
= { 7 | listen: (handler: HandleInput) => ForgetSG 8 | } 9 | 10 | // Forget all future values of a SignalGen 11 | export type ForgetSG = { 12 | forget: () => void 13 | } 14 | 15 | // Handle events from a SignalGen 16 | export type HandleInput = (a: A) => any 17 | 18 | // Turn a pair of inputs into an input of pairs 19 | export function and (input1: SignalGen, input2: SignalGen): SignalGen<[Evt, Evt]> { 20 | return new SGVector(input1, input2) 21 | } 22 | 23 | export function delay (ms: number, a: A): SignalGen> { 24 | return stepInput((f) => setTimeout(f, ms, a), clearTimeout) 25 | } 26 | 27 | export const never: SignalGen = { 28 | listen () { return forgetNever } 29 | } 30 | 31 | const forgetNever: ForgetSG = { 32 | forget () {} 33 | } 34 | 35 | export type Occur = (a: A) => void 36 | 37 | export function signalGen (): [Occur, SignalGen] { 38 | const source = new SGSource() 39 | return [(x) => source.handler(x), source] 40 | } 41 | 42 | const noop = () => {} 43 | 44 | class SGSource { 45 | handler: HandleInput 46 | 47 | constructor () { 48 | this.handler = noop 49 | } 50 | 51 | listen (handler: HandleInput): ForgetSG { 52 | this.handler = handler 53 | return new ForgetSGSource(this) 54 | } 55 | } 56 | 57 | class ForgetSGSource { 58 | source: SGSource 59 | 60 | constructor (source: SGSource) { 61 | this.source = source 62 | } 63 | 64 | forget (): void { 65 | this.source.handler = noop 66 | } 67 | } 68 | 69 | const emptySignalVector = [NoEvent, NoEvent] 70 | 71 | const empty = input => input instanceof SGVector ? emptySignalVector : NoEvent 72 | 73 | class SGVector { 74 | first: SignalGen 75 | second: SignalGen 76 | 77 | constructor (first: SignalGen, second: SignalGen) { 78 | this.first = first 79 | this.second = second 80 | } 81 | 82 | listen (handler: HandleInput<[Evt, Evt]>): ForgetSG { 83 | const forgetFirst = this.first.listen(a => handler([a, empty(this.second)])) 84 | const forgetSecond = this.second.listen(a => handler([empty(this.first), a])) 85 | return new ForgetSGVector(forgetFirst, forgetSecond) 86 | } 87 | } 88 | 89 | class ForgetSGVector { 90 | forgetFirst: ForgetSG 91 | forgetSecond: ForgetSG 92 | 93 | constructor (forgetFirst: ForgetSG, forgetSecond: ForgetSG) { 94 | this.forgetFirst = forgetFirst 95 | this.forgetSecond = forgetSecond 96 | } 97 | 98 | forget (): void { 99 | this.forgetFirst.forget() 100 | this.forgetSecond.forget() 101 | } 102 | } 103 | 104 | export function stepInput (set: (f: HandleInput) => C, forget: (c: C) => any): SignalGen { 105 | return new PushSG(set, forget) 106 | } 107 | 108 | class PushSG { 109 | set: (f: HandleInput) => C 110 | forget: (c: C) => any 111 | 112 | constructor(set: (f: HandleInput) => C, forget: (c: C) => any) { 113 | this.set = set 114 | this.forget = forget 115 | } 116 | 117 | listen (f) { 118 | return new ForgetPushSG(this.forget, this.set.call(undefined, f)) 119 | } 120 | } 121 | 122 | class ForgetPushSG { 123 | _forget: (c: C) => any 124 | context: C 125 | 126 | constructor (forget: (c: C) => any, context: C) { 127 | this._forget = forget 128 | this.context = context 129 | } 130 | 131 | forget (): void { 132 | this._forget.call(undefined, this.context) 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /examples/stopwatch/index.js: -------------------------------------------------------------------------------- 1 | import { time, both, unsplit, pipe } from '../../src/signal' 2 | import { map, or, eventTime, accum } from '../../src/event' 3 | import { and, signalGen, never} from '../../src/signalgen' 4 | import { clockSession } from '../../src/session' 5 | import { loop } from '../../src/run' 6 | import { animationFrame } from '../../src/dom' 7 | import { html, init, events, attrs, clss, vdomPatch } from '../../src/vdom' 8 | const { div, span, ol, li, button } = html 9 | 10 | // TODO: combining many inputs and signals. Need a better way 11 | const anyInput = (...inputs) => inputs.reduce(and) 12 | const anySignal = (...signals) => signals.reduce(or) 13 | 14 | const container = document.getElementById('app') 15 | const patch = init([events, attrs, clss]) 16 | 17 | const [start, startInput] = signalGen() 18 | const [stop, stopInput] = signalGen() 19 | const [reset, resetInput] = signalGen() 20 | const [lap, lapInput] = signalGen() 21 | 22 | const timerInputs = anyInput(startInput, stopInput, resetInput, lapInput) 23 | const stoppedInputs = anyInput(timerInputs, never) 24 | const runningInputs = anyInput(timerInputs, animationFrame) 25 | 26 | // Render timer using current time 27 | // Returns [inputs, vtree] 28 | const render = (timer, time) => { 29 | const elapsed = timerElapsed(time, timer) 30 | const zero = elapsed === 0 31 | const vtree = div('.timer', { class: { running: timer.running, zero } }, [ 32 | div('.elapsed', renderDuration(elapsed)), 33 | div('.lap-elapsed', renderDuration(timerCurrentLap(time, timer))), 34 | button('.reset', { on: { click: reset }, attrs: { disabled: timer.running || zero } }, 'Reset'), 35 | button('.start', { on: { click: start } }, 'Start'), 36 | button('.stop', { on: { click: stop } }, 'Stop'), 37 | button('.lap', { on: { click: lap }, attrs: { disabled: !timer.running } }, 'Lap'), 38 | ol('.laps', { attrs: { reversed: true } }, timer.laps.map(({ start, end }) => 39 | li(renderDuration(end - start))) 40 | ) 41 | ]) 42 | 43 | return [vtree, timer.running ? runningInputs : stoppedInputs] 44 | } 45 | 46 | // Timer formatting 47 | const renderDuration = ms => [ 48 | span('.minutes', `${mins(ms)}`), 49 | span('.seconds', `${secs(ms)}`), 50 | span('.hundredths', `${hundredths(ms)}`) 51 | ] 52 | 53 | const mins = ms => pad((ms / (1000 * 60)) % 60) 54 | const secs = ms => pad((ms / 1000) % 60) 55 | const hundredths = ms => pad((ms / 10) % 100) 56 | const pad = n => n < 10 ? `0${Math.floor(n)}` : `${Math.floor(n)}` 57 | 58 | // Timer functions 59 | const timerZero = ({ running: false, origin: 0, total: 0, laps: [] }) 60 | const timerReset = time => (_) => ({ running: false, origin: time, total: 0, laps: [] }) 61 | 62 | const timerStart = time => ({ total, laps }) => 63 | ({ running: true, origin: time, total, laps }) 64 | const timerStop = time => ({ origin, total, laps }) => 65 | ({ running: false, origin: time, total: timerTotal(origin, total, time), laps }) 66 | const timerLap = time => ({ running, origin, total, laps }) => 67 | ({ running, origin, total, laps: timerAddLap(timerTotal(origin, total, time), laps) }) 68 | 69 | const timerAddLap = (end, laps) => [{ start: timerLastLapEnd(laps), end }].concat(laps) 70 | const timerLastLapEnd = laps => laps.length === 0 ? 0 : laps[0].end 71 | const timerCurrentLap = (time, { running, origin, total, laps }) => timerTotal(origin, total, time) - timerLastLapEnd(laps) 72 | const timerElapsed = (time, { origin, total }) => timerTotal(origin, total, time) 73 | const timerTotal = (origin, total, time) => total + (time - origin) 74 | 75 | // Timer events, each tagged with its occurrence time 76 | const doStart = pipe(eventTime, map(timerStart)) 77 | const doStop = pipe(eventTime, map(timerStop)) 78 | const doReset = pipe(eventTime, map(timerReset)) 79 | const doLap = pipe(eventTime, map(timerLap)) 80 | 81 | // An interactive timer that responds to start, stop, reset, and lap events 82 | // by changing (i.e. accumulating) state 83 | const timer = pipe(anySignal(doStart, doStop, doReset, doLap), accum(timerZero)) 84 | 85 | // Pair an interactive timer, with the (continuous) current time 86 | const runTimer = both(timer, time) 87 | const displayTimer = unsplit(render) 88 | 89 | const [vtree, inputs] = render(timerZero, 0) 90 | const updateTimer = pipe(runTimer, displayTimer, vdomPatch(patch, patch(container, vtree))) 91 | 92 | loop(clockSession(), inputs, updateTimer) 93 | -------------------------------------------------------------------------------- /src/event.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import type { SFTime, StepTime, Time } from './signal' 3 | import { both, pipe, lift, unsplit } from './signal' 4 | import { pair } from './pair' 5 | 6 | // An event, which has a value when it occurs, and 7 | // has no value when it doesn't occur 8 | export type Evt = A | void 9 | 10 | // Event non-occurrence 11 | export const NoEvent = undefined 12 | 13 | // Turn Events of A instead Events of B 14 | function mapE (f: (a: A) => B): (a: Evt) => Evt { 15 | return a => a === undefined ? a : f(a) 16 | } 17 | 18 | // Return the Event that occurred, preferring a1 if both occurred 19 | function mergeE (a1: Evt, a2: Evt): Evt { 20 | return a1 === undefined ? a2 : a1 21 | } 22 | 23 | // Internal helper to allow continuous value transformations to be 24 | // applied when an event occurs 25 | // TODO: Consider exposing this if it seems useful 26 | function liftE (ab: SFTime): SFTime, Evt> { 27 | return new LiftE(ab) 28 | } 29 | 30 | class LiftE { 31 | ab: SFTime 32 | 33 | constructor (ab) { 34 | this.ab = ab 35 | } 36 | 37 | step (t: Time, a: Evt): StepTime, Evt> { 38 | if (a === undefined) { 39 | return { value: NoEvent, next: this } 40 | } 41 | const { value, next } = this.ab.step(t, a) 42 | return { value, next: liftE(next) } 43 | } 44 | } 45 | 46 | // Sample the current time when an event occurs 47 | export const eventTime: SFTime, Evt, B], Evt> { 66 | return lift(([a, b]) => a === undefined ? NoEvent : f(a, b)) 67 | } 68 | 69 | export function sample (): SFTime<[Evt, B], Evt<[A, B]>> { 70 | return sampleWith(pair) 71 | } 72 | 73 | // Merge events, preferring the left in the case of 74 | // simultaneous occurrence 75 | export function merge (): SFTime<[Evt, Evt], Evt> { 76 | return unsplit(mergeE) 77 | } 78 | 79 | // Merge event SignalFuncs 80 | export function or (left: SFTime, Evt>, right: SFTime, Evt>): SFTime, Evt> { 81 | return liftE(pipe(both(left, right), merge())) 82 | } 83 | 84 | // Turn an event into a stepped continuous value 85 | export function hold (initial: A): SFTime, A> { 86 | return new Hold(initial) 87 | } 88 | 89 | class Hold { 90 | value: A 91 | 92 | constructor (value: A) { 93 | this.value = value 94 | } 95 | 96 | step (t: Time, a: A): StepTime, A> { 97 | return a === undefined 98 | ? { value: this.value, next: this } 99 | : { value: a, next: hold(a) } 100 | } 101 | } 102 | 103 | // Accumulate event 104 | export function scanE (f: (b: B, a: A) => B, initial: B): SFTime, Evt> { 105 | return new Accum(f, initial) 106 | } 107 | 108 | // Accumulate event to a continuous value 109 | export function scan (f: (b: B, a: A) => B, initial: B): SFTime, B> { 110 | return pipe(scanE(f, initial), hold(initial)) 111 | } 112 | 113 | // Accumulate event, given an initial value and a update-function event 114 | export function accumE (initial: A): SFTime A>, Evt> { 115 | return scanE((a, f) => f(a), initial) 116 | } 117 | 118 | // Accumulate event to a continuous value, given an initial value and a update-function event 119 | export function accum (initial: A): SFTime A>, A> { 120 | return pipe(accumE(initial), hold(initial)) 121 | } 122 | 123 | class Accum { 124 | f: (b: B, a: A) => B 125 | value: B 126 | 127 | constructor (f: (b: B, a: A) => B, value: B) { 128 | this.f = f 129 | this.value = value 130 | } 131 | 132 | step (t: Time, a: A): StepTime, B> { 133 | if (a === undefined) { 134 | return { value: NoEvent, next: this } 135 | } 136 | const f = this.f 137 | const value = f(this.value, a) 138 | return { value, next: new Accum(f, value) } 139 | } 140 | } 141 | 142 | -------------------------------------------------------------------------------- /dist/arrow.min.js: -------------------------------------------------------------------------------- 1 | (function(global,factory){typeof exports==="object"&&typeof module!=="undefined"?factory(exports):typeof define==="function"&&define.amd?define(["exports"],factory):factory(global.arrow=global.arrow||{})})(this,function(exports){"use strict";function dup(a){return pair(a,a)}function pair(a,b){return[a,b]}function swap(ref){var a=ref[0];var b=ref[1];return[b,a]}function uncurry(f){return function(ref){var a=ref[0];var b=ref[1];return f(a,b)}}var step=function(value,next){return{value:value,next:next}};var time={step:function(value,_){return{value:value,next:time}}};function lift(f){return new Lift(f)}function unsplit(f){return lift(uncurry(f))}function always(a){return lift(constant(a))}function identity(a){return a}function constant(a){return function(_){return a}}var Lift=function Lift(f){this.f=f};Lift.prototype.step=function step$1(t,a){var f=this.f;return step(f(a),this)};function id(){return lift(identity)}function first(ab){return new First(ab)}function second(ab){return dimap(swap,swap,first(ab))}var First=function First(ab){this.ab=ab};First.prototype.step=function step$2(t,ref){var a=ref[0];var c=ref[1];var ref$1=this.ab.step(t,a);var b=ref$1.value;var next=ref$1.next;return step([b,c],first(next))};function unfirst(ab,c){return new Unfirst(ab,c)}var Unfirst=function Unfirst(ab,c){this.ab=ab;this.value=c};Unfirst.prototype.step=function step$3(t,a){var ref=this.ab.step(t,[a,this.value]);var ref_value=ref.value;var b=ref_value[0];var c=ref_value[1];var next=ref.next;return step(b,unfirst(next,c))};function pipe(ab){var rest=[],len=arguments.length-1;while(len-- >0)rest[len]=arguments[len+1];return rest.reduce(pipe2,ab)}function pipe2(ab,bc){return new Pipe(ab,bc)}function dimap(fab,fcd,bc){return pipe2(pipe2(lift(fab),bc),lift(fcd))}function lmap(fab,bc){return pipe2(lift(fab),bc)}function rmap(fbc,ab){return pipe2(ab,lift(fbc))}var Pipe=function Pipe(ab,bc){this.ab=ab;this.bc=bc};Pipe.prototype.step=function step$4(t,a){var ref=this.ab.step(t,a);var b=ref.value;var ab=ref.next;var ref$1=this.bc.step(t,b);var c=ref$1.value;var bc=ref$1.next;return step(c,pipe2(ab,bc))};function split(ab,ac){return lmap(dup,both(ab,ac))}function both(ab,cd){return new Both(ab,cd)}var Both=function Both(ab,cd){this.ab=ab;this.cd=cd};Both.prototype.step=function step$5(t,ref){var a=ref[0];var c=ref[1];var ref$1=this.ab.step(t,a);var b=ref$1.value;var anext=ref$1.next;var ref$2=this.cd.step(t,c);var d=ref$2.value;var cnext=ref$2.next;return step([b,d],both(anext,cnext))};var NoEvent=undefined;function map(f){return function(a){return a===undefined?a:f(a)}}function mergeE(a1,a2){return a1===undefined?a2:a1}function liftE(ab){return new LiftE(ab)}var LiftE=function LiftE(ab){this.ab=ab};LiftE.prototype.step=function step(t,a){if(a===undefined){return{value:NoEvent,next:this}}var ref=this.ab.step(t,a);var value=ref.value;var next=ref.next;return{value:value,next:liftE(next)}};var eventTime={step:function step(t,a){return{value:a===undefined?NoEvent:t,next:this}}};function mapE(f){return lift(map(f))}function as(b){return mapE(function(_){return b})}function sampleWith(f){return lift(function(ref){var a=ref[0];var b=ref[1];return a===undefined?NoEvent:f(a,b)})}function sample(){return sampleWith(pair)}function merge(){return unsplit(mergeE)}function or(left,right){return liftE(pipe(both(left,right),merge()))}function hold(initial){return new Hold(initial)}var Hold=function Hold(value){this.value=value};Hold.prototype.step=function step(t,a){return a===undefined?{value:this.value,next:this}:{value:a,next:hold(a)}};function scanE(f,initial){return new Accum(f,initial)}function scan(f,initial){return pipe(scanE(f,initial),hold(initial))}function accumE(initial){return scanE(function(a,f){return f(a)},initial)}function accum(initial){return pipe(accumE(initial),hold(initial))}var Accum=function Accum(f,value){this.f=f;this.value=value};Accum.prototype.step=function step(t,a){if(a===undefined){return{value:NoEvent,next:this}}var f=this.f;var value=f(this.value,a);return{value:value,next:new Accum(f,value)}};function both$1(input1,input2){return function(f){var dispose1=input1(function(a1){return f([a1,NoEvent])});var dispose2=input2(function(a2){return f([NoEvent,a2])});return function(){return[dispose1(),dispose2()]}}}var never=function(){return noop};var noop=function(){};function newInput(){var _occur;var occur=function(x){if(typeof _occur==="function"){_occur(x)}};var input=function(f){_occur=f;return function(){_occur=undefined}};return[occur,input]}function loop(session,input,sf){var dispose=input(function(a){var ref=session.step();var sample=ref.sample;var nextSession=ref.nextSession;var ref$1=sf.step(sample,a);var ref$1_value=ref$1.value;var _=ref$1_value[0];var nextInput=ref$1_value[1];var next=ref$1.next;dispose=switchInput(nextSession,nextInput,next,dispose)});return dispose}var switchInput=function(session,input,sf,dispose){dispose();return loop(session,input,sf)};function newSession(step,init){return new SteppedSession(step,init)}var countSession=function(){return newSession(function(n){return n+1},0)};var SteppedSession=function SteppedSession(step,value){this._step=step;this.value=value};SteppedSession.prototype.step=function step(){var sample=this._step(this.value);return{sample:sample,nextSession:newSession(this._step,sample)}};var clockSession=function(){return new ClockSession(Date.now())};var ClockSession=function ClockSession(start){this.start=start;this.time=Infinity};ClockSession.prototype.step=function step(){var t=Date.now();if(t = { 10 | step: (t: T, a: A) => SignalStep 11 | } 12 | 13 | // A Step is the result of applying a SignalFunc 14 | // to an A to get a B and a new SignalFunc 15 | export type SignalStep = { 16 | value: B, 17 | next: SignalFunc 18 | } 19 | 20 | // SignalFunc specialized for Time type 21 | // Note: Flow can't infer generics, IOW, it can't info the 22 | // type T *later* based on the Session type provided when running 23 | // a SignalFunc. Flow needs to be able to determine T at the 24 | // instant a SignalFunc is created, but the type is only known 25 | // later when a Session is used to run the SignalFunc 26 | export type SFTime = SignalFunc 27 | 28 | // SignalStep specialized for Time type 29 | // re: Flow, similarly 30 | export type StepTime = SignalStep 31 | 32 | // Simple helper to construct a Step 33 | const step = (value, next) => ({ value, next }) 34 | 35 | export const time: SFTime = 36 | { step: (value, _) => ({ value, next: time }) } 37 | 38 | // Lift a function into a SignalFunc 39 | export function lift (f: (a: A) => B): SFTime { 40 | return new Lift(f) 41 | } 42 | 43 | // Combine a pair of signals into a signal of C 44 | export function unsplit (f: (a: A, b: B) => C): SFTime<[A, B], C> { 45 | return lift(uncurry(f)) 46 | } 47 | 48 | // SignalFunc that runs any signal into a signal whose 49 | // value is always a 50 | // TODO: Give this its own type so it can be composed efficiently 51 | export function always (a: A): SFTime { 52 | return lift(constant(a)) 53 | } 54 | 55 | function identity (a: A): A { 56 | return a 57 | } 58 | 59 | function constant (a: A): (b?: any) => A { 60 | return (_) => a 61 | } 62 | 63 | class Lift { 64 | f: (a: A) => B 65 | 66 | constructor (f: (a: A) => B) { 67 | this.f = f 68 | } 69 | 70 | step (t: Time, a: A): StepTime { 71 | const f = this.f 72 | return step(f(a), this) 73 | } 74 | } 75 | 76 | // id :: SFTime a a 77 | // Reactive transformation that yields its input at each step 78 | // TODO: Give this its own type so it can be composed efficiently 79 | export function id (): SFTime { 80 | return lift(identity) 81 | } 82 | 83 | // first :: SFTime a b -> SFTime [a, c] [b, c] 84 | // Apply a SignalFunc to the first signal of a pair 85 | export function first (ab: SFTime): SFTime<[A, C], [B, C]> { 86 | return new First(ab) 87 | } 88 | 89 | // second :: SFTime a b -> SFTime [c, a] [c, b] 90 | export function second (ab: SFTime): SFTime<[C, A], [C, B]> { 91 | return promap(swap, swap, first(ab)) 92 | } 93 | 94 | class First { 95 | ab: SFTime 96 | 97 | constructor (ab: SFTime) { 98 | this.ab = ab 99 | } 100 | 101 | step (t: Time, [a, c]: [A, C]): StepTime<[A, C], [B, C]> { 102 | const { value: b, next } = this.ab.step(t, a) 103 | return step([b, c], first(next)) 104 | } 105 | } 106 | 107 | // unfirst :: c -> Reactive [a, c] [b, c] -> Reactive a b 108 | // unsecond :: c -> Reactive [c, a] [c, b] -> Reactive a b 109 | // Tie a Reactive into a loop that feeds c back into itself 110 | export function unfirst (ab: SFTime<[A, C], [B, C]>, c: C): SFTime { 111 | return new Unfirst(ab, c) 112 | } 113 | // export const unsecond = (arrow, c) => unfirst(dimap(swap, swap, arrow), c) 114 | 115 | class Unfirst { 116 | ab: SFTime<[A, C], [B, C]> 117 | value: C 118 | 119 | constructor (ab: SFTime<[A, C], [B, C]>, c: C) { 120 | this.ab = ab 121 | this.value = c 122 | } 123 | 124 | step (t: Time, a: A): StepTime { 125 | const { value: [b, c], next } = this.ab.step(t, [a, this.value]) 126 | return step(b, unfirst(next, c)) 127 | } 128 | } 129 | 130 | // pipe :: (SFTime a b ... SFTime y z) -> SFTime a z 131 | // Compose many Reactive transformations, left to right 132 | export function pipe (ab: SFTime, ...rest: Array>): SFTime { 133 | return rest.reduce(pipe2, ab) 134 | } 135 | 136 | // pipe2 :: SFTime a b -> SFTime b c -> SFTime a c 137 | // Compose 2 Reactive transformations left to right 138 | export function pipe2 (ab: SFTime, bc: SFTime): SFTime { 139 | return new Pipe(ab, bc) 140 | } 141 | 142 | export function promap (fab: (a: A) => B, fcd: (c: C) => D, bc: SFTime): SFTime { 143 | return pipe2(pipe2(lift(fab), bc), lift(fcd)) 144 | } 145 | 146 | export function lmap (fab: (a: A) => B, bc: SFTime): SFTime { 147 | return pipe2(lift(fab), bc) 148 | } 149 | 150 | export function rmap (fbc: (b: B) => C, ab: SFTime): SFTime { 151 | return pipe2(ab, lift(fbc)) 152 | } 153 | 154 | class Pipe { 155 | ab: SFTime 156 | bc: SFTime 157 | 158 | constructor (ab: SFTime, bc: SFTime) { 159 | this.ab = ab 160 | this.bc = bc 161 | } 162 | 163 | step (t: Time, a: A): StepTime { 164 | const { value: b, next: ab } = this.ab.step(t, a) 165 | const { value: c, next: bc } = this.bc.step(t, b) 166 | return step(c, pipe2(ab, bc)) 167 | } 168 | } 169 | 170 | // split :: SFTime a b -> SFTime a c -> SFTime [b, c] 171 | // Duplicates input a and pass it through Reactive transformations 172 | // ab and ac to yield [b, c] 173 | export function split (ab: SFTime, ac: SFTime): SFTime { 174 | return lmap(dup, both(ab, ac)) 175 | } 176 | 177 | // both :: SFTime a b -> SFTime c d -> Reactive [a, c] [b, d] 178 | // Given an [a, c] input, pass a through Reactive transformation ab and 179 | // c through Reactive transformation cd to yield [b, d] 180 | export function both (ab: SFTime, cd: SFTime): SFTime<[A, C], [B, D]> { 181 | return new Both(ab, cd) 182 | } 183 | 184 | class Both { 185 | ab: SFTime 186 | cd: SFTime 187 | 188 | constructor (ab: SFTime, cd: SFTime) { 189 | this.ab = ab 190 | this.cd = cd 191 | } 192 | 193 | step (t: Time, [a, c]: [A, C]): StepTime<[A, C], [B, D]> { 194 | const { value: b, next: anext } = this.ab.step(t, a) 195 | const { value: d, next: cnext } = this.cd.step(t, c) 196 | return step([b, d], both(anext, cnext)) 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /dist/arrow.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : 3 | typeof define === 'function' && define.amd ? define(['exports'], factory) : 4 | (factory((global.arrow = global.arrow || {}))); 5 | }(this, (function (exports) { 'use strict'; 6 | 7 | // 8 | 9 | // Turn a single value into a pair 10 | function dup (a ) { 11 | return pair(a, a) 12 | } 13 | 14 | function pair (a , b ) { 15 | return [a, b] 16 | } 17 | 18 | // swap the contents of a pair 19 | function swap (ref ) { 20 | var a = ref[0]; 21 | var b = ref[1]; 22 | 23 | return [b, a] 24 | } 25 | 26 | function uncurry (f ) { 27 | return function (ref) { 28 | var a = ref[0]; 29 | var b = ref[1]; 30 | 31 | return f(a, b); 32 | } 33 | } 34 | 35 | // 36 | // Signal Function is a time varying transformation that 37 | // turns Signals of A into Signals of B. It may carry state 38 | // and evolve over time 39 | 40 | 41 | 42 | 43 | // A Step is the result of applying a SignalFunc 44 | // to an A to get a B and a new SignalFunc 45 | 46 | 47 | 48 | 49 | 50 | // SignalFunc specialized for Time type 51 | // Note: Flow can't infer generics, IOW, it can't info the 52 | // type T *later* based on the Session type provided when running 53 | // a SignalFunc. Flow needs to be able to determine T at the 54 | // instant a SignalFunc is created, but the type is only known 55 | // later when a Session is used to run the SignalFunc 56 | 57 | 58 | // SignalStep specialized for Time type 59 | // re: Flow, similarly 60 | 61 | 62 | // Simple helper to construct a Step 63 | var step = function (value, next) { return ({ value: value, next: next }); } 64 | 65 | var time = 66 | { step: function (value, _) { return ({ value: value, next: time }); } } 67 | 68 | // Lift a function into a SignalFunc 69 | function lift (f ) { 70 | return new Lift(f) 71 | } 72 | 73 | // Combine a pair of signals into a signal of C 74 | function unsplit (f ) { 75 | return lift(uncurry(f)) 76 | } 77 | 78 | // SignalFunc that runs any signal into a signal whose 79 | // value is always a 80 | // TODO: Give this its own type so it can be composed efficiently 81 | function always (a ) { 82 | return lift(constant(a)) 83 | } 84 | 85 | function identity (a ) { 86 | return a 87 | } 88 | 89 | function constant (a ) { 90 | return function (_) { return a; } 91 | } 92 | 93 | var Lift = function Lift (f ) { 94 | this.f = f 95 | }; 96 | 97 | Lift.prototype.step = function step$1 (t , a ) { 98 | var f = this.f 99 | return step(f(a), this) 100 | }; 101 | 102 | // id :: SFTime a a 103 | // Reactive transformation that yields its input at each step 104 | // TODO: Give this its own type so it can be composed efficiently 105 | function id () { 106 | return lift(identity) 107 | } 108 | 109 | // first :: SFTime a b -> SFTime [a, c] [b, c] 110 | // Apply a SignalFunc to the first signal of a pair 111 | function first (ab ) { 112 | return new First(ab) 113 | } 114 | 115 | // second :: SFTime a b -> SFTime [c, a] [c, b] 116 | function second (ab ) { 117 | return dimap(swap, swap, first(ab)) 118 | } 119 | 120 | var First = function First (ab ) { 121 | this.ab = ab 122 | }; 123 | 124 | First.prototype.step = function step$2 (t , ref ) { 125 | var a = ref[0]; 126 | var c = ref[1]; 127 | 128 | var ref$1 = this.ab.step(t, a); 129 | var b = ref$1.value; 130 | var next = ref$1.next; 131 | return step([b, c], first(next)) 132 | }; 133 | 134 | // unfirst :: c -> Reactive [a, c] [b, c] -> Reactive a b 135 | // unsecond :: c -> Reactive [c, a] [c, b] -> Reactive a b 136 | // Tie a Reactive into a loop that feeds c back into itself 137 | function unfirst (ab , c ) { 138 | return new Unfirst(ab, c) 139 | } 140 | // export const unsecond = (arrow, c) => unfirst(dimap(swap, swap, arrow), c) 141 | 142 | var Unfirst = function Unfirst (ab , c ) { 143 | this.ab = ab 144 | this.value = c 145 | }; 146 | 147 | Unfirst.prototype.step = function step$3 (t , a ) { 148 | var ref = this.ab.step(t, [a, this.value]); 149 | var ref_value = ref.value; 150 | var b = ref_value[0]; 151 | var c = ref_value[1]; 152 | var next = ref.next; 153 | return step(b, unfirst(next, c)) 154 | }; 155 | 156 | // pipe :: (SFTime a b ... SFTime y z) -> SFTime a z 157 | // Compose many Reactive transformations, left to right 158 | function pipe (ab ) { 159 | var rest = [], len = arguments.length - 1; 160 | while ( len-- > 0 ) rest[ len ] = arguments[ len + 1 ]; 161 | 162 | return rest.reduce(pipe2, ab) 163 | } 164 | 165 | // pipe2 :: SFTime a b -> SFTime b c -> SFTime a c 166 | // Compose 2 Reactive transformations left to right 167 | function pipe2 (ab , bc ) { 168 | return new Pipe(ab, bc) 169 | } 170 | 171 | function dimap (fab , fcd , bc ) { 172 | return pipe2(pipe2(lift(fab), bc), lift(fcd)) 173 | } 174 | 175 | function lmap (fab , bc ) { 176 | return pipe2(lift(fab), bc) 177 | } 178 | 179 | function rmap (fbc , ab ) { 180 | return pipe2(ab, lift(fbc)) 181 | } 182 | 183 | var Pipe = function Pipe (ab , bc ) { 184 | this.ab = ab 185 | this.bc = bc 186 | }; 187 | 188 | Pipe.prototype.step = function step$4 (t , a ) { 189 | var ref = this.ab.step(t, a); 190 | var b = ref.value; 191 | var ab = ref.next; 192 | var ref$1 = this.bc.step(t, b); 193 | var c = ref$1.value; 194 | var bc = ref$1.next; 195 | return step(c, pipe2(ab, bc)) 196 | }; 197 | 198 | // split :: SFTime a b -> SFTime a c -> SFTime [b, c] 199 | // Duplicates input a and pass it through Reactive transformations 200 | // ab and ac to yield [b, c] 201 | function split (ab , ac ) { 202 | return lmap(dup, both(ab, ac)) 203 | } 204 | 205 | // both :: SFTime a b -> SFTime c d -> Reactive [a, c] [b, d] 206 | // Given an [a, c] input, pass a through Reactive transformation ab and 207 | // c through Reactive transformation cd to yield [b, d] 208 | function both (ab , cd ) { 209 | return new Both(ab, cd) 210 | } 211 | 212 | var Both = function Both (ab , cd ) { 213 | this.ab = ab 214 | this.cd = cd 215 | }; 216 | 217 | Both.prototype.step = function step$5 (t , ref ) { 218 | var a = ref[0]; 219 | var c = ref[1]; 220 | 221 | var ref$1 = this.ab.step(t, a); 222 | var b = ref$1.value; 223 | var anext = ref$1.next; 224 | var ref$2 = this.cd.step(t, c); 225 | var d = ref$2.value; 226 | var cnext = ref$2.next; 227 | return step([b, d], both(anext, cnext)) 228 | }; 229 | 230 | // 231 | 232 | // An event, which has a value when it occurs, and 233 | // has no value when it doesn't occur 234 | 235 | 236 | // Event non-occurrence 237 | var NoEvent = undefined 238 | 239 | // Turn Events of A instead Events of B 240 | function map (f ) { 241 | return function (a) { return a === undefined ? a : f(a); } 242 | } 243 | 244 | // Return the Event that occurred, preferring a1 if both occurred 245 | function mergeE (a1 , a2 ) { 246 | return a1 === undefined ? a2 : a1 247 | } 248 | 249 | // Internal helper to allow continuous value transformations to be 250 | // applied when an event occurs 251 | // TODO: Consider exposing this if it seems useful 252 | function liftE (ab ) { 253 | return new LiftE(ab) 254 | } 255 | 256 | var LiftE = function LiftE (ab) { 257 | this.ab = ab 258 | }; 259 | 260 | LiftE.prototype.step = function step (t , a ) { 261 | if (a === undefined) { 262 | return { value: NoEvent, next: this } 263 | } 264 | var ref = this.ab.step(t, a); 265 | var value = ref.value; 266 | var next = ref.next; 267 | return { value: value, next: liftE(next) } 268 | }; 269 | 270 | // Sample the current time when an event occurs 271 | var eventTime = { 272 | step: function step (t , a ) { 273 | return { value: a === undefined ? NoEvent : t, next: this } 274 | } 275 | } 276 | 277 | // Transform event values 278 | function mapE (f ) { 279 | return lift(map(f)) 280 | } 281 | 282 | // When an event occurs, make its value b 283 | function as (b ) { 284 | return mapE(function (_) { return b; }) 285 | } 286 | 287 | // When A occurs, sample the value of B, and produce f(a, b) 288 | // When A does not occur, produce NoEvent 289 | function sampleWith (f ) { 290 | return lift(function (ref) { 291 | var a = ref[0]; 292 | var b = ref[1]; 293 | 294 | return a === undefined ? NoEvent : f(a, b); 295 | }) 296 | } 297 | 298 | function sample () { 299 | return sampleWith(pair) 300 | } 301 | 302 | // Merge events, preferring the left in the case of 303 | // simultaneous occurrence 304 | function merge () { 305 | return unsplit(mergeE) 306 | } 307 | 308 | // Merge event SignalFuncs 309 | function or (left , right ) { 310 | return liftE(pipe(both(left, right), merge())) 311 | } 312 | 313 | // Turn an event into a stepped continuous value 314 | function hold (initial ) { 315 | return new Hold(initial) 316 | } 317 | 318 | var Hold = function Hold (value ) { 319 | this.value = value 320 | }; 321 | 322 | Hold.prototype.step = function step (t , a ) { 323 | return a === undefined 324 | ? { value: this.value, next: this } 325 | : { value: a, next: hold(a) } 326 | }; 327 | 328 | // Accumulate event 329 | function scanE (f , initial ) { 330 | return new Accum(f, initial) 331 | } 332 | 333 | // Accumulate event to a continuous value 334 | function scan (f , initial ) { 335 | return pipe(scanE(f, initial), hold(initial)) 336 | } 337 | 338 | // Accumulate event, given an initial value and a update-function event 339 | function accumE (initial ) { 340 | return scanE(function (a, f) { return f(a); }, initial) 341 | } 342 | 343 | // Accumulate event to a continuous value, given an initial value and a update-function event 344 | function accum (initial ) { 345 | return pipe(accumE(initial), hold(initial)) 346 | } 347 | 348 | var Accum = function Accum (f , value ) { 349 | this.f = f 350 | this.value = value 351 | }; 352 | 353 | Accum.prototype.step = function step (t , a ) { 354 | if (a === undefined) { 355 | return { value: NoEvent, next: this } 356 | } 357 | var f = this.f 358 | var value = f(this.value, a) 359 | return { value: value, next: new Accum(f, value) } 360 | }; 361 | 362 | // 363 | 364 | // Dispose an Input 365 | 366 | 367 | // Handle input events 368 | 369 | 370 | // An Input allows events to be pushed into the system 371 | // It's basically any unary higher order function 372 | 373 | 374 | 375 | 376 | // Turn a pair of inputs into an input of pairs 377 | function both$1 (input1 , input2 ) { 378 | return function (f) { 379 | var dispose1 = input1(function (a1) { return f([a1, NoEvent]); }) 380 | var dispose2 = input2(function (a2) { return f([NoEvent, a2]); }) 381 | return function () { return [dispose1(), dispose2()]; } 382 | } 383 | } 384 | 385 | var never = function () { return noop; } 386 | var noop = function () {} 387 | 388 | function newInput () { 389 | var _occur 390 | var occur = function (x) { 391 | if (typeof _occur === 'function') { 392 | _occur(x) 393 | } 394 | } 395 | 396 | var input = function (f) { 397 | _occur = f 398 | return function () { 399 | _occur = undefined 400 | } 401 | } 402 | 403 | return [occur, input] 404 | } 405 | 406 | // 407 | 408 | 409 | 410 | 411 | function loop (session , input , sf ) { 412 | var dispose = input(function (a) { 413 | var ref = session.step(); 414 | var sample = ref.sample; 415 | var nextSession = ref.nextSession; 416 | var ref$1 = sf.step(sample, a); 417 | var ref$1_value = ref$1.value; 418 | var _ = ref$1_value[0]; 419 | var nextInput = ref$1_value[1]; 420 | var next = ref$1.next; // eslint-disable-line no-unused-vars 421 | dispose = switchInput(nextSession, nextInput, next, dispose) 422 | }) 423 | 424 | return dispose 425 | } 426 | 427 | var switchInput = function (session, input, sf, dispose) { 428 | dispose() 429 | return loop(session, input, sf) 430 | } 431 | 432 | // 433 | 434 | // A session provides a sample of state that will be fed into 435 | // a signal function when events occur 436 | 437 | 438 | 439 | 440 | 441 | 442 | function newSession (step , init ) { 443 | return new SteppedSession(step, init) 444 | } 445 | 446 | // Session that yields an incrementing count at each step 447 | var countSession = function () { return newSession(function (n) { return n + 1; }, 0); } 448 | 449 | var SteppedSession = function SteppedSession (step , value ) { 450 | this._step = step 451 | this.value = value 452 | }; 453 | 454 | SteppedSession.prototype.step = function step () { 455 | var sample = this._step(this.value) 456 | return { sample: sample, nextSession: newSession(this._step, sample) } 457 | }; 458 | 459 | // Session that yields a time delta from its start time at each step 460 | var clockSession = function () { return new ClockSession(Date.now()); } 461 | 462 | var ClockSession = function ClockSession (start ) { 463 | this.start = start 464 | this.time = Infinity 465 | }; 466 | 467 | ClockSession.prototype.step = function step () { 468 | var t = Date.now() 469 | if (t < this.time) { 470 | this.time = t - this.start 471 | } 472 | return { sample: this.time, nextSession: new ClockSession(this.start) } 473 | }; 474 | 475 | exports.bothI = both$1; 476 | exports.never = never; 477 | exports.newInput = newInput; 478 | exports.clockSession = clockSession; 479 | exports.countSession = countSession; 480 | exports.time = time; 481 | exports.lift = lift; 482 | exports.unsplit = unsplit; 483 | exports.always = always; 484 | exports.id = id; 485 | exports.first = first; 486 | exports.second = second; 487 | exports.unfirst = unfirst; 488 | exports.pipe = pipe; 489 | exports.pipe2 = pipe2; 490 | exports.dimap = dimap; 491 | exports.lmap = lmap; 492 | exports.rmap = rmap; 493 | exports.split = split; 494 | exports.both = both; 495 | exports.NoEvent = NoEvent; 496 | exports.eventTime = eventTime; 497 | exports.mapE = mapE; 498 | exports.as = as; 499 | exports.sampleWith = sampleWith; 500 | exports.sample = sample; 501 | exports.merge = merge; 502 | exports.or = or; 503 | exports.hold = hold; 504 | exports.scanE = scanE; 505 | exports.scan = scan; 506 | exports.accumE = accumE; 507 | exports.accum = accum; 508 | exports.loop = loop; 509 | 510 | Object.defineProperty(exports, '__esModule', { value: true }); 511 | 512 | }))); 513 | //# sourceMappingURL=arrow.js.map 514 | -------------------------------------------------------------------------------- /examples/timer/app.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | function uncurry (f ) { 5 | return function (ref) { 6 | var a = ref[0]; 7 | var b = ref[1]; 8 | 9 | return f(a, b); 10 | } 11 | } 12 | 13 | // 14 | // Signal Function is a time varying transformation that 15 | // turns Signals of A into Signals of B. It may carry state 16 | // and evolve over time 17 | 18 | 19 | 20 | 21 | // A Step is the result of applying a SignalFunc 22 | // to an A to get a B and a new SignalFunc 23 | 24 | 25 | 26 | 27 | 28 | // SignalFunc specialized for Time type 29 | // Note: Flow can't infer generics, IOW, it can't info the 30 | // type T *later* based on the Session type provided when running 31 | // a SignalFunc. Flow needs to be able to determine T at the 32 | // instant a SignalFunc is created, but the type is only known 33 | // later when a Session is used to run the SignalFunc 34 | 35 | 36 | // SignalStep specialized for Time type 37 | // re: Flow, similarly 38 | 39 | 40 | // Simple helper to construct a Step 41 | var step = function (value, next) { return ({ value: value, next: next }); } 42 | 43 | var time = 44 | { step: function (value, _) { return ({ value: value, next: time }); } } 45 | 46 | // Lift a function into a SignalFunc 47 | function lift (f ) { 48 | return new Lift(f) 49 | } 50 | 51 | // Combine a pair of signals into a signal of C 52 | function unsplit (f ) { 53 | return lift(uncurry(f)) 54 | } 55 | 56 | var Lift = function Lift (f ) { 57 | this.f = f 58 | }; 59 | 60 | Lift.prototype.step = function step$1 (t , a ) { 61 | var f = this.f 62 | return step(f(a), this) 63 | }; 64 | 65 | // first :: SFTime a b -> SFTime [a, c] [b, c] 66 | // Apply a SignalFunc to the first signal of a pair 67 | function first (ab ) { 68 | return new First(ab) 69 | } 70 | 71 | var First = function First (ab ) { 72 | this.ab = ab 73 | }; 74 | 75 | First.prototype.step = function step$2 (t , ref ) { 76 | var a = ref[0]; 77 | var c = ref[1]; 78 | 79 | var ref$1 = this.ab.step(t, a); 80 | var b = ref$1.value; 81 | var next = ref$1.next; 82 | return step([b, c], first(next)) 83 | }; 84 | 85 | // pipe :: (SFTime a b ... SFTime y z) -> SFTime a z 86 | // Compose many Reactive transformations, left to right 87 | function pipe (ab ) { 88 | var rest = [], len = arguments.length - 1; 89 | while ( len-- > 0 ) rest[ len ] = arguments[ len + 1 ]; 90 | 91 | return rest.reduce(pipe2, ab) 92 | } 93 | 94 | // pipe2 :: SFTime a b -> SFTime b c -> SFTime a c 95 | // Compose 2 Reactive transformations left to right 96 | function pipe2 (ab , bc ) { 97 | return new Pipe(ab, bc) 98 | } 99 | 100 | var Pipe = function Pipe (ab , bc ) { 101 | this.ab = ab 102 | this.bc = bc 103 | }; 104 | 105 | Pipe.prototype.step = function step$4 (t , a ) { 106 | var ref = this.ab.step(t, a); 107 | var b = ref.value; 108 | var ab = ref.next; 109 | var ref$1 = this.bc.step(t, b); 110 | var c = ref$1.value; 111 | var bc = ref$1.next; 112 | return step(c, pipe2(ab, bc)) 113 | }; 114 | 115 | // both :: SFTime a b -> SFTime c d -> Reactive [a, c] [b, d] 116 | // Given an [a, c] input, pass a through Reactive transformation ab and 117 | // c through Reactive transformation cd to yield [b, d] 118 | function both (ab , cd ) { 119 | return new Both(ab, cd) 120 | } 121 | 122 | var Both = function Both (ab , cd ) { 123 | this.ab = ab 124 | this.cd = cd 125 | }; 126 | 127 | Both.prototype.step = function step$5 (t , ref ) { 128 | var a = ref[0]; 129 | var c = ref[1]; 130 | 131 | var ref$1 = this.ab.step(t, a); 132 | var b = ref$1.value; 133 | var anext = ref$1.next; 134 | var ref$2 = this.cd.step(t, c); 135 | var d = ref$2.value; 136 | var cnext = ref$2.next; 137 | return step([b, d], both(anext, cnext)) 138 | }; 139 | 140 | // 141 | 142 | // An event, which has a value when it occurs, and 143 | // has no value when it doesn't occur 144 | 145 | 146 | // Event non-occurrence 147 | var NoEvent = undefined 148 | 149 | // Sample the current time when an event occurs 150 | var eventTime = { 151 | step: function step (t , a ) { 152 | return { value: a === undefined ? NoEvent : t, next: this } 153 | } 154 | } 155 | 156 | // Turn an event into a stepped continuous value 157 | function hold (initial ) { 158 | return new Hold(initial) 159 | } 160 | 161 | var Hold = function Hold (value ) { 162 | this.value = value 163 | }; 164 | 165 | Hold.prototype.step = function step (t , a ) { 166 | return a === undefined 167 | ? { value: this.value, next: this } 168 | : { value: a, next: hold(a) } 169 | }; 170 | 171 | // Accumulate event 172 | function scanE (f , initial ) { 173 | return new Accum(f, initial) 174 | } 175 | 176 | // Accumulate event to a continuous value 177 | function scan (f , initial ) { 178 | return pipe(scanE(f, initial), hold(initial)) 179 | } 180 | 181 | var Accum = function Accum (f , value ) { 182 | this.f = f 183 | this.value = value 184 | }; 185 | 186 | Accum.prototype.step = function step (t , a ) { 187 | if (a === undefined) { 188 | return { value: NoEvent, next: this } 189 | } 190 | var f = this.f 191 | var value = f(this.value, a) 192 | return { value: value, next: new Accum(f, value) } 193 | }; 194 | 195 | // 196 | 197 | // A SignalGen generates values or events of a signal 198 | 199 | 200 | 201 | 202 | // Forget all future values of a SignalGen 203 | 204 | 205 | 206 | 207 | // Handle events from a SignalGen 208 | 209 | 210 | // Turn a pair of inputs into an input of pairs 211 | function and (input1 , input2 ) { 212 | return new SGVector(input1, input2) 213 | } 214 | 215 | function delay (ms , a ) { 216 | return stepInput(function (f) { return setTimeout(f, ms, a); }, clearTimeout) 217 | } 218 | 219 | function signalGen () { 220 | var source = new SGSource() 221 | return [function (x) { return source.handler(x); }, source] 222 | } 223 | 224 | var noop = function () {} 225 | 226 | var SGSource = function SGSource () { 227 | this.handler = noop 228 | }; 229 | 230 | SGSource.prototype.listen = function listen (handler ) { 231 | this.handler = handler 232 | return new ForgetSGSource(this) 233 | }; 234 | 235 | var ForgetSGSource = function ForgetSGSource (source ) { 236 | this.source = source 237 | }; 238 | 239 | ForgetSGSource.prototype.forget = function forget () { 240 | this.source.handler = noop 241 | }; 242 | 243 | var emptySignalVector = [NoEvent, NoEvent] 244 | 245 | var empty = function (input) { return input instanceof SGVector ? emptySignalVector : NoEvent; } 246 | 247 | var SGVector = function SGVector (first , second ) { 248 | this.first = first 249 | this.second = second 250 | }; 251 | 252 | SGVector.prototype.listen = function listen (handler ) { 253 | var this$1 = this; 254 | 255 | var forgetFirst = this.first.listen(function (a) { return handler([a, empty(this$1.second)]); }) 256 | var forgetSecond = this.second.listen(function (a) { return handler([empty(this$1.first), a]); }) 257 | return new ForgetSGVector(forgetFirst, forgetSecond) 258 | }; 259 | 260 | var ForgetSGVector = function ForgetSGVector (forgetFirst , forgetSecond ) { 261 | this.forgetFirst = forgetFirst 262 | this.forgetSecond = forgetSecond 263 | }; 264 | 265 | ForgetSGVector.prototype.forget = function forget () { 266 | this.forgetFirst.forget() 267 | this.forgetSecond.forget() 268 | }; 269 | 270 | function stepInput (set , forget ) { 271 | return new PushSG(set, forget) 272 | } 273 | 274 | var PushSG = function PushSG(set , forget ) { 275 | this.set = set 276 | this.forget = forget 277 | }; 278 | 279 | PushSG.prototype.listen = function listen (f) { 280 | return new ForgetPushSG(this.forget, this.set.call(undefined, f)) 281 | }; 282 | 283 | var ForgetPushSG = function ForgetPushSG (forget , context ) { 284 | this._forget = forget 285 | this.context = context 286 | }; 287 | 288 | ForgetPushSG.prototype.forget = function forget () { 289 | this._forget.call(undefined, this.context) 290 | }; 291 | 292 | // Session that yields a time delta from its start time at each step 293 | var clockSession = function () { return new ClockSession(Date.now()); } 294 | 295 | var ClockSession = function ClockSession (start ) { 296 | this.start = start 297 | }; 298 | 299 | ClockSession.prototype.step = function step () { 300 | return { sample: Date.now() - this.start, nextSession: new ClockSession(this.start) } 301 | }; 302 | 303 | // 304 | 305 | 306 | 307 | 308 | function loop (session , input , sf ) { 309 | var forget = input.listen(function (a) { 310 | var ref = session.step(); 311 | var sample = ref.sample; 312 | var nextSession = ref.nextSession; 313 | var ref$1 = sf.step(sample, a); 314 | var ref$1_value = ref$1.value; 315 | var _ = ref$1_value[0]; 316 | var nextInput = ref$1_value[1]; 317 | var next = ref$1.next; // eslint-disable-line no-unused-vars 318 | forget = switchInput(nextSession, nextInput, next, forget) 319 | }) 320 | 321 | return forget 322 | } 323 | 324 | var switchInput = function (session, input, sf, forget) { 325 | forget.forget() 326 | return loop(session, input, sf) 327 | } 328 | 329 | function interopDefault(ex) { 330 | return ex && typeof ex === 'object' && 'default' in ex ? ex['default'] : ex; 331 | } 332 | 333 | function createCommonjsModule(fn, module) { 334 | return module = { exports: {} }, fn(module, module.exports), module.exports; 335 | } 336 | 337 | var eventlisteners = createCommonjsModule(function (module) { 338 | function invokeHandler(handler, vnode, event) { 339 | if (typeof handler === "function") { 340 | // call function handler 341 | handler.call(vnode, event, vnode); 342 | } else if (typeof handler === "object") { 343 | // call handler with arguments 344 | if (typeof handler[0] === "function") { 345 | // special case for single argument for performance 346 | if (handler.length === 2) { 347 | handler[0].call(vnode, handler[1], event, vnode); 348 | } else { 349 | var args = handler.slice(1); 350 | args.push(event); 351 | args.push(vnode); 352 | handler[0].apply(vnode, args); 353 | } 354 | } else { 355 | // call multiple handlers 356 | for (var i = 0; i < handler.length; i++) { 357 | invokeHandler(handler[i]); 358 | } 359 | } 360 | } 361 | } 362 | 363 | function handleEvent(event, vnode) { 364 | var name = event.type, 365 | on = vnode.data.on; 366 | 367 | // call event handler(s) if exists 368 | if (on && on[name]) { 369 | invokeHandler(on[name], vnode, event); 370 | } 371 | } 372 | 373 | function createListener() { 374 | return function handler(event) { 375 | handleEvent(event, handler.vnode); 376 | } 377 | } 378 | 379 | function updateEventListeners(oldVnode, vnode) { 380 | var oldOn = oldVnode.data.on, 381 | oldListener = oldVnode.listener, 382 | oldElm = oldVnode.elm, 383 | on = vnode && vnode.data.on, 384 | elm = vnode && vnode.elm, 385 | name; 386 | 387 | // optimization for reused immutable handlers 388 | if (oldOn === on) { 389 | return; 390 | } 391 | 392 | // remove existing listeners which no longer used 393 | if (oldOn && oldListener) { 394 | // if element changed or deleted we remove all existing listeners unconditionally 395 | if (!on) { 396 | for (name in oldOn) { 397 | // remove listener if element was changed or existing listeners removed 398 | oldElm.removeEventListener(name, oldListener, false); 399 | } 400 | } else { 401 | for (name in oldOn) { 402 | // remove listener if existing listener removed 403 | if (!on[name]) { 404 | oldElm.removeEventListener(name, oldListener, false); 405 | } 406 | } 407 | } 408 | } 409 | 410 | // add new listeners which has not already attached 411 | if (on) { 412 | // reuse existing listener or create new 413 | var listener = vnode.listener = oldVnode.listener || createListener(); 414 | // update vnode for listener 415 | listener.vnode = vnode; 416 | 417 | // if element changed or added we add all needed listeners unconditionally 418 | if (!oldOn) { 419 | for (name in on) { 420 | // add listener if element was changed or new listeners added 421 | elm.addEventListener(name, listener, false); 422 | } 423 | } else { 424 | for (name in on) { 425 | // add listener if new listener added 426 | if (!oldOn[name]) { 427 | elm.addEventListener(name, listener, false); 428 | } 429 | } 430 | } 431 | } 432 | } 433 | 434 | module.exports = { 435 | create: updateEventListeners, 436 | update: updateEventListeners, 437 | destroy: updateEventListeners 438 | }; 439 | }); 440 | 441 | var events = interopDefault(eventlisteners); 442 | 443 | var attributes = createCommonjsModule(function (module) { 444 | var booleanAttrs = ["allowfullscreen", "async", "autofocus", "autoplay", "checked", "compact", "controls", "declare", 445 | "default", "defaultchecked", "defaultmuted", "defaultselected", "defer", "disabled", "draggable", 446 | "enabled", "formnovalidate", "hidden", "indeterminate", "inert", "ismap", "itemscope", "loop", "multiple", 447 | "muted", "nohref", "noresize", "noshade", "novalidate", "nowrap", "open", "pauseonexit", "readonly", 448 | "required", "reversed", "scoped", "seamless", "selected", "sortable", "spellcheck", "translate", 449 | "truespeed", "typemustmatch", "visible"]; 450 | 451 | var booleanAttrsDict = {}; 452 | for(var i=0, len = booleanAttrs.length; i < len; i++) { 453 | booleanAttrsDict[booleanAttrs[i]] = true; 454 | } 455 | 456 | function updateAttrs(oldVnode, vnode) { 457 | var key, cur, old, elm = vnode.elm, 458 | oldAttrs = oldVnode.data.attrs, attrs = vnode.data.attrs; 459 | 460 | if (!oldAttrs && !attrs) return; 461 | oldAttrs = oldAttrs || {}; 462 | attrs = attrs || {}; 463 | 464 | // update modified attributes, add new attributes 465 | for (key in attrs) { 466 | cur = attrs[key]; 467 | old = oldAttrs[key]; 468 | if (old !== cur) { 469 | // TODO: add support to namespaced attributes (setAttributeNS) 470 | if(!cur && booleanAttrsDict[key]) 471 | elm.removeAttribute(key); 472 | else 473 | elm.setAttribute(key, cur); 474 | } 475 | } 476 | //remove removed attributes 477 | // use `in` operator since the previous `for` iteration uses it (.i.e. add even attributes with undefined value) 478 | // the other option is to remove all attributes with value == undefined 479 | for (key in oldAttrs) { 480 | if (!(key in attrs)) { 481 | elm.removeAttribute(key); 482 | } 483 | } 484 | } 485 | 486 | module.exports = {create: updateAttrs, update: updateAttrs}; 487 | }); 488 | 489 | interopDefault(attributes); 490 | 491 | var props = createCommonjsModule(function (module) { 492 | function updateProps(oldVnode, vnode) { 493 | var key, cur, old, elm = vnode.elm, 494 | oldProps = oldVnode.data.props, props = vnode.data.props; 495 | 496 | if (!oldProps && !props) return; 497 | oldProps = oldProps || {}; 498 | props = props || {}; 499 | 500 | for (key in oldProps) { 501 | if (!props[key]) { 502 | delete elm[key]; 503 | } 504 | } 505 | for (key in props) { 506 | cur = props[key]; 507 | old = oldProps[key]; 508 | if (old !== cur && (key !== 'value' || elm[key] !== cur)) { 509 | elm[key] = cur; 510 | } 511 | } 512 | } 513 | 514 | module.exports = {create: updateProps, update: updateProps}; 515 | }); 516 | 517 | interopDefault(props); 518 | 519 | var _class = createCommonjsModule(function (module) { 520 | function updateClass(oldVnode, vnode) { 521 | var cur, name, elm = vnode.elm, 522 | oldClass = oldVnode.data.class, 523 | klass = vnode.data.class; 524 | 525 | if (!oldClass && !klass) return; 526 | oldClass = oldClass || {}; 527 | klass = klass || {}; 528 | 529 | for (name in oldClass) { 530 | if (!klass[name]) { 531 | elm.classList.remove(name); 532 | } 533 | } 534 | for (name in klass) { 535 | cur = klass[name]; 536 | if (cur !== oldClass[name]) { 537 | elm.classList[cur ? 'add' : 'remove'](name); 538 | } 539 | } 540 | } 541 | 542 | module.exports = {create: updateClass, update: updateClass}; 543 | }); 544 | 545 | interopDefault(_class); 546 | 547 | var vnode = createCommonjsModule(function (module) { 548 | module.exports = function(sel, data, children, text, elm) { 549 | var key = data === undefined ? undefined : data.key; 550 | return {sel: sel, data: data, children: children, 551 | text: text, elm: elm, key: key}; 552 | }; 553 | }); 554 | 555 | var vnode$1 = interopDefault(vnode); 556 | 557 | 558 | var require$$1 = Object.freeze({ 559 | default: vnode$1 560 | }); 561 | 562 | var is = createCommonjsModule(function (module) { 563 | module.exports = { 564 | array: Array.isArray, 565 | primitive: function(s) { return typeof s === 'string' || typeof s === 'number'; }, 566 | }; 567 | }); 568 | 569 | var is$1 = interopDefault(is); 570 | var array = is.array; 571 | var primitive = is.primitive; 572 | 573 | var require$$0 = Object.freeze({ 574 | default: is$1, 575 | array: array, 576 | primitive: primitive 577 | }); 578 | 579 | var htmldomapi = createCommonjsModule(function (module) { 580 | function createElement(tagName){ 581 | return document.createElement(tagName); 582 | } 583 | 584 | function createElementNS(namespaceURI, qualifiedName){ 585 | return document.createElementNS(namespaceURI, qualifiedName); 586 | } 587 | 588 | function createTextNode(text){ 589 | return document.createTextNode(text); 590 | } 591 | 592 | 593 | function insertBefore(parentNode, newNode, referenceNode){ 594 | parentNode.insertBefore(newNode, referenceNode); 595 | } 596 | 597 | 598 | function removeChild(node, child){ 599 | node.removeChild(child); 600 | } 601 | 602 | function appendChild(node, child){ 603 | node.appendChild(child); 604 | } 605 | 606 | function parentNode(node){ 607 | return node.parentElement; 608 | } 609 | 610 | function nextSibling(node){ 611 | return node.nextSibling; 612 | } 613 | 614 | function tagName(node){ 615 | return node.tagName; 616 | } 617 | 618 | function setTextContent(node, text){ 619 | node.textContent = text; 620 | } 621 | 622 | module.exports = { 623 | createElement: createElement, 624 | createElementNS: createElementNS, 625 | createTextNode: createTextNode, 626 | appendChild: appendChild, 627 | removeChild: removeChild, 628 | insertBefore: insertBefore, 629 | parentNode: parentNode, 630 | nextSibling: nextSibling, 631 | tagName: tagName, 632 | setTextContent: setTextContent 633 | }; 634 | }); 635 | 636 | var htmldomapi$1 = interopDefault(htmldomapi); 637 | var createElement = htmldomapi.createElement; 638 | var createElementNS = htmldomapi.createElementNS; 639 | var createTextNode = htmldomapi.createTextNode; 640 | var appendChild = htmldomapi.appendChild; 641 | var removeChild = htmldomapi.removeChild; 642 | var insertBefore = htmldomapi.insertBefore; 643 | var parentNode = htmldomapi.parentNode; 644 | var nextSibling = htmldomapi.nextSibling; 645 | var tagName = htmldomapi.tagName; 646 | var setTextContent = htmldomapi.setTextContent; 647 | 648 | var require$$0$1 = Object.freeze({ 649 | default: htmldomapi$1, 650 | createElement: createElement, 651 | createElementNS: createElementNS, 652 | createTextNode: createTextNode, 653 | appendChild: appendChild, 654 | removeChild: removeChild, 655 | insertBefore: insertBefore, 656 | parentNode: parentNode, 657 | nextSibling: nextSibling, 658 | tagName: tagName, 659 | setTextContent: setTextContent 660 | }); 661 | 662 | var snabbdom = createCommonjsModule(function (module) { 663 | // jshint newcap: false 664 | /* global require, module, document, Node */ 665 | 'use strict'; 666 | 667 | var VNode = interopDefault(require$$1); 668 | var is = interopDefault(require$$0); 669 | var domApi = interopDefault(require$$0$1); 670 | 671 | function isUndef(s) { return s === undefined; } 672 | function isDef(s) { return s !== undefined; } 673 | 674 | var emptyNode = VNode('', {}, [], undefined, undefined); 675 | 676 | function sameVnode(vnode1, vnode2) { 677 | return vnode1.key === vnode2.key && vnode1.sel === vnode2.sel; 678 | } 679 | 680 | function createKeyToOldIdx(children, beginIdx, endIdx) { 681 | var i, map = {}, key; 682 | for (i = beginIdx; i <= endIdx; ++i) { 683 | key = children[i].key; 684 | if (isDef(key)) map[key] = i; 685 | } 686 | return map; 687 | } 688 | 689 | var hooks = ['create', 'update', 'remove', 'destroy', 'pre', 'post']; 690 | 691 | function init(modules, api) { 692 | var i, j, cbs = {}; 693 | 694 | if (isUndef(api)) api = domApi; 695 | 696 | for (i = 0; i < hooks.length; ++i) { 697 | cbs[hooks[i]] = []; 698 | for (j = 0; j < modules.length; ++j) { 699 | if (modules[j][hooks[i]] !== undefined) cbs[hooks[i]].push(modules[j][hooks[i]]); 700 | } 701 | } 702 | 703 | function emptyNodeAt(elm) { 704 | var id = elm.id ? '#' + elm.id : ''; 705 | var c = elm.className ? '.' + elm.className.split(' ').join('.') : ''; 706 | return VNode(api.tagName(elm).toLowerCase() + id + c, {}, [], undefined, elm); 707 | } 708 | 709 | function createRmCb(childElm, listeners) { 710 | return function() { 711 | if (--listeners === 0) { 712 | var parent = api.parentNode(childElm); 713 | api.removeChild(parent, childElm); 714 | } 715 | }; 716 | } 717 | 718 | function createElm(vnode, insertedVnodeQueue) { 719 | var i, data = vnode.data; 720 | if (isDef(data)) { 721 | if (isDef(i = data.hook) && isDef(i = i.init)) { 722 | i(vnode); 723 | data = vnode.data; 724 | } 725 | } 726 | var elm, children = vnode.children, sel = vnode.sel; 727 | if (isDef(sel)) { 728 | // Parse selector 729 | var hashIdx = sel.indexOf('#'); 730 | var dotIdx = sel.indexOf('.', hashIdx); 731 | var hash = hashIdx > 0 ? hashIdx : sel.length; 732 | var dot = dotIdx > 0 ? dotIdx : sel.length; 733 | var tag = hashIdx !== -1 || dotIdx !== -1 ? sel.slice(0, Math.min(hash, dot)) : sel; 734 | elm = vnode.elm = isDef(data) && isDef(i = data.ns) ? api.createElementNS(i, tag) 735 | : api.createElement(tag); 736 | if (hash < dot) elm.id = sel.slice(hash + 1, dot); 737 | if (dotIdx > 0) elm.className = sel.slice(dot + 1).replace(/\./g, ' '); 738 | if (is.array(children)) { 739 | for (i = 0; i < children.length; ++i) { 740 | api.appendChild(elm, createElm(children[i], insertedVnodeQueue)); 741 | } 742 | } else if (is.primitive(vnode.text)) { 743 | api.appendChild(elm, api.createTextNode(vnode.text)); 744 | } 745 | for (i = 0; i < cbs.create.length; ++i) cbs.create[i](emptyNode, vnode); 746 | i = vnode.data.hook; // Reuse variable 747 | if (isDef(i)) { 748 | if (i.create) i.create(emptyNode, vnode); 749 | if (i.insert) insertedVnodeQueue.push(vnode); 750 | } 751 | } else { 752 | elm = vnode.elm = api.createTextNode(vnode.text); 753 | } 754 | return vnode.elm; 755 | } 756 | 757 | function addVnodes(parentElm, before, vnodes, startIdx, endIdx, insertedVnodeQueue) { 758 | for (; startIdx <= endIdx; ++startIdx) { 759 | api.insertBefore(parentElm, createElm(vnodes[startIdx], insertedVnodeQueue), before); 760 | } 761 | } 762 | 763 | function invokeDestroyHook(vnode) { 764 | var i, j, data = vnode.data; 765 | if (isDef(data)) { 766 | if (isDef(i = data.hook) && isDef(i = i.destroy)) i(vnode); 767 | for (i = 0; i < cbs.destroy.length; ++i) cbs.destroy[i](vnode); 768 | if (isDef(i = vnode.children)) { 769 | for (j = 0; j < vnode.children.length; ++j) { 770 | invokeDestroyHook(vnode.children[j]); 771 | } 772 | } 773 | } 774 | } 775 | 776 | function removeVnodes(parentElm, vnodes, startIdx, endIdx) { 777 | for (; startIdx <= endIdx; ++startIdx) { 778 | var i, listeners, rm, ch = vnodes[startIdx]; 779 | if (isDef(ch)) { 780 | if (isDef(ch.sel)) { 781 | invokeDestroyHook(ch); 782 | listeners = cbs.remove.length + 1; 783 | rm = createRmCb(ch.elm, listeners); 784 | for (i = 0; i < cbs.remove.length; ++i) cbs.remove[i](ch, rm); 785 | if (isDef(i = ch.data) && isDef(i = i.hook) && isDef(i = i.remove)) { 786 | i(ch, rm); 787 | } else { 788 | rm(); 789 | } 790 | } else { // Text node 791 | api.removeChild(parentElm, ch.elm); 792 | } 793 | } 794 | } 795 | } 796 | 797 | function updateChildren(parentElm, oldCh, newCh, insertedVnodeQueue) { 798 | var oldStartIdx = 0, newStartIdx = 0; 799 | var oldEndIdx = oldCh.length - 1; 800 | var oldStartVnode = oldCh[0]; 801 | var oldEndVnode = oldCh[oldEndIdx]; 802 | var newEndIdx = newCh.length - 1; 803 | var newStartVnode = newCh[0]; 804 | var newEndVnode = newCh[newEndIdx]; 805 | var oldKeyToIdx, idxInOld, elmToMove, before; 806 | 807 | while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) { 808 | if (isUndef(oldStartVnode)) { 809 | oldStartVnode = oldCh[++oldStartIdx]; // Vnode has been moved left 810 | } else if (isUndef(oldEndVnode)) { 811 | oldEndVnode = oldCh[--oldEndIdx]; 812 | } else if (sameVnode(oldStartVnode, newStartVnode)) { 813 | patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue); 814 | oldStartVnode = oldCh[++oldStartIdx]; 815 | newStartVnode = newCh[++newStartIdx]; 816 | } else if (sameVnode(oldEndVnode, newEndVnode)) { 817 | patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue); 818 | oldEndVnode = oldCh[--oldEndIdx]; 819 | newEndVnode = newCh[--newEndIdx]; 820 | } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right 821 | patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue); 822 | api.insertBefore(parentElm, oldStartVnode.elm, api.nextSibling(oldEndVnode.elm)); 823 | oldStartVnode = oldCh[++oldStartIdx]; 824 | newEndVnode = newCh[--newEndIdx]; 825 | } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left 826 | patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue); 827 | api.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm); 828 | oldEndVnode = oldCh[--oldEndIdx]; 829 | newStartVnode = newCh[++newStartIdx]; 830 | } else { 831 | if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx); 832 | idxInOld = oldKeyToIdx[newStartVnode.key]; 833 | if (isUndef(idxInOld)) { // New element 834 | api.insertBefore(parentElm, createElm(newStartVnode, insertedVnodeQueue), oldStartVnode.elm); 835 | newStartVnode = newCh[++newStartIdx]; 836 | } else { 837 | elmToMove = oldCh[idxInOld]; 838 | patchVnode(elmToMove, newStartVnode, insertedVnodeQueue); 839 | oldCh[idxInOld] = undefined; 840 | api.insertBefore(parentElm, elmToMove.elm, oldStartVnode.elm); 841 | newStartVnode = newCh[++newStartIdx]; 842 | } 843 | } 844 | } 845 | if (oldStartIdx > oldEndIdx) { 846 | before = isUndef(newCh[newEndIdx+1]) ? null : newCh[newEndIdx+1].elm; 847 | addVnodes(parentElm, before, newCh, newStartIdx, newEndIdx, insertedVnodeQueue); 848 | } else if (newStartIdx > newEndIdx) { 849 | removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx); 850 | } 851 | } 852 | 853 | function patchVnode(oldVnode, vnode, insertedVnodeQueue) { 854 | var i, hook; 855 | if (isDef(i = vnode.data) && isDef(hook = i.hook) && isDef(i = hook.prepatch)) { 856 | i(oldVnode, vnode); 857 | } 858 | var elm = vnode.elm = oldVnode.elm, oldCh = oldVnode.children, ch = vnode.children; 859 | if (oldVnode === vnode) return; 860 | if (!sameVnode(oldVnode, vnode)) { 861 | var parentElm = api.parentNode(oldVnode.elm); 862 | elm = createElm(vnode, insertedVnodeQueue); 863 | api.insertBefore(parentElm, elm, oldVnode.elm); 864 | removeVnodes(parentElm, [oldVnode], 0, 0); 865 | return; 866 | } 867 | if (isDef(vnode.data)) { 868 | for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode); 869 | i = vnode.data.hook; 870 | if (isDef(i) && isDef(i = i.update)) i(oldVnode, vnode); 871 | } 872 | if (isUndef(vnode.text)) { 873 | if (isDef(oldCh) && isDef(ch)) { 874 | if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue); 875 | } else if (isDef(ch)) { 876 | if (isDef(oldVnode.text)) api.setTextContent(elm, ''); 877 | addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue); 878 | } else if (isDef(oldCh)) { 879 | removeVnodes(elm, oldCh, 0, oldCh.length - 1); 880 | } else if (isDef(oldVnode.text)) { 881 | api.setTextContent(elm, ''); 882 | } 883 | } else if (oldVnode.text !== vnode.text) { 884 | api.setTextContent(elm, vnode.text); 885 | } 886 | if (isDef(hook) && isDef(i = hook.postpatch)) { 887 | i(oldVnode, vnode); 888 | } 889 | } 890 | 891 | return function(oldVnode, vnode) { 892 | var i, elm, parent; 893 | var insertedVnodeQueue = []; 894 | for (i = 0; i < cbs.pre.length; ++i) cbs.pre[i](); 895 | 896 | if (isUndef(oldVnode.sel)) { 897 | oldVnode = emptyNodeAt(oldVnode); 898 | } 899 | 900 | if (sameVnode(oldVnode, vnode)) { 901 | patchVnode(oldVnode, vnode, insertedVnodeQueue); 902 | } else { 903 | elm = oldVnode.elm; 904 | parent = api.parentNode(elm); 905 | 906 | createElm(vnode, insertedVnodeQueue); 907 | 908 | if (parent !== null) { 909 | api.insertBefore(parent, vnode.elm, api.nextSibling(elm)); 910 | removeVnodes(parent, [oldVnode], 0, 0); 911 | } 912 | } 913 | 914 | for (i = 0; i < insertedVnodeQueue.length; ++i) { 915 | insertedVnodeQueue[i].data.hook.insert(insertedVnodeQueue[i]); 916 | } 917 | for (i = 0; i < cbs.post.length; ++i) cbs.post[i](); 918 | return vnode; 919 | }; 920 | } 921 | 922 | module.exports = {init: init}; 923 | }); 924 | 925 | var snabbdom$1 = interopDefault(snabbdom); 926 | 927 | var h = createCommonjsModule(function (module) { 928 | var VNode = interopDefault(require$$1); 929 | var is = interopDefault(require$$0); 930 | 931 | function addNS(data, children, sel) { 932 | data.ns = 'http://www.w3.org/2000/svg'; 933 | 934 | if (sel !== 'foreignObject' && children !== undefined) { 935 | for (var i = 0; i < children.length; ++i) { 936 | addNS(children[i].data, children[i].children, children[i].sel); 937 | } 938 | } 939 | } 940 | 941 | module.exports = function h(sel, b, c) { 942 | var data = {}, children, text, i; 943 | if (c !== undefined) { 944 | data = b; 945 | if (is.array(c)) { children = c; } 946 | else if (is.primitive(c)) { text = c; } 947 | } else if (b !== undefined) { 948 | if (is.array(b)) { children = b; } 949 | else if (is.primitive(b)) { text = b; } 950 | else { data = b; } 951 | } 952 | if (is.array(children)) { 953 | for (i = 0; i < children.length; ++i) { 954 | if (is.primitive(children[i])) children[i] = VNode(undefined, undefined, undefined, children[i]); 955 | } 956 | } 957 | if (sel[0] === 's' && sel[1] === 'v' && sel[2] === 'g') { 958 | addNS(data, children, sel); 959 | } 960 | return VNode(sel, data, children, text, undefined); 961 | }; 962 | }); 963 | 964 | var sh = interopDefault(h); 965 | 966 | var index = createCommonjsModule(function (module, exports) { 967 | 'use strict'; 968 | 969 | Object.defineProperty(exports, '__esModule', { 970 | value: true 971 | }); 972 | var isValidString = function isValidString(param) { 973 | return typeof param === 'string' && param.length > 0; 974 | }; 975 | 976 | var startsWith = function startsWith(string, start) { 977 | return string[0] === start; 978 | }; 979 | 980 | var isSelector = function isSelector(param) { 981 | return isValidString(param) && (startsWith(param, '.') || startsWith(param, '#')); 982 | }; 983 | 984 | var node = function node(h) { 985 | return function (tagName) { 986 | return function (first) { 987 | var arguments$1 = arguments; 988 | 989 | for (var _len = arguments.length, rest = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { 990 | rest[_key - 1] = arguments$1[_key]; 991 | } 992 | 993 | if (isSelector(first)) { 994 | return h.apply(undefined, [tagName + first].concat(rest)); 995 | } else if (typeof first === 'undefined') { 996 | return h(tagName); 997 | } else { 998 | return h.apply(undefined, [tagName, first].concat(rest)); 999 | } 1000 | }; 1001 | }; 1002 | }; 1003 | 1004 | var TAG_NAMES = ['a', 'abbr', 'acronym', 'address', 'applet', 'area', 'article', 'aside', 'audio', 'b', 'base', 'basefont', 'bdi', 'bdo', 'bgsound', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'command', 'content', 'data', 'datalist', 'dd', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'embed', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'frame', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'iframe', 'image', 'img', 'input', 'ins', 'isindex', 'kbd', 'keygen', 'label', 'legend', 'li', 'link', 'listing', 'main', 'map', 'mark', 'marquee', 'math', 'menu', 'menuitem', 'meta', 'meter', 'multicol', 'nav', 'nextid', 'nobr', 'noembed', 'noframes', 'noscript', 'object', 'ol', 'optgroup', 'option', 'output', 'p', 'param', 'picture', 'plaintext', 'pre', 'progress', 'q', 'rb', 'rbc', 'rp', 'rt', 'rtc', 'ruby', 's', 'samp', 'script', 'section', 'select', 'shadow', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'svg', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'title', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr', 'xmp']; 1005 | 1006 | exports['default'] = function (h) { 1007 | var createTag = node(h); 1008 | var exported = { TAG_NAMES: TAG_NAMES, isSelector: isSelector, createTag: createTag }; 1009 | TAG_NAMES.forEach(function (n) { 1010 | exported[n] = createTag(n); 1011 | }); 1012 | return exported; 1013 | }; 1014 | 1015 | module.exports = exports['default']; 1016 | }); 1017 | 1018 | var hh = interopDefault(index); 1019 | 1020 | // 1021 | 1022 | 1023 | var init = function (modules) { 1024 | if ( modules === void 0 ) modules = []; 1025 | 1026 | return snabbdom$1.init(modules); 1027 | } 1028 | 1029 | var html = hh(sh) 1030 | 1031 | 1032 | 1033 | function vdomPatch (patch , init ) { 1034 | return first(scan(patch, init)) 1035 | } 1036 | 1037 | var div = html.div; 1038 | var p = html.p; 1039 | var button = html.button; 1040 | 1041 | var container = document.getElementById('app') 1042 | var patch = init([events]) 1043 | 1044 | // Counter component 1045 | var render = function (start, now) { 1046 | var elapsed = now - start 1047 | var ref = signalGen(); 1048 | var click = ref[0]; 1049 | var reset = ref[1]; 1050 | return [ 1051 | div([ 1052 | p(("Seconds passed: " + (Math.floor(elapsed * .001)))), 1053 | button({ on: { click: click } }, "Reset") 1054 | ]), 1055 | and(reset, delay(1000 - (elapsed % 1000))) // account for setTimeout drift 1056 | ] 1057 | } 1058 | 1059 | var reset = pipe(eventTime, hold(0)) 1060 | var counter = pipe(both(reset, time), unsplit(render)) 1061 | 1062 | // Render initial UI and get initial inputs 1063 | var ref = render(0, 0); 1064 | var vtree = ref[0]; 1065 | var input = ref[1]; 1066 | 1067 | // Append vdom updater to counter component 1068 | var update = vdomPatch(patch, patch(container, vtree)) 1069 | var updateCounter = pipe(counter, update) 1070 | 1071 | // Run it 1072 | loop(clockSession(), input, updateCounter) 1073 | 1074 | }()); -------------------------------------------------------------------------------- /examples/counter/app.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | function uncurry (f ) { 5 | return function (ref) { 6 | var a = ref[0]; 7 | var b = ref[1]; 8 | 9 | return f(a, b); 10 | } 11 | } 12 | 13 | // 14 | // Signal Function is a time varying transformation that 15 | // turns Signals of A into Signals of B. It may carry state 16 | // and evolve over time 17 | 18 | 19 | 20 | 21 | // A Step is the result of applying a SignalFunc 22 | // to an A to get a B and a new SignalFunc 23 | 24 | 25 | 26 | 27 | 28 | // SignalFunc specialized for Time type 29 | // Note: Flow can't infer generics, IOW, it can't info the 30 | // type T *later* based on the Session type provided when running 31 | // a SignalFunc. Flow needs to be able to determine T at the 32 | // instant a SignalFunc is created, but the type is only known 33 | // later when a Session is used to run the SignalFunc 34 | 35 | 36 | // SignalStep specialized for Time type 37 | // re: Flow, similarly 38 | 39 | 40 | // Simple helper to construct a Step 41 | var step = function (value, next) { return ({ value: value, next: next }); } 42 | 43 | // Lift a function into a SignalFunc 44 | function lift (f ) { 45 | return new Lift(f) 46 | } 47 | 48 | // Combine a pair of signals into a signal of C 49 | function unsplit (f ) { 50 | return lift(uncurry(f)) 51 | } 52 | 53 | var Lift = function Lift (f ) { 54 | this.f = f 55 | }; 56 | 57 | Lift.prototype.step = function step$1 (t , a ) { 58 | var f = this.f 59 | return step(f(a), this) 60 | }; 61 | 62 | // first :: SFTime a b -> SFTime [a, c] [b, c] 63 | // Apply a SignalFunc to the first signal of a pair 64 | function first (ab ) { 65 | return new First(ab) 66 | } 67 | 68 | var First = function First (ab ) { 69 | this.ab = ab 70 | }; 71 | 72 | First.prototype.step = function step$2 (t , ref ) { 73 | var a = ref[0]; 74 | var c = ref[1]; 75 | 76 | var ref$1 = this.ab.step(t, a); 77 | var b = ref$1.value; 78 | var next = ref$1.next; 79 | return step([b, c], first(next)) 80 | }; 81 | 82 | // pipe :: (SFTime a b ... SFTime y z) -> SFTime a z 83 | // Compose many Reactive transformations, left to right 84 | function pipe (ab ) { 85 | var rest = [], len = arguments.length - 1; 86 | while ( len-- > 0 ) rest[ len ] = arguments[ len + 1 ]; 87 | 88 | return rest.reduce(pipe2, ab) 89 | } 90 | 91 | // pipe2 :: SFTime a b -> SFTime b c -> SFTime a c 92 | // Compose 2 Reactive transformations left to right 93 | function pipe2 (ab , bc ) { 94 | return new Pipe(ab, bc) 95 | } 96 | 97 | var Pipe = function Pipe (ab , bc ) { 98 | this.ab = ab 99 | this.bc = bc 100 | }; 101 | 102 | Pipe.prototype.step = function step$4 (t , a ) { 103 | var ref = this.ab.step(t, a); 104 | var b = ref.value; 105 | var ab = ref.next; 106 | var ref$1 = this.bc.step(t, b); 107 | var c = ref$1.value; 108 | var bc = ref$1.next; 109 | return step(c, pipe2(ab, bc)) 110 | }; 111 | 112 | // both :: SFTime a b -> SFTime c d -> Reactive [a, c] [b, d] 113 | // Given an [a, c] input, pass a through Reactive transformation ab and 114 | // c through Reactive transformation cd to yield [b, d] 115 | function both (ab , cd ) { 116 | return new Both(ab, cd) 117 | } 118 | 119 | var Both = function Both (ab , cd ) { 120 | this.ab = ab 121 | this.cd = cd 122 | }; 123 | 124 | Both.prototype.step = function step$5 (t , ref ) { 125 | var a = ref[0]; 126 | var c = ref[1]; 127 | 128 | var ref$1 = this.ab.step(t, a); 129 | var b = ref$1.value; 130 | var anext = ref$1.next; 131 | var ref$2 = this.cd.step(t, c); 132 | var d = ref$2.value; 133 | var cnext = ref$2.next; 134 | return step([b, d], both(anext, cnext)) 135 | }; 136 | 137 | // 138 | 139 | // An event, which has a value when it occurs, and 140 | // has no value when it doesn't occur 141 | 142 | 143 | // Event non-occurrence 144 | var NoEvent = undefined 145 | 146 | // Turn Events of A instead Events of B 147 | function mapE (f ) { 148 | return function (a) { return a === undefined ? a : f(a); } 149 | } 150 | 151 | // Return the Event that occurred, preferring a1 if both occurred 152 | function mergeE (a1 , a2 ) { 153 | return a1 === undefined ? a2 : a1 154 | } 155 | 156 | // Internal helper to allow continuous value transformations to be 157 | // applied when an event occurs 158 | // TODO: Consider exposing this if it seems useful 159 | function liftE (ab ) { 160 | return new LiftE(ab) 161 | } 162 | 163 | var LiftE = function LiftE (ab) { 164 | this.ab = ab 165 | }; 166 | 167 | LiftE.prototype.step = function step (t , a ) { 168 | if (a === undefined) { 169 | return { value: NoEvent, next: this } 170 | } 171 | var ref = this.ab.step(t, a); 172 | var value = ref.value; 173 | var next = ref.next; 174 | return { value: value, next: liftE(next) } 175 | }; 176 | 177 | // Transform event values 178 | function map (f ) { 179 | return lift(mapE(f)) 180 | } 181 | 182 | // When an event occurs, make its value b 183 | function as (b ) { 184 | return map(function (_) { return b; }) 185 | } 186 | 187 | // Merge events, preferring the left in the case of 188 | // simultaneous occurrence 189 | function merge () { 190 | return unsplit(mergeE) 191 | } 192 | 193 | // Merge event SignalFuncs 194 | function or (left , right ) { 195 | return liftE(pipe(both(left, right), merge())) 196 | } 197 | 198 | // Turn an event into a stepped continuous value 199 | function hold (initial ) { 200 | return new Hold(initial) 201 | } 202 | 203 | var Hold = function Hold (value ) { 204 | this.value = value 205 | }; 206 | 207 | Hold.prototype.step = function step (t , a ) { 208 | return a === undefined 209 | ? { value: this.value, next: this } 210 | : { value: a, next: hold(a) } 211 | }; 212 | 213 | // Accumulate event 214 | function scanE (f , initial ) { 215 | return new Accum(f, initial) 216 | } 217 | 218 | // Accumulate event to a continuous value 219 | function scan (f , initial ) { 220 | return pipe(scanE(f, initial), hold(initial)) 221 | } 222 | 223 | var Accum = function Accum (f , value ) { 224 | this.f = f 225 | this.value = value 226 | }; 227 | 228 | Accum.prototype.step = function step (t , a ) { 229 | if (a === undefined) { 230 | return { value: NoEvent, next: this } 231 | } 232 | var f = this.f 233 | var value = f(this.value, a) 234 | return { value: value, next: new Accum(f, value) } 235 | }; 236 | 237 | // 238 | 239 | // A SignalGen generates values or events of a signal 240 | 241 | 242 | 243 | 244 | // Forget all future values of a SignalGen 245 | 246 | 247 | 248 | 249 | // Handle events from a SignalGen 250 | 251 | 252 | // Turn a pair of inputs into an input of pairs 253 | function and (input1 , input2 ) { 254 | return new SGVector(input1, input2) 255 | } 256 | 257 | function signalGen () { 258 | var source = new SGSource() 259 | return [function (x) { return source.handler(x); }, source] 260 | } 261 | 262 | var noop = function () {} 263 | 264 | var SGSource = function SGSource () { 265 | this.handler = noop 266 | }; 267 | 268 | SGSource.prototype.listen = function listen (handler ) { 269 | this.handler = handler 270 | return new ForgetSGSource(this) 271 | }; 272 | 273 | var ForgetSGSource = function ForgetSGSource (source ) { 274 | this.source = source 275 | }; 276 | 277 | ForgetSGSource.prototype.forget = function forget () { 278 | this.source.handler = noop 279 | }; 280 | 281 | var emptySignalVector = [NoEvent, NoEvent] 282 | 283 | var empty = function (input) { return input instanceof SGVector ? emptySignalVector : NoEvent; } 284 | 285 | var SGVector = function SGVector (first , second ) { 286 | this.first = first 287 | this.second = second 288 | }; 289 | 290 | SGVector.prototype.listen = function listen (handler ) { 291 | var this$1 = this; 292 | 293 | var forgetFirst = this.first.listen(function (a) { return handler([a, empty(this$1.second)]); }) 294 | var forgetSecond = this.second.listen(function (a) { return handler([empty(this$1.first), a]); }) 295 | return new ForgetSGVector(forgetFirst, forgetSecond) 296 | }; 297 | 298 | var ForgetSGVector = function ForgetSGVector (forgetFirst , forgetSecond ) { 299 | this.forgetFirst = forgetFirst 300 | this.forgetSecond = forgetSecond 301 | }; 302 | 303 | ForgetSGVector.prototype.forget = function forget () { 304 | this.forgetFirst.forget() 305 | this.forgetSecond.forget() 306 | }; 307 | 308 | // Session that yields a time delta from its start time at each step 309 | var clockSession = function () { return new ClockSession(Date.now()); } 310 | 311 | var ClockSession = function ClockSession (start ) { 312 | this.start = start 313 | }; 314 | 315 | ClockSession.prototype.step = function step () { 316 | return { sample: Date.now() - this.start, nextSession: new ClockSession(this.start) } 317 | }; 318 | 319 | // 320 | 321 | 322 | 323 | 324 | function loop (session , input , sf ) { 325 | var forget = input.listen(function (a) { 326 | var ref = session.step(); 327 | var sample = ref.sample; 328 | var nextSession = ref.nextSession; 329 | var ref$1 = sf.step(sample, a); 330 | var ref$1_value = ref$1.value; 331 | var _ = ref$1_value[0]; 332 | var nextInput = ref$1_value[1]; 333 | var next = ref$1.next; // eslint-disable-line no-unused-vars 334 | forget = switchInput(nextSession, nextInput, next, forget) 335 | }) 336 | 337 | return forget 338 | } 339 | 340 | var switchInput = function (session, input, sf, forget) { 341 | forget.forget() 342 | return loop(session, input, sf) 343 | } 344 | 345 | function interopDefault(ex) { 346 | return ex && typeof ex === 'object' && 'default' in ex ? ex['default'] : ex; 347 | } 348 | 349 | function createCommonjsModule(fn, module) { 350 | return module = { exports: {} }, fn(module, module.exports), module.exports; 351 | } 352 | 353 | var eventlisteners = createCommonjsModule(function (module) { 354 | function invokeHandler(handler, vnode, event) { 355 | if (typeof handler === "function") { 356 | // call function handler 357 | handler.call(vnode, event, vnode); 358 | } else if (typeof handler === "object") { 359 | // call handler with arguments 360 | if (typeof handler[0] === "function") { 361 | // special case for single argument for performance 362 | if (handler.length === 2) { 363 | handler[0].call(vnode, handler[1], event, vnode); 364 | } else { 365 | var args = handler.slice(1); 366 | args.push(event); 367 | args.push(vnode); 368 | handler[0].apply(vnode, args); 369 | } 370 | } else { 371 | // call multiple handlers 372 | for (var i = 0; i < handler.length; i++) { 373 | invokeHandler(handler[i]); 374 | } 375 | } 376 | } 377 | } 378 | 379 | function handleEvent(event, vnode) { 380 | var name = event.type, 381 | on = vnode.data.on; 382 | 383 | // call event handler(s) if exists 384 | if (on && on[name]) { 385 | invokeHandler(on[name], vnode, event); 386 | } 387 | } 388 | 389 | function createListener() { 390 | return function handler(event) { 391 | handleEvent(event, handler.vnode); 392 | } 393 | } 394 | 395 | function updateEventListeners(oldVnode, vnode) { 396 | var oldOn = oldVnode.data.on, 397 | oldListener = oldVnode.listener, 398 | oldElm = oldVnode.elm, 399 | on = vnode && vnode.data.on, 400 | elm = vnode && vnode.elm, 401 | name; 402 | 403 | // optimization for reused immutable handlers 404 | if (oldOn === on) { 405 | return; 406 | } 407 | 408 | // remove existing listeners which no longer used 409 | if (oldOn && oldListener) { 410 | // if element changed or deleted we remove all existing listeners unconditionally 411 | if (!on) { 412 | for (name in oldOn) { 413 | // remove listener if element was changed or existing listeners removed 414 | oldElm.removeEventListener(name, oldListener, false); 415 | } 416 | } else { 417 | for (name in oldOn) { 418 | // remove listener if existing listener removed 419 | if (!on[name]) { 420 | oldElm.removeEventListener(name, oldListener, false); 421 | } 422 | } 423 | } 424 | } 425 | 426 | // add new listeners which has not already attached 427 | if (on) { 428 | // reuse existing listener or create new 429 | var listener = vnode.listener = oldVnode.listener || createListener(); 430 | // update vnode for listener 431 | listener.vnode = vnode; 432 | 433 | // if element changed or added we add all needed listeners unconditionally 434 | if (!oldOn) { 435 | for (name in on) { 436 | // add listener if element was changed or new listeners added 437 | elm.addEventListener(name, listener, false); 438 | } 439 | } else { 440 | for (name in on) { 441 | // add listener if new listener added 442 | if (!oldOn[name]) { 443 | elm.addEventListener(name, listener, false); 444 | } 445 | } 446 | } 447 | } 448 | } 449 | 450 | module.exports = { 451 | create: updateEventListeners, 452 | update: updateEventListeners, 453 | destroy: updateEventListeners 454 | }; 455 | }); 456 | 457 | var events = interopDefault(eventlisteners); 458 | 459 | var attributes = createCommonjsModule(function (module) { 460 | var booleanAttrs = ["allowfullscreen", "async", "autofocus", "autoplay", "checked", "compact", "controls", "declare", 461 | "default", "defaultchecked", "defaultmuted", "defaultselected", "defer", "disabled", "draggable", 462 | "enabled", "formnovalidate", "hidden", "indeterminate", "inert", "ismap", "itemscope", "loop", "multiple", 463 | "muted", "nohref", "noresize", "noshade", "novalidate", "nowrap", "open", "pauseonexit", "readonly", 464 | "required", "reversed", "scoped", "seamless", "selected", "sortable", "spellcheck", "translate", 465 | "truespeed", "typemustmatch", "visible"]; 466 | 467 | var booleanAttrsDict = {}; 468 | for(var i=0, len = booleanAttrs.length; i < len; i++) { 469 | booleanAttrsDict[booleanAttrs[i]] = true; 470 | } 471 | 472 | function updateAttrs(oldVnode, vnode) { 473 | var key, cur, old, elm = vnode.elm, 474 | oldAttrs = oldVnode.data.attrs, attrs = vnode.data.attrs; 475 | 476 | if (!oldAttrs && !attrs) return; 477 | oldAttrs = oldAttrs || {}; 478 | attrs = attrs || {}; 479 | 480 | // update modified attributes, add new attributes 481 | for (key in attrs) { 482 | cur = attrs[key]; 483 | old = oldAttrs[key]; 484 | if (old !== cur) { 485 | // TODO: add support to namespaced attributes (setAttributeNS) 486 | if(!cur && booleanAttrsDict[key]) 487 | elm.removeAttribute(key); 488 | else 489 | elm.setAttribute(key, cur); 490 | } 491 | } 492 | //remove removed attributes 493 | // use `in` operator since the previous `for` iteration uses it (.i.e. add even attributes with undefined value) 494 | // the other option is to remove all attributes with value == undefined 495 | for (key in oldAttrs) { 496 | if (!(key in attrs)) { 497 | elm.removeAttribute(key); 498 | } 499 | } 500 | } 501 | 502 | module.exports = {create: updateAttrs, update: updateAttrs}; 503 | }); 504 | 505 | interopDefault(attributes); 506 | 507 | var props = createCommonjsModule(function (module) { 508 | function updateProps(oldVnode, vnode) { 509 | var key, cur, old, elm = vnode.elm, 510 | oldProps = oldVnode.data.props, props = vnode.data.props; 511 | 512 | if (!oldProps && !props) return; 513 | oldProps = oldProps || {}; 514 | props = props || {}; 515 | 516 | for (key in oldProps) { 517 | if (!props[key]) { 518 | delete elm[key]; 519 | } 520 | } 521 | for (key in props) { 522 | cur = props[key]; 523 | old = oldProps[key]; 524 | if (old !== cur && (key !== 'value' || elm[key] !== cur)) { 525 | elm[key] = cur; 526 | } 527 | } 528 | } 529 | 530 | module.exports = {create: updateProps, update: updateProps}; 531 | }); 532 | 533 | interopDefault(props); 534 | 535 | var _class = createCommonjsModule(function (module) { 536 | function updateClass(oldVnode, vnode) { 537 | var cur, name, elm = vnode.elm, 538 | oldClass = oldVnode.data.class, 539 | klass = vnode.data.class; 540 | 541 | if (!oldClass && !klass) return; 542 | oldClass = oldClass || {}; 543 | klass = klass || {}; 544 | 545 | for (name in oldClass) { 546 | if (!klass[name]) { 547 | elm.classList.remove(name); 548 | } 549 | } 550 | for (name in klass) { 551 | cur = klass[name]; 552 | if (cur !== oldClass[name]) { 553 | elm.classList[cur ? 'add' : 'remove'](name); 554 | } 555 | } 556 | } 557 | 558 | module.exports = {create: updateClass, update: updateClass}; 559 | }); 560 | 561 | interopDefault(_class); 562 | 563 | var vnode = createCommonjsModule(function (module) { 564 | module.exports = function(sel, data, children, text, elm) { 565 | var key = data === undefined ? undefined : data.key; 566 | return {sel: sel, data: data, children: children, 567 | text: text, elm: elm, key: key}; 568 | }; 569 | }); 570 | 571 | var vnode$1 = interopDefault(vnode); 572 | 573 | 574 | var require$$1 = Object.freeze({ 575 | default: vnode$1 576 | }); 577 | 578 | var is = createCommonjsModule(function (module) { 579 | module.exports = { 580 | array: Array.isArray, 581 | primitive: function(s) { return typeof s === 'string' || typeof s === 'number'; }, 582 | }; 583 | }); 584 | 585 | var is$1 = interopDefault(is); 586 | var array = is.array; 587 | var primitive = is.primitive; 588 | 589 | var require$$0 = Object.freeze({ 590 | default: is$1, 591 | array: array, 592 | primitive: primitive 593 | }); 594 | 595 | var htmldomapi = createCommonjsModule(function (module) { 596 | function createElement(tagName){ 597 | return document.createElement(tagName); 598 | } 599 | 600 | function createElementNS(namespaceURI, qualifiedName){ 601 | return document.createElementNS(namespaceURI, qualifiedName); 602 | } 603 | 604 | function createTextNode(text){ 605 | return document.createTextNode(text); 606 | } 607 | 608 | 609 | function insertBefore(parentNode, newNode, referenceNode){ 610 | parentNode.insertBefore(newNode, referenceNode); 611 | } 612 | 613 | 614 | function removeChild(node, child){ 615 | node.removeChild(child); 616 | } 617 | 618 | function appendChild(node, child){ 619 | node.appendChild(child); 620 | } 621 | 622 | function parentNode(node){ 623 | return node.parentElement; 624 | } 625 | 626 | function nextSibling(node){ 627 | return node.nextSibling; 628 | } 629 | 630 | function tagName(node){ 631 | return node.tagName; 632 | } 633 | 634 | function setTextContent(node, text){ 635 | node.textContent = text; 636 | } 637 | 638 | module.exports = { 639 | createElement: createElement, 640 | createElementNS: createElementNS, 641 | createTextNode: createTextNode, 642 | appendChild: appendChild, 643 | removeChild: removeChild, 644 | insertBefore: insertBefore, 645 | parentNode: parentNode, 646 | nextSibling: nextSibling, 647 | tagName: tagName, 648 | setTextContent: setTextContent 649 | }; 650 | }); 651 | 652 | var htmldomapi$1 = interopDefault(htmldomapi); 653 | var createElement = htmldomapi.createElement; 654 | var createElementNS = htmldomapi.createElementNS; 655 | var createTextNode = htmldomapi.createTextNode; 656 | var appendChild = htmldomapi.appendChild; 657 | var removeChild = htmldomapi.removeChild; 658 | var insertBefore = htmldomapi.insertBefore; 659 | var parentNode = htmldomapi.parentNode; 660 | var nextSibling = htmldomapi.nextSibling; 661 | var tagName = htmldomapi.tagName; 662 | var setTextContent = htmldomapi.setTextContent; 663 | 664 | var require$$0$1 = Object.freeze({ 665 | default: htmldomapi$1, 666 | createElement: createElement, 667 | createElementNS: createElementNS, 668 | createTextNode: createTextNode, 669 | appendChild: appendChild, 670 | removeChild: removeChild, 671 | insertBefore: insertBefore, 672 | parentNode: parentNode, 673 | nextSibling: nextSibling, 674 | tagName: tagName, 675 | setTextContent: setTextContent 676 | }); 677 | 678 | var snabbdom = createCommonjsModule(function (module) { 679 | // jshint newcap: false 680 | /* global require, module, document, Node */ 681 | 'use strict'; 682 | 683 | var VNode = interopDefault(require$$1); 684 | var is = interopDefault(require$$0); 685 | var domApi = interopDefault(require$$0$1); 686 | 687 | function isUndef(s) { return s === undefined; } 688 | function isDef(s) { return s !== undefined; } 689 | 690 | var emptyNode = VNode('', {}, [], undefined, undefined); 691 | 692 | function sameVnode(vnode1, vnode2) { 693 | return vnode1.key === vnode2.key && vnode1.sel === vnode2.sel; 694 | } 695 | 696 | function createKeyToOldIdx(children, beginIdx, endIdx) { 697 | var i, map = {}, key; 698 | for (i = beginIdx; i <= endIdx; ++i) { 699 | key = children[i].key; 700 | if (isDef(key)) map[key] = i; 701 | } 702 | return map; 703 | } 704 | 705 | var hooks = ['create', 'update', 'remove', 'destroy', 'pre', 'post']; 706 | 707 | function init(modules, api) { 708 | var i, j, cbs = {}; 709 | 710 | if (isUndef(api)) api = domApi; 711 | 712 | for (i = 0; i < hooks.length; ++i) { 713 | cbs[hooks[i]] = []; 714 | for (j = 0; j < modules.length; ++j) { 715 | if (modules[j][hooks[i]] !== undefined) cbs[hooks[i]].push(modules[j][hooks[i]]); 716 | } 717 | } 718 | 719 | function emptyNodeAt(elm) { 720 | var id = elm.id ? '#' + elm.id : ''; 721 | var c = elm.className ? '.' + elm.className.split(' ').join('.') : ''; 722 | return VNode(api.tagName(elm).toLowerCase() + id + c, {}, [], undefined, elm); 723 | } 724 | 725 | function createRmCb(childElm, listeners) { 726 | return function() { 727 | if (--listeners === 0) { 728 | var parent = api.parentNode(childElm); 729 | api.removeChild(parent, childElm); 730 | } 731 | }; 732 | } 733 | 734 | function createElm(vnode, insertedVnodeQueue) { 735 | var i, data = vnode.data; 736 | if (isDef(data)) { 737 | if (isDef(i = data.hook) && isDef(i = i.init)) { 738 | i(vnode); 739 | data = vnode.data; 740 | } 741 | } 742 | var elm, children = vnode.children, sel = vnode.sel; 743 | if (isDef(sel)) { 744 | // Parse selector 745 | var hashIdx = sel.indexOf('#'); 746 | var dotIdx = sel.indexOf('.', hashIdx); 747 | var hash = hashIdx > 0 ? hashIdx : sel.length; 748 | var dot = dotIdx > 0 ? dotIdx : sel.length; 749 | var tag = hashIdx !== -1 || dotIdx !== -1 ? sel.slice(0, Math.min(hash, dot)) : sel; 750 | elm = vnode.elm = isDef(data) && isDef(i = data.ns) ? api.createElementNS(i, tag) 751 | : api.createElement(tag); 752 | if (hash < dot) elm.id = sel.slice(hash + 1, dot); 753 | if (dotIdx > 0) elm.className = sel.slice(dot + 1).replace(/\./g, ' '); 754 | if (is.array(children)) { 755 | for (i = 0; i < children.length; ++i) { 756 | api.appendChild(elm, createElm(children[i], insertedVnodeQueue)); 757 | } 758 | } else if (is.primitive(vnode.text)) { 759 | api.appendChild(elm, api.createTextNode(vnode.text)); 760 | } 761 | for (i = 0; i < cbs.create.length; ++i) cbs.create[i](emptyNode, vnode); 762 | i = vnode.data.hook; // Reuse variable 763 | if (isDef(i)) { 764 | if (i.create) i.create(emptyNode, vnode); 765 | if (i.insert) insertedVnodeQueue.push(vnode); 766 | } 767 | } else { 768 | elm = vnode.elm = api.createTextNode(vnode.text); 769 | } 770 | return vnode.elm; 771 | } 772 | 773 | function addVnodes(parentElm, before, vnodes, startIdx, endIdx, insertedVnodeQueue) { 774 | for (; startIdx <= endIdx; ++startIdx) { 775 | api.insertBefore(parentElm, createElm(vnodes[startIdx], insertedVnodeQueue), before); 776 | } 777 | } 778 | 779 | function invokeDestroyHook(vnode) { 780 | var i, j, data = vnode.data; 781 | if (isDef(data)) { 782 | if (isDef(i = data.hook) && isDef(i = i.destroy)) i(vnode); 783 | for (i = 0; i < cbs.destroy.length; ++i) cbs.destroy[i](vnode); 784 | if (isDef(i = vnode.children)) { 785 | for (j = 0; j < vnode.children.length; ++j) { 786 | invokeDestroyHook(vnode.children[j]); 787 | } 788 | } 789 | } 790 | } 791 | 792 | function removeVnodes(parentElm, vnodes, startIdx, endIdx) { 793 | for (; startIdx <= endIdx; ++startIdx) { 794 | var i, listeners, rm, ch = vnodes[startIdx]; 795 | if (isDef(ch)) { 796 | if (isDef(ch.sel)) { 797 | invokeDestroyHook(ch); 798 | listeners = cbs.remove.length + 1; 799 | rm = createRmCb(ch.elm, listeners); 800 | for (i = 0; i < cbs.remove.length; ++i) cbs.remove[i](ch, rm); 801 | if (isDef(i = ch.data) && isDef(i = i.hook) && isDef(i = i.remove)) { 802 | i(ch, rm); 803 | } else { 804 | rm(); 805 | } 806 | } else { // Text node 807 | api.removeChild(parentElm, ch.elm); 808 | } 809 | } 810 | } 811 | } 812 | 813 | function updateChildren(parentElm, oldCh, newCh, insertedVnodeQueue) { 814 | var oldStartIdx = 0, newStartIdx = 0; 815 | var oldEndIdx = oldCh.length - 1; 816 | var oldStartVnode = oldCh[0]; 817 | var oldEndVnode = oldCh[oldEndIdx]; 818 | var newEndIdx = newCh.length - 1; 819 | var newStartVnode = newCh[0]; 820 | var newEndVnode = newCh[newEndIdx]; 821 | var oldKeyToIdx, idxInOld, elmToMove, before; 822 | 823 | while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) { 824 | if (isUndef(oldStartVnode)) { 825 | oldStartVnode = oldCh[++oldStartIdx]; // Vnode has been moved left 826 | } else if (isUndef(oldEndVnode)) { 827 | oldEndVnode = oldCh[--oldEndIdx]; 828 | } else if (sameVnode(oldStartVnode, newStartVnode)) { 829 | patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue); 830 | oldStartVnode = oldCh[++oldStartIdx]; 831 | newStartVnode = newCh[++newStartIdx]; 832 | } else if (sameVnode(oldEndVnode, newEndVnode)) { 833 | patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue); 834 | oldEndVnode = oldCh[--oldEndIdx]; 835 | newEndVnode = newCh[--newEndIdx]; 836 | } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right 837 | patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue); 838 | api.insertBefore(parentElm, oldStartVnode.elm, api.nextSibling(oldEndVnode.elm)); 839 | oldStartVnode = oldCh[++oldStartIdx]; 840 | newEndVnode = newCh[--newEndIdx]; 841 | } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left 842 | patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue); 843 | api.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm); 844 | oldEndVnode = oldCh[--oldEndIdx]; 845 | newStartVnode = newCh[++newStartIdx]; 846 | } else { 847 | if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx); 848 | idxInOld = oldKeyToIdx[newStartVnode.key]; 849 | if (isUndef(idxInOld)) { // New element 850 | api.insertBefore(parentElm, createElm(newStartVnode, insertedVnodeQueue), oldStartVnode.elm); 851 | newStartVnode = newCh[++newStartIdx]; 852 | } else { 853 | elmToMove = oldCh[idxInOld]; 854 | patchVnode(elmToMove, newStartVnode, insertedVnodeQueue); 855 | oldCh[idxInOld] = undefined; 856 | api.insertBefore(parentElm, elmToMove.elm, oldStartVnode.elm); 857 | newStartVnode = newCh[++newStartIdx]; 858 | } 859 | } 860 | } 861 | if (oldStartIdx > oldEndIdx) { 862 | before = isUndef(newCh[newEndIdx+1]) ? null : newCh[newEndIdx+1].elm; 863 | addVnodes(parentElm, before, newCh, newStartIdx, newEndIdx, insertedVnodeQueue); 864 | } else if (newStartIdx > newEndIdx) { 865 | removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx); 866 | } 867 | } 868 | 869 | function patchVnode(oldVnode, vnode, insertedVnodeQueue) { 870 | var i, hook; 871 | if (isDef(i = vnode.data) && isDef(hook = i.hook) && isDef(i = hook.prepatch)) { 872 | i(oldVnode, vnode); 873 | } 874 | var elm = vnode.elm = oldVnode.elm, oldCh = oldVnode.children, ch = vnode.children; 875 | if (oldVnode === vnode) return; 876 | if (!sameVnode(oldVnode, vnode)) { 877 | var parentElm = api.parentNode(oldVnode.elm); 878 | elm = createElm(vnode, insertedVnodeQueue); 879 | api.insertBefore(parentElm, elm, oldVnode.elm); 880 | removeVnodes(parentElm, [oldVnode], 0, 0); 881 | return; 882 | } 883 | if (isDef(vnode.data)) { 884 | for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode); 885 | i = vnode.data.hook; 886 | if (isDef(i) && isDef(i = i.update)) i(oldVnode, vnode); 887 | } 888 | if (isUndef(vnode.text)) { 889 | if (isDef(oldCh) && isDef(ch)) { 890 | if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue); 891 | } else if (isDef(ch)) { 892 | if (isDef(oldVnode.text)) api.setTextContent(elm, ''); 893 | addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue); 894 | } else if (isDef(oldCh)) { 895 | removeVnodes(elm, oldCh, 0, oldCh.length - 1); 896 | } else if (isDef(oldVnode.text)) { 897 | api.setTextContent(elm, ''); 898 | } 899 | } else if (oldVnode.text !== vnode.text) { 900 | api.setTextContent(elm, vnode.text); 901 | } 902 | if (isDef(hook) && isDef(i = hook.postpatch)) { 903 | i(oldVnode, vnode); 904 | } 905 | } 906 | 907 | return function(oldVnode, vnode) { 908 | var i, elm, parent; 909 | var insertedVnodeQueue = []; 910 | for (i = 0; i < cbs.pre.length; ++i) cbs.pre[i](); 911 | 912 | if (isUndef(oldVnode.sel)) { 913 | oldVnode = emptyNodeAt(oldVnode); 914 | } 915 | 916 | if (sameVnode(oldVnode, vnode)) { 917 | patchVnode(oldVnode, vnode, insertedVnodeQueue); 918 | } else { 919 | elm = oldVnode.elm; 920 | parent = api.parentNode(elm); 921 | 922 | createElm(vnode, insertedVnodeQueue); 923 | 924 | if (parent !== null) { 925 | api.insertBefore(parent, vnode.elm, api.nextSibling(elm)); 926 | removeVnodes(parent, [oldVnode], 0, 0); 927 | } 928 | } 929 | 930 | for (i = 0; i < insertedVnodeQueue.length; ++i) { 931 | insertedVnodeQueue[i].data.hook.insert(insertedVnodeQueue[i]); 932 | } 933 | for (i = 0; i < cbs.post.length; ++i) cbs.post[i](); 934 | return vnode; 935 | }; 936 | } 937 | 938 | module.exports = {init: init}; 939 | }); 940 | 941 | var snabbdom$1 = interopDefault(snabbdom); 942 | 943 | var h = createCommonjsModule(function (module) { 944 | var VNode = interopDefault(require$$1); 945 | var is = interopDefault(require$$0); 946 | 947 | function addNS(data, children, sel) { 948 | data.ns = 'http://www.w3.org/2000/svg'; 949 | 950 | if (sel !== 'foreignObject' && children !== undefined) { 951 | for (var i = 0; i < children.length; ++i) { 952 | addNS(children[i].data, children[i].children, children[i].sel); 953 | } 954 | } 955 | } 956 | 957 | module.exports = function h(sel, b, c) { 958 | var data = {}, children, text, i; 959 | if (c !== undefined) { 960 | data = b; 961 | if (is.array(c)) { children = c; } 962 | else if (is.primitive(c)) { text = c; } 963 | } else if (b !== undefined) { 964 | if (is.array(b)) { children = b; } 965 | else if (is.primitive(b)) { text = b; } 966 | else { data = b; } 967 | } 968 | if (is.array(children)) { 969 | for (i = 0; i < children.length; ++i) { 970 | if (is.primitive(children[i])) children[i] = VNode(undefined, undefined, undefined, children[i]); 971 | } 972 | } 973 | if (sel[0] === 's' && sel[1] === 'v' && sel[2] === 'g') { 974 | addNS(data, children, sel); 975 | } 976 | return VNode(sel, data, children, text, undefined); 977 | }; 978 | }); 979 | 980 | var sh = interopDefault(h); 981 | 982 | var index = createCommonjsModule(function (module, exports) { 983 | 'use strict'; 984 | 985 | Object.defineProperty(exports, '__esModule', { 986 | value: true 987 | }); 988 | var isValidString = function isValidString(param) { 989 | return typeof param === 'string' && param.length > 0; 990 | }; 991 | 992 | var startsWith = function startsWith(string, start) { 993 | return string[0] === start; 994 | }; 995 | 996 | var isSelector = function isSelector(param) { 997 | return isValidString(param) && (startsWith(param, '.') || startsWith(param, '#')); 998 | }; 999 | 1000 | var node = function node(h) { 1001 | return function (tagName) { 1002 | return function (first) { 1003 | var arguments$1 = arguments; 1004 | 1005 | for (var _len = arguments.length, rest = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { 1006 | rest[_key - 1] = arguments$1[_key]; 1007 | } 1008 | 1009 | if (isSelector(first)) { 1010 | return h.apply(undefined, [tagName + first].concat(rest)); 1011 | } else if (typeof first === 'undefined') { 1012 | return h(tagName); 1013 | } else { 1014 | return h.apply(undefined, [tagName, first].concat(rest)); 1015 | } 1016 | }; 1017 | }; 1018 | }; 1019 | 1020 | var TAG_NAMES = ['a', 'abbr', 'acronym', 'address', 'applet', 'area', 'article', 'aside', 'audio', 'b', 'base', 'basefont', 'bdi', 'bdo', 'bgsound', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'command', 'content', 'data', 'datalist', 'dd', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'embed', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'frame', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'iframe', 'image', 'img', 'input', 'ins', 'isindex', 'kbd', 'keygen', 'label', 'legend', 'li', 'link', 'listing', 'main', 'map', 'mark', 'marquee', 'math', 'menu', 'menuitem', 'meta', 'meter', 'multicol', 'nav', 'nextid', 'nobr', 'noembed', 'noframes', 'noscript', 'object', 'ol', 'optgroup', 'option', 'output', 'p', 'param', 'picture', 'plaintext', 'pre', 'progress', 'q', 'rb', 'rbc', 'rp', 'rt', 'rtc', 'ruby', 's', 'samp', 'script', 'section', 'select', 'shadow', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'svg', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'title', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr', 'xmp']; 1021 | 1022 | exports['default'] = function (h) { 1023 | var createTag = node(h); 1024 | var exported = { TAG_NAMES: TAG_NAMES, isSelector: isSelector, createTag: createTag }; 1025 | TAG_NAMES.forEach(function (n) { 1026 | exported[n] = createTag(n); 1027 | }); 1028 | return exported; 1029 | }; 1030 | 1031 | module.exports = exports['default']; 1032 | }); 1033 | 1034 | var hh = interopDefault(index); 1035 | 1036 | // 1037 | 1038 | 1039 | var init = function (modules) { 1040 | if ( modules === void 0 ) modules = []; 1041 | 1042 | return snabbdom$1.init(modules); 1043 | } 1044 | 1045 | var html = hh(sh) 1046 | 1047 | 1048 | 1049 | function vdomPatch (patch , init ) { 1050 | return first(scan(patch, init)) 1051 | } 1052 | 1053 | // 1054 | var div = html.div; 1055 | var p = html.p; 1056 | var button = html.button; 1057 | 1058 | var container = document.getElementById('app') 1059 | var patch = init([events]) 1060 | 1061 | var ref = signalGen(); 1062 | var inc = ref[0]; 1063 | var incInput = ref[1]; 1064 | var ref$1 = signalGen(); 1065 | var dec = ref$1[0]; 1066 | var decInput = ref$1[1]; 1067 | 1068 | var render = function (value) { return [div('#app', [ 1069 | p(value), 1070 | button({ on: { click: dec } }, '-'), 1071 | button({ on: { click: inc } }, '+') 1072 | ]), and(incInput, decInput)]; } 1073 | 1074 | var add = function (a, b) { return a + b; } 1075 | var counter = pipe(or(as(1), as(-1)), scan(add, 0)) 1076 | 1077 | var ref$2 = render(0); 1078 | var vtree = ref$2[0]; 1079 | var inputs = ref$2[1]; 1080 | var update = vdomPatch(patch, patch(container, vtree)); 1081 | var runCounter = pipe(counter, lift(render), update) 1082 | 1083 | loop(clockSession(), inputs, runCounter) 1084 | 1085 | }()); -------------------------------------------------------------------------------- /examples/mouse-position/app.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | // 5 | 6 | // Turn a single value into a pair 7 | function dup (a ) { 8 | return pair(a, a) 9 | } 10 | 11 | function pair (a , b ) { 12 | return [a, b] 13 | } 14 | 15 | function uncurry (f ) { 16 | return function (ref) { 17 | var a = ref[0]; 18 | var b = ref[1]; 19 | 20 | return f(a, b); 21 | } 22 | } 23 | 24 | // 25 | // Signal Function is a time varying transformation that 26 | // turns Signals of A into Signals of B. It may carry state 27 | // and evolve over time 28 | 29 | 30 | 31 | 32 | // A Step is the result of applying a SignalFunc 33 | // to an A to get a B and a new SignalFunc 34 | 35 | 36 | 37 | 38 | 39 | // SignalFunc specialized for Time type 40 | // Note: Flow can't infer generics, IOW, it can't info the 41 | // type T *later* based on the Session type provided when running 42 | // a SignalFunc. Flow needs to be able to determine T at the 43 | // instant a SignalFunc is created, but the type is only known 44 | // later when a Session is used to run the SignalFunc 45 | 46 | 47 | // SignalStep specialized for Time type 48 | // re: Flow, similarly 49 | 50 | 51 | // Simple helper to construct a Step 52 | var step = function (value, next) { return ({ value: value, next: next }); } 53 | 54 | // Lift a function into a SignalFunc 55 | function lift (f ) { 56 | return new Lift(f) 57 | } 58 | 59 | // Combine a pair of signals into a signal of C 60 | function unsplit (f ) { 61 | return lift(uncurry(f)) 62 | } 63 | 64 | // SignalFunc that runs any signal into a signal whose 65 | // value is always a 66 | // TODO: Give this its own type so it can be composed efficiently 67 | function always (a ) { 68 | return lift(constant(a)) 69 | } 70 | 71 | function constant (a ) { 72 | return function (_) { return a; } 73 | } 74 | 75 | var Lift = function Lift (f ) { 76 | this.f = f 77 | }; 78 | 79 | Lift.prototype.step = function step$1 (t , a ) { 80 | var f = this.f 81 | return step(f(a), this) 82 | }; 83 | 84 | // first :: SFTime a b -> SFTime [a, c] [b, c] 85 | // Apply a SignalFunc to the first signal of a pair 86 | function first (ab ) { 87 | return new First(ab) 88 | } 89 | 90 | var First = function First (ab ) { 91 | this.ab = ab 92 | }; 93 | 94 | First.prototype.step = function step$2 (t , ref ) { 95 | var a = ref[0]; 96 | var c = ref[1]; 97 | 98 | var ref$1 = this.ab.step(t, a); 99 | var b = ref$1.value; 100 | var next = ref$1.next; 101 | return step([b, c], first(next)) 102 | }; 103 | 104 | // pipe :: (SFTime a b ... SFTime y z) -> SFTime a z 105 | // Compose many Reactive transformations, left to right 106 | function pipe (ab ) { 107 | var rest = [], len = arguments.length - 1; 108 | while ( len-- > 0 ) rest[ len ] = arguments[ len + 1 ]; 109 | 110 | return rest.reduce(pipe2, ab) 111 | } 112 | 113 | // pipe2 :: SFTime a b -> SFTime b c -> SFTime a c 114 | // Compose 2 Reactive transformations left to right 115 | function pipe2 (ab , bc ) { 116 | return new Pipe(ab, bc) 117 | } 118 | 119 | function lmap (fab , bc ) { 120 | return pipe2(lift(fab), bc) 121 | } 122 | 123 | var Pipe = function Pipe (ab , bc ) { 124 | this.ab = ab 125 | this.bc = bc 126 | }; 127 | 128 | Pipe.prototype.step = function step$4 (t , a ) { 129 | var ref = this.ab.step(t, a); 130 | var b = ref.value; 131 | var ab = ref.next; 132 | var ref$1 = this.bc.step(t, b); 133 | var c = ref$1.value; 134 | var bc = ref$1.next; 135 | return step(c, pipe2(ab, bc)) 136 | }; 137 | 138 | // split :: SFTime a b -> SFTime a c -> SFTime [b, c] 139 | // Duplicates input a and pass it through Reactive transformations 140 | // ab and ac to yield [b, c] 141 | function split (ab , ac ) { 142 | return lmap(dup, both(ab, ac)) 143 | } 144 | 145 | // both :: SFTime a b -> SFTime c d -> Reactive [a, c] [b, d] 146 | // Given an [a, c] input, pass a through Reactive transformation ab and 147 | // c through Reactive transformation cd to yield [b, d] 148 | function both (ab , cd ) { 149 | return new Both(ab, cd) 150 | } 151 | 152 | var Both = function Both (ab , cd ) { 153 | this.ab = ab 154 | this.cd = cd 155 | }; 156 | 157 | Both.prototype.step = function step$5 (t , ref ) { 158 | var a = ref[0]; 159 | var c = ref[1]; 160 | 161 | var ref$1 = this.ab.step(t, a); 162 | var b = ref$1.value; 163 | var anext = ref$1.next; 164 | var ref$2 = this.cd.step(t, c); 165 | var d = ref$2.value; 166 | var cnext = ref$2.next; 167 | return step([b, d], both(anext, cnext)) 168 | }; 169 | 170 | // 171 | 172 | // An event, which has a value when it occurs, and 173 | // has no value when it doesn't occur 174 | 175 | 176 | // Event non-occurrence 177 | var NoEvent = undefined 178 | 179 | // Turn Events of A instead Events of B 180 | function mapE (f ) { 181 | return function (a) { return a === undefined ? a : f(a); } 182 | } 183 | 184 | // Transform event values 185 | function map (f ) { 186 | return lift(mapE(f)) 187 | } 188 | 189 | // Turn an event into a stepped continuous value 190 | function hold (initial ) { 191 | return new Hold(initial) 192 | } 193 | 194 | var Hold = function Hold (value ) { 195 | this.value = value 196 | }; 197 | 198 | Hold.prototype.step = function step (t , a ) { 199 | return a === undefined 200 | ? { value: this.value, next: this } 201 | : { value: a, next: hold(a) } 202 | }; 203 | 204 | // Accumulate event 205 | function scanE (f , initial ) { 206 | return new Accum(f, initial) 207 | } 208 | 209 | // Accumulate event to a continuous value 210 | function scan (f , initial ) { 211 | return pipe(scanE(f, initial), hold(initial)) 212 | } 213 | 214 | var Accum = function Accum (f , value ) { 215 | this.f = f 216 | this.value = value 217 | }; 218 | 219 | Accum.prototype.step = function step (t , a ) { 220 | if (a === undefined) { 221 | return { value: NoEvent, next: this } 222 | } 223 | var f = this.f 224 | var value = f(this.value, a) 225 | return { value: value, next: new Accum(f, value) } 226 | }; 227 | 228 | // 229 | 230 | // A SignalGen generates values or events of a signal 231 | 232 | 233 | 234 | 235 | // Forget all future values of a SignalGen 236 | 237 | 238 | 239 | 240 | // Handle events from a SignalGen 241 | 242 | 243 | // Turn a pair of inputs into an input of pairs 244 | function and (input1 , input2 ) { 245 | return new SGVector(input1, input2) 246 | } 247 | 248 | var emptySignalVector = [NoEvent, NoEvent] 249 | 250 | var empty = function (input) { return input instanceof SGVector ? emptySignalVector : NoEvent; } 251 | 252 | var SGVector = function SGVector (first , second ) { 253 | this.first = first 254 | this.second = second 255 | }; 256 | 257 | SGVector.prototype.listen = function listen (handler ) { 258 | var this$1 = this; 259 | 260 | var forgetFirst = this.first.listen(function (a) { return handler([a, empty(this$1.second)]); }) 261 | var forgetSecond = this.second.listen(function (a) { return handler([empty(this$1.first), a]); }) 262 | return new ForgetSGVector(forgetFirst, forgetSecond) 263 | }; 264 | 265 | var ForgetSGVector = function ForgetSGVector (forgetFirst , forgetSecond ) { 266 | this.forgetFirst = forgetFirst 267 | this.forgetSecond = forgetSecond 268 | }; 269 | 270 | ForgetSGVector.prototype.forget = function forget () { 271 | this.forgetFirst.forget() 272 | this.forgetSecond.forget() 273 | }; 274 | 275 | function stepInput (set , forget ) { 276 | return new PushSG(set, forget) 277 | } 278 | 279 | var PushSG = function PushSG(set , forget ) { 280 | this.set = set 281 | this.forget = forget 282 | }; 283 | 284 | PushSG.prototype.listen = function listen (f) { 285 | return new ForgetPushSG(this.forget, this.set.call(undefined, f)) 286 | }; 287 | 288 | var ForgetPushSG = function ForgetPushSG (forget , context ) { 289 | this._forget = forget 290 | this.context = context 291 | }; 292 | 293 | ForgetPushSG.prototype.forget = function forget () { 294 | this._forget.call(undefined, this.context) 295 | }; 296 | 297 | // 298 | 299 | // A session provides a sample of state that will be fed into 300 | // a signal function when events occur 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | // Session that yields an incrementing count at each step 311 | var countSession = function () { return new CountSession(1); } 312 | 313 | var CountSession = function CountSession (count ) { 314 | this.count = count 315 | }; 316 | 317 | CountSession.prototype.step = function step () { 318 | return { sample: this.count, nextSession: new CountSession(this.count + 1) } 319 | }; 320 | 321 | // 322 | 323 | 324 | 325 | 326 | function loop (session , input , sf ) { 327 | var forget = input.listen(function (a) { 328 | var ref = session.step(); 329 | var sample = ref.sample; 330 | var nextSession = ref.nextSession; 331 | var ref$1 = sf.step(sample, a); 332 | var ref$1_value = ref$1.value; 333 | var _ = ref$1_value[0]; 334 | var nextInput = ref$1_value[1]; 335 | var next = ref$1.next; // eslint-disable-line no-unused-vars 336 | forget = switchInput(nextSession, nextInput, next, forget) 337 | }) 338 | 339 | return forget 340 | } 341 | 342 | var switchInput = function (session, input, sf, forget) { 343 | forget.forget() 344 | return loop(session, input, sf) 345 | } 346 | 347 | // 348 | 349 | /* global EventTarget, Event, requestAnimationFrame, cancelAnimationFrame */ 350 | 351 | 352 | 353 | var cancelDomEvent = function (ref) { 354 | var node = ref.node; 355 | var name = ref.name; 356 | var handler = ref.handler; 357 | 358 | return node.removeEventListener(name, handler, false); 359 | } 360 | 361 | var fromDomEvent = function (name) { return function (node) { return stepInput(function (handler) { 362 | node.addEventListener(name, handler, false) 363 | return { node: node, name: name, handler: handler } 364 | }, cancelDomEvent); }; } 365 | 366 | var mousemove = fromDomEvent('mousemove') 367 | var keydown = fromDomEvent('keydown') 368 | 369 | var animationFrame = stepInput(requestAnimationFrame, cancelAnimationFrame) 370 | 371 | function interopDefault(ex) { 372 | return ex && typeof ex === 'object' && 'default' in ex ? ex['default'] : ex; 373 | } 374 | 375 | function createCommonjsModule(fn, module) { 376 | return module = { exports: {} }, fn(module, module.exports), module.exports; 377 | } 378 | 379 | var eventlisteners = createCommonjsModule(function (module) { 380 | function invokeHandler(handler, vnode, event) { 381 | if (typeof handler === "function") { 382 | // call function handler 383 | handler.call(vnode, event, vnode); 384 | } else if (typeof handler === "object") { 385 | // call handler with arguments 386 | if (typeof handler[0] === "function") { 387 | // special case for single argument for performance 388 | if (handler.length === 2) { 389 | handler[0].call(vnode, handler[1], event, vnode); 390 | } else { 391 | var args = handler.slice(1); 392 | args.push(event); 393 | args.push(vnode); 394 | handler[0].apply(vnode, args); 395 | } 396 | } else { 397 | // call multiple handlers 398 | for (var i = 0; i < handler.length; i++) { 399 | invokeHandler(handler[i]); 400 | } 401 | } 402 | } 403 | } 404 | 405 | function handleEvent(event, vnode) { 406 | var name = event.type, 407 | on = vnode.data.on; 408 | 409 | // call event handler(s) if exists 410 | if (on && on[name]) { 411 | invokeHandler(on[name], vnode, event); 412 | } 413 | } 414 | 415 | function createListener() { 416 | return function handler(event) { 417 | handleEvent(event, handler.vnode); 418 | } 419 | } 420 | 421 | function updateEventListeners(oldVnode, vnode) { 422 | var oldOn = oldVnode.data.on, 423 | oldListener = oldVnode.listener, 424 | oldElm = oldVnode.elm, 425 | on = vnode && vnode.data.on, 426 | elm = vnode && vnode.elm, 427 | name; 428 | 429 | // optimization for reused immutable handlers 430 | if (oldOn === on) { 431 | return; 432 | } 433 | 434 | // remove existing listeners which no longer used 435 | if (oldOn && oldListener) { 436 | // if element changed or deleted we remove all existing listeners unconditionally 437 | if (!on) { 438 | for (name in oldOn) { 439 | // remove listener if element was changed or existing listeners removed 440 | oldElm.removeEventListener(name, oldListener, false); 441 | } 442 | } else { 443 | for (name in oldOn) { 444 | // remove listener if existing listener removed 445 | if (!on[name]) { 446 | oldElm.removeEventListener(name, oldListener, false); 447 | } 448 | } 449 | } 450 | } 451 | 452 | // add new listeners which has not already attached 453 | if (on) { 454 | // reuse existing listener or create new 455 | var listener = vnode.listener = oldVnode.listener || createListener(); 456 | // update vnode for listener 457 | listener.vnode = vnode; 458 | 459 | // if element changed or added we add all needed listeners unconditionally 460 | if (!oldOn) { 461 | for (name in on) { 462 | // add listener if element was changed or new listeners added 463 | elm.addEventListener(name, listener, false); 464 | } 465 | } else { 466 | for (name in on) { 467 | // add listener if new listener added 468 | if (!oldOn[name]) { 469 | elm.addEventListener(name, listener, false); 470 | } 471 | } 472 | } 473 | } 474 | } 475 | 476 | module.exports = { 477 | create: updateEventListeners, 478 | update: updateEventListeners, 479 | destroy: updateEventListeners 480 | }; 481 | }); 482 | 483 | interopDefault(eventlisteners); 484 | 485 | var attributes = createCommonjsModule(function (module) { 486 | var booleanAttrs = ["allowfullscreen", "async", "autofocus", "autoplay", "checked", "compact", "controls", "declare", 487 | "default", "defaultchecked", "defaultmuted", "defaultselected", "defer", "disabled", "draggable", 488 | "enabled", "formnovalidate", "hidden", "indeterminate", "inert", "ismap", "itemscope", "loop", "multiple", 489 | "muted", "nohref", "noresize", "noshade", "novalidate", "nowrap", "open", "pauseonexit", "readonly", 490 | "required", "reversed", "scoped", "seamless", "selected", "sortable", "spellcheck", "translate", 491 | "truespeed", "typemustmatch", "visible"]; 492 | 493 | var booleanAttrsDict = {}; 494 | for(var i=0, len = booleanAttrs.length; i < len; i++) { 495 | booleanAttrsDict[booleanAttrs[i]] = true; 496 | } 497 | 498 | function updateAttrs(oldVnode, vnode) { 499 | var key, cur, old, elm = vnode.elm, 500 | oldAttrs = oldVnode.data.attrs, attrs = vnode.data.attrs; 501 | 502 | if (!oldAttrs && !attrs) return; 503 | oldAttrs = oldAttrs || {}; 504 | attrs = attrs || {}; 505 | 506 | // update modified attributes, add new attributes 507 | for (key in attrs) { 508 | cur = attrs[key]; 509 | old = oldAttrs[key]; 510 | if (old !== cur) { 511 | // TODO: add support to namespaced attributes (setAttributeNS) 512 | if(!cur && booleanAttrsDict[key]) 513 | elm.removeAttribute(key); 514 | else 515 | elm.setAttribute(key, cur); 516 | } 517 | } 518 | //remove removed attributes 519 | // use `in` operator since the previous `for` iteration uses it (.i.e. add even attributes with undefined value) 520 | // the other option is to remove all attributes with value == undefined 521 | for (key in oldAttrs) { 522 | if (!(key in attrs)) { 523 | elm.removeAttribute(key); 524 | } 525 | } 526 | } 527 | 528 | module.exports = {create: updateAttrs, update: updateAttrs}; 529 | }); 530 | 531 | interopDefault(attributes); 532 | 533 | var props = createCommonjsModule(function (module) { 534 | function updateProps(oldVnode, vnode) { 535 | var key, cur, old, elm = vnode.elm, 536 | oldProps = oldVnode.data.props, props = vnode.data.props; 537 | 538 | if (!oldProps && !props) return; 539 | oldProps = oldProps || {}; 540 | props = props || {}; 541 | 542 | for (key in oldProps) { 543 | if (!props[key]) { 544 | delete elm[key]; 545 | } 546 | } 547 | for (key in props) { 548 | cur = props[key]; 549 | old = oldProps[key]; 550 | if (old !== cur && (key !== 'value' || elm[key] !== cur)) { 551 | elm[key] = cur; 552 | } 553 | } 554 | } 555 | 556 | module.exports = {create: updateProps, update: updateProps}; 557 | }); 558 | 559 | interopDefault(props); 560 | 561 | var _class = createCommonjsModule(function (module) { 562 | function updateClass(oldVnode, vnode) { 563 | var cur, name, elm = vnode.elm, 564 | oldClass = oldVnode.data.class, 565 | klass = vnode.data.class; 566 | 567 | if (!oldClass && !klass) return; 568 | oldClass = oldClass || {}; 569 | klass = klass || {}; 570 | 571 | for (name in oldClass) { 572 | if (!klass[name]) { 573 | elm.classList.remove(name); 574 | } 575 | } 576 | for (name in klass) { 577 | cur = klass[name]; 578 | if (cur !== oldClass[name]) { 579 | elm.classList[cur ? 'add' : 'remove'](name); 580 | } 581 | } 582 | } 583 | 584 | module.exports = {create: updateClass, update: updateClass}; 585 | }); 586 | 587 | interopDefault(_class); 588 | 589 | var vnode = createCommonjsModule(function (module) { 590 | module.exports = function(sel, data, children, text, elm) { 591 | var key = data === undefined ? undefined : data.key; 592 | return {sel: sel, data: data, children: children, 593 | text: text, elm: elm, key: key}; 594 | }; 595 | }); 596 | 597 | var vnode$1 = interopDefault(vnode); 598 | 599 | 600 | var require$$1 = Object.freeze({ 601 | default: vnode$1 602 | }); 603 | 604 | var is = createCommonjsModule(function (module) { 605 | module.exports = { 606 | array: Array.isArray, 607 | primitive: function(s) { return typeof s === 'string' || typeof s === 'number'; }, 608 | }; 609 | }); 610 | 611 | var is$1 = interopDefault(is); 612 | var array = is.array; 613 | var primitive = is.primitive; 614 | 615 | var require$$0 = Object.freeze({ 616 | default: is$1, 617 | array: array, 618 | primitive: primitive 619 | }); 620 | 621 | var htmldomapi = createCommonjsModule(function (module) { 622 | function createElement(tagName){ 623 | return document.createElement(tagName); 624 | } 625 | 626 | function createElementNS(namespaceURI, qualifiedName){ 627 | return document.createElementNS(namespaceURI, qualifiedName); 628 | } 629 | 630 | function createTextNode(text){ 631 | return document.createTextNode(text); 632 | } 633 | 634 | 635 | function insertBefore(parentNode, newNode, referenceNode){ 636 | parentNode.insertBefore(newNode, referenceNode); 637 | } 638 | 639 | 640 | function removeChild(node, child){ 641 | node.removeChild(child); 642 | } 643 | 644 | function appendChild(node, child){ 645 | node.appendChild(child); 646 | } 647 | 648 | function parentNode(node){ 649 | return node.parentElement; 650 | } 651 | 652 | function nextSibling(node){ 653 | return node.nextSibling; 654 | } 655 | 656 | function tagName(node){ 657 | return node.tagName; 658 | } 659 | 660 | function setTextContent(node, text){ 661 | node.textContent = text; 662 | } 663 | 664 | module.exports = { 665 | createElement: createElement, 666 | createElementNS: createElementNS, 667 | createTextNode: createTextNode, 668 | appendChild: appendChild, 669 | removeChild: removeChild, 670 | insertBefore: insertBefore, 671 | parentNode: parentNode, 672 | nextSibling: nextSibling, 673 | tagName: tagName, 674 | setTextContent: setTextContent 675 | }; 676 | }); 677 | 678 | var htmldomapi$1 = interopDefault(htmldomapi); 679 | var createElement = htmldomapi.createElement; 680 | var createElementNS = htmldomapi.createElementNS; 681 | var createTextNode = htmldomapi.createTextNode; 682 | var appendChild = htmldomapi.appendChild; 683 | var removeChild = htmldomapi.removeChild; 684 | var insertBefore = htmldomapi.insertBefore; 685 | var parentNode = htmldomapi.parentNode; 686 | var nextSibling = htmldomapi.nextSibling; 687 | var tagName = htmldomapi.tagName; 688 | var setTextContent = htmldomapi.setTextContent; 689 | 690 | var require$$0$1 = Object.freeze({ 691 | default: htmldomapi$1, 692 | createElement: createElement, 693 | createElementNS: createElementNS, 694 | createTextNode: createTextNode, 695 | appendChild: appendChild, 696 | removeChild: removeChild, 697 | insertBefore: insertBefore, 698 | parentNode: parentNode, 699 | nextSibling: nextSibling, 700 | tagName: tagName, 701 | setTextContent: setTextContent 702 | }); 703 | 704 | var snabbdom = createCommonjsModule(function (module) { 705 | // jshint newcap: false 706 | /* global require, module, document, Node */ 707 | 'use strict'; 708 | 709 | var VNode = interopDefault(require$$1); 710 | var is = interopDefault(require$$0); 711 | var domApi = interopDefault(require$$0$1); 712 | 713 | function isUndef(s) { return s === undefined; } 714 | function isDef(s) { return s !== undefined; } 715 | 716 | var emptyNode = VNode('', {}, [], undefined, undefined); 717 | 718 | function sameVnode(vnode1, vnode2) { 719 | return vnode1.key === vnode2.key && vnode1.sel === vnode2.sel; 720 | } 721 | 722 | function createKeyToOldIdx(children, beginIdx, endIdx) { 723 | var i, map = {}, key; 724 | for (i = beginIdx; i <= endIdx; ++i) { 725 | key = children[i].key; 726 | if (isDef(key)) map[key] = i; 727 | } 728 | return map; 729 | } 730 | 731 | var hooks = ['create', 'update', 'remove', 'destroy', 'pre', 'post']; 732 | 733 | function init(modules, api) { 734 | var i, j, cbs = {}; 735 | 736 | if (isUndef(api)) api = domApi; 737 | 738 | for (i = 0; i < hooks.length; ++i) { 739 | cbs[hooks[i]] = []; 740 | for (j = 0; j < modules.length; ++j) { 741 | if (modules[j][hooks[i]] !== undefined) cbs[hooks[i]].push(modules[j][hooks[i]]); 742 | } 743 | } 744 | 745 | function emptyNodeAt(elm) { 746 | var id = elm.id ? '#' + elm.id : ''; 747 | var c = elm.className ? '.' + elm.className.split(' ').join('.') : ''; 748 | return VNode(api.tagName(elm).toLowerCase() + id + c, {}, [], undefined, elm); 749 | } 750 | 751 | function createRmCb(childElm, listeners) { 752 | return function() { 753 | if (--listeners === 0) { 754 | var parent = api.parentNode(childElm); 755 | api.removeChild(parent, childElm); 756 | } 757 | }; 758 | } 759 | 760 | function createElm(vnode, insertedVnodeQueue) { 761 | var i, data = vnode.data; 762 | if (isDef(data)) { 763 | if (isDef(i = data.hook) && isDef(i = i.init)) { 764 | i(vnode); 765 | data = vnode.data; 766 | } 767 | } 768 | var elm, children = vnode.children, sel = vnode.sel; 769 | if (isDef(sel)) { 770 | // Parse selector 771 | var hashIdx = sel.indexOf('#'); 772 | var dotIdx = sel.indexOf('.', hashIdx); 773 | var hash = hashIdx > 0 ? hashIdx : sel.length; 774 | var dot = dotIdx > 0 ? dotIdx : sel.length; 775 | var tag = hashIdx !== -1 || dotIdx !== -1 ? sel.slice(0, Math.min(hash, dot)) : sel; 776 | elm = vnode.elm = isDef(data) && isDef(i = data.ns) ? api.createElementNS(i, tag) 777 | : api.createElement(tag); 778 | if (hash < dot) elm.id = sel.slice(hash + 1, dot); 779 | if (dotIdx > 0) elm.className = sel.slice(dot + 1).replace(/\./g, ' '); 780 | if (is.array(children)) { 781 | for (i = 0; i < children.length; ++i) { 782 | api.appendChild(elm, createElm(children[i], insertedVnodeQueue)); 783 | } 784 | } else if (is.primitive(vnode.text)) { 785 | api.appendChild(elm, api.createTextNode(vnode.text)); 786 | } 787 | for (i = 0; i < cbs.create.length; ++i) cbs.create[i](emptyNode, vnode); 788 | i = vnode.data.hook; // Reuse variable 789 | if (isDef(i)) { 790 | if (i.create) i.create(emptyNode, vnode); 791 | if (i.insert) insertedVnodeQueue.push(vnode); 792 | } 793 | } else { 794 | elm = vnode.elm = api.createTextNode(vnode.text); 795 | } 796 | return vnode.elm; 797 | } 798 | 799 | function addVnodes(parentElm, before, vnodes, startIdx, endIdx, insertedVnodeQueue) { 800 | for (; startIdx <= endIdx; ++startIdx) { 801 | api.insertBefore(parentElm, createElm(vnodes[startIdx], insertedVnodeQueue), before); 802 | } 803 | } 804 | 805 | function invokeDestroyHook(vnode) { 806 | var i, j, data = vnode.data; 807 | if (isDef(data)) { 808 | if (isDef(i = data.hook) && isDef(i = i.destroy)) i(vnode); 809 | for (i = 0; i < cbs.destroy.length; ++i) cbs.destroy[i](vnode); 810 | if (isDef(i = vnode.children)) { 811 | for (j = 0; j < vnode.children.length; ++j) { 812 | invokeDestroyHook(vnode.children[j]); 813 | } 814 | } 815 | } 816 | } 817 | 818 | function removeVnodes(parentElm, vnodes, startIdx, endIdx) { 819 | for (; startIdx <= endIdx; ++startIdx) { 820 | var i, listeners, rm, ch = vnodes[startIdx]; 821 | if (isDef(ch)) { 822 | if (isDef(ch.sel)) { 823 | invokeDestroyHook(ch); 824 | listeners = cbs.remove.length + 1; 825 | rm = createRmCb(ch.elm, listeners); 826 | for (i = 0; i < cbs.remove.length; ++i) cbs.remove[i](ch, rm); 827 | if (isDef(i = ch.data) && isDef(i = i.hook) && isDef(i = i.remove)) { 828 | i(ch, rm); 829 | } else { 830 | rm(); 831 | } 832 | } else { // Text node 833 | api.removeChild(parentElm, ch.elm); 834 | } 835 | } 836 | } 837 | } 838 | 839 | function updateChildren(parentElm, oldCh, newCh, insertedVnodeQueue) { 840 | var oldStartIdx = 0, newStartIdx = 0; 841 | var oldEndIdx = oldCh.length - 1; 842 | var oldStartVnode = oldCh[0]; 843 | var oldEndVnode = oldCh[oldEndIdx]; 844 | var newEndIdx = newCh.length - 1; 845 | var newStartVnode = newCh[0]; 846 | var newEndVnode = newCh[newEndIdx]; 847 | var oldKeyToIdx, idxInOld, elmToMove, before; 848 | 849 | while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) { 850 | if (isUndef(oldStartVnode)) { 851 | oldStartVnode = oldCh[++oldStartIdx]; // Vnode has been moved left 852 | } else if (isUndef(oldEndVnode)) { 853 | oldEndVnode = oldCh[--oldEndIdx]; 854 | } else if (sameVnode(oldStartVnode, newStartVnode)) { 855 | patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue); 856 | oldStartVnode = oldCh[++oldStartIdx]; 857 | newStartVnode = newCh[++newStartIdx]; 858 | } else if (sameVnode(oldEndVnode, newEndVnode)) { 859 | patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue); 860 | oldEndVnode = oldCh[--oldEndIdx]; 861 | newEndVnode = newCh[--newEndIdx]; 862 | } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right 863 | patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue); 864 | api.insertBefore(parentElm, oldStartVnode.elm, api.nextSibling(oldEndVnode.elm)); 865 | oldStartVnode = oldCh[++oldStartIdx]; 866 | newEndVnode = newCh[--newEndIdx]; 867 | } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left 868 | patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue); 869 | api.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm); 870 | oldEndVnode = oldCh[--oldEndIdx]; 871 | newStartVnode = newCh[++newStartIdx]; 872 | } else { 873 | if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx); 874 | idxInOld = oldKeyToIdx[newStartVnode.key]; 875 | if (isUndef(idxInOld)) { // New element 876 | api.insertBefore(parentElm, createElm(newStartVnode, insertedVnodeQueue), oldStartVnode.elm); 877 | newStartVnode = newCh[++newStartIdx]; 878 | } else { 879 | elmToMove = oldCh[idxInOld]; 880 | patchVnode(elmToMove, newStartVnode, insertedVnodeQueue); 881 | oldCh[idxInOld] = undefined; 882 | api.insertBefore(parentElm, elmToMove.elm, oldStartVnode.elm); 883 | newStartVnode = newCh[++newStartIdx]; 884 | } 885 | } 886 | } 887 | if (oldStartIdx > oldEndIdx) { 888 | before = isUndef(newCh[newEndIdx+1]) ? null : newCh[newEndIdx+1].elm; 889 | addVnodes(parentElm, before, newCh, newStartIdx, newEndIdx, insertedVnodeQueue); 890 | } else if (newStartIdx > newEndIdx) { 891 | removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx); 892 | } 893 | } 894 | 895 | function patchVnode(oldVnode, vnode, insertedVnodeQueue) { 896 | var i, hook; 897 | if (isDef(i = vnode.data) && isDef(hook = i.hook) && isDef(i = hook.prepatch)) { 898 | i(oldVnode, vnode); 899 | } 900 | var elm = vnode.elm = oldVnode.elm, oldCh = oldVnode.children, ch = vnode.children; 901 | if (oldVnode === vnode) return; 902 | if (!sameVnode(oldVnode, vnode)) { 903 | var parentElm = api.parentNode(oldVnode.elm); 904 | elm = createElm(vnode, insertedVnodeQueue); 905 | api.insertBefore(parentElm, elm, oldVnode.elm); 906 | removeVnodes(parentElm, [oldVnode], 0, 0); 907 | return; 908 | } 909 | if (isDef(vnode.data)) { 910 | for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode); 911 | i = vnode.data.hook; 912 | if (isDef(i) && isDef(i = i.update)) i(oldVnode, vnode); 913 | } 914 | if (isUndef(vnode.text)) { 915 | if (isDef(oldCh) && isDef(ch)) { 916 | if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue); 917 | } else if (isDef(ch)) { 918 | if (isDef(oldVnode.text)) api.setTextContent(elm, ''); 919 | addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue); 920 | } else if (isDef(oldCh)) { 921 | removeVnodes(elm, oldCh, 0, oldCh.length - 1); 922 | } else if (isDef(oldVnode.text)) { 923 | api.setTextContent(elm, ''); 924 | } 925 | } else if (oldVnode.text !== vnode.text) { 926 | api.setTextContent(elm, vnode.text); 927 | } 928 | if (isDef(hook) && isDef(i = hook.postpatch)) { 929 | i(oldVnode, vnode); 930 | } 931 | } 932 | 933 | return function(oldVnode, vnode) { 934 | var i, elm, parent; 935 | var insertedVnodeQueue = []; 936 | for (i = 0; i < cbs.pre.length; ++i) cbs.pre[i](); 937 | 938 | if (isUndef(oldVnode.sel)) { 939 | oldVnode = emptyNodeAt(oldVnode); 940 | } 941 | 942 | if (sameVnode(oldVnode, vnode)) { 943 | patchVnode(oldVnode, vnode, insertedVnodeQueue); 944 | } else { 945 | elm = oldVnode.elm; 946 | parent = api.parentNode(elm); 947 | 948 | createElm(vnode, insertedVnodeQueue); 949 | 950 | if (parent !== null) { 951 | api.insertBefore(parent, vnode.elm, api.nextSibling(elm)); 952 | removeVnodes(parent, [oldVnode], 0, 0); 953 | } 954 | } 955 | 956 | for (i = 0; i < insertedVnodeQueue.length; ++i) { 957 | insertedVnodeQueue[i].data.hook.insert(insertedVnodeQueue[i]); 958 | } 959 | for (i = 0; i < cbs.post.length; ++i) cbs.post[i](); 960 | return vnode; 961 | }; 962 | } 963 | 964 | module.exports = {init: init}; 965 | }); 966 | 967 | var snabbdom$1 = interopDefault(snabbdom); 968 | 969 | var h = createCommonjsModule(function (module) { 970 | var VNode = interopDefault(require$$1); 971 | var is = interopDefault(require$$0); 972 | 973 | function addNS(data, children, sel) { 974 | data.ns = 'http://www.w3.org/2000/svg'; 975 | 976 | if (sel !== 'foreignObject' && children !== undefined) { 977 | for (var i = 0; i < children.length; ++i) { 978 | addNS(children[i].data, children[i].children, children[i].sel); 979 | } 980 | } 981 | } 982 | 983 | module.exports = function h(sel, b, c) { 984 | var data = {}, children, text, i; 985 | if (c !== undefined) { 986 | data = b; 987 | if (is.array(c)) { children = c; } 988 | else if (is.primitive(c)) { text = c; } 989 | } else if (b !== undefined) { 990 | if (is.array(b)) { children = b; } 991 | else if (is.primitive(b)) { text = b; } 992 | else { data = b; } 993 | } 994 | if (is.array(children)) { 995 | for (i = 0; i < children.length; ++i) { 996 | if (is.primitive(children[i])) children[i] = VNode(undefined, undefined, undefined, children[i]); 997 | } 998 | } 999 | if (sel[0] === 's' && sel[1] === 'v' && sel[2] === 'g') { 1000 | addNS(data, children, sel); 1001 | } 1002 | return VNode(sel, data, children, text, undefined); 1003 | }; 1004 | }); 1005 | 1006 | var sh = interopDefault(h); 1007 | 1008 | var index = createCommonjsModule(function (module, exports) { 1009 | 'use strict'; 1010 | 1011 | Object.defineProperty(exports, '__esModule', { 1012 | value: true 1013 | }); 1014 | var isValidString = function isValidString(param) { 1015 | return typeof param === 'string' && param.length > 0; 1016 | }; 1017 | 1018 | var startsWith = function startsWith(string, start) { 1019 | return string[0] === start; 1020 | }; 1021 | 1022 | var isSelector = function isSelector(param) { 1023 | return isValidString(param) && (startsWith(param, '.') || startsWith(param, '#')); 1024 | }; 1025 | 1026 | var node = function node(h) { 1027 | return function (tagName) { 1028 | return function (first) { 1029 | var arguments$1 = arguments; 1030 | 1031 | for (var _len = arguments.length, rest = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { 1032 | rest[_key - 1] = arguments$1[_key]; 1033 | } 1034 | 1035 | if (isSelector(first)) { 1036 | return h.apply(undefined, [tagName + first].concat(rest)); 1037 | } else if (typeof first === 'undefined') { 1038 | return h(tagName); 1039 | } else { 1040 | return h.apply(undefined, [tagName, first].concat(rest)); 1041 | } 1042 | }; 1043 | }; 1044 | }; 1045 | 1046 | var TAG_NAMES = ['a', 'abbr', 'acronym', 'address', 'applet', 'area', 'article', 'aside', 'audio', 'b', 'base', 'basefont', 'bdi', 'bdo', 'bgsound', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'command', 'content', 'data', 'datalist', 'dd', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'embed', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'frame', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'iframe', 'image', 'img', 'input', 'ins', 'isindex', 'kbd', 'keygen', 'label', 'legend', 'li', 'link', 'listing', 'main', 'map', 'mark', 'marquee', 'math', 'menu', 'menuitem', 'meta', 'meter', 'multicol', 'nav', 'nextid', 'nobr', 'noembed', 'noframes', 'noscript', 'object', 'ol', 'optgroup', 'option', 'output', 'p', 'param', 'picture', 'plaintext', 'pre', 'progress', 'q', 'rb', 'rbc', 'rp', 'rt', 'rtc', 'ruby', 's', 'samp', 'script', 'section', 'select', 'shadow', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'svg', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'title', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr', 'xmp']; 1047 | 1048 | exports['default'] = function (h) { 1049 | var createTag = node(h); 1050 | var exported = { TAG_NAMES: TAG_NAMES, isSelector: isSelector, createTag: createTag }; 1051 | TAG_NAMES.forEach(function (n) { 1052 | exported[n] = createTag(n); 1053 | }); 1054 | return exported; 1055 | }; 1056 | 1057 | module.exports = exports['default']; 1058 | }); 1059 | 1060 | var hh = interopDefault(index); 1061 | 1062 | // 1063 | 1064 | 1065 | var init = function (modules) { 1066 | if ( modules === void 0 ) modules = []; 1067 | 1068 | return snabbdom$1.init(modules); 1069 | } 1070 | 1071 | var html = hh(sh) 1072 | 1073 | 1074 | 1075 | function vdomPatch (patch , init ) { 1076 | return first(scan(patch, init)) 1077 | } 1078 | 1079 | // 1080 | var span = html.span; 1081 | 1082 | var container = document.getElementById('app') 1083 | var patch = init() 1084 | 1085 | var inputs = and(mousemove(document), keydown(document)) 1086 | 1087 | var render = function (pos, key) { return span(((pos.clientX) + "," + (pos.clientY) + ":" + key)); } 1088 | var withInputs = always(inputs) 1089 | 1090 | var coords = hold('-,-') 1091 | var keyCode = pipe(map(function (e) { return e.keyCode; }), hold('-')) 1092 | var mouseAndKey = pipe(both(coords, keyCode), unsplit(render)) 1093 | 1094 | var update = vdomPatch(patch, patch(container, span('move the mouse and press some keys'))) 1095 | 1096 | loop(countSession(), inputs, pipe(split(mouseAndKey, withInputs), update)) 1097 | 1098 | }()); -------------------------------------------------------------------------------- /examples/stopwatch/app.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | function uncurry (f ) { 5 | return function (ref) { 6 | var a = ref[0]; 7 | var b = ref[1]; 8 | 9 | return f(a, b); 10 | } 11 | } 12 | 13 | // 14 | // Signal Function is a time varying transformation that 15 | // turns Signals of A into Signals of B. It may carry state 16 | // and evolve over time 17 | 18 | 19 | 20 | 21 | // A Step is the result of applying a SignalFunc 22 | // to an A to get a B and a new SignalFunc 23 | 24 | 25 | 26 | 27 | 28 | // SignalFunc specialized for Time type 29 | // Note: Flow can't infer generics, IOW, it can't info the 30 | // type T *later* based on the Session type provided when running 31 | // a SignalFunc. Flow needs to be able to determine T at the 32 | // instant a SignalFunc is created, but the type is only known 33 | // later when a Session is used to run the SignalFunc 34 | 35 | 36 | // SignalStep specialized for Time type 37 | // re: Flow, similarly 38 | 39 | 40 | // Simple helper to construct a Step 41 | var step = function (value, next) { return ({ value: value, next: next }); } 42 | 43 | var time = 44 | { step: function (value, _) { return ({ value: value, next: time }); } } 45 | 46 | // Lift a function into a SignalFunc 47 | function lift (f ) { 48 | return new Lift(f) 49 | } 50 | 51 | // Combine a pair of signals into a signal of C 52 | function unsplit (f ) { 53 | return lift(uncurry(f)) 54 | } 55 | 56 | var Lift = function Lift (f ) { 57 | this.f = f 58 | }; 59 | 60 | Lift.prototype.step = function step$1 (t , a ) { 61 | var f = this.f 62 | return step(f(a), this) 63 | }; 64 | 65 | // first :: SFTime a b -> SFTime [a, c] [b, c] 66 | // Apply a SignalFunc to the first signal of a pair 67 | function first (ab ) { 68 | return new First(ab) 69 | } 70 | 71 | var First = function First (ab ) { 72 | this.ab = ab 73 | }; 74 | 75 | First.prototype.step = function step$2 (t , ref ) { 76 | var a = ref[0]; 77 | var c = ref[1]; 78 | 79 | var ref$1 = this.ab.step(t, a); 80 | var b = ref$1.value; 81 | var next = ref$1.next; 82 | return step([b, c], first(next)) 83 | }; 84 | 85 | // pipe :: (SFTime a b ... SFTime y z) -> SFTime a z 86 | // Compose many Reactive transformations, left to right 87 | function pipe (ab ) { 88 | var rest = [], len = arguments.length - 1; 89 | while ( len-- > 0 ) rest[ len ] = arguments[ len + 1 ]; 90 | 91 | return rest.reduce(pipe2, ab) 92 | } 93 | 94 | // pipe2 :: SFTime a b -> SFTime b c -> SFTime a c 95 | // Compose 2 Reactive transformations left to right 96 | function pipe2 (ab , bc ) { 97 | return new Pipe(ab, bc) 98 | } 99 | 100 | var Pipe = function Pipe (ab , bc ) { 101 | this.ab = ab 102 | this.bc = bc 103 | }; 104 | 105 | Pipe.prototype.step = function step$4 (t , a ) { 106 | var ref = this.ab.step(t, a); 107 | var b = ref.value; 108 | var ab = ref.next; 109 | var ref$1 = this.bc.step(t, b); 110 | var c = ref$1.value; 111 | var bc = ref$1.next; 112 | return step(c, pipe2(ab, bc)) 113 | }; 114 | 115 | // both :: SFTime a b -> SFTime c d -> Reactive [a, c] [b, d] 116 | // Given an [a, c] input, pass a through Reactive transformation ab and 117 | // c through Reactive transformation cd to yield [b, d] 118 | function both (ab , cd ) { 119 | return new Both(ab, cd) 120 | } 121 | 122 | var Both = function Both (ab , cd ) { 123 | this.ab = ab 124 | this.cd = cd 125 | }; 126 | 127 | Both.prototype.step = function step$5 (t , ref ) { 128 | var a = ref[0]; 129 | var c = ref[1]; 130 | 131 | var ref$1 = this.ab.step(t, a); 132 | var b = ref$1.value; 133 | var anext = ref$1.next; 134 | var ref$2 = this.cd.step(t, c); 135 | var d = ref$2.value; 136 | var cnext = ref$2.next; 137 | return step([b, d], both(anext, cnext)) 138 | }; 139 | 140 | // 141 | 142 | // An event, which has a value when it occurs, and 143 | // has no value when it doesn't occur 144 | 145 | 146 | // Event non-occurrence 147 | var NoEvent = undefined 148 | 149 | // Turn Events of A instead Events of B 150 | function mapE (f ) { 151 | return function (a) { return a === undefined ? a : f(a); } 152 | } 153 | 154 | // Return the Event that occurred, preferring a1 if both occurred 155 | function mergeE (a1 , a2 ) { 156 | return a1 === undefined ? a2 : a1 157 | } 158 | 159 | // Internal helper to allow continuous value transformations to be 160 | // applied when an event occurs 161 | // TODO: Consider exposing this if it seems useful 162 | function liftE (ab ) { 163 | return new LiftE(ab) 164 | } 165 | 166 | var LiftE = function LiftE (ab) { 167 | this.ab = ab 168 | }; 169 | 170 | LiftE.prototype.step = function step (t , a ) { 171 | if (a === undefined) { 172 | return { value: NoEvent, next: this } 173 | } 174 | var ref = this.ab.step(t, a); 175 | var value = ref.value; 176 | var next = ref.next; 177 | return { value: value, next: liftE(next) } 178 | }; 179 | 180 | // Sample the current time when an event occurs 181 | var eventTime = { 182 | step: function step (t , a ) { 183 | return { value: a === undefined ? NoEvent : t, next: this } 184 | } 185 | } 186 | 187 | // Transform event values 188 | function map (f ) { 189 | return lift(mapE(f)) 190 | } 191 | 192 | // Merge events, preferring the left in the case of 193 | // simultaneous occurrence 194 | function merge () { 195 | return unsplit(mergeE) 196 | } 197 | 198 | // Merge event SignalFuncs 199 | function or (left , right ) { 200 | return liftE(pipe(both(left, right), merge())) 201 | } 202 | 203 | // Turn an event into a stepped continuous value 204 | function hold (initial ) { 205 | return new Hold(initial) 206 | } 207 | 208 | var Hold = function Hold (value ) { 209 | this.value = value 210 | }; 211 | 212 | Hold.prototype.step = function step (t , a ) { 213 | return a === undefined 214 | ? { value: this.value, next: this } 215 | : { value: a, next: hold(a) } 216 | }; 217 | 218 | // Accumulate event 219 | function scanE (f , initial ) { 220 | return new Accum(f, initial) 221 | } 222 | 223 | // Accumulate event to a continuous value 224 | function scan (f , initial ) { 225 | return pipe(scanE(f, initial), hold(initial)) 226 | } 227 | 228 | // Accumulate event, given an initial value and a update-function event 229 | function accumE (initial ) { 230 | return scanE(function (a, f) { return f(a); }, initial) 231 | } 232 | 233 | // Accumulate event to a continuous value, given an initial value and a update-function event 234 | function accum (initial ) { 235 | return pipe(accumE(initial), hold(initial)) 236 | } 237 | 238 | var Accum = function Accum (f , value ) { 239 | this.f = f 240 | this.value = value 241 | }; 242 | 243 | Accum.prototype.step = function step (t , a ) { 244 | if (a === undefined) { 245 | return { value: NoEvent, next: this } 246 | } 247 | var f = this.f 248 | var value = f(this.value, a) 249 | return { value: value, next: new Accum(f, value) } 250 | }; 251 | 252 | // 253 | 254 | // A SignalGen generates values or events of a signal 255 | 256 | 257 | 258 | 259 | // Forget all future values of a SignalGen 260 | 261 | 262 | 263 | 264 | // Handle events from a SignalGen 265 | 266 | 267 | // Turn a pair of inputs into an input of pairs 268 | function and (input1 , input2 ) { 269 | return new SGVector(input1, input2) 270 | } 271 | 272 | var never = { 273 | listen: function listen () { return forgetNever } 274 | } 275 | 276 | var forgetNever = { 277 | forget: function forget () {} 278 | } 279 | 280 | 281 | 282 | function signalGen () { 283 | var source = new SGSource() 284 | return [function (x) { return source.handler(x); }, source] 285 | } 286 | 287 | var noop = function () {} 288 | 289 | var SGSource = function SGSource () { 290 | this.handler = noop 291 | }; 292 | 293 | SGSource.prototype.listen = function listen (handler ) { 294 | this.handler = handler 295 | return new ForgetSGSource(this) 296 | }; 297 | 298 | var ForgetSGSource = function ForgetSGSource (source ) { 299 | this.source = source 300 | }; 301 | 302 | ForgetSGSource.prototype.forget = function forget () { 303 | this.source.handler = noop 304 | }; 305 | 306 | var emptySignalVector = [NoEvent, NoEvent] 307 | 308 | var empty = function (input) { return input instanceof SGVector ? emptySignalVector : NoEvent; } 309 | 310 | var SGVector = function SGVector (first , second ) { 311 | this.first = first 312 | this.second = second 313 | }; 314 | 315 | SGVector.prototype.listen = function listen (handler ) { 316 | var this$1 = this; 317 | 318 | var forgetFirst = this.first.listen(function (a) { return handler([a, empty(this$1.second)]); }) 319 | var forgetSecond = this.second.listen(function (a) { return handler([empty(this$1.first), a]); }) 320 | return new ForgetSGVector(forgetFirst, forgetSecond) 321 | }; 322 | 323 | var ForgetSGVector = function ForgetSGVector (forgetFirst , forgetSecond ) { 324 | this.forgetFirst = forgetFirst 325 | this.forgetSecond = forgetSecond 326 | }; 327 | 328 | ForgetSGVector.prototype.forget = function forget () { 329 | this.forgetFirst.forget() 330 | this.forgetSecond.forget() 331 | }; 332 | 333 | function stepInput (set , forget ) { 334 | return new PushSG(set, forget) 335 | } 336 | 337 | var PushSG = function PushSG(set , forget ) { 338 | this.set = set 339 | this.forget = forget 340 | }; 341 | 342 | PushSG.prototype.listen = function listen (f) { 343 | return new ForgetPushSG(this.forget, this.set.call(undefined, f)) 344 | }; 345 | 346 | var ForgetPushSG = function ForgetPushSG (forget , context ) { 347 | this._forget = forget 348 | this.context = context 349 | }; 350 | 351 | ForgetPushSG.prototype.forget = function forget () { 352 | this._forget.call(undefined, this.context) 353 | }; 354 | 355 | // Session that yields a time delta from its start time at each step 356 | var clockSession = function () { return new ClockSession(Date.now()); } 357 | 358 | var ClockSession = function ClockSession (start ) { 359 | this.start = start 360 | }; 361 | 362 | ClockSession.prototype.step = function step () { 363 | return { sample: Date.now() - this.start, nextSession: new ClockSession(this.start) } 364 | }; 365 | 366 | // 367 | 368 | 369 | 370 | 371 | function loop (session , input , sf ) { 372 | var forget = input.listen(function (a) { 373 | var ref = session.step(); 374 | var sample = ref.sample; 375 | var nextSession = ref.nextSession; 376 | var ref$1 = sf.step(sample, a); 377 | var ref$1_value = ref$1.value; 378 | var _ = ref$1_value[0]; 379 | var nextInput = ref$1_value[1]; 380 | var next = ref$1.next; // eslint-disable-line no-unused-vars 381 | forget = switchInput(nextSession, nextInput, next, forget) 382 | }) 383 | 384 | return forget 385 | } 386 | 387 | var switchInput = function (session, input, sf, forget) { 388 | forget.forget() 389 | return loop(session, input, sf) 390 | } 391 | 392 | // 393 | 394 | var animationFrame = stepInput(requestAnimationFrame, cancelAnimationFrame) 395 | 396 | function interopDefault(ex) { 397 | return ex && typeof ex === 'object' && 'default' in ex ? ex['default'] : ex; 398 | } 399 | 400 | function createCommonjsModule(fn, module) { 401 | return module = { exports: {} }, fn(module, module.exports), module.exports; 402 | } 403 | 404 | var eventlisteners = createCommonjsModule(function (module) { 405 | function invokeHandler(handler, vnode, event) { 406 | if (typeof handler === "function") { 407 | // call function handler 408 | handler.call(vnode, event, vnode); 409 | } else if (typeof handler === "object") { 410 | // call handler with arguments 411 | if (typeof handler[0] === "function") { 412 | // special case for single argument for performance 413 | if (handler.length === 2) { 414 | handler[0].call(vnode, handler[1], event, vnode); 415 | } else { 416 | var args = handler.slice(1); 417 | args.push(event); 418 | args.push(vnode); 419 | handler[0].apply(vnode, args); 420 | } 421 | } else { 422 | // call multiple handlers 423 | for (var i = 0; i < handler.length; i++) { 424 | invokeHandler(handler[i]); 425 | } 426 | } 427 | } 428 | } 429 | 430 | function handleEvent(event, vnode) { 431 | var name = event.type, 432 | on = vnode.data.on; 433 | 434 | // call event handler(s) if exists 435 | if (on && on[name]) { 436 | invokeHandler(on[name], vnode, event); 437 | } 438 | } 439 | 440 | function createListener() { 441 | return function handler(event) { 442 | handleEvent(event, handler.vnode); 443 | } 444 | } 445 | 446 | function updateEventListeners(oldVnode, vnode) { 447 | var oldOn = oldVnode.data.on, 448 | oldListener = oldVnode.listener, 449 | oldElm = oldVnode.elm, 450 | on = vnode && vnode.data.on, 451 | elm = vnode && vnode.elm, 452 | name; 453 | 454 | // optimization for reused immutable handlers 455 | if (oldOn === on) { 456 | return; 457 | } 458 | 459 | // remove existing listeners which no longer used 460 | if (oldOn && oldListener) { 461 | // if element changed or deleted we remove all existing listeners unconditionally 462 | if (!on) { 463 | for (name in oldOn) { 464 | // remove listener if element was changed or existing listeners removed 465 | oldElm.removeEventListener(name, oldListener, false); 466 | } 467 | } else { 468 | for (name in oldOn) { 469 | // remove listener if existing listener removed 470 | if (!on[name]) { 471 | oldElm.removeEventListener(name, oldListener, false); 472 | } 473 | } 474 | } 475 | } 476 | 477 | // add new listeners which has not already attached 478 | if (on) { 479 | // reuse existing listener or create new 480 | var listener = vnode.listener = oldVnode.listener || createListener(); 481 | // update vnode for listener 482 | listener.vnode = vnode; 483 | 484 | // if element changed or added we add all needed listeners unconditionally 485 | if (!oldOn) { 486 | for (name in on) { 487 | // add listener if element was changed or new listeners added 488 | elm.addEventListener(name, listener, false); 489 | } 490 | } else { 491 | for (name in on) { 492 | // add listener if new listener added 493 | if (!oldOn[name]) { 494 | elm.addEventListener(name, listener, false); 495 | } 496 | } 497 | } 498 | } 499 | } 500 | 501 | module.exports = { 502 | create: updateEventListeners, 503 | update: updateEventListeners, 504 | destroy: updateEventListeners 505 | }; 506 | }); 507 | 508 | var events = interopDefault(eventlisteners); 509 | 510 | var attributes = createCommonjsModule(function (module) { 511 | var booleanAttrs = ["allowfullscreen", "async", "autofocus", "autoplay", "checked", "compact", "controls", "declare", 512 | "default", "defaultchecked", "defaultmuted", "defaultselected", "defer", "disabled", "draggable", 513 | "enabled", "formnovalidate", "hidden", "indeterminate", "inert", "ismap", "itemscope", "loop", "multiple", 514 | "muted", "nohref", "noresize", "noshade", "novalidate", "nowrap", "open", "pauseonexit", "readonly", 515 | "required", "reversed", "scoped", "seamless", "selected", "sortable", "spellcheck", "translate", 516 | "truespeed", "typemustmatch", "visible"]; 517 | 518 | var booleanAttrsDict = {}; 519 | for(var i=0, len = booleanAttrs.length; i < len; i++) { 520 | booleanAttrsDict[booleanAttrs[i]] = true; 521 | } 522 | 523 | function updateAttrs(oldVnode, vnode) { 524 | var key, cur, old, elm = vnode.elm, 525 | oldAttrs = oldVnode.data.attrs, attrs = vnode.data.attrs; 526 | 527 | if (!oldAttrs && !attrs) return; 528 | oldAttrs = oldAttrs || {}; 529 | attrs = attrs || {}; 530 | 531 | // update modified attributes, add new attributes 532 | for (key in attrs) { 533 | cur = attrs[key]; 534 | old = oldAttrs[key]; 535 | if (old !== cur) { 536 | // TODO: add support to namespaced attributes (setAttributeNS) 537 | if(!cur && booleanAttrsDict[key]) 538 | elm.removeAttribute(key); 539 | else 540 | elm.setAttribute(key, cur); 541 | } 542 | } 543 | //remove removed attributes 544 | // use `in` operator since the previous `for` iteration uses it (.i.e. add even attributes with undefined value) 545 | // the other option is to remove all attributes with value == undefined 546 | for (key in oldAttrs) { 547 | if (!(key in attrs)) { 548 | elm.removeAttribute(key); 549 | } 550 | } 551 | } 552 | 553 | module.exports = {create: updateAttrs, update: updateAttrs}; 554 | }); 555 | 556 | var attrs = interopDefault(attributes); 557 | 558 | var props = createCommonjsModule(function (module) { 559 | function updateProps(oldVnode, vnode) { 560 | var key, cur, old, elm = vnode.elm, 561 | oldProps = oldVnode.data.props, props = vnode.data.props; 562 | 563 | if (!oldProps && !props) return; 564 | oldProps = oldProps || {}; 565 | props = props || {}; 566 | 567 | for (key in oldProps) { 568 | if (!props[key]) { 569 | delete elm[key]; 570 | } 571 | } 572 | for (key in props) { 573 | cur = props[key]; 574 | old = oldProps[key]; 575 | if (old !== cur && (key !== 'value' || elm[key] !== cur)) { 576 | elm[key] = cur; 577 | } 578 | } 579 | } 580 | 581 | module.exports = {create: updateProps, update: updateProps}; 582 | }); 583 | 584 | interopDefault(props); 585 | 586 | var _class = createCommonjsModule(function (module) { 587 | function updateClass(oldVnode, vnode) { 588 | var cur, name, elm = vnode.elm, 589 | oldClass = oldVnode.data.class, 590 | klass = vnode.data.class; 591 | 592 | if (!oldClass && !klass) return; 593 | oldClass = oldClass || {}; 594 | klass = klass || {}; 595 | 596 | for (name in oldClass) { 597 | if (!klass[name]) { 598 | elm.classList.remove(name); 599 | } 600 | } 601 | for (name in klass) { 602 | cur = klass[name]; 603 | if (cur !== oldClass[name]) { 604 | elm.classList[cur ? 'add' : 'remove'](name); 605 | } 606 | } 607 | } 608 | 609 | module.exports = {create: updateClass, update: updateClass}; 610 | }); 611 | 612 | var clss = interopDefault(_class); 613 | 614 | var vnode = createCommonjsModule(function (module) { 615 | module.exports = function(sel, data, children, text, elm) { 616 | var key = data === undefined ? undefined : data.key; 617 | return {sel: sel, data: data, children: children, 618 | text: text, elm: elm, key: key}; 619 | }; 620 | }); 621 | 622 | var vnode$1 = interopDefault(vnode); 623 | 624 | 625 | var require$$1 = Object.freeze({ 626 | default: vnode$1 627 | }); 628 | 629 | var is = createCommonjsModule(function (module) { 630 | module.exports = { 631 | array: Array.isArray, 632 | primitive: function(s) { return typeof s === 'string' || typeof s === 'number'; }, 633 | }; 634 | }); 635 | 636 | var is$1 = interopDefault(is); 637 | var array = is.array; 638 | var primitive = is.primitive; 639 | 640 | var require$$0 = Object.freeze({ 641 | default: is$1, 642 | array: array, 643 | primitive: primitive 644 | }); 645 | 646 | var htmldomapi = createCommonjsModule(function (module) { 647 | function createElement(tagName){ 648 | return document.createElement(tagName); 649 | } 650 | 651 | function createElementNS(namespaceURI, qualifiedName){ 652 | return document.createElementNS(namespaceURI, qualifiedName); 653 | } 654 | 655 | function createTextNode(text){ 656 | return document.createTextNode(text); 657 | } 658 | 659 | 660 | function insertBefore(parentNode, newNode, referenceNode){ 661 | parentNode.insertBefore(newNode, referenceNode); 662 | } 663 | 664 | 665 | function removeChild(node, child){ 666 | node.removeChild(child); 667 | } 668 | 669 | function appendChild(node, child){ 670 | node.appendChild(child); 671 | } 672 | 673 | function parentNode(node){ 674 | return node.parentElement; 675 | } 676 | 677 | function nextSibling(node){ 678 | return node.nextSibling; 679 | } 680 | 681 | function tagName(node){ 682 | return node.tagName; 683 | } 684 | 685 | function setTextContent(node, text){ 686 | node.textContent = text; 687 | } 688 | 689 | module.exports = { 690 | createElement: createElement, 691 | createElementNS: createElementNS, 692 | createTextNode: createTextNode, 693 | appendChild: appendChild, 694 | removeChild: removeChild, 695 | insertBefore: insertBefore, 696 | parentNode: parentNode, 697 | nextSibling: nextSibling, 698 | tagName: tagName, 699 | setTextContent: setTextContent 700 | }; 701 | }); 702 | 703 | var htmldomapi$1 = interopDefault(htmldomapi); 704 | var createElement = htmldomapi.createElement; 705 | var createElementNS = htmldomapi.createElementNS; 706 | var createTextNode = htmldomapi.createTextNode; 707 | var appendChild = htmldomapi.appendChild; 708 | var removeChild = htmldomapi.removeChild; 709 | var insertBefore = htmldomapi.insertBefore; 710 | var parentNode = htmldomapi.parentNode; 711 | var nextSibling = htmldomapi.nextSibling; 712 | var tagName = htmldomapi.tagName; 713 | var setTextContent = htmldomapi.setTextContent; 714 | 715 | var require$$0$1 = Object.freeze({ 716 | default: htmldomapi$1, 717 | createElement: createElement, 718 | createElementNS: createElementNS, 719 | createTextNode: createTextNode, 720 | appendChild: appendChild, 721 | removeChild: removeChild, 722 | insertBefore: insertBefore, 723 | parentNode: parentNode, 724 | nextSibling: nextSibling, 725 | tagName: tagName, 726 | setTextContent: setTextContent 727 | }); 728 | 729 | var snabbdom = createCommonjsModule(function (module) { 730 | // jshint newcap: false 731 | /* global require, module, document, Node */ 732 | 'use strict'; 733 | 734 | var VNode = interopDefault(require$$1); 735 | var is = interopDefault(require$$0); 736 | var domApi = interopDefault(require$$0$1); 737 | 738 | function isUndef(s) { return s === undefined; } 739 | function isDef(s) { return s !== undefined; } 740 | 741 | var emptyNode = VNode('', {}, [], undefined, undefined); 742 | 743 | function sameVnode(vnode1, vnode2) { 744 | return vnode1.key === vnode2.key && vnode1.sel === vnode2.sel; 745 | } 746 | 747 | function createKeyToOldIdx(children, beginIdx, endIdx) { 748 | var i, map = {}, key; 749 | for (i = beginIdx; i <= endIdx; ++i) { 750 | key = children[i].key; 751 | if (isDef(key)) map[key] = i; 752 | } 753 | return map; 754 | } 755 | 756 | var hooks = ['create', 'update', 'remove', 'destroy', 'pre', 'post']; 757 | 758 | function init(modules, api) { 759 | var i, j, cbs = {}; 760 | 761 | if (isUndef(api)) api = domApi; 762 | 763 | for (i = 0; i < hooks.length; ++i) { 764 | cbs[hooks[i]] = []; 765 | for (j = 0; j < modules.length; ++j) { 766 | if (modules[j][hooks[i]] !== undefined) cbs[hooks[i]].push(modules[j][hooks[i]]); 767 | } 768 | } 769 | 770 | function emptyNodeAt(elm) { 771 | var id = elm.id ? '#' + elm.id : ''; 772 | var c = elm.className ? '.' + elm.className.split(' ').join('.') : ''; 773 | return VNode(api.tagName(elm).toLowerCase() + id + c, {}, [], undefined, elm); 774 | } 775 | 776 | function createRmCb(childElm, listeners) { 777 | return function() { 778 | if (--listeners === 0) { 779 | var parent = api.parentNode(childElm); 780 | api.removeChild(parent, childElm); 781 | } 782 | }; 783 | } 784 | 785 | function createElm(vnode, insertedVnodeQueue) { 786 | var i, data = vnode.data; 787 | if (isDef(data)) { 788 | if (isDef(i = data.hook) && isDef(i = i.init)) { 789 | i(vnode); 790 | data = vnode.data; 791 | } 792 | } 793 | var elm, children = vnode.children, sel = vnode.sel; 794 | if (isDef(sel)) { 795 | // Parse selector 796 | var hashIdx = sel.indexOf('#'); 797 | var dotIdx = sel.indexOf('.', hashIdx); 798 | var hash = hashIdx > 0 ? hashIdx : sel.length; 799 | var dot = dotIdx > 0 ? dotIdx : sel.length; 800 | var tag = hashIdx !== -1 || dotIdx !== -1 ? sel.slice(0, Math.min(hash, dot)) : sel; 801 | elm = vnode.elm = isDef(data) && isDef(i = data.ns) ? api.createElementNS(i, tag) 802 | : api.createElement(tag); 803 | if (hash < dot) elm.id = sel.slice(hash + 1, dot); 804 | if (dotIdx > 0) elm.className = sel.slice(dot + 1).replace(/\./g, ' '); 805 | if (is.array(children)) { 806 | for (i = 0; i < children.length; ++i) { 807 | api.appendChild(elm, createElm(children[i], insertedVnodeQueue)); 808 | } 809 | } else if (is.primitive(vnode.text)) { 810 | api.appendChild(elm, api.createTextNode(vnode.text)); 811 | } 812 | for (i = 0; i < cbs.create.length; ++i) cbs.create[i](emptyNode, vnode); 813 | i = vnode.data.hook; // Reuse variable 814 | if (isDef(i)) { 815 | if (i.create) i.create(emptyNode, vnode); 816 | if (i.insert) insertedVnodeQueue.push(vnode); 817 | } 818 | } else { 819 | elm = vnode.elm = api.createTextNode(vnode.text); 820 | } 821 | return vnode.elm; 822 | } 823 | 824 | function addVnodes(parentElm, before, vnodes, startIdx, endIdx, insertedVnodeQueue) { 825 | for (; startIdx <= endIdx; ++startIdx) { 826 | api.insertBefore(parentElm, createElm(vnodes[startIdx], insertedVnodeQueue), before); 827 | } 828 | } 829 | 830 | function invokeDestroyHook(vnode) { 831 | var i, j, data = vnode.data; 832 | if (isDef(data)) { 833 | if (isDef(i = data.hook) && isDef(i = i.destroy)) i(vnode); 834 | for (i = 0; i < cbs.destroy.length; ++i) cbs.destroy[i](vnode); 835 | if (isDef(i = vnode.children)) { 836 | for (j = 0; j < vnode.children.length; ++j) { 837 | invokeDestroyHook(vnode.children[j]); 838 | } 839 | } 840 | } 841 | } 842 | 843 | function removeVnodes(parentElm, vnodes, startIdx, endIdx) { 844 | for (; startIdx <= endIdx; ++startIdx) { 845 | var i, listeners, rm, ch = vnodes[startIdx]; 846 | if (isDef(ch)) { 847 | if (isDef(ch.sel)) { 848 | invokeDestroyHook(ch); 849 | listeners = cbs.remove.length + 1; 850 | rm = createRmCb(ch.elm, listeners); 851 | for (i = 0; i < cbs.remove.length; ++i) cbs.remove[i](ch, rm); 852 | if (isDef(i = ch.data) && isDef(i = i.hook) && isDef(i = i.remove)) { 853 | i(ch, rm); 854 | } else { 855 | rm(); 856 | } 857 | } else { // Text node 858 | api.removeChild(parentElm, ch.elm); 859 | } 860 | } 861 | } 862 | } 863 | 864 | function updateChildren(parentElm, oldCh, newCh, insertedVnodeQueue) { 865 | var oldStartIdx = 0, newStartIdx = 0; 866 | var oldEndIdx = oldCh.length - 1; 867 | var oldStartVnode = oldCh[0]; 868 | var oldEndVnode = oldCh[oldEndIdx]; 869 | var newEndIdx = newCh.length - 1; 870 | var newStartVnode = newCh[0]; 871 | var newEndVnode = newCh[newEndIdx]; 872 | var oldKeyToIdx, idxInOld, elmToMove, before; 873 | 874 | while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) { 875 | if (isUndef(oldStartVnode)) { 876 | oldStartVnode = oldCh[++oldStartIdx]; // Vnode has been moved left 877 | } else if (isUndef(oldEndVnode)) { 878 | oldEndVnode = oldCh[--oldEndIdx]; 879 | } else if (sameVnode(oldStartVnode, newStartVnode)) { 880 | patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue); 881 | oldStartVnode = oldCh[++oldStartIdx]; 882 | newStartVnode = newCh[++newStartIdx]; 883 | } else if (sameVnode(oldEndVnode, newEndVnode)) { 884 | patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue); 885 | oldEndVnode = oldCh[--oldEndIdx]; 886 | newEndVnode = newCh[--newEndIdx]; 887 | } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right 888 | patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue); 889 | api.insertBefore(parentElm, oldStartVnode.elm, api.nextSibling(oldEndVnode.elm)); 890 | oldStartVnode = oldCh[++oldStartIdx]; 891 | newEndVnode = newCh[--newEndIdx]; 892 | } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left 893 | patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue); 894 | api.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm); 895 | oldEndVnode = oldCh[--oldEndIdx]; 896 | newStartVnode = newCh[++newStartIdx]; 897 | } else { 898 | if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx); 899 | idxInOld = oldKeyToIdx[newStartVnode.key]; 900 | if (isUndef(idxInOld)) { // New element 901 | api.insertBefore(parentElm, createElm(newStartVnode, insertedVnodeQueue), oldStartVnode.elm); 902 | newStartVnode = newCh[++newStartIdx]; 903 | } else { 904 | elmToMove = oldCh[idxInOld]; 905 | patchVnode(elmToMove, newStartVnode, insertedVnodeQueue); 906 | oldCh[idxInOld] = undefined; 907 | api.insertBefore(parentElm, elmToMove.elm, oldStartVnode.elm); 908 | newStartVnode = newCh[++newStartIdx]; 909 | } 910 | } 911 | } 912 | if (oldStartIdx > oldEndIdx) { 913 | before = isUndef(newCh[newEndIdx+1]) ? null : newCh[newEndIdx+1].elm; 914 | addVnodes(parentElm, before, newCh, newStartIdx, newEndIdx, insertedVnodeQueue); 915 | } else if (newStartIdx > newEndIdx) { 916 | removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx); 917 | } 918 | } 919 | 920 | function patchVnode(oldVnode, vnode, insertedVnodeQueue) { 921 | var i, hook; 922 | if (isDef(i = vnode.data) && isDef(hook = i.hook) && isDef(i = hook.prepatch)) { 923 | i(oldVnode, vnode); 924 | } 925 | var elm = vnode.elm = oldVnode.elm, oldCh = oldVnode.children, ch = vnode.children; 926 | if (oldVnode === vnode) return; 927 | if (!sameVnode(oldVnode, vnode)) { 928 | var parentElm = api.parentNode(oldVnode.elm); 929 | elm = createElm(vnode, insertedVnodeQueue); 930 | api.insertBefore(parentElm, elm, oldVnode.elm); 931 | removeVnodes(parentElm, [oldVnode], 0, 0); 932 | return; 933 | } 934 | if (isDef(vnode.data)) { 935 | for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode); 936 | i = vnode.data.hook; 937 | if (isDef(i) && isDef(i = i.update)) i(oldVnode, vnode); 938 | } 939 | if (isUndef(vnode.text)) { 940 | if (isDef(oldCh) && isDef(ch)) { 941 | if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue); 942 | } else if (isDef(ch)) { 943 | if (isDef(oldVnode.text)) api.setTextContent(elm, ''); 944 | addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue); 945 | } else if (isDef(oldCh)) { 946 | removeVnodes(elm, oldCh, 0, oldCh.length - 1); 947 | } else if (isDef(oldVnode.text)) { 948 | api.setTextContent(elm, ''); 949 | } 950 | } else if (oldVnode.text !== vnode.text) { 951 | api.setTextContent(elm, vnode.text); 952 | } 953 | if (isDef(hook) && isDef(i = hook.postpatch)) { 954 | i(oldVnode, vnode); 955 | } 956 | } 957 | 958 | return function(oldVnode, vnode) { 959 | var i, elm, parent; 960 | var insertedVnodeQueue = []; 961 | for (i = 0; i < cbs.pre.length; ++i) cbs.pre[i](); 962 | 963 | if (isUndef(oldVnode.sel)) { 964 | oldVnode = emptyNodeAt(oldVnode); 965 | } 966 | 967 | if (sameVnode(oldVnode, vnode)) { 968 | patchVnode(oldVnode, vnode, insertedVnodeQueue); 969 | } else { 970 | elm = oldVnode.elm; 971 | parent = api.parentNode(elm); 972 | 973 | createElm(vnode, insertedVnodeQueue); 974 | 975 | if (parent !== null) { 976 | api.insertBefore(parent, vnode.elm, api.nextSibling(elm)); 977 | removeVnodes(parent, [oldVnode], 0, 0); 978 | } 979 | } 980 | 981 | for (i = 0; i < insertedVnodeQueue.length; ++i) { 982 | insertedVnodeQueue[i].data.hook.insert(insertedVnodeQueue[i]); 983 | } 984 | for (i = 0; i < cbs.post.length; ++i) cbs.post[i](); 985 | return vnode; 986 | }; 987 | } 988 | 989 | module.exports = {init: init}; 990 | }); 991 | 992 | var snabbdom$1 = interopDefault(snabbdom); 993 | 994 | var h = createCommonjsModule(function (module) { 995 | var VNode = interopDefault(require$$1); 996 | var is = interopDefault(require$$0); 997 | 998 | function addNS(data, children, sel) { 999 | data.ns = 'http://www.w3.org/2000/svg'; 1000 | 1001 | if (sel !== 'foreignObject' && children !== undefined) { 1002 | for (var i = 0; i < children.length; ++i) { 1003 | addNS(children[i].data, children[i].children, children[i].sel); 1004 | } 1005 | } 1006 | } 1007 | 1008 | module.exports = function h(sel, b, c) { 1009 | var data = {}, children, text, i; 1010 | if (c !== undefined) { 1011 | data = b; 1012 | if (is.array(c)) { children = c; } 1013 | else if (is.primitive(c)) { text = c; } 1014 | } else if (b !== undefined) { 1015 | if (is.array(b)) { children = b; } 1016 | else if (is.primitive(b)) { text = b; } 1017 | else { data = b; } 1018 | } 1019 | if (is.array(children)) { 1020 | for (i = 0; i < children.length; ++i) { 1021 | if (is.primitive(children[i])) children[i] = VNode(undefined, undefined, undefined, children[i]); 1022 | } 1023 | } 1024 | if (sel[0] === 's' && sel[1] === 'v' && sel[2] === 'g') { 1025 | addNS(data, children, sel); 1026 | } 1027 | return VNode(sel, data, children, text, undefined); 1028 | }; 1029 | }); 1030 | 1031 | var sh = interopDefault(h); 1032 | 1033 | var index = createCommonjsModule(function (module, exports) { 1034 | 'use strict'; 1035 | 1036 | Object.defineProperty(exports, '__esModule', { 1037 | value: true 1038 | }); 1039 | var isValidString = function isValidString(param) { 1040 | return typeof param === 'string' && param.length > 0; 1041 | }; 1042 | 1043 | var startsWith = function startsWith(string, start) { 1044 | return string[0] === start; 1045 | }; 1046 | 1047 | var isSelector = function isSelector(param) { 1048 | return isValidString(param) && (startsWith(param, '.') || startsWith(param, '#')); 1049 | }; 1050 | 1051 | var node = function node(h) { 1052 | return function (tagName) { 1053 | return function (first) { 1054 | var arguments$1 = arguments; 1055 | 1056 | for (var _len = arguments.length, rest = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { 1057 | rest[_key - 1] = arguments$1[_key]; 1058 | } 1059 | 1060 | if (isSelector(first)) { 1061 | return h.apply(undefined, [tagName + first].concat(rest)); 1062 | } else if (typeof first === 'undefined') { 1063 | return h(tagName); 1064 | } else { 1065 | return h.apply(undefined, [tagName, first].concat(rest)); 1066 | } 1067 | }; 1068 | }; 1069 | }; 1070 | 1071 | var TAG_NAMES = ['a', 'abbr', 'acronym', 'address', 'applet', 'area', 'article', 'aside', 'audio', 'b', 'base', 'basefont', 'bdi', 'bdo', 'bgsound', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'command', 'content', 'data', 'datalist', 'dd', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'embed', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'frame', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'iframe', 'image', 'img', 'input', 'ins', 'isindex', 'kbd', 'keygen', 'label', 'legend', 'li', 'link', 'listing', 'main', 'map', 'mark', 'marquee', 'math', 'menu', 'menuitem', 'meta', 'meter', 'multicol', 'nav', 'nextid', 'nobr', 'noembed', 'noframes', 'noscript', 'object', 'ol', 'optgroup', 'option', 'output', 'p', 'param', 'picture', 'plaintext', 'pre', 'progress', 'q', 'rb', 'rbc', 'rp', 'rt', 'rtc', 'ruby', 's', 'samp', 'script', 'section', 'select', 'shadow', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'svg', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'title', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr', 'xmp']; 1072 | 1073 | exports['default'] = function (h) { 1074 | var createTag = node(h); 1075 | var exported = { TAG_NAMES: TAG_NAMES, isSelector: isSelector, createTag: createTag }; 1076 | TAG_NAMES.forEach(function (n) { 1077 | exported[n] = createTag(n); 1078 | }); 1079 | return exported; 1080 | }; 1081 | 1082 | module.exports = exports['default']; 1083 | }); 1084 | 1085 | var hh = interopDefault(index); 1086 | 1087 | // 1088 | 1089 | 1090 | var init = function (modules) { 1091 | if ( modules === void 0 ) modules = []; 1092 | 1093 | return snabbdom$1.init(modules); 1094 | } 1095 | 1096 | var html = hh(sh) 1097 | 1098 | 1099 | 1100 | function vdomPatch (patch , init ) { 1101 | return first(scan(patch, init)) 1102 | } 1103 | 1104 | var div = html.div; 1105 | var span = html.span; 1106 | var ol = html.ol; 1107 | var li = html.li; 1108 | var button = html.button; 1109 | 1110 | // TODO: combining many inputs and signals. Need a better way 1111 | var anyInput = function () { 1112 | var inputs = [], len = arguments.length; 1113 | while ( len-- ) inputs[ len ] = arguments[ len ]; 1114 | 1115 | return inputs.reduce(and); 1116 | } 1117 | var anySignal = function () { 1118 | var signals = [], len = arguments.length; 1119 | while ( len-- ) signals[ len ] = arguments[ len ]; 1120 | 1121 | return signals.reduce(or); 1122 | } 1123 | 1124 | var container = document.getElementById('app') 1125 | var patch = init([events, attrs, clss]) 1126 | 1127 | var ref = signalGen(); 1128 | var start = ref[0]; 1129 | var startInput = ref[1]; 1130 | var ref$1 = signalGen(); 1131 | var stop = ref$1[0]; 1132 | var stopInput = ref$1[1]; 1133 | var ref$2 = signalGen(); 1134 | var reset = ref$2[0]; 1135 | var resetInput = ref$2[1]; 1136 | var ref$3 = signalGen(); 1137 | var lap = ref$3[0]; 1138 | var lapInput = ref$3[1]; 1139 | 1140 | var timerInputs = anyInput(startInput, stopInput, resetInput, lapInput) 1141 | var stoppedInputs = anyInput(timerInputs, never) 1142 | var runningInputs = anyInput(timerInputs, animationFrame) 1143 | 1144 | // Render timer using current time 1145 | // Returns [inputs, vtree] 1146 | var render = function (timer, time) { 1147 | var elapsed = timerElapsed(time, timer) 1148 | var zero = elapsed === 0 1149 | var vtree = div('.timer', { class: { running: timer.running, zero: zero } }, [ 1150 | div('.elapsed', renderDuration(elapsed)), 1151 | div('.lap-elapsed', renderDuration(timerCurrentLap(time, timer))), 1152 | button('.reset', { on: { click: reset }, attrs: { disabled: timer.running || zero } }, 'Reset'), 1153 | button('.start', { on: { click: start } }, 'Start'), 1154 | button('.stop', { on: { click: stop } }, 'Stop'), 1155 | button('.lap', { on: { click: lap }, attrs: { disabled: !timer.running } }, 'Lap'), 1156 | ol('.laps', { attrs: { reversed: true } }, timer.laps.map(function (ref) { 1157 | var start = ref.start; 1158 | var end = ref.end; 1159 | 1160 | return li(renderDuration(end - start)); 1161 | }) 1162 | ) 1163 | ]) 1164 | 1165 | return [vtree, timer.running ? runningInputs : stoppedInputs] 1166 | } 1167 | 1168 | // Timer formatting 1169 | var renderDuration = function (ms) { return [ 1170 | span('.minutes', ("" + (mins(ms)))), 1171 | span('.seconds', ("" + (secs(ms)))), 1172 | span('.hundredths', ("" + (hundredths(ms)))) 1173 | ]; } 1174 | 1175 | var mins = function (ms) { return pad((ms / (1000 * 60)) % 60); } 1176 | var secs = function (ms) { return pad((ms / 1000) % 60); } 1177 | var hundredths = function (ms) { return pad((ms / 10) % 100); } 1178 | var pad = function (n) { return n < 10 ? ("0" + (Math.floor(n))) : ("" + (Math.floor(n))); } 1179 | 1180 | // Timer functions 1181 | var timerZero = ({ running: false, origin: 0, total: 0, laps: [] }) 1182 | var timerReset = function (time) { return function (_) { return ({ running: false, origin: time, total: 0, laps: [] }); }; } 1183 | 1184 | var timerStart = function (time) { return function (ref) { 1185 | var total = ref.total; 1186 | var laps = ref.laps; 1187 | 1188 | return ({ running: true, origin: time, total: total, laps: laps }); 1189 | ; } } 1190 | var timerStop = function (time) { return function (ref) { 1191 | var origin = ref.origin; 1192 | var total = ref.total; 1193 | var laps = ref.laps; 1194 | 1195 | return ({ running: false, origin: time, total: timerTotal(origin, total, time), laps: laps }); 1196 | ; } } 1197 | var timerLap = function (time) { return function (ref) { 1198 | var running = ref.running; 1199 | var origin = ref.origin; 1200 | var total = ref.total; 1201 | var laps = ref.laps; 1202 | 1203 | return ({ running: running, origin: origin, total: total, laps: timerAddLap(timerTotal(origin, total, time), laps) }); 1204 | ; } } 1205 | 1206 | var timerAddLap = function (end, laps) { return [{ start: timerLastLapEnd(laps), end: end }].concat(laps); } 1207 | var timerLastLapEnd = function (laps) { return laps.length === 0 ? 0 : laps[0].end; } 1208 | var timerCurrentLap = function (time, ref) { 1209 | var running = ref.running; 1210 | var origin = ref.origin; 1211 | var total = ref.total; 1212 | var laps = ref.laps; 1213 | 1214 | return timerTotal(origin, total, time) - timerLastLapEnd(laps); 1215 | } 1216 | var timerElapsed = function (time, ref) { 1217 | var origin = ref.origin; 1218 | var total = ref.total; 1219 | 1220 | return timerTotal(origin, total, time); 1221 | } 1222 | var timerTotal = function (origin, total, time) { return total + (time - origin); } 1223 | 1224 | // Timer events, each tagged with its occurrence time 1225 | var doStart = pipe(eventTime, map(timerStart)) 1226 | var doStop = pipe(eventTime, map(timerStop)) 1227 | var doReset = pipe(eventTime, map(timerReset)) 1228 | var doLap = pipe(eventTime, map(timerLap)) 1229 | 1230 | // An interactive timer that responds to start, stop, reset, and lap events 1231 | // by changing (i.e. accumulating) state 1232 | var timer = pipe(anySignal(doStart, doStop, doReset, doLap), accum(timerZero)) 1233 | 1234 | // Pair an interactive timer, with the (continuous) current time 1235 | var runTimer = both(timer, time) 1236 | var displayTimer = unsplit(render) 1237 | 1238 | var ref$4 = render(timerZero, 0); 1239 | var vtree = ref$4[0]; 1240 | var inputs = ref$4[1]; 1241 | var updateTimer = pipe(runTimer, displayTimer, vdomPatch(patch, patch(container, vtree))) 1242 | 1243 | loop(clockSession(), inputs, updateTimer) 1244 | 1245 | }()); --------------------------------------------------------------------------------