Join our webinar on Wednesday, June 26, at 1pm EDTHow Chia Mitigates Risk in the Crypto Industry.Register
Socket
Socket
Sign inDemoInstall

attain

Package Overview
Dependencies
1
Maintainers
1
Versions
172
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.0.182 to 0.0.183

934

docs/examples/tape-player.js
/* globals v */
function App({ v, route, stream }){
v.css.css('body', `
margin: 0px;
padding: 0px;
d grid
jc center
ac center
bc rgba(0,0,0,0.05)
`)
v.css.css('body', `
margin: 0px;
padding: 0px;
d grid
jc center
ac center
bc rgba(0,0,0,0.05)
`)
// ⚠ Warning typos abound. PR's Welcome 😃
// https://gitlab.com/harth/attain
// ⚠ Warning typos abound. PR's Welcome 😃
// https://gitlab.com/harth/attain
route=
// our state machine is a router
route.subroute('TapePlayer', x => x.Stopped({ time: 0 }), {
Stopped: '/stopped/:time',
Playing: '/playing/:time',
Paused: '/paused/:time',
FF: '/ff/:after/:time',
RW: '/rw/:after/:time'
}, {
// default to history.replaceState for this router
// so our history doesn't have thousands of entry for each
// new tape player state
replace: true
})
route=
// our state machine is a router
route.subroute('TapePlayer', x => x.Stopped({ time: 0 }), {
Stopped: '/stopped/:time',
Playing: '/playing/:time',
Paused: '/paused/:time',
FF: '/ff/:after/:time',
RW: '/rw/:after/:time'
}, {
// default to history.replaceState for this router
// so our history doesn't have thousands of entry for each
// new tape player state
replace: true
})
// Creates a partial fold we can spread into route.fold
// to avoid a lot of repetition.
const _ =
v.otherwise(['Paused', 'Stopped', 'Playing', 'RW', 'FF'])
// Creates a partial fold we can spread into route.fold
// to avoid a lot of repetition.
const _ =
v.otherwise(['Paused', 'Stopped', 'Playing', 'RW', 'FF'])
const seek =
v.otherwise(['FF', 'RW'])
const seek =
v.otherwise(['FF', 'RW'])
const scrubbable =
v.otherwise(['Paused', 'FF', 'RW'])
const scrubbable =
v.otherwise(['Paused', 'FF', 'RW'])
// Whenever we transition from 1 state to the next we use this
// reducer. It 's a nested fold of the route type.
// It returns an Either of the next state which allows
// us to reject a state with a message for debugging.
// `Y(next)` means we can proceed to the next state
// `N(message)` means we cannot proceed, and there's a reason
// why encoded in the structure.
//
// `Either` is built into attain for situations exactly like this.
// It's also a sum-type just like our router.
// Whenever we transition from 1 state to the next we use this
// reducer. It 's a nested fold of the route type.
// It returns an Either of the next state which allows
// us to reject a state with a message for debugging.
// `Y(next)` means we can proceed to the next state
// `N(message)` means we cannot proceed, and there's a reason
// why encoded in the structure.
//
// `Either` is built into attain for situations exactly like this.
// It's also a sum-type just like our router.
const transition = (prev, next) => {
const transition = (prev, next) => {
// This is a partial fold we can mix into a larger
// fold to avoid repeating logic.
const scrubs = {
RW: ({ time }) => time > 0
? v.Y(next)
: v.N('Already at the beginning')
, FF: ({ time }) => time < total()
? v.Y(next)
: v.N('Already at the end')
}
// This is a partial fold we can mix into a larger
// fold to avoid repeating logic.
const scrubs = {
RW: ({ time }) => time > 0
? v.Y(next)
: v.N('Already at the beginning')
, FF: ({ time }) => time < total()
? v.Y(next)
: v.N('Already at the end')
}
const decision =
v.run(
prev
,
route.fold({
Playing: () => v.run(
next
, route.fold({
..._( () => v.Y(next) )
,
...scrubs
, Playing: () => v.N('Already playing')
})
)
,
Paused: () => v.run(
next
, route.fold({
..._( () => v.Y(next) )
,
...scrubs
, Paused: () => v.N('Already paused')
})
)
,
Stopped: () => v.run(
next
, route.fold({
..._( () => v.Y(next) )
,
...scrubs
, Playing({ time }){
if( Number(time) >= total() ) {
return v.N('You cannot press play if you have reached the end of a track')
}
return v.Y(next)
}
, Paused: () => v.N('You cannot pause if you are stopped')
, Stopped: () => v.N('Already stopped')
})
)
,
FF: ({ time }) => v.run(
time
, v.tagBy( 'Reached the end of the track', x => x < total )
, v.map( () => next )
, v.chain(
route.fold({
..._( () => v.Y(next) )
,
...scrubs
, FF: () => v.N('Already fast-forwarding.')
})
)
// enforce existing time
, v.map( v.$.value.time(time) )
)
,
RW: ({ time }) => v.run(
time
, v.tagBy( 'Reached the end of the track', x => x > 0 )
, v.map( () => next )
, v.chain(
route.fold({
..._( () => v.Y(next) )
,
...scrubs
, RW: () => v.N('Already rewinding.')
})
)
// enforce existing time
, v.map( v.$.value.time(time) )
)
})
)
const decision =
v.run(
prev
,
route.fold({
Playing: () => v.run(
next
, route.fold({
..._( () => v.Y(next) )
,
...scrubs
, Playing: () => v.N('Already playing')
})
)
,
Paused: () => v.run(
next
, route.fold({
..._( () => v.Y(next) )
,
...scrubs
, Paused: () => v.N('Already paused')
})
)
,
Stopped: () => v.run(
next
, route.fold({
..._( () => v.Y(next) )
,
...scrubs
, Playing({ time }){
if( Number(time) >= total() ) {
return v.N('You cannot press play if you have reached the end of a track')
}
return v.Y(next)
}
, Paused: () => v.N('You cannot pause if you are stopped')
, Stopped: () => v.N('Already stopped')
})
)
,
FF: ({ time }) => v.run(
time
, v.tagBy( 'Reached the end of the track', x => x < total )
, v.map( () => next )
, v.chain(
route.fold({
..._( () => v.Y(next) )
,
...scrubs
, FF: () => v.N('Already fast-forwarding.')
})
)
// enforce existing time
, v.map( v.$.value.time(time) )
)
,
RW: ({ time }) => v.run(
time
, v.tagBy( 'Reached the end of the track', x => x > 0 )
, v.map( () => next )
, v.chain(
route.fold({
..._( () => v.Y(next) )
,
...scrubs
, RW: () => v.N('Already rewinding.')
})
)
// enforce existing time
, v.map( v.$.value.time(time) )
)
})
)
return decision
}
return decision
}
// Not all state changes are transitions.
// This loop function runs every requestAnimationFrame
// and increments the time forward/backward depending
// on the current state.
const loop = ({ x, dt }) => {
// Not all state changes are transitions.
// This loop function runs every requestAnimationFrame
// and increments the time forward/backward depending
// on the current state.
const loop = ({ x, dt }) => {
const decision = v.run(
x
, route.fold({
Paused: route.Paused
,
Stopped: route.Stopped
,
Playing({ time }){
return (
Number(time) + dt > total()
? route.Stopped({ time: total() })
: route.Playing({ time: (Number(time) + dt).toFixed(1) })
)
}
,
FF({ time, after }){
const shift = dt * 8
return (
Number(time) > total()
? route.Stopped({ time: total() })
: route.FF({
time: (Number(time) + shift).toFixed(1)
, after
})
)
}
,
RW({ time, after }){
const shift = dt * 8
return (
Number(time) - dt <= 0
? route.Stopped({ time: 0 })
: route.RW({
time:
Math.max(0, (Number(time) - shift))
.toFixed(1)
, after
})
)
}
})
)
const decision = v.run(
x
, route.fold({
Paused: route.Paused
,
Stopped: route.Stopped
,
Playing({ time }){
return (
Number(time) + dt > total()
? route.Stopped({ time: total() })
: route.Playing({ time: (Number(time) + dt).toFixed(1) })
)
}
,
FF({ time, after }){
const shift = dt * 8
return (
Number(time) > total()
? route.Stopped({ time: total() })
: route.FF({
time: (Number(time) + shift).toFixed(1)
, after
})
)
}
,
RW({ time, after }){
const shift = dt * 8
return (
Number(time) - dt <= 0
? route.Stopped({ time: 0 })
: route.RW({
time:
Math.max(0, (Number(time) - shift))
.toFixed(1)
, after
})
)
}
})
)
return decision
}
return decision
}
const validTransition = (b) => v.isY( transition(route(), b))
const update = stream()
const transitions = stream()
// `redrawService` inspect transitions and redraws
// so disabled button states refresh
const redrawService = (a,b) => {
if( a.value.time < total() && b.value.time >= total() ) {
v.redraw()
} else if (a.value.time >= 0 && b.value.time <= 0 ) {
v.redraw()
}
}
// Because there's two ways state progresses
// we model that as sum type as well.
// Our Update's are either requestAnimatinFrame
// "game loop" style changes or Transitions
// from Sum Type state to the next "FSM style".
const Update =
v.tags('Update', ['RAF', 'Transition'])
const validTransition = (b) => v.isY( transition(route(), b))
const update = stream()
const transitions = stream()
// When the UI pushes into the transitions stream
// we tag the data with Update.Transition and push
// it into the central update stream for processing.
transitions.map(
next => update(Update.Transition(next))
)
// Because there's two ways state progresses
// we model that as sum type as well.
// Our Update's are either requestAnimatinFrame
// "game loop" style changes or Transitions
// from Sum Type state to the next "FSM style".
const Update =
v.tags('Update', ['RAF', 'Transition'])
// Every ~16ms we push an Update.Raf value
// into the central update stream with the
// delta time in ms.
// Attain provides this as a primative because
// it's so useful for UI programming.
stream.raf().map(
({ dt }) => update(Update.RAF({ dt }))
)
// When the UI pushes into the transitions stream
// we tag the data with Update.Transition and push
// it into the central update stream for processing.
transitions.map(
next => update(Update.Transition(next))
)
// Now we process each update.
// We always have access to the previous state `a`
// and the next state `b`
// `b` is our `Update` value, so we can fold over it
// to process each case of `Update` specifically.
// Every ~16ms we push an Update.Raf value
// into the central update stream with the
// delta time in ms.
// Attain provides this as a primative because
// it's so useful for UI programming.
stream.raf().map(
({ dt }) => update(Update.RAF({ dt }))
)
stream.scan( route()) (
(a,b) => {
// Now we process each update.
// We always have access to the previous state `a`
// and the next state `b`
// `b` is our `Update` value, so we can fold over it
// to process each case of `Update` specifically.
const f = Update.fold({
stream.scan( route()) (
(a,b) => {
RAF: ({ dt }) => loop({ x: a, dt })
const f = Update.fold({
,
RAF: ({ dt }) => loop({ x: a, dt })
Transition: b =>
v.getOr(a) (transition(a,b))
,
})
Transition: b =>
v.getOr(a) (transition(a,b))
// Here we apply the fold
// with the next update value
const out = f(b)
})
// And by returning the result
// it is now streamed directly into the router
return out
}
) (update)
// Here we apply the fold
// with the next update value
const out = f(b)
// this is where the router receives the new state
.map( route )
// Decide when to render based on
// certain boundary conditions
redrawService(a, out)
const button = (src, attrs={}) =>
v('div'
+ v.css`
bc #1043d8
border 0
w 3.5em
h 3.5em
br 0.8em
transition 0.2s
d grid
jc center
ac center
`
.$nest('[disabled]',`
bc rgba(0,0,0,0.2)
`)
, attrs
, v('img'
+ v.css`
filter: invert(100);
`
, { src }
)
)
// And by returning the result
// it is now streamed directly into the router
return out
}
) (update)
const transitionButton = (icon, transition, attrs=() => {}) =>
button(icon, {
...attrs( transition )
})
// this is where the router receives the new state
.map( route )
const play = () =>
transitionButton(
'https://attain.harth.io/examples/play.svg'
, () => route.Playing({ time: route().value.time })
, normalBehaviour
)
const button = (src, attrs={}) =>
v('div'
+ v.css`
bc #1043d8
border 0
w 3.5em
h 3.5em
br 0.8em
transition 0.2s
d grid
jc center
ac center
`
.$nest('[disabled]',`
bc rgba(0,0,0,0.2)
`)
, attrs
, v('img'
+ v.css`
filter: invert(100);
`
, { src }
)
)
const pause = () =>
transitionButton(
'https://attain.harth.io/examples/pause.svg'
, () => route.Paused({ time: route().value.time })
, normalBehaviour
)
const transitionButton = (icon, transition, attrs=() => {}) =>
button(icon, {
...attrs( transition )
})
const stop = () =>
transitionButton(
'https://attain.harth.io/examples/square.svg'
, () => route.Stopped({ time: route().value.time })
, normalBehaviour
)
const play = () =>
transitionButton(
'https://attain.harth.io/examples/play.svg'
, () => route.Playing({ time: route().value.time })
, normalBehaviour
)
const pause = () =>
transitionButton(
'https://attain.harth.io/examples/pause.svg'
, () => route.Paused({ time: route().value.time })
, normalBehaviour
)
const mouseUpBehaviour = x => ({
onmousedown: () => transitions(x())
, onmouseup: () => {
const stop = () =>
transitionButton(
'https://attain.harth.io/examples/square.svg'
, () => route.Stopped({ time: route().value.time })
, normalBehaviour
)
const after = route.fold({
... _ ( () => v.N() ),
... seek (({ after }) => v.Y(after))
})
const pauseAfter =
v.chain( v.tagBy('NotPaused', x => x == 'Paused' ) )
const mouseUpBehaviour = x => ({
onmousedown: () => transitions(x())
, onmouseup: () => {
v.run(
route()
, after
, pauseAfter
, v.map(() => transitions(
route.Paused({ time: route().value.time })
))
)
}
, disabled: !validTransition( x() )
})
const after = route.fold({
... _ ( () => v.N() ),
... seek (({ after }) => v.Y(after))
})
const normalBehaviour = x => ({
onmousedown(){ transitions(x()) }
, disabled: !validTransition(x())
})
const pauseAfter =
v.chain( v.tagBy('NotPaused', x => x == 'Paused' ) )
v.run(
route()
, after
, pauseAfter
, v.map(() => transitions(
route.Paused({ time: route().value.time })
))
)
}
, disabled: !validTransition( x() )
})
const scrubbingBehaviour =
route.map(
route.fold({
..._ ( () => normalBehaviour )
,
... scrubbable (
() => mouseUpBehaviour
)
})
)
const normalBehaviour = x => ({
onmousedown(){ transitions(x()) }
, disabled: !validTransition(x())
})
const ff = () =>
transitionButton(
'https://attain.harth.io/examples/fast-forward.svg'
, () => route.FF({ time: route().value.time, after: route().tag })
, scrubbingBehaviour()
)
const rw = () =>
transitionButton(
'https://attain.harth.io/examples/rewind.svg'
, () => route.RW({ time: route().value.time, after: route().tag })
, scrubbingBehaviour()
)
const scrubbingBehaviour =
route.map(
route.fold({
..._ ( () => normalBehaviour )
,
... scrubbable (
() => mouseUpBehaviour
)
})
)
const timeIsACircle = (scale) =>
v('.time-circle'
+ v.css`
border-radius: 100%;
width: 8em;
height: 8em;
bc rgba(0,0,0,0.15)
justify-self center;
align-self center;
d grid
jc center
ac center
transform scale(var(--scale))
`
,
{ hook: ({ dom }) =>
scale.map( x => dom.style.setProperty('--scale', x))
}
, v('.center'
+ v.css(`
bc rgba(0,0,0,0.5)
w 1em
h 1em
br 100%
`)
)
)
const ff = () =>
transitionButton(
'https://attain.harth.io/examples/fast-forward.svg'
, () => route.FF({ time: route().value.time, after: route().tag })
, scrubbingBehaviour()
)
const time = route.map( x => x.value.time )
const rw = () =>
transitionButton(
'https://attain.harth.io/examples/rewind.svg'
, () => route.RW({ time: route().value.time, after: route().tag })
, scrubbingBehaviour()
)
const total = stream(30000)
const timeIsACircle = (scale) =>
v('.time-circle'
+ v.css`
border-radius: 100%;
width: 8em;
height: 8em;
bc rgba(0,0,0,0.15)
justify-self center;
align-self center;
d grid
jc center
ac center
transform scale(var(--scale))
`
,
{ hook: ({ dom }) =>
scale.map( x => dom.style.setProperty('--scale', x))
}
, v('.center'
+ v.css(`
bc rgba(0,0,0,0.5)
w 1em
h 1em
br 100%
`)
)
)
const elapsed =
stream.merge([ time, total ]).map(
([time,total]) => (time / total)
)
const time = route.map( x => x.value.time )
const remaining = elapsed.map( x => 1-x )
const total = stream(30000)
return () => v('.app'
+ v.css`
ff Helvetica
d grid
jc center
ac center
gap 1em
const elapsed =
stream.merge([ time, total ]).map(
([time,total]) => (time / total)
)
font-size: 0.9em
`
.desktop(`
font-size: 1em;
`)
.$nest('*', `
user-select: none;
`)
, v('url'
+ v.css`
bc rgba(0,0,0,0.85)
p 1em
br 0.25em
c white
box-shadow 0px 0px 10px 5px rgba(0,0,0,0.1)
position: relative
`
, v('span'
,
{ hook: ({ dom }) =>
route.map(route.toURL).map( x => dom.textContent = x )
}
)
)
, v('.player'
+ v.css(`
bc white
br 1em
p 1em
min-height: 300px;
gap 1em
box-shadow 0px 0px 30px 1px rgba(0,0,0,0.1)
d grid
gtr auto 1fr auto
`)
.desktop(`
min-width 20em
`)
, v('h1'
+ v.css.m(0).p(0).fw(100)
,
// This is a demonstration of using a hook
// to write directly into a dom property
// the stream will automatically be cleaned up
// when the dom node unmounts
{ hook: ({ dom }) =>
time
.map( x => Math.floor(Number(x) / 1000) )
.map( x => dom.textContent = x )
}
)
, v('.time'
+ v.css`
d grid
bc #EEE
border-radius: 1em
padding 1em
display grid
gtc 1fr 1fr
`
, timeIsACircle( remaining )
, timeIsACircle( elapsed )
)
, v('.controls'
+ v.css`
d grid
grid-auto-flow column
justify-content space-between
`
, play()
, rw()
, ff()
, stop()
, pause()
)
)
, v('.credits'
+ v.css`
fs 0.8em
c gray
d grid
jc center
`
.$nest('a, a:visited', `c inherit`)
, v('p'
, 'Original '
, v('a',
{ href: 'https://codesandbox.io/s/state-designer-counter-2nmd5'}
, 'State Designer Example'
)
, ' by '
, v('a',
{ href: 'https://twitter.com/steveruizok' }
, '@steveruizok'
)
const remaining = elapsed.map( x => 1-x )
)
)
)
return () => v('.app'
+ v.css`
ff Helvetica
d grid
jc center
ac center
gap 1em
font-size: 0.9em
`
.desktop(`
font-size: 1em;
`)
.$nest('*', `
user-select: none;
`)
, v('url'
+ v.css`
bc rgba(0,0,0,0.85)
p 1em
br 0.25em
c white
box-shadow 0px 0px 10px 5px rgba(0,0,0,0.1)
position: relative
`
, v('span'
,
{ hook: ({ dom }) =>
route.map(route.toURL).map( x => dom.textContent = x )
}
)
)
, v('.player'
+ v.css(`
bc white
br 1em
p 1em
min-height: 300px;
gap 1em
box-shadow 0px 0px 30px 1px rgba(0,0,0,0.1)
d grid
gtr auto 1fr auto
`)
.desktop(`
min-width 20em
`)
, v('h1'
+ v.css.m(0).p(0).fw(100)
,
// This is a demonstration of using a hook
// to write directly into a dom property
// the stream will automatically be cleaned up
// when the dom node unmounts
{ hook: ({ dom }) =>
time
.map( x => Math.floor(Number(x) / 1000) )
.map( x => dom.textContent = x )
}
)
, v('.time'
+ v.css`
d grid
bc #EEE
border-radius: 1em
padding 1em
display grid
gtc 1fr 1fr
`
, timeIsACircle( remaining )
, timeIsACircle( elapsed )
)
, v('.controls'
+ v.css`
d grid
grid-auto-flow column
justify-content space-between
`
, play()
, rw()
, ff()
, stop()
, pause()
)
)
, v('.credits'
+ v.css`
fs 0.8em
c gray
d grid
jc center
`
.$nest('a, a:visited', `c inherit`)
, v('p'
, 'Original '
, v('a',
{ href: 'https://codesandbox.io/s/state-designer-counter-2nmd5'}
, 'State Designer Example'
)
, ' by '
, v('a',
{ href: 'https://twitter.com/steveruizok' }
, '@steveruizok'
)
)
)
)
}
v(document.body, {
render({ v, ...attrs }){
return v(App, { v, ...attrs })
}
render({ v, ...attrs }){
return v(App, { v, ...attrs })
}
})
{
"name": "attain",
"version": "0.0.182",
"version": "0.0.183",
"description": "A library for modelling and accessing data.",

@@ -5,0 +5,0 @@ "main": "dist/attain.min.js",

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc