#!/usr/bin/env node

/* eslint-disable no-console */
const fs = require('fs');
const path = require('path');
const { exec } = require('child_process');

function main() {
  const args = process.argv;
  const componentName = args[2];

  if (componentName === undefined) {
    console.error('Component name is required');
    process.exit(1);
  }

  addFiles(componentName);
  exec(path.resolve(__dirname, './cruiser.sh'));
}

main();

function firstAlphabetUpper(str) {
  return str.substring(0, 1).toUpperCase() + str.substring(1);
}

function getComponentName(name) {
  let componentName;
  if (name.includes('-')) {
    componentName = name
      .split('-')
      .map(item => {
        return firstAlphabetUpper(item);
      })
      .join('');
  } else {
    componentName = firstAlphabetUpper(name);
  }
  return componentName;
}

function sortByModulePath(lines) {
  lines.sort((a, b) => {
    const modPathRegexp = /'.+'/;
    const modA = modPathRegexp.exec(a)[0];
    const modB = modPathRegexp.exec(b)[0];

    return modA.localeCompare(modB);
  });

  return lines;
}

// ts
function insertTs(name) {
  const tsIndexPath = path.resolve(__dirname, '../src/index.ts');
  const tsIndexFile = fs.readFileSync(tsIndexPath, { encoding: 'utf-8' });

  const moduleExports = tsIndexFile.trim().split('\n');
  moduleExports.push(`export * from './${name}';`);
  sortByModulePath(moduleExports);

  fs.writeFileSync(tsIndexPath, `${moduleExports.join('\n')}\n`);
}

// style
function insertCss(name) {
  const cssIndexPath = path.resolve(__dirname, '../assets/index.scss');
  const cssIndexFile = fs.readFileSync(cssIndexPath, { encoding: 'utf-8' });
  const cssImportStr = `@import './${name}';`;

  const cssIndexFileArr = cssIndexFile.trim().split('\n');
  cssIndexFileArr.push(cssImportStr);

  // Make sure base comes first
  const base = cssIndexFileArr.splice(0, 1);
  sortByModulePath(cssIndexFileArr);
  cssIndexFileArr.unshift(base);

  fs.writeFileSync(cssIndexPath, `${cssIndexFileArr.join('\n')}\n`);
}

// js/css 加到index文件导出
function addFilesToIndex(name) {
  insertCss(name);
  insertTs(name);
}

// 创建组件文件夹以及js/css文件
function addFiles(name) {
  const packagesDir = path.resolve(__dirname, '../src');
  const assetsDir = path.resolve(__dirname, '../assets');
  const componentDir = `${packagesDir}/${name}`;
  const upperComponentName = getComponentName(name);

  console.log(`Adding new component：${upperComponentName}`);

  if (!fs.existsSync(componentDir)) {
    fs.mkdirSync(componentDir);
  } else {
    console.log(
      `${upperComponentName} already exists, please choose another name.`
    );
    process.exit(2);
  }

  fs.writeFileSync(
    `${componentDir}/index.ts`,
    `import ${upperComponentName} from './${upperComponentName}';\n\nexport * from './${upperComponentName}';\n`
  );

  fs.writeFileSync(
    `${componentDir}/README_en-US.md`,
    `---
title: ${upperComponentName}
path: component/${name}
group: FIXME group name here
---

## ${upperComponentName}

Describe ${upperComponentName}.

### API

| Property     |  Description  | Type     |  Required  |   Default  | Alternative   |
| ------------ | -------------- | -------- | ---------- | ---------- | ------------- |
| className    | Custom class name  | string     |  No        |  |  |`
  );

  fs.writeFileSync(
    `${componentDir}/README_zh-CN.md`,
    `---
title: ${upperComponentName}
subtitle: FIXME 中文名
path: component/${name}
group: FIXME 中文分组名
---

## ${upperComponentName}

简单描述 ${upperComponentName} 组件。

### API

| 参数        |   说明       | 类型     | 是否必须    | 默认值      | 备选值     |
| ------------| ----------- | -------- | ---------- | ---------- | ---------- |
| className   | 额外类名     | string   |  否         |           |            |
`
  );
  fs.writeFileSync(`${componentDir}/${upperComponentName}.tsx`, '');
  fs.writeFileSync(`${assetsDir}/${name}.scss`, '');

  addFilesToIndex(name);
}


#!/usr/bin/env node

/* eslint-disable */

var fs = require('fs');
var path = require('path');

// 为src目录下的所有子目录创建alias
function createMapper() {
  var packagesDir = path.resolve(__dirname, '../src');
  var packages = fs.readdirSync(packagesDir);

  return packages
    .filter(p => fs.statSync(path.join(packagesDir, p)).isDirectory())
    .reduce((alias, p) => {
      alias[`^${p}$`] = `<rootDir>/src/${p}`;
      alias[`^${p}/(.+)$`] = `<rootDir>/src/${p}/$1`;
      return alias;
    }, {});
}

var jestConfig = require('../jest.config.json');
jestConfig.moduleNameMapper = createMapper();

fs.writeFileSync(
  require.resolve('../jest.config.json'),
  JSON.stringify(jestConfig, null, '  ')
);


(function($) {
  "use strict"; // Start of use strict

  // Closes the sidebar menu
  $(".menu-toggle").click(function(e) {
    e.preventDefault();
    $("#sidebar-wrapper").toggleClass("active");
    $(".menu-toggle > .fa-bars, .menu-toggle > .fa-times").toggleClass("fa-bars fa-times");
    $(this).toggleClass("active");
  });

  // Smooth scrolling using jQuery easing
  $('a.js-scroll-trigger[href*="#"]:not([href="#"])').click(function() {
    if (location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') && location.hostname == this.hostname) {
      var target = $(this.hash);
      target = target.length ? target : $('[name=' + this.hash.slice(1) + ']');
      if (target.length) {
        $('html, body').animate({
          scrollTop: target.offset().top
        }, 1000, "easeInOutExpo");
        return false;
      }
    }
  });

  // Closes responsive menu when a scroll trigger link is clicked
  $('#sidebar-wrapper .js-scroll-trigger').click(function() {
    $("#sidebar-wrapper").removeClass("active");
    $(".menu-toggle").removeClass("active");
    $(".menu-toggle > .fa-bars, .menu-toggle > .fa-times").toggleClass("fa-bars fa-times");
  });

  // Scroll to top button appear
  $(document).scroll(function() {
    var scrollDistance = $(this).scrollTop();
    if (scrollDistance > 100) {
      $('.scroll-to-top').fadeIn();
    } else {
      $('.scroll-to-top').fadeOut();
    }
  });

})(jQuery); // End of use strict

// Disable Google Maps scrolling
// See http://stackoverflow.com/a/25904582/1607849
// Disable scroll zooming and bind back the click event
var onMapMouseleaveHandler = function(event) {
  var that = $(this);
  that.on('click', onMapClickHandler);
  that.off('mouseleave', onMapMouseleaveHandler);
  that.find('iframe').css("pointer-events", "none");
}
var onMapClickHandler = function(event) {
  var that = $(this);
  // Disable the click handler until the user leaves the map area
  that.off('click', onMapClickHandler);
  // Enable scrolling zoom
  that.find('iframe').css("pointer-events", "auto");
  // Handle the mouse leave event
  that.on('mouseleave', onMapMouseleaveHandler);
}
// Enable map zooming with mouse scroll when the user clicks the map
$('.map').on('click', onMapClickHandler);


export default function sidebar(options) {
  const {headersContainer, sidebarContainer} = options;

  const list = document.createElement('ul');
  list.classList.add('no-mobile');

  sidebarContainer.appendChild(list);
  sidebarFollowScroll(sidebarContainer.firstChild);
  activeLinks(sidebarContainer);
  scrollSpy(sidebarContainer, headersContainer);
}

function sidebarFollowScroll(sidebarContainer) {
  const linksContainer = sidebarContainer.querySelector('ul');
  const {height, navHeight, footerHeight, menuHeight, sidebarTop, titleHeight} =
    getPositionsKeyElements(sidebarContainer);
  const positionSidebar = () => {
    const currentScroll = window.pageYOffset;
    if (currentScroll > sidebarTop - navHeight) {
      const fold = height - footerHeight - menuHeight - navHeight;
      if (currentScroll > fold) {
        sidebarContainer.style.top = `${(fold - currentScroll + navHeight ) - 200}px`;
      } else {
        sidebarContainer.style.top = null;
      }
      sidebarContainer.classList.add('fixed');
      linksContainer.style.maxHeight = `calc(100vh - ${titleHeight + navHeight}px)`;
    } else {
      sidebarContainer.classList.remove('fixed');
      linksContainer.style.maxHeight = '';
    }
  };

  window.addEventListener('load', positionSidebar);
  document.addEventListener('DOMContentLoaded', positionSidebar);
  document.addEventListener('scroll', positionSidebar);
}

function scrollSpy(sidebarContainer, headersContainer) {
  const headers = [...headersContainer.querySelectorAll('h2')];

  const setActiveSidebarLink = header => {
    [...sidebarContainer.querySelectorAll('a')].forEach(item => {
      const currentHref = item.getAttribute('href');
      const anchorToFind = `#${header.getAttribute('id')}`;
      const isCurrentHeader =
        currentHref.indexOf(anchorToFind) ===
        currentHref.length - anchorToFind.length;
      if (isCurrentHeader) {
        item.classList.add('active');
      } else {
        item.classList.remove('active');
      }
    });
  };

  const findActiveSidebarLink = () => {
    const highestVisibleHeaders = headers
      .map(header => ({element: header, rect: header.getBoundingClientRect()}))
      .filter(({rect}) =>
        rect.top < window.innerHeight / 3 && rect.bottom < window.innerHeight
        // top element relative viewport position should be at least 1/3 viewport
        // and element should be in viewport
      )
      // then we take the closest to this position as reference
      .sort((header1, header2) => Math.abs(header1.rect.top) < Math.abs(header2.rect.top) ? -1 : 1);

    if (headers[0] && highestVisibleHeaders.length === 0) {
      setActiveSidebarLink(headers[0]);
      return;
    }

    if (highestVisibleHeaders[0]) {
      setActiveSidebarLink(highestVisibleHeaders[0].element);
    }
  };

  findActiveSidebarLink();
  window.addEventListener('load', findActiveSidebarLink);
  document.addEventListener('DOMContentLoaded', findActiveSidebarLink);
  document.addEventListener('scroll', findActiveSidebarLink);
}

// The Following code is used to set active items
// On the documentation sidebar depending on the
// clicked item
function activeLinks(sidebarContainer) {
  const linksContainer = sidebarContainer.querySelector('ul');

  linksContainer.addEventListener('click', e => {
    if (e.target.tagName === 'A') {
      [...linksContainer.querySelectorAll('a')].forEach(item => item.classList.remove('active'));
      e.target.classList.add('active');
    }
  });
}
// The Following function will make the '.sidebar-opener'
// clickable and it will open/close the sidebar on the
// documentations

function toggleDocumentationSidebar() {
  const sidebarNav = document.querySelector('nav.sidebar');
  const trigger = document.querySelector('.sidebar-opener');

  function init() {
    const bodySize = document.body.clientWidth;
    if (bodySize <= 960 && sidebarNav) {
      trigger.addEventListener('click', () => {
        sidebarNav.classList.toggle('Showed');
        trigger.classList.toggle('Showed');
      });
    }
  }
  init();
}
toggleDocumentationSidebar();

window.addEventListener('resize', () => {
  toggleDocumentationSidebar();
});

function getPositionsKeyElements($sidebar) {
  const sidebarBBox = $sidebar.getBoundingClientRect();
  const title = $sidebar.querySelector('.sidebar-header');
  const bodyBBox = document.body.getBoundingClientRect();
  const sidebarTop = sidebarBBox.top - bodyBBox.top;
  const footer = document.querySelector('#footer');
  const navigation = document.querySelector('.algc-navigation');
  const menu = document.querySelector('.sidebar-container');
  const height = document.querySelector('html').getBoundingClientRect().height;
  const navHeight = navigation.offsetHeight;
  const footerHeight = footer.offsetHeight;
  const menuHeight = menu.offsetHeight;
  const titleHeight = title.offsetHeight;

  return {sidebarTop, height, navHeight, footerHeight, menuHeight, titleHeight};
}


// extracted from https://github.com/treygriffith/metalsmith-assets
// converted to es6 (http://lebab.io/try-it)
// tweaked to add `stats` to the file object

import fs from 'fs';
import path from 'path';
import readdir from 'recursive-readdir';
import mode from 'stat-mode';
import {each} from 'async';

/**
 * Expose `assets`.
 */

export default assets;

/**
 * Default plugin options
 */
const defaults = {
  source: './public',
  destination: '.',
};

/**
 * Metalsmith plugin to include static assets.
 *
 * @param {Object} userOptions (optional)
 *   @property {String} source Path to copy static assets from (relative to working directory). Defaults to './public'
 *   @property {String} destination Path to copy static assets to (relative to destination directory). Defaults to '.'
 * @return {Function} a Metalsmith plugin
 */
function assets(userOptions = {}) {
  const options = {
    ...defaults,
    ...userOptions,
  };

  return (files, metalsmith, cb) => {
    const src = metalsmith.path(options.source);
    const dest = options.destination;

    // copied almost line for line from https://github.com/segmentio/metalsmith/blob/master/lib/index.js
    readdir(src, (readDirError, arr) => {
      if (readDirError) {
        cb(readDirError);
        return;
      }

      each(arr, read, err => cb(err, files));
    });

    function read(file, done) {
      const name = path.join(dest, path.relative(src, file));
      fs.stat(file, (statError, stats) => {
        if (statError) {
          done(statError);
          return;
        }

        fs.readFile(file, (err, buffer) => {
          if (err) {
            done(err);
            return;
          }

          const newFile = {};

          newFile.contents = buffer;
          newFile.stats = stats;

          newFile.mode = mode(stats).toOctal();
          files[name] = newFile;
          done();
        });
      });
    }
  };
}


/**
 * Fixes a sidebar in the boundaries of its parent
 * @param {object} $0 options to init the fixed sidebar
 * @param {HTMLElement} $0.sidebarContainer the holder of the menu
 * @param {topOffset} $0.topOffset an optional top offset for sticky menu
 */
export function fixSidebar({sidebarContainer, topOffset}) {
  const siderbarParent = sidebarContainer.parentElement;
  const boundaries = getStartStopBoundaries(siderbarParent, sidebarContainer, topOffset);

  siderbarParent.style.position = 'relative';

  const positionSidebar = () => {
    const currentScroll = window.pageYOffset;
    const {start, stop} = boundaries;
    if(currentScroll > boundaries.start) {
      if(currentScroll > boundaries.stop) {
        sidebarContainer.style.position = 'absolute';
        sidebarContainer.style.bottom = `0`;
        sidebarContainer.classList.remove('fixed');
      } else {
        sidebarContainer.style.position = null;
        sidebarContainer.style.bottom = null;
        sidebarContainer.classList.add('fixed');
      }
    } else {
      sidebarContainer.classList.remove('fixed');
    }
  };

  window.addEventListener('load', positionSidebar);
  document.addEventListener('DOMContentLoaded', positionSidebar);
  document.addEventListener('scroll', positionSidebar);
}

/**
 * Defines the limits where to start or stop the stickiness
 * @param {HTMLElement} parent the outer container of the sidebar
 * @param {HTMLElement} sidebar the sidebar
 * @param {number} topOffset an optional top offset for sticky menu
 */
function getStartStopBoundaries(parent, sidebar, topOffset) {
  const bbox = parent.getBoundingClientRect();
  const sidebarBbox = sidebar.getBoundingClientRect();
  const bodyBbox = document.body.getBoundingClientRect();

  const containerAbsoluteTop = bbox.top - bodyBbox.top;
  const sidebarAbsoluteTop = sidebarBbox.top - bodyBbox.top;
  const marginTop = sidebarAbsoluteTop - containerAbsoluteTop;
  const start = containerAbsoluteTop - topOffset;
  const stop = bbox.height + containerAbsoluteTop - sidebarBbox.height - marginTop - topOffset;

  return {
    start,
    stop,
  };
}

export function followSidebarNavigation(sidebarLinks, contentHeaders) {
  const links = [...sidebarLinks];
  const headers = [...contentHeaders];

  const setActiveSidebarLink = header => {
    links.forEach(item => {
      const currentHref = item.getAttribute('href');
      const anchorToFind = `#${header.getAttribute('id')}`;
      const isCurrentHeader =
        currentHref.indexOf(anchorToFind) != -1;
      if (isCurrentHeader) {
        item.classList.add('navItem-active');
      } else {
        item.classList.remove('navItem-active');
      }
    });
  };

  const findActiveSidebarLink = () => {
    const highestVisibleHeaders = headers
      .map(header => ({element: header, rect: header.getBoundingClientRect()}))
      .filter(({rect}) =>
        rect.top < window.innerHeight / 3 && rect.bottom < window.innerHeight
        // top element relative viewport position should be at least 1/3 viewport
        // and element should be in viewport
      )
      // then we take the closest to this position as reference
      .sort((header1, header2) => Math.abs(header1.rect.top) < Math.abs(header2.rect.top) ? -1 : 1);

    if (headers[0] && highestVisibleHeaders.length === 0) {
      setActiveSidebarLink(headers[0]);
      return;
    }

    if (highestVisibleHeaders[0]) {
      setActiveSidebarLink(highestVisibleHeaders[0].element);
    }
  };

  findActiveSidebarLink();
  window.addEventListener('load', findActiveSidebarLink);
  document.addEventListener('DOMContentLoaded', findActiveSidebarLink);
  document.addEventListener('scroll', findActiveSidebarLink);
}


#!/usr/bin/env node

var ejs = require('ejs')
var fs = require('fs')
var minimatch = require('minimatch')
var mkdirp = require('mkdirp')
var path = require('path')
var program = require('commander')
var readline = require('readline')
var sortedObject = require('sorted-object')
var util = require('util')

var MODE_0666 = parseInt('0666', 8)
var MODE_0755 = parseInt('0755', 8)
var TEMPLATE_DIR = path.join(__dirname, '..', 'templates')
var VERSION = require('../package').version

var _exit = process.exit

// Re-assign process.exit because of commander
// TODO: Switch to a different command framework
process.exit = exit

// CLI

around(program, 'optionMissingArgument', function (fn, args) {
  program.outputHelp()
  fn.apply(this, args)
  return { args: [], unknown: [] }
})

before(program, 'outputHelp', function () {
  // track if help was shown for unknown option
  this._helpShown = true
})

before(program, 'unknownOption', function () {
  // allow unknown options if help was shown, to prevent trailing error
  this._allowUnknownOption = this._helpShown

  // show help if not yet shown
  if (!this._helpShown) {
    program.outputHelp()
  }
})

program
  .name('express')
  .version(VERSION, '    --version')
  .usage('[options] [dir]')
  .option('-e, --ejs', 'add ejs engine support', renamedOption('--ejs', '--view=ejs'))
  .option('    --pug', 'add pug engine support', renamedOption('--pug', '--view=pug'))
  .option('    --hbs', 'add handlebars engine support', renamedOption('--hbs', '--view=hbs'))
  .option('-H, --hogan', 'add hogan.js engine support', renamedOption('--hogan', '--view=hogan'))
  .option('-v, --view <engine>', 'add view <engine> support (dust|ejs|hbs|hjs|jade|pug|twig|vash) (defaults to jade)')
  .option('    --no-view', 'use static html instead of view engine')
  .option('-c, --css <engine>', 'add stylesheet <engine> support (less|stylus|compass|sass) (defaults to plain css)')
  .option('    --git', 'add .gitignore')
  .option('-f, --force', 'force on non-empty directory')
  .parse(process.argv)

if (!exit.exited) {
  main()
}

/**
 * Install an around function; AOP.
 */

function around (obj, method, fn) {
  var old = obj[method]

  obj[method] = function () {
    var args = new Array(arguments.length)
    for (var i = 0; i < args.length; i++) args[i] = arguments[i]
    return fn.call(this, old, args)
  }
}

/**
 * Install a before function; AOP.
 */

function before (obj, method, fn) {
  var old = obj[method]

  obj[method] = function () {
    fn.call(this)
    old.apply(this, arguments)
  }
}

/**
 * Prompt for confirmation on STDOUT/STDIN
 */

function confirm (msg, callback) {
  var rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
  })

  rl.question(msg, function (input) {
    rl.close()
    callback(/^y|yes|ok|true$/i.test(input))
  })
}

/**
 * Copy file from template directory.
 */

function copyTemplate (from, to) {
  write(to, fs.readFileSync(path.join(TEMPLATE_DIR, from), 'utf-8'))
}

/**
 * Copy multiple files from template directory.
 */

function copyTemplateMulti (fromDir, toDir, nameGlob) {
  fs.readdirSync(path.join(TEMPLATE_DIR, fromDir))
    .filter(minimatch.filter(nameGlob, { matchBase: true }))
    .forEach(function (name) {
      copyTemplate(path.join(fromDir, name), path.join(toDir, name))
    })
}

/**
 * Create application at the given directory.
 *
 * @param {string} name
 * @param {string} dir
 */

function createApplication (name, dir) {
  console.log()

  // Package
  var pkg = {
    name: name,
    version: '0.0.0',
    private: true,
    scripts: {
      start: 'node ./bin/www'
    },
    dependencies: {
      'debug': '~2.6.9',
      'express': '~4.16.1'
    }
  }

  // JavaScript
  var app = loadTemplate('js/app.js')
  var www = loadTemplate('js/www')

  // App name
  www.locals.name = name

  // App modules
  app.locals.localModules = Object.create(null)
  app.locals.modules = Object.create(null)
  app.locals.mounts = []
  app.locals.uses = []

  // Request logger
  app.locals.modules.logger = 'morgan'
  app.locals.uses.push("logger('dev')")
  pkg.dependencies.morgan = '~1.9.1'

  // Body parsers
  app.locals.uses.push('express.json()')
  app.locals.uses.push('express.urlencoded({ extended: false })')

  // Cookie parser
  app.locals.modules.cookieParser = 'cookie-parser'
  app.locals.uses.push('cookieParser()')
  pkg.dependencies['cookie-parser'] = '~1.4.4'

  if (dir !== '.') {
    mkdir(dir, '.')
  }

  mkdir(dir, 'public')
  mkdir(dir, 'public/javascripts')
  mkdir(dir, 'public/images')
  mkdir(dir, 'public/stylesheets')

  // copy css templates
  switch (program.css) {
    case 'less':
      copyTemplateMulti('css', dir + '/public/stylesheets', '*.less')
      break
    case 'stylus':
      copyTemplateMulti('css', dir + '/public/stylesheets', '*.styl')
      break
    case 'compass':
      copyTemplateMulti('css', dir + '/public/stylesheets', '*.scss')
      break
    case 'sass':
      copyTemplateMulti('css', dir + '/public/stylesheets', '*.sass')
      break
    default:
      copyTemplateMulti('css', dir + '/public/stylesheets', '*.css')
      break
  }

  // copy route templates
  mkdir(dir, 'routes')
  copyTemplateMulti('js/routes', dir + '/routes', '*.js')

  if (program.view) {
    // Copy view templates
    mkdir(dir, 'views')
    pkg.dependencies['http-errors'] = '~1.6.3'
    switch (program.view) {
      case 'dust':
        copyTemplateMulti('views', dir + '/views', '*.dust')
        break
      case 'ejs':
        copyTemplateMulti('views', dir + '/views', '*.ejs')
        break
      case 'hbs':
        copyTemplateMulti('views', dir + '/views', '*.hbs')
        break
      case 'hjs':
        copyTemplateMulti('views', dir + '/views', '*.hjs')
        break
      case 'jade':
        copyTemplateMulti('views', dir + '/views', '*.jade')
        break
      case 'pug':
        copyTemplateMulti('views', dir + '/views', '*.pug')
        break
      case 'twig':
        copyTemplateMulti('views', dir + '/views', '*.twig')
        break
      case 'vash':
        copyTemplateMulti('views', dir + '/views', '*.vash')
        break
    }
  } else {
    // Copy extra public files
    copyTemplate('js/index.html', path.join(dir, 'public/index.html'))
  }

  // CSS Engine support
  switch (program.css) {
    case 'compass':
      app.locals.modules.compass = 'node-compass'
      app.locals.uses.push("compass({ mode: 'expanded' })")
      pkg.dependencies['node-compass'] = '0.2.3'
      break
    case 'less':
      app.locals.modules.lessMiddleware = 'less-middleware'
      app.locals.uses.push("lessMiddleware(path.join(__dirname, 'public'))")
      pkg.dependencies['less-middleware'] = '~2.2.1'
      break
    case 'sass':
      app.locals.modules.sassMiddleware = 'node-sass-middleware'
      app.locals.uses.push("sassMiddleware({\n  src: path.join(__dirname, 'public'),\n  dest: path.join(__dirname, 'public'),\n  indentedSyntax: true, // true = .sass and false = .scss\n  sourceMap: true\n})")
      pkg.dependencies['node-sass-middleware'] = '0.11.0'
      break
    case 'stylus':
      app.locals.modules.stylus = 'stylus'
      app.locals.uses.push("stylus.middleware(path.join(__dirname, 'public'))")
      pkg.dependencies['stylus'] = '0.54.5'
      break
  }

  // Index router mount
  app.locals.localModules.indexRouter = './routes/index'
  app.locals.mounts.push({ path: '/', code: 'indexRouter' })

  // User router mount
  app.locals.localModules.usersRouter = './routes/users'
  app.locals.mounts.push({ path: '/users', code: 'usersRouter' })

  // Template support
  switch (program.view) {
    case 'dust':
      app.locals.modules.adaro = 'adaro'
      app.locals.view = {
        engine: 'dust',
        render: 'adaro.dust()'
      }
      pkg.dependencies.adaro = '~1.0.4'
      break
    case 'ejs':
      app.locals.view = { engine: 'ejs' }
      pkg.dependencies.ejs = '~2.6.1'
      break
    case 'hbs':
      app.locals.view = { engine: 'hbs' }
      pkg.dependencies.hbs = '~4.0.4'
      break
    case 'hjs':
      app.locals.view = { engine: 'hjs' }
      pkg.dependencies.hjs = '~0.0.6'
      break
    case 'jade':
      app.locals.view = { engine: 'jade' }
      pkg.dependencies.jade = '~1.11.0'
      break
    case 'pug':
      app.locals.view = { engine: 'pug' }
      pkg.dependencies.pug = '2.0.0-beta11'
      break
    case 'twig':
      app.locals.view = { engine: 'twig' }
      pkg.dependencies.twig = '~0.10.3'
      break
    case 'vash':
      app.locals.view = { engine: 'vash' }
      pkg.dependencies.vash = '~0.12.6'
      break
    default:
      app.locals.view = false
      break
  }

  // Static files
  app.locals.uses.push("express.static(path.join(__dirname, 'public'))")

  if (program.git) {
    copyTemplate('js/gitignore', path.join(dir, '.gitignore'))
  }

  // sort dependencies like npm(1)
  pkg.dependencies = sortedObject(pkg.dependencies)

  // write files
  write(path.join(dir, 'app.js'), app.render())
  write(path.join(dir, 'package.json'), JSON.stringify(pkg, null, 2) + '\n')
  mkdir(dir, 'bin')
  write(path.join(dir, 'bin/www'), www.render(), MODE_0755)

  var prompt = launchedFromCmd() ? '>' : '$'

  if (dir !== '.') {
    console.log()
    console.log('   change directory:')
    console.log('     %s cd %s', prompt, dir)
  }

  console.log()
  console.log('   install dependencies:')
  console.log('     %s npm install', prompt)
  console.log()
  console.log('   run the app:')

  if (launchedFromCmd()) {
    console.log('     %s SET DEBUG=%s:* & npm start', prompt, name)
  } else {
    console.log('     %s DEBUG=%s:* npm start', prompt, name)
  }

  console.log()
}

/**
 * Create an app name from a directory path, fitting npm naming requirements.
 *
 * @param {String} pathName
 */

function createAppName (pathName) {
  return path.basename(pathName)
    .replace(/[^A-Za-z0-9.-]+/g, '-')
    .replace(/^[-_.]+|-+$/g, '')
    .toLowerCase()
}

/**
 * Check if the given directory `dir` is empty.
 *
 * @param {String} dir
 * @param {Function} fn
 */

function emptyDirectory (dir, fn) {
  fs.readdir(dir, function (err, files) {
    if (err && err.code !== 'ENOENT') throw err
    fn(!files || !files.length)
  })
}

/**
 * Graceful exit for async STDIO
 */

function exit (code) {
  // flush output for Node.js Windows pipe bug
  // https://github.com/joyent/node/issues/6247 is just one bug example
  // https://github.com/visionmedia/mocha/issues/333 has a good discussion
  function done () {
    if (!(draining--)) _exit(code)
  }

  var draining = 0
  var streams = [process.stdout, process.stderr]

  exit.exited = true

  streams.forEach(function (stream) {
    // submit empty write request and wait for completion
    draining += 1
    stream.write('', done)
  })

  done()
}

/**
 * Determine if launched from cmd.exe
 */

function launchedFromCmd () {
  return process.platform === 'win32' &&
    process.env._ === undefined
}

/**
 * Load template file.
 */

function loadTemplate (name) {
  var contents = fs.readFileSync(path.join(__dirname, '..', 'templates', (name + '.ejs')), 'utf-8')
  var locals = Object.create(null)

  function render () {
    return ejs.render(contents, locals, {
      escape: util.inspect
    })
  }

  return {
    locals: locals,
    render: render
  }
}

/**
 * Main program.
 */

function main () {
  // Path
  var destinationPath = program.args.shift() || '.'

  // App name
  var appName = createAppName(path.resolve(destinationPath)) || 'hello-world'

  // View engine
  if (program.view === true) {
    if (program.ejs) program.view = 'ejs'
    if (program.hbs) program.view = 'hbs'
    if (program.hogan) program.view = 'hjs'
    if (program.pug) program.view = 'pug'
  }

  // Default view engine
  if (program.view === true) {
    warning('the default view engine will not be jade in future releases\n' +
      "use `--view=jade' or `--help' for additional options")
    program.view = 'jade'
  }

  // Generate application
  emptyDirectory(destinationPath, function (empty) {
    if (empty || program.force) {
      createApplication(appName, destinationPath)
    } else {
      confirm('destination is not empty, continue? [y/N] ', function (ok) {
        if (ok) {
          process.stdin.destroy()
          createApplication(appName, destinationPath)
        } else {
          console.error('aborting')
          exit(1)
        }
      })
    }
  })
}

/**
 * Make the given dir relative to base.
 *
 * @param {string} base
 * @param {string} dir
 */

function mkdir (base, dir) {
  var loc = path.join(base, dir)

  console.log('   \x1b[36mcreate\x1b[0m : ' + loc + path.sep)
  mkdirp.sync(loc, MODE_0755)
}

/**
 * Generate a callback function for commander to warn about renamed option.
 *
 * @param {String} originalName
 * @param {String} newName
 */

function renamedOption (originalName, newName) {
  return function (val) {
    warning(util.format("option `%s' has been renamed to `%s'", originalName, newName))
    return val
  }
}

/**
 * Display a warning similar to how errors are displayed by commander.
 *
 * @param {String} message
 */

function warning (message) {
  console.error()
  message.split('\n').forEach(function (line) {
    console.error('  warning: %s', line)
  })
  console.error()
}

/**
 * echo str > file.
 *
 * @param {String} file
 * @param {String} str
 */

function write (file, str, mode) {
  fs.writeFileSync(file, str, { mode: mode || MODE_0666 })
  console.log('   \x1b[36mcreate\x1b[0m : ' + file)
}


/*!
 * reactjs-popup v1.3.2
 * (c) 2019-present Youssouf EL AZIZI <youssoufelazizi@gmail.com>
 * Released under the MIT License.
 */
(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('react'), require('react-dom')) :
  typeof define === 'function' && define.amd ? define(['react', 'react-dom'], factory) :
  (global.reactjsPopup = factory(global.React,global.reactDom));
}(this, (function (React,reactDom) { 'use strict';

  React = React && React.hasOwnProperty('default') ? React['default'] : React;

  function _classCallCheck(instance, Constructor) {
    if (!(instance instanceof Constructor)) {
      throw new TypeError("Cannot call a class as a function");
    }
  }

  function _defineProperties(target, props) {
    for (var i = 0; i < props.length; i++) {
      var descriptor = props[i];
      descriptor.enumerable = descriptor.enumerable || false;
      descriptor.configurable = true;
      if ("value" in descriptor) descriptor.writable = true;
      Object.defineProperty(target, descriptor.key, descriptor);
    }
  }

  function _createClass(Constructor, protoProps, staticProps) {
    if (protoProps) _defineProperties(Constructor.prototype, protoProps);
    if (staticProps) _defineProperties(Constructor, staticProps);
    return Constructor;
  }

  function _defineProperty(obj, key, value) {
    if (key in obj) {
      Object.defineProperty(obj, key, {
        value: value,
        enumerable: true,
        configurable: true,
        writable: true
      });
    } else {
      obj[key] = value;
    }

    return obj;
  }

  function _extends() {
    _extends = Object.assign || function (target) {
      for (var i = 1; i < arguments.length; i++) {
        var source = arguments[i];

        for (var key in source) {
          if (Object.prototype.hasOwnProperty.call(source, key)) {
            target[key] = source[key];
          }
        }
      }

      return target;
    };

    return _extends.apply(this, arguments);
  }

  function _inherits(subClass, superClass) {
    if (typeof superClass !== "function" && superClass !== null) {
      throw new TypeError("Super expression must either be null or a function");
    }

    subClass.prototype = Object.create(superClass && superClass.prototype, {
      constructor: {
        value: subClass,
        writable: true,
        configurable: true
      }
    });
    if (superClass) _setPrototypeOf(subClass, superClass);
  }

  function _getPrototypeOf(o) {
    _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
      return o.__proto__ || Object.getPrototypeOf(o);
    };
    return _getPrototypeOf(o);
  }

  function _setPrototypeOf(o, p) {
    _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
      o.__proto__ = p;
      return o;
    };

    return _setPrototypeOf(o, p);
  }

  function _assertThisInitialized(self) {
    if (self === void 0) {
      throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
    }

    return self;
  }

  function _possibleConstructorReturn(self, call) {
    if (call && (typeof call === "object" || typeof call === "function")) {
      return call;
    }

    return _assertThisInitialized(self);
  }

  function _toConsumableArray(arr) {
    return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();
  }

  function _arrayWithoutHoles(arr) {
    if (Array.isArray(arr)) {
      for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];

      return arr2;
    }
  }

  function _iterableToArray(iter) {
    if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter);
  }

  function _nonIterableSpread() {
    throw new TypeError("Invalid attempt to spread non-iterable instance");
  }

  /* Algo to calculate position
    1. center position for popup content : the center of the trigger will be the center of the content content
        so the popup content position will be like this :
        top => the y of the center for the trigger element : trigger.top + trigger.height/2
        left => the x of the center for the trigger element : trigger.left + trigger.width/2

    2. translate position according to the first  position attribute  passed  in the function argument
        for example :
          position = 'left top'
          we need to handle the first argument in the position: 'left' => that's mean we need to translate the popup content according to the X axis by - content.width/2

    3.translate position according to the first  position attribute  passed  in the function argument
      for example :
        position = 'left top'
        the second argument 'top' => translate popup content by + content.height*4/5

    4. check if calculated position is going out of bounds of wrapper box or not. If yes repeat 1-3 for next position enum. By default wrapper box is window element
  */
  function getCoordinatesForPosition(triggerBounding, ContentBounding, position, arrow, _ref) {
    var offsetX = _ref.offsetX,
        offsetY = _ref.offsetY;
    var margin = arrow ? 8 : 0;
    var args = position.split(" "); // the step N 1 : center the popup content => ok

    var CenterTop = triggerBounding.top + triggerBounding.height / 2;
    var CenterLeft = triggerBounding.left + triggerBounding.width / 2;
    var height = ContentBounding.height,
        width = ContentBounding.width;
    var top = CenterTop - height / 2;
    var left = CenterLeft - width / 2;
    var transform = "";
    var arrowTop = "0%";
    var arrowLeft = "0%"; // the  step N 2 : => ok

    switch (args[0]) {
      case "top":
        top -= height / 2 + triggerBounding.height / 2 + margin;
        transform = "rotate(45deg)";
        arrowTop = "100%";
        arrowLeft = "50%";
        break;

      case "bottom":
        top += height / 2 + triggerBounding.height / 2 + margin;
        transform = "rotate(225deg)";
        arrowLeft = "50%";
        break;

      case "left":
        left -= width / 2 + triggerBounding.width / 2 + margin;
        transform = " rotate(-45deg)";
        arrowLeft = "100%";
        arrowTop = "50%";
        break;

      case "right":
        left += width / 2 + triggerBounding.width / 2 + margin;
        transform = "rotate(135deg)";
        arrowTop = "50%";
        break;
    }

    switch (args[1]) {
      case "top":
        top = triggerBounding.top;
        arrowTop = triggerBounding.height / 2 + "px";
        break;

      case "bottom":
        top = triggerBounding.top - height + triggerBounding.height;
        arrowTop = height - triggerBounding.height / 2 + "px";
        break;

      case "left":
        left = triggerBounding.left;
        arrowLeft = triggerBounding.width / 2 + "px";
        break;

      case "right":
        left = triggerBounding.left - width + triggerBounding.width;
        arrowLeft = width - triggerBounding.width / 2 + "px";
        break;
    }

    top = args[0] === "top" ? top - offsetY : top + offsetY;
    left = args[0] === "left" ? left - offsetX : left + offsetX;
    return {
      top: top,
      left: left,
      transform: transform,
      arrowLeft: arrowLeft,
      arrowTop: arrowTop
    };
  }

  function calculatePosition(triggerBounding, ContentBounding, positions, arrow, _ref2, wrapperBox) {
    var offsetX = _ref2.offsetX,
        offsetY = _ref2.offsetY;
    var bestCoords;
    var i = 0;

    while (i < positions.length) {
      bestCoords = getCoordinatesForPosition(triggerBounding, ContentBounding, positions[i], arrow, {
        offsetX: offsetX,
        offsetY: offsetY
      });
      var contentBox = {
        top: bestCoords.top,
        left: bestCoords.left,
        width: ContentBounding.width,
        height: ContentBounding.height
      };

      if (contentBox.top <= wrapperBox.top || contentBox.left <= wrapperBox.left || contentBox.top + contentBox.height >= wrapperBox.top + wrapperBox.height || contentBox.left + contentBox.width >= wrapperBox.left + wrapperBox.width) {
        i++;
      } else {
        break;
      }
    }

    return bestCoords;
  }

  var styles = {
    popupContent: {
      tooltip: {
        position: "absolute",
        zIndex: "2",
        width: "200px",
        background: "rgb(255, 255, 255)",
        border: "1px solid rgb(187, 187, 187)",
        boxShadow: "rgba(0, 0, 0, 0.2) 0px 1px 3px",
        padding: "5px"
      },
      modal: {
        position: "relative",
        background: "rgb(255, 255, 255)",
        width: "50%",
        margin: "auto",
        border: "1px solid rgb(187, 187, 187)",
        padding: "5px"
      }
    },
    popupArrow: {
      height: "10px",
      width: "10px",
      position: "absolute",
      background: "rgb(255, 255, 255)",
      transform: "rotate(45deg)",
      margin: "-5px",
      zIndex: "-1",
      boxShadow: "rgba(0, 0, 0, 0.2) 1px 1px 1px"
    },
    overlay: {
      tooltip: {
        position: "fixed",
        top: "0",
        bottom: "0",
        left: "0",
        right: "0"
      },
      modal: {
        position: "fixed",
        top: "0",
        bottom: "0",
        left: "0",
        right: "0",
        background: "rgba(0, 0, 0,0.5)",
        display: "flex",
        zIndex: "999"
      }
    }
  };

  var POSITION_TYPES = ["top left", "top center", "top right", "right top", "right center", "right bottom", "bottom left", "bottom center", "bottom right", "left top", "left center", "left bottom"];

  var Popup =
  /*#__PURE__*/
  function (_React$PureComponent) {
    _inherits(Popup, _React$PureComponent);

    function Popup(props) {
      var _this;

      _classCallCheck(this, Popup);

      _this = _possibleConstructorReturn(this, _getPrototypeOf(Popup).call(this, props));

      _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "state", {
        isOpen: _this.props.open || _this.props.defaultOpen,
        modal: _this.props.modal ? true : !_this.props.trigger // we create this modal state because the popup can't be a tooltip if the trigger prop doesn't exist

      });

      _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "repositionOnResize", function () {
        _this.setPosition();
      });

      _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "onEscape", function (e) {
        if (e.key === "Escape") _this.closePopup();
      });

      _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "lockScroll", function () {
        if (_this.state.modal && _this.props.lockScroll) document.getElementsByTagName("body")[0].style.overflow = "hidden";
      });

      _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "resetScroll", function () {
        if (_this.state.modal && _this.props.lockScroll) document.getElementsByTagName("body")[0].style.overflow = "auto";
      });

      _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "togglePopup", function () {
        if (_this.state.isOpen) _this.closePopup();else _this.openPopup();
      });

      _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "openPopup", function () {
        if (_this.state.isOpen || _this.props.disabled) return;

        _this.setState({
          isOpen: true
        }, function () {
          _this.setPosition();

          _this.props.onOpen();

          _this.lockScroll();
        });
      });

      _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "closePopup", function () {
        if (!_this.state.isOpen) return;

        _this.props.onClose();

        _this.setState({
          isOpen: false
        }, function () {
          _this.resetScroll();
        });
      });

      _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "onMouseEnter", function () {
        clearTimeout(_this.timeOut);
        var mouseEnterDelay = _this.props.mouseEnterDelay;
        _this.timeOut = setTimeout(function () {
          return _this.openPopup();
        }, mouseEnterDelay);
      });

      _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "onMouseLeave", function () {
        clearTimeout(_this.timeOut);
        var mouseLeaveDelay = _this.props.mouseLeaveDelay;
        _this.timeOut = setTimeout(function () {
          return _this.closePopup();
        }, mouseLeaveDelay);
      });

      _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "getTooltipBoundary", function () {
        var keepTooltipInside = _this.props.keepTooltipInside;
        var boundingBox = {
          top: 0,
          left: 0,
          width: window.innerWidth,
          height: window.innerHeight
        };

        if (typeof keepTooltipInside === "string") {
          var selector = document.querySelector(keepTooltipInside);

          {
            if (selector === null) throw new Error("".concat(keepTooltipInside, " selector is not exist : keepTooltipInside must be a valid html selector 'class' or 'Id'  or a boolean value"));
          }

          boundingBox = selector.getBoundingClientRect();
        }

        return boundingBox;
      });

      _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "setPosition", function () {
        var _this$state = _this.state,
            modal = _this$state.modal,
            isOpen = _this$state.isOpen;
        if (modal || !isOpen) return;
        var _this$props = _this.props,
            arrow = _this$props.arrow,
            position = _this$props.position,
            offsetX = _this$props.offsetX,
            offsetY = _this$props.offsetY,
            keepTooltipInside = _this$props.keepTooltipInside,
            arrowStyle = _this$props.arrowStyle;

        var helper = _this.HelperEl.getBoundingClientRect();

        var trigger = _this.TriggerEl.getBoundingClientRect();

        var content = _this.ContentEl.getBoundingClientRect();

        var boundingBox = _this.getTooltipBoundary();

        var positions = Array.isArray(position) ? position : [position]; // keepTooltipInside would be activated if the  keepTooltipInside exist or the position is Array

        if (keepTooltipInside || Array.isArray(position)) positions = [].concat(_toConsumableArray(positions), POSITION_TYPES);
        var cords = calculatePosition(trigger, content, positions, arrow, {
          offsetX: offsetX,
          offsetY: offsetY
        }, boundingBox);
        _this.ContentEl.style.top = cords.top - helper.top + "px";
        _this.ContentEl.style.left = cords.left - helper.left + "px";

        if (arrow) {
          _this.ArrowEl.style["transform"] = cords.transform;
          _this.ArrowEl.style["-ms-transform"] = cords.transform;
          _this.ArrowEl.style["-webkit-transform"] = cords.transform;
          _this.ArrowEl.style.top = arrowStyle.top || cords.arrowTop;
          _this.ArrowEl.style.left = arrowStyle.left || cords.arrowLeft;
        }

        if (window.getComputedStyle(_this.TriggerEl, null).getPropertyValue("position") == "static" || window.getComputedStyle(_this.TriggerEl, null).getPropertyValue("position") == "") _this.TriggerEl.style.position = "relative";
      });

      _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "addWarperAction", function () {
        var _this$props2 = _this.props,
            contentStyle = _this$props2.contentStyle,
            className = _this$props2.className,
            on = _this$props2.on;
        var modal = _this.state.modal;
        var popupContentStyle = modal ? styles.popupContent.modal : styles.popupContent.tooltip;
        var childrenElementProps = {
          className: "popup-content ".concat(className),
          style: Object.assign({}, popupContentStyle, contentStyle),
          ref: _this.setContentRef,
          onClick: function onClick(e) {
            e.stopPropagation();
          }
        };

        if (!modal && on.indexOf("hover") >= 0) {
          childrenElementProps.onMouseEnter = _this.onMouseEnter;
          childrenElementProps.onMouseLeave = _this.onMouseLeave;
        }

        return childrenElementProps;
      });

      _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "renderTrigger", function () {
        var triggerProps = {
          key: "T"
        };
        var _this$props3 = _this.props,
            on = _this$props3.on,
            trigger = _this$props3.trigger;
        var onAsArray = Array.isArray(on) ? on : [on];

        for (var i = 0, len = onAsArray.length; i < len; i++) {
          switch (onAsArray[i]) {
            case "click":
              triggerProps.onClick = _this.togglePopup;
              break;

            case "hover":
              triggerProps.onMouseEnter = _this.onMouseEnter;
              triggerProps.onMouseLeave = _this.onMouseLeave;
              break;

            case "focus":
              triggerProps.onFocus = _this.onMouseEnter;
              break;
          }
        }

        if (typeof trigger === "function") return React.cloneElement(trigger(_this.state.isOpen), triggerProps);
        return React.cloneElement(trigger, triggerProps);
      });

      _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "renderContent", function () {
        var _this$props4 = _this.props,
            arrow = _this$props4.arrow,
            arrowStyle = _this$props4.arrowStyle;
        var modal = _this.state.modal;
        return React.createElement("div", _extends({}, _this.addWarperAction(), {
          key: "C"
        }), arrow && !modal && React.createElement("div", {
          ref: _this.setArrowRef,
          style: Object.assign({}, styles.popupArrow, arrowStyle)
        }), typeof _this.props.children === "function" ? _this.props.children(_this.closePopup, _this.state.isOpen) : _this.props.children);
      });

      _this.setTriggerRef = function (r) {
        return _this.TriggerEl = r;
      };

      _this.setContentRef = function (r) {
        return _this.ContentEl = r;
      };

      _this.setArrowRef = function (r) {
        return _this.ArrowEl = r;
      };

      _this.setHelperRef = function (r) {
        return _this.HelperEl = r;
      };

      _this.timeOut = 0;
      return _this;
    }

    _createClass(Popup, [{
      key: "componentDidMount",
      value: function componentDidMount() {
        var _this$props5 = this.props,
            closeOnEscape = _this$props5.closeOnEscape,
            defaultOpen = _this$props5.defaultOpen,
            repositionOnResize = _this$props5.repositionOnResize;
        if (defaultOpen) this.setPosition();

        if (closeOnEscape) {
          window.addEventListener("keyup", this.onEscape);
        }

        if (repositionOnResize) {
          window.addEventListener('resize', this.repositionOnResize);
        }
      }
    }, {
      key: "componentWillReceiveProps",
      value: function componentWillReceiveProps(nextProps) {
        if (this.props.open === nextProps.open) return;
        if (nextProps.open) this.openPopup();else this.closePopup();
      }
    }, {
      key: "componentDidUpdate",
      value: function componentDidUpdate(prevProps) {
        if (prevProps.disabled !== this.props.disabled && this.props.disabled && this.state.isOpen) {
          this.closePopup();
        }
      }
    }, {
      key: "componentWillUnmount",
      value: function componentWillUnmount() {
        // kill any function to execute if the component is unmounted
        clearTimeout(this.timeOut);
        var _this$props6 = this.props,
            closeOnEscape = _this$props6.closeOnEscape,
            repositionOnResize = _this$props6.repositionOnResize; // remove events listeners

        if (closeOnEscape) {
          window.removeEventListener("keyup", this.onEscape);
        }

        if (repositionOnResize) {
          window.removeEventListener('resize', this.repositionOnResize);
        }
      }
    }, {
      key: "render",
      value: function render() {
        var _this$props7 = this.props,
            overlayStyle = _this$props7.overlayStyle,
            closeOnDocumentClick = _this$props7.closeOnDocumentClick,
            on = _this$props7.on;
        var modal = this.state.modal;
        var overlay = this.state.isOpen && !(on.indexOf("hover") >= 0);
        var ovStyle = modal ? styles.overlay.modal : styles.overlay.tooltip;
        return [!!this.props.trigger && React.createElement(Ref, {
          innerRef: this.setTriggerRef,
          key: "R"
        }, this.renderTrigger()), this.state.isOpen && React.createElement("div", {
          key: "H",
          style: {
            position: "absolute",
            top: "0px",
            left: "0px"
          },
          ref: this.setHelperRef
        }), overlay && React.createElement("div", {
          key: "O",
          className: "popup-overlay",
          style: Object.assign({}, ovStyle, overlayStyle),
          onClick: closeOnDocumentClick ? this.closePopup : undefined
        }, modal && this.renderContent()), this.state.isOpen && !modal && this.renderContent()];
      }
    }]);

    return Popup;
  }(React.PureComponent);

  _defineProperty(Popup, "defaultProps", {
    children: function children() {
      return React.createElement("span", null, " Your Content Here !!");
    },
    trigger: null,
    onOpen: function onOpen() {},
    onClose: function onClose() {},
    defaultOpen: false,
    open: false,
    disabled: false,
    closeOnDocumentClick: true,
    repositionOnResize: true,
    closeOnEscape: true,
    on: ["click"],
    contentStyle: {},
    arrowStyle: {},
    overlayStyle: {},
    className: "",
    position: "bottom center",
    modal: false,
    lockScroll: false,
    arrow: true,
    offsetX: 0,
    offsetY: 0,
    mouseEnterDelay: 100,
    mouseLeaveDelay: 100,
    keepTooltipInside: false
  });

  {
    var PropTypes = require("prop-types");

    var TRIGGER_TYPES = ["hover", "click", "focus"];
    Popup.propTypes = {
      arrowStyle: PropTypes.object,
      contentStyle: PropTypes.object,
      overlayStyle: PropTypes.object,
      className: PropTypes.string,
      modal: PropTypes.bool,
      closeOnDocumentClick: PropTypes.bool,
      repositionOnResize: PropTypes.bool,
      disabled: PropTypes.bool,
      lockScroll: PropTypes.bool,
      offsetX: PropTypes.number,
      offsetY: PropTypes.number,
      mouseEnterDelay: PropTypes.number,
      mouseLeaveDelay: PropTypes.number,
      onOpen: PropTypes.func,
      onClose: PropTypes.func,
      open: PropTypes.bool,
      defaultOpen: PropTypes.bool,
      trigger: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
      // for uncontrolled component we don't need the trigger Element
      on: PropTypes.oneOfType([PropTypes.oneOf(TRIGGER_TYPES), PropTypes.arrayOf(PropTypes.oneOf(TRIGGER_TYPES))]),
      children: PropTypes.oneOfType([PropTypes.func, PropTypes.element, PropTypes.string]).isRequired,
      position: PropTypes.oneOfType([PropTypes.oneOf(POSITION_TYPES), PropTypes.arrayOf(PropTypes.oneOf(POSITION_TYPES))]),
      keepTooltipInside: PropTypes.oneOfType([PropTypes.bool, PropTypes.string])
    };
  }

  var Ref =
  /*#__PURE__*/
  function (_React$PureComponent2) {
    _inherits(Ref, _React$PureComponent2);

    function Ref() {
      _classCallCheck(this, Ref);

      return _possibleConstructorReturn(this, _getPrototypeOf(Ref).apply(this, arguments));
    }

    _createClass(Ref, [{
      key: "componentDidMount",
      value: function componentDidMount() {
        var innerRef = this.props.innerRef;
        if (innerRef) innerRef(reactDom.findDOMNode(this));
      }
    }, {
      key: "render",
      value: function render() {
        var children = this.props.children;
        return React.Children.only(children);
      }
    }]);

    return Ref;
  }(React.PureComponent);

  return Popup;

})));


var _ = require('underscore')
var utils = require('./utils.js')
var THREE = require('three')
var widgets = require('@jupyter-widgets/base');
var ndarray = require('ndarray')

function ascii_decode(buf) {
    return String.fromCharCode.apply(null, new Uint8Array(buf));
}

function read_uint16_LE(buffer) {
    var view = new DataView(buffer);
    var val = view.getUint8(0);
    val |= view.getUint8(1) << 8;
    return val;
}

function numpy_buffer_to_ndarray(buf) {
    var magic = ascii_decode(buf.slice(0, 6));
    if (magic.slice(1, 6) != 'NUMPY') {
        throw new Error('unknown file type');
    }

    var version = new Uint8Array(buf.slice(6, 8));
    var headerLength = read_uint16_LE(buf.slice(8, 10));
    var headerStr = ascii_decode(buf.slice(10, 10 + headerLength));
    var offsetBytes = 10 + headerLength;
    //rest = buf.slice(10+headerLength);  XXX -- This makes a copy!!! https://www.khronos.org/registry/typedarray/specs/latest/#5

    var info = JSON.parse(headerStr.toLowerCase().replace('(', '[').replace(',),', ']').replace('),', ']').replace(/'/g, "\""));

    // Intepret the bytes according to the specified dtype
    var data;
    if (info.descr === "|u1") {
        data = new Uint8Array(buf, offsetBytes);
    } else if (info.descr === "|i1") {
        data = new Int8Array(buf, offsetBytes);
    } else if (info.descr === "<u2") {
        data = new Uint16Array(buf, offsetBytes);
    } else if (info.descr === "<i2") {
        data = new Int16Array(buf, offsetBytes);
    } else if (info.descr === "<u4") {
        data = new Uint32Array(buf, offsetBytes);
    } else if (info.descr === "<i4") {
        data = new Int32Array(buf, offsetBytes);
    } else if (info.descr === "<f4") {
        data = new Float32Array(buf, offsetBytes);
    } else if (info.descr === "<f8") {
        data = new Float64Array(buf, offsetBytes);
    } else {
        throw new Error('unknown numeric dtype')
    }

    return ndarray(data, info.shape);

}

// see https://github.com/jovyan/pythreejs/pull/80/files
// should go to a seperate package/module
var typesToArray = {
    int8: Int8Array,
    int16: Int16Array,
    int32: Int32Array,
    uint8: Uint8Array,
    uint16: Uint16Array,
    uint32: Uint32Array,
    float32: Float32Array,
    float64: Float64Array
}

var arrayToTypes = {
    Int8Array: 'int8',
    Int16Array: 'int16',
    Int32Array: 'int32',
    Uint8Array: 'uint8',
    Uint16Array: 'uint16',
    Uint32Array: 'uint32',
    Float32Array: 'float32',
    Float64Array: 'float64'
}

function deserialize_typed_array(data, manager) {
    var type = typesToArray[data.dtype];
    if (data == null) {
        console.log('data is null')
    }
    if (!data.data) {
        console.log('data.data is null')
    }
    if (!data.data.buffer) {
        console.log('data.data.buffer is null')
    }
    return new type(data.data.buffer); //

}

function deserialize_array_or_json(data, manager) {
    if (data == null)
        return null;
    var arrays = null;
    if (_.isNumber(data)) { // plain number
        return data;
    } else { // should be an array of buffer+dtype+shape
        arrays = _.map(data, function (data) { return deserialize_typed_array(data, manager) })
    }
    arrays.original_data = data;
    return arrays;
    /*
    if(_.isArray(data) && !data.buffer) { // plain json, or list of buffers
        if(data.length == 0) {
            arrays = []
        } else
        if(!data[0].buffer) {
            // plain json
            if(_.isArray(data[0])) {
                arrays = _.map(data, function(array1d) { return new Float32Array(array1d)})
            } else {
                arrays = [new Float32Array(data)]
            }
        } else {
            arrays = _.map(data, function(data) { return new Float32Array(data.buffer)});
        }
    } else {
        nd = numpy_buffer_to_ndarray(data.buffer)
        if (nd.shape.length == 2) {
            arrays = []
            for(var i = 0; i < nd.shape[0]; i++) {
                var ar = nd.data.slice(i*nd.stride[0], (i+1)*nd.stride[0])
                arrays.push(ar)
            }
        } else { // assume 1d... ?
            arrays = [nd.data];
        }
    }
    arrays.original_data = data;
    return arrays;*/
}

function deserialize_color_or_json(data, manager) {
    if (data == null)
        return null;
    var arrays = null;

    // It can deals with these 6 cases: which are threated in the same order
    // shape is 0 dim, and it's a string, interpret as color
    // shape is 1 dim, items are strings, seperate color for each item
    // shape is 2 dim, items are strings, sequence of the above
    // shape is 1 dim, items are floats, it should be of length 3 -> rgb values
    // shape is 2 dim, items are float, it should be of shape (len(x), 3) -> rgb values
    // shape is 3 dim, items are float, it should be (sequence_length, len(x), 3) -> rgb values
    function string_array_to_rgb(string_array) {
        var rgbs = new Float32Array(string_array.length * 3);
        for (var i = 0; i < string_array.length; i++) {
            var color = new THREE.Color(string_array[i]);
            rgbs[i * 3 + 0] = color.r;
            rgbs[i * 3 + 1] = color.g;
            rgbs[i * 3 + 2] = color.b;
        }
        return rgbs;
    }
    function rgb_array_to_rgb(rgb_array) {
        var rgbs = new Float32Array(rgb_array.length * 3);
        for (var i = 0; i < rgb_array.length; i++) {
            rgbs[i * 3 + 0] = rgb_array[i][0];
            rgbs[i * 3 + 1] = rgb_array[i][1];
            rgbs[i * 3 + 2] = rgb_array[i][2];
        }
        return rgbs;
    }

    if (typeof data == "string") { // single color
        //var color = new THREE.Color(data)
        //arrays = new Float32Array([color.r, color.g, color.b]) // no sequence, scalar
        arrays = data; // special case, if we keep it a string, we can control it via colorppicker
    } else {
        if (typeof data[0].dtype !== "undefined") { // we have a list of ndarrays
            arrays = _.map(data, function (data) { return deserialize_typed_array(data, manager) })
        } else {
            // must be a plain list of string, or list of list
            if (dimension == 1 && typeof data[0] == "string") {
                arrays = string_array_to_rgb(data)
            } else
                if (dimension == 2 && typeof data[0][0] == "string") {
                    arrays = _.map(data, string_array_to_rgb)
                } else {
                    console.error("don't understand color type")
                }
        }
    }
    arrays.original_data = data;
    return arrays;
}

function serialize_array_or_json(obj, manager) {
    if (_.isNumber(obj)) return obj; // return numbers directly
    if (obj != null) {
        if (typeof obj.original_data == "undefined") // if someone modified the data from javascript land, we don't have this
            return obj
        else
            return obj.original_data; // ftm we just feed back the original data, we don't modify currently
    } else {
        return null;
    }
}
function deserialize_texture(data, manager) {
    if (typeof data == "string") {
        if (data.startsWith('IPY_MODEL_')) {
            return widgets.unpack_models(data, manager)
        }
    }
    return data
}
function deserialize_ndarray(data, manager) {
    if (data === null)
        return null;
    return ndarray(deserialize_typed_array(data, manager), data.shape);
}

function serialize_ndarray(data, manager) {
    if (data === null)
        return null;
    var ar = data;
    if (_.isArray(data) && !data.data) { // plain list of list
        var ar = require("ndarray-pack")(data)
    }
    var data_json = { 'data': ar.data.buffer, dtype: arrayToTypes[ar.data.constructor.name], shape: ar.shape }
    return data_json;
}

function serialize_texture(data, manager) {
    return data;
}

window.ndarray = ndarray;

module.exports = {
    texture: { deserialize: deserialize_texture, serialize: serialize_texture },
    serialize_array_or_json: serialize_array_or_json,
    deserialize_array_or_json: deserialize_array_or_json,
    deserialize_color_or_json: deserialize_color_or_json,
    array_or_json: { deserialize: deserialize_array_or_json, serialize: serialize_array_or_json },
    color_or_json: { deserialize: deserialize_color_or_json, serialize: serialize_array_or_json },
    ndarray: { deserialize: deserialize_ndarray, serialize: serialize_ndarray },
}