Clear

const clear$ = frp.compose(
    dom.createEventStream('#ex1-button', 'click'),
    frp.map(event => '')
)

clear$(text => dom.select('#ex1-input').value = text)

Copy

const copy$ = frp.compose(
    dom.createEventStream('#ex2-input', 'input'),
    frp.map(event => event.target.value)
)

copy$(text => dom.select('#ex2-label').textContent = text)

Reverse

const copy$ = frp.compose(
    dom.createEventStream('#ex3-input', 'input'),
    frp.map(event => event.target.value),
    frp.map(text => text.split('').reverse().join(''))
)

copy$(text => dom.select('#ex3-label').textContent = text)

Count

const count$ = frp.compose(
    dom.createEventStream('#ex4-button', 'click'),
    frp.map(event => 1),
    frp.fold((accum, curr) => accum + curr, 0)
)

count$(count => dom.select('#ex4-label').textContent = count)

Step

const add$ = frp.compose(
    dom.createEventStream('#ex5-addBtn', 'click'),
    frp.map(event => +1)
)
const sub$ = frp.compose(
    dom.createEventStream('#ex5-subBtn', 'click'),
    frp.map(event => -1)
)
const count$ = frp.compose(
    add$,
    frp.merge(sub$),
    frp.fold((accum, curr) => accum + curr, 0)
)

count$(count => dom.select('#ex5-label').textContent = count)

Hold

const red$ = frp.compose(
    dom.createEventStream('#ex6-redBtn', 'click'),
    frp.map(event => 'red')
)
const green$ = frp.compose(
    dom.createEventStream('#ex6-greenBtn', 'click'),
    frp.map(event => 'green')
)
const color$ = frp.compose(
    red$,
    frp.merge(green$)
)

const colorValue = frp.stepper(color$, null)
color$(color => 
  dom.select('#ex6-label').textContent = frp.snapshot(colorValue)
)

Encrypt

const input$ = frp.compose(
    dom.createEventStream('#ex7-input', 'input'),
    frp.map(event => event.target.value)
)
const inputValue = frp.stepper(input$, 'Hello')
const click$ = dom.createEventStream('#ex7-button', 'click')

click$(click => 
  dom.select('#ex7-label').textContent = rot13(frp.snapshot(inputValue))
)

Add

const input1$ = frp.compose(
    dom.createEventStream('#ex8-input1', 'input'),
    frp.map(event => event.target.value),
    frp.map(value => parseInt(value))
)
const input2$ = frp.compose(
    dom.createEventStream('#ex8-input2', 'input'),
    frp.map(event => event.target.value),
    frp.map(value => parseInt(value))
)
const inputs$ = frp.compose(
    input1$,
    frp.merge(input2$)
)
const totalValue = frp.liftN(
    (a, b) => a + b, 
    frp.stepper(input1$, 5), 
    frp.stepper(input2$, 10)
)

inputs$(input => 
  dom.select('#ex8-label').textContent = frp.snapshot(totalValue)
)

Hub

const prices = Array.from(dom.selectAll('.price'))
const totals = Array.from(dom.selectAll('.total'))

const vat$ = frp.compose(
    dom.createEventStream('#vat', 'change'),
    frp.map(event => 1 + (event.target.value / 100)),
    frp.hub()
)

totals.forEach(function(total, i) {
    const price = Number(prices[i].textContent)
    vat$(factor => total.textContent = (price * factor).toFixed(2))  
})
Prices VAT(%) Totals
430 473.00
375 412.50
120 132.00
250 275.00
215 236.50

Form validation

// Helpers for validating input
// Returns an error message if validation fails
const validators = {
    "name": function(value) {
        const name = value.trim()

        if (name.length == 0)
            return "Required"
        if (/([^A-Za-z\s])/.test(name))
            return "Cannot contains special characters"
        if (name.indexOf(' ') < 0)
            return "Please enter your full name"
        return ""
    },

    "quantity": function(value) {
        const number = parseInt(value)

        if (number < 1 || number > 3)
            return "Choose a number between 1 and 3"
        return ""
    },

    "email": function(value) {
        const email = value.trim()

        if (/\S+@\S+\.\S+/.test(email))
            return ""
        return "Please enter a valid email address"
    }
}

// Helper for verifying if all inputs are valid 
function allValidationsPassed(events) {
    let validations = []
    for(let key in events) {
        if (typeof events[key] == "object")
            validations.push(events[key].valid)
    }
    return validations.length == 3 &&
           validations.reduce((a, b) => a && b)
}

// Input stream
const inputs$ = frp.compose(
    // Merge the input streams
    $.createEventStream('#name', 'input'),
    frp.merge($.createEventStream('#email', 'change')),
    frp.merge($.createEventStream('#quantity', 'input')),

    // Pull out the element, name, value and assign a validator
    frp.map(event => ({
        element: event.target,
        name: event.target.name,
        value: event.target.value,
        validator: validators[event.target.name]
    })),

    // Run the validator on the input value
    frp.map(event => {
        event.errorMessage = event.validator(event.value),
        event.valid = (event.errorMessage == "") ? true : false

        return event
    }),

    // Fold all events, keep track of the current event
    // and check if all validations have passed
    frp.fold((accumulator, currentEvent) => {
        accumulator[currentEvent.name] = currentEvent
        accumulator.current = currentEvent.name
        accumulator.allValid = allValidationsPassed(accumulator)

        return accumulator
    }, {})
)

// Activation
inputs$(events => {
    // Show/hide the error message
    const currentEvent     = events[events.current]
    const formGroupElement = currentEvent.element.parentElement
    const errorSpanElement = currentEvent.element.nextElementSibling

    if (currentEvent.valid == false) {
        $.addClass(formGroupElement, 'has-error')
        errorSpanElement.textContent = currentEvent.errorMessage
    } else {
        $.removeClass(formGroupElement, 'has-error')
        errorSpanElement.textContent = ""
    }

    // Enable/disable the submit button
    $.select('#submit').disabled = !events.allValid
})