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

import fs from 'fs-extra'
import path from 'path'
import { spawnSync } from 'child_process'

import { Command, Option } from 'commander/esm.mjs'

const program = new Command()

const defaultImageTag = '20220305-21e8e49'

function processCommonOptions() {
  const options = program.opts()

  let sourceDir = '.'
  if (options.sourceDir) {
    sourceDir = options.sourceDir
  }

  // Check that the source directory exists and chdir to it.
  if (!fs.existsSync(sourceDir)) {
    console.error('The source directory: ' + sourceDir + ' does not exist!')
    process.exit(1)
  }
  process.chdir(sourceDir)

  let buildDir = 'web-build'
  if (options.buildDir) {
    buildDir = options.buildDir
  }

  // Make the build directory to hold the dockcross script and the CMake
  // build.
  try {
    fs.mkdirSync(buildDir)
  } catch (err) {
    if (err.code !== 'EEXIST') throw err
  }

  // Check that we have docker and can run it.
  const dockerVersion = spawnSync('docker', ['--version'], {
    env: process.env,
    stdio: [ 'ignore', 'ignore', 'ignore' ]
  })
  if (dockerVersion.status !== 0) {
    console.error("Could not run the 'docker' command.")
    console.error('This tool requires Docker.')
    console.error('')
    console.error('Please find installation instructions at:')
    console.error('')
    console.error('  https://docs.docker.com/install/')
    console.error('')
    process.exit(dockerVersion.status)
  }

  let dockerImage = `itkwasm/emscripten:${defaultImageTag}`
  if (options.image) {
    dockerImage = options.image
  }

  // Ensure we have the 'dockcross' Docker build environment driver script
  const dockcrossScript = path.join(buildDir, 'itk-wasm-build-env')
  try {
    fs.statSync(dockcrossScript)
  } catch (err) {
    if (err.code === 'ENOENT') {
      const output = fs.openSync(dockcrossScript, 'w')
      const dockerCall = spawnSync('docker', ['run', '--rm', dockerImage], {
        env: process.env,
        stdio: [ 'ignore', output, null ]
      })
      if (dockerCall.status !== 0) {
        console.error(dockerCall.stderr.toString())
        process.exit(dockerCall.status)
      }
      fs.closeSync(output)
      fs.chmodSync(dockcrossScript, '755')
    } else {
      throw err
    }
  }

  return { dockerImage, dockcrossScript, buildDir }
}

function build(options) {
  const { buildDir, dockcrossScript } = processCommonOptions()

  const hyphenIndex = program.rawArgs.findIndex((arg) => arg === '--')
  let cmakeArgs = []
  if (hyphenIndex !== -1) {
    cmakeArgs = program.rawArgs.slice(hyphenIndex + 1)
  }
  if(process.platform === "win32"){
    var dockerBuild = spawnSync('"C:\\Program Files\\Git\\bin\\sh.exe"',
      ["--login", "-i", "-c", `"${buildDir}/itk-wasm-build-env web-build ${buildDir} ` + cmakeArgs + '"'], {
      env: process.env,
      stdio: 'inherit',
      shell: true
    });

    if (dockerBuild.status !== 0) {
      console.error(dockerBuild.error);
    }
    process.exit(dockerBuild.status);
  } else {
    const dockerBuild = spawnSync('bash', [dockcrossScript, 'web-build', buildDir].concat(cmakeArgs), {
      env: process.env,
      stdio: 'inherit'
    })
    if (dockerBuild.status !== 0) {
      console.error(dockerBuild.error);
    }
    process.exit(dockerBuild.status)
  }
}

function test(options) {
  const { buildDir, dockcrossScript } = processCommonOptions()

  const hyphenIndex = program.rawArgs.findIndex((arg) => arg === '--')
  let ctestArgs = []
  if (hyphenIndex !== -1) {
    ctestArgs = program.rawArgs.slice(hyphenIndex + 1)
  }
  if(process.platform === "win32"){
    var dockerBuild = spawnSync('"C:\\Program Files\\Git\\bin\\sh.exe"',
      ["--login", "-i", "-c", `"${buildDir}/itk-wasm-build-env ctest --test-dir ${buildDir} ` + ctestArgs + '"'], {
      env: process.env,
      stdio: 'inherit',
      shell: true
    });

    if (dockerBuild.status !== 0) {
      console.error(dockerBuild.error);
    }
    process.exit(dockerBuild.status);
  } else {
    const dockerBuild = spawnSync('bash', [dockcrossScript, 'ctest', '--test-dir', buildDir].concat(ctestArgs), {
      env: process.env,
      stdio: 'inherit'
    })
    if (dockerBuild.status !== 0) {
      console.error(dockerBuild.error);
    }
    process.exit(dockerBuild.status)
  }
}

function run(wasmBinary, options) {
  const { buildDir, dockcrossScript } = processCommonOptions()

  const hyphenIndex = program.rawArgs.findIndex((arg) => arg === '--')
  let wasmBinaryArgs = []
  if (hyphenIndex !== -1) {
    wasmBinaryArgs = program.rawArgs.slice(hyphenIndex + 1)
  }
  const wasmBinaryRelativePath = `${buildDir}/${wasmBinary}`

  let wasmRuntime = 'wasmtime'
  if (options.runtime) {
    wasmRuntime = options.runtime
  }
  let wasmRuntimeArgs = []
  switch (wasmRuntime) {
  case 'wasmtime':
    wasmRuntimeArgs = ['--args', '-e WASMTIME_BACKTRACE_DETAILS=1', '/wasi-runtimes/wasmtime/bin/wasmtime-pwd.sh',]
    break
  case 'wasmer':
    wasmRuntimeArgs = ['sudo', '/wasi-runtimes/wasmer/bin/wasmer-pwd.sh',]
    break
  case 'wasm3':
    wasmRuntimeArgs = ['/wasi-runtimes/wasm3/bin/wasm3',]
    break
  case 'wavm':
    wasmRuntimeArgs = ['/wasi-runtimes/wavm/bin/wavm', 'run']
    break
  default:
    throw Error('unexpected wasm runtime')
  }

  if(process.platform === "win32"){
    var dockerRun = spawnSync('"C:\\Program Files\\Git\\bin\\sh.exe"',
      ["--login", "-i", "-c", `"${buildDir}/itk-wasm-build-env ` + wasmRuntimeArgs + wasmBinaryRelativePath + wasmBinaryArgs + '"'], {
      env: process.env,
      stdio: 'inherit',
      shell: true
    });

    if (dockerRun.status !== 0) {
      console.error(dockerRun.error);
    }
    process.exit(dockerRun.status);
  } else {
    const dockerRun = spawnSync('bash', [dockcrossScript,].concat(wasmRuntimeArgs).concat(wasmBinaryRelativePath).concat(wasmBinaryArgs), {
      env: process.env,
      stdio: 'inherit'
    })
    if (dockerRun.status !== 0) {
      console.error(dockerRun.error);
    }
    process.exit(dockerRun.status)
  }
}

program
  .option('-i, --image <image>', 'build environment Docker image, defaults to itkwasm/emscripten')
  .option('-s, --source-dir <source-directory>', 'path to build directory, defaults to "."')
  .option('-b, --build-dir <build-directory>', 'build directory whose path is relative to the source directory, defaults to "web-build"')
program
  .command('build')
  .usage('[-- <cmake arguments>]')
  .description('build the CMake project found in the source directory')
  .action(build)
program
  .command('test')
  .usage('[-- <ctest arguments>]')
  .description('Run the tests for the CMake project found in the build directory')
  .action(test)
program
  .command('run <wasmBinary>')
  .addOption(new Option('-r, --runtime <wasm-runtime>', 'wasm runtime to use for execution, defaults to "wasmtime"').choices(['wasmtime', 'wasmer', 'wasm3', 'wavm']))
  .option('-r, --runtime <wasm-runtime>', 'wasm runtime to use for execution, defaults to "wasmtime"')
  .usage('[options] <wasmBinary> [-- <wasm binary arguments>]')
  .description('run the wasm binary, whose path is specified relative to the build directory')
  .action(run)

program
  .parse(process.argv)

program.help()
<commit_msg>feat(itk-wasm-cli): Update default Docker image for 1.0.0-b.2<commit_after>#!/usr/bin/env node

import fs from 'fs-extra'
import path from 'path'
import { spawnSync } from 'child_process'

import { Command, Option } from 'commander/esm.mjs'

const program = new Command()

const defaultImageTag = '20220308-ddaa7d56'

function processCommonOptions() {
  const options = program.opts()

  let sourceDir = '.'
  if (options.sourceDir) {
    sourceDir = options.sourceDir
  }

  // Check that the source directory exists and chdir to it.
  if (!fs.existsSync(sourceDir)) {
    console.error('The source directory: ' + sourceDir + ' does not exist!')
    process.exit(1)
  }
  process.chdir(sourceDir)

  let buildDir = 'web-build'
  if (options.buildDir) {
    buildDir = options.buildDir
  }

  // Make the build directory to hold the dockcross script and the CMake
  // build.
  try {
    fs.mkdirSync(buildDir)
  } catch (err) {
    if (err.code !== 'EEXIST') throw err
  }

  // Check that we have docker and can run it.
  const dockerVersion = spawnSync('docker', ['--version'], {
    env: process.env,
    stdio: [ 'ignore', 'ignore', 'ignore' ]
  })
  if (dockerVersion.status !== 0) {
    console.error("Could not run the 'docker' command.")
    console.error('This tool requires Docker.')
    console.error('')
    console.error('Please find installation instructions at:')
    console.error('')
    console.error('  https://docs.docker.com/install/')
    console.error('')
    process.exit(dockerVersion.status)
  }

  let dockerImage = `itkwasm/emscripten:${defaultImageTag}`
  if (options.image) {
    dockerImage = options.image
  }

  // Ensure we have the 'dockcross' Docker build environment driver script
  const dockcrossScript = path.join(buildDir, 'itk-wasm-build-env')
  try {
    fs.statSync(dockcrossScript)
  } catch (err) {
    if (err.code === 'ENOENT') {
      const output = fs.openSync(dockcrossScript, 'w')
      const dockerCall = spawnSync('docker', ['run', '--rm', dockerImage], {
        env: process.env,
        stdio: [ 'ignore', output, null ]
      })
      if (dockerCall.status !== 0) {
        console.error(dockerCall.stderr.toString())
        process.exit(dockerCall.status)
      }
      fs.closeSync(output)
      fs.chmodSync(dockcrossScript, '755')
    } else {
      throw err
    }
  }

  return { dockerImage, dockcrossScript, buildDir }
}

function build(options) {
  const { buildDir, dockcrossScript } = processCommonOptions()

  const hyphenIndex = program.rawArgs.findIndex((arg) => arg === '--')
  let cmakeArgs = []
  if (hyphenIndex !== -1) {
    cmakeArgs = program.rawArgs.slice(hyphenIndex + 1)
  }
  if(process.platform === "win32"){
    var dockerBuild = spawnSync('"C:\\Program Files\\Git\\bin\\sh.exe"',
      ["--login", "-i", "-c", `"${buildDir}/itk-wasm-build-env web-build ${buildDir} ` + cmakeArgs + '"'], {
      env: process.env,
      stdio: 'inherit',
      shell: true
    });

    if (dockerBuild.status !== 0) {
      console.error(dockerBuild.error);
    }
    process.exit(dockerBuild.status);
  } else {
    const dockerBuild = spawnSync('bash', [dockcrossScript, 'web-build', buildDir].concat(cmakeArgs), {
      env: process.env,
      stdio: 'inherit'
    })
    if (dockerBuild.status !== 0) {
      console.error(dockerBuild.error);
    }
    process.exit(dockerBuild.status)
  }
}

function test(options) {
  const { buildDir, dockcrossScript } = processCommonOptions()

  const hyphenIndex = program.rawArgs.findIndex((arg) => arg === '--')
  let ctestArgs = []
  if (hyphenIndex !== -1) {
    ctestArgs = program.rawArgs.slice(hyphenIndex + 1)
  }
  if(process.platform === "win32"){
    var dockerBuild = spawnSync('"C:\\Program Files\\Git\\bin\\sh.exe"',
      ["--login", "-i", "-c", `"${buildDir}/itk-wasm-build-env ctest --test-dir ${buildDir} ` + ctestArgs + '"'], {
      env: process.env,
      stdio: 'inherit',
      shell: true
    });

    if (dockerBuild.status !== 0) {
      console.error(dockerBuild.error);
    }
    process.exit(dockerBuild.status);
  } else {
    const dockerBuild = spawnSync('bash', [dockcrossScript, 'ctest', '--test-dir', buildDir].concat(ctestArgs), {
      env: process.env,
      stdio: 'inherit'
    })
    if (dockerBuild.status !== 0) {
      console.error(dockerBuild.error);
    }
    process.exit(dockerBuild.status)
  }
}

function run(wasmBinary, options) {
  const { buildDir, dockcrossScript } = processCommonOptions()

  const hyphenIndex = program.rawArgs.findIndex((arg) => arg === '--')
  let wasmBinaryArgs = []
  if (hyphenIndex !== -1) {
    wasmBinaryArgs = program.rawArgs.slice(hyphenIndex + 1)
  }
  const wasmBinaryRelativePath = `${buildDir}/${wasmBinary}`

  let wasmRuntime = 'wasmtime'
  if (options.runtime) {
    wasmRuntime = options.runtime
  }
  let wasmRuntimeArgs = []
  switch (wasmRuntime) {
  case 'wasmtime':
    wasmRuntimeArgs = ['--args', '-e WASMTIME_BACKTRACE_DETAILS=1', '/wasi-runtimes/wasmtime/bin/wasmtime-pwd.sh',]
    break
  case 'wasmer':
    wasmRuntimeArgs = ['sudo', '/wasi-runtimes/wasmer/bin/wasmer-pwd.sh',]
    break
  case 'wasm3':
    wasmRuntimeArgs = ['/wasi-runtimes/wasm3/bin/wasm3',]
    break
  case 'wavm':
    wasmRuntimeArgs = ['/wasi-runtimes/wavm/bin/wavm', 'run']
    break
  default:
    throw Error('unexpected wasm runtime')
  }

  if(process.platform === "win32"){
    var dockerRun = spawnSync('"C:\\Program Files\\Git\\bin\\sh.exe"',
      ["--login", "-i", "-c", `"${buildDir}/itk-wasm-build-env ` + wasmRuntimeArgs + wasmBinaryRelativePath + wasmBinaryArgs + '"'], {
      env: process.env,
      stdio: 'inherit',
      shell: true
    });

    if (dockerRun.status !== 0) {
      console.error(dockerRun.error);
    }
    process.exit(dockerRun.status);
  } else {
    const dockerRun = spawnSync('bash', [dockcrossScript,].concat(wasmRuntimeArgs).concat(wasmBinaryRelativePath).concat(wasmBinaryArgs), {
      env: process.env,
      stdio: 'inherit'
    })
    if (dockerRun.status !== 0) {
      console.error(dockerRun.error);
    }
    process.exit(dockerRun.status)
  }
}

program
  .option('-i, --image <image>', 'build environment Docker image, defaults to itkwasm/emscripten')
  .option('-s, --source-dir <source-directory>', 'path to build directory, defaults to "."')
  .option('-b, --build-dir <build-directory>', 'build directory whose path is relative to the source directory, defaults to "web-build"')
program
  .command('build')
  .usage('[-- <cmake arguments>]')
  .description('build the CMake project found in the source directory')
  .action(build)
program
  .command('test')
  .usage('[-- <ctest arguments>]')
  .description('Run the tests for the CMake project found in the build directory')
  .action(test)
program
  .command('run <wasmBinary>')
  .addOption(new Option('-r, --runtime <wasm-runtime>', 'wasm runtime to use for execution, defaults to "wasmtime"').choices(['wasmtime', 'wasmer', 'wasm3', 'wavm']))
  .option('-r, --runtime <wasm-runtime>', 'wasm runtime to use for execution, defaults to "wasmtime"')
  .usage('[options] <wasmBinary> [-- <wasm binary arguments>]')
  .description('run the wasm binary, whose path is specified relative to the build directory')
  .action(run)

program
  .parse(process.argv)

program.help()
<|endoftext|>


<commit_before>/// @file JSRoot.openui5.js
/// Bootstraping of OpenUI5 functionality in JSROOT
/// Openui5 loaded directly in the script

JSROOT.define(['jquery', 'jquery-ui'], () => {

   "use strict";

   let resolveFunc;

   JSROOT.completeUI5Loading = function() {
      // when running with THttpServer, automatically set "rootui5" folder
      let rootui5sys = undefined;
      if (JSROOT.source_dir.indexOf("jsrootsys") >= 0)
         rootui5sys = JSROOT.source_dir.replace(/jsrootsys/g, "rootui5sys");

      sap.ui.loader.config({
         paths: {
            jsroot: JSROOT.source_dir,
            rootui5: rootui5sys
         }
      });

      if (resolveFunc) {
         resolveFunc(sap);
         resolveFunc = null;
      }
   }

   function TryOpenOpenUI(sources) {

      // where to take openui5 sources
      let src = sources.shift();

      if ((src.indexOf("roothandler")==0) && (src.indexOf("://")<0)) src = src.replace(/\:\//g,"://");

      let element = document.createElement("script");
      element.setAttribute('type', "text/javascript");
      element.setAttribute('id', "sap-ui-bootstrap");
      // use nojQuery while we are already load jquery and jquery-ui, later one can use directly sap-ui-core.js

      // this is location of openui5 scripts when working with THttpServer or when scripts are installed inside JSROOT
      element.setAttribute('src', src + "resources/sap-ui-core-nojQuery.js"); // latest openui5 version

      element.setAttribute('data-sap-ui-libs', JSROOT.openui5libs || "sap.m, sap.ui.layout, sap.ui.unified, sap.ui.commons");

      element.setAttribute('data-sap-ui-theme', JSROOT.openui5theme || 'sap_belize');
      element.setAttribute('data-sap-ui-compatVersion', 'edge');
      // element.setAttribute('data-sap-ui-bindingSyntax', 'complex');

      element.setAttribute('data-sap-ui-preload', 'async'); // '' to disable Component-preload.js

      element.setAttribute('data-sap-ui-evt-oninit', "JSROOT.completeUI5Loading()");

      element.onerror = function() {
         // remove failed element
         element.parentNode.removeChild(element);
         // and try next
         TryOpenOpenUI(sources);
      }

      element.onload = function() {
         console.log('Load openui5 from ' + src);
      }

      document.getElementsByTagName("head")[0].appendChild(element);
   }

   let openui5_sources = [],
       openui5_dflt = "https://openui5.hana.ondemand.com/1.72.0/",
       openui5_root = JSROOT.source_dir.replace(/jsrootsys/g, "rootui5sys/distribution");

   if (openui5_root == JSROOT.source_dir) openui5_root = "";

   if (typeof JSROOT.openui5src == 'string') {
      switch (JSROOT.openui5src) {
         case "nodefault": openui5_dflt = ""; break;
         case "default": openui5_sources.push(openui5_dflt); openui5_dflt = ""; break;
         case "nojsroot": openui5_root = ""; break;
         case "jsroot": openui5_sources.push(openui5_root); openui5_root = ""; break;
         default: openui5_sources.push(JSROOT.openui5src); break;
      }
   }

   if (openui5_root && (openui5_sources.indexOf(openui5_root)<0)) openui5_sources.push(openui5_root);
   if (openui5_dflt && (openui5_sources.indexOf(openui5_dflt)<0)) openui5_sources.push(openui5_dflt);

   TryOpenOpenUI(openui5_sources);

   // return Promise let loader wait before dependent source will be invoked
   return new Promise(resolve => { resolveFunc = resolve; });

});

<commit_msg>Let use already loaded openui5<commit_after>/// @file JSRoot.openui5.js
/// Bootstraping of OpenUI5 functionality in JSROOT
/// Openui5 loaded directly in the script

JSROOT.define(['jquery', 'jquery-ui'], () => {

   "use strict";

   // very simple - openui5 was loaded before and will be used as is
   if (typeof sap == 'object')
      return sap;

   let resolveFunc;

   JSROOT._.completeUI5Loading = function() {
      // when running with THttpServer, automatically set "rootui5" folder
      let rootui5sys = undefined;
      if (JSROOT.source_dir.indexOf("jsrootsys") >= 0)
         rootui5sys = JSROOT.source_dir.replace(/jsrootsys/g, "rootui5sys");

      sap.ui.loader.config({
         paths: {
            jsroot: JSROOT.source_dir,
            rootui5: rootui5sys
         }
      });

      if (resolveFunc) {
         resolveFunc(sap);
         resolveFunc = null;
      }
   }

   function TryOpenOpenUI(sources) {

      // where to take openui5 sources
      let src = sources.shift();

      if ((src.indexOf("roothandler")==0) && (src.indexOf("://")<0)) src = src.replace(/\:\//g,"://");

      let element = document.createElement("script");
      element.setAttribute('type', "text/javascript");
      element.setAttribute('id', "sap-ui-bootstrap");
      // use nojQuery while we are already load jquery and jquery-ui, later one can use directly sap-ui-core.js

      // this is location of openui5 scripts when working with THttpServer or when scripts are installed inside JSROOT
      element.setAttribute('src', src + "resources/sap-ui-core-nojQuery.js"); // latest openui5 version

      element.setAttribute('data-sap-ui-libs', JSROOT.openui5libs || "sap.m, sap.ui.layout, sap.ui.unified, sap.ui.commons");

      element.setAttribute('data-sap-ui-theme', JSROOT.openui5theme || 'sap_belize');
      element.setAttribute('data-sap-ui-compatVersion', 'edge');
      // element.setAttribute('data-sap-ui-bindingSyntax', 'complex');

      element.setAttribute('data-sap-ui-preload', 'async'); // '' to disable Component-preload.js

      element.setAttribute('data-sap-ui-evt-oninit', "JSROOT._.completeUI5Loading()");

      element.onerror = function() {
         // remove failed element
         element.parentNode.removeChild(element);
         // and try next
         TryOpenOpenUI(sources);
      }

      element.onload = function() {
         console.log('Load openui5 from ' + src);
      }

      document.getElementsByTagName("head")[0].appendChild(element);
   }

   let openui5_sources = [],
       openui5_dflt = "https://openui5.hana.ondemand.com/1.72.0/",
       openui5_root = JSROOT.source_dir.replace(/jsrootsys/g, "rootui5sys/distribution");

   if (openui5_root == JSROOT.source_dir) openui5_root = "";

   if (typeof JSROOT.openui5src == 'string') {
      switch (JSROOT.openui5src) {
         case "nodefault": openui5_dflt = ""; break;
         case "default": openui5_sources.push(openui5_dflt); openui5_dflt = ""; break;
         case "nojsroot": openui5_root = ""; break;
         case "jsroot": openui5_sources.push(openui5_root); openui5_root = ""; break;
         default: openui5_sources.push(JSROOT.openui5src); break;
      }
   }

   if (openui5_root && (openui5_sources.indexOf(openui5_root)<0)) openui5_sources.push(openui5_root);
   if (openui5_dflt && (openui5_sources.indexOf(openui5_dflt)<0)) openui5_sources.push(openui5_dflt);

   // return Promise let loader wait before dependent source will be invoked
   return new Promise(resolve => {
      resolveFunc = resolve;
      TryOpenOpenUI(openui5_sources);
   });

});

<|endoftext|>


<commit_before>import _ from 'lodash'
import d3 from 'd3'
import moment from 'moment'
import api from 'data/api'
import path from 'vue/src/parsers/path'
import util from 'util/data'

function melt (data, indicatorArray) {
  var dataset = data.objects
  var baseIndicators = _.map(indicatorArray, function (indicator) {
    return { indicator: indicator + '', value: 0 }
  })
  var o = _(dataset)
    .map(function (d) {
      var base = _.omit(d, 'indicators')
      var indicatorFullList = _.assign(_.cloneDeep(baseIndicators), d.indicators)
      return _.map(indicatorFullList, function (indicator) {
        return _.assign({}, base, indicator)
      })
    })
    .flatten()
    .value()
  return o
}
function _groupBySeries (data, groups, groupBy) {
  return _(data)
    .groupBy(groupBy)
    .map(function (d, ind) {
      return seriesObject(
        _.sortBy(d, _.method('campaign.start_date.getTime')),
        ind,
        null,
        groups
      )
    })
    .value()
}

function seriesObject (d, ind, collection, groups) {
  return {
    name: groups[ind].name,
    values: d
  }
}

function value (datapoint) {
  if (datapoint && datapoint.hasOwnProperty('value')) {
    return datapoint.value
  }
  return null
}

var tooltipDiv = document.createElement('div') // Vue needs a el to bind to to hold tooltips outside the svg, seems like the least messy solution
document.body.appendChild(tooltipDiv)

function _columnData (data, groups, groupBy) {
  var columnData = _(data)
    .groupBy(groupBy)
    .map(_.partialRight(seriesObject, groups))
    .value()
  var baseCampaigns = []
  _.each(columnData, function (series) {
    _.each(series.values, function (value) { // build the base campaign array that includes all campaigns present in any datapoint, used to fill in missing values so the stacked chart doesn't have gaps
      if (!_.find(baseCampaigns, function (campaign) { return campaign.id === value.campaign.id })) {
        baseCampaigns.push(value.campaign)
      }
    })
    _.each(series.values, function (val) { // replace all null values with 0, caused d3 rect rendering errors in the chart
      if (_.isNull(val.value)) {
        val.value = 0
      }
    })
  })
  baseCampaigns = _.sortBy(baseCampaigns, _.method('campaign.start_date.getTime'))
  _.each(columnData, function (series) {
    _.each(baseCampaigns, function (baseCampaign, index) {
      if (!_.find(series.values, function (value) { return value.campaign.id === baseCampaign.id })) {
        series.values.splice(index, 0, { campaign: baseCampaign, location: series.values[0].location, indicator: series.values[0].indicator, value: 0 })
      }
    })
    series.values = _.sortBy(series.values, _.method('campaign.start_date.getTime'))
  })
  var stack = d3.layout.stack()
    .order('default')
    .offset('zero')
    .values(function (d) { return d.values })
    .x(function (d) { return d.campaign.start_date })
    .y(function (d) { return d.value })

  return stack(columnData)
}
function _barData (datapoints, indicators, properties, series) {
  return _(datapoints)
    .pick(indicators)
    .values()
    .flatten()
    .map(_mapProperties(properties))
    .thru(_filterMissing)
    .thru(_makeSeries(series))
    .value()
}
function _mapProperties (mapping) {
  return function (d) {
    var datum = _.clone(d)

    _.forEach(mapping, function (to, from) {
      path.set(datum, to, path.get(datum, from))
    })

    return datum
  }
}
function _filterMissing (data) {
  return _(data)
    .groupBy('y')
    .filter(function (v) {
      return _(v).pluck('x').some(_.partial(util.defined, _, _.identity))
    })
    .values()
    .flatten()
    .forEach(function (d) {
      if (!util.defined(d.x)) {
        d.x = 0
      }
    })
    .value()
}
function _makeSeries (getSeries) {
  return function (data) {
    return _(data)
      .groupBy(getSeries)
      .map(function (v, k) {
        return {
          name: k,
          values: v
        }
      })
      .value()
  }
}
function _getIndicator (d) {
  return d.indicator.short_name
}

const aspects = {
  0: {
    lineChart: 2.664831804,
    pieChart: 1,
    choroplethMap: 1,
    columnChart: 2.664831804,
    scatterChart: 2.664831804,
    barChart: 2.664831804
  },
  1: {
    lineChart: 2.664831804,
    pieChart: 1,
    choroplethMap: 1,
    columnChart: 2.664831804,
    scatterChart: 2.664831804,
    barChart: 2.664831804
  },
  2: {
    lineChart: 1,
    pieChart: 1,
    choroplethMap: 1,
    columnChart: 1,
    scatterChart: 1,
    barChart: 1.5
  },
  3: {
    lineChart: 1,
    pieChart: 1,
    choroplethMap: 1,
    columnChart: 1,
    scatterChart: 1,
    barChart: 1
  }
}

export default {
  init: function (dataPromise, chartType, indicators, locations, lower, upper, groups, groupBy, xAxis, yAxis, layout) {
    let indicatorArray = _.map(indicators, _.property('id'))
    let meltPromise = dataPromise.then(data => { return melt(data, indicatorArray) })
    let chartProcessors = {
      LineChart: {
        fn: this.processLineChart,
        para: [meltPromise, lower, upper, groups, groupBy, layout]
      },
      PieChart: {
        fn: this.processPieChart,
        para: [meltPromise, indicators, layout]
      },
      ChoroplethMap: {
        fn: this.processChoroplethMap,
        para: [meltPromise, locations, xAxis, yAxis, layout]
      },
      ColumnChart: {
        fn: this.processColumnChart,
        para: [meltPromise, lower, upper, groups, groupBy, layout]
      },
      ScatterChart: {
        fn: this.processScatterChart,
        para: [dataPromise, locations, indicators, xAxis, yAxis, layout]
      },
      BarChart: {
        fn: this.processBarChart,
        para: [dataPromise, locations, indicators, xAxis, yAxis, layout]
      }
    }
    return chartProcessors[chartType].fn(...chartProcessors[chartType].para)
  },
  processLineChart: function (dataPromise, lower, upper, groups, groupBy, layout) {
    return dataPromise.then(function (data) {
      if (!data || data.length === 0) {
        return { options: null, data: null }
      }
      if (!lower) { // set the lower bound from the lowest datapoint value
        var sortedDates = _.sortBy(data, _.method('campaign.start_date.getTime'))
        lower = moment(_.first(sortedDates).campaign.start_date)
      }
      var chartOptions = {
        domain: _.constant([lower.toDate(), upper.toDate()]),
        aspect: aspects[layout].lineChart,
        values: _.property('values'),
        x: _.property('campaign.start_date'),
        xFormat: (d) => { return moment(d).format('MMM YYYY') },
        y: _.property('value')
      }
      var chartData = _groupBySeries(data, groups, groupBy)
      return { options: chartOptions, data: chartData }
    })
  },
  processPieChart: function (dataPromise, indicators, layout) {
    var idx = _.indexBy(indicators, 'id')

    return dataPromise.then(function (data) {
      if (!data || data.length === 0) {
        return { options: null, data: null }
      }
      var total = _(data).map(function (n) { return n.value }).sum()
      var chartOptions = {
        aspect: aspects[layout].pieChart,
        domain: _.constant([0, total]),
        name: d => _.get(idx, '[' + d.indicator + '].name', ''),
        margin: {
          top: 0,
          right: 200,
          bottom: 0,
          left: 0
        }
      }
      return { options: chartOptions, data: data }
    })
  },
  processChoroplethMap: function (dataPromise, locations, xAxis, yAxis, layout) {
    var locationsIndex = _.indexBy(locations, 'id')
    return Promise.all([dataPromise, api.geo({ location__in: _.map(locations, function (location) { return location.id }) }, null, {'cache-control': 'max-age=604800, public'})])
    .then(_.spread(function (data, border) {
      var chartOptions = {
        aspect: aspects[layout].choroplethMap,
        domain: _.constant([0, 0.1]),
        name: d => _.get(locationsIndex, '[' + d.properties.location_id + '].name', ''),
        bubblesValue: _.property('properties.bubbleValue'),
        legend: [100, 1000, 5000],
        maxRadius: 20,
        border: border.objects.features
      }

      if (!data || data.length === 0) {
        return { options: chartOptions, data: border.objects.features }
      }

      let indicatorIndex = _(data).groupBy('indicator').value()
      var index = _.indexBy(indicatorIndex[xAxis], 'location')
      let bubbleIndex = _.indexBy(indicatorIndex[yAxis], 'location')

      var chartData = _.map(border.objects.features, function (feature) {
        var location = _.get(index, feature.properties.location_id)
        let bubbleLocation = _.get(bubbleIndex, feature.properties.location_id)
        return _.merge({}, feature, {
          properties: {
            value: _.get(location, 'value'),
            bubbleValue: _.get(bubbleLocation, 'value')
          }
        })
      })

      console.log(chartData.map(d => d.properties))

      return { options: chartOptions, data: chartData }
    }))
  },
  processColumnChart: function (dataPromise, lower, upper, groups, groupBy, layout) {
    return dataPromise.then(function (data) {
      if (!data || data.length === 0) {
        return { options: null, data: null }
      }
      if (!lower) { // set the lower bound from the lowest datapoint value
        var sortedDates = _.sortBy(data, _.method('campaign.start_date.getTime'))
        lower = moment(_.first(sortedDates).campaign.start_date)
      }
      var columnScale = _.map(d3.time.scale()
          .domain([lower.valueOf(), upper.valueOf()])
          .ticks(d3.time.month, 1),
        _.method('getTime')
      )
      var chartData = _columnData(data, groups, groupBy)

      var chartOptions = {
        aspect: aspects[layout].columnChart,
        values: _.property('values'),
        domain: _.constant(columnScale),
        x: function (d) {
          var start = d.campaign.start_date
          return moment(start).startOf('month').toDate().getTime()
        },
        xFormat: function (d) { return moment(d).format('MMM YYYY') }
      }
      return { options: chartOptions, data: chartData }
    })
  },
  processScatterChart: function (dataPromise, locations, indicators, xAxis, yAxis, layout) {
    var locationsIndex = _.indexBy(locations, 'id')

    return dataPromise.then(function (data) {
      if (!data || data.length === 0) {
        return { options: null, data: null }
      }
      var domain = d3.extent(_(data.objects)
        .pluck('indicators')
        .flatten()
        .filter(function (d) {
          return +d.indicator === xAxis
        })
        .pluck('value')
        .value()
      )
      var range = d3.extent(_(data.objects)
        .pluck('indicators')
        .flatten()
        .filter(function (d) {
          return +d.indicator === yAxis
        })
        .pluck('value')
        .value()
      )

      var chartData = _(data.objects)
        .map(function (d) {
          var index = _.indexBy(d.indicators, 'indicator')

          return {
            id: d.location,
            name: locationsIndex[d.location].name,
            x: value(index[xAxis]),
            y: value(index[yAxis])
          }
        })
        .filter(function (d) {
          return _.isFinite(d.x) && _.isFinite(d.y)
        })
        .value()
      var showTooltip = function () {}
      var hideTooltip = function () {}
      var chartOptions = {
        aspect: aspects[layout].scatterChart,
        domain: _.constant(domain),
        onMouseOut: hideTooltip,
        onMouseOver: showTooltip,
        range: _.constant(range),
        xLabel: 'Caregiver Awareness',
        yLabel: 'Missed Children'
      }
      return { options: chartOptions, data: chartData }
    })
  },
  processBarChart: function (dataPromise, locations, indicators, xAxis, yAxis, layout) {
    return dataPromise.then(function (data) {
      if (!data || data.length === 0) {
        return { options: null, data: null }
      }
      var indicatorsIndex = _.indexBy(indicators, 'id')
      var locationsIndex = _.indexBy(locations, 'id')
      var datapoints = _(data)
        .thru(util.unpivot)
        .forEach(function (d) {
          d.indicator = indicatorsIndex[d.indicator]
          d.location = locationsIndex[d.location]
        })
        .groupBy(function (d) {
          return d.indicator.id
        }).value()

      var locationMapping = {
        'value': 'x',
        'location.name': 'y'
      }

      var chartOptions = {
        aspect: aspects[layout].barChart,
        offset: 'zero',
        yFormat: String
      }
      var chartData = _barData(datapoints, _.pluck(indicators, 'id'), locationMapping, _getIndicator)
      return { options: chartOptions, data: chartData }
    })
  }
}
<commit_msg>update the map chart process data logic<commit_after>import _ from 'lodash'
import d3 from 'd3'
import moment from 'moment'
import api from 'data/api'
import path from 'vue/src/parsers/path'
import util from 'util/data'

function melt (data, indicatorArray) {
  var dataset = data.objects
  var baseIndicators = _.map(indicatorArray, function (indicator) {
    return { indicator: indicator + '', value: 0 }
  })
  var o = _(dataset)
    .map(function (d) {
      var base = _.omit(d, 'indicators')
      var indicatorFullList = _.assign(_.cloneDeep(baseIndicators), d.indicators)
      return _.map(indicatorFullList, function (indicator) {
        return _.assign({}, base, indicator)
      })
    })
    .flatten()
    .value()
  return o
}
function _groupBySeries (data, groups, groupBy) {
  return _(data)
    .groupBy(groupBy)
    .map(function (d, ind) {
      return seriesObject(
        _.sortBy(d, _.method('campaign.start_date.getTime')),
        ind,
        null,
        groups
      )
    })
    .value()
}

function seriesObject (d, ind, collection, groups) {
  return {
    name: groups[ind].name,
    values: d
  }
}

function value (datapoint) {
  if (datapoint && datapoint.hasOwnProperty('value')) {
    return datapoint.value
  }
  return null
}

var tooltipDiv = document.createElement('div') // Vue needs a el to bind to to hold tooltips outside the svg, seems like the least messy solution
document.body.appendChild(tooltipDiv)

function _columnData (data, groups, groupBy) {
  var columnData = _(data)
    .groupBy(groupBy)
    .map(_.partialRight(seriesObject, groups))
    .value()
  var baseCampaigns = []
  _.each(columnData, function (series) {
    _.each(series.values, function (value) { // build the base campaign array that includes all campaigns present in any datapoint, used to fill in missing values so the stacked chart doesn't have gaps
      if (!_.find(baseCampaigns, function (campaign) { return campaign.id === value.campaign.id })) {
        baseCampaigns.push(value.campaign)
      }
    })
    _.each(series.values, function (val) { // replace all null values with 0, caused d3 rect rendering errors in the chart
      if (_.isNull(val.value)) {
        val.value = 0
      }
    })
  })
  baseCampaigns = _.sortBy(baseCampaigns, _.method('campaign.start_date.getTime'))
  _.each(columnData, function (series) {
    _.each(baseCampaigns, function (baseCampaign, index) {
      if (!_.find(series.values, function (value) { return value.campaign.id === baseCampaign.id })) {
        series.values.splice(index, 0, { campaign: baseCampaign, location: series.values[0].location, indicator: series.values[0].indicator, value: 0 })
      }
    })
    series.values = _.sortBy(series.values, _.method('campaign.start_date.getTime'))
  })
  var stack = d3.layout.stack()
    .order('default')
    .offset('zero')
    .values(function (d) { return d.values })
    .x(function (d) { return d.campaign.start_date })
    .y(function (d) { return d.value })

  return stack(columnData)
}
function _barData (datapoints, indicators, properties, series) {
  return _(datapoints)
    .pick(indicators)
    .values()
    .flatten()
    .map(_mapProperties(properties))
    .thru(_filterMissing)
    .thru(_makeSeries(series))
    .value()
}
function _mapProperties (mapping) {
  return function (d) {
    var datum = _.clone(d)

    _.forEach(mapping, function (to, from) {
      path.set(datum, to, path.get(datum, from))
    })

    return datum
  }
}
function _filterMissing (data) {
  return _(data)
    .groupBy('y')
    .filter(function (v) {
      return _(v).pluck('x').some(_.partial(util.defined, _, _.identity))
    })
    .values()
    .flatten()
    .forEach(function (d) {
      if (!util.defined(d.x)) {
        d.x = 0
      }
    })
    .value()
}
function _makeSeries (getSeries) {
  return function (data) {
    return _(data)
      .groupBy(getSeries)
      .map(function (v, k) {
        return {
          name: k,
          values: v
        }
      })
      .value()
  }
}
function _getIndicator (d) {
  return d.indicator.short_name
}

const aspects = {
  0: {
    lineChart: 2.664831804,
    pieChart: 1,
    choroplethMap: 1,
    columnChart: 2.664831804,
    scatterChart: 2.664831804,
    barChart: 2.664831804
  },
  1: {
    lineChart: 2.664831804,
    pieChart: 1,
    choroplethMap: 1,
    columnChart: 2.664831804,
    scatterChart: 2.664831804,
    barChart: 2.664831804
  },
  2: {
    lineChart: 1,
    pieChart: 1,
    choroplethMap: 1,
    columnChart: 1,
    scatterChart: 1,
    barChart: 1.5
  },
  3: {
    lineChart: 1,
    pieChart: 1,
    choroplethMap: 1,
    columnChart: 1,
    scatterChart: 1,
    barChart: 1
  }
}

export default {
  init: function (dataPromise, chartType, indicators, locations, lower, upper, groups, groupBy, xAxis, yAxis, layout) {
    let indicatorArray = _.map(indicators, _.property('id'))
    let meltPromise = dataPromise.then(data => { return melt(data, indicatorArray) })
    let chartProcessors = {
      LineChart: {
        fn: this.processLineChart,
        para: [meltPromise, lower, upper, groups, groupBy, layout]
      },
      PieChart: {
        fn: this.processPieChart,
        para: [meltPromise, indicators, layout]
      },
      ChoroplethMap: {
        fn: this.processChoroplethMap,
        para: [meltPromise, locations, xAxis, yAxis, layout]
      },
      ColumnChart: {
        fn: this.processColumnChart,
        para: [meltPromise, lower, upper, groups, groupBy, layout]
      },
      ScatterChart: {
        fn: this.processScatterChart,
        para: [dataPromise, locations, indicators, xAxis, yAxis, layout]
      },
      BarChart: {
        fn: this.processBarChart,
        para: [dataPromise, locations, indicators, xAxis, yAxis, layout]
      }
    }
    return chartProcessors[chartType].fn(...chartProcessors[chartType].para)
  },
  processLineChart: function (dataPromise, lower, upper, groups, groupBy, layout) {
    return dataPromise.then(function (data) {
      if (!data || data.length === 0) {
        return { options: null, data: null }
      }
      if (!lower) { // set the lower bound from the lowest datapoint value
        var sortedDates = _.sortBy(data, _.method('campaign.start_date.getTime'))
        lower = moment(_.first(sortedDates).campaign.start_date)
      }
      var chartOptions = {
        domain: _.constant([lower.toDate(), upper.toDate()]),
        aspect: aspects[layout].lineChart,
        values: _.property('values'),
        x: _.property('campaign.start_date'),
        xFormat: (d) => { return moment(d).format('MMM YYYY') },
        y: _.property('value')
      }
      var chartData = _groupBySeries(data, groups, groupBy)
      return { options: chartOptions, data: chartData }
    })
  },
  processPieChart: function (dataPromise, indicators, layout) {
    var idx = _.indexBy(indicators, 'id')

    return dataPromise.then(function (data) {
      if (!data || data.length === 0) {
        return { options: null, data: null }
      }
      var total = _(data).map(function (n) { return n.value }).sum()
      var chartOptions = {
        aspect: aspects[layout].pieChart,
        domain: _.constant([0, total]),
        name: d => _.get(idx, '[' + d.indicator + '].name', ''),
        margin: {
          top: 0,
          right: 200,
          bottom: 0,
          left: 0
        }
      }
      return { options: chartOptions, data: data }
    })
  },
  processChoroplethMap: function (dataPromise, locations, xAxis, yAxis, layout) {
    var locationsIndex = _.indexBy(locations, 'id')
    return Promise.all([dataPromise, api.geo({ location__in: _.map(locations, function (location) { return location.id }) }, null, {'cache-control': 'max-age=604800, public'})])
    .then(_.spread(function (data, border) {
      let maxValue = 0
      let maxRadius = 20

      var chartOptions = {
        aspect: aspects[layout].choroplethMap,
        name: d => _.get(locationsIndex, '[' + d.properties.location_id + '].name', ''),
        bubblesValue: _.property('properties.bubbleValue'),
        maxValue: maxValue,
        maxRadius: maxRadius,
        border: border.objects.features
      }

      if (!data || data.length === 0) {
        return { options: chartOptions, data: border.objects.features }
      }

      let indicatorIndex = _(data).groupBy('indicator').value()
      var index = _.indexBy(indicatorIndex[xAxis], 'location')
      let bubbleIndex = _.indexBy(indicatorIndex[yAxis], 'location')

      maxValue = Math.max(...indicatorIndex[yAxis].map(d => d.value))
      chartOptions.maxValue = maxValue

      var chartData = _.map(border.objects.features, function (feature) {
        var location = _.get(index, feature.properties.location_id)
        let bubbleLocation = _.get(bubbleIndex, feature.properties.location_id)
        return _.merge({}, feature, {
          properties: {
            value: _.get(location, 'value'),
            bubbleValue: _.get(bubbleLocation, 'value')
          }
        })
      })

      return { options: chartOptions, data: chartData }
    }))
  },
  processColumnChart: function (dataPromise, lower, upper, groups, groupBy, layout) {
    return dataPromise.then(function (data) {
      if (!data || data.length === 0) {
        return { options: null, data: null }
      }
      if (!lower) { // set the lower bound from the lowest datapoint value
        var sortedDates = _.sortBy(data, _.method('campaign.start_date.getTime'))
        lower = moment(_.first(sortedDates).campaign.start_date)
      }
      var columnScale = _.map(d3.time.scale()
          .domain([lower.valueOf(), upper.valueOf()])
          .ticks(d3.time.month, 1),
        _.method('getTime')
      )
      var chartData = _columnData(data, groups, groupBy)

      var chartOptions = {
        aspect: aspects[layout].columnChart,
        values: _.property('values'),
        domain: _.constant(columnScale),
        x: function (d) {
          var start = d.campaign.start_date
          return moment(start).startOf('month').toDate().getTime()
        },
        xFormat: function (d) { return moment(d).format('MMM YYYY') }
      }
      return { options: chartOptions, data: chartData }
    })
  },
  processScatterChart: function (dataPromise, locations, indicators, xAxis, yAxis, layout) {
    var locationsIndex = _.indexBy(locations, 'id')

    return dataPromise.then(function (data) {
      if (!data || data.length === 0) {
        return { options: null, data: null }
      }
      var domain = d3.extent(_(data.objects)
        .pluck('indicators')
        .flatten()
        .filter(function (d) {
          return +d.indicator === xAxis
        })
        .pluck('value')
        .value()
      )
      var range = d3.extent(_(data.objects)
        .pluck('indicators')
        .flatten()
        .filter(function (d) {
          return +d.indicator === yAxis
        })
        .pluck('value')
        .value()
      )

      var chartData = _(data.objects)
        .map(function (d) {
          var index = _.indexBy(d.indicators, 'indicator')

          return {
            id: d.location,
            name: locationsIndex[d.location].name,
            x: value(index[xAxis]),
            y: value(index[yAxis])
          }
        })
        .filter(function (d) {
          return _.isFinite(d.x) && _.isFinite(d.y)
        })
        .value()
      var showTooltip = function () {}
      var hideTooltip = function () {}
      var chartOptions = {
        aspect: aspects[layout].scatterChart,
        domain: _.constant(domain),
        onMouseOut: hideTooltip,
        onMouseOver: showTooltip,
        range: _.constant(range),
        xLabel: 'Caregiver Awareness',
        yLabel: 'Missed Children'
      }
      return { options: chartOptions, data: chartData }
    })
  },
  processBarChart: function (dataPromise, locations, indicators, xAxis, yAxis, layout) {
    return dataPromise.then(function (data) {
      if (!data || data.length === 0) {
        return { options: null, data: null }
      }
      var indicatorsIndex = _.indexBy(indicators, 'id')
      var locationsIndex = _.indexBy(locations, 'id')
      var datapoints = _(data)
        .thru(util.unpivot)
        .forEach(function (d) {
          d.indicator = indicatorsIndex[d.indicator]
          d.location = locationsIndex[d.location]
        })
        .groupBy(function (d) {
          return d.indicator.id
        }).value()

      var locationMapping = {
        'value': 'x',
        'location.name': 'y'
      }

      var chartOptions = {
        aspect: aspects[layout].barChart,
        offset: 'zero',
        yFormat: String
      }
      var chartData = _barData(datapoints, _.pluck(indicators, 'id'), locationMapping, _getIndicator)
      return { options: chartOptions, data: chartData }
    })
  }
}
<|endoftext|>


<commit_before>/* jshint node: true */
'use strict';

var gulp = require('gulp'),
    postcss = require('gulp-postcss'),
    g = require('gulp-load-plugins')({ lazy: false }),
    replace = require('gulp-replace'),
    noop = g.util.noop,
    es = require('event-stream'),
    bowerFiles = require('main-bower-files'),
    rimraf = require('rimraf'),
    queue = require('streamqueue'),
    lazypipe = require('lazypipe'),
    stylish = require('jshint-stylish'),
    bower = require('./bower'),
    isWatching = false;

var htmlminOpts = {
    removeComments: true,
    collapseWhitespace: true,
    removeEmptyAttributes: false,
    collapseBooleanAttributes: true,
    removeRedundantAttributes: true
};

var baseUrl = g.util.env.baseUrl || '/';
var theme = g.util.env.theme || 'space-thumbnail';

/**
 * JS Hint
 */
gulp.task('jshint', function () {
    return gulp.src([
        './gulpfile.js',
        './src/app/**/*.js'
    ])
        .pipe(g.cached('jshint'))
        .pipe(jshint('./.jshintrc'))
        .pipe(livereload());
});

/**
 * CSS
 */
gulp.task('clean-css', function (done) {
    rimraf.sync('./.tmp/css', {});
    done();
});

//Plugins
var atImport = require('postcss-import');
var customProperties = require('postcss-custom-properties');
var customMedia = require('postcss-custom-media');
var calc = require('postcss-calc');
var pixrem = require('pixrem');
var colorFunction = require('postcss-color-function');
var autoprefixer = require('autoprefixer');
var cssnano = require('cssnano');

//Processor
gulp.task('styles', ['clean-css'], function () {
    var processors = [
        atImport({
            from: './src/themes/' + theme + '/src/app.css'
        }),
        customMedia,
        customProperties,
        calc,
        pixrem,
        colorFunction,
        autoprefixer({
            browsers: ['last 2 versions']
        })
    ];

    return gulp.src(
        './src/themes/' + theme + '/src/app.css')
        .pipe(postcss(processors))
        .pipe(replace(/url\(\/assets\/img\/(.*)\)/g, 'url(' + baseUrl + 'assets/img/$1)'))
        .pipe(gulp.dest('./.tmp/css/'))
        .pipe(g.cached('built-css'))
        .pipe(livereload());
});

gulp.task('styles-dist', function () {
    var processors = [
        atImport({
            from: './src/themes/' + theme + '/src/app.css'
        }),
        customMedia,
        customProperties,
        calc,
        pixrem,
        colorFunction,
        autoprefixer({
            browsers: ['last 2 versions']
        }),
        cssnano
    ];
    return gulp.src([
        './src/themes/' + theme + '/src/app.css'
    ])
        .pipe(postcss(processors))
        .pipe(replace(/url\(\/assets\/img\/(.*)\)/g, 'url(' + baseUrl + 'assets/img/$1)'))
        .pipe(gulp.dest('./dist/css/'));
});

gulp.task('csslint', ['styles'], function () {
});

/**
 * Scripts
 */
gulp.task('scripts-dist', ['templates-dist'], function () {
    return es.merge(appConfigSource(), appFiles())
        .pipe(g.angularFilesort())
        .pipe(dist('js', bower.name, { ngAnnotate: true }));
});

gulp.task('scripts', [], function () {
    return appConfigSource()
        .pipe(g.angularFilesort())
        .pipe(gulp.dest('./.tmp'));
});

/**
 * Templates
 */
gulp.task('templates', function () {
    return templateFiles().pipe(buildTemplates());
});

gulp.task('templates-dist', function () {
    return templateFiles({ min: true }).pipe(buildTemplates());
});

/**
 * Vendors
 */
gulp.task('vendors', function () {
    var files = bowerFiles();
    var vendorJs = fileTypeFilter(files, 'js');
    var vendorCss = fileTypeFilter(files, 'css');
    var q = new queue({ objectMode: true });
    if (vendorJs.length) {
        q.queue(gulp.src(vendorJs).pipe(dist('js', 'vendors')));
    }
    if (vendorCss.length) {
        q.queue(gulp.src(vendorCss).pipe(dist('css', 'vendors')));
    }
    return q.done();
});

/**
 * Index
 */
gulp.task('index', index);
gulp.task('build-all', ['styles', 'templates', 'scripts'], index);

function index() {
    var opt = { read: false };
    return gulp.src('./src/app/index.html')
        .pipe(g.inject(gulp.src('./src/themes/' + theme + '/assets/js/*.js'), { addRootSlash: false, ignorePath: 'src/themes/' + theme, starttag: '<!-- inject:vendorTheme -->' }))
        .pipe(g.inject(gulp.src(bowerFiles(), opt), { addRootSlash: false, ignorePath: 'bower_components', starttag: '<!-- inject:vendor:{{ext}} -->' }))
        .pipe(g.inject(es.merge(appFiles(), cssFiles(opt)), { addRootSlash: false, ignorePath: ['.tmp', 'src/app', 'src/themes/' + theme] }))
        .pipe(replace(/\"\/assets\/img\/(.*)\"/g, baseUrl + '/assets/img/$1'))
        .pipe(replace('<base href="/" />', '<base href="' + baseUrl + '" />'))
        .pipe(g.embedlr())
        .pipe(gulp.dest('./.tmp/'))
        .pipe(livereload());
}

/**
 * Assets
 */
gulp.task('assets', ['favicon'], function () {
    return gulp.src(['./src/assets/**', './src/themes/' + theme + '/assets/**'])
        .pipe(gulp.dest('./dist/assets'));
});
gulp.task('favicon', function () {
    return gulp.src(['./src/themes/' + theme + '/favicon.ico'])
        .pipe(gulp.dest('./dist'));
});

/**
 * Clenaup
 */
gulp.task('clean-dist', function (done) {
    rimraf.sync('./dist', {});
    done();
});

/**
 * Dist
 */
gulp.task('dist', ['clean-dist', 'vendors', 'assets', 'styles-dist', 'scripts-dist'], function () {
    return gulp.src('./src/app/index.html')
        .pipe(g.inject(gulp.src('./dist/vendors.min.{js,css}'), { addRootSlash: false, ignorePath: 'dist', starttag: '<!-- inject:vendor:{{ext}} -->' }))
        .pipe(replace('<base href="/" />', '<base href="' + baseUrl + '" />'))
        .pipe(g.inject(gulp.src('./dist/' + bower.name + '.min.{js,css}'), { addRootSlash: false, ignorePath: 'dist' }))
        .pipe(g.inject(cssFilesDist({}), { addRootSlash: false, ignorePath: ['dist', 'src/app', 'src/themes/' + theme] }))
        .pipe(g.htmlmin(htmlminOpts))
        .pipe(gulp.dest('./dist/'));
});

/**
 * Watch
 */
gulp.task('serve', ['watch'], g.serve({
    port: 3000,
    root: ['./.tmp', './.tmp/src/app', './src/app', './bower_components', './src', './src/themes/' + theme],
    middleware: function (req, res, next) {
        if (req.url.indexOf('.') === -1) {
            req.url = '/index.html';
        }

        return next();
    }
}));
gulp.task('watch', ['default'], function () {
    isWatching = true;
    // Initiate livereload server:
    g.livereload.listen();
    gulp.watch('./src/app/**/*.js', ['jshint', 'scripts']).on('change', function (evt) {
        if (evt.type !== 'changed') {
            gulp.start('index');
        } else {
            g.livereload.changed(evt);
        }
    });
    gulp.watch('./src/themes/' + theme + '/templates/**/*.js', ['jshint']).on('change', function (evt) {
        if (evt.type !== 'changed') {
            gulp.start('index');
        } else {
            g.livereload.changed(evt);
        }
    });
    gulp.watch('./src/app/index.html', ['index']);
    gulp.watch(['./src/themes/' + theme + '/templates/**/*.html'], ['templates']);
    gulp.watch(['./src/themes/' + theme + '/src/**/*.css'], ['csslint']).on('change', function (evt) {
        if (evt.type !== 'changed') {
            gulp.start('index');
        } else {
            g.livereload.changed(evt);
        }
    });
});

/**
 * Default task
 */
gulp.task('default', ['lint', 'build-all']);

/**
 * Lint everything
 */
gulp.task('lint', ['jshint', 'csslint']);

/**
 * Test
 */
gulp.task('test', ['templates'], function () {
    return testFiles()
        .pipe(g.karma({
            configFile: 'karma.conf.js',
            action: 'run'
        }));
});

/**
 * Inject all files for tests into karma.conf.js
 * to be able to run `karma` without gulp.
 */
gulp.task('karma-conf', ['templates'], function () {
    return gulp.src('./karma.conf.js')
        .pipe(g.inject(testFiles(), {
            starttag: 'files: [',
            endtag: ']',
            addRootSlash: false,
            transform: function (filepath, file, i, length) {
                return '  \'' + filepath + '\'' + (i + 1 < length ? ',' : '');
            }
        }))
        .pipe(gulp.dest('./'));
});

/**
 * Test files
 */
function testFiles() {
    return new queue({ objectMode: true })
        .queue(gulp.src(fileTypeFilter(bowerFiles(), 'js')))
        .queue(gulp.src('./bower_components/angular-mocks/angular-mocks.js'))
        .queue(appFiles())
        .queue(gulp.src(['./src/app/**/*_test.js', './.tmp/src/app/**/*_test.js']))
        .done();
}

/**
 * All CSS files as a stream
 */
function cssFiles(opt) {
    return gulp.src('./.tmp/css/**/*.css', opt);
}

function cssFilesDist(opt) {
    return gulp.src('./dist/css/**/*.css', opt);
}

/**
 * All AngularJS application files as a stream
 */
function appFiles() {
    var files = [
        './.tmp/' + bower.name + '-templates.js',
        './.tmp/**/*.js',
        '!./.tmp/app.config.js',
        '!./.tmp/src/app/**/*_test.js',
        './src/app/**/*.js',
        '!./src/app/**/*_test.js',
        './src/themes/' + theme + '/**/*.js',
        '!./src/app/app.config.js'
    ];
    return gulp.src(files)
        .pipe(g.angularFilesort());
}

/**
 * All AngularJS templates/partials as a stream
 */
function templateFiles(opt) {
    return gulp.src('./src/themes/' + theme + '/templates/**/*.html', opt)
        .pipe(opt && opt.min ? g.htmlmin(htmlminOpts) : noop());
}

/**
 * Build AngularJS templates/partials
 */
function buildTemplates() {
    return lazypipe()
        .pipe(g.ngHtml2js, {
            declareModule: false,
            moduleName: 'myBlog',
            prefix: 'templates/'
        })
        .pipe(g.concat, bower.name + '-templates.js')
        .pipe(gulp.dest, './.tmp')
        .pipe(livereload)();
}

/**
 * Filter an array of files according to file type
 *
 * @param {Array} files
 * @param {String} extension
 * @return {Array}
 */
function fileTypeFilter(files, extension) {
    var regExp = new RegExp('\\.' + extension + '$');
    return files.filter(regExp.test.bind(regExp));
}

/**
 * Concat, rename, minify
 *
 * @param {String} ext
 * @param {String} name
 * @param {Object} opt
 */
function dist(ext, name, opt) {
    opt = opt || {};
    return lazypipe()
        .pipe(g.concat, name + '.' + ext)
        .pipe(gulp.dest, './dist')
        .pipe(opt.ngAnnotate ? g.ngAnnotate : noop)
        .pipe(opt.ngAnnotate ? g.rename : noop, name + '.annotated.' + ext)
        .pipe(opt.ngAnnotate ? gulp.dest : noop, './dist')
        .pipe(ext === 'js' ? g.uglify : g.minifyCss)
        .pipe(g.rename, name + '.min.' + ext)
        .pipe(gulp.dest, './dist')();
}

/**
 * Livereload (or noop if not run by watch)
 */
function livereload() {
    return lazypipe()
        .pipe(isWatching ? g.livereload : noop)();
}

/**
 * Jshint with stylish reporter
 */
function jshint(jshintfile) {
    return lazypipe()
        .pipe(g.jshint, jshintfile)
        .pipe(g.jshint.reporter, stylish)();
}

function extend() {
    for (var i = 1; i < arguments.length; i++) {
        for (var key in arguments[i]) {
            if (arguments[i].hasOwnProperty(key)) {
                arguments[0][key] = arguments[i][key];
            }
        }
    }
    return arguments[0];
}

function module_exists(name) {
    try { return require.resolve(name) }
    catch (e) { return false }
}

function baasicAppConfiguratinProvider() {
    var themeConfigPath = './src/themes/' + theme + '/app.conf.json';
    var rootAppConfig = require('./app.conf.json');
    var themeAppConfig = {};
    if (module_exists(themeConfigPath)) {
        themeAppConfig = require(themeConfigPath);
    }
    return extend({
        apiKey: ''
    }, themeAppConfig, rootAppConfig);
}

function appConfigSource() {
    var appConfig = baasicAppConfiguratinProvider();
    return gulp.src(['./src/app/app.config.js'])
        .pipe(replace('<apiKey>', appConfig.apiKey))
        .pipe(replace('<apiRootUrl>', appConfig.apiRootUrl))
        .pipe(replace('<apiVersion>', appConfig.apiVersion));
};<commit_msg>The issue with gulp serve is fixed.<commit_after>/* jshint node: true */
'use strict';

var gulp = require('gulp'),
    postcss = require('gulp-postcss'),
    g = require('gulp-load-plugins')({ lazy: false }),
    replace = require('gulp-replace'),
    noop = g.util.noop,
    es = require('event-stream'),
    bowerFiles = require('main-bower-files'),
    rimraf = require('rimraf'),
    queue = require('streamqueue'),
    lazypipe = require('lazypipe'),
    stylish = require('jshint-stylish'),
    bower = require('./bower'),
    isWatching = false;

var htmlminOpts = {
    removeComments: true,
    collapseWhitespace: true,
    removeEmptyAttributes: false,
    collapseBooleanAttributes: true,
    removeRedundantAttributes: true
};

var baseUrl = g.util.env.baseUrl || '/';
var theme = g.util.env.theme || 'space-thumbnail';

/**
 * JS Hint
 */
gulp.task('jshint', function () {
    return gulp.src([
        './gulpfile.js',
        './src/app/**/*.js'
    ])
        .pipe(g.cached('jshint'))
        .pipe(jshint('./.jshintrc'))
        .pipe(livereload());
});

/**
 * CSS
 */
gulp.task('clean-css', function (done) {
    rimraf.sync('./.tmp/css', {});
    done();
});

//Plugins
var atImport = require('postcss-import');
var customProperties = require('postcss-custom-properties');
var customMedia = require('postcss-custom-media');
var calc = require('postcss-calc');
var pixrem = require('pixrem');
var colorFunction = require('postcss-color-function');
var autoprefixer = require('autoprefixer');
var cssnano = require('cssnano');

//Processor
gulp.task('styles', ['clean-css'], function () {
    var processors = [
        atImport({
            from: './src/themes/' + theme + '/src/app.css'
        }),
        customMedia,
        customProperties,
        calc,
        pixrem,
        colorFunction,
        autoprefixer({
            browsers: ['last 2 versions']
        })
    ];

    return gulp.src(
        './src/themes/' + theme + '/src/app.css')
        .pipe(postcss(processors))
        .pipe(replace(/url\(\/assets\/img\/(.*)\)/g, 'url(' + baseUrl + 'assets/img/$1)'))
        .pipe(gulp.dest('./.tmp/css/'))
        .pipe(g.cached('built-css'))
        .pipe(livereload());
});

gulp.task('styles-dist', function () {
    var processors = [
        atImport({
            from: './src/themes/' + theme + '/src/app.css'
        }),
        customMedia,
        customProperties,
        calc,
        pixrem,
        colorFunction,
        autoprefixer({
            browsers: ['last 2 versions']
        }),
        cssnano
    ];
    return gulp.src([
        './src/themes/' + theme + '/src/app.css'
    ])
        .pipe(postcss(processors))
        .pipe(replace(/url\(\/assets\/img\/(.*)\)/g, 'url(' + baseUrl + 'assets/img/$1)'))
        .pipe(gulp.dest('./dist/css/'));
});

gulp.task('csslint', ['styles'], function () {
});

/**
 * Scripts
 */
gulp.task('scripts-dist', ['templates-dist'], function () {
    return es.merge(appConfigSource(), appFiles())
        .pipe(g.angularFilesort())
        .pipe(dist('js', bower.name, { ngAnnotate: true }));
});

gulp.task('scripts', [], function () {
    return appConfigSource()
        .pipe(g.angularFilesort())
        .pipe(gulp.dest('./.tmp'));
});

/**
 * Templates
 */
gulp.task('templates', function () {
    return templateFiles().pipe(buildTemplates());
});

gulp.task('templates-dist', function () {
    return templateFiles({ min: true }).pipe(buildTemplates());
});

/**
 * Vendors
 */
gulp.task('vendors', function () {
    var files = bowerFiles();
    var vendorJs = fileTypeFilter(files, 'js');
    var vendorCss = fileTypeFilter(files, 'css');
    var q = new queue({ objectMode: true });
    if (vendorJs.length) {
        q.queue(gulp.src(vendorJs).pipe(dist('js', 'vendors')));
    }
    if (vendorCss.length) {
        q.queue(gulp.src(vendorCss).pipe(dist('css', 'vendors')));
    }
    return q.done();
});

/**
 * Index
 */
gulp.task('index', index);
gulp.task('build-all', ['styles', 'templates', 'scripts'], index);

function index() {
    var opt = { read: false };
    return gulp.src('./src/app/index.html')
        .pipe(g.inject(gulp.src('./src/themes/' + theme + '/assets/js/*.js'), { addRootSlash: false, ignorePath: 'src/themes/' + theme, starttag: '<!-- inject:vendorTheme -->' }))
        .pipe(g.inject(gulp.src(bowerFiles(), opt), { addRootSlash: false, ignorePath: 'bower_components', starttag: '<!-- inject:vendor:{{ext}} -->' }))
        .pipe(g.inject(es.merge(appFiles(), cssFiles(opt)), { addRootSlash: false, ignorePath: ['.tmp', 'src/app', 'src/themes/' + theme] }))
        .pipe(replace(/\"\/assets\/img\/(.*)\"/g, baseUrl + '/assets/img/$1'))
        .pipe(replace('<base href="/" />', '<base href="' + baseUrl + '" />'))
        .pipe(g.embedlr())
        .pipe(gulp.dest('./.tmp/'))
        .pipe(livereload());
}

/**
 * Assets
 */
gulp.task('assets', ['favicon'], function () {
    return gulp.src(['./src/assets/**', './src/themes/' + theme + '/assets/**'])
        .pipe(gulp.dest('./dist/assets'));
});
gulp.task('favicon', function () {
    return gulp.src(['./src/themes/' + theme + '/favicon.ico'])
        .pipe(gulp.dest('./dist'));
});

/**
 * Clenaup
 */
gulp.task('clean-dist', function (done) {
    rimraf.sync('./dist', {});
    done();
});

/**
 * Dist
 */
gulp.task('dist', ['clean-dist', 'vendors', 'assets', 'styles-dist', 'scripts-dist'], function () {
    return gulp.src('./src/app/index.html')
        .pipe(g.inject(gulp.src('./dist/vendors.min.{js,css}'), { addRootSlash: false, ignorePath: 'dist', starttag: '<!-- inject:vendor:{{ext}} -->' }))
        .pipe(replace('<base href="/" />', '<base href="' + baseUrl + '" />'))
        .pipe(g.inject(gulp.src('./dist/' + bower.name + '.min.{js,css}'), { addRootSlash: false, ignorePath: 'dist' }))
        .pipe(g.inject(cssFilesDist({}), { addRootSlash: false, ignorePath: ['dist', 'src/app', 'src/themes/' + theme] }))
        .pipe(g.htmlmin(htmlminOpts))
        .pipe(gulp.dest('./dist/'));
});

/**
 * Watch
 */
gulp.task('serve', ['watch'], g.serve({
    port: 3000,
    root: ['./.tmp', './.tmp/src/app', './src/app', './bower_components', './src', './src/themes/' + theme],
    middleware: function (req, res, next) {
        if (req.url.indexOf('.') === -1) {
            req.url = '/index.html';
        }

        return next();
    }
}));
gulp.task('watch', ['default'], function () {
    isWatching = true;
    // Initiate livereload server:
    g.livereload.listen();
    gulp.watch('./src/app/**/*.js', ['jshint', 'scripts']).on('change', function (evt) {
        if (evt.type !== 'changed') {
            gulp.start('index');
        } else {
            g.livereload.changed(evt);
        }
    });
    gulp.watch('./src/themes/' + theme + '/templates/**/*.js', ['jshint']).on('change', function (evt) {
        if (evt.type !== 'changed') {
            gulp.start('index');
        } else {
            g.livereload.changed(evt);
        }
    });
    gulp.watch('./src/app/index.html', ['index']);
    gulp.watch(['./src/themes/' + theme + '/templates/**/*.html'], ['templates']);
    gulp.watch(['./src/themes/' + theme + '/src/**/*.css'], ['csslint']).on('change', function (evt) {
        if (evt.type !== 'changed') {
            gulp.start('index');
        } else {
            g.livereload.changed(evt);
        }
    });
});

/**
 * Default task
 */
gulp.task('default', ['lint', 'build-all']);

/**
 * Lint everything
 */
gulp.task('lint', ['jshint', 'csslint']);

/**
 * Test
 */
gulp.task('test', ['templates'], function () {
    return testFiles()
        .pipe(g.karma({
            configFile: 'karma.conf.js',
            action: 'run'
        }));
});

/**
 * Inject all files for tests into karma.conf.js
 * to be able to run `karma` without gulp.
 */
gulp.task('karma-conf', ['templates'], function () {
    return gulp.src('./karma.conf.js')
        .pipe(g.inject(testFiles(), {
            starttag: 'files: [',
            endtag: ']',
            addRootSlash: false,
            transform: function (filepath, file, i, length) {
                return '  \'' + filepath + '\'' + (i + 1 < length ? ',' : '');
            }
        }))
        .pipe(gulp.dest('./'));
});

/**
 * Test files
 */
function testFiles() {
    return new queue({ objectMode: true })
        .queue(gulp.src(fileTypeFilter(bowerFiles(), 'js')))
        .queue(gulp.src('./bower_components/angular-mocks/angular-mocks.js'))
        .queue(appFiles())
        .queue(gulp.src(['./src/app/**/*_test.js', './.tmp/src/app/**/*_test.js']))
        .done();
}

/**
 * All CSS files as a stream
 */
function cssFiles(opt) {
    return gulp.src('./.tmp/css/**/*.css', opt);
}

function cssFilesDist(opt) {
    return gulp.src('./dist/css/**/*.css', opt);
}

/**
 * All AngularJS application files as a stream
 */
function appFiles() {
    var files = [
        './.tmp/' + bower.name + '-templates.js',
        './.tmp/**/*.js',
        '!./.tmp/src/app/**/*_test.js',
        './src/app/**/*.js',
        '!./src/app/**/*_test.js',
        './src/themes/' + theme + '/**/*.js',
        '!./src/app/app.config.js'
    ];
    return gulp.src(files)
        .pipe(g.angularFilesort());
}

/**
 * All AngularJS templates/partials as a stream
 */
function templateFiles(opt) {
    return gulp.src('./src/themes/' + theme + '/templates/**/*.html', opt)
        .pipe(opt && opt.min ? g.htmlmin(htmlminOpts) : noop());
}

/**
 * Build AngularJS templates/partials
 */
function buildTemplates() {
    return lazypipe()
        .pipe(g.ngHtml2js, {
            declareModule: false,
            moduleName: 'myBlog',
            prefix: 'templates/'
        })
        .pipe(g.concat, bower.name + '-templates.js')
        .pipe(gulp.dest, './.tmp')
        .pipe(livereload)();
}

/**
 * Filter an array of files according to file type
 *
 * @param {Array} files
 * @param {String} extension
 * @return {Array}
 */
function fileTypeFilter(files, extension) {
    var regExp = new RegExp('\\.' + extension + '$');
    return files.filter(regExp.test.bind(regExp));
}

/**
 * Concat, rename, minify
 *
 * @param {String} ext
 * @param {String} name
 * @param {Object} opt
 */
function dist(ext, name, opt) {
    opt = opt || {};
    return lazypipe()
        .pipe(g.concat, name + '.' + ext)
        .pipe(gulp.dest, './dist')
        .pipe(opt.ngAnnotate ? g.ngAnnotate : noop)
        .pipe(opt.ngAnnotate ? g.rename : noop, name + '.annotated.' + ext)
        .pipe(opt.ngAnnotate ? gulp.dest : noop, './dist')
        .pipe(ext === 'js' ? g.uglify : g.minifyCss)
        .pipe(g.rename, name + '.min.' + ext)
        .pipe(gulp.dest, './dist')();
}

/**
 * Livereload (or noop if not run by watch)
 */
function livereload() {
    return lazypipe()
        .pipe(isWatching ? g.livereload : noop)();
}

/**
 * Jshint with stylish reporter
 */
function jshint(jshintfile) {
    return lazypipe()
        .pipe(g.jshint, jshintfile)
        .pipe(g.jshint.reporter, stylish)();
}

function extend() {
    for (var i = 1; i < arguments.length; i++) {
        for (var key in arguments[i]) {
            if (arguments[i].hasOwnProperty(key)) {
                arguments[0][key] = arguments[i][key];
            }
        }
    }
    return arguments[0];
}

function module_exists(name) {
    try { return require.resolve(name) }
    catch (e) { return false }
}

function baasicAppConfiguratinProvider() {
    var themeConfigPath = './src/themes/' + theme + '/app.conf.json';
    var rootAppConfig = require('./app.conf.json');
    var themeAppConfig = {};
    if (module_exists(themeConfigPath)) {
        themeAppConfig = require(themeConfigPath);
    }
    return extend({
        apiKey: ''
    }, themeAppConfig, rootAppConfig);
}

function appConfigSource() {
    var appConfig = baasicAppConfiguratinProvider();
    return gulp.src(['./src/app/app.config.js'])
        .pipe(replace('<apiKey>', appConfig.apiKey))
        .pipe(replace('<apiRootUrl>', appConfig.apiRootUrl))
        .pipe(replace('<apiVersion>', appConfig.apiVersion));
};<|endoftext|>


<commit_before>import ext from './ext-finder'
export default ext
<commit_msg>Add args from conole<commit_after>import yargs from 'yargs'
import ext from './ext-finder'
export default ext

let argv = yargs.argv
argv._.map(filename => {
  console.log(ext(filename))
})
<|endoftext|>


<commit_before>// https://github.com/zeit/next.js/blob/master/examples/custom-server-koa/server.js
//

const Koa = require('koa');
const next = require('next');
const Rollbar = require('rollbar');
const send = require('koa-send');

// Server related config & credentials
//
const serverConfig = {
  ROLLBAR_SERVER_TOKEN: process.env.ROLLBAR_SERVER_TOKEN,
  ROLLBAR_ENV: process.env.ROLLBAR_ENV || 'localhost',
  PORT: process.env.PORT || 3000,
};

const enableRollbar = !!serverConfig.ROLLBAR_SERVER_TOKEN;
const rollbar = new Rollbar({
  enabled: enableRollbar,
  captureUncaught: enableRollbar,
  captureUnhandledRejections: enableRollbar,
  accessToken: serverConfig.ROLLBAR_SERVER_TOKEN,
  environment: serverConfig.ROLLBAR_ENV,
});

const app = next({ dev: process.env.NODE_ENV !== 'production' });

const routes = require('./routes');
const handler = routes.getRequestHandler(app);

app.prepare().then(() => {
  const server = new Koa();

  // Rollbar error handling
  //
  server.use(async (ctx, next) => {
    try {
      await next();
    } catch (err) {
      rollbar.error(err, ctx.request);
    }
  });

  //
  server.use(async (ctx, next) => {
    switch (ctx.path) {
      case '/': {
        const lang = ctx.acceptsLanguages(['zh', 'en']) || 'en';
        await send(ctx, `./static/index-${lang}.html`);
        break;
      }
      case '/en':
        await send(ctx, './static/index-en.html');
        break;
      case '/tw':
        await send(ctx, './static/index-zh.html');
        break;
      case '/workshop':
        ctx.redirect(
          'https://via.hypothes.is/https://g0v.hackmd.io/s/r1NGjuG5m'
        );
        break;
      default:
        await next();
    }
  });

  // Server-side routing
  //

  /* Required for hot-reload to work. */
  server.use(async (ctx, next) => {
    ctx.respond = false;
    ctx.res.statusCode = 200;
    handler(ctx.req, ctx.res);
    await next();
  });

  server.listen(serverConfig.PORT, err => {
    if (err) throw err;
      console.log('Listening port', serverConfig.PORT); // eslint-disable-line
  });
});
<commit_msg>Add /analytics to point to datastudio<commit_after>// https://github.com/zeit/next.js/blob/master/examples/custom-server-koa/server.js
//

const Koa = require('koa');
const next = require('next');
const Rollbar = require('rollbar');
const send = require('koa-send');

// Server related config & credentials
//
const serverConfig = {
  ROLLBAR_SERVER_TOKEN: process.env.ROLLBAR_SERVER_TOKEN,
  ROLLBAR_ENV: process.env.ROLLBAR_ENV || 'localhost',
  PORT: process.env.PORT || 3000,
};

const enableRollbar = !!serverConfig.ROLLBAR_SERVER_TOKEN;
const rollbar = new Rollbar({
  enabled: enableRollbar,
  captureUncaught: enableRollbar,
  captureUnhandledRejections: enableRollbar,
  accessToken: serverConfig.ROLLBAR_SERVER_TOKEN,
  environment: serverConfig.ROLLBAR_ENV,
});

const app = next({ dev: process.env.NODE_ENV !== 'production' });

const routes = require('./routes');
const handler = routes.getRequestHandler(app);

app.prepare().then(() => {
  const server = new Koa();

  // Rollbar error handling
  //
  server.use(async (ctx, next) => {
    try {
      await next();
    } catch (err) {
      rollbar.error(err, ctx.request);
    }
  });

  //
  server.use(async (ctx, next) => {
    switch (ctx.path) {
      case '/': {
        const lang = ctx.acceptsLanguages(['zh', 'en']) || 'en';
        await send(ctx, `./static/index-${lang}.html`);
        break;
      }
      case '/en':
        await send(ctx, './static/index-en.html');
        break;
      case '/tw':
        await send(ctx, './static/index-zh.html');
        break;
      case '/workshop':
        ctx.redirect(
          'https://via.hypothes.is/https://g0v.hackmd.io/s/r1NGjuG5m'
        );
        break;
      case '/analytics':
        ctx.redirect(
          'https://datastudio.google.com/u/0/reporting/18J8jZYumsoaCPBk9bdRd97GKvi_W5v-r/page/mVfZ'
        );
        break;
      default:
        await next();
    }
  });

  // Server-side routing
  //

  /* Required for hot-reload to work. */
  server.use(async (ctx, next) => {
    ctx.respond = false;
    ctx.res.statusCode = 200;
    handler(ctx.req, ctx.res);
    await next();
  });

  server.listen(serverConfig.PORT, err => {
    if (err) throw err;
      console.log('Listening port', serverConfig.PORT); // eslint-disable-line
  });
});
<|endoftext|>


<commit_before>import styled, { css } from 'styled-components'
import rem from 'polished/lib/helpers/rem'
import { mobile } from '../utils/media'

export const Container = styled.div`
  padding-left: ${rem(300)};

  ${mobile(css`
    padding-left: 0;
    padding-top: ${rem(70)};
  `)}
`

export const Content = styled.div`
  width: ${rem(1024)};
  max-width: 100%;
  margin: 0 auto;
  padding: ${rem(30)} ${rem(40)};
  box-sizing: border-box;

  ${mobile(css`
    padding: ${rem(30)} ${rem(20)};
  `)}
`

export const Title = styled.h1`
  display: block;
  text-align: left;
  width: 100%;
  color: rgb(243, 182, 97);
  font-size: ${rem(42)};
  font-weight: bold;
`

export const Header = styled.h2`
  font-size: ${rem(32)};
  font-weight: normal;
`

export const SubHeader = styled.h3`
  display: block;
  margin: ${rem(20)} 0 ${rem(12)} 0;
  font-size: ${rem(22)};
  font-weight: normal;
`
<commit_msg>Adjust content margin to match sidebar logo<commit_after>import styled, { css } from 'styled-components'
import rem from 'polished/lib/helpers/rem'
import { mobile } from '../utils/media'

export const Container = styled.div`
  padding-left: ${rem(300)};

  ${mobile(css`
    padding-left: 0;
    padding-top: ${rem(70)};
  `)}
`

export const Content = styled.div`
  width: ${rem(1024)};
  max-width: 100%;
  margin: 0 auto;
  padding: ${rem(50)} ${rem(40)} ${rem(30)} ${rem(40)};
  box-sizing: border-box;

  ${mobile(css`
    padding: ${rem(30)} ${rem(20)};
  `)}
`

export const Title = styled.h1`
  display: block;
  text-align: left;
  width: 100%;
  color: rgb(243, 182, 97);
  font-size: ${rem(42)};
  font-weight: bold;
`

export const Header = styled.h2`
  font-size: ${rem(32)};
  font-weight: normal;
`

export const SubHeader = styled.h3`
  display: block;
  margin: ${rem(20)} 0 ${rem(12)} 0;
  font-size: ${rem(22)};
  font-weight: normal;
`
<|endoftext|>


<commit_before>// Core Javascript file for GSoft.Dynamite

// Chrome-JS intermitted crash fix (otherwise the ribbon breaks in Chrome 9 times out of 10)
if (window.chrome) {
    window[addEventListener ? 'addEventListener' : 'attachEvent'](addEventListener ? 'load' : 'onload', function () {
        try {
            if (_spBodyOnLoadWrapper) {
                _spBodyOnLoadWrapper();
            }
        } catch (e) {
            // console.log("Error while forcing execution of _spBodyOnLoadWrapper " + e);
        }
    });
}

moment.lang('fr', {
    longDateFormat: {
        // Use the Microsoft official fr-CA regional setting date format, for consistency between presentation and SharePoint backend format
        L: "YYYY-MM-DD"
    }
});

moment.lang('en', {
    longDateFormat: {
        // Use the Microsoft official fr-CA regional setting date format, for consistency between presentation and SharePoint backend format
        L: "DD/MM/YYYY"
    }
});

// GSoft namespace root
window.GSoft = window.GSoft || {};

// GSoft.Dynamite namespace root
window.GSoft.Dynamite = window.GSoft.Dynamite || {};

// GSoft.Dynamite Client namespace root
window.GSoft.Dynamite = window.GSoft.Dynamite || {};

// ====================
// Core module
// ====================
(function (Core, $, undefined) {

    Core.initialize = function (params) {

    };
} (GSoft.Dynamite.Core = GSoft.Dynamite.Core || {}, jq110));

// ====================
// Resources module
// ====================
(function(Res, $, undefined) {
    // SharePoint resource files can be fetched through the OOTB ScriptResource.ashx HttpHandler.
    // However, all resource files that get loaded tend to hog the Res namespace exclusivaly,
    // leading to problems when you wanna have multiple resources files loaded on the same page.
    //
    // The solution is that, before applying any KO bindings in your page, call this ensure method
    // which will sequentially load all the pre-req resourfiles (or at least confirm than they were
    // already loaded) then execture your binding-applying snippet of code.
    //
    // For example, IntactNet.Res.ensureResThenExecute(["IFC.IntactNet", "IFC.IntactNet.News"], function () { // apply some bindings here });
    Res.ensureResThenExecute = function(prerequisiteResourceFiles, funcToExecute) {
        innerEnsureResThenExecute(prerequisiteResourceFiles, prerequisiteResourceFiles, funcToExecute);
    };

    function innerEnsureResThenExecute(allPrerequisiteResourceFiles, restOfPrerequisiteResourceFiles, funcToExecute) {
        if (!$.isArray(allPrerequisiteResourceFiles) || allPrerequisiteResourceFiles.length == 0
            || !$.isArray(restOfPrerequisiteResourceFiles) || restOfPrerequisiteResourceFiles.length == 0) {
            throw new Exception("Only call ensureResThenExecute with a non-empty array. E.g. ['IFC.IntactNet', 'IFC.IntactNet.News']");
        }

        if (restOfPrerequisiteResourceFiles.length > 0) {
            // many resource file dependencies: fetch the first then do the tail recursively
            var first = restOfPrerequisiteResourceFiles[0];
            var tail = _.tail(restOfPrerequisiteResourceFiles);

            if (!Res[first]) {
                // this file in particular was never fetched yet, wait for it
                Res[first] = "fetching"; // flag this as "currently fetching" so that we don't attempt to fetch it multiple times (and wait for it to

                $.getScript(formatScriptResxLink(first))
                    .done(function() {
                        // when we load the SharePoint Resx script from its dedicated handler,
                        // it automatically assigns itself to the window.Res global object,
                        // wiping out any previously assigned value (which is why we're maintaining
                        // references to those other resource data in our own Res object).
                        Res[first] = window.Res;
                    })
                    .fail(function() { console.log("Failed to load resource file for module: " + first); });
            }

            if (tail.length > 0) {
                innerEnsureResThenExecute(allPrerequisiteResourceFiles, tail, funcToExecute);
            } else {
                waitUntilAllResourceFilesAreLoadedThenExecute(allPrerequisiteResourceFiles, funcToExecute);
            }
        }
    }

    function waitUntilAllResourceFilesAreLoadedThenExecute(allPrerequisiteResourceFiles, funcToExecute) {
            setTimeout(function() {
                var allLoaded = _.all(allPrerequisiteResourceFiles, function(prereqFileKey) {
                    return Res[prereqFileKey] && Res[prereqFileKey] != "fetching";
                });

                if (allLoaded) {
                    // We're ready, all resource file AJAX calls have come back
                    funcToExecute();
                } else {
                    // still waiting for some resource file to load, try again a bit later
                    waitUntilAllResourceFilesAreLoadedThenExecute(allPrerequisiteResourceFiles, funcToExecute);
                }
            }, 100);
        }

        function formatScriptResxLink(resourceFileName) {
            return GSoft.Dynamite.Utils.CurrentWebUrl + "/_layouts/scriptresx.ashx?culture=" + currentCulture() + "&name=" + resourceFileName;
        }

        function currentCulture() {
            if (_spPageContextInfo.currentLanguage == 1033) {
                return "en-US";
            } else {
                return "fr-FR";
            }
        }

        return Res;
    }

    (GSoft.Dynamite.Res = GSoft.Dynamite.Res || {}, jq110));

    // ====================
    // Edit-mode Metadata Panel module
    // ====================
    (function(MetadataPanel, $, undefined) {
        MetadataPanel.viewModelInstance = null;

        MetadataPanel.initialize = function(tabs) {
            $(document).ready(function() {
                if (MetadataPanel.viewModelInstance == null) {
                    if ($("#metadata-panel").length == 1) {
                        MetadataPanel.viewModelInstance = new MetadataPanel.MetadataPanelViewModel(tabs);

                        // support only one metadata panel per page
                        ko.applyBindings(MetadataPanel.viewModelInstance, $("#metadata-panel")[0]);
                    }
                }
            });
        };

        MetadataPanel.MetadataPanelViewModel = function(tabs) {
            var self = this;

            self.tabs = ko.observableArray(tabs);

            self.findTab = function(tabId) {
                return _.find(self.tabs(), function(oneTab) {
                    return oneTab.id() == tabId;
                });
            };

            self.toggleTab = function(tab) {
                if (!tab.isSelected()) {
                    // un-select all the other tabs then activate the newly selected one
                    _.each(self.tabs(), function(oneOfTheTabs) {
                        oneOfTheTabs.isSelected(false);
                    });

                    tab.isSelected(true);
                } else {
                    // tab was already selected, just hid it (leaving just a row of unselected tabs)
                    tab.isSelected(false);
                }
            };

            self.lastSelectedTab = null;

            self.toggleAllTabs = function() {
                var alreadySelected = _.find(self.tabs(), function(oneTab) {
                    return oneTab.isSelected();
                });

                if (alreadySelected) {
                    // store the selected one until we toggleAll again
                    self.lastSelectedTab = alreadySelected;
                    alreadySelected.isSelected(false);

                    // deselect all
                    _.each(self.tabs(), function(oneOfTheTabs) {
                        oneOfTheTabs.isSelected(false);
                    });
                } else if (self.lastSelectedTab) {
                    // re-select the one that previously was
                    self.lastSelectedTab.isSelected(true);
                } else {
                    // just open the first tab by default
                    self.tabs()[0].isSelected(true);
                }
            };
        };

        MetadataPanel.Tab = function(id, res, isSelected) {
            var self = this;

            self.id = ko.observable(id);
            self.resourceString = ko.observable(res);
            self.isSelected = ko.observable(isSelected);
        };

        return MetadataPanel;
    }(GSoft.Dynamite.MetadataPanel = GSoft.Dynamite.MetadataPanel || {}, jq110));

    // ====================
    // Utils module
    // ====================
    (function(Utils, $, undefined) {
        Utils.CurrentWebUrl = null;
        Utils.ParentFolderUrl = "#";

        Utils.initialize = function(params) {

        };

        Utils.shortenAndEllipsis = function(text, size) {
            if (text != null && text.length > size) {
                text = text.substring(0, size);

                // find the last blank space to cut the text
                text = text.substring(0, text.lastIndexOf(' ')) + '...';
            }

            return text;
        };

        Utils.QueryObject = function() {
            var result = {}, queryString = location.search.slice(1),
                re = /([^&=]+)=([^&]*)/g, m;

            while (m = re.exec(queryString)) {
                result[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
            }

            return result;
        };

        Utils.initializeParentFolderLink = function() {
            if (Utils.ParentFolderUrl.length > 1) {
                ExecuteOrDelayUntilScriptLoaded(addLinkToSiteActions, "sp.js");
            }
        };

        function addLinkToSiteActions() {
            GSoft.Dynamite.Res.ensureResThenExecute(["GSoft.Dynamite"], function() {
                var newLink = $('

');
                var img = newLink.find("img");
                img.attr("src", "/_layouts/GSoft.Dynamite/Img/icon_open_parent.png");
                $(".ms-siteactionscontainer .s4-breadcrumb-anchor").after(newLink);
            });
        };
    }(GSoft.Dynamite.Utils = GSoft.Dynamite.Utils || {}, jq110));

<commit_msg>[StyleCop] Fixed indentation of core js file.<commit_after>// Core Javascript file for GSoft.Dynamite

// Chrome-JS intermitted crash fix (otherwise the ribbon breaks in Chrome 9 times out of 10)
if (window.chrome) {
    window[addEventListener ? 'addEventListener' : 'attachEvent'](addEventListener ? 'load' : 'onload', function () {
        try {
            if (_spBodyOnLoadWrapper) {
                _spBodyOnLoadWrapper();
            }
        } catch (e) {
            // console.log("Error while forcing execution of _spBodyOnLoadWrapper " + e);
        }
    });
}

moment.lang('fr', {
    longDateFormat: {
        // Use the Microsoft official fr-CA regional setting date format, for consistency between presentation and SharePoint backend format
        L: "YYYY-MM-DD"
    }
});

moment.lang('en', {
    longDateFormat: {
        // Use the Microsoft official fr-CA regional setting date format, for consistency between presentation and SharePoint backend format
        L: "DD/MM/YYYY"
    }
});

// GSoft namespace root
window.GSoft = window.GSoft || {};

// GSoft.Dynamite namespace root
window.GSoft.Dynamite = window.GSoft.Dynamite || {};

// GSoft.Dynamite Client namespace root
window.GSoft.Dynamite = window.GSoft.Dynamite || {};

// ====================
// Core module
// ====================
(function (Core, $, undefined) {

    Core.initialize = function (params) {

    };
} (GSoft.Dynamite.Core = GSoft.Dynamite.Core || {}, jq110));

// ====================
// Resources module
// ====================
(function(Res, $, undefined) {
    // SharePoint resource files can be fetched through the OOTB ScriptResource.ashx HttpHandler.
    // However, all resource files that get loaded tend to hog the Res namespace exclusivaly,
    // leading to problems when you wanna have multiple resources files loaded on the same page.
    //
    // The solution is that, before applying any KO bindings in your page, call this ensure method
    // which will sequentially load all the pre-req resourfiles (or at least confirm than they were
    // already loaded) then execture your binding-applying snippet of code.
    //
    // For example, IntactNet.Res.ensureResThenExecute(["IFC.IntactNet", "IFC.IntactNet.News"], function () { // apply some bindings here });
    Res.ensureResThenExecute = function(prerequisiteResourceFiles, funcToExecute) {
        innerEnsureResThenExecute(prerequisiteResourceFiles, prerequisiteResourceFiles, funcToExecute);
    };

    function innerEnsureResThenExecute(allPrerequisiteResourceFiles, restOfPrerequisiteResourceFiles, funcToExecute) {
        if (!$.isArray(allPrerequisiteResourceFiles) || allPrerequisiteResourceFiles.length == 0
            || !$.isArray(restOfPrerequisiteResourceFiles) || restOfPrerequisiteResourceFiles.length == 0) {
            throw new Exception("Only call ensureResThenExecute with a non-empty array. E.g. ['IFC.IntactNet', 'IFC.IntactNet.News']");
        }

        if (restOfPrerequisiteResourceFiles.length > 0) {
            // many resource file dependencies: fetch the first then do the tail recursively
            var first = restOfPrerequisiteResourceFiles[0];
            var tail = _.tail(restOfPrerequisiteResourceFiles);

            if (!Res[first]) {
                // this file in particular was never fetched yet, wait for it
                Res[first] = "fetching"; // flag this as "currently fetching" so that we don't attempt to fetch it multiple times (and wait for it to

                $.getScript(formatScriptResxLink(first))
                    .done(function() {
                        // when we load the SharePoint Resx script from its dedicated handler,
                        // it automatically assigns itself to the window.Res global object,
                        // wiping out any previously assigned value (which is why we're maintaining
                        // references to those other resource data in our own Res object).
                        Res[first] = window.Res;
                    })
                    .fail(function() { console.log("Failed to load resource file for module: " + first); });
            }

            if (tail.length > 0) {
                innerEnsureResThenExecute(allPrerequisiteResourceFiles, tail, funcToExecute);
            } else {
                waitUntilAllResourceFilesAreLoadedThenExecute(allPrerequisiteResourceFiles, funcToExecute);
            }
        }
    }

    function waitUntilAllResourceFilesAreLoadedThenExecute(allPrerequisiteResourceFiles, funcToExecute) {
            setTimeout(function() {
                var allLoaded = _.all(allPrerequisiteResourceFiles, function(prereqFileKey) {
                    return Res[prereqFileKey] && Res[prereqFileKey] != "fetching";
                });

                if (allLoaded) {
                    // We're ready, all resource file AJAX calls have come back
                    funcToExecute();
                } else {
                    // still waiting for some resource file to load, try again a bit later
                    waitUntilAllResourceFilesAreLoadedThenExecute(allPrerequisiteResourceFiles, funcToExecute);
                }
            }, 100);
        }

    function formatScriptResxLink(resourceFileName) {
        return GSoft.Dynamite.Utils.CurrentWebUrl + "/_layouts/scriptresx.ashx?culture=" + currentCulture() + "&name=" + resourceFileName;
    }

    function currentCulture() {
        if (_spPageContextInfo.currentLanguage == 1033) {
            return "en-US";
        } else {
            return "fr-FR";
        }
    }

    return Res;
    } (GSoft.Dynamite.Res = GSoft.Dynamite.Res || {}, jq110));

// ====================
// Edit-mode Metadata Panel module
// ====================
(function(MetadataPanel, $, undefined) {
    MetadataPanel.viewModelInstance = null;

    MetadataPanel.initialize = function(tabs) {
        $(document).ready(function() {
            if (MetadataPanel.viewModelInstance == null) {
                if ($("#metadata-panel").length == 1) {
                    MetadataPanel.viewModelInstance = new MetadataPanel.MetadataPanelViewModel(tabs);

                    // support only one metadata panel per page
                    ko.applyBindings(MetadataPanel.viewModelInstance, $("#metadata-panel")[0]);
                }
            }
        });
    };

    MetadataPanel.MetadataPanelViewModel = function(tabs) {
        var self = this;

        self.tabs = ko.observableArray(tabs);

        self.findTab = function(tabId) {
            return _.find(self.tabs(), function(oneTab) {
                return oneTab.id() == tabId;
            });
        };

        self.toggleTab = function(tab) {
            if (!tab.isSelected()) {
                // un-select all the other tabs then activate the newly selected one
                _.each(self.tabs(), function(oneOfTheTabs) {
                    oneOfTheTabs.isSelected(false);
                });

                tab.isSelected(true);
            } else {
                // tab was already selected, just hid it (leaving just a row of unselected tabs)
                tab.isSelected(false);
            }
        };

        self.lastSelectedTab = null;

        self.toggleAllTabs = function() {
            var alreadySelected = _.find(self.tabs(), function(oneTab) {
                return oneTab.isSelected();
            });

            if (alreadySelected) {
                // store the selected one until we toggleAll again
                self.lastSelectedTab = alreadySelected;
                alreadySelected.isSelected(false);

                // deselect all
                _.each(self.tabs(), function(oneOfTheTabs) {
                    oneOfTheTabs.isSelected(false);
                });
            } else if (self.lastSelectedTab) {
                // re-select the one that previously was
                self.lastSelectedTab.isSelected(true);
            } else {
                // just open the first tab by default
                self.tabs()[0].isSelected(true);
            }
        };
    };

    MetadataPanel.Tab = function(id, res, isSelected) {
        var self = this;

        self.id = ko.observable(id);
        self.resourceString = ko.observable(res);
        self.isSelected = ko.observable(isSelected);
    };

    return MetadataPanel;
}(GSoft.Dynamite.MetadataPanel = GSoft.Dynamite.MetadataPanel || {}, jq110));

// ====================
// Utils module
// ====================
(function(Utils, $, undefined) {
    Utils.CurrentWebUrl = null;
    Utils.ParentFolderUrl = "#";

    Utils.initialize = function(params) {

    };

    Utils.shortenAndEllipsis = function(text, size) {
        if (text != null && text.length > size) {
            text = text.substring(0, size);

            // find the last blank space to cut the text
            text = text.substring(0, text.lastIndexOf(' ')) + '...';
        }

        return text;
    };

    Utils.QueryObject = function() {
        var result = {}, queryString = location.search.slice(1),
            re = /([^&=]+)=([^&]*)/g, m;

        while (m = re.exec(queryString)) {
            result[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
        }

        return result;
    };

    Utils.initializeParentFolderLink = function() {
        if (Utils.ParentFolderUrl.length > 1) {
            ExecuteOrDelayUntilScriptLoaded(addLinkToSiteActions, "sp.js");
        }
    };

    function addLinkToSiteActions() {
        GSoft.Dynamite.Res.ensureResThenExecute(["GSoft.Dynamite"], function() {
            var newLink = $('

');
            var img = newLink.find("img");
            img.attr("src", "/_layouts/GSoft.Dynamite/Img/icon_open_parent.png");
            $(".ms-siteactionscontainer .s4-breadcrumb-anchor").after(newLink);
        });
    };
}(GSoft.Dynamite.Utils = GSoft.Dynamite.Utils || {}, jq110));

<|endoftext|>