<commit_before>var Stores = {};

function GetStore(id) {
  var store = Stores[id];
  if (!store) {
    store = new Store(id);
    Stores[id] = store;
  }
  return store;
}

function Store(id) {
  this.id        = id;
  this.data      = undefined;
  this.callbacks = [];

  this.get = function() {
    return this.data;
  }

  this.set = function(data, metadata) {
    this.data = data;
    this.notify(data, metadata);
  }

  this.notify = function(data, metadata) {
    this.callbacks.forEach(function (callback) {
      callback(data, metadata);
    });
  }

  this.on = function(callback) {
    console.log(this.id, "on", callback);
    this.callbacks.push(callback);
  }

  this.off = function(callback) {
    console.log(this.id, "off", callback);
    var idx = this.callbacks.indexOf(callback);
    if (idx == -1) {
      return;
    }
    this.callbacks.splice(idx, 1);
  }
}

var Mixin = {
  componentWillMount: function() {
    var watches = parseBindExpr(this.props.binding);
    this.setupWatches(watches);
    if (!this.state) {
      return;
    }
    this.watches.forEach(function (watch) {
      if (watch.store.get() !== this.state[watch.ref]) {
        watch.store.set(this.state[watch.ref]);
      }
    }.bind(this));
  },
  componentWillReceiveProps: function(nextProps) {
    var watches = parseBindExpr(nextProps.watch);
    this.setupWatches(watches);
  },
  setupWatches: function(watches) {
    this.removeWatches();

    this.watches = [];
    var state    = {};
    var watches  = parseBindExpr(this.props.binding);
    watches.forEach(function (watch) {
      watch.store = GetStore(watch.id);
      state[watch.ref] = watch.store.get();
      watch.callback = this.receiveUpdate.bind(this, watch.ref);
      this.watches.push(watch);
      watch.store.on(watch.callback);
    }.bind(this));
    this.setState(state);
  },
  receiveUpdate: function(ref, data, metadata) {
    var state = {};
    state[ref] = data;
    var callback = null;
    if (this.props.onKizuna) {
      callback = function() {
        this.props.onKizuna(ref, data, metadata)
      }.bind(this);
    }
    this.setState(state, callback);
  },
  componentWillUpdate: function(nextProps, nextState) {
    var watches = this.watches;
    watches.forEach(function (watch) {
      if (this.state[watch.ref] !== nextState[watch.ref] &&
          watch.store.get() !== nextState[watch.ref]) {
        watch.store.set(nextState[watch.ref], {src: "componentWillUpdate"});
      }
    }.bind(this));
  },
  componentWillUnmount: function() {
    this.removeWatches();
  },
  removeWatches: function() {
    var watches = this.watches;
    if (!watches) {
      return;
    }
    watches.forEach(function (watch) {
      var store = GetStore(watch.id);
      store.off(watch.callback);
    });
  }
}

function parseBindExpr(watchExpr) {
  if (!watchExpr) {
    return [];
  }

  if (Object.prototype.toString.call(watchExpr) != "[object Array]") {
    return parseBindObject(watchExpr);
  }

  var watches = [];
  watchExpr.forEach(function (watch) {
    var id, ref;
    if (typeof watch == "string") {
      id  = watch;
      ref = watch;
    } else if (Object.prototype.toString.call(watch) == "[object Array]") {
      id  = watch[1];
      ref = watch[0];
    } else {
      throw "Kizuna: invalid watch expression";
    }
    watches.push({id: id, ref: ref});
  });
  return watches;
}

function parseBindObject(watchExpr) {
  var watches = [];
  Object.keys(watchExpr).forEach(function (ref) {
    var id = watchExpr[ref];
    watches.push({id: id, ref: ref});
  });
  return watches;
}

module.exports = {
  Store: Store,
  Mixin: Mixin,

  getStore: GetStore
}<commit_msg>clean up<commit_after>var Stores = {};

function getStore(id) {
  var store = Stores[id];
  if (!store) {
    store = new Store(id);
    Stores[id] = store;
  }
  return store;
}

function Store(id) {
  this.id        = id;
  this.data      = undefined;
  this.callbacks = [];

  this.get = function() {
    return this.data;
  }

  this.set = function(data, metadata) {
    this.data = data;
    this.notify(data, metadata);
  }

  this.notify = function(data, metadata) {
    this.callbacks.forEach(function (callback) {
      callback(data, metadata);
    });
  }

  this.on = function(callback) {
    this.callbacks.push(callback);
  }

  this.off = function(callback) {
    var idx = this.callbacks.indexOf(callback);
    if (idx == -1) {
      return;
    }
    this.callbacks.splice(idx, 1);
  }
}

var Mixin = {
  componentWillMount: function() {
    this.setupWatches();
    if (!this.state) {
      return;
    }

    // reflect values given in getInitialState()
    this.watches.forEach(function (watch) {
      if (watch.store.get() !== this.state[watch.ref]) {
        watch.store.set(this.state[watch.ref]);
      }
    }.bind(this));
  },
  componentWillReceiveProps: function(nextProps) {
    this.setupWatches();
  },
  receiveUpdate: function(ref, data, metadata) {
    var state = {};
    state[ref] = data;
    var callback = null;
    if (this.props.onKizuna) {
      callback = function() {
        this.props.onKizuna(ref, data, metadata)
      }.bind(this);
    }
    this.setState(state, callback);
  },
  componentWillUpdate: function(nextProps, nextState) {
    this.watches.forEach(function (watch) {
      if (this.state[watch.ref] !== nextState[watch.ref] &&
          watch.store.get() !== nextState[watch.ref]) {
        watch.store.set(nextState[watch.ref], {src: "componentWillUpdate"});
      }
    }, this);
  },
  componentWillUnmount: function() {
    this.removeWatches();
  },
  setupWatches: function() {
    this.removeWatches();

    this.watches = [];
    var state    = {};
    var watches  = parseBindExpr(this.props.binding);
    watches.forEach(function (watch) {
      watch.store = getStore(watch.id);
      state[watch.ref] = watch.store.get();
      watch.callback = this.receiveUpdate.bind(this, watch.ref);
      this.watches.push(watch);
      watch.store.on(watch.callback);
    }, this);
    this.setState(state);
  },
  removeWatches: function() {
    var watches = this.watches;
    if (!watches) {
      return;
    }
    watches.forEach(function (watch) {
      var store = getStore(watch.id);
      store.off(watch.callback);
    });
  }
}

function parseBindExpr(bindExpr) {
  if (!bindExpr) {
    return [];
  }

  if (Object.prototype.toString.call(bindExpr) != "[object Array]") {
    return parseBindObject(bindExpr);
  }

  var watches = [];
  bindExpr.forEach(function (watch) {
    var id, ref;
    if (typeof watch == "string") {
      id  = watch;
      ref = watch;
    } else if (Object.prototype.toString.call(watch) == "[object Array]") {
      id  = watch[1];
      ref = watch[0];
    } else {
      throw "Kizuna: invalid watch expression";
    }
    watches.push({id: id, ref: ref});
  });
  return watches;
}

function parseBindObject(bindExpr) {
  var watches = [];
  Object.keys(bindExpr).forEach(function (ref) {
    var id = bindExpr[ref];
    watches.push({id: id, ref: ref});
  });
  return watches;
}

module.exports = {
  Store: Store,
  Mixin: Mixin,

  getStore: getStore
}<|endoftext|>


<commit_before>#!/usr/bin/env node
// @flow

import fs from 'sb-fs'
import path from 'path'
import pMap from 'p-map'
import mapValues from 'lodash/mapValues'
import get from 'lodash/get'
import omit from 'lodash/omit'
import pick from 'lodash/pick'
import uniq from 'lodash/uniq'
import chalk from 'chalk'
import mkdirp from 'mkdirp'
import express from 'express'
import minimist from 'minimist'
import coolTrim from 'cool-trim'
import gzipSize from 'gzip-size'
import stripAnsi from 'strip-ansi'
import prettyBytes from 'pretty-bytes'
import { getPundle, getWatcher } from 'pundle-core'
import { getPundleDevMiddleware, getChunksAffectedByImports } from 'pundle-dev-middleware'
import type { Context, ChunksGenerated } from 'pundle-api'

import manifest from '../package.json'
import { getNextPort, getStaticMappings } from './helpers'

const PUNDLE_ARGS = ['directory', 'configFilePath', 'configLoadFile']
const OMIT_ARGS = PUNDLE_ARGS.concat(['_', 'dev', 'watch'])

const argv = mapValues(
  minimist(process.argv.slice(2), {
    string: ['configFilePath', 'directory'],
    boolean: ['configLoadFile'],
    default: {
      dev: {},
      watch: {},
      configLoadFile: true,
    },
  }),
  value => {
    if (value === 'true') return true
    if (value === 'false') return false
    return value
  },
)

const pundleArgs = pick(argv, PUNDLE_ARGS)
const pundleConfig = omit(argv, OMIT_ARGS)

function log(contents: string): void {
  console.log(chalk.supportsColor ? contents : stripAnsi(contents))
}

async function writeFile(context: Context, filePath: string, contents: string | Buffer): Promise<void> {
  await new Promise(function(resolve, reject) {
    mkdirp(path.dirname(filePath), function(err) {
      if (err) {
        reject(err)
      } else resolve()
    })
  })
  await fs.writeFile(filePath, contents)
  const gzipSizeOfFile = await gzipSize(contents)
  log(
    `  Writing '${chalk.blue(path.relative(context.config.rootDirectory, filePath))}' ${chalk.green(
      prettyBytes(contents.length),
    )} (${chalk.red(prettyBytes(gzipSizeOfFile))} gzipped) `,
  )
}
async function logFile(filePath: string, contents: string | Buffer): Promise<void> {
  const gzipSizeOfFile = await gzipSize(contents)
  log(
    `  ${chalk.blue(filePath)} - ${chalk.green(prettyBytes(contents.length))} (${chalk.red(
      prettyBytes(gzipSizeOfFile),
    )} gzipped) `,
  )
}
async function writeCompiledChunks(context: Context, generated: ChunksGenerated) {
  await pMap(
    generated.outputs,
    async ({ filePath, sourceMap, contents }) => {
      if (!filePath) {
        // Ignore this one
        return
      }
      await writeFile(context, path.resolve(generated.directory, filePath), contents)
      if (sourceMap && sourceMap.filePath) {
        await writeFile(context, path.resolve(generated.directory, sourceMap.filePath), sourceMap.contents)
      }
    },
    { concurrency: 1 },
  )
}
async function generateForWatcher({ pundle, job, changed }) {
  const transformedJob = await pundle.transformJob(job)
  const chunks = Array.from(transformedJob.chunks.values())
  const chunksToRegenerate = changed ? getChunksAffectedByImports(job, chunks, changed) : chunks
  await writeCompiledChunks(pundle.context, await pundle.generate(job, chunksToRegenerate))
}

// TODO: Use report for all errors
// TODO: Try to break this with errors in different places
let pundle
async function main() {
  if (argv.v || argv.version) {
    console.log(`Pundle v${manifest.version} - The Next Generation Module Bundler`)
    process.exit(0)
  }
  if (argv.help || argv.h) {
    log(coolTrim`
    Usage: pundle [--version] [--help] [--watch] [--dev]

    These are the most commonly used option combinations:
      pundle                      Compile the contents of a project and write to output directory
      pundle --watch              Just like compile but watches and recompiles on changes
      pundle --dev                Compile the contents and start an http server on a port (3000 by default)
                                  but do not write to output directory
      pundle --directory <path>   Start Pundle in a specific directory (supports other options)

    These are the most popular used options (but others/all are supported from Pundle in dot notation):
      --version                   Print the version of the program
      --help                      Show this help text
      --directory                 Start pundle in a specific directory (is process.cwd() by default)
      --configFilePath            File path to Pundle config file (by default it's 'pundle.config.js')
                                  resolved from above mentioned directory
      --configLoadFile            Controls config loading behavior, set to false to disable loading
                                  config file
      --dev.hmr                   Controls availability of HMR APIs in dev server (enabled by default)
      --dev.lazy                  Controls if the dev server waits for first request to compile or
                                  compiles instantly
      --dev.static local::public  Allows mapping local static directories to the dist server. May be specified
                                  multiple times. Paths may be relative to pundle config's root directory.
                                  Use '::' to separate local and server paths. (eg. --dev.static ./static::/assets)
      --watch.adapter             Choose between nsfw and chokidar. Adapters used to watch filesystem changes
      --cache                     Controls whether to enable or disable caching (enabled by default)
      --cache.reset               Controls whether to reset cache on boot
      --output.rootDirectory      Override control defined output directory to use this one
    `)
    process.exit(1)
  }
  pundle = await getPundle({
    ...pundleArgs,
    config: pundleConfig,
  })

  const isDev = argv.dev === true || Object.keys(argv.dev).length > 0
  const isWatch = argv.watch === true || Object.keys(argv.watch).length > 0

  const watchAdapter = get(argv, 'watch.adapter')
  const watchConfig = { ...(watchAdapter ? { adapter: watchAdapter } : {}) }

  const headerText = `${pundle.context.config.cache.enabled ? 'with' : 'without'}${
    pundle.context.config.cache.reset ? ' resetted' : ''
  } cache${watchAdapter ? ` and with adapter ${watchAdapter}` : ''}`

  if (!isDev) {
    if (isWatch) {
      log(`Watching (${headerText})`)
      const { job, initialCompile } = await getWatcher({
        ...watchConfig,
        pundle,
        generate({ changed }) {
          const changedFiles = uniq(changed.map(i => i.filePath)).map(i =>
            path.relative(pundle.context.config.rootDirectory, i),
          )
          log(`Files affected:\n${changedFiles.map(i => `  - ${i}`).join('\n')}`)
          return generateForWatcher({ pundle, job, changed })
        },
      })
      try {
        await initialCompile()
      } catch (error) {
        // TODO: Report instead
        console.error(error)
      }
      await generateForWatcher({ pundle, job, changed: null })
      return
    }
    log(`Compiling (${headerText})`)
    console.time('Compiled')
    const result = await pundle.execute()
    console.timeEnd('Compiled')
    await writeCompiledChunks(pundle.context, result)
    pundle.dispose()
    return
  }

  const specifiedDevPort = parseInt(get(argv, 'dev.port', 0), 10)
  const devPort = specifiedDevPort || 3000
  const devHost = get(argv, 'dev.host', '127.0.0.1')
  const staticMappings = getStaticMappings(pundle, argv)

  const devPortToUse = await getNextPort(devPort)
  if (devPortToUse !== devPort && specifiedDevPort) {
    log(chalk.yellow(`Unable to listen on port ${specifiedDevPort} - Is another program using that port?`))
  }

  log(`Starting Dev Server at ${chalk.blue(`http://localhost:${devPortToUse}/`)} (${headerText})`)
  if (staticMappings.length) {
    log(`Static mappings:`)
    staticMappings.sort((a, b) => a.remote.length - b.remote.length).forEach(item => {
      log(`  ${item.remote} ~> ${item.local}`)
    })
  }

  const app = express()
  const middlewarePromise = getPundleDevMiddleware({
    ...pundleArgs,
    publicPath: '/',
    ...argv.dev,
    config: pundleConfig,
    watchConfig,
    changedCallback(changed) {
      const changedFiles = uniq(changed.map(i => i.filePath)).map(i => path.relative(pundle.context.config.rootDirectory, i))
      log(`  Files affected:\n${changedFiles.map(i => `    - ${i}`).join('\n')}`)
    },
    generatedCallback(url, contents) {
      logFile(url, contents).catch(pundle.report)
    },
  })
  app.use(function(req, res, next) {
    middlewarePromise.then(() => next(), next)
  })
  await new Promise((resolve, reject) => {
    const server = app.listen(devPort, devHost)
    server.on('error', reject)
    server.on('listening', resolve)
  })
  app.use(await middlewarePromise)
  staticMappings.sort((a, b) => b.remote.length - a.remote.length).forEach(item => {
    app.use(item.remote, express.static(item.local))
  })
  log('Started Successfully')
}

main().catch(error => {
  if (pundle) {
    pundle.dispose()
  }
  console.error(error)
})
<commit_msg>:new: Add dev.singlepage parameter<commit_after>#!/usr/bin/env node
// @flow

import fs from 'sb-fs'
import path from 'path'
import pMap from 'p-map'
import mapValues from 'lodash/mapValues'
import get from 'lodash/get'
import omit from 'lodash/omit'
import pick from 'lodash/pick'
import uniq from 'lodash/uniq'
import chalk from 'chalk'
import mkdirp from 'mkdirp'
import express from 'express'
import minimist from 'minimist'
import coolTrim from 'cool-trim'
import gzipSize from 'gzip-size'
import stripAnsi from 'strip-ansi'
import prettyBytes from 'pretty-bytes'
import { getPundle, getWatcher } from 'pundle-core'
import { getPundleDevMiddleware, getChunksAffectedByImports } from 'pundle-dev-middleware'
import type { Context, ChunksGenerated } from 'pundle-api'

import manifest from '../package.json'
import { getNextPort, getStaticMappings } from './helpers'

const PUNDLE_ARGS = ['directory', 'configFilePath', 'configLoadFile']
const OMIT_ARGS = PUNDLE_ARGS.concat(['_', 'dev', 'watch'])

const argv = mapValues(
  minimist(process.argv.slice(2), {
    string: ['configFilePath', 'directory'],
    boolean: ['configLoadFile'],
    default: {
      dev: {},
      watch: {},
      configLoadFile: true,
    },
  }),
  value => {
    if (value === 'true') return true
    if (value === 'false') return false
    return value
  },
)

const pundleArgs = pick(argv, PUNDLE_ARGS)
const pundleConfig = omit(argv, OMIT_ARGS)

function log(contents: string): void {
  console.log(chalk.supportsColor ? contents : stripAnsi(contents))
}

async function writeFile(context: Context, filePath: string, contents: string | Buffer): Promise<void> {
  await new Promise(function(resolve, reject) {
    mkdirp(path.dirname(filePath), function(err) {
      if (err) {
        reject(err)
      } else resolve()
    })
  })
  await fs.writeFile(filePath, contents)
  const gzipSizeOfFile = await gzipSize(contents)
  log(
    `  Writing '${chalk.blue(path.relative(context.config.rootDirectory, filePath))}' ${chalk.green(
      prettyBytes(contents.length),
    )} (${chalk.red(prettyBytes(gzipSizeOfFile))} gzipped) `,
  )
}
async function logFile(filePath: string, contents: string | Buffer): Promise<void> {
  const gzipSizeOfFile = await gzipSize(contents)
  log(
    `  ${chalk.blue(filePath)} - ${chalk.green(prettyBytes(contents.length))} (${chalk.red(
      prettyBytes(gzipSizeOfFile),
    )} gzipped) `,
  )
}
async function writeCompiledChunks(context: Context, generated: ChunksGenerated) {
  await pMap(
    generated.outputs,
    async ({ filePath, sourceMap, contents }) => {
      if (!filePath) {
        // Ignore this one
        return
      }
      await writeFile(context, path.resolve(generated.directory, filePath), contents)
      if (sourceMap && sourceMap.filePath) {
        await writeFile(context, path.resolve(generated.directory, sourceMap.filePath), sourceMap.contents)
      }
    },
    { concurrency: 1 },
  )
}
async function generateForWatcher({ pundle, job, changed }) {
  const transformedJob = await pundle.transformJob(job)
  const chunks = Array.from(transformedJob.chunks.values())
  const chunksToRegenerate = changed ? getChunksAffectedByImports(job, chunks, changed) : chunks
  await writeCompiledChunks(pundle.context, await pundle.generate(job, chunksToRegenerate))
}

// TODO: Use report for all errors
// TODO: Try to break this with errors in different places
let pundle
async function main() {
  if (argv.v || argv.version) {
    console.log(`Pundle v${manifest.version} - The Next Generation Module Bundler`)
    process.exit(0)
  }
  if (argv.help || argv.h) {
    log(coolTrim`
    Usage: pundle [--version] [--help] [--watch] [--dev]

    These are the most commonly used option combinations:
      pundle                      Compile the contents of a project and write to output directory
      pundle --watch              Just like compile but watches and recompiles on changes
      pundle --dev                Compile the contents and start an http server on a port (3000 by default)
                                  but do not write to output directory
      pundle --directory <path>   Start Pundle in a specific directory (supports other options)

    These are the most popular used options (but others/all are supported from Pundle in dot notation):
      --version                   Print the version of the program
      --help                      Show this help text
      --directory                 Start pundle in a specific directory (is process.cwd() by default)
      --configFilePath            File path to Pundle config file (by default it's 'pundle.config.js')
                                  resolved from above mentioned directory
      --configLoadFile            Controls config loading behavior, set to false to disable loading
                                  config file
      --dev.hmr                   Controls availability of HMR APIs in dev server (enabled by default)
      --dev.lazy                  Controls if the dev server waits for first request to compile or
                                  compiles instantly
      --dev.static local::public  Allows mapping local static directories to the dist server. May be specified
                                  multiple times. Paths may be relative to pundle config's root directory.
                                  Use '::' to separate local and server paths. (eg. --dev.static ./static::/assets)
      --dev.singlepage            Redirects all non-404 requests to /index.html. Useful for react single page
                                  apps
      --watch.adapter             Choose between nsfw and chokidar. Adapters used to watch filesystem changes
      --cache                     Controls whether to enable or disable caching (enabled by default)
      --cache.reset               Controls whether to reset cache on boot
      --output.rootDirectory      Override control defined output directory to use this one
    `)
    process.exit(1)
  }
  pundle = await getPundle({
    ...pundleArgs,
    config: pundleConfig,
  })

  const isDev = argv.dev === true || Object.keys(argv.dev).length > 0
  const isWatch = argv.watch === true || Object.keys(argv.watch).length > 0

  const watchAdapter = get(argv, 'watch.adapter')
  const watchConfig = { ...(watchAdapter ? { adapter: watchAdapter } : {}) }

  const headerText = `${pundle.context.config.cache.enabled ? 'with' : 'without'}${
    pundle.context.config.cache.reset ? ' resetted' : ''
  } cache${watchAdapter ? ` and with adapter ${watchAdapter}` : ''}`

  if (!isDev) {
    if (isWatch) {
      log(`Watching (${headerText})`)
      const { job, initialCompile } = await getWatcher({
        ...watchConfig,
        pundle,
        generate({ changed }) {
          const changedFiles = uniq(changed.map(i => i.filePath)).map(i =>
            path.relative(pundle.context.config.rootDirectory, i),
          )
          log(`Files affected:\n${changedFiles.map(i => `  - ${i}`).join('\n')}`)
          return generateForWatcher({ pundle, job, changed })
        },
      })
      try {
        await initialCompile()
      } catch (error) {
        // TODO: Report instead
        console.error(error)
      }
      await generateForWatcher({ pundle, job, changed: null })
      return
    }
    log(`Compiling (${headerText})`)
    console.time('Compiled')
    const result = await pundle.execute()
    console.timeEnd('Compiled')
    await writeCompiledChunks(pundle.context, result)
    pundle.dispose()
    return
  }

  const specifiedDevPort = parseInt(get(argv, 'dev.port', 0), 10)
  const devPort = specifiedDevPort || 3000
  const devHost = get(argv, 'dev.host', '127.0.0.1')
  const staticMappings = getStaticMappings(pundle, argv)

  const devPortToUse = await getNextPort(devPort)
  if (devPortToUse !== devPort && specifiedDevPort) {
    log(chalk.yellow(`Unable to listen on port ${specifiedDevPort} - Is another program using that port?`))
  }

  log(`Starting Dev Server at ${chalk.blue(`http://localhost:${devPortToUse}/`)} (${headerText})`)
  if (staticMappings.length) {
    log(`Static mappings:`)
    staticMappings.sort((a, b) => a.remote.length - b.remote.length).forEach(item => {
      log(`  ${item.remote} ~> ${item.local}`)
    })
  }

  const app = express()
  const middlewarePromise = getPundleDevMiddleware({
    ...pundleArgs,
    publicPath: '/',
    ...argv.dev,
    config: pundleConfig,
    watchConfig,
    changedCallback(changed) {
      const changedFiles = uniq(changed.map(i => i.filePath)).map(i => path.relative(pundle.context.config.rootDirectory, i))
      log(`  Files affected:\n${changedFiles.map(i => `    - ${i}`).join('\n')}`)
    },
    generatedCallback(url, contents) {
      logFile(url, contents).catch(pundle.report)
    },
  })
  app.use(function(req, res, next) {
    middlewarePromise.then(() => next(), next)
  })
  await new Promise((resolve, reject) => {
    const server = app.listen(devPort, devHost)
    server.on('error', reject)
    server.on('listening', resolve)
  })
  app.use(await middlewarePromise)
  staticMappings.sort((a, b) => b.remote.length - a.remote.length).forEach(item => {
    app.use(item.remote, express.static(item.local))
  })
  app.use(function(req, res, next) {
    if (req.url !== '/index.html') {
      req.url = '/index.html'
      middlewarePromise.then(callback => callback(req, res, next)).catch(next)
    } else next()
  })
  log('Started Successfully')
}

main().catch(error => {
  if (pundle) {
    pundle.dispose()
  }
  console.error(error)
})
<|endoftext|>


<commit_before>//usage:
//tab_metro.setUp(bpm , the element you want to attach metronome);<=must do this
//tab_metor.play();
//you can use setBpm to change bpm, and attachTo() to attach metronome on more element
//Don't use like :document.addEventListener('click',tab_metor.play)
//Use like :document.addEventListener('click',function(){ tab_metor.play(); })
let tab_metro={
	bpm:120,
	playing:false,
	setUp:function(b=120,e=null){
		this.bpm=b;
		this.content=document.createElement('div');
		this.content.setAttribute('style',`
			width:420px;height:250px;
			background:URL('./img/tick_bg.png') #565656;
			text-align:center;
			position:relative;`);
		this.content.innerHTML=`
${this.bpm}

bpm

			`;
		this.tick=(function(){this.content.children[3].style.transform="translateX(0px)";}).bind(this);
		this.tock=(function(){this.content.children[3].style.transform="translateX(390px)";}).bind(this);
		this.attachTo(e);
	},
	attachTo:function(e){
		this.displayer=e;
		e.appendChild(this.content);
	},
	setBpm:function(b){
		this.bpm=b;
		this.content.children[0].innerHTML=this.bpm;
	},
	play:function(){
		if(this.playing)return;
		this.playing=true;
		let spb=60000/this.bpm;
		this.content.children[3].style.transition=`transform ${spb}ms linear`;
		this.content.children[3].style.transform="translateX(390px)";
		setTimeout(()=>{
			this.content.children[3].style.transform="translateX(0px)";
			this.tickInt=setInterval(this.tick,spb*2);
		},spb);
		this.tockInt=setInterval(this.tock,spb*2);
	},
	stop:function(){
		clearInterval(this.tickInt);
		clearInterval(this.tockInt);
		this.content.children[3].style.transform="translateX(0px)";
	}
};<commit_msg>update metronome<commit_after>//usage:
//tab_metro.setUp(bpm , the element you want to attach metronome);<=must do this
//tab_metor.play();
//you can use setBpm to change bpm, and attachTo() to attach metronome on more element
//Don't use like :document.addEventListener('click',tab_metor.play)
//Use like :document.addEventListener('click',function(){ tab_metor.play(); })
let tab_metro={
	bpm:120,
	playing:false,
	setUp:function(b=120,e=null){
		this.bpm=b;
		this.content=document.createElement('div');
		this.content.setAttribute('style',`
			width:420px;height:250px;
			background:URL('./img/tick_bg.png') #565656;
			text-align:center;
			position:relative;`);
		this.content.innerHTML=`
${this.bpm}

bpm

			`;
		this.tick=(function(){this.content.children[3].style.transform="translateX(0px)";}).bind(this);
		this.tock=(function(){this.content.children[3].style.transform="translateX(390px)";}).bind(this);
		this.attachTo(e);
	},
	attachTo:function(e){
		this.displayer=e;
		e.appendChild(this.content);
	},
	setBpm:function(b){
		this.bpm=b;
		this.content.children[0].innerHTML=this.bpm;
	},
	play:function(){
		if(this.playing)return;
		this.playing=true;
		let spb=60000/this.bpm;
		this.content.children[3].style.transition=`transform ${spb}ms linear`;
		this.content.children[3].style.transform="translateX(390px)";
		setTimeout(()=>{
			this.content.children[3].style.transform="translateX(0px)";
			this.tickInt=setInterval(this.tick,spb*2);
		},spb);
		this.tockInt=setInterval(this.tock,spb*2);
	},
	stop:function(){
		clearInterval(this.tickInt);
		clearInterval(this.tockInt);
		this.playing=false;
		this.content.children[3].style.transform="translateX(0px)";
	}
};<|endoftext|>


<commit_before>// Dependencies

let assign = require('object-assign');
let type = require('focus').component.types;
let builder = require('focus').component.builder;
let React = require('react');

// Components

let SearchBar  = require('../../../search/search-bar').component;

// Mixins

let ScrollInfoMixin = require('../common/scroll-info-mixin').mixin;
let GroupByMixin= require('../common/group-by-mixin').mixin;
let SearchMixin = require('../common/search-mixin').mixin;

/**
 * General search mixin.
 * Contains a search bar, and a results list.
 * @type {Object}
 */
let QuickSearchMixin = {
    mixins: [ScrollInfoMixin, GroupByMixin, SearchMixin],
    /**
     * Tag name.
     */
    displayName: 'quick-search',
    /**
     * Component initialization
     */
    componentDidMount(){
        this._registerListeners();
    },

    /**
     * Actions before component will unmount.
     * @constructor
     */
    componentWillUnmount(){
        this._unRegisterListeners();
    },
    getDefaultProps(){
        return {
            isSelection: false,
            idField: 'id',
            SearchBar: SearchBar,
            groupMaxRows: 3
        };
    },
    /**
     * properties validation
     */
    propTypes: {
        lineMap: type('object'),
        isSelection: type('bool'),
        lineOperationList: type('array'),
        idField: type('string'),
        SearchBar: type('func'),
        groupMaxRows: type('number')
    },
    /**
     * Initial state of the list component.
     * @returns {{list: (*|Array)}} the state
     */
    getInitialState() {
        return {
            isAllSelected: false,
            selected: []
        };
    },
    getCriteria() {
      if(!this.refs.searchBar){
        return {};
      }
      return this.refs.searchBar.getValue();
    },
    /**
     * Register a listener on the store.
     * @private
     */
    _registerListeners(){
        if(this.store){
            this.store.addSearchChangeListener(this.onSearchChange);
        } else {
            console.warn('Search result has no store to listen to. Please provide one as a "store" property.');
        }
    },
    /**
     * Unregister a listener on the store.
     * @private
     */
    _unRegisterListeners(){
        if(this.store){
            this.store.removeSearchChangeListener(this.onSearchChange);
        }
    },
    /**
     * Handler when store emit a change event.
     */
    onSearchChange() {
        this.setState(assign({isLoadingSearch: false}, this.getScrollState()));
    },
    /**
     * Action on item selection.
     * @param {object} item selected
     */
    _selectItem(item){
        let selected = this.state.selected;
        let index = selected.indexOf(item);
        if(index){
            selected.splice(index, index);
        }else{
            selected.push(item);
        }
        this.setState({selected});
    },
    /**
     * Action on line click.
     * @param {object} item  the item clicked
     */
    _lineClick(item){
        if(this.props.onLineClick){
            this.props.onLineClick(item);
        }
    },
    _prepareSearch(searchValues){
        clearTimeout(this._searchTimeout);
        this._searchTimeout = setTimeout(() => {
            this.setState(
                assign(
                    {isLoadingSearch: true},
                    searchValues,
                    this.getNoFetchState()
                ),
                this.search
            );
        }, 500);
    },
    /**
     * return a SearchBar
     * @returns {XML} the component
     */
    getSearchBarComponent() {
        return (
            <this.props.SearchBar handleChange={this._prepareSearch}
                ref='searchBar'
                scope={this.props.scope}
                scopes={this.props.scopeList}
                loading={this.state.isLoadingSearch}
            />
        );
    }
};

module.exports = builder(QuickSearchMixin, true);
<commit_msg>Quick search is now a full component<commit_after>// Dependencies

let assign = require('object-assign');
let type = require('focus').component.types;
let builder = require('focus').component.builder;
let React = require('react');

// Components

let SearchBar  = require('../../../search/search-bar').component;

// Mixins

let ScrollInfoMixin = require('../common/scroll-info-mixin').mixin;
let GroupByMixin= require('../common/group-by-mixin').mixin;
let SearchMixin = require('../common/search-mixin').mixin;

/**
 * General search mixin.
 * Contains a search bar, and a results list.
 * @type {Object}
 */
let QuickSearchComponent = {
    mixins: [ScrollInfoMixin, GroupByMixin, SearchMixin],
    /**
     * Tag name.
     */
    displayName: 'quick-search',
    /**
     * Component initialization
     */
    componentDidMount(){
        this._registerListeners();
    },

    /**
     * Actions before component will unmount.
     * @constructor
     */
    componentWillUnmount(){
        this._unRegisterListeners();
    },
    getDefaultProps(){
        return {
            isSelection: false,
            idField: 'id',
            SearchBar: SearchBar,
            groupMaxRows: 3
        };
    },
    /**
     * properties validation
     */
    propTypes: {
        lineMap: type('object'),
        isSelection: type('bool'),
        lineOperationList: type('array'),
        idField: type('string'),
        SearchBar: type('func'),
        groupMaxRows: type('number')
    },
    /**
     * Initial state of the list component.
     * @returns {{list: (*|Array)}} the state
     */
    getInitialState() {
        return {
            isAllSelected: false,
            selected: []
        };
    },
    getCriteria() {
      if(!this.refs.searchBar){
        return {};
      }
      return this.refs.searchBar.getValue();
    },
    /**
     * Register a listener on the store.
     * @private
     */
    _registerListeners(){
        if(this.props.store){
            this.props.store.addSearchChangeListener(this.onSearchChange);
        } else {
            console.warn('Search result has no store to listen to. Please provide one as a "store" property.');
        }
    },
    /**
     * Unregister a listener on the store.
     * @private
     */
    _unRegisterListeners(){
        if(this.props.store){
            this.props.store.removeSearchChangeListener(this.onSearchChange);
        }
    },
    /**
     * Handler when store emit a change event.
     */
    onSearchChange() {
        this.setState(assign({isLoadingSearch: false}, this.getScrollState()));
    },
    /**
     * Action on item selection.
     * @param {object} item selected
     */
    _selectItem(item){
        let selected = this.state.selected;
        let index = selected.indexOf(item);
        if(index){
            selected.splice(index, index);
        }else{
            selected.push(item);
        }
        this.setState({selected});
    },
    /**
     * Action on line click.
     * @param {object} item  the item clicked
     */
    _lineClick(item){
        if(this.props.onLineClick){
            this.props.onLineClick(item);
        }
    },
    _prepareSearch(searchValues){
        clearTimeout(this._searchTimeout);
        this._searchTimeout = setTimeout(() => {
            this.setState(
                assign(
                    {isLoadingSearch: true},
                    searchValues,
                    this.getNoFetchState()
                ),
                this.search
            );
        }, 500);
    },
    /**
     * return a SearchBar
     * @returns {XML} the component
     */
    getSearchBarComponent() {
        return (
            <this.props.SearchBar handleChange={this._prepareSearch}
                ref='searchBar'
                scope={this.props.scope}
                scopes={this.props.scopeList}
                loading={this.state.isLoadingSearch}
            />
        );
    },
    render() {
        return (
            <div className="search-panel" data-focus="quick-search">
                {this.getSearchBarComponent()}
                {this.getResultListComponent()}
            </div>
        );
    }
};

module.exports = builder(QuickSearchComponent);
<|endoftext|>


<commit_before>define(function() {
  return (opt = {}) => {
    let tableStyleStr = '';
    let cellStyleStr = '';
    let editor = opt.editor;
    let tableStyle = opt.tableStyle || {};
    let cellStyle = opt.cellStyle || {};
    let bm = editor.BlockManager;
    for (let prop in tableStyle) {
      tableStyleStr += `${prop}: ${tableStyle[prop]}; `;
    }
    for (let prop in cellStyle) {
      cellStyleStr += `${prop}: ${cellStyle[prop]}; `;
    }
    bm.getAll().reset();
    bm.add('sect100', {
      label: opt.sect100BlkLabel,
      category: opt.categoryLabel,
      attributes: { class: 'gjs-fonts gjs-f-b1' },
      content: `

`,
    });
    bm.add('sect50', {
      label: opt.sect50BlkLabel,
      category: opt.categoryLabel,
      attributes: { class: 'gjs-fonts gjs-f-b2' },
      content: `

`,
    });
    bm.add('sect30', {
      label: opt.sect30BlkLabel,
      category: opt.categoryLabel,
      attributes: { class: 'gjs-fonts gjs-f-b3' },
      content: `

`,
    });
    bm.add('sect37', {
      label: opt.sect37BlkLabel,
      category: opt.categoryLabel,
      attributes: { class: 'gjs-fonts gjs-f-b37' },
      content: `

`,
    });
    bm.add('button', {
      label: opt.buttonBlkLabel,
      category: opt.categoryLabel,
      content: 'Button',
      attributes: { class: 'gjs-fonts gjs-f-button' }
    });
    bm.add('divider', {
      label: opt.dividerBlkLabel,
      category: opt.categoryLabel,
      content: `

        `,
      attributes: { class: 'gjs-fonts gjs-f-divider' }
    });
    bm.add('text', {
      label: opt.textBlkLabel,
      category: opt.categoryLabel,
      attributes: { class: 'gjs-fonts gjs-f-text' },
      content: {
        type: 'text',
        content: 'Insert your text here',
        style: {
          padding: '10px'
        },
        activeOnRender: 1
      },
    });
    bm.add('text-sect', {
      label: opt.textSectionBlkLabel,
      category: opt.categoryLabel,
      content: '
Insert title here

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua
',
      attributes: { class: 'gjs-fonts gjs-f-h1p' }
    });
    bm.add('image', {
      label: opt.imageBlkLabel,
      category: opt.categoryLabel,
      attributes: { class: 'gjs-fonts gjs-f-image' },
      content: {
        type: 'image',
        style: {
          color: 'black'
        },
        activeOnRender: 1
      },
    });
    bm.add('quote', {
      label: opt.quoteBlkLabel,
      category: opt.categoryLabel,
      content: '
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore ipsum dolor sit
',
      attributes: { class: 'fa fa-quote-right' }
    });
    bm.add('link', {
      label: opt.linkBlkLabel,
      category: opt.categoryLabel,
      attributes: { class: 'fa fa-link' },
      content: {
        type: 'link',
        content: 'Link',
        style: {
          color: '#3b97e3'
        }
      },
    });
    bm.add('link-block', {
      label: opt.linkBlockBlkLabel,
      category: opt.categoryLabel,
      attributes: { class: 'fa fa-link' },
      content: {
        type: 'link',
        editable: false,
        droppable: true,
        style: {
          display: 'inline-block',
          padding: '5px',
          'min-height': '50px',
          'min-width': '50px'
        }
      },
    });
    let gridItem =
      `

Title here

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt

`;
    bm.add('grid-items', {
      label: opt.gridItemsBlkLabel,
      category: opt.categoryLabel,
      content: `

          ${gridItem}
          ${gridItem}

`,
      attributes: { class: 'fa fa-th' }
    });
    let listItem =
      `

Title here

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt

`;
    bm.add('list-items', {
      label: opt.listItemsBlkLabel,
      category: opt.categoryLabel,
      content: listItem + listItem,
      attributes: { class: 'fa fa-th-list' }
    });
  };
})
<commit_msg>More tabs adjustments<commit_after>define(function() {
  return (opt = {}) => {
    let tableStyleStr = '';
    let cellStyleStr = '';
    let editor = opt.editor;
    let tableStyle = opt.tableStyle || {};
    let cellStyle = opt.cellStyle || {};
    let bm = editor.BlockManager;
    for (let prop in tableStyle){
      tableStyleStr += `${prop}: ${tableStyle[prop]}; `;
    }
    for (let prop in cellStyle){
      cellStyleStr += `${prop}: ${cellStyle[prop]}; `;
    }
    bm.getAll().reset();
    bm.add('sect100', {
      label: opt.sect100BlkLabel,
      category: opt.categoryLabel,
      attributes: {class:'gjs-fonts gjs-f-b1'},
      content: `

`,
    });
    bm.add('sect50', {
      label: opt.sect50BlkLabel,
      category: opt.categoryLabel,
      attributes: {class:'gjs-fonts gjs-f-b2'},
      content: `

`,
    });
    bm.add('sect30', {
      label: opt.sect30BlkLabel,
      category: opt.categoryLabel,
      attributes: {class:'gjs-fonts gjs-f-b3'},
      content: `

`,
    });
    bm.add('sect37', {
      label: opt.sect37BlkLabel,
      category: opt.categoryLabel,
      attributes: {class:'gjs-fonts gjs-f-b37'},
      content: `

`,
    });
    bm.add('button', {
      label: opt.buttonBlkLabel,
      category: opt.categoryLabel,
      content: 'Button',
      attributes: {class:'gjs-fonts gjs-f-button'}
    });
    bm.add('divider', {
      label: opt.dividerBlkLabel,
      category: opt.categoryLabel,
      content: `

        `,
      attributes: {class:'gjs-fonts gjs-f-divider'}
    });
    bm.add('text', {
      label: opt.textBlkLabel,
      category: opt.categoryLabel,
      attributes: {class:'gjs-fonts gjs-f-text'},
      content: {
        type: 'text',
        content: 'Insert your text here',
        style: {
          padding: '10px'
        },
        activeOnRender: 1
      },
    });
    bm.add('text-sect', {
      label: opt.textSectionBlkLabel,
      category: opt.categoryLabel,
      content: '
Insert title here

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua
',
      attributes: { class:'gjs-fonts gjs-f-h1p' }
    });
    bm.add('image', {
      label: opt.imageBlkLabel,
      category: opt.categoryLabel,
      attributes: { class:'gjs-fonts gjs-f-image' },
      content: {
        type: 'image',
        style: {color:'black'},
        activeOnRender: 1
      },
    });
    bm.add('quote', {
      label: opt.quoteBlkLabel,
      category: opt.categoryLabel,
      content: '
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore ipsum dolor sit
',
      attributes: {class:'fa fa-quote-right'}
    });
    bm.add('link', {
      label: opt.linkBlkLabel,
      category: opt.categoryLabel,
      attributes: {class:'fa fa-link'},
      content: {
        type: 'link',
        content: 'Link',
        style: {color:'#3b97e3'}
      },
    });
    bm.add('link-block', {
      label: opt.linkBlockBlkLabel,
      category: opt.categoryLabel,
      attributes: {class:'fa fa-link'},
      content: {
        type: 'link',
        editable: false,
        droppable: true,
        style: {
          display: 'inline-block',
          padding: '5px',
          'min-height': '50px',
          'min-width': '50px'
        }
      },
    });
    let gridItem =
      `

Title here

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt

`;
    bm.add('grid-items', {
      label: opt.gridItemsBlkLabel,
      category: opt.categoryLabel,
      content: `

          ${gridItem}
          ${gridItem}

`,
      attributes: {class:'fa fa-th'}
    });
    let listItem =
      `

Title here

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt

`;
    bm.add('list-items', {
      label: opt.listItemsBlkLabel,
      category: opt.categoryLabel,
      content: listItem + listItem,
      attributes: {class:'fa fa-th-list'}
    });
  };
})
<|endoftext|>


<commit_before>'use strict'
/** allow to use Elm from nodejs...
 */
const path = require('path')
const vm = require('vm')
const fs = require('fs')
const proc = require('process')
const spawn = require('child_process').spawnSync
const debug = proc.env.DEBUG || false
const RETVAL = {
	SUCCESS: 0,
	FAIL: 1,
	EXCEPTION: 2
}

// extract source folder from elm-package.json
const cwd = (() => {
	try {
		const data = fs.readFileSync('elm-package.json' )
		return JSON.parse( data )['source-directories'][0]
	} catch (e) {
		return './'
	}
})()
const testfilename = path.resolve( cwd, './DoctestTempModule__.elm')

function log(o) { console.log(o) }
if ( debug ) log('############## debug mode is ON ##############')

/** loads Elm compiled javascript
 * and returns Elm object
 */
function loadElm( path ) {
	const data = fs.readFileSync( path )
	const context = { console, setInterval, setTimeout, setImmediate }
	vm.runInNewContext( data, context, path )
	return context.Elm
}

/** use generator to serialize the test runner for multiple files
 * if watch is true, then it does not quit even if queue is empty.
 */
let elmfile = ''
function* fileIterator( pretest, watch ) {
	while ( watch || fileQueue.length > 0 ) {
		if ( watch && fileQueue.length == 0 ) {
			yield true
			continue
		}
		elmfile = fileQueue.shift()
		// log(`elmfile: ${ elmfile }`)
		try {
			const run_repl = ( pretest.length == 0 ) || (() => {
				const cmd = pretest[0]
				const args = pretest.slice(1)
				const { stdout, stderr, status } = spawn(cmd, args, { encoding: 'utf8'})
				if ( stdout ) log(`pretest: ${ stdout }`)
				if ( stderr ) log(`pretest err: ${ stderr }`)
				log(`pretest status: ${ status }`)
				return status == 0
			})()
			if ( run_repl ) {
				const elmsrc = fs.readFileSync( elmfile, 'utf8')
				// for some reason, Elm port does not work without delay.
				setTimeout(() => {
					log(`\n processing: ${ elmfile }`)
					app.ports.srccode.send( elmsrc )
				}, 1)
			}
			yield true
		} catch(e) {
			log(`failed to run tests: ${ e }`)
			process.exit( RETVAL.EXCEPTION )
		}
	}
	return
}

// load main Elm script
const Elm = loadElm( path.resolve(__dirname, '../distribution/index.js'))
const app = Elm.Main.worker()

/** receive evaluate message from Elm and elm-make and evaluate
 * test cases, then send it back to Elm.
 */
app.ports.evaluate.subscribe( resource => {
	if ( debug ) {
		log('----------- evaluate called.')
		log( resource )
	}
	if ( resource.src.length == 0 ) return
	// log('writing temporary source into file...')
	fs.writeFileSync( testfilename, resource.src )
	const { stdout, status, error } = spawn(elm_repl, [], { input: resource.runner, encoding: 'utf8'})
	if ( error ) {
		log(`elm-repl failed to run:\n ${ error }`)
	}
	if ( debug ) log( stdout )
	if ( status != 0 ) {
		log(`elm-repl exited with ${ status }`)
	} else {
		const match = stdout.match(/^> (.+)/gm)
		if ( !match ) return []
		const resultStr = match[0].replace(/[^"]*(".+")( : .*)?/, '$1')
		if ( debug ) log( resultStr )
		app.ports.result.send({ stdout: JSON.parse( resultStr ), filename: elmfile })
	}
	if ( !debug && fs.existsSync( testfilename ))
		fs.unlinkSync( testfilename )
})

/** Receive report message from Elm and
 * display results
 */
let returnValue = true
app.ports.report.subscribe( report => {
	if ( report.text.length == 0 ) return
	log( report.text )
	if ( report.failed ) returnValue = false
	runNext( fi )
})

/** Helper function to serialize the tests
 */
function runNext( fi ) {
	const v = fi.next()
	if ( debug ) log( v )
	if ( v.done ) {
		process.exit( returnValue ? RETVAL.SUCCESS : RETVAL.FAIL )
	}
}

/** parse commandline options
 */
function parseOpt( argv ) {
	const optSpec = {
		boolean: ['help', 'version', 'watch'],
		string: ['elm-repl-path', 'pretest'],
		alias: {h: 'help', v: 'version', w: 'watch'}
	}
	const opts = require('minimist')( argv, optSpec )
	if ( debug ) console.log('options:', opts )

	// show usage/help message
	if ( opts.version || opts.help || !opts._ ) {
		(function showHelpMessage() {
			const version = require('../package.json').version
			log(`elm-doctest ${ version }`)
			log('')
			log('Usage: elm-doctest [--watch] [--help] [--elm-repl-path PATH]')
			log('                   [--pretest CMD] FILES...')
			log('  run doctest against given Elm files')
			log('')
			log('Available options:')
			log('  -h,--help\t\t'
				+ 'Show this help text')
			log('  --pretest CMD\t\t'
				+ 'command to run before doc-test')
			log('  --elm-repl-path PATH\t'
				+ 'Path to elm-repl executable')
			log('  -w,--watch\t\t'
				+ 'Watch and run tests when target files get updated')
		})()
		process.exit( RETVAL.SUCCESS )
	}

	return {
		elm_repl: opts['elm-repl-path'] || 'elm-repl',
		fileQueue: opts._,
		pretest: opts.pretest ? opts.pretest.split(' ') : [],
		watch: opts.watch
	}
}

/** main */
const { elm_repl, fileQueue, pretest, watch } = parseOpt( proc.argv.slice(2))
log('Starting elm-doctest ...')

// persist/watch files if `--watch` option was given
if ( watch ) {
	const chokidar = require('chokidar')
	console.log('start watching...', fileQueue )

	chokidar.watch( fileQueue ).on('change', (path, stats) => {
		if ( stats.size == 0 ) return
		console.log(`\nfile has changed...`)
		fileQueue.push( path )
		runNext( fi )
	})
}

// kick-off the test for the 1st time
const fi = fileIterator( pretest, watch )
runNext( fi )

<commit_msg>fix: eslint warning<commit_after>'use strict'
/** allow to use Elm from nodejs...
 */
const path = require('path')
const vm = require('vm')
const fs = require('fs')
const proc = require('process')
const spawn = require('child_process').spawnSync
const debug = proc.env.DEBUG || false
const RETVAL = {
	SUCCESS: 0,
	FAIL: 1,
	EXCEPTION: 2
}

// extract source folder from elm-package.json
const cwd = (() => {
	try {
		const data = fs.readFileSync('elm-package.json' )
		return JSON.parse( data )['source-directories'][0]
	} catch (e) {
		return './'
	}
})()
const testfilename = path.resolve( cwd, './DoctestTempModule__.elm')

function log(o) { console.log(o) }
if ( debug ) log('############## debug mode is ON ##############')

/** loads Elm compiled javascript
 * and returns Elm object
 */
function loadElm( path ) {
	const data = fs.readFileSync( path )
	const context = { console, setInterval, setTimeout, setImmediate }
	vm.runInNewContext( data, context, path )
	return context.Elm
}

/** use generator to serialize the test runner for multiple files
 * if watch is true, then it does not quit even if queue is empty.
 */
let elmfile = ''
function* fileIterator( pretest, watch ) {
	while ( watch || fileQueue.length > 0 ) {
		if ( watch && fileQueue.length == 0 ) {
			yield true
			continue
		}
		elmfile = fileQueue.shift()
		// log(`elmfile: ${ elmfile }`)
		try {
			const run_repl = ( pretest.length == 0 ) || (() => {
				const cmd = pretest[0]
				const args = pretest.slice(1)
				const { stdout, stderr, status } = spawn(cmd, args, { encoding: 'utf8'})
				if ( stdout ) log(`pretest: ${ stdout }`)
				if ( stderr ) log(`pretest err: ${ stderr }`)
				log(`pretest status: ${ status }`)
				return status == 0
			})()
			if ( run_repl ) {
				const elmsrc = fs.readFileSync( elmfile, 'utf8')
				// for some reason, Elm port does not work without delay.
				setTimeout(() => {
					log(`\n processing: ${ elmfile }`)
					app.ports.srccode.send( elmsrc )
				}, 1)
			}
			yield true
		} catch(e) {
			log(`failed to run tests: ${ e }`)
			process.exit( RETVAL.EXCEPTION )
		}
	}
	return
}

// load main Elm script
const Elm = loadElm( path.resolve(__dirname, '../distribution/index.js'))
const app = Elm.Main.worker()

/** receive evaluate message from Elm and elm-make and evaluate
 * test cases, then send it back to Elm.
 */
app.ports.evaluate.subscribe( resource => {
	if ( debug ) {
		log('----------- evaluate called.')
		log( resource )
	}
	if ( resource.src.length == 0 ) return
	// log('writing temporary source into file...')
	fs.writeFileSync( testfilename, resource.src )
	const { stdout, status, error } = spawn(elm_repl, [], { input: resource.runner, encoding: 'utf8'})
	if ( error ) {
		log(`elm-repl failed to run:\n ${ error }`)
	}
	if ( debug ) log( stdout )
	if ( status != 0 ) {
		log(`elm-repl exited with ${ status }`)
	} else {
		const match = stdout.match(/^> (.+)/gm)
		if ( !match ) return []
		const resultStr = match[0].replace(/[^"]*(".+")( : .*)?/, '$1')
		if ( debug ) log( resultStr )
		app.ports.result.send({ stdout: JSON.parse( resultStr ), filename: elmfile })
	}
	if ( !debug && fs.existsSync( testfilename ))
		fs.unlinkSync( testfilename )
})

/** Receive report message from Elm and
 * display results
 */
let returnValue = true
app.ports.report.subscribe( report => {
	if ( report.text.length == 0 ) return
	log( report.text )
	if ( report.failed ) returnValue = false
	runNext( fi )
})

/** Helper function to serialize the tests
 */
function runNext( fi ) {
	const v = fi.next()
	if ( debug ) log( v )
	if ( v.done ) {
		process.exit( returnValue ? RETVAL.SUCCESS : RETVAL.FAIL )
	}
}

/** parse commandline options
 */
function parseOpt( argv ) {
	const optSpec = {
		boolean: ['help', 'version', 'watch'],
		string: ['elm-repl-path', 'pretest'],
		alias: {h: 'help', v: 'version', w: 'watch'}
	}
	const opts = require('minimist')( argv, optSpec )
	if ( debug ) console.log('options:', opts )

	// show usage/help message
	if ( opts.version || opts.help || !opts._ ) {
		(function showHelpMessage() {
			const version = require('../package.json').version
			log(`elm-doctest ${ version }`)
			log('')
			log('Usage: elm-doctest [--watch] [--help] [--elm-repl-path PATH]')
			log('                   [--pretest CMD] FILES...')
			log('  run doctest against given Elm files')
			log('')
			log('Available options:')
			log('  -h,--help\t\t'
				+ 'Show this help text')
			log('  --pretest CMD\t\t'
				+ 'command to run before doc-test')
			log('  --elm-repl-path PATH\t'
				+ 'Path to elm-repl executable')
			log('  -w,--watch\t\t'
				+ 'Watch and run tests when target files get updated')
		})()
		process.exit( RETVAL.SUCCESS )
	}

	return {
		elm_repl: opts['elm-repl-path'] || 'elm-repl',
		fileQueue: opts._,
		pretest: opts.pretest ? opts.pretest.split(' ') : [],
		watch: opts.watch
	}
}

/** main */
const { elm_repl, fileQueue, pretest, watch } = parseOpt( proc.argv.slice(2))
log('Starting elm-doctest ...')

// persist/watch files if `--watch` option was given
if ( watch ) {
	const chokidar = require('chokidar')
	console.log('start watching...', fileQueue )

	chokidar.watch( fileQueue ).on('change', (path, stats) => {
		if ( stats.size == 0 ) return
		console.log('\nfile has changed...')
		fileQueue.push( path )
		runNext( fi )
	})
}

// kick-off the test for the 1st time
const fi = fileIterator( pretest, watch )
runNext( fi )

<|endoftext|>


<commit_before>/** allow to use Elm from nodejs...
 */
const path = require('path')
const vm = require('vm')
const fs = require('fs')
const proc = require('process')
const exec = require('child_process').execSync
const debug = proc.env.DEBUG || false

// extract source folder from elm-package.json
const cwd = (() => {
	try {
		const data = fs.readFileSync('elm-package.json' )
		return JSON.parse( data )['source-directories'][0]
	} catch (e) {
		return './'
	}
})()
const testfilename = path.resolve(cwd, './DoctestTempModule__.elm')
const runnerScript = path.resolve(cwd, './RunnerDoctestTempModule__.elm')

function log( o ) {
	console.log( o )
}
if ( debug ) log('############## debug mode is ON ##############')

/** loads Elm compiled javascript
 * and returns Elm object
 */
function loadElm( path ) {
	const MockBrowser = new require('mock-browser').mocks.MockBrowser
	const browser = new MockBrowser()
	const window = browser.getWindow()
	const document = browser.getDocument()
	const data = fs.readFileSync( path )
	const context = { console, window, document, setInterval, setTimeout, setImmediate }
	vm.runInNewContext( data, context, path )
	return context.Elm
}

/** main */
const Elm = loadElm(path.resolve(__dirname, '../distribution/index.js'))
const app = Elm.Main.worker()

log('Starting elm-doctest ...')
if ( proc.argv.length != 3 ) {
	console.error('need provide elm source file path')
	process.exit( 1 )
}
const elmfile = proc.argv[ 2 ]
try {
	const elmsrc = fs.readFileSync( elmfile, 'utf8')
	setTimeout(() => {  // for some reason, port does not work without delay.
		app.ports.srccode.send( elmsrc )
	}, 1)
} catch(e) {
	log(e)
	process.exit( 1 )
}

app.ports.evaluate.subscribe( resource => {
	if ( debug ) {
		log('----------- evaluate called.')
		log( resource )
	}
	if ( resource.src.length == 0 ) return
	// log('writing temporary source into file...')
	fs.writeFileSync( testfilename, resource.src )
	fs.writeFileSync( runnerScript, resource.runner )
	const stdout = exec(`elm-make ${runnerScript} --output ${runnerScript}.js`,
		{ encoding: 'utf8'})
	if ( debug ) log( stdout )

	const Elm_runner = loadElm(`${runnerScript}.js`)
	const app_runner = Elm_runner.RunnerDoctestTempModule__.worker()
	app_runner.ports.evalResults.subscribe( results => {
		if ( debug ) log( results )
		app.ports.result.send({ stdout: JSON.stringify(results), filename: elmfile })
	})
	if ( !debug && fs.existsSync( testfilename ) && fs.existsSync( runnerScript )) {
		fs.unlinkSync( testfilename )
		fs.unlinkSync( runnerScript )
		fs.unlinkSync(`${runnerScript}.js`)
	}
})

app.ports.report.subscribe( report => {
	if ( report.text.length == 0 ) return
	log( report.text )
	if ( report.failed ) process.exit(1)
	process.exit()
})

<commit_msg>format<commit_after>/** allow to use Elm from nodejs...
 */
const path = require('path')
const vm = require('vm')
const fs = require('fs')
const proc = require('process')
const exec = require('child_process').execSync
const debug = proc.env.DEBUG || false

// extract source folder from elm-package.json
const cwd = (() => {
	try {
		const data = fs.readFileSync('elm-package.json' )
		return JSON.parse( data )['source-directories'][0]
	} catch (e) {
		return './'
	}
})()
const testfilename = path.resolve(cwd, './DoctestTempModule__.elm')
const runnerScript = path.resolve(cwd, './RunnerDoctestTempModule__.elm')

function log(o) {
	console.log(o)
}
if ( debug ) log('############## debug mode is ON ##############')

/** loads Elm compiled javascript
 * and returns Elm object
 */
function loadElm( path ) {
	const MockBrowser = new require('mock-browser').mocks.MockBrowser
	const browser = new MockBrowser()
	const window = browser.getWindow()
	const document = browser.getDocument()
	const data = fs.readFileSync( path )
	const context = { console, window, document, setInterval, setTimeout, setImmediate }
	vm.runInNewContext( data, context, path )
	return context.Elm
}

/** main */
const Elm = loadElm(path.resolve(__dirname, '../distribution/index.js'))
const app = Elm.Main.worker()

log('Starting elm-doctest ...')
if ( proc.argv.length != 3 ) {
	console.error('need provide elm source file path')
	process.exit( 1 )
}
const elmfile = proc.argv[ 2 ]
try {
	const elmsrc = fs.readFileSync( elmfile, 'utf8')
	setTimeout(() => {  // for some reason, port does not work without delay.
		app.ports.srccode.send( elmsrc )
	}, 0)
} catch(e) {
	log(e)
	process.exit(1)
}

app.ports.evaluate.subscribe( resource => {
	if ( debug ) {
		log('----------- evaluate called.')
		log( resource )
	}
	if ( resource.src.length == 0 ) return
	// log('writing temporary source into file...')
	fs.writeFileSync( testfilename, resource.src )
	fs.writeFileSync( runnerScript, resource.runner )
	const stdout = exec(`elm-make ${runnerScript} --output ${runnerScript}.js`,
		{ encoding: 'utf8'})
	if ( debug ) log( stdout )

	const app_runner = loadElm(`${runnerScript}.js`)
		.RunnerDoctestTempModule__.worker()
	app_runner.ports.evalResults.subscribe( results => {
		if ( debug ) log( results )
		app.ports.result.send({ stdout: JSON.stringify(results), filename: elmfile })
	})
	if ( !debug && fs.existsSync( testfilename ) && fs.existsSync( runnerScript )) {
		fs.unlinkSync( testfilename )
		fs.unlinkSync( runnerScript )
		fs.unlinkSync(`${runnerScript}.js`)
	}
})

app.ports.report.subscribe( report => {
	if ( report.text.length == 0 ) return
	log( report.text )
	if ( report.failed ) process.exit(1)
	process.exit()
})

<|endoftext|>


<commit_before>import bar from './charts/bar.js';
import donut from './charts/donut.js';
import legend from './charts/legend.js';
import line from './charts/line.js';
import loadingStates from './charts/helpers/load.js';
import tooltip from './charts/tooltip.js';
import miniTooltip from './charts/mini-tooltip.js';
import sparkline from './charts/sparkline.js';
import stackedArea from './charts/stacked-area.js';
import groupedBar from './charts/grouped-bar.js';
import stackedBar from './charts/stacked-bar.js';
import step from './charts/step.js';
import brush from './charts/brush.js';
import colors from './charts/helpers/color.js';

export {
        bar,
        donut,
        legend,
        line,
        loadingStates,
        tooltip,
        miniTooltip,
        sparkline,
        stackedArea,
        groupedBar,
        stackedBar,
        step,
        brush,
        colors
    };
<commit_msg>Add missing heatmap chart to the bundle (#700)<commit_after>import bar from './charts/bar.js';
import donut from './charts/donut.js';
import legend from './charts/legend.js';
import line from './charts/line.js';
import loadingStates from './charts/helpers/load.js';
import tooltip from './charts/tooltip.js';
import miniTooltip from './charts/mini-tooltip.js';
import sparkline from './charts/sparkline.js';
import stackedArea from './charts/stacked-area.js';
import groupedBar from './charts/grouped-bar.js';
import stackedBar from './charts/stacked-bar.js';
import heatmap from './charts/heatmap.js';
import step from './charts/step.js';
import brush from './charts/brush.js';
import colors from './charts/helpers/color.js';

export {
        bar,
        donut,
        legend,
        line,
        loadingStates,
        tooltip,
        miniTooltip,
        sparkline,
        stackedArea,
        groupedBar,
        stackedBar,
        heatmap,
        step,
        brush,
        colors
    };
<|endoftext|>


<commit_before>/**
 * Sigma.js Camera Class
 * ======================
 *
 * Class designed to store camera information & used to update it.
 */
import {EventEmitter} from 'events';

import * as easings from './easings';
import {assign} from './utils';

/**
 * Defaults.
 */
const ANIMATE_DEFAULTS = {
  easing: 'quadraticInOut',
  duration: 150
};

// TODO: animate options = number polymorphism?
// TODO: pan, zoom, unzoom, reset, rotate, zoomTo
// TODO: add width / height to camera and add #.resize
// TODO: bind camera to renderer rather than sigma
// TODO: add #.graphToDisplay, #.displayToGraph, batch methods later

/**
 * Camera class
 *
 * @constructor
 */
export default class Camera extends EventEmitter {
  constructor(dimensions) {
    dimensions = dimensions || {};

    super();

    // Properties
    this.x = 0;
    this.y = 0;
    this.angle = 0;
    this.ratio = 1;
    this.width = dimensions.width || 0;
    this.height = dimensions.height || 0;

    // State
    this.nextFrame = null;
    this.enabled = true;
  }

  /**
   * Method used to enable the camera.
   *
   * @return {Camera}
   */
  enable() {
    this.enabled = true;
    return this;
  }

  /**
   * Method used to disable the camera.
   *
   * @return {Camera}
   */
  disable() {
    this.enabled = false;
    return this;
  }

  /**
   * Method used to retrieve the camera's current state.
   *
   * @return {object}
   */
  getState() {
    return {
      x: this.x,
      y: this.y,
      angle: this.angle,
      ratio: this.ratio
    };
  }

  /**
   * Method used to retrieve the camera's dimensions.
   *
   * @return {object}
   */
  getDimensions() {
    return {
      width: this.width,
      height: this.height
    };
  }

  /**
   * Method used to check whether the camera is currently being animated.
   *
   * @return {boolean}
   */
  isAnimated() {
    return !!this.nextFrame;
  }

  /**
   * Method returning the coordinates of a point from the display frame to the
   * graph one but ignores the offset and dimensions of the container.
   *
   * @param  {number} x The X coordinate.
   * @param  {number} y The Y coordinate.
   * @return {object}   The point coordinates in the frame of the graph.
   */
  abstractDisplayToGraph(x, y) {
    const cos = Math.cos(this.angle),
          sin = Math.sin(this.angle);

    return {
      x: (x * cos - y * sin) * this.ratio,
      y: (y * cos + x * sin) * this.ratio
    };
  }

  /**
   * Method returning the coordinates of a point from the display frame to the
   * graph one.
   *
   * @param  {number} x The X coordinate.
   * @param  {number} y The Y coordinate.
   * @return {object}   The point coordinates in the frame of the graph.
   */
  displayToGraph(x, y) {
    const cos = Math.cos(this.angle),
          sin = Math.sin(this.angle);

    const xOffset = this.width / 2 - (this.x * cos + this.y * sin) / this.ratio,
          yOffset = this.height / 2 - (this.y * cos - this.x * sin) / this.ratio;

    const X = x - xOffset,
          Y = y - yOffset;

    return {
      x: (X * cos - Y * sin) * this.ratio,
      y: (Y * cos + X * sin) * this.ratio
    };
  }

  /**
   * Method returning the coordinates of a point from the graph frame to the
   * display one.
   *
   * @param  {number} x The X coordinate.
   * @param  {number} y The Y coordinate.
   * @return {object}   The point coordinates in the frame of the display.
   */
  graphToDisplay(x, y) {
    const relCos = Math.cos(this.angle) / this.ratio,
          relSin = Math.sin(this.angle) / this.ratio,
          xOffset = (this.width / 2) - this.x * relCos - this.y * relSin,
          yOffset = (this.height / 2) - this.y * relCos + this.x * relSin;

    return {
      x: x * relCos + y * relSin + xOffset,
      y: y * relCos - x * relSin + yOffset
    };
  }

  /**
   * Method used to set the camera's state.
   *
   * @param  {object} state - New state.
   * @return {Camera}
   */
  setState(state) {

    if (!this.enabled)
      return this;

    // TODO: validations
    // TODO: update by function

    if ('x' in state)
      this.x = state.x;

    if ('y' in state)
      this.y = state.y;

    if ('angle' in state)
      this.angle = state.angle;

    if ('ratio' in state)
      this.ratio = state.ratio;

    // Emitting
    // TODO: don't emit if nothing changed?
    this.emit('updated', this.getState());

    return this;
  }

  /**
   * Method used to resize the camera's dimensions.
   *
   * @param  {object} dimensions - New dimensions.
   * @return {Camera}
   */
  resize(dimensions) {

    if (!this.enabled)
      return this;

    if ('width' in dimensions)
      this.width = dimensions.width;

    if ('height' in dimensions)
      this.height = dimensions.height;

    this.emit('resized', this.getDimensions());

    return this;
  }

  /**
   * Method used to animate the camera.
   *
   * @param  {object}   state      - State to reach eventually.
   * @param  {object}   options    - Options:
   * @param  {number}     duration - Duration of the animation.
   * @param  {function} callback   - Callback
   * @return {function}            - Return a function to cancel the animation.
   */
  animate(state, options /*, callback */) {

    if (!this.enabled)
      return this;

    // TODO: validation

    options = assign({}, ANIMATE_DEFAULTS, options);

    const easing = typeof options.easing === 'function' ?
      options.easing :
      easings[options.easing];

    // Canceling previous animation if needed
    if (this.nextFrame)
      cancelAnimationFrame(this.nextFrame);

    // State
    const start = Date.now(),
          initialState = this.getState();

    // Function performing the animation
    const fn = () => {
      const t = (Date.now() - start) / options.duration;

      // The animation is over:
      if (t >= 1) {
        this.nextFrame = null;
        this.setState(state);

        return;
      }

      const coefficient = easing(t);

      const newState = {};

      if ('x' in state)
        newState.x = initialState.x + (state.x - initialState.x) * coefficient;
      if ('y' in state)
        newState.y = initialState.y + (state.y - initialState.y) * coefficient;
      if ('angle' in state)
        newState.angle = initialState.angle + (state.angle - initialState.angle) * coefficient;
      if ('ratio' in state)
        newState.ratio = initialState.ratio + (state.ratio - initialState.ratio) * coefficient;

      this.setState(newState);

      this.nextFrame = requestAnimationFrame(fn);
    };

    fn();
  }
}
<commit_msg>Fixing camera?<commit_after>/**
 * Sigma.js Camera Class
 * ======================
 *
 * Class designed to store camera information & used to update it.
 */
import {EventEmitter} from 'events';

import * as easings from './easings';
import {assign} from './utils';

/**
 * Defaults.
 */
const ANIMATE_DEFAULTS = {
  easing: 'quadraticInOut',
  duration: 150
};

// TODO: animate options = number polymorphism?
// TODO: pan, zoom, unzoom, reset, rotate, zoomTo
// TODO: add width / height to camera and add #.resize
// TODO: bind camera to renderer rather than sigma
// TODO: add #.graphToDisplay, #.displayToGraph, batch methods later

/**
 * Camera class
 *
 * @constructor
 */
export default class Camera extends EventEmitter {
  constructor(dimensions) {
    dimensions = dimensions || {};

    super();

    // Properties
    this.x = 0;
    this.y = 0;
    this.angle = 0;
    this.ratio = 1;
    this.width = dimensions.width || 0;
    this.height = dimensions.height || 0;

    // State
    this.nextFrame = null;
    this.enabled = true;
  }

  /**
   * Method used to enable the camera.
   *
   * @return {Camera}
   */
  enable() {
    this.enabled = true;
    return this;
  }

  /**
   * Method used to disable the camera.
   *
   * @return {Camera}
   */
  disable() {
    this.enabled = false;
    return this;
  }

  /**
   * Method used to retrieve the camera's current state.
   *
   * @return {object}
   */
  getState() {
    return {
      x: this.x,
      y: this.y,
      angle: this.angle,
      ratio: this.ratio
    };
  }

  /**
   * Method used to retrieve the camera's dimensions.
   *
   * @return {object}
   */
  getDimensions() {
    return {
      width: this.width,
      height: this.height
    };
  }

  /**
   * Method used to check whether the camera is currently being animated.
   *
   * @return {boolean}
   */
  isAnimated() {
    return !!this.nextFrame;
  }

  /**
   * Method returning the coordinates of a point from the display frame to the
   * graph one but ignores the offset and dimensions of the container.
   *
   * @param  {number} x The X coordinate.
   * @param  {number} y The Y coordinate.
   * @return {object}   The point coordinates in the frame of the graph.
   */
  abstractDisplayToGraph(x, y) {
    const cos = Math.cos(this.angle),
          sin = Math.sin(this.angle);

    return {
      x: (x * cos - y * sin) * this.ratio,
      y: (y * cos + x * sin) * this.ratio
    };
  }

  /**
   * Method returning the coordinates of a point from the display frame to the
   * graph one.
   *
   * @param  {number} x The X coordinate.
   * @param  {number} y The Y coordinate.
   * @return {object}   The point coordinates in the frame of the graph.
   */
  displayToGraph(x, y) {
    const cos = Math.cos(this.angle),
          sin = Math.sin(this.angle);

    const xOffset = this.width / 2 - (this.x * cos + this.y * sin) / this.ratio,
          yOffset = this.height / 2 - (this.y * cos - this.x * sin) / this.ratio;

    const X = x - xOffset,
          Y = y - yOffset;

    return {
      x: (X * cos - Y * sin) * this.ratio,
      y: (Y * cos + X * sin) * this.ratio
    };
  }

  /**
   * Method returning the coordinates of a point from the graph frame to the
   * display one.
   *
   * @param  {number} x The X coordinate.
   * @param  {number} y The Y coordinate.
   * @return {object}   The point coordinates in the frame of the display.
   */
  graphToDisplay(x, y) {
    const relCos = Math.cos(this.angle) / this.ratio,
          relSin = Math.sin(this.angle) / this.ratio,
          xOffset = (this.width / 2) - this.x * relCos - this.y * relSin,
          yOffset = (this.height / 2) - this.y * relCos + this.x * relSin;

    return {
      x: x * relCos + y * relSin + xOffset,
      y: y * relCos - x * relSin + yOffset
    };
  }

  /**
   * Method used to set the camera's state.
   *
   * @param  {object} state - New state.
   * @return {Camera}
   */
  setState(state) {

    if (!this.enabled)
      return this;

    // TODO: validations
    // TODO: update by function

    if ('x' in state)
      this.x = state.x;

    if ('y' in state)
      this.y = state.y;

    if ('angle' in state)
      this.angle = state.angle;

    if ('ratio' in state)
      this.ratio = state.ratio;

    // Emitting
    // TODO: don't emit if nothing changed?
    this.emit('updated', this.getState());

    return this;
  }

  /**
   * Method used to resize the camera's dimensions.
   *
   * @param  {object} dimensions - New dimensions.
   * @return {Camera}
   */
  resize(dimensions) {

    if (!this.enabled)
      return this;

    if ('width' in dimensions)
      this.width = dimensions.width;

    if ('height' in dimensions)
      this.height = dimensions.height;

    this.emit('resized', this.getDimensions());

    return this;
  }

  /**
   * Method used to animate the camera.
   *
   * @param  {object}   state      - State to reach eventually.
   * @param  {object}   options    - Options:
   * @param  {number}     duration - Duration of the animation.
   * @param  {function} callback   - Callback
   * @return {function}            - Return a function to cancel the animation.
   */
  animate(state, options /*, callback */) {

    if (!this.enabled)
      return this;

    // TODO: validation

    options = assign({}, ANIMATE_DEFAULTS, options);

    const easing = typeof options.easing === 'function' ?
      options.easing :
      easings[options.easing];

    // Canceling previous animation if needed
    // if (this.nextFrame)
    //   cancelAnimationFrame(this.nextFrame);

    // State
    const start = Date.now(),
          initialState = this.getState();

    // Function performing the animation
    const fn = () => {
      const t = (Date.now() - start) / options.duration;

      // The animation is over:
      if (t >= 1) {
        this.nextFrame = null;
        this.setState(state);

        return;
      }

      const coefficient = easing(t);

      const newState = {};

      if ('x' in state)
        newState.x = initialState.x + (state.x - initialState.x) * coefficient;
      if ('y' in state)
        newState.y = initialState.y + (state.y - initialState.y) * coefficient;
      if ('angle' in state)
        newState.angle = initialState.angle + (state.angle - initialState.angle) * coefficient;
      if ('ratio' in state)
        newState.ratio = initialState.ratio + (state.ratio - initialState.ratio) * coefficient;

      this.setState(newState);

      this.nextFrame = requestAnimationFrame(fn);
    };

    if (this.nextFrame) {
      cancelAnimationFrame(this.nextFrame);
      this.nextFrame = requestAnimationFrame(fn);
    }
    else {
      fn();
    }
  }
}
<|endoftext|>


<commit_before>(function ($, MITHGrid, OAC) {
	/**
	* MITHGrid Canvas
	* Creates a canvas using the Raphael JS library
	*/
	// generating the canvasId allows us to have multiple instances of the app on a page and still
	// have a unique ID as expected by the Raphaël library
	var canvasId = 1;
	OAC.Client.StreamingVideo.initApp = function (container, options) {
		var renderListItem, annoActiveController, app, svgLens, textLens,
		    myCanvasId = 'OAC-Client-StreamingVideo-SVG-Canvas-' + canvasId;

		canvasId += 1;

		/*
		* Creating application to run DOM and presentations
		*
		*/
		app = MITHGrid.Application.initApp("OAC.Client.StreamingVideo", container,
			$.extend(true, {}, {
				viewSetup: '

'+
				'

',
				presentations: {
					raphsvg: {
						container: "#" + myCanvasId,
						lenses: {
							/*
							* The following are lenses for shapes that
							* are found in the dataStore. These items are using
							* the MITHGrid.Presentation.RaphaelCanvas.canvas
							* object, which is a Raphaël paper object, to draw
							* themselves.
							*/
						}
					},
					annoItem: {
						lenses: {
				//			Rectangle: textLens,
				//			Ellipse: textLens
						} //annoItem lenses
					} //annoItem
				},
				cWidth: options.width,
				cHeight: options.height
			}, options)
		);

		/*
		svgLens builds an object with functionality common to all SVG shapes on the canvas.
		The methods expect the SVG shape object to be in that.shape
		 */
		app.initShapeLens = function (container, view, model, itemId) {
			var that = {id: itemId};

			that.eventFocus = function() {
				that.shape.attr({
					opacity: 1
				}).toFront();
				view.events.onDelete.addListener(that.eventDelete);
			};

			that.eventUnfocus = function() {
				that.shape.attr({
					opacity:0.5
				}).toBack();
				view.events.onDelete.removeListener(that.eventDelete);
			};

			that.eventDelete = function (id) {
				if(id === itemId) {
					model.removeItems([itemId]);
				}
			};

			that.eventResize = function (id, pos) {
				if(id === itemId) {
					// update item with new width/height
					model.updateItems([{
						id: itemId,
						w: pos.width,
						h: pos.height
					}]);
				}
			};

			that.eventMove = function (id, pos) {
				if(id === itemId) {
					// update item with new x/y
					model.updateItems([{
						id: itemId,
						x: pos.x,
						y: pos.y
					}]);
				}
			};

			that.remove = function (item) {
				// getting the removeItems callback
				that.shape.remove();
			};

			return that;
		};

		/*
		textLens returns a rendering of the text body of an annotation regardless of the shape
		 */
		app.initTextLens = function (container, view, model, itemId) {
			var that = {}, item = model.getItem(itemId),
			itemEl, annoEvents;

			itemEl = renderListItem(item, container);

			// attaching controller to make the
			// HTML highlighted when shape is selected
			annoEvents = annoActiveController.bind(itemEl, {
				model: model,
				itemId: itemId
			});

			that.eventUpdate = function (id, data) {
				if(id === itemId) {
					model.updateItems([{
						id: itemId,
						bodyContent: data
					}]);
				 }
			};

			that.eventFocus = function() {
				itemEl.addClass('selected');
			};

			that.eventUnfocus = function() {
				itemEl.removeClass('selected');
			};

			annoEvents.events.onClick.addListener(app.setActiveAnnotation);
			annoEvents.events.onUpdate.addListener(that.eventUpdate);

			that.update = function (item) {
				$(itemEl).find(".bodyContent").text(item.bodyContent[0]);
				$(itemEl).find(".bodyContentTextArea").text(item.bodyContent[0]);
			};

			that.remove = function () {
				$(itemEl).remove();
			};

			return that;
		};

		renderListItem = function (item, container) {
			var el = $(
				'
'+
					'
'+
						'<textarea class="bodyContentTextArea"></textarea>'+
					'
'+
					'
'+
						'

' +
					'
'+
				'
'),
				bodyContentTextArea = $(el).find(".bodyContentTextArea"),
				bodyContent = $(el).find(".bodyContent");
			$(bodyContentTextArea).text(item.bodyContent[0]);
			$(bodyContent).text(item.bodyContent[0]);
			$(container).append(el);
			$(el).find(".editArea").hide();
			return $(el);
		};

		app.addShape = function(key, svgShape) {
			app.presentation.raphsvg.addLens(key, svgShape);
		};

		app.addBody = function(key, textLens) {
			app.presentation.annoItem.addLens(key, textLens);
		};

		app.ready(function() {
			annoActiveController = app.controller.annoActive;
			app.events.onActiveAnnotationChange.addListener(app.presentation.raphsvg.eventFocusChange);
			app.events.onActiveAnnotationChange.addListener(app.presentation.annoItem.eventFocusChange);
			app.events.onCurrentTimeChange.addListener(function(t) {
				// five seconds on either side of the current time
				app.dataView.currentAnnotations.setKeyRange(t-5, t+5);
			});
		});

		app.ready(function() {
			app.addShape("Rectangle", function (container, view, model, itemId) {
				// Note: Rectangle measurements x,y start at CENTER
				var that = app.initShapeLens(container, view, model, itemId),
				item = model.getItem(itemId),
				c, ox, oy, bbox;

				ox = (item.x - (item.w[0] / 2));
				oy = (item.y - (item.h[0] / 2));

				// Accessing the view.canvas Object that was created in MITHGrid.Presentation.RaphSVG
				c = view.canvas.rect(ox, oy, item.w[0], item.h[0]);
				// fill and set opacity
				c.attr({
					fill: "red",
					opacity: 0.5
				});

				that.update = function (item) {
					// receiving the Object passed through
					// model.updateItems in move()
					try {
						if (item.x !== undefined && item.y !== undefined && item.w !== undefined && item.y !== undefined) {
							c.attr({
								x: item.x[0] - item.w[0]/2,
								y: item.y[0] - item.h[0]/2,
								width: item.w[0],
								height: item.h[0]
							});
						}
					} catch(e) {
						MITHGrid.debug(e);
					}
					// Raphael object is updated

				};

				// calculate the extents (x, y, width, height)
				// of this type of shape
				that.getExtents = function () {
					return {
						x: c.attr("x") + (c.attr("width")/2),
						y: c.attr("y") + (c.attr("height")/2),
						width: c.attr("width"),
						height: c.attr("height")
					};
				};

				// register shape
				that.shape = c;

				return that;
			});
			app.addShape("Ellipse", function (container, view, model, itemId) {
				var that = app.initShapeLens(container, view, model, itemId),
				item = model.getItem(itemId),
				c;

				// create the shape
				c = view.canvas.ellipse(item.x[0], item.y[0], item.w[0]/2, item.h[0]/2);
				// fill shape
				c.attr({
					fill: "red",
					opacity: 0.5
				});

				that.update = function (item) {
					// receiving the Object passed through
					// model.updateItems in move()
					try {
						if (item.x !== undefined && item.y !== undefined) {
							c.attr({
								cx: item.x[0],
								cy: item.y[0],
								rx: item.w[0] / 2,
								ry: item.h[0] / 2
							});
						}
					} catch(e) {
						MITHGrid.debug(e);

					}
					// Raphael object is updated
				};

				// calculate the extents (x, y, width, height)
				// of this type of shape
				that.getExtents = function () {
					return {
						x: c.attr("cx"),
						y: c.attr("cy"),
						width: (c.attr("rx") * 2),
						height: (c.attr("ry") * 2)
					};
				};

				// register shape
				that.shape = c;

				return that;
			});

			app.addBody("Rectangle", app.initTextLens);
			app.addBody("Ellipse", app.initTextLens);
		});

		return app;
	};
} (jQuery, MITHGrid, OAC));

// Default library for the Canvas application<commit_msg>Moving renderListItem into textLens - canvas.js<commit_after>(function ($, MITHGrid, OAC) {
	/**
	* MITHGrid Canvas
	* Creates a canvas using the Raphael JS library
	*/
	// generating the canvasId allows us to have multiple instances of the app on a page and still
	// have a unique ID as expected by the Raphaël library
	var canvasId = 1;
	OAC.Client.StreamingVideo.initApp = function (container, options) {
		var renderListItem, annoActiveController, app, svgLens, textLens,
		    myCanvasId = 'OAC-Client-StreamingVideo-SVG-Canvas-' + canvasId;

		canvasId += 1;

		/*
		* Creating application to run DOM and presentations
		*
		*/
		app = MITHGrid.Application.initApp("OAC.Client.StreamingVideo", container,
			$.extend(true, {}, {
				viewSetup: '

'+
				'

',
				presentations: {
					raphsvg: {
						container: "#" + myCanvasId,
						lenses: {
							/*
							* The following are lenses for shapes that
							* are found in the dataStore. These items are using
							* the MITHGrid.Presentation.RaphaelCanvas.canvas
							* object, which is a Raphaël paper object, to draw
							* themselves.
							*/
						}
					},
					annoItem: {
						lenses: {
				//			Rectangle: textLens,
				//			Ellipse: textLens
						} //annoItem lenses
					} //annoItem
				},
				cWidth: options.width,
				cHeight: options.height
			}, options)
		);

		/*
		svgLens builds an object with functionality common to all SVG shapes on the canvas.
		The methods expect the SVG shape object to be in that.shape
		 */
		app.initShapeLens = function (container, view, model, itemId) {
			var that = {id: itemId};

			that.eventFocus = function() {
				that.shape.attr({
					opacity: 1
				}).toFront();
				view.events.onDelete.addListener(that.eventDelete);
			};

			that.eventUnfocus = function() {
				that.shape.attr({
					opacity:0.5
				}).toBack();
				view.events.onDelete.removeListener(that.eventDelete);
			};

			that.eventDelete = function (id) {
				if(id === itemId) {
					model.removeItems([itemId]);
				}
			};

			that.eventResize = function (id, pos) {
				if(id === itemId) {
					// update item with new width/height
					model.updateItems([{
						id: itemId,
						w: pos.width,
						h: pos.height
					}]);
				}
			};

			that.eventMove = function (id, pos) {
				if(id === itemId) {
					// update item with new x/y
					model.updateItems([{
						id: itemId,
						x: pos.x,
						y: pos.y
					}]);
				}
			};

			that.remove = function (item) {
				// getting the removeItems callback
				that.shape.remove();
			};

			return that;
		};

		/*
		textLens returns a rendering of the text body of an annotation regardless of the shape
		 */
		app.initTextLens = function (container, view, model, itemId) {
			var that = {}, item = model.getItem(itemId),
			itemEl, annoEvents, bodyContentTextArea, bodyContent;

			itemEl =
			$('
'+
				'
'+
					'<textarea class="bodyContentTextArea"></textarea>'+
				'
'+
				'
'+
					'

' +
				'
'+
			'
');

			bodyContentTextArea = $(itemEl).find(".bodyContentTextArea");
			bodyContent = $(itemEl).find(".bodyContent");

			$(bodyContentTextArea).text(item.bodyContent[0]);
			$(bodyContent).text(item.bodyContent[0]);

			$(container).append(itemEl);
			$(itemEl).find(".editArea").hide();

			// attaching controller to make the
			// HTML highlighted when shape is selected
			annoEvents = annoActiveController.bind(itemEl, {
				model: model,
				itemId: itemId
			});

			that.eventUpdate = function (id, data) {
				if(id === itemId) {
					model.updateItems([{
						id: itemId,
						bodyContent: data
					}]);
				 }
			};

			that.eventFocus = function() {
				itemEl.addClass('selected');
			};

			that.eventUnfocus = function() {
				itemEl.removeClass('selected');
			};

			annoEvents.events.onClick.addListener(app.setActiveAnnotation);
			annoEvents.events.onUpdate.addListener(that.eventUpdate);

			that.update = function (item) {
				$(itemEl).find(".bodyContent").text(item.bodyContent[0]);
				$(itemEl).find(".bodyContentTextArea").text(item.bodyContent[0]);
			};

			that.remove = function () {
				$(itemEl).remove();
			};

			return that;
		};

		/*
		Creates an HTML div that acts as a button
		*/
		app.buttonFeature = function(container, view, model, itemId) {
			var that = {}, item = model.getItem(itemId);

			return that;
		};

		app.addShape = function(key, svgShape) {
			app.presentation.raphsvg.addLens(key, svgShape);
		};

		app.addBody = function(key, textLens) {
			app.presentation.annoItem.addLens(key, textLens);
		};

		app.ready(function() {
			annoActiveController = app.controller.annoActive;
			app.events.onActiveAnnotationChange.addListener(app.presentation.raphsvg.eventFocusChange);
			app.events.onActiveAnnotationChange.addListener(app.presentation.annoItem.eventFocusChange);
			app.events.onCurrentTimeChange.addListener(function(t) {
				// five seconds on either side of the current time
				app.dataView.currentAnnotations.setKeyRange(t-5, t+5);
			});
		});

		app.ready(function() {
			app.addShape("Rectangle", function (container, view, model, itemId) {
				// Note: Rectangle measurements x,y start at CENTER
				var that = app.initShapeLens(container, view, model, itemId),
				item = model.getItem(itemId),
				c, ox, oy, bbox;

				ox = (item.x - (item.w[0] / 2));
				oy = (item.y - (item.h[0] / 2));

				// Accessing the view.canvas Object that was created in MITHGrid.Presentation.RaphSVG
				c = view.canvas.rect(ox, oy, item.w[0], item.h[0]);
				// fill and set opacity
				c.attr({
					fill: "red",
					opacity: 0.5
				});

				that.update = function (item) {
					// receiving the Object passed through
					// model.updateItems in move()
					try {
						if (item.x !== undefined && item.y !== undefined && item.w !== undefined && item.y !== undefined) {
							c.attr({
								x: item.x[0] - item.w[0]/2,
								y: item.y[0] - item.h[0]/2,
								width: item.w[0],
								height: item.h[0]
							});
						}
					} catch(e) {
						MITHGrid.debug(e);
					}
					// Raphael object is updated

				};

				// calculate the extents (x, y, width, height)
				// of this type of shape
				that.getExtents = function () {
					return {
						x: c.attr("x") + (c.attr("width")/2),
						y: c.attr("y") + (c.attr("height")/2),
						width: c.attr("width"),
						height: c.attr("height")
					};
				};

				// register shape
				that.shape = c;

				return that;
			});
			app.addShape("Ellipse", function (container, view, model, itemId) {
				var that = app.initShapeLens(container, view, model, itemId),
				item = model.getItem(itemId),
				c;

				// create the shape
				c = view.canvas.ellipse(item.x[0], item.y[0], item.w[0]/2, item.h[0]/2);
				// fill shape
				c.attr({
					fill: "red",
					opacity: 0.5
				});

				that.update = function (item) {
					// receiving the Object passed through
					// model.updateItems in move()
					try {
						if (item.x !== undefined && item.y !== undefined) {
							c.attr({
								cx: item.x[0],
								cy: item.y[0],
								rx: item.w[0] / 2,
								ry: item.h[0] / 2
							});
						}
					} catch(e) {
						MITHGrid.debug(e);

					}
					// Raphael object is updated
				};

				// calculate the extents (x, y, width, height)
				// of this type of shape
				that.getExtents = function () {
					return {
						x: c.attr("cx"),
						y: c.attr("cy"),
						width: (c.attr("rx") * 2),
						height: (c.attr("ry") * 2)
					};
				};

				// register shape
				that.shape = c;

				return that;
			});

			app.addBody("Rectangle", app.initTextLens);
			app.addBody("Ellipse", app.initTextLens);
		});

		return app;
	};
} (jQuery, MITHGrid, OAC));

// Default library for the Canvas application<|endoftext|>