<commit_before>const b = require('substance-bundler')
const fs = require('fs')
const path = require('path')
const fork = require('substance-bundler/extensions/fork')
const vfs = require('substance-bundler/extensions/vfs')

const DIST = 'dist/'
const APPDIST = 'app-dist/'
const TMP = 'tmp/'
const RNG_SEARCH_DIRS = [
  path.join(__dirname, 'src', 'article')
]

const RNG_FILES = [
  'src/article/JATS-publishing.rng',
  'src/article/JATS-archiving.rng',
  'src/article/DarArticle.rng',
  'src/article/TextureArticle.rng'
]

// Server configuration

const port = 4000 // TODO: make this configurable
b.setServerPort(port)
b.yargs.option('d', {
  type: 'string',
  alias: 'rootDir',
  describe: 'Root directory of served archives'
})
let argv = b.yargs.argv
if (argv.d) {
  const darServer = require('dar-server')
  const rootDir = argv.d
  const archiveDir = path.resolve(path.join(__dirname, rootDir))
  darServer.serve(b.server, {
    port,
    serverUrl: 'http://localhost:'+port,
    rootDir: archiveDir,
    apiUrl: '/archives'
  })
}
b.serve({ static: true, route: '/', folder: './dist' })

// Make Targets

b.task('clean', function() {
  b.rm(DIST)
  b.rm(TMP)
  b.rm(APPDIST)
}).describe('removes all generated files and folders.')

b.task('lib', ['clean', 'build:schema', 'build:assets', 'build:lib'])
.describe('builds the library bundle.')

b.task('browser', ['clean', 'build:schema', 'build:assets', 'build:browser'])
.describe('builds the browser bundle.')

b.task('publish', ['clean', 'build:schema', 'build:assets', 'build:lib', 'build:web'])
.describe('builds the release bundle (library + web).')

b.task('web', ['clean', 'build:schema', 'build:assets', 'build:browser', 'build:web'])
.describe('builds the web bundle (browser + web).')

b.task('app', ['clean', 'build:schema', 'build:assets', 'build:browser', 'build:app'])
.describe('builds the app bundle (electron app).')

b.task('test-nodejs', ['clean', 'build:schema', 'build:nodejs', 'build:test-assets', 'build:test-nodejs'], () => {
  fork(b, require.resolve('substance-test/bin/test'), './tmp/tests.cjs.js', { verbose: true, await: true })
})
.describe('builds and runs the test-suite in nodejs.')

b.task('test-browser', ['clean', 'build:schema', 'build:browser', 'build:test-assets', 'build:test-browser'])
.describe('builds the test-suite for the browser.')

b.task('default', ['publish'])
.describe('default: publish')

// spawns electron after build is ready
b.task('run-app', ['app'], () => {
  // Note: `await=false` is important, as otherwise bundler would await this to finish
  fork(b, require.resolve('electron/cli.js'), '.', { verbose: true, cwd: APPDIST, await: false })
})
.describe('runs the application in electron.')

b.task('cover', ['build:instrumented-tests'], () => {
  b.rm('coverage')
  fork(b, 'node_modules/substance-test/bin/coverage', 'tmp/tests.cov.js', { await: true })
})

// low-level make targets

b.task('schema:single-jats-file', _singleJATSFile)

b.task('schema:jats', () => {
  _compileSchema('JATS-publishing', RNG_FILES[0], RNG_SEARCH_DIRS, RNG_FILES.slice(0,1))
  _compileSchema('JATS-archiving', RNG_FILES[1], RNG_SEARCH_DIRS, RNG_FILES.slice(1,2))
})

b.task('schema:dar-article', () => {
  _compileSchema('DarArticle', RNG_FILES[2], RNG_SEARCH_DIRS, RNG_FILES.slice(0,3))
})

b.task('schema:texture-article', () => {
  _compileSchema('TextureArticle', RNG_FILES[3], RNG_SEARCH_DIRS, RNG_FILES.slice(0,4))
})

b.task('schema:debug', () => {
  _compileSchema('JATS-publishing', RNG_FILES[0], RNG_SEARCH_DIRS, RNG_FILES.slice(0,1), { debug: true })
  _compileSchema('JATS-archiving', RNG_FILES[1], RNG_SEARCH_DIRS, RNG_FILES.slice(1,2), { debug: true })
  _compileSchema('DarArticle', RNG_FILES[2], RNG_SEARCH_DIRS, RNG_FILES.slice(0,3), { debug: true })
  _compileSchema('TextureArticle', RNG_FILES[3], RNG_SEARCH_DIRS, RNG_FILES.slice(0,4), { debug: true })
})

b.task('build:assets', function() {
  b.copy('./node_modules/font-awesome', DIST+'font-awesome')
  b.copy('./node_modules/katex/dist', DIST+'katex')
  b.copy('./node_modules/substance/dist', DIST+'substance/dist')
  b.css('texture.css', DIST+'texture.css')
  b.css('./node_modules/substance/substance-pagestyle.css', DIST+'texture-pagestyle.css')
  b.css('./node_modules/substance/substance-reset.css', DIST+'texture-reset.css')
})

b.task('build:schema', ['schema:jats', 'schema:dar-article', 'schema:texture-article'])

b.task('build:browser', () => {
  _buildLib(DIST, 'browser')
})

b.task('build:nodejs', () => {
  _buildLib(DIST, 'nodejs')
})

b.task('build:lib', () => {
  _buildLib(DIST, 'all')
})

b.task('build:lib', () => {
  _buildLib(DIST, 'cover')
})

b.task('build:app', () => {
  b.copy('app/index.html', APPDIST)
  b.copy('app/build-resources', APPDIST)
  b.copy('data', APPDIST)
  // FIXME: this command leads to an extra run when a  file is updated
  // .. instead copying the files explicitly for now
  // b.copy('dist', APPDIST+'lib/')
  b.copy('dist/font-awesome', APPDIST+'lib/')
  b.copy('dist/katex', APPDIST+'lib/')
  b.copy('dist/substance', APPDIST+'lib/')
  ;[
    'texture.js',
    'texture.css',
    'texture-pagestyle.css',
    'texture-reset.css'
  ].forEach(f => {
    b.copy(`dist/${f}`, APPDIST+'lib/')
    b.copy(`dist/${f}.map`, APPDIST+'lib/')
  })

  // TODO: maybe we could come up with an extension
  // that expands a source file using a given dict.
  b.custom('Creating application package.json...', {
    src: 'app/package.json.in',
    dest: APPDIST+'package.json',
    execute() {
      let { version } = require('./package.json')
      let tpl = fs.readFileSync('app/package.json.in', 'utf8')
      let out = tpl.replace('${version}', version)
      fs.writeFileSync(APPDIST+'package.json', out)
    }
  })
  b.js('app/main.js', {
    target: {
      dest: APPDIST+'main.js',
      format: 'cjs',
    },
    external: ['electron', 'path', 'url']
  })
  b.js('app/app.js', {
    target: {
      dest: APPDIST+'app.js',
      format: 'umd',
      moduleName: 'textureApp',
      globals: {
        'substance': 'window.substance',
        'substance-texture': 'window.texture',
        'katex': 'window.katex'
      }
    },
    external: [ 'substance', 'substance-texture', 'katex' ]
  })
  // execute 'install-app-deps'
  fork(b, require.resolve('electron-builder/out/cli/cli.js'), 'install-app-deps', { verbose: true, cwd: APPDIST, await: true })
})

b.task('build:vfs', () => {
  vfs(b, {
    src: ['./data/**/*'],
    dest: DIST+'/vfs.js',
    format: 'umd', moduleName: 'vfs',
    rootDir: path.join(__dirname, 'data')
  })
})

b.task('build:web', ['build:vfs'], () => {
  b.copy('web/index.html', DIST)
  b.js('./web/editor.js', {
    targets: [{
      dest: DIST+'editor.js',
      format: 'umd',
      moduleName: 'textureEditor',
      globals: {
        'substance': 'window.substance',
        'substance-texture': 'window.texture',
        'katex': 'window.katex'
      }
    }],
    external: ['substance', 'substance-texture', 'katex']
  })
  b.copy('./data', DIST+'data')
})

b.task('build:test-assets', () => {
  vfs(b, {
    src: ['./test/fixture/**/*.xml'],
    dest: './tmp/test-vfs.js',
    format: 'es', moduleName: 'fixtures'
  })
})

b.task('build:test-browser', ['build:vfs', 'build:assets', 'build:test-assets'], () => {
  b.copy('test/index.html', 'dist/test/index.html')
  b.copy('node_modules/substance-test/dist/testsuite.js', 'dist/test/testsuite.js')
  b.copy('node_modules/substance-test/dist/test.css', 'dist/test/test.css')
  b.js('test/**/*.test.js', {
    dest: 'dist/test/tests.js',
    format: 'umd', moduleName: 'tests',
    globals: {
      'substance': 'window.substance',
      'substance-test': 'window.substanceTest',
      'substance-texture': 'window.texture',
      'katex': 'window.katex',
      'vfs': 'window.vfs'
    },
    external: [ 'substance', 'substance-test', 'substance-texture', 'katex', 'vfs']
  })
})

b.task('build:vfs-es', () => {
  vfs(b, {
    src: ['./data/**/*'],
    dest: TMP+'/vfs.es.js',
    format: 'es',
    rootDir: path.join(__dirname, 'data')
  })
})

b.task('build:test-nodejs', ['build:test-assets', 'build:vfs-es'], () => {
  b.js([
    'test/testGlobals.js',
    'test/**/*.test.js'
  ], {
    dest: 'tmp/tests.cjs.js',
    format: 'cjs',
    external: [
      'substance-test',
      'substance',
      'substance-texture'
    ],
    // do not require substance-texture from 'node_modules' but from the dist folder
    paths: {
      'substance-texture': '../dist/texture.cjs.js'
    }
  })
})

b.task('build:instrumented-tests', ['build:test-assets', 'build:vfs-es'], () => {
  _buildLib(TMP, 'cover')
  b.js([
    'test/testGlobals.js',
    'test/**/*.test.js'
  ], {
    dest: 'tmp/tests.cov.js',
    format: 'cjs',
    external: [
      'substance-test',
      'substance',
      'substance-texture'
    ],
    // do not require substance-texture from 'node_modules' but from the dist folder
    paths: {
      'substance-texture': '../tmp/texture.cov.js'
    }
  })
})

/* HELPERS */

function _buildLib(DEST, platform) {
  let targets = []
  const globals = {
    'substance': 'substance',
    'katex': 'katex',
    'vfs': 'window.vfs'
  }
  let istanbul
  if (platform === 'browser' || platform === 'all') {
    targets.push({
      dest: DEST+'texture.js',
      format: 'umd', moduleName: 'texture', sourceMapRoot: __dirname, sourceMapPrefix: 'texture',
      globals
    })
  }
  if (platform === 'nodejs' || platform === 'all') {
    targets.push({
      dest: DEST+'texture.cjs.js',
      format: 'cjs',
      globals
    })
  }
  if (platform === 'es' || platform === 'all') {
    targets.push({
      dest: DEST+'texture.es.js',
      format: 'es',
      globals
    })
  }
  if (platform === 'cover') {
    targets.push({
      dest: DEST+'texture.cov.js',
      format: 'cjs',
      globals,
    })
    istanbul = {
      include: ['src/**/*.js']
    }
  }
  b.js('./index.es.js', {
    targets,
    external: ['substance', 'katex', 'vfs'],
    istanbul
  })
}

function _compileSchema(name, src, searchDirs, deps, options = {} ) {
  const DEST = `tmp/${name}.data.js`
  const ISSUES = `tmp/${name}.issues.txt`
  const SCHEMA = `tmp/${name}.schema.md`
  const entry = path.basename(src)
  b.custom(`Compiling schema '${name}'...`, {
    src: deps,
    dest: DEST,
    execute() {
      const { compileRNG, checkSchema } = require('substance')
      const xmlSchema = compileRNG(fs, searchDirs, entry)
      b.writeFileSync(DEST, `export default ${JSON.stringify(xmlSchema)}`)
      b.writeFileSync(SCHEMA, xmlSchema.toMD())
      if (options.debug) {
        const issues = checkSchema(xmlSchema)
        const issuesData = [`${issues.length} issues:`, ''].concat(issues).join('\n')
        b.writeFileSync(ISSUES, issuesData)
      }
    }
  })
}

// we used this internally just to get a single-file version of
// the offficial JATS 1.1 rng data set
function _singleJATSFile() {
  [{
    RNG_DIR: 'data/jats/archiving',
    ENTRY: 'JATS-archive-oasis-article1-mathml3.rng',
    DEST: 'src/article/JATS-archiving.rng',
  },
  {
    RNG_DIR: 'data/jats/publishing',
    ENTRY: 'JATS-journalpublishing-oasis-article1-mathml3.rng',
    DEST: 'src/article/JATS-publishing.rng',
  }].forEach(({ RNG_DIR, ENTRY, DEST}) => {
    b.custom(`Pulling JATS spec into a single file...`, {
      src: [RNG_DIR+'/*.rng'],
      dest: DEST,
      execute() {
        const { loadRNG } = require('substance')
        let rng = loadRNG(fs, [RNG_DIR], ENTRY)
        // sort definitions by name
        let grammar = rng.find('grammar')
        let others = []
        let defines = []
        grammar.getChildren().forEach((child) => {
          if (child.tagName === 'define') {
            defines.push(child)
          } else {
            others.push(child)
          }
        })
        defines.sort((a,b) => {
          const aname = a.getAttribute('name').toLowerCase()
          const bname = b.getAttribute('name').toLowerCase()
          if (aname < bname) return -1
          if (bname < aname) return 1
          return 0
        })
        grammar.empty()
        defines.concat(others).forEach((el) => {
          grammar.appendChild('\n  ')
          grammar.appendChild(el)
        })
        let xml = rng.serialize()
        b.writeFileSync(DEST, xml)
      }
    })
  })
}
<commit_msg>Fix make configuration.<commit_after>const b = require('substance-bundler')
const fs = require('fs')
const path = require('path')
const fork = require('substance-bundler/extensions/fork')
const vfs = require('substance-bundler/extensions/vfs')

const DIST = 'dist/'
const APPDIST = 'app-dist/'
const TMP = 'tmp/'
const RNG_SEARCH_DIRS = [
  path.join(__dirname, 'src', 'article')
]

const RNG_FILES = [
  'src/article/JATS-publishing.rng',
  'src/article/JATS-archiving.rng',
  'src/article/DarArticle.rng',
  'src/article/TextureArticle.rng'
]

// Server configuration

const port = 4000 // TODO: make this configurable
b.setServerPort(port)
b.yargs.option('d', {
  type: 'string',
  alias: 'rootDir',
  describe: 'Root directory of served archives'
})
let argv = b.yargs.argv
if (argv.d) {
  const darServer = require('dar-server')
  const rootDir = argv.d
  const archiveDir = path.resolve(path.join(__dirname, rootDir))
  darServer.serve(b.server, {
    port,
    serverUrl: 'http://localhost:'+port,
    rootDir: archiveDir,
    apiUrl: '/archives'
  })
}
b.serve({ static: true, route: '/', folder: './dist' })

// Make Targets

b.task('clean', function() {
  b.rm(DIST)
  b.rm(TMP)
  b.rm(APPDIST)
}).describe('removes all generated files and folders.')

b.task('lib', ['clean', 'build:schema', 'build:assets', 'build:lib'])
.describe('builds the library bundle.')

b.task('browser', ['clean', 'build:schema', 'build:assets', 'build:browser'])
.describe('builds the browser bundle.')

b.task('publish', ['clean', 'build:schema', 'build:assets', 'build:lib', 'build:web'])
.describe('builds the release bundle (library + web).')

b.task('web', ['clean', 'build:schema', 'build:assets', 'build:browser', 'build:web'])
.describe('builds the web bundle (browser + web).')

b.task('app', ['clean', 'build:schema', 'build:assets', 'build:browser', 'build:app'])
.describe('builds the app bundle (electron app).')

b.task('test-nodejs', ['clean', 'build:schema', 'build:nodejs', 'build:test-assets', 'build:test-nodejs'], () => {
  fork(b, require.resolve('substance-test/bin/test'), './tmp/tests.cjs.js', { verbose: true, await: true })
})
.describe('builds and runs the test-suite in nodejs.')

b.task('test-browser', ['clean', 'build:schema', 'build:browser', 'build:test-assets', 'build:test-browser'])
.describe('builds the test-suite for the browser.')

b.task('default', ['publish'])
.describe('default: publish')

// spawns electron after build is ready
b.task('run-app', ['app'], () => {
  // Note: `await=false` is important, as otherwise bundler would await this to finish
  fork(b, require.resolve('electron/cli.js'), '.', { verbose: true, cwd: APPDIST, await: false })
})
.describe('runs the application in electron.')

b.task('cover', ['build:instrumented-tests'], () => {
  b.rm('coverage')
  fork(b, 'node_modules/substance-test/bin/coverage', 'tmp/tests.cov.js', { await: true })
})

// low-level make targets

b.task('schema:single-jats-file', _singleJATSFile)

b.task('schema:jats', () => {
  _compileSchema('JATS-publishing', RNG_FILES[0], RNG_SEARCH_DIRS, RNG_FILES.slice(0,1))
  _compileSchema('JATS-archiving', RNG_FILES[1], RNG_SEARCH_DIRS, RNG_FILES.slice(1,2))
})

b.task('schema:dar-article', () => {
  _compileSchema('DarArticle', RNG_FILES[2], RNG_SEARCH_DIRS, RNG_FILES.slice(0,3))
})

b.task('schema:texture-article', () => {
  _compileSchema('TextureArticle', RNG_FILES[3], RNG_SEARCH_DIRS, RNG_FILES.slice(0,4))
})

b.task('schema:debug', () => {
  _compileSchema('JATS-publishing', RNG_FILES[0], RNG_SEARCH_DIRS, RNG_FILES.slice(0,1), { debug: true })
  _compileSchema('JATS-archiving', RNG_FILES[1], RNG_SEARCH_DIRS, RNG_FILES.slice(1,2), { debug: true })
  _compileSchema('DarArticle', RNG_FILES[2], RNG_SEARCH_DIRS, RNG_FILES.slice(0,3), { debug: true })
  _compileSchema('TextureArticle', RNG_FILES[3], RNG_SEARCH_DIRS, RNG_FILES.slice(0,4), { debug: true })
})

b.task('build:assets', function() {
  b.copy('./node_modules/font-awesome', DIST+'font-awesome')
  b.copy('./node_modules/katex/dist', DIST+'katex')
  b.copy('./node_modules/substance/dist', DIST+'substance/dist')
  b.css('texture.css', DIST+'texture.css')
  b.css('./node_modules/substance/substance-pagestyle.css', DIST+'texture-pagestyle.css')
  b.css('./node_modules/substance/substance-reset.css', DIST+'texture-reset.css')
})

b.task('build:schema', ['schema:jats', 'schema:dar-article', 'schema:texture-article'])

b.task('build:browser', () => {
  _buildLib(DIST, 'browser')
})

b.task('build:nodejs', () => {
  _buildLib(DIST, 'nodejs')
})

b.task('build:lib', () => {
  _buildLib(DIST, 'all')
})

b.task('build:cover', () => {
  _buildLib(TMP, 'cover')
})

b.task('build:app', () => {
  b.copy('app/index.html', APPDIST)
  b.copy('app/build-resources', APPDIST)
  b.copy('data', APPDIST)
  // FIXME: this command leads to an extra run when a  file is updated
  // .. instead copying the files explicitly for now
  // b.copy('dist', APPDIST+'lib/')
  b.copy('dist/font-awesome', APPDIST+'lib/')
  b.copy('dist/katex', APPDIST+'lib/')
  b.copy('dist/substance', APPDIST+'lib/')
  ;[
    'texture.js',
    'texture.css',
    'texture-pagestyle.css',
    'texture-reset.css'
  ].forEach(f => {
    b.copy(`dist/${f}`, APPDIST+'lib/')
    b.copy(`dist/${f}.map`, APPDIST+'lib/')
  })

  // TODO: maybe we could come up with an extension
  // that expands a source file using a given dict.
  b.custom('Creating application package.json...', {
    src: 'app/package.json.in',
    dest: APPDIST+'package.json',
    execute() {
      let { version } = require('./package.json')
      let tpl = fs.readFileSync('app/package.json.in', 'utf8')
      let out = tpl.replace('${version}', version)
      fs.writeFileSync(APPDIST+'package.json', out)
    }
  })
  b.js('app/main.js', {
    target: {
      dest: APPDIST+'main.js',
      format: 'cjs',
    },
    external: ['electron', 'path', 'url']
  })
  b.js('app/app.js', {
    target: {
      dest: APPDIST+'app.js',
      format: 'umd',
      moduleName: 'textureApp',
      globals: {
        'substance': 'window.substance',
        'substance-texture': 'window.texture',
        'katex': 'window.katex'
      }
    },
    external: [ 'substance', 'substance-texture', 'katex' ]
  })
  // execute 'install-app-deps'
  fork(b, require.resolve('electron-builder/out/cli/cli.js'), 'install-app-deps', { verbose: true, cwd: APPDIST, await: true })
})

b.task('build:vfs', () => {
  vfs(b, {
    src: ['./data/**/*'],
    dest: DIST+'/vfs.js',
    format: 'umd', moduleName: 'vfs',
    rootDir: path.join(__dirname, 'data')
  })
})

b.task('build:web', ['build:vfs'], () => {
  b.copy('web/index.html', DIST)
  b.js('./web/editor.js', {
    targets: [{
      dest: DIST+'editor.js',
      format: 'umd',
      moduleName: 'textureEditor',
      globals: {
        'substance': 'window.substance',
        'substance-texture': 'window.texture',
        'katex': 'window.katex'
      }
    }],
    external: ['substance', 'substance-texture', 'katex']
  })
  b.copy('./data', DIST+'data')
})

b.task('build:test-assets', () => {
  vfs(b, {
    src: ['./test/fixture/**/*.xml'],
    dest: './tmp/test-vfs.js',
    format: 'es', moduleName: 'fixtures'
  })
})

b.task('build:test-browser', ['build:vfs', 'build:assets', 'build:test-assets'], () => {
  b.copy('test/index.html', 'dist/test/index.html')
  b.copy('node_modules/substance-test/dist/testsuite.js', 'dist/test/testsuite.js')
  b.copy('node_modules/substance-test/dist/test.css', 'dist/test/test.css')
  b.js('test/**/*.test.js', {
    dest: 'dist/test/tests.js',
    format: 'umd', moduleName: 'tests',
    globals: {
      'substance': 'window.substance',
      'substance-test': 'window.substanceTest',
      'substance-texture': 'window.texture',
      'katex': 'window.katex',
      'vfs': 'window.vfs'
    },
    external: [ 'substance', 'substance-test', 'substance-texture', 'katex', 'vfs']
  })
})

b.task('build:vfs-es', () => {
  vfs(b, {
    src: ['./data/**/*'],
    dest: TMP+'/vfs.es.js',
    format: 'es',
    rootDir: path.join(__dirname, 'data')
  })
})

b.task('build:test-nodejs', ['build:test-assets', 'build:vfs-es'], () => {
  b.js([
    'test/testGlobals.js',
    'test/**/*.test.js'
  ], {
    dest: 'tmp/tests.cjs.js',
    format: 'cjs',
    external: [
      'substance-test',
      'substance',
      'substance-texture'
    ],
    // do not require substance-texture from 'node_modules' but from the dist folder
    paths: {
      'substance-texture': '../dist/texture.cjs.js'
    }
  })
})

b.task('build:instrumented-tests', ['build:test-assets', 'build:vfs-es', 'build:cover'], () => {
  b.js([
    'test/testGlobals.js',
    'test/**/*.test.js'
  ], {
    dest: 'tmp/tests.cov.js',
    format: 'cjs',
    external: [
      'substance-test',
      'substance',
      'substance-texture'
    ],
    // do not require substance-texture from 'node_modules' but from the dist folder
    paths: {
      'substance-texture': '../tmp/texture.cov.js'
    }
  })
})

/* HELPERS */

function _buildLib(DEST, platform) {
  let targets = []
  const globals = {
    'substance': 'substance',
    'katex': 'katex',
    'vfs': 'window.vfs'
  }
  let istanbul
  if (platform === 'browser' || platform === 'all') {
    targets.push({
      dest: DEST+'texture.js',
      format: 'umd', moduleName: 'texture', sourceMapRoot: __dirname, sourceMapPrefix: 'texture',
      globals
    })
  }
  if (platform === 'nodejs' || platform === 'all') {
    targets.push({
      dest: DEST+'texture.cjs.js',
      format: 'cjs',
      globals
    })
  }
  if (platform === 'es' || platform === 'all') {
    targets.push({
      dest: DEST+'texture.es.js',
      format: 'es',
      globals
    })
  }
  if (platform === 'cover') {
    targets.push({
      dest: DEST+'texture.cov.js',
      format: 'cjs',
      globals,
    })
    istanbul = {
      include: ['src/**/*.js']
    }
  }
  b.js('./index.es.js', {
    targets,
    external: ['substance', 'katex', 'vfs'],
    istanbul
  })
}

function _compileSchema(name, src, searchDirs, deps, options = {} ) {
  const DEST = `tmp/${name}.data.js`
  const ISSUES = `tmp/${name}.issues.txt`
  const SCHEMA = `tmp/${name}.schema.md`
  const entry = path.basename(src)
  b.custom(`Compiling schema '${name}'...`, {
    src: deps,
    dest: DEST,
    execute() {
      const { compileRNG, checkSchema } = require('substance')
      const xmlSchema = compileRNG(fs, searchDirs, entry)
      b.writeFileSync(DEST, `export default ${JSON.stringify(xmlSchema)}`)
      b.writeFileSync(SCHEMA, xmlSchema.toMD())
      if (options.debug) {
        const issues = checkSchema(xmlSchema)
        const issuesData = [`${issues.length} issues:`, ''].concat(issues).join('\n')
        b.writeFileSync(ISSUES, issuesData)
      }
    }
  })
}

// we used this internally just to get a single-file version of
// the offficial JATS 1.1 rng data set
function _singleJATSFile() {
  [{
    RNG_DIR: 'data/jats/archiving',
    ENTRY: 'JATS-archive-oasis-article1-mathml3.rng',
    DEST: 'src/article/JATS-archiving.rng',
  },
  {
    RNG_DIR: 'data/jats/publishing',
    ENTRY: 'JATS-journalpublishing-oasis-article1-mathml3.rng',
    DEST: 'src/article/JATS-publishing.rng',
  }].forEach(({ RNG_DIR, ENTRY, DEST}) => {
    b.custom(`Pulling JATS spec into a single file...`, {
      src: [RNG_DIR+'/*.rng'],
      dest: DEST,
      execute() {
        const { loadRNG } = require('substance')
        let rng = loadRNG(fs, [RNG_DIR], ENTRY)
        // sort definitions by name
        let grammar = rng.find('grammar')
        let others = []
        let defines = []
        grammar.getChildren().forEach((child) => {
          if (child.tagName === 'define') {
            defines.push(child)
          } else {
            others.push(child)
          }
        })
        defines.sort((a,b) => {
          const aname = a.getAttribute('name').toLowerCase()
          const bname = b.getAttribute('name').toLowerCase()
          if (aname < bname) return -1
          if (bname < aname) return 1
          return 0
        })
        grammar.empty()
        defines.concat(others).forEach((el) => {
          grammar.appendChild('\n  ')
          grammar.appendChild(el)
        })
        let xml = rng.serialize()
        b.writeFileSync(DEST, xml)
      }
    })
  })
}
<|endoftext|>


<commit_before>var requirejs = require('requirejs');
var Backbone = require('backbone');
var sanitizer = require('sanitizer');

var ServicesView = require('../views/services');
var HomepageView = require('../views/homepage');

var ErrorView = require('../views/error');

var ServicesCollection = requirejs('common/collections/services');
var Collection = requirejs('extensions/collections/collection');
var PageConfig = requirejs('page_config');

var get_dashboard_and_render = require('../mixins/get_dashboard_and_render');

var dataSource = {
  'data-group': 'service-aggregates',
  'data-type': 'latest-dataset-values',
  'query-params': {
    'sort_by': '_timestamp:descending'
  }
};

var renderContent = function (req, res, client_instance) {

  var servicesCollection = new ServicesCollection(client_instance.get('items')),
    services = servicesCollection.filterDashboards(ServicesCollection.SERVICES),
    contentDashboards = servicesCollection.filterDashboards(ServicesCollection.CONTENT),
    collection;

  var transactions = new Collection([], {dataSource: dataSource});

  transactions.on('sync', function () {
    var showcaseServiceSlugs = ['pay-register-birth-abroad', 'book-practical-driving-test', 'carers-allowance', 'renew-patent'],
      showcaseServices = [];
    services = formatCollectionData(services);
    services = addServiceDataToCollection(services,  transactions.toJSON());
    collection = new ServicesCollection(services, servicesController.serviceAxes);

    _.each(showcaseServiceSlugs, function (slug) {
      showcaseServices.push(_.findWhere(services, function(service) {
        if (slug === service.slug) {
          service.deptCode = (service.department && service.department.abbr &&
            service.department.abbr.toLowerCase()) || '';
          return true;
        } else {
          return false;
        }
      }));
    });

    var departments = collection.getDepartments();
    var agencies = collection.getAgencies();

    var model = new Backbone.Model(_.extend(PageConfig.commonConfig(req), {
      title: 'Services data',
      'page-type': 'services',
      filter: sanitizer.escape(req.query.filter || ''),
      departmentFilter: req.query.department || null,
      departments: departments,
      agencies: agencies,
      data: services,
      script: (servicesController.type === 'services') ? true : false,
      noun: 'service',
      axesOptions: servicesController.serviceAxes,
      params: _.extend({
        sortby:  'number_of_transactions',
        sortorder:  'descending'
      }, client_instance.get('params'))
    }));

    var client_instance_status = client_instance.get('status');
    var view;
    var View = (servicesController.type === 'services') ? ServicesView : HomepageView;
    if (client_instance_status === 200) {
      view = new View({
        model: model,
        collection: collection,
        contentDashboards: contentDashboards,
        showcaseServices: showcaseServices
      });
    } else {
      view = new ErrorView({
        model: model,
        collection: collection
      });
    }
    view.render();
    res.set('Cache-Control', 'public, max-age=7200');
    res.send(view.html);
  });

  transactions.fetch();

};

function formatCollectionData(services) {
  var kpis = {
    'total_cost': null,
    'number_of_transactions': null,
    'cost_per_transaction': null,
    'tx_digital_takeup': null,
    'digital_takeup': null,
    'completion_rate': null,
    'user_satisfaction_score': null
  };
  _.each(services, function (service) {
    _.extend(service, kpis);
    service.titleLink = '' + service.title + '';
  });
  return services;
}

function addServiceDataToCollection (services, serviceData) {
  var dashboard;

  _.each(serviceData, function (item) {
    var slug = item.dashboard_slug;

    // only bother looking for the dashboard if we don't already have it
    if (!dashboard || (dashboard.slug !== slug)) {
      dashboard = _.findWhere(services, {
        slug: slug
      });
    }
    if (dashboard) {
      delete item._id;
      delete item._timestamp;
      delete item.dashboard_slug;
      _.extend(dashboard, item);
    }

  });
  return services;
}

function servicesController (type, req, res) {
  var client_instance;
  servicesController.type = type;
  client_instance = get_dashboard_and_render(req, res, renderContent);
  client_instance.setPath('');
}

servicesController.serviceAxes = {
  axes: {
    x: {
      key: 'titleLink',
      label: 'Service name'
    },
    y: [
      {
        key: 'number_of_transactions',
        label: 'Transactions per year',
        format: 'integer'
      },
      {
        key: 'total_cost',
        label: 'Annual cost',
        format: 'currency'
      },
      {
        label: 'Cost per transaction',
        key: 'cost_per_transaction',
        format: {
          type: 'currency',
          pence: true
        }
      },
      {
        label: 'Digital take-up',
        key: 'digital_takeup',
        format: {
          type: 'percent',
          dps: 1,
          fixed: 1
        }
      },
      {
        label: 'User satisfaction',
        key: 'user_satisfaction_score',
        format: {
          type: 'percent',
          dps: 1,
          fixed: 1
        }
      },
      {
        label: 'Completion rate',
        key: 'completion_rate',
        format: {
          type: 'percent',
          dps: 1,
          fixed: 1
        }
      }
    ]
  }
};

module.exports = servicesController;<commit_msg>Use Prison visits in list of showcase dashboards<commit_after>var requirejs = require('requirejs');
var Backbone = require('backbone');
var sanitizer = require('sanitizer');

var ServicesView = require('../views/services');
var HomepageView = require('../views/homepage');

var ErrorView = require('../views/error');

var ServicesCollection = requirejs('common/collections/services');
var Collection = requirejs('extensions/collections/collection');
var PageConfig = requirejs('page_config');

var get_dashboard_and_render = require('../mixins/get_dashboard_and_render');

var dataSource = {
  'data-group': 'service-aggregates',
  'data-type': 'latest-dataset-values',
  'query-params': {
    'sort_by': '_timestamp:descending'
  }
};

var renderContent = function (req, res, client_instance) {

  var servicesCollection = new ServicesCollection(client_instance.get('items')),
    services = servicesCollection.filterDashboards(ServicesCollection.SERVICES),
    contentDashboards = servicesCollection.filterDashboards(ServicesCollection.CONTENT),
    collection;

  var transactions = new Collection([], {dataSource: dataSource});

  transactions.on('sync', function () {
    var showcaseServiceSlugs = ['prison-visits', 'book-practical-driving-test', 'carers-allowance', 'renew-patent'],
      showcaseServices = [];
    services = formatCollectionData(services);
    services = addServiceDataToCollection(services,  transactions.toJSON());
    collection = new ServicesCollection(services, servicesController.serviceAxes);

    _.each(showcaseServiceSlugs, function (slug) {
      showcaseServices.push(_.findWhere(services, function(service) {
        if (slug === service.slug) {
          service.deptCode = (service.department && service.department.abbr &&
            service.department.abbr.toLowerCase()) || '';
          return true;
        } else {
          return false;
        }
      }));
    });

    var departments = collection.getDepartments();
    var agencies = collection.getAgencies();

    var model = new Backbone.Model(_.extend(PageConfig.commonConfig(req), {
      title: 'Services data',
      'page-type': 'services',
      filter: sanitizer.escape(req.query.filter || ''),
      departmentFilter: req.query.department || null,
      departments: departments,
      agencies: agencies,
      data: services,
      script: (servicesController.type === 'services') ? true : false,
      noun: 'service',
      axesOptions: servicesController.serviceAxes,
      params: _.extend({
        sortby:  'number_of_transactions',
        sortorder:  'descending'
      }, client_instance.get('params'))
    }));

    var client_instance_status = client_instance.get('status');
    var view;
    var View = (servicesController.type === 'services') ? ServicesView : HomepageView;
    if (client_instance_status === 200) {
      view = new View({
        model: model,
        collection: collection,
        contentDashboards: contentDashboards,
        showcaseServices: showcaseServices
      });
    } else {
      view = new ErrorView({
        model: model,
        collection: collection
      });
    }
    view.render();
    res.set('Cache-Control', 'public, max-age=7200');
    res.send(view.html);
  });

  transactions.fetch();

};

function formatCollectionData(services) {
  var kpis = {
    'total_cost': null,
    'number_of_transactions': null,
    'cost_per_transaction': null,
    'tx_digital_takeup': null,
    'digital_takeup': null,
    'completion_rate': null,
    'user_satisfaction_score': null
  };
  _.each(services, function (service) {
    _.extend(service, kpis);
    service.titleLink = '' + service.title + '';
  });
  return services;
}

function addServiceDataToCollection (services, serviceData) {
  var dashboard;

  _.each(serviceData, function (item) {
    var slug = item.dashboard_slug;

    // only bother looking for the dashboard if we don't already have it
    if (!dashboard || (dashboard.slug !== slug)) {
      dashboard = _.findWhere(services, {
        slug: slug
      });
    }
    if (dashboard) {
      delete item._id;
      delete item._timestamp;
      delete item.dashboard_slug;
      _.extend(dashboard, item);
    }

  });
  return services;
}

function servicesController (type, req, res) {
  var client_instance;
  servicesController.type = type;
  client_instance = get_dashboard_and_render(req, res, renderContent);
  client_instance.setPath('');
}

servicesController.serviceAxes = {
  axes: {
    x: {
      key: 'titleLink',
      label: 'Service name'
    },
    y: [
      {
        key: 'number_of_transactions',
        label: 'Transactions per year',
        format: 'integer'
      },
      {
        key: 'total_cost',
        label: 'Annual cost',
        format: 'currency'
      },
      {
        label: 'Cost per transaction',
        key: 'cost_per_transaction',
        format: {
          type: 'currency',
          pence: true
        }
      },
      {
        label: 'Digital take-up',
        key: 'digital_takeup',
        format: {
          type: 'percent',
          dps: 1,
          fixed: 1
        }
      },
      {
        label: 'User satisfaction',
        key: 'user_satisfaction_score',
        format: {
          type: 'percent',
          dps: 1,
          fixed: 1
        }
      },
      {
        label: 'Completion rate',
        key: 'completion_rate',
        format: {
          type: 'percent',
          dps: 1,
          fixed: 1
        }
      }
    ]
  }
};

module.exports = servicesController;<|endoftext|>


<commit_before>angular.module('mewpipe')
       .factory('errorsInterceptor', errorsInterceptor);

errorsInterceptor.$inject = ['$location','$q','Config'];

function errorsInterceptor($location,$q,Config){
	return {

		'responseError' : function(rejection){

			switch(rejection.status){

				case 401 :
					$location.path('/login');
					break;
				case 404 :
					$location.path('/error/404');
					break;
				case 500 :
					$location.path('/error/500');
					break;

			}

			return $q.reject(rejection);
		}

	}
}<commit_msg>Catch 403 errors and redirect to error pge<commit_after>angular.module('mewpipe')
       .factory('errorsInterceptor', errorsInterceptor);

errorsInterceptor.$inject = ['$location','$q','Config'];

function errorsInterceptor($location,$q,Config){
	return {

		'responseError' : function(rejection){

			switch(rejection.status){

				case 401 :
					$location.path('/login');
					break;
				case 403 :
					$location.path('/error/403');
					break;
				case 404 :
					$location.path('/error/404');
					break;
				case 500 :
					$location.path('/error/500');
					break;

			}

			return $q.reject(rejection);
		}

	}
}<|endoftext|>


<commit_before>#!/usr/bin/env node
/**
 * @fileoverview
 * Compiles our icons into static .js files that can be imported in the browser
 * and are tree-shakeable.
 * The static .js files go in icons/{filename}.js.
 * Also generates an index.js that exports all icons by title, but is not tree-shakeable
 */

const dataFile = "../_data/simple-icons.json";
const indexFile = `${__dirname}/../index.js`;
const iconsDir = `${__dirname}/../icons`;
const data = require(dataFile);
const fs = require("fs");

const { titleToFilename } = require("./utils");

const icons = {};
data.icons.forEach(icon => {
    const filename = titleToFilename(icon.title);
    icon.svg = fs.readFileSync(`${iconsDir}/${filename}.svg`, "utf8");
    icons[icon.title] = icon;
    // write the static .js file for the icon
    fs.writeFileSync(
        `${iconsDir}/${filename}.js`,
        `module.exports=${JSON.stringify(icon)};`
    );
});

// write our generic index.js
fs.writeFileSync(indexFile, `module.exports=${JSON.stringify(icons)};`);
<commit_msg>Add backwards compatability for Adobe Lightroom<commit_after>#!/usr/bin/env node
/**
 * @fileoverview
 * Compiles our icons into static .js files that can be imported in the browser
 * and are tree-shakeable.
 * The static .js files go in icons/{filename}.js.
 * Also generates an index.js that exports all icons by title, but is not tree-shakeable
 */

const dataFile = "../_data/simple-icons.json";
const indexFile = `${__dirname}/../index.js`;
const iconsDir = `${__dirname}/../icons`;
const data = require(dataFile);
const fs = require("fs");

const { titleToFilename } = require("./utils");

const icons = {};
data.icons.forEach(icon => {
    const filename = titleToFilename(icon.title);
    icon.svg = fs.readFileSync(`${iconsDir}/${filename}.svg`, "utf8");
    icons[icon.title] = icon;
    // write the static .js file for the icon
    fs.writeFileSync(
        `${iconsDir}/${filename}.js`,
        `module.exports=${JSON.stringify(icon)};`
    );
});

/* Backwards compatibility */
// https://github.com/simple-icons/simple-icons/pull/1365
const adobeLightroom = icons["Adobe Lightroom Classic"];
adobeLightroom.title = "Adobe Lightroom";
icons["Adobe Lightroom"] = adobeLightroom;
fs.writeFileSync(
    `${iconsDir}/adobelightroom.svg`,
    adobeLightroom.svg
);
fs.writeFileSync(
    `${iconsDir}/adobelightroom.js`,
    `module.exports=${JSON.stringify(adobeLightroom)};`
);

// write our generic index.js
fs.writeFileSync(indexFile, `module.exports=${JSON.stringify(icons)};`);
<|endoftext|>


<commit_before>if (process.env.BROWSERSTACK_USER === undefined ||
    process.env.BROWSERSTACK_ACCESS_KEY === undefined) {
  process.exit(1);
}
<commit_msg>Add logging line to preversion script on fail. (#517)<commit_after>if (process.env.BROWSERSTACK_USER === undefined ||
    process.env.BROWSERSTACK_ACCESS_KEY === undefined) {
  console.error(
      'ERROR: Both BROWSERSTACK_USER and BROWSERSTACK_ACCESS_KEY must be set');
  process.exit(1);
}
<|endoftext|>


<commit_before>
export function addTodo (text) {
  return {type: 'ADD_TODO', text: text, completed: false, id: Date.now()}
}

export function updateTodo (todo, updates) {
  return {type: 'UPDATE_TODO', id: todo.id, updates: updates}
}

export function deleteTodo (todo) {
  return {type: 'DELETE_TODO', id: todo.id}
}

export function selectTodoFilter (todoFilter) {
  return {type: 'SELECT_TODO_FILTER', todoFilter: todoFilter}
}

export function showDeleteButtonOnTodo (id) {
  return {type: 'SHOW_DELETE_BUTTON_ON_TODO', todo: id}
}

<commit_msg>Use arrows functions as action creators<commit_after>
export const addTodo = (text) => {
  return {type: 'ADD_TODO', text: text, completed: false, id: Date.now()}
}

export const updateTodo = (todo, updates) => {
  return {type: 'UPDATE_TODO', id: todo.id, updates: updates}
}

export const deleteTodo = (todo) => {
  return {type: 'DELETE_TODO', id: todo.id}
}

export const selectTodoFilter = (todoFilter) => {
  return {type: 'SELECT_TODO_FILTER', todoFilter: todoFilter}
}

export const showDeleteButtonOnTodo = (id) => {
  return {type: 'SHOW_DELETE_BUTTON_ON_TODO', todo: id}
}
<|endoftext|>


<commit_before>module.exports = function(robot) {
    robot.respond(/send mail/i, function(msg){
        msg.reply("mail sent");
    });
}<commit_msg>deneme<commit_after>var nodemailer = require('nodemailer');

module.exports = function(robot) {
    robot.respond(/send mail/i, function(msg){
        msg.reply("mail sent");
    });
}<|endoftext|>


<commit_before>/**
 * @class Oskari.mapframework.bundle.postprocessor.PostProcessorBundleInstance
 *
 * Used for highlighting wfs features on pageload etc. Calls other bundles to accomplish stuff
 * after everything has been loaded and started.
 *
 * See Oskari.mapframework.bundle.postprocessor.PostProcessorBundle for bundle definition.
 *
 */
Oskari.clazz.define("Oskari.mapframework.bundle.postprocessor.PostProcessorBundleInstance",

    /**
     * @method create called automatically on construction
     * @static
     */

    function () {
        this.sandbox = null;
    }, {
        /**
         * @static
         * @property __name
         */
        __name: 'PostProcessor',
        /**
         * @method getName
         * @return {String} the name for the component
         */
        "getName": function () {
            return this.__name;
        },
        /**
         * @method start
         * implements BundleInstance protocol start methdod
         */
        "start": function () {
            var me = this;
            var conf = this.conf;
            var sandboxName = (conf ? conf.sandbox : null) || 'sandbox';
            var sandbox = Oskari.getSandbox(sandboxName);

            this.sandbox = sandbox;

            sandbox.register(this);
            if (this.state) {
                var hiliteLayerId = this.state.highlightFeatureLayerId;
                this._highlightFeature(hiliteLayerId, this.state.highlightFeatureId);
            }
        },
        /**
         * @method _highlightFeature
         * @private
         * Adds the layer if its not yet selected
         * @param {String} layerId
         * @param {String/String[]} featureId single or array of feature ids to hilight
         */
        _highlightFeature: function (layerId, featureId) {
            if (featureId && layerId) {

                // move map to location
                var points = this.state.featurePoints;
                if (points) {
                    this._showPoints(points);
                }

                // request for highlight image, note that the map must be in correct
                // location BEFORE this or we get a blank image
                var builder = this.sandbox.getEventBuilder('WFSFeaturesSelectedEvent');
                var featureIdList = [];
                // check if the param is already an array
                if (Object.prototype.toString.call(featureId) === '[object Array]') {
                    featureIdList = featureId;
                } else {
                    featureIdList.push(featureId);
                }
                // create dummy layer since the real one might not be available and we only need it for id
                var mapLayerService = this.sandbox.getService('Oskari.mapframework.service.MapLayerService');
                if (!mapLayerService) {
                    // service not found - should never happen
                    return;
                }
                var dummyLayer = mapLayerService.createLayerTypeInstance('wfslayer');
                if (!dummyLayer) {
                    // layer couldnt be created - wfslayermodelbuilder not registered!
                    return;
                }
                dummyLayer.setId(layerId);
                dummyLayer.setOpacity(100);
                var event = builder(featureIdList, dummyLayer);
                this.sandbox.notifyAll(event);
            }
        },
        /**
         * @method _showPoints
         * @private
         * Sends a mapmoverequest to fit the points on the map viewport
         * @param {Object[]} points array of objects containing lon/lat properties
         */
        _showPoints: function (points) {
            var olPoints = new OpenLayers.Geometry.MultiPoint(),
                count,
                point,
                olPoint;
            for (count = 0; count < points.length; ++count) {
                point = points[count];
                olPoint = new OpenLayers.Geometry.Point(point.lat, point.lon);
                olPoints.addPoint(olPoint);
            }
            var bounds = olPoints.getBounds();
            var centroid = olPoints.getCentroid();

            var rb = this.sandbox.getRequestBuilder('MapMoveRequest'),
                req;
            if (rb && count > 0) {
                if (count === 1) {
                    // zoom to level 9 if a single point
                    req = rb(centroid.x, centroid.y, 9);
                    this.sandbox.request(this, req);
                } else {
                    req = rb(centroid.x, centroid.y, bounds);
                    this.sandbox.request(this, req);
                }
            }
        },
        /**
         * @method onEvent
         * @param {Oskari.mapframework.event.Event} event a Oskari event object
         * Event is handled forwarded to correct #eventHandlers if found or discarded if not.
         */
        onEvent: function (event) {

            var handler = this.eventHandlers[event.getName()];
            if (!handler) {
                return;
            }
            return handler.apply(this, [event]);
        },
        /**
         * @property {Object} eventHandlers
         * @static
         */
        eventHandlers: {
            /**
             * @method MapLayerEvent
             * If start has already campleted hilighting, this won't trigger, if not
             * this calls highlight no the selected target
             * @param {Oskari.mapframework.event.common.MapLayerEvent} event
             */
            'MapLayerEvent': function (event) {
                // layerId in event is null for initial ajax load
                if ('add' === event.getOperation() && !event.getLayerId()) {
                    this._highlightFeature(this.state.highlightFeatureLayerId, this.state.highlightFeatureId);
                }
            }
        },
        /**
         * @method init
         * implements Module protocol init method - does nothing atm
         */
        "init": function () {
            return null;
        },
        /**
         * @method update
         * implements BundleInstance protocol update method - does nothing atm
         */
        "update": function () {

        },
        /**
         * @method stop
         * implements BundleInstance protocol stop method
         */
        "stop": function () {}
    }, {
        /**
         * @property {String[]} protocol
         * @static
         */
        "protocol": ["Oskari.bundle.BundleInstance", 'Oskari.mapframework.module.Module']
    });<commit_msg>Exchanged lan <> lot values since backend now returns them correctly<commit_after>/**
 * @class Oskari.mapframework.bundle.postprocessor.PostProcessorBundleInstance
 *
 * Used for highlighting wfs features on pageload etc. Calls other bundles to accomplish stuff
 * after everything has been loaded and started.
 *
 * See Oskari.mapframework.bundle.postprocessor.PostProcessorBundle for bundle definition.
 *
 */
Oskari.clazz.define("Oskari.mapframework.bundle.postprocessor.PostProcessorBundleInstance",

    /**
     * @method create called automatically on construction
     * @static
     */

    function () {
        this.sandbox = null;
    }, {
        /**
         * @static
         * @property __name
         */
        __name: 'PostProcessor',
        /**
         * @method getName
         * @return {String} the name for the component
         */
        "getName": function () {
            return this.__name;
        },
        /**
         * @method start
         * implements BundleInstance protocol start methdod
         */
        "start": function () {
            var me = this;
            var conf = this.conf;
            var sandboxName = (conf ? conf.sandbox : null) || 'sandbox';
            var sandbox = Oskari.getSandbox(sandboxName);

            this.sandbox = sandbox;

            sandbox.register(this);
            if (this.state) {
                var hiliteLayerId = this.state.highlightFeatureLayerId;
                this._highlightFeature(hiliteLayerId, this.state.highlightFeatureId);
            }
        },
        /**
         * @method _highlightFeature
         * @private
         * Adds the layer if its not yet selected
         * @param {String} layerId
         * @param {String/String[]} featureId single or array of feature ids to hilight
         */
        _highlightFeature: function (layerId, featureId) {
            if (featureId && layerId) {

                // move map to location
                var points = this.state.featurePoints;
                if (points) {
                    this._showPoints(points);
                }

                // request for highlight image, note that the map must be in correct
                // location BEFORE this or we get a blank image
                var builder = this.sandbox.getEventBuilder('WFSFeaturesSelectedEvent');
                var featureIdList = [];
                // check if the param is already an array
                if (Object.prototype.toString.call(featureId) === '[object Array]') {
                    featureIdList = featureId;
                } else {
                    featureIdList.push(featureId);
                }
                // create dummy layer since the real one might not be available and we only need it for id
                var mapLayerService = this.sandbox.getService('Oskari.mapframework.service.MapLayerService');
                if (!mapLayerService) {
                    // service not found - should never happen
                    return;
                }
                var dummyLayer = mapLayerService.createLayerTypeInstance('wfslayer');
                if (!dummyLayer) {
                    // layer couldnt be created - wfslayermodelbuilder not registered!
                    return;
                }
                dummyLayer.setId(layerId);
                dummyLayer.setOpacity(100);
                var event = builder(featureIdList, dummyLayer);
                this.sandbox.notifyAll(event);
            }
        },
        /**
         * @method _showPoints
         * @private
         * Sends a mapmoverequest to fit the points on the map viewport
         * @param {Object[]} points array of objects containing lon/lat properties
         */
        _showPoints: function (points) {
            var olPoints = new OpenLayers.Geometry.MultiPoint(),
                count,
                point,
                olPoint;
            for (count = 0; count < points.length; ++count) {
                point = points[count];
                olPoint = new OpenLayers.Geometry.Point(point.lon, point.lat);
                olPoints.addPoint(olPoint);
            }
            var bounds = olPoints.getBounds();
            var centroid = olPoints.getCentroid();

            var rb = this.sandbox.getRequestBuilder('MapMoveRequest'),
                req;
            if (rb && count > 0) {
                if (count === 1) {
                    // zoom to level 9 if a single point
                    req = rb(centroid.x, centroid.y, 9);
                    this.sandbox.request(this, req);
                } else {
                    req = rb(centroid.x, centroid.y, bounds);
                    this.sandbox.request(this, req);
                }
            }
        },
        /**
         * @method onEvent
         * @param {Oskari.mapframework.event.Event} event a Oskari event object
         * Event is handled forwarded to correct #eventHandlers if found or discarded if not.
         */
        onEvent: function (event) {

            var handler = this.eventHandlers[event.getName()];
            if (!handler) {
                return;
            }
            return handler.apply(this, [event]);
        },
        /**
         * @property {Object} eventHandlers
         * @static
         */
        eventHandlers: {
            /**
             * @method MapLayerEvent
             * If start has already campleted hilighting, this won't trigger, if not
             * this calls highlight no the selected target
             * @param {Oskari.mapframework.event.common.MapLayerEvent} event
             */
            'MapLayerEvent': function (event) {
                // layerId in event is null for initial ajax load
                if ('add' === event.getOperation() && !event.getLayerId()) {
                    this._highlightFeature(this.state.highlightFeatureLayerId, this.state.highlightFeatureId);
                }
            }
        },
        /**
         * @method init
         * implements Module protocol init method - does nothing atm
         */
        "init": function () {
            return null;
        },
        /**
         * @method update
         * implements BundleInstance protocol update method - does nothing atm
         */
        "update": function () {

        },
        /**
         * @method stop
         * implements BundleInstance protocol stop method
         */
        "stop": function () {}
    }, {
        /**
         * @property {String[]} protocol
         * @static
         */
        "protocol": ["Oskari.bundle.BundleInstance", 'Oskari.mapframework.module.Module']
    });<|endoftext|>


<commit_before><commit_msg>Fix dxo when giving only one argument<commit_after><|endoftext|>


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

var util = require('util'),
    http = require('http'),
    fs = require('fs'),
    url = require('url'),
    events = require('events');

var DEFAULT_PORT = 8000;

function main(argv) {
  new HttpServer({
    'GET': createServlet(StaticServlet),
    'HEAD': createServlet(StaticServlet)
  }).start(Number(argv[2]) || DEFAULT_PORT);
}

function escapeHtml(value) {
  return value.toString().
    replace('<', '<').
    replace('>', '>').
    replace('"', '"');
}

function createServlet(Class) {
  var servlet = new Class();
  return servlet.handleRequest.bind(servlet);
}

/**
 * An Http server implementation that uses a map of methods to decide
 * action routing.
 *
 * @param {Object} Map of method => Handler function
 */
function HttpServer(handlers) {
  this.handlers = handlers;
  this.server = http.createServer(this.handleRequest_.bind(this));
}

HttpServer.prototype.start = function(port) {
  this.port = port;
  this.server.listen(port);
  util.puts('Http Server running at http://localhost:' + port + '/');
};

HttpServer.prototype.parseUrl_ = function(urlString) {
  var parsed = url.parse(urlString);
  parsed.pathname = url.resolve('/', parsed.pathname);
  return url.parse(url.format(parsed), true);
};

HttpServer.prototype.handleRequest_ = function(req, res) {
  var logEntry = req.method + ' ' + req.url;
  if (req.headers['user-agent']) {
    logEntry += ' ' + req.headers['user-agent'];
  }
  util.puts(logEntry);
  req.url = this.parseUrl_(req.url);
  var handler = this.handlers[req.method];
  if (!handler) {
    res.writeHead(501);
    res.end();
  } else {
    handler.call(this, req, res);
  }
};

/**
 * Handles static content.
 */
function StaticServlet() {}

StaticServlet.MimeMap = {
  'txt': 'text/plain',
  'html': 'text/html',
  'css': 'text/css',
  'xml': 'application/xml',
  'json': 'application/json',
  'js': 'application/javascript',
  'jpg': 'image/jpeg',
  'jpeg': 'image/jpeg',
  'gif': 'image/gif',
  'png': 'image/png',
  'svg': 'image/svg+xml'
};

StaticServlet.prototype.handleRequest = function(req, res) {
  var self = this;
  var path = ('./' + req.url.pathname).replace('//','/').replace(/%(..)/g, function(match, hex){
    return String.fromCharCode(parseInt(hex, 16));
  });
  var parts = path.split('/');
  if (parts[parts.length-1].charAt(0) === '.')
    return self.sendForbidden_(req, res, path);
  fs.stat(path, function(err, stat) {
    if (err)
      return self.sendMissing_(req, res, path);
    if (stat.isDirectory())
      return self.sendDirectory_(req, res, path);
    return self.sendFile_(req, res, path);
  });
}

StaticServlet.prototype.sendError_ = function(req, res, error) {
  res.writeHead(500, {
      'Content-Type': 'text/html'
  });
  res.write('<!doctype html>\n');
  res.write('<title>Internal Server Error</title>\n');
  res.write('
Internal Server Error
');
  res.write('
' + escapeHtml(util.inspect(error)) + '
');
  util.puts('500 Internal Server Error');
  util.puts(util.inspect(error));
};

StaticServlet.prototype.sendMissing_ = function(req, res, path) {
  path = path.substring(1);
  res.writeHead(404, {
      'Content-Type': 'text/html'
  });
  res.write('<!doctype html>\n');
  res.write('<title>404 Not Found</title>\n');
  res.write('
Not Found
');
  res.write(
    '
The requested URL ' +
    escapeHtml(path) +
    ' was not found on this server.
'
  );
  res.end();
  util.puts('404 Not Found: ' + path);
};

StaticServlet.prototype.sendForbidden_ = function(req, res, path) {
  path = path.substring(1);
  res.writeHead(403, {
      'Content-Type': 'text/html'
  });
  res.write('<!doctype html>\n');
  res.write('<title>403 Forbidden</title>\n');
  res.write('
Forbidden
');
  res.write(
    '
You do not have permission to access ' +
    escapeHtml(path) + ' on this server.
'
  );
  res.end();
  util.puts('403 Forbidden: ' + path);
};

StaticServlet.prototype.sendRedirect_ = function(req, res, redirectUrl) {
  res.writeHead(301, {
      'Content-Type': 'text/html',
      'Location': redirectUrl
  });
  res.write('<!doctype html>\n');
  res.write('<title>301 Moved Permanently</title>\n');
  res.write('
Moved Permanently
');
  res.write(
    '
The document has moved here.
'
  );
  res.end();
  util.puts('301 Moved Permanently: ' + redirectUrl);
};

StaticServlet.prototype.sendFile_ = function(req, res, path) {
  var self = this;
  var file = fs.createReadStream(path);
  res.writeHead(200, {
    'Content-Type': StaticServlet.
      MimeMap[path.split('.').pop()] || 'text/plain'
  });
  if (req.method === 'HEAD') {
    res.end();
  } else {
    file.on('data', res.write.bind(res));
    file.on('close', function() {
      res.end();
    });
    file.on('error', function(error) {
      self.sendError_(req, res, error);
    });
  }
};

StaticServlet.prototype.sendDirectory_ = function(req, res, path) {
  var self = this;
  if (path.match(/[^\/]$/)) {
    req.url.pathname += '/';
    var redirectUrl = url.format(url.parse(url.format(req.url)));
    return self.sendRedirect_(req, res, redirectUrl);
  }
  fs.readdir(path, function(err, files) {
    if (err)
      return self.sendError_(req, res, error);

    if (!files.length)
      return self.writeDirectoryIndex_(req, res, path, []);

    var remaining = files.length;
    files.forEach(function(fileName, index) {
      fs.stat(path + '/' + fileName, function(err, stat) {
        if (err)
          return self.sendError_(req, res, err);
        if (stat.isDirectory()) {
          files[index] = fileName + '/';
        }
        if (!(--remaining))
          return self.writeDirectoryIndex_(req, res, path, files);
      });
    });
  });
};

StaticServlet.prototype.writeDirectoryIndex_ = function(req, res, path, files) {
  path = path.substring(1);
  res.writeHead(200, {
    'Content-Type': 'text/html'
  });
  if (req.method === 'HEAD') {
    res.end();
    return;
  }
  res.write('<!doctype html>\n');
  res.write('<title>' + escapeHtml(path) + '</title>\n');
  res.write('\n');
  res.write('
Directory: ' + escapeHtml(path) + '
');
  res.write('
');
  files.forEach(function(fileName) {
    if (fileName.charAt(0) !== '.') {
      res.write('
' +
        escapeHtml(fileName) + '
');
    }
  });
  res.write('
');
  res.end();
};

// Must be last,
main(process.argv);
<commit_msg>heroky port<commit_after>#!/usr/bin/env node

var util = require('util'),
    http = require('http'),
    fs = require('fs'),
    url = require('url'),
    events = require('events');

var DEFAULT_PORT = process.env.PORT || 8000;

function main(argv) {
  new HttpServer({
    'GET': createServlet(StaticServlet),
    'HEAD': createServlet(StaticServlet)
  }).start(Number(argv[2]) || DEFAULT_PORT);
}

function escapeHtml(value) {
  return value.toString().
    replace('<', '<').
    replace('>', '>').
    replace('"', '"');
}

function createServlet(Class) {
  var servlet = new Class();
  return servlet.handleRequest.bind(servlet);
}

/**
 * An Http server implementation that uses a map of methods to decide
 * action routing.
 *
 * @param {Object} Map of method => Handler function
 */
function HttpServer(handlers) {
  this.handlers = handlers;
  this.server = http.createServer(this.handleRequest_.bind(this));
}

HttpServer.prototype.start = function(port) {
  this.port = port;
  this.server.listen(port);
  util.puts('Http Server running at http://localhost:' + port + '/');
};

HttpServer.prototype.parseUrl_ = function(urlString) {
  var parsed = url.parse(urlString);
  parsed.pathname = url.resolve('/', parsed.pathname);
  return url.parse(url.format(parsed), true);
};

HttpServer.prototype.handleRequest_ = function(req, res) {
  var logEntry = req.method + ' ' + req.url;
  if (req.headers['user-agent']) {
    logEntry += ' ' + req.headers['user-agent'];
  }
  util.puts(logEntry);
  req.url = this.parseUrl_(req.url);
  var handler = this.handlers[req.method];
  if (!handler) {
    res.writeHead(501);
    res.end();
  } else {
    handler.call(this, req, res);
  }
};

/**
 * Handles static content.
 */
function StaticServlet() {}

StaticServlet.MimeMap = {
  'txt': 'text/plain',
  'html': 'text/html',
  'css': 'text/css',
  'xml': 'application/xml',
  'json': 'application/json',
  'js': 'application/javascript',
  'jpg': 'image/jpeg',
  'jpeg': 'image/jpeg',
  'gif': 'image/gif',
  'png': 'image/png',
  'svg': 'image/svg+xml'
};

StaticServlet.prototype.handleRequest = function(req, res) {
  var self = this;
  var path = ('./' + req.url.pathname).replace('//','/').replace(/%(..)/g, function(match, hex){
    return String.fromCharCode(parseInt(hex, 16));
  });
  var parts = path.split('/');
  if (parts[parts.length-1].charAt(0) === '.')
    return self.sendForbidden_(req, res, path);
  fs.stat(path, function(err, stat) {
    if (err)
      return self.sendMissing_(req, res, path);
    if (stat.isDirectory())
      return self.sendDirectory_(req, res, path);
    return self.sendFile_(req, res, path);
  });
}

StaticServlet.prototype.sendError_ = function(req, res, error) {
  res.writeHead(500, {
      'Content-Type': 'text/html'
  });
  res.write('<!doctype html>\n');
  res.write('<title>Internal Server Error</title>\n');
  res.write('
Internal Server Error
');
  res.write('
' + escapeHtml(util.inspect(error)) + '
');
  util.puts('500 Internal Server Error');
  util.puts(util.inspect(error));
};

StaticServlet.prototype.sendMissing_ = function(req, res, path) {
  path = path.substring(1);
  res.writeHead(404, {
      'Content-Type': 'text/html'
  });
  res.write('<!doctype html>\n');
  res.write('<title>404 Not Found</title>\n');
  res.write('
Not Found
');
  res.write(
    '
The requested URL ' +
    escapeHtml(path) +
    ' was not found on this server.
'
  );
  res.end();
  util.puts('404 Not Found: ' + path);
};

StaticServlet.prototype.sendForbidden_ = function(req, res, path) {
  path = path.substring(1);
  res.writeHead(403, {
      'Content-Type': 'text/html'
  });
  res.write('<!doctype html>\n');
  res.write('<title>403 Forbidden</title>\n');
  res.write('
Forbidden
');
  res.write(
    '
You do not have permission to access ' +
    escapeHtml(path) + ' on this server.
'
  );
  res.end();
  util.puts('403 Forbidden: ' + path);
};

StaticServlet.prototype.sendRedirect_ = function(req, res, redirectUrl) {
  res.writeHead(301, {
      'Content-Type': 'text/html',
      'Location': redirectUrl
  });
  res.write('<!doctype html>\n');
  res.write('<title>301 Moved Permanently</title>\n');
  res.write('
Moved Permanently
');
  res.write(
    '
The document has moved here.
'
  );
  res.end();
  util.puts('301 Moved Permanently: ' + redirectUrl);
};

StaticServlet.prototype.sendFile_ = function(req, res, path) {
  var self = this;
  var file = fs.createReadStream(path);
  res.writeHead(200, {
    'Content-Type': StaticServlet.
      MimeMap[path.split('.').pop()] || 'text/plain'
  });
  if (req.method === 'HEAD') {
    res.end();
  } else {
    file.on('data', res.write.bind(res));
    file.on('close', function() {
      res.end();
    });
    file.on('error', function(error) {
      self.sendError_(req, res, error);
    });
  }
};

StaticServlet.prototype.sendDirectory_ = function(req, res, path) {
  var self = this;
  if (path.match(/[^\/]$/)) {
    req.url.pathname += '/';
    var redirectUrl = url.format(url.parse(url.format(req.url)));
    return self.sendRedirect_(req, res, redirectUrl);
  }
  fs.readdir(path, function(err, files) {
    if (err)
      return self.sendError_(req, res, error);

    if (!files.length)
      return self.writeDirectoryIndex_(req, res, path, []);

    var remaining = files.length;
    files.forEach(function(fileName, index) {
      fs.stat(path + '/' + fileName, function(err, stat) {
        if (err)
          return self.sendError_(req, res, err);
        if (stat.isDirectory()) {
          files[index] = fileName + '/';
        }
        if (!(--remaining))
          return self.writeDirectoryIndex_(req, res, path, files);
      });
    });
  });
};

StaticServlet.prototype.writeDirectoryIndex_ = function(req, res, path, files) {
  path = path.substring(1);
  res.writeHead(200, {
    'Content-Type': 'text/html'
  });
  if (req.method === 'HEAD') {
    res.end();
    return;
  }
  res.write('<!doctype html>\n');
  res.write('<title>' + escapeHtml(path) + '</title>\n');
  res.write('\n');
  res.write('
Directory: ' + escapeHtml(path) + '
');
  res.write('
');
  files.forEach(function(fileName) {
    if (fileName.charAt(0) !== '.') {
      res.write('
' +
        escapeHtml(fileName) + '
');
    }
  });
  res.write('
');
  res.end();
};

// Must be last,
main(process.argv);
<|endoftext|>


<commit_before>import { $unserialize } from 'core/types';
import { _doc } from 'core/variables';

const PARAMETER_REGEXP = /([:*])(\w+)/g;
const WILDCARD_REGEXP = /\*/g;
const REPLACE_VARIABLE_REGEXP = '([^\/]+)';
const REPLACE_WILDCARD = '(?:.*)';
const FOLLOWED_BY_SLASH_REGEXP = '(?:\/$|$)';
const MATCH_REGEXP_FLAGS = '';
const REMOVE_SLASHES_REGEXP = /^\/|\/$/g;
let _routes = [];

/**
 * Add a route to routes array
 *
 * @private
 * @param routes
 */
function _add(routes) {
	const count = routes.length;

	for (let i = 0; i < count; i++) {
		let route = _getRoute(routes[i].path);

		if (route) {
			_routes[route.index] = routes[i];
			break;
		}

		_routes.push(routes[i]);
	}
}

/**
 * Retrieve existing route with associated index
 *
 * @param {string} value
 * @param {string} key
 * @returns {Object}
 * @private
 */
function _getRoute(value) {
	const count = _routes.length;

	for (let i = 0; i < count; i++) {
		if (_routes[i].path === value || _routes[i].name === value) {
			return {
				route: _routes[i],
				index: i
			};
		}
	}

	return null;
}

/**
 * Parse url and return results
 *
 * @private
 * @param {string} value
 * @returns {Object}
 * @private
 */
function _parseUrl(value) {
	const a = _doc.createElement('a');
	a.href = value || window.location;

	const search = a.search,
		path = a.pathname.replace(REMOVE_SLASHES_REGEXP, '');

	return {
		full: '/' + path + search + a.hash,
		hash: a.hash.slice(1),
		path: '/' + path,
		query: search ? $unserialize(search) : {},
		segments: path.split('/'),
		url: a.href,
		port: a.port
	};
}

export default {
	map(routes) {
		_add(routes);

		return this;
	},
	reset() {
		_routes = [];
	},
	routes(value) {
		if (value) {
			let result = _getRoute(value);

			if (result) {
				return result.route;
			}
		}

		return _routes;
	},
	run() {
		//
	},
	uri(value) {
		return _parseUrl(value);
	}
};<commit_msg>Add code comments to currently defined public route methods<commit_after>import { $unserialize } from 'core/types';
import { _doc } from 'core/variables';

const PARAMETER_REGEXP = /([:*])(\w+)/g;
const WILDCARD_REGEXP = /\*/g;
const REPLACE_VARIABLE_REGEXP = '([^\/]+)';
const REPLACE_WILDCARD = '(?:.*)';
const FOLLOWED_BY_SLASH_REGEXP = '(?:\/$|$)';
const MATCH_REGEXP_FLAGS = '';
const REMOVE_SLASHES_REGEXP = /^\/|\/$/g;
let _routes = [];

/**
 * Add a route to routes array
 *
 * @private
 * @param routes
 */
function _add(routes) {
	const count = routes.length;

	for (let i = 0; i < count; i++) {
		let route = _getRoute(routes[i].path);

		if (route) {
			_routes[route.index] = routes[i];
			break;
		}

		_routes.push(routes[i]);
	}
}

/**
 * Retrieve existing route with associated index
 *
 * @param {string} value
 * @param {string} key
 * @returns {Object}
 * @private
 */
function _getRoute(value) {
	const count = _routes.length;

	for (let i = 0; i < count; i++) {
		if (_routes[i].path === value || _routes[i].name === value) {
			return {
				route: _routes[i],
				index: i
			};
		}
	}

	return null;
}

/**
 * Parse url and return results
 *
 * @private
 * @param {string} value
 * @returns {Object}
 * @private
 */
function _parseUrl(value) {
	const a = _doc.createElement('a');
	a.href = value || window.location;

	const search = a.search,
		path = a.pathname.replace(REMOVE_SLASHES_REGEXP, '');

	return {
		full: '/' + path + search + a.hash,
		hash: a.hash.slice(1),
		path: '/' + path,
		query: search ? $unserialize(search) : {},
		segments: path.split('/'),
		url: a.href,
		port: a.port
	};
}

export default {
	/**
	 * Register routes
	 *
	 * @param {Array} routes
	 * @returns {Object}
	 */
	map(routes) {
		_add(routes);

		return this;
	},

	/**
	 * Reset all routes - mainly for testing purposes
	 */
	reset() {
		_routes = [];
	},

	/**
	 * Retrieve all routes or specific route by name/path
	 *
	 * @param {string} [value]
	 * @returns {Object|Array}
	 */
	routes(value) {
		if (value) {
			let result = _getRoute(value);

			if (result) {
				return result.route;
			}
		}

		return _routes;
	},
	run() {
		//
	},
	uri(value) {
		return _parseUrl(value);
	}
};<|endoftext|>


<commit_before>var HaProxy = require("github.com/NetSys/quilt/specs/haproxy/haproxy").Haproxy;
var Mongo = require("github.com/NetSys/quilt/specs/mongo/mongo");
var Node = require("github.com/NetSys/quilt/specs/node/node");
var Inf = require("github.com/ejj/quilt-demo/inf");

var count = 4;
var mongo = new Mongo(count);
var app = new Node({
  nWorker: count,
  image: "quilt/mean-service",
  env: {
    PORT: "80",
    MONGO_URI: mongo.uri("mean-example")
  }
});
var haproxy = new HaProxy(count, app.services());

mongo.connect(mongo.port(), app);
app.connect(mongo.port(), mongo);
haproxy.public();

var inf = Inf.New(4);
inf.deploy(app);
inf.deploy(mongo);
inf.deploy(haproxy);
<commit_msg>Migrate spec to node.js<commit_after>"use strict";

const {Range, githubKeys, LabelRule} = require("@quilt/quilt");
var HaProxy = require("@quilt/haproxy").Haproxy;
var Mongo = require("@quilt/mongo");
var Node = require("@quilt/nodejs");
var Inf = require("./inf.js");

var count = 4;
var mongo = new Mongo(count);
var app = new Node({
    nWorker: count,
    repo: "https://github.com/tejasmanohar/node-todo.git",
    env: {
        PORT: "80",
        MONGO_URI: mongo.uri("mean-example")
    }
});
var haproxy = new HaProxy(count, app.services());

mongo.connect(mongo.port(), app);
app.connect(mongo.port(), mongo);
haproxy.public();

var inf = Inf.New(4);
inf.deploy(app);
inf.deploy(mongo);
inf.deploy(haproxy);
<|endoftext|>


<commit_before>var currentTable;
var tempSelection;

// Add onclick function to tabs
$("[id$=-tab]").each(function () {
    $(this).click(function(){
        var oldtable = $('.tab.active').data('name') || 'zone';
        $('.tab').removeClass("active");
        $(this).addClass("active");

        // Remember scroll position
        var scroll = $('.tabulator-tableHolder').scrollTop();

        // Store previously selected buildings before creating new table
        if (inputstore.getData(oldtable).length) {
            if (inputstore.getSelected()) {
                tempSelection = [...inputstore.getSelected()];
            }
        }

        var name = $(this).data('name');
        $('#property-table').empty();
        createTable("property-table",`${name}`,
            inputstore.getData(name),
            inputstore.getColumns(name),
            inputstore.getColumnTypes(name)
        );

        // Restore previous selected
        inputstore.setSelected(tempSelection);
        if (inputstore.getSelected()) {
            currentTable.selectRow(inputstore.getSelected());
        }
        filterSelection(inputstore.getSelected());

        currentTable.redraw();
        $('.tabulator-tableHolder').scrollTop(scroll);

    });
});

function createTable(parent, name, values, columns, types) {
    var placeholder = '';
    var tool = '';
    if (!inputstore.getData(name).length) {
        placeholder = '
File cannot be found.
';
        if (name === 'zone') {
            tool = 'zone-helper';
        } else if (name === 'district') {
            tool = 'district-helper';
        } else {
            tool = 'data-helper';
        }

        placeholder += `<div>You can create the file using the <a href="/tools/${tool}">${tool}</a> tool.</div>`;
    } else {
        placeholder = '
No matching records found.
';
    }

    $(`#${parent}`).append(`<div id="${name}-table"></div>`);
    currentTable = new Tabulator(`#${name}-table`, {
        index: 'Name',
        data: values,
        columns: defineColumns(columns, types),
        placeholder: placeholder,

        layout: (['occupancy','architecture'].includes(name)) ? 'fitDataFill' : 'fitColumns',
        height: '300px',
        cellClick:selectRow,
        cellEdited: updateData,
        rowSelectionChanged: addToSelection

    });

    $('#select-all-button').prop('disabled', !values.length);
    $('#filter-button').prop('disabled', !values.length);
}

function defineColumns(columns, column_types) {
    var out = [];
    var editor = '';
    $.each(columns, function (index, column) {
        if (column === 'Name' || column === 'REFERENCE') {
            out.push({title: column, field: column});
        } else {
            if (column_types[column] === 'str') {
                editor = "input";
            } else  {
                editor = "number";
            }
            out.push({title: column, field: column, editor: editor});
        }
    });
    return out
}

function updateData(data) {
    var name = data.getData()['Name'];
    var column = data.getField();
    var value = data.getValue();

    // Find the layer of the property
    var table = $('.tab.active').data('name');

    // TODO: Move updates to InputStore class
    // FIXME: Not very efficient. Too many loops to find index of data
    // Update table data
    var row = currentTable.getRow(name).getData();
    row[column] = value;
    if (row['REFERENCE']) {
        row['REFERENCE'] = 'User - assumption';
        $('.tab.active').trigger('click');
    }

    // Update geometries
    if (table === 'zone' || table === 'district') {
        var properties = inputstore.getGeojson(table)['features'][inputstore.getGeojsonID(table, name)]['properties'];
        properties[column] = value;
        if (properties['REFERENCE']) {
            properties['REFERENCE'] = 'User - assumption';
        }
        if (column === 'height_ag') {
            inputstore.createNewGeojson(table);
            redrawBuildings();
        }
    }

    inputstore.addChange('update', table, data.getData()['Name'], data.getField(), data.getValue());

}

function selectRow(e, cell) {
    var value = cell.getValue();
    if(cell.getField() === 'Name') {
        if (cell.getRow().isSelected()) {
            currentTable.deselectRow(value);
        } else {
            currentTable.selectRow(value);
        }
    }
}

function addToSelection(data, row) {
    var buttons = [$('#clear-button'),$('#delete-button'),$('#edit-button')];
    $.each(buttons, function (_, button) {
        if (data.length) {
            button.show();
        } else {
            button.hide();
        }
    });

    var out = [];
    $.each(data, function (_, building) {
        out.push(building['Name']);
    });

    inputstore.setSelected(out);
    if (currentTable) {
        var scroll = $('.tabulator-tableHolder').scrollTop();
        filterSelection(out);
        $('.tabulator-tableHolder').scrollTop(scroll);
    }

    redrawBuildings();
}

function filterSelection(selection) {
    if ($('#filter-button').hasClass('btn-success')){
        currentTable.setFilter("Name", "in", selection);
    } else {
        currentTable.clearFilter();
    }
}

$(window).load(function () {
    $('#cea-inputs').show();

    $('#zone-tab').trigger('click');

    $('#select-all-button').click(function () {
        currentTable.selectRow();
    }).show();

    $('#filter-button').click(function () {
        $(this).toggleClass('btn-success');
        filterSelection(inputstore.getSelected());
    });

    $('#edit-button').click(function () {
        var table = $('.tab.active').data('name');
        var columns = inputstore.getColumns(table);

        $('#cea-column-editor .modal-title').text(`Editing ${table} table`);
        $('#selected-buildings').text(`Buidlings selected:`);
        $.each(inputstore.getSelected(), function (_, building) {
            var row = currentTable.getRow(building).getData();
            var out = {};
            $.each(inputstore.getColumns(table), function (_, column) {
                out[column] = row[column];
            });
            delete out['Name'];
            delete out['REFERENCE'];
            out = JSON.stringify(out);
            $('#selected-buildings').append(`<div>${building}: ${out}</div>`);
        });

        // TODO: Add input validation
        $('#cea-column-editor-form').empty();
        $.each(columns, function (_, column) {
            var type = (inputstore.getColumnTypes(table)[column] === 'str') ? 'text':'number';
            if (column !== 'Name' && column !== 'REFERENCE') {
                var input =
                    `

                      ${ column }

                        <input type="${ type }" id="cea-input-${ column }" name="${ column }" placeholder="unchanged"
                               class="form-control col-md-7 col-xs-12">

`;
                $('#cea-column-editor-form').append(input);
                if (type === 'text') {
                    $(`#cea-input-${ column }`).prop('pattern', '[T][0-9]+')
                        .prop('title', 'T[number]');
                }
            }
        });

        $('#cea-column-editor').modal({'show': true, 'backdrop': 'static'});
    });

    $('#delete-button').click(function () {
        var selected = inputstore.getSelected();
        var layer = ($('.tab.active').data('name') !== 'district') ? 'zone':'district';
        var out = '\n';
        $.each(selected, function (_, building) {
            out += `${building}\n`
        });
        if (confirm("This will delete the following buildings from every table:" + out)) {
            inputstore.deleteBuildings(layer, selected);
            $('.tab.active').trigger('click');
        }
    });

    $('#clear-button').click(function () {
        currentTable.deselectRow();
        filterSelection(inputstore.getSelected());
    });

    $('#discard-button').click(function () {
        var changes = inputstore.changes;
        if (!Object.keys(changes['update']).length && !Object.keys(changes['delete']).length) {
            alert('No changes detected');
        } else {
            if (confirm("This will discard all unsaved changes.\n" + inputstore.changesToString())) {
                inputstore.resetChanges();
                $('.tab.active').trigger('click');
            }
        }
    });

    $('#save-button').click(function () {
        var changes = inputstore.changes;
        if (!Object.keys(changes['update']).length && !Object.keys(changes['delete']).length) {
            alert('No changes detected');
        } else {
            if (confirm("Save these changes?\n" +
                "WARNING: Any buildings deleted this way cannot be recovered once saved!\n" +
                inputstore.changesToString())) {
                $('#saving-text').text('Saving Changes...');
                $('#saving-popup').modal({'show': true, 'backdrop': 'static'});

                $.ajax({
                    type: 'POST',
                    url: '/inputs/building-properties',
                    data: JSON.stringify({
                        changes: changes,
                        geojson: inputstore.geojsondata,
                        tables: inputstore.data
                    }),
                    contentType: 'application/json'
                }).done(function (data) {
                    // TODO: Either refresh page or do applyChanges()
                    inputstore.applyChanges(data);

                    $('#saving-text').text('✔ Changes Saved!');
                    setTimeout(function(){
                        $('#saving-popup').modal('hide');
                    }, 1500);
                }).fail(function () {
                    var header =
                        '<button type="button" class="close cea-modal-close" data-dismiss="modal">' +
                        'Back' +
                        '</button>';
                    $('#saving-text').text('Something went wrong')
                        .append(header);
                });
            }
        }
    });

    $('#cea-column-editor-form').submit(function (e) {
        e.preventDefault();
        var table = $('.tab.active').data('name');
        var props = {};
        var form = $('#cea-column-editor-form').serialize().split('&');
        $.each(form, function (_, prop) {
            var temp = prop.split('=');
            if (temp[1] !== '') {
                var value = temp[1];
                if (inputstore.getColumnTypes(table)[temp[0]] !== 'str'){
                    value = Number(value);
                }
                props[temp[0]] = value;
            }
        });

        var data = [];
        $.each(inputstore.getSelected(), function (_, building) {
            // Add to changes
            $.each(props, function (key, value) {
                inputstore.addChange('update', table, building, key, value);
            });

            // Update table data
            var out = {Name: building, ...props};
            var row = currentTable.getRow(building).getData();
            if (row['REFERENCE']) {
                out['REFERENCE'] = 'User - assumption';
            }
            data.push(out);

            // Update geojsons
            // FIXME: Copied from updateData()
            if (table === 'zone' || table === 'district') {
                Object.assign(inputstore.getGeojson(table)['features'][inputstore.getGeojsonID(table, building)]['properties'], out);
            }
        });

        if (props['height_ag']) {
            inputstore.createNewGeojson(table);
            redrawBuildings();
        }

        currentTable.updateData(data);

        $('#cea-column-editor').modal('hide');
    });
});
<commit_msg>Add tooltips to table header<commit_after>var currentTable;
var tempSelection;

// Add onclick function to tabs
$("[id$=-tab]").each(function () {
    $(this).click(function(){
        var oldtable = $('.tab.active').data('name') || 'zone';
        $('.tab').removeClass("active");
        $(this).addClass("active");

        // Remember scroll position
        var scroll = $('.tabulator-tableHolder').scrollTop();

        // Store previously selected buildings before creating new table
        if (inputstore.getData(oldtable).length) {
            if (inputstore.getSelected()) {
                tempSelection = [...inputstore.getSelected()];
            }
        }

        var name = $(this).data('name');
        $('#property-table').empty();
        createTable("property-table",`${name}`,
            inputstore.getData(name),
            inputstore.getColumns(name),
            inputstore.getColumnTypes(name)
        );

        // Restore previous selected
        inputstore.setSelected(tempSelection);
        if (inputstore.getSelected()) {
            currentTable.selectRow(inputstore.getSelected());
        }
        filterSelection(inputstore.getSelected());

        currentTable.redraw();
        $('.tabulator-tableHolder').scrollTop(scroll);

    });
});

function createTable(parent, name, values, columns, types) {
    var placeholder = '';
    var tool = '';
    if (!inputstore.getData(name).length) {
        placeholder = '
File cannot be found.
';
        if (name === 'zone') {
            tool = 'zone-helper';
        } else if (name === 'district') {
            tool = 'district-helper';
        } else {
            tool = 'data-helper';
        }

        placeholder += `<div>You can create the file using the <a href="/tools/${tool}">${tool}</a> tool.</div>`;
    } else {
        placeholder = '
No matching records found.
';
    }

    $(`#${parent}`).append(`<div id="${name}-table"></div>`);
    currentTable = new Tabulator(`#${name}-table`, {
        index: 'Name',
        data: values,
        columns: defineColumns(columns, types),
        placeholder: placeholder,
        tooltipsHeader:true,

        layout: (['occupancy','architecture'].includes(name)) ? 'fitDataFill' : 'fitColumns',
        height: '300px',
        cellClick:selectRow,
        cellEdited: updateData,
        rowSelectionChanged: addToSelection,
        tooltips: createTooltip
    });

    $('#select-all-button').prop('disabled', !values.length);
    $('#filter-button').prop('disabled', !values.length);
}

function defineColumns(columns, column_types) {
    var out = [];
    var editor = '';
    $.each(columns, function (index, column) {
        if (column === 'Name' || column === 'REFERENCE') {
            out.push({title: column, field: column});
        } else {
            if (column_types[column] === 'str') {
                editor = "input";
            } else  {
                editor = "number";
            }
            out.push({title: column, field: column, editor: editor});
        }
    });
    return out
}

function updateData(data) {
    var name = data.getData()['Name'];
    var column = data.getField();
    var value = data.getValue();

    // Find the layer of the property
    var table = $('.tab.active').data('name');

    // TODO: Move updates to InputStore class
    // FIXME: Not very efficient. Too many loops to find index of data
    // Update table data
    var row = currentTable.getRow(name).getData();
    row[column] = value;
    if (row['REFERENCE']) {
        row['REFERENCE'] = 'User - assumption';
        $('.tab.active').trigger('click');
    }

    // Update geometries
    if (table === 'zone' || table === 'district') {
        var properties = inputstore.getGeojson(table)['features'][inputstore.getGeojsonID(table, name)]['properties'];
        properties[column] = value;
        if (properties['REFERENCE']) {
            properties['REFERENCE'] = 'User - assumption';
        }
        if (column === 'height_ag') {
            inputstore.createNewGeojson(table);
            redrawBuildings();
        }
    }

    inputstore.addChange('update', table, data.getData()['Name'], data.getField(), data.getValue());

}

function selectRow(e, cell) {
    var value = cell.getValue();
    if(cell.getField() === 'Name') {
        if (cell.getRow().isSelected()) {
            currentTable.deselectRow(value);
        } else {
            currentTable.selectRow(value);
        }
    }
}

function addToSelection(data, row) {
    var buttons = [$('#clear-button'),$('#delete-button'),$('#edit-button')];
    $.each(buttons, function (_, button) {
        if (data.length) {
            button.show();
        } else {
            button.hide();
        }
    });

    var out = [];
    $.each(data, function (_, building) {
        out.push(building['Name']);
    });

    inputstore.setSelected(out);
    if (currentTable) {
        var scroll = $('.tabulator-tableHolder').scrollTop();
        filterSelection(out);
        $('.tabulator-tableHolder').scrollTop(scroll);
    }

    redrawBuildings();
}

function filterSelection(selection) {
    if ($('#filter-button').hasClass('btn-success')){
        currentTable.setFilter("Name", "in", selection);
    } else {
        currentTable.clearFilter();
    }
}

function createTooltip(column) {

}

$(window).load(function () {
    $('#cea-inputs').show();

    $('#zone-tab').trigger('click');

    $('#select-all-button').click(function () {
        currentTable.selectRow();
    }).show();

    $('#filter-button').click(function () {
        $(this).toggleClass('btn-success');
        filterSelection(inputstore.getSelected());
    });

    $('#edit-button').click(function () {
        var table = $('.tab.active').data('name');
        var columns = inputstore.getColumns(table);

        $('#cea-column-editor .modal-title').text(`Editing ${table} table`);
        $('#selected-buildings').text(`Buidlings selected:`);
        $.each(inputstore.getSelected(), function (_, building) {
            var row = currentTable.getRow(building).getData();
            var out = {};
            $.each(inputstore.getColumns(table), function (_, column) {
                out[column] = row[column];
            });
            delete out['Name'];
            delete out['REFERENCE'];
            out = JSON.stringify(out);
            $('#selected-buildings').append(`<div>${building}: ${out}</div>`);
        });

        // TODO: Add input validation
        $('#cea-column-editor-form').empty();
        $.each(columns, function (_, column) {
            var type = (inputstore.getColumnTypes(table)[column] === 'str') ? 'text':'number';
            if (column !== 'Name' && column !== 'REFERENCE') {
                var input =
                    `

                      ${ column }

                        <input type="${ type }" id="cea-input-${ column }" name="${ column }" placeholder="unchanged"
                               class="form-control col-md-7 col-xs-12">

`;
                $('#cea-column-editor-form').append(input);
                if (type === 'text') {
                    $(`#cea-input-${ column }`).prop('pattern', '[T][0-9]+')
                        .prop('title', 'T[number]');
                }
            }
        });

        $('#cea-column-editor').modal({'show': true, 'backdrop': 'static'});
    });

    $('#delete-button').click(function () {
        var selected = inputstore.getSelected();
        var layer = ($('.tab.active').data('name') !== 'district') ? 'zone':'district';
        var out = '\n';
        $.each(selected, function (_, building) {
            out += `${building}\n`
        });
        if (confirm("This will delete the following buildings from every table:" + out)) {
            inputstore.deleteBuildings(layer, selected);
            $('.tab.active').trigger('click');
        }
    });

    $('#clear-button').click(function () {
        currentTable.deselectRow();
        filterSelection(inputstore.getSelected());
    });

    $('#discard-button').click(function () {
        var changes = inputstore.changes;
        if (!Object.keys(changes['update']).length && !Object.keys(changes['delete']).length) {
            alert('No changes detected');
        } else {
            if (confirm("This will discard all unsaved changes.\n" + inputstore.changesToString())) {
                inputstore.resetChanges();
                $('.tab.active').trigger('click');
            }
        }
    });

    $('#save-button').click(function () {
        var changes = inputstore.changes;
        if (!Object.keys(changes['update']).length && !Object.keys(changes['delete']).length) {
            alert('No changes detected');
        } else {
            if (confirm("Save these changes?\n" +
                "WARNING: Any buildings deleted this way cannot be recovered once saved!\n" +
                inputstore.changesToString())) {
                $('#saving-text').text('Saving Changes...');
                $('#saving-popup').modal({'show': true, 'backdrop': 'static'});

                $.ajax({
                    type: 'POST',
                    url: '/inputs/building-properties',
                    data: JSON.stringify({
                        changes: changes,
                        geojson: inputstore.geojsondata,
                        tables: inputstore.data
                    }),
                    contentType: 'application/json'
                }).done(function (data) {
                    // TODO: Either refresh page or do applyChanges()
                    inputstore.applyChanges(data);

                    $('#saving-text').text('✔ Changes Saved!');
                    setTimeout(function(){
                        $('#saving-popup').modal('hide');
                    }, 1500);
                }).fail(function () {
                    var header =
                        '<button type="button" class="close cea-modal-close" data-dismiss="modal">' +
                        'Back' +
                        '</button>';
                    $('#saving-text').text('Something went wrong')
                        .append(header);
                });
            }
        }
    });

    $('#cea-column-editor-form').submit(function (e) {
        e.preventDefault();
        var table = $('.tab.active').data('name');
        var props = {};
        var form = $('#cea-column-editor-form').serialize().split('&');
        $.each(form, function (_, prop) {
            var temp = prop.split('=');
            if (temp[1] !== '') {
                var value = temp[1];
                if (inputstore.getColumnTypes(table)[temp[0]] !== 'str'){
                    value = Number(value);
                }
                props[temp[0]] = value;
            }
        });

        var data = [];
        $.each(inputstore.getSelected(), function (_, building) {
            // Add to changes
            $.each(props, function (key, value) {
                inputstore.addChange('update', table, building, key, value);
            });

            // Update table data
            var out = {Name: building, ...props};
            var row = currentTable.getRow(building).getData();
            if (row['REFERENCE']) {
                out['REFERENCE'] = 'User - assumption';
            }
            data.push(out);

            // Update geojsons
            // FIXME: Copied from updateData()
            if (table === 'zone' || table === 'district') {
                Object.assign(inputstore.getGeojson(table)['features'][inputstore.getGeojsonID(table, building)]['properties'], out);
            }
        });

        if (props['height_ag']) {
            inputstore.createNewGeojson(table);
            redrawBuildings();
        }

        currentTable.updateData(data);

        $('#cea-column-editor').modal('hide');
    });
});
<|endoftext|>