<commit_before>import fs from 'fs';
import path from 'path';

import webpack from 'webpack';
import sources from 'webpack-sources';

const thing = path.resolve(__dirname, './hotLoader.js');

const { ConcatSource, SourceMapSource, OriginalSource } = sources;
const { Template, util: { createHash } } = webpack;

const NS = path.dirname(fs.realpathSync(__filename));

const pluginName = 'extract-css-chunks-webpack-plugin';

const REGEXP_CHUNKHASH = /\[chunkhash(?::(\d+))?\]/i;
const REGEXP_CONTENTHASH = /\[contenthash(?::(\d+))?\]/i;
const REGEXP_NAME = /\[name\]/i;

class CssDependency extends webpack.Dependency {
  constructor(
    { identifier, content, media, sourceMap },
    context,
    identifierIndex,
  ) {
    super();
    this.identifier = identifier;
    this.identifierIndex = identifierIndex;
    this.content = content;
    this.media = media;
    this.sourceMap = sourceMap;
    this.context = context;
  }

  getResourceIdentifier() {
    return `css-module-${this.identifier}-${this.identifierIndex}`;
  }
}

class CssDependencyTemplate {
  apply() { // eslint-disable-line class-methods-use-this
  }
}

class CssModule extends webpack.Module {
  constructor(dependency) {
    super(NS, dependency.context);
    this._identifier = dependency.identifier;
    this._identifierIndex = dependency.identifierIndex;
    this.content = dependency.content;
    this.media = dependency.media;
    this.sourceMap = dependency.sourceMap;
  }

  // no source() so webpack doesn't do add stuff to the bundle

  size() {
    return this.content.length;
  }

  identifier() {
    return `css ${this._identifier} ${this._identifierIndex}`;
  }

  readableIdentifier(requestShortener) {
    return `css ${requestShortener.shorten(this._identifier)}${
      this._identifierIndex ? ` (${this._identifierIndex})` : ''
      }`;
  }

  nameForCondition() {
    const resource = this._identifier.split('!').pop();
    const idx = resource.indexOf('?');
    if (idx >= 0) return resource.substring(0, idx);
    return resource;
  }

  updateCacheModule(module) {
    this.content = module.content;
    this.media = module.media;
    this.sourceMap = module.sourceMap;
  }

  needRebuild() { // eslint-disable-line class-methods-use-this
    return true;
  }

  build(options, compilation, resolver, fileSystem, callback) {
    this.buildInfo = {};
    this.buildMeta = {};
    callback();
  }

  updateHash(hash) {
    super.updateHash(hash);
    hash.update(this.content);
    hash.update(this.media || '');
    hash.update(JSON.stringify(this.sourceMap || ''));
  }
}

class CssModuleFactory {
  create({ dependencies: [dependency] }, callback) { // eslint-disable-line class-methods-use-this
    callback(null, new CssModule(dependency));
  }
}

class ExtractCssChunks {
  constructor(options) {
    this.options = Object.assign(
      {
        filename: '[name].css',
      },
      options,
    );
    if (!this.options.chunkFilename) {
      const { filename } = this.options;
      const hasName = filename.includes('[name]');
      const hasId = filename.includes('[id]');
      const hasChunkHash = filename.includes('[chunkhash]');
      // Anything changing depending on chunk is fine
      if (hasChunkHash || hasName || hasId) {
        this.options.chunkFilename = filename;
      } else {
        // Elsewise prefix '[id].' in front of the basename to make it changing
        this.options.chunkFilename = filename.replace(
          /(^|\/)([^/]*(?:\?|$))/,
          '$1[id].$2',
        );
      }
    }
  }

  apply(compiler) {
    const updatedRules = compiler.options.module.rules.reduce((rules, rule) => {
      if (rule.use) {
        const isMiniCss = rule.use.some((l) => {
          const needle = l.loader || l;
          return needle.includes(pluginName);
        });
        if (isMiniCss) {
          rule.use.unshift(thing);
        }
      }
      rules.push(rule);

      return rules;
    }, []);

    compiler.options.module.rules = updatedRules;

    compiler.hooks.thisCompilation.tap(pluginName, (compilation) => {
      compilation.hooks.normalModuleLoader.tap(pluginName, (lc, m) => {
        const loaderContext = lc;
        const module = m;
        loaderContext[NS] = (content) => {
          if (!Array.isArray(content) && content != null) {
            throw new Error(
              `Exported value was not extracted as an array: ${JSON.stringify(
                content,
              )}`,
            );
          }
          const identifierCountMap = new Map();
          for (const line of content) {
            const count = identifierCountMap.get(line.identifier) || 0;
            module.addDependency(new CssDependency(line, m.context, count));
            identifierCountMap.set(line.identifier, count + 1);
          }
        };
      });
      compilation.dependencyFactories.set(
        CssDependency,
        new CssModuleFactory(),
      );
      compilation.dependencyTemplates.set(
        CssDependency,
        new CssDependencyTemplate(),
      );
      compilation.mainTemplate.hooks.renderManifest.tap(
        pluginName,
        (result, { chunk }) => {
          const renderedModules = Array.from(chunk.modulesIterable).filter(
            module => module.type === NS,
          );
          if (renderedModules.length > 0) {
            result.push({
              render: () =>
                this.renderContentAsset(
                  renderedModules,
                  compilation.runtimeTemplate.requestShortener,
                ),
              filenameTemplate: this.options.filename,
              pathOptions: {
                chunk,
                contentHashType: NS,
              },
              identifier: `extract-css-chunks-webpack-plugin.${chunk.id}`,
              hash: chunk.contentHash[NS],
            });
          }
        },
      );
      compilation.chunkTemplate.hooks.renderManifest.tap(
        pluginName,
        (result, { chunk }) => {
          const renderedModules = Array.from(chunk.modulesIterable).filter(
            module => module.type === NS,
          );
          if (renderedModules.length > 0) {
            result.push({
              render: () =>
                this.renderContentAsset(
                  renderedModules,
                  compilation.runtimeTemplate.requestShortener,
                ),
              filenameTemplate: this.options.chunkFilename,
              pathOptions: {
                chunk,
                contentHashType: NS,
              },
              identifier: `extract-css-chunks-webpack-plugin.${chunk.id}`,
              hash: chunk.contentHash[NS],
            });
          }
        },
      );
      compilation.mainTemplate.hooks.hashForChunk.tap(
        pluginName,
        (hash, chunk) => {
          const { chunkFilename } = this.options;
          if (REGEXP_CHUNKHASH.test(chunkFilename)) {
            hash.update(JSON.stringify(chunk.getChunkMaps(true).hash));
          }
          if (REGEXP_CONTENTHASH.test(chunkFilename)) {
            hash.update(
              JSON.stringify(chunk.getChunkMaps(true).contentHash[NS] || {}),
            );
          }
          if (REGEXP_NAME.test(chunkFilename)) {
            hash.update(JSON.stringify(chunk.getChunkMaps(true).name));
          }
        },
      );
      compilation.hooks.contentHash.tap(pluginName, (chunk) => {
        const { outputOptions } = compilation;
        const { hashFunction, hashDigest, hashDigestLength } = outputOptions;
        const hash = createHash(hashFunction);
        for (const m of chunk.modulesIterable) {
          if (m.type === NS) {
            m.updateHash(hash);
          }
        }
        const { contentHash } = chunk;
        contentHash[NS] = hash
          .digest(hashDigest)
          .substring(0, hashDigestLength);
      });
      const { mainTemplate } = compilation;
      mainTemplate.hooks.localVars.tap(pluginName, (source, chunk) => {
        const chunkMap = this.getCssChunkObject(chunk);
        if (Object.keys(chunkMap).length > 0) {
          return Template.asString([
            source,
            '',
            '// object to store loaded CSS chunks',
            'var installedCssChunks = {',
            Template.indent(
              chunk.ids.map(id => `${JSON.stringify(id)}: 0`).join(',\n'),
            ),
            '}',
          ]);
        }
        return source;
      });
      mainTemplate.hooks.requireEnsure.tap(
        pluginName,
        (source, chunk, hash) => {
          const chunkMap = this.getCssChunkObject(chunk);
          if (Object.keys(chunkMap).length > 0) {
            const chunkMaps = chunk.getChunkMaps();
            const linkHrefPath = mainTemplate.getAssetPath(
              JSON.stringify(this.options.chunkFilename),
              {
                hash: `" + ${mainTemplate.renderCurrentHashCode(hash)} + "`,
                hashWithLength: length =>
                  `" + ${mainTemplate.renderCurrentHashCode(hash, length)} + "`,
                chunk: {
                  id: '" + chunkId + "',
                  hash: `" + ${JSON.stringify(chunkMaps.hash)}[chunkId] + "`,
                  hashWithLength(length) {
                    const shortChunkHashMap = Object.create(null);
                    for (const chunkId of Object.keys(chunkMaps.hash)) {
                      if (typeof chunkMaps.hash[chunkId] === 'string') {
                        shortChunkHashMap[chunkId] = chunkMaps.hash[
                          chunkId
                          ].substring(0, length);
                      }
                    }
                    return `" + ${JSON.stringify(
                      shortChunkHashMap,
                    )}[chunkId] + "`;
                  },
                  contentHash: {
                    [NS]: `" + ${JSON.stringify(
                      chunkMaps.contentHash[NS],
                    )}[chunkId] + "`,
                  },
                  contentHashWithLength: {
                    [NS]: (length) => {
                      const shortContentHashMap = {};
                      const contentHash = chunkMaps.contentHash[NS];
                      for (const chunkId of Object.keys(contentHash)) {
                        if (typeof contentHash[chunkId] === 'string') {
                          shortContentHashMap[chunkId] = contentHash[
                            chunkId
                            ].substring(0, length);
                        }
                      }
                      return `" + ${JSON.stringify(
                        shortContentHashMap,
                      )}[chunkId] + "`;
                    },
                  },
                  name: `" + (${JSON.stringify(
                    chunkMaps.name,
                  )}[chunkId]||chunkId) + "`,
                },
                contentHashType: NS,
              },
            );
            return Template.asString([
              source,
              '',
              '// extract-css-chunks-webpack-plugin CSS loading',
              `var cssChunks = ${JSON.stringify(chunkMap)};`,
              'if(installedCssChunks[chunkId]) promises.push(installedCssChunks[chunkId]);',
              'else if(installedCssChunks[chunkId] !== 0 && cssChunks[chunkId]) {',
              Template.indent([
                'promises.push(installedCssChunks[chunkId] = new Promise(function(resolve, reject) {',
                Template.indent([
                  `var href = ${linkHrefPath};`,
                  `var fullhref = ${mainTemplate.requireFn}.p + href;`,
                  'var existingLinkTags = document.getElementsByTagName("link");',
                  'for(var i = 0; i < existingLinkTags.length; i++) {',
                  Template.indent([
                    'var tag = existingLinkTags[i];',
                    'var dataHref = tag.getAttribute("data-href") || tag.getAttribute("href");',
                    'if(tag.rel === "stylesheet" && (dataHref === href || dataHref === fullhref)) return resolve();',
                  ]),
                  '}',
                  'var existingStyleTags = document.getElementsByTagName("style");',
                  'for(var i = 0; i < existingStyleTags.length; i++) {',
                  Template.indent([
                    'var tag = existingStyleTags[i];',
                    'var dataHref = tag.getAttribute("data-href");',
                    'if(dataHref === href || dataHref === fullhref) return resolve();',
                  ]),
                  '}',
                  'var linkTag = document.createElement("link");',
                  'linkTag.rel = "stylesheet";',
                  'linkTag.type = "text/css";',
                  'linkTag.onload = resolve;',
                  'linkTag.onerror = function(event) {',
                  Template.indent([
                    'var request = event && event.target && event.target.src || fullhref;',
                    'var err = new Error("Loading CSS chunk " + chunkId + " failed.\\n(" + request + ")");',
                    'err.request = request;',
                    'reject(err);',
                  ]),
                  '};',
                  'linkTag.href = fullhref;',
                  'var head = document.getElementsByTagName("head")[0];',
                  'head.appendChild(linkTag);',
                ]),
                '}).then(function() {',
                Template.indent(['installedCssChunks[chunkId] = 0;']),
                '}));',
              ]),
              '}',
            ]);
          }
          return source;
        },
      );
    });
  }

  getCssChunkObject(mainChunk) { // eslint-disable-line class-methods-use-this
    const obj = {};
    for (const chunk of mainChunk.getAllAsyncChunks()) {
      for (const module of chunk.modulesIterable) {
        if (module.type === NS) {
          obj[chunk.id] = 1;
          break;
        }
      }
    }
    return obj;
  }

  renderContentAsset(modules, requestShortener) { // eslint-disable-line class-methods-use-this
    modules.sort((a, b) => a.index2 - b.index2);
    const source = new ConcatSource();
    const externalsSource = new ConcatSource();
    for (const m of modules) {
      if (/^@import url/.test(m.content)) {
        // HACK for IE
        // http://stackoverflow.com/a/14676665/1458162
        let { content } = m;
        if (m.media) {
          // insert media into the @import
          // this is rar
          // TODO improve this and parse the CSS to support multiple medias
          content = content.replace(/;|\s*$/, m.media);
        }
        externalsSource.add(content);
        externalsSource.add('\n');
      } else {
        if (m.media) {
          source.add(`@media ${m.media} {\n`);
        }
        if (m.sourceMap) {
          source.add(
            new SourceMapSource(
              m.content,
              m.readableIdentifier(requestShortener),
              m.sourceMap,
            ),
          );
        } else {
          source.add(
            new OriginalSource(
              m.content,
              m.readableIdentifier(requestShortener),
            ),
          );
        }
        source.add('\n');
        if (m.media) {
          source.add('}\n');
        }
      }
    }
    return new ConcatSource(externalsSource, source);
  }
}

ExtractCssChunks.loader = require.resolve('./loader');

export default ExtractCssChunks;
<commit_msg>fix(src/index.js): Loader crashes on unexpected use key type<commit_after>import fs from 'fs';
import path from 'path';

import webpack from 'webpack';
import sources from 'webpack-sources';

const thing = path.resolve(__dirname, './hotLoader.js');

const { ConcatSource, SourceMapSource, OriginalSource } = sources;
const { Template, util: { createHash } } = webpack;

const NS = path.dirname(fs.realpathSync(__filename));

const pluginName = 'extract-css-chunks-webpack-plugin';

const REGEXP_CHUNKHASH = /\[chunkhash(?::(\d+))?\]/i;
const REGEXP_CONTENTHASH = /\[contenthash(?::(\d+))?\]/i;
const REGEXP_NAME = /\[name\]/i;

class CssDependency extends webpack.Dependency {
  constructor(
    { identifier, content, media, sourceMap },
    context,
    identifierIndex,
  ) {
    super();
    this.identifier = identifier;
    this.identifierIndex = identifierIndex;
    this.content = content;
    this.media = media;
    this.sourceMap = sourceMap;
    this.context = context;
  }

  getResourceIdentifier() {
    return `css-module-${this.identifier}-${this.identifierIndex}`;
  }
}

class CssDependencyTemplate {
  apply() { // eslint-disable-line class-methods-use-this
  }
}

class CssModule extends webpack.Module {
  constructor(dependency) {
    super(NS, dependency.context);
    this._identifier = dependency.identifier;
    this._identifierIndex = dependency.identifierIndex;
    this.content = dependency.content;
    this.media = dependency.media;
    this.sourceMap = dependency.sourceMap;
  }

  // no source() so webpack doesn't do add stuff to the bundle

  size() {
    return this.content.length;
  }

  identifier() {
    return `css ${this._identifier} ${this._identifierIndex}`;
  }

  readableIdentifier(requestShortener) {
    return `css ${requestShortener.shorten(this._identifier)}${
      this._identifierIndex ? ` (${this._identifierIndex})` : ''
      }`;
  }

  nameForCondition() {
    const resource = this._identifier.split('!').pop();
    const idx = resource.indexOf('?');
    if (idx >= 0) return resource.substring(0, idx);
    return resource;
  }

  updateCacheModule(module) {
    this.content = module.content;
    this.media = module.media;
    this.sourceMap = module.sourceMap;
  }

  needRebuild() { // eslint-disable-line class-methods-use-this
    return true;
  }

  build(options, compilation, resolver, fileSystem, callback) {
    this.buildInfo = {};
    this.buildMeta = {};
    callback();
  }

  updateHash(hash) {
    super.updateHash(hash);
    hash.update(this.content);
    hash.update(this.media || '');
    hash.update(JSON.stringify(this.sourceMap || ''));
  }
}

class CssModuleFactory {
  create({ dependencies: [dependency] }, callback) { // eslint-disable-line class-methods-use-this
    callback(null, new CssModule(dependency));
  }
}

class ExtractCssChunks {
  constructor(options) {
    this.options = Object.assign(
      {
        filename: '[name].css',
      },
      options,
    );
    if (!this.options.chunkFilename) {
      const { filename } = this.options;
      const hasName = filename.includes('[name]');
      const hasId = filename.includes('[id]');
      const hasChunkHash = filename.includes('[chunkhash]');
      // Anything changing depending on chunk is fine
      if (hasChunkHash || hasName || hasId) {
        this.options.chunkFilename = filename;
      } else {
        // Elsewise prefix '[id].' in front of the basename to make it changing
        this.options.chunkFilename = filename.replace(
          /(^|\/)([^/]*(?:\?|$))/,
          '$1[id].$2',
        );
      }
    }
  }

  apply(compiler) {
    const updatedRules = compiler.options.module.rules.reduce((rules, rule) => {

      if (rule.use && typeof rule.use !== 'string') {
        const isMiniCss = rule.use.some((l) => {
          const needle = l.loader || l;
          return needle.includes(pluginName);
        });
        if (isMiniCss) {
          rule.use.unshift(thing);
        }
      }
      rules.push(rule);

      return rules;
    }, []);

    compiler.options.module.rules = updatedRules;

    compiler.hooks.thisCompilation.tap(pluginName, (compilation) => {
      compilation.hooks.normalModuleLoader.tap(pluginName, (lc, m) => {
        const loaderContext = lc;
        const module = m;
        loaderContext[NS] = (content) => {
          if (!Array.isArray(content) && content != null) {
            throw new Error(
              `Exported value was not extracted as an array: ${JSON.stringify(
                content,
              )}`,
            );
          }
          const identifierCountMap = new Map();
          for (const line of content) {
            const count = identifierCountMap.get(line.identifier) || 0;
            module.addDependency(new CssDependency(line, m.context, count));
            identifierCountMap.set(line.identifier, count + 1);
          }
        };
      });
      compilation.dependencyFactories.set(
        CssDependency,
        new CssModuleFactory(),
      );
      compilation.dependencyTemplates.set(
        CssDependency,
        new CssDependencyTemplate(),
      );
      compilation.mainTemplate.hooks.renderManifest.tap(
        pluginName,
        (result, { chunk }) => {
          const renderedModules = Array.from(chunk.modulesIterable).filter(
            module => module.type === NS,
          );
          if (renderedModules.length > 0) {
            result.push({
              render: () =>
                this.renderContentAsset(
                  renderedModules,
                  compilation.runtimeTemplate.requestShortener,
                ),
              filenameTemplate: this.options.filename,
              pathOptions: {
                chunk,
                contentHashType: NS,
              },
              identifier: `extract-css-chunks-webpack-plugin.${chunk.id}`,
              hash: chunk.contentHash[NS],
            });
          }
        },
      );
      compilation.chunkTemplate.hooks.renderManifest.tap(
        pluginName,
        (result, { chunk }) => {
          const renderedModules = Array.from(chunk.modulesIterable).filter(
            module => module.type === NS,
          );
          if (renderedModules.length > 0) {
            result.push({
              render: () =>
                this.renderContentAsset(
                  renderedModules,
                  compilation.runtimeTemplate.requestShortener,
                ),
              filenameTemplate: this.options.chunkFilename,
              pathOptions: {
                chunk,
                contentHashType: NS,
              },
              identifier: `extract-css-chunks-webpack-plugin.${chunk.id}`,
              hash: chunk.contentHash[NS],
            });
          }
        },
      );
      compilation.mainTemplate.hooks.hashForChunk.tap(
        pluginName,
        (hash, chunk) => {
          const { chunkFilename } = this.options;
          if (REGEXP_CHUNKHASH.test(chunkFilename)) {
            hash.update(JSON.stringify(chunk.getChunkMaps(true).hash));
          }
          if (REGEXP_CONTENTHASH.test(chunkFilename)) {
            hash.update(
              JSON.stringify(chunk.getChunkMaps(true).contentHash[NS] || {}),
            );
          }
          if (REGEXP_NAME.test(chunkFilename)) {
            hash.update(JSON.stringify(chunk.getChunkMaps(true).name));
          }
        },
      );
      compilation.hooks.contentHash.tap(pluginName, (chunk) => {
        const { outputOptions } = compilation;
        const { hashFunction, hashDigest, hashDigestLength } = outputOptions;
        const hash = createHash(hashFunction);
        for (const m of chunk.modulesIterable) {
          if (m.type === NS) {
            m.updateHash(hash);
          }
        }
        const { contentHash } = chunk;
        contentHash[NS] = hash
          .digest(hashDigest)
          .substring(0, hashDigestLength);
      });
      const { mainTemplate } = compilation;
      mainTemplate.hooks.localVars.tap(pluginName, (source, chunk) => {
        const chunkMap = this.getCssChunkObject(chunk);
        if (Object.keys(chunkMap).length > 0) {
          return Template.asString([
            source,
            '',
            '// object to store loaded CSS chunks',
            'var installedCssChunks = {',
            Template.indent(
              chunk.ids.map(id => `${JSON.stringify(id)}: 0`).join(',\n'),
            ),
            '}',
          ]);
        }
        return source;
      });
      mainTemplate.hooks.requireEnsure.tap(
        pluginName,
        (source, chunk, hash) => {
          const chunkMap = this.getCssChunkObject(chunk);
          if (Object.keys(chunkMap).length > 0) {
            const chunkMaps = chunk.getChunkMaps();
            const linkHrefPath = mainTemplate.getAssetPath(
              JSON.stringify(this.options.chunkFilename),
              {
                hash: `" + ${mainTemplate.renderCurrentHashCode(hash)} + "`,
                hashWithLength: length =>
                  `" + ${mainTemplate.renderCurrentHashCode(hash, length)} + "`,
                chunk: {
                  id: '" + chunkId + "',
                  hash: `" + ${JSON.stringify(chunkMaps.hash)}[chunkId] + "`,
                  hashWithLength(length) {
                    const shortChunkHashMap = Object.create(null);
                    for (const chunkId of Object.keys(chunkMaps.hash)) {
                      if (typeof chunkMaps.hash[chunkId] === 'string') {
                        shortChunkHashMap[chunkId] = chunkMaps.hash[
                          chunkId
                          ].substring(0, length);
                      }
                    }
                    return `" + ${JSON.stringify(
                      shortChunkHashMap,
                    )}[chunkId] + "`;
                  },
                  contentHash: {
                    [NS]: `" + ${JSON.stringify(
                      chunkMaps.contentHash[NS],
                    )}[chunkId] + "`,
                  },
                  contentHashWithLength: {
                    [NS]: (length) => {
                      const shortContentHashMap = {};
                      const contentHash = chunkMaps.contentHash[NS];
                      for (const chunkId of Object.keys(contentHash)) {
                        if (typeof contentHash[chunkId] === 'string') {
                          shortContentHashMap[chunkId] = contentHash[
                            chunkId
                            ].substring(0, length);
                        }
                      }
                      return `" + ${JSON.stringify(
                        shortContentHashMap,
                      )}[chunkId] + "`;
                    },
                  },
                  name: `" + (${JSON.stringify(
                    chunkMaps.name,
                  )}[chunkId]||chunkId) + "`,
                },
                contentHashType: NS,
              },
            );
            return Template.asString([
              source,
              '',
              '// extract-css-chunks-webpack-plugin CSS loading',
              `var cssChunks = ${JSON.stringify(chunkMap)};`,
              'if(installedCssChunks[chunkId]) promises.push(installedCssChunks[chunkId]);',
              'else if(installedCssChunks[chunkId] !== 0 && cssChunks[chunkId]) {',
              Template.indent([
                'promises.push(installedCssChunks[chunkId] = new Promise(function(resolve, reject) {',
                Template.indent([
                  `var href = ${linkHrefPath};`,
                  `var fullhref = ${mainTemplate.requireFn}.p + href;`,
                  'var existingLinkTags = document.getElementsByTagName("link");',
                  'for(var i = 0; i < existingLinkTags.length; i++) {',
                  Template.indent([
                    'var tag = existingLinkTags[i];',
                    'var dataHref = tag.getAttribute("data-href") || tag.getAttribute("href");',
                    'if(tag.rel === "stylesheet" && (dataHref === href || dataHref === fullhref)) return resolve();',
                  ]),
                  '}',
                  'var existingStyleTags = document.getElementsByTagName("style");',
                  'for(var i = 0; i < existingStyleTags.length; i++) {',
                  Template.indent([
                    'var tag = existingStyleTags[i];',
                    'var dataHref = tag.getAttribute("data-href");',
                    'if(dataHref === href || dataHref === fullhref) return resolve();',
                  ]),
                  '}',
                  'var linkTag = document.createElement("link");',
                  'linkTag.rel = "stylesheet";',
                  'linkTag.type = "text/css";',
                  'linkTag.onload = resolve;',
                  'linkTag.onerror = function(event) {',
                  Template.indent([
                    'var request = event && event.target && event.target.src || fullhref;',
                    'var err = new Error("Loading CSS chunk " + chunkId + " failed.\\n(" + request + ")");',
                    'err.request = request;',
                    'reject(err);',
                  ]),
                  '};',
                  'linkTag.href = fullhref;',
                  'var head = document.getElementsByTagName("head")[0];',
                  'head.appendChild(linkTag);',
                ]),
                '}).then(function() {',
                Template.indent(['installedCssChunks[chunkId] = 0;']),
                '}));',
              ]),
              '}',
            ]);
          }
          return source;
        },
      );
    });
  }

  getCssChunkObject(mainChunk) { // eslint-disable-line class-methods-use-this
    const obj = {};
    for (const chunk of mainChunk.getAllAsyncChunks()) {
      for (const module of chunk.modulesIterable) {
        if (module.type === NS) {
          obj[chunk.id] = 1;
          break;
        }
      }
    }
    return obj;
  }

  renderContentAsset(modules, requestShortener) { // eslint-disable-line class-methods-use-this
    modules.sort((a, b) => a.index2 - b.index2);
    const source = new ConcatSource();
    const externalsSource = new ConcatSource();
    for (const m of modules) {
      if (/^@import url/.test(m.content)) {
        // HACK for IE
        // http://stackoverflow.com/a/14676665/1458162
        let { content } = m;
        if (m.media) {
          // insert media into the @import
          // this is rar
          // TODO improve this and parse the CSS to support multiple medias
          content = content.replace(/;|\s*$/, m.media);
        }
        externalsSource.add(content);
        externalsSource.add('\n');
      } else {
        if (m.media) {
          source.add(`@media ${m.media} {\n`);
        }
        if (m.sourceMap) {
          source.add(
            new SourceMapSource(
              m.content,
              m.readableIdentifier(requestShortener),
              m.sourceMap,
            ),
          );
        } else {
          source.add(
            new OriginalSource(
              m.content,
              m.readableIdentifier(requestShortener),
            ),
          );
        }
        source.add('\n');
        if (m.media) {
          source.add('}\n');
        }
      }
    }
    return new ConcatSource(externalsSource, source);
  }
}

ExtractCssChunks.loader = require.resolve('./loader');

export default ExtractCssChunks;
<|endoftext|>


<commit_before>import React from 'react';
import { findDOMNode } from 'react-dom';
import throttle from 'lodash.throttle';
import raf from 'raf';
import getDisplayName from 'react-display-name';
import hoist from 'hoist-non-react-statics';
import { noop, intBetween } from './util';

const DEFAULT_BUFFER = 150;

export function createHorizontalStrength(_buffer) {
  return function defaultHorizontalStrength({ x, w }, point) {
    const buffer = Math.min(w / 2, _buffer);

    if (point.x >= x && point.x <= x + w) {
      if (point.x < x + buffer) {
        return (point.x - x - buffer) / buffer;
      } else if (point.x > (x + w - buffer)) {
        return -(x + w - point.x - buffer) / buffer;
      }
    }

    return 0;
  };
}

export function createVerticalStrength(_buffer) {
  return function defaultVerticalStrength({ y, h }, point) {
    const buffer = Math.min(h / 2, _buffer);

    if (point.y >= y && point.y <= y + h) {
      if (point.y < y + buffer) {
        return (point.y - y - buffer) / buffer;
      } else if (point.y > (y + h - buffer)) {
        return -(y + h - point.y - buffer) / buffer;
      }
    }

    return 0;
  };
}

export const defaultHorizontalStrength = createHorizontalStrength(DEFAULT_BUFFER);

export const defaultVerticalStrength = createVerticalStrength(DEFAULT_BUFFER);

export default function createScrollingComponent(WrappedComponent) {
  class ScrollingComponent extends React.Component {

    static displayName = `Scrolling(${getDisplayName(WrappedComponent)})`;

    static propTypes = {
      onScrollChange: React.PropTypes.func,
      verticalStrength: React.PropTypes.func,
      horizontalStrength: React.PropTypes.func,
      speed: React.PropTypes.number,
    };

    static defaultProps = {
      onScrollChange: noop,
      verticalStrength: defaultVerticalStrength,
      horizontalStrength: defaultHorizontalStrength,
      speed: 30,
    };

    static contextTypes = { dragDropManager: React.PropTypes.object };

    constructor(props, ctx) {
      super(props, ctx);

      this.scaleX = 0;
      this.scaleY = 0;
      this.frame = null;
      this.attached = false;
      this.state = {
        isDragging: false,
      };
    }

    componentDidMount() {
      this.container = findDOMNode(this.wrappedInstance);
      this.container.addEventListener('touchmove', this.handleDragOver);
      this.container.addEventListener('dragover', this.handleDragOver);

      this.clearMonitorSubscription =
        this.context
          .dragDropManager
          .getMonitor()
          .subscribeToStateChange(() => this.handleMonitorChange());
    }

    componentWillUnmount() {
      this.clearMonitorSubscription();
      if (this.frame) { raf.cancel(this.frame); }
      this.detach();
    }

    getCoords(evt) {
      if (evt.type === 'touchmove') {
        return { x: evt.changedTouches[0].clientX, y: evt.changedTouches[0].clientY };
      }

      return { x: evt.clientX, y: evt.clientY };
    }

    handleMonitorChange() {
      const isDragging = this.context.dragDropManager.getMonitor().isDragging();
      if (this.state.isDragging !== isDragging) {
        this.setState({ isDragging });
      }
    }

    attach() {
      window.document.body.addEventListener('touchmove', this.updateScrolling);
      window.document.body.addEventListener('dragover', this.updateScrolling);
      window.document.body.addEventListener('dragend', this.stopScrolling);
      window.document.body.addEventListener('drop', this.stopScrolling);
      this.attached = true;
    }

    detach() {
      window.document.body.removeEventListener('touchmove', this.updateScrolling);
      window.document.body.removeEventListener('dragover', this.updateScrolling);
      window.document.body.removeEventListener('dragend', this.stopScrolling);
      window.document.body.removeEventListener('drop', this.stopScrolling);
      this.attached = false;
    }

    // we don't care about the body's dragover events until this
    // component gets dragged over for the first time
    handleDragOver = (evt, ...rest) => {
      // give users a chance to preventDefault
      if (typeof this.props.onDragOver === 'function') { this.props.onDragOver(evt, ...rest); }

      if (!this.attached) {
        this.attach();
        this.updateScrolling(evt);
      }
    }

    // Update scaleX and scaleY every 100ms or so
    // and start scrolling if necessary
    updateScrolling = throttle(evt => {
      const { left: x, top: y, width: w, height: h } = this.container.getBoundingClientRect();
      const box = { x, y, w, h };
      const coords = this.getCoords(evt);

      // calculate strength
      this.scaleX = this.props.horizontalStrength(box, coords);
      this.scaleY = this.props.verticalStrength(box, coords);

      // start scrolling if we need to
      if (this.state.isDragging && !this.frame && (this.scaleX || this.scaleY)) {
        this.startScrolling();
      }
    }, 100, { trailing: false })

    startScrolling() {
      let i = 0;
      const tick = () => {
        const { scaleX, scaleY, container } = this;
        const { speed, onScrollChange } = this.props;

        // stop scrolling if there's nothing to do
        if (speed === 0 || scaleX + scaleY === 0 || !this.state.isDragging) {
          this.stopScrolling();
          return;
        }

        // there's a bug in safari where it seems like we can't get
        // dragover events from a container that also emits a scroll
        // event that same frame. So we double the speed and only adjust
        // the scroll position at 30fps
        if (i++ % 2) {
          const {
            scrollLeft,
            scrollTop,
            scrollWidth,
            scrollHeight,
            clientWidth,
            clientHeight,
          } = container;

          const newLeft = scaleX
            ? container.scrollLeft = intBetween(
              0,
              scrollWidth - clientWidth,
              scrollLeft + scaleX * speed
            )
            : scrollLeft;

          const newTop = scaleY
            ? container.scrollTop = intBetween(
              0,
              scrollHeight - clientHeight,
              scrollTop + scaleY * speed
            )
            : scrollTop;

          onScrollChange(newLeft, newTop);
        }
        this.frame = raf(tick);
      };

      tick();
    }

    stopScrolling = () => {
      if (this.frame) {
        this.detach();
        raf.cancel(this.frame);
        this.frame = null;
        this.scaleX = 0;
        this.scaleY = 0;
      }
    }

    render() {
      const {
        // not passing down these props
        speed,
        verticalStrength,
        horizontalStrength,
        onScrollChange,

        ...props,
      } = this.props;

      return (
        <WrappedComponent
          ref={(ref) => { this.wrappedInstance = ref; }}
          {...props}
        />
      );
    }
  }

  return hoist(ScrollingComponent, WrappedComponent);
}
<commit_msg>Touchmove started outside our container should be handled.<commit_after>import React from 'react';
import { findDOMNode } from 'react-dom';
import throttle from 'lodash.throttle';
import raf from 'raf';
import getDisplayName from 'react-display-name';
import hoist from 'hoist-non-react-statics';
import { noop, intBetween } from './util';

const DEFAULT_BUFFER = 150;

export function createHorizontalStrength(_buffer) {
  return function defaultHorizontalStrength({ x, w }, point) {
    const buffer = Math.min(w / 2, _buffer);

    if (point.x >= x && point.x <= x + w) {
      if (point.x < x + buffer) {
        return (point.x - x - buffer) / buffer;
      } else if (point.x > (x + w - buffer)) {
        return -(x + w - point.x - buffer) / buffer;
      }
    }

    return 0;
  };
}

export function createVerticalStrength(_buffer) {
  return function defaultVerticalStrength({ y, h }, point) {
    const buffer = Math.min(h / 2, _buffer);

    if (point.y >= y && point.y <= y + h) {
      if (point.y < y + buffer) {
        return (point.y - y - buffer) / buffer;
      } else if (point.y > (y + h - buffer)) {
        return -(y + h - point.y - buffer) / buffer;
      }
    }

    return 0;
  };
}

export const defaultHorizontalStrength = createHorizontalStrength(DEFAULT_BUFFER);

export const defaultVerticalStrength = createVerticalStrength(DEFAULT_BUFFER);

export default function createScrollingComponent(WrappedComponent) {
  class ScrollingComponent extends React.Component {

    static displayName = `Scrolling(${getDisplayName(WrappedComponent)})`;

    static propTypes = {
      onScrollChange: React.PropTypes.func,
      verticalStrength: React.PropTypes.func,
      horizontalStrength: React.PropTypes.func,
      speed: React.PropTypes.number,
    };

    static defaultProps = {
      onScrollChange: noop,
      verticalStrength: defaultVerticalStrength,
      horizontalStrength: defaultHorizontalStrength,
      speed: 30,
    };

    static contextTypes = { dragDropManager: React.PropTypes.object };

    constructor(props, ctx) {
      super(props, ctx);

      this.scaleX = 0;
      this.scaleY = 0;
      this.frame = null;
      this.attached = false;
      this.state = {
        isDragging: false,
      };
    }

    componentDidMount() {
      this.container = findDOMNode(this.wrappedInstance);
      this.container.addEventListener('dragover', this.handleDragOver);
      window.document.body.addEventListener('touchmove', this.handleTouchMove);

      this.clearMonitorSubscription =
        this.context
          .dragDropManager
          .getMonitor()
          .subscribeToStateChange(() => this.handleMonitorChange());
    }

    componentWillUnmount() {
      this.clearMonitorSubscription();
      if (this.frame) { raf.cancel(this.frame); }
      window.document.body.removeEventListener('touchmove', this.handleTouchMove);
      this.detach();
    }

    getCoords(evt) {
      if (evt.type === 'touchmove') {
        return { x: evt.changedTouches[0].clientX, y: evt.changedTouches[0].clientY };
      }

      return { x: evt.clientX, y: evt.clientY };
    }

    attach() {
      window.document.body.addEventListener('dragover', this.updateScrolling);
      window.document.body.addEventListener('dragend', this.stopScrolling);
      window.document.body.addEventListener('drop', this.stopScrolling);
      this.attached = true;
    }

    detach() {
      window.document.body.removeEventListener('dragover', this.updateScrolling);
      window.document.body.removeEventListener('dragend', this.stopScrolling);
      window.document.body.removeEventListener('drop', this.stopScrolling);
      this.attached = false;
    }

    // we don't care about the body's dragover events until this
    // component gets dragged over for the first time
    handleDragOver = (evt, ...rest) => {
      // give users a chance to preventDefault
      if (typeof this.props.onDragOver === 'function') { this.props.onDragOver(evt, ...rest); }

      if (!this.attached) {
        this.attach();
        this.updateScrolling(evt);
      }
    }

    // we need to catch touchmove events
    handleTouchMove = (evt, ...rest) => {
      // give users a chance to preventDefault
      if (typeof this.props.onDragOver === 'function') { this.props.onDragOver(evt, ...rest); }
      this.updateScrolling(evt);
    }

    handleMonitorChange() {
      const isDragging = this.context.dragDropManager.getMonitor().isDragging();
      if (this.state.isDragging !== isDragging) {
        this.setState({ isDragging });
      }
    }

    // Update scaleX and scaleY every 100ms or so
    // and start scrolling if necessary
    updateScrolling = throttle(evt => {
      const { left: x, top: y, width: w, height: h } = this.container.getBoundingClientRect();
      const box = { x, y, w, h };
      const coords = this.getCoords(evt);

      // calculate strength
      this.scaleX = this.props.horizontalStrength(box, coords);
      this.scaleY = this.props.verticalStrength(box, coords);

      // start scrolling if we need to
      if (this.state.isDragging && !this.frame && (this.scaleX || this.scaleY)) {
        this.startScrolling();
      }
    }, 100, { trailing: false })

    startScrolling() {
      let i = 0;
      const tick = () => {
        const { scaleX, scaleY, container } = this;
        const { speed, onScrollChange } = this.props;

        // stop scrolling if there's nothing to do
        if (speed === 0 || scaleX + scaleY === 0 || !this.state.isDragging) {
          this.stopScrolling();
          return;
        }

        // there's a bug in safari where it seems like we can't get
        // dragover events from a container that also emits a scroll
        // event that same frame. So we double the speed and only adjust
        // the scroll position at 30fps
        if (i++ % 2) {
          const {
            scrollLeft,
            scrollTop,
            scrollWidth,
            scrollHeight,
            clientWidth,
            clientHeight,
          } = container;

          const newLeft = scaleX
            ? container.scrollLeft = intBetween(
              0,
              scrollWidth - clientWidth,
              scrollLeft + scaleX * speed
            )
            : scrollLeft;

          const newTop = scaleY
            ? container.scrollTop = intBetween(
              0,
              scrollHeight - clientHeight,
              scrollTop + scaleY * speed
            )
            : scrollTop;

          onScrollChange(newLeft, newTop);
        }
        this.frame = raf(tick);
      };

      tick();
    }

    stopScrolling = () => {
      if (this.frame) {
        this.detach();
        raf.cancel(this.frame);
        this.frame = null;
        this.scaleX = 0;
        this.scaleY = 0;
      }
    }

    render() {
      const {
        // not passing down these props
        speed,
        verticalStrength,
        horizontalStrength,
        onScrollChange,

        ...props,
      } = this.props;

      return (
        <WrappedComponent
          ref={(ref) => { this.wrappedInstance = ref; }}
          {...props}
        />
      );
    }
  }

  return hoist(ScrollingComponent, WrappedComponent);
}
<|endoftext|>


<commit_before>/**
 * Created by wajihakanwal on 6/14/17.
 */

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';

function Square(props) {
    return (
        <button className="square" onClick={props.onClick}>
            {props.value}
        </button>
    );
}

class Board extends React.Component {
    constructor() {
        super();
        this.state = {
            squares: Array(9).fill(null),
            xIsNext: true
        };
    }

    renderSquare(i) {
        return <Square
            value={this.state.squares[i]}
            onClick={() => this.handleClick(i)}
        />;
    }

    handleClick(i) {
        const squares = this.state.squares.slice();
        squares[i] = this.state.xIsNext ? 'X' : 'O';
        this.setState({
            squares: squares,
            xIsNext: !this.state.xIsNext
        });
    }

    render() {
        const status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');

        return (

{status}

                    {this.renderSquare(0)}
                    {this.renderSquare(1)}
                    {this.renderSquare(2)}

                    {this.renderSquare(3)}
                    {this.renderSquare(4)}
                    {this.renderSquare(5)}

                    {this.renderSquare(6)}
                    {this.renderSquare(7)}
                    {this.renderSquare(8)}

        );
    }
}

class Game extends React.Component {
    render() {
        return (

                    <Board />

{/* status */}

{/* TODO */}

        );
    }
}

// class ShoppingList extends React.Component{
//     render() {
//         return (
//             React.createElement(
//                 'div',
//                 {className: 'MyShoppingList'},
//                 React.createElement(
//                     'h1',
//                     null,
//                     'My Shopping List'
//                 ),
//                 React.createElement(
//                     'ul',
//                     null,
//                     React.createElement(
//                         'li',
//                         null,
//                         'Potatoes'
//                     ),
//                     React.createElement(
//                         'li',
//                         null,
//                         'Cabbage'
//                     ),
//                     React.createElement(
//                         'li',
//                         null,
//                         'Flour'
//                     )
//                 )
//             )
//         )
//     }
// }

// ========================================

// ReactDOM.render(
//     <ShoppingList />,
//     document.getElementById('root')
// );
ReactDOM.render(
    <Game />,
    document.getElementById('root')
);
<commit_msg>Declare winner<commit_after>/**
 * Created by wajihakanwal on 6/14/17.
 */

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';

function Square(props) {
    return (
        <button className="square" onClick={props.onClick}>
            {props.value}
        </button>
    );
}

class Board extends React.Component {
    constructor() {
        super();
        this.state = {
            squares: Array(9).fill(null),
            xIsNext: true
        };
    }

    renderSquare(i) {
        return <Square
            value={this.state.squares[i]}
            onClick={() => this.handleClick(i)}
        />;
    }

    handleClick(i) {
        const squares = this.state.squares.slice();
        if (calculateWinner(squares) || squares[i]) {
            return;
        }
        squares[i] = this.state.xIsNext ? 'X' : 'O';
        this.setState({
            squares: squares,
            xIsNext: !this.state.xIsNext
        });
    }

    render() {
        const winner = calculateWinner(this.state.squares);
        let status;
        if (winner) {
            status = 'Winner: ' + winner;
        } else {
            status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');
        }

        return (

{status}

                    {this.renderSquare(0)}
                    {this.renderSquare(1)}
                    {this.renderSquare(2)}

                    {this.renderSquare(3)}
                    {this.renderSquare(4)}
                    {this.renderSquare(5)}

                    {this.renderSquare(6)}
                    {this.renderSquare(7)}
                    {this.renderSquare(8)}

        );
    }
}

class Game extends React.Component {
    render() {
        return (

                    <Board />

{/* status */}

{/* TODO */}

        );
    }
}

function calculateWinner(squares) {
    const lines = [
        [0, 1, 2],
        [3, 4, 5],
        [6, 7, 8],
        [0, 3, 6],
        [1, 4, 7],
        [2, 5, 8],
        [0, 4, 8],
        [2, 4, 6],
    ];
    for (let i = 0; i < lines.length; i++) {
        const [a, b, c] = lines[i];
        if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
            return squares[a];
        }
    }
    return null;
}

ReactDOM.render(
    <Game />,
    document.getElementById('root')
);
<|endoftext|>


<commit_before>/* Index module */
if (typeof define !== 'function') {
    var define = require('amdefine')(module);
}
define(["./form", "./client", "./meta", "./modal"], function(form, client, meta, modal) {
   /**
    * The main include path for our public API
    * @exports nor-ui
    * @version 0.1.0
    */
	var index = {
		'form':   form,
		'client': client,
		'meta':   meta,
		'modal':  modal
	};
	return index;
});
<commit_msg>Testing @borrows<commit_after>/* Index module */
if (typeof define !== 'function') {
    var define = require('amdefine')(module);
}
define(["./form", "./client", "./meta", "./modal"], function(form, client, meta, modal) {
	/**
	 * The main include path for our public API
	 * @exports nor-ui
	 * @version 0.1.0
	 * @namespace
	 * @borrows form as nor-ui/form
	 * @borrows client as nor-ui/client
	 * @borrows meta as nor-ui/meta
	 * @borrows modal as nor-ui/modal
	 */
	var index = {
		'form':   form,
		'client': client,
		'meta':   meta,
		'modal':  modal
	};
	return index;
});
<|endoftext|>


<commit_before>'use strict';
const Alexa = require('alexa-sdk');
const itemList = require('./items.json').items;

const APP_ID = 'amzn1.ask.skill.480b7029-32cb-4d29-9a9c-dac3d9c4316e';

exports.handler = function(event, context, callback) {
    let alexa = Alexa.handler(event, context);
    alexa.APP_ID = APP_ID;
    alexa.registerHandlers(handlers);
    alexa.execute();
};

let handlers = {
    'LaunchRequest': function () {
        this.emit('StartSuitecaseGameIntent');
    },
    'StartSuitecaseGameIntent': function () {
        this.emit(':ask', 'Schön, dass du mit mir spielen willst. Du fängst an, was nimmst du mit?');
    },
    'RepeatAndExtendIntent': function () {
        let repeatedItems, newUserItem, newAlexaItem;

        // first, check if this is the first item
        if (!this.attributes['items']) {
            this.attributes['items'] = [];
        // if it is not the first item check the repeated items
        } else {
            let repeatedItemsSlot = this.event.request.intent.slots.Items;
            if (repeatedItemsSlot && repeatedItemsSlot.value) {
                let repeatedItemsArray = _getItemArrayFromString(repeatedItemsSlot.value);

                // check, if items are repeated correctly
                if ( !_areItemsCorrect(repeatedItemsArray, this.attributes['items']) ) {
                    this.emit('GameOverIntend');
                    return;
                }
            }
        }

        // save the user's new item to the session
        let itemSlot = this.event.request.intent.slots.Item;
        if (itemSlot && itemSlot.value) {
            newUserItem = itemSlot.value;
            this.attributes['items'].push(newUserItem);
        } else {
            console.warn('No new user item found');
        }

        // get a new random item and save it
        newAlexaItem = _getRandomItem();
        this.attributes['items'].push(newAlexaItem);

        this.emit(':ask', 'Ich packe meinen Koffer und nehme mit. ' + _getItemStringfromArray(this.attributes['items']) );
    },
    'GameOverIntend': function() {
        this.emit(':tell', 'Leider war das nicht richtig. Du hast verloren.');
    },
    'SessionEndedRequest': function() {
        this.emit(':tell', 'Danke fürs Spielen.');
    }
};

function _areItemsCorrect(repeatedItems, savedItems) {
    if (repeatedItems.length !== savedItems.length) {
        return false;
    }
    return savedItems.filter(function(item) {
        return !repeatedItems.includes(item);
    }).length === 0;
}

function _getRandomItem() {
    return itemList[ Math.floor(Math.random() * itemList.length) ];
}

function _getItemArrayFromString(itemString) {
    return itemString.split(' ');
}

function _getItemStringfromArray(itemArray) {
    let itemsWithoutLast = itemArray.slice(0, itemArray.length - 1);
    const lastItem = itemArray[itemArray.length - 1];
    return itemsWithoutLast.join(', ') + ' und ' + lastItem;
}<commit_msg>moved improved game over logic to GameOverIntent<commit_after>'use strict';
const Alexa = require('alexa-sdk');
const itemList = require('./items.json').items;

const APP_ID = 'amzn1.ask.skill.480b7029-32cb-4d29-9a9c-dac3d9c4316e';

exports.handler = function(event, context, callback) {
    let alexa = Alexa.handler(event, context);
    alexa.APP_ID = APP_ID;
    alexa.registerHandlers(handlers);
    alexa.execute();
};

let handlers = {
    'LaunchRequest': function () {
        this.emit('StartSuitecaseGameIntent');
    },
    'StartSuitecaseGameIntent': function () {
        this.emit(':ask', 'Schön, dass du mit mir spielen willst. Du fängst an, was nimmst du mit?');
    },
    'RepeatAndExtendIntent': function () {
        let repeatedItems, newUserItem, newAlexaItem;

        // first, check if this is the first item
        if (!this.attributes['items']) {
            this.attributes['items'] = [];
        // if it is not the first item check the repeated items
        } else {
            let repeatedItemsSlot = this.event.request.intent.slots.Items;
            if (repeatedItemsSlot && repeatedItemsSlot.value) {
                let repeatedItemsArray = _getItemArrayFromString(repeatedItemsSlot.value);

                // check, if items are repeated correctly
                if ( !_areItemsCorrect(repeatedItemsArray, this.attributes['items']) ) {
                    this.emit('GameOverIntend', repeatedItemsArray);
                    return;
                }
            }
        }

        // save the user's new item to the session
        let itemSlot = this.event.request.intent.slots.Item;
        if (itemSlot && itemSlot.value) {
            newUserItem = itemSlot.value;
            this.attributes['items'].push(newUserItem);
        } else {
            console.warn('No new user item found');
        }

        // get a new random item and save it
        newAlexaItem = _getRandomItem();
        this.attributes['items'].push(newAlexaItem);

        this.emit(':ask', 'Ich packe meinen Koffer und nehme mit. ' + _getItemStringfromArray(this.attributes['items']) );
    },
    'GameOverIntend': function(repeatedItemsArray) {
        let gameOverMessage = 'Leider war das nicht richtig.';

        let forgottenItems = _getForgottenItems(repeatedItemsArray, this.attributes['items']);
        if (forgottenItems.length) {
            gameOverMessage += ' Du hast ' + _getItemStringfromArray(forgottenItems) + ' vergessen.';
        }

        let unnecessaryItems = _getUnnecessaryItems(repeatedItemsArray, this.attributes['items']);
        if (unnecessaryItems.length) {
            gameOverMessage += ' ' + _getItemStringfromArray(unnecessaryItems) + ' hat niemand eingepackt.';
        }

        gameOverMessage += ' Du hast damit verloren.';

        this.emit(':tell', gameOverMessage);
    },
    'SessionEndedRequest': function() {
        this.emit(':tell', 'Danke fürs Spielen.');
    }
};

function _areItemsCorrect(repeatedItems, savedItems) {
    // if length of items is not equal we can instantly stop here
    if (repeatedItems.length !== savedItems.length) {
        return false;
    }
    // if length of items is equal we can still have forgotten and unnecessary items
    let areItemsForgotten = _getForgottenItems(repeatedItems, savedItems).length !== 0;
    let areItemsUnnecessary = _getUnnecessaryItems(repeatedItems, savedItems).length !== 0;
    return !areItemsForgotten && !areItemsUnnecessary;
}

function _getForgottenItems(repeatedItems, savedItems) {
    return savedItems.filter(function(item) {
        return !repeatedItems.includes(item);
    });
}

function _getUnnecessaryItems(repeatedItems, savedItems) {
    return repeatedItems.filter(function(item) {
        return !savedItems.includes(item);
    });
}

function _getRandomItem() {
    return itemList[ Math.floor(Math.random() * itemList.length) ];
}

function _getItemArrayFromString(itemString) {
    return itemString.split(' ');
}

function _getItemStringfromArray(itemArray) {
    if (itemArray.length === 0) {
        return '';
    }
    if (itemArray.length === 1) {
        return itemArray[0];
    }
    let itemsWithoutLast = itemArray.slice(0, itemArray.length - 1);
    const lastItem = itemArray[itemArray.length - 1];
    return itemsWithoutLast.join(', ') + ' und ' + lastItem;
}<|endoftext|>


<commit_before>import express from 'express';
import cors from 'cors';
import mongoose from 'mongoose';
import Promise from 'bluebird';
import bodyParser from 'body-parser';

import saveDataInDb from './saveDataInDb';
import Pet from './models/Pet';
import User from './models/User';
import isAdmin from './middlewares/isAdmin';

mongoose.Promise = Promise;
mongoose.connect('mongodb://publicdb.mgbeta.ru/airakobra45_skb3');

const app = express();
app.use(cors());
app.use(bodyParser.json());
app.use(isAdmin);

app.get('/clear', async(req, res) => {
  await User.remove({});
  await Pet.remove({});
  return res.send('OK');
});
app.get('/users', async(req, res) => {
  const users = await User.find();
  return res.json(users);
});
app.get('/pets', async(req, res) => {
  const pets = await Pet.find().populate('owner');
  return res.json(pets); //json(pets.slice(+pets.length-2)
});
app.post('/data', async(req, res) => {
  const data = req.body;
  if (!data.user) return res.status(400).send('User required');
  if (!data.pets) data.pets = [];

  const user = await User.findOne({
    name: data.user.name,
  });
  if (user) return res.status(400).send('User: "' + user.name + '" is exists!');

  try {
    const result = await saveDataInDb(data);
    return res.json(result);
  } catch (err) {

    return res.status(500).json(err);
  }

});

app.listen(3000, () => {
  console.log('Your app listening on port 3000!');
});
<commit_msg>isAdmin added to Clear<commit_after>import express from 'express';
import cors from 'cors';
import mongoose from 'mongoose';
import Promise from 'bluebird';
import bodyParser from 'body-parser';

import saveDataInDb from './saveDataInDb';
import Pet from './models/Pet';
import User from './models/User';
import isAdmin from './middlewares/isAdmin';

mongoose.Promise = Promise;
mongoose.connect('mongodb://publicdb.mgbeta.ru/airakobra45_skb3');

const app = express();
app.use(cors());
app.use(bodyParser.json());
//app.use(isAdmin);

app.get('/clear', isAdmin, async(req, res) => {
  await User.remove({});
  await Pet.remove({});
  return res.send('OK');
});
app.get('/users', async(req, res) => {
  const users = await User.find();
  return res.json(users);
});
app.get('/pets', async(req, res) => {
  const pets = await Pet.find().populate('owner');
  return res.json(pets); //json(pets.slice(+pets.length-2)
});
app.post('/data', async(req, res) => {
  const data = req.body;
  if (!data.user) return res.status(400).send('User required');
  if (!data.pets) data.pets = [];

  const user = await User.findOne({
    name: data.user.name,
  });
  if (user) return res.status(400).send('User: "' + user.name + '" is exists!');

  try {
    const result = await saveDataInDb(data);
    return res.json(result);
  } catch (err) {

    return res.status(500).json(err);
  }

});

app.listen(3000, () => {
  console.log('Your app listening on port 3000!');
});
<|endoftext|>


<commit_before>import React from 'react';
import ReactDOM from 'react-dom';
import { Router, useRouterHistory } from 'react-router';
import { createHashHistory } from 'history';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import reducer from './reducer';
import routes from './routes';
import injectTapEventPlugin from 'react-tap-event-plugin';

// Needed for onTouchTap
// http://stackoverflow.com/a/34015469/988941
injectTapEventPlugin();

const store = createStore(reducer,
  window.devToolsExtension ? window.devToolsExtension() : undefined
);

// Set initial (test) state.
store.dispatch({
  type: 'SET_STATE',
  state: {
    mainForm: {
      includeClasses: false,
      classTotal: {
        grade: null,
        credits: null,
      },
      canSubmit: true,
    },
    classes: [],
    snackbar: {
      open: false,
      message: '',
    },
    classForm: {
      selectedClass: 0,
      canSubmit: true,
      dialogOpen: false,
    },
  },
});

const appHistory = useRouterHistory(createHashHistory)({ queryKey: false });

ReactDOM.render(
    <Provider store={store}>
      <Router history={appHistory} onUpdate={() => window.scrollTo(0, 0)}>
          {routes}
      </Router>
    </Provider>,
    document.getElementById('app')
);
<commit_msg>Updated initial state.<commit_after>import React from 'react';
import ReactDOM from 'react-dom';
import { Router, useRouterHistory } from 'react-router';
import { createHashHistory } from 'history';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import { fromJS } from 'immutable';
import reducer from './reducer';
import routes from './routes';
import injectTapEventPlugin from 'react-tap-event-plugin';

// Needed for onTouchTap
// http://stackoverflow.com/a/34015469/988941
injectTapEventPlugin();

const initialState = fromJS({
  mainForm: {
    includeClasses: false,
    classTotal: {
      grade: 0,
      credits: 0,
    },
    canSubmit: true,
  },
  classes: [],
  snackbar: {
    open: false,
    message: '',
  },
  classForm: {
    selectedClass: 0,
    canSubmit: true,
    dialogOpen: false,
  },
});

const store = createStore(
  reducer,
  initialState,
  window.devToolsExtension ? window.devToolsExtension() : undefined
);

const appHistory = useRouterHistory(createHashHistory)({ queryKey: false });

ReactDOM.render(
    <Provider store={store}>
      <Router history={appHistory} onUpdate={() => window.scrollTo(0, 0)}>
          {routes}
      </Router>
    </Provider>,
    document.getElementById('app')
);
<|endoftext|>


<commit_before>(function() {
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
  module.exports = domify
} else {
  Node.prototype.dom = domify
  Document.prototype.dom = domify
}

/**
 * Just generates some kind-of-random ID. It just needs to be distinguishable from regular IDs
 * @return {string}  The generated ID
 */
let counter = 0
function generateId () {
  counter++
  return `p-${counter}-${Date.now()}`
}

/**
 * Generates an array of DOM Nodes
 * @param  {...any} partials   Might be anything. DOM Nodes are handled, arrays are iterated over and then handled, everything else just gets passed through
 * @return {Node[]}            An array of DOM Nodes
 */
function generateNodes (doc, ...partials) {
  // Array of placeholder IDs
  const placeholders = []

  // Generate regular HTML string first
  const html = partials.reduce((carry, partial) => {
    if (Array.isArray(partial)) {
      carry.concat(partial)
    } else if (typeof partial === 'object' && partial instanceof Node) {
      const id = generateId()
      placeholders.push({ id, node: partial })
      return carry.concat(`<${partial.nodeName} id="${id}"></${partial.nodeName}>`)
    } else {
      return carry.concat(partial)
    }
  }, []).join('')

  // Wrap in temporary container node
  let template = doc.createElement('template')
  template.innerHTML = html
  let container = template.content

  // Replace placeholders with real Nodes
  placeholders.forEach(({ id, node }) => {
    const placeholder = container.querySelector(`${node.nodeName}#${id}`)
    placeholder.parentNode.replaceChild(node, placeholder)
  })

  // Get array of Nodes
  return container
}

/**
 * A function that is suitable to be used as a function for tagged template strings
 * @param  {string[]}    strings  The literal parts of the template string
 * @param  {...values}   values   The interpolated parts of the template string
 * @return {Node[]}               An array of DOM Nodes
 */
function taggedTemplateHandler (doc, strings, ...values) {
  // Create an array that puts the values back in their place
  const arr = strings.reduce((carry, current, index) => {
    return carry.concat(current, (index + 1 === strings.length) ? [] : values[index])
  }, [])

  // Generate the Node array
  return generateNodes(doc, ...arr)
}

function domify (strings, ...values) {
  var doc = document
  if (this) {
    if (this.nodeType == Node.DOCUMENT_NODE) doc = this
    else if (this.ownerDocument) doc = this.ownerDocument
  }
  let result = taggedTemplateHandler(doc, strings, ...values)
  if (result.childNodes.length == 1) {
    let child = result.firstChild
    result.removeChild(child)
    return child
  } else {
    return result
  }
}

})()
<commit_msg>Implement support for DocumentFragment and NodeList<commit_after>(function() {
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
  module.exports = domify
} else {
  Node.prototype.dom = domify
  Document.prototype.dom = domify
}

/**
 * Just generates some kind-of-random ID. It just needs to be distinguishable from regular IDs
 * @return {string}  The generated ID
 */
let counter = 0
function generateId () {
  counter++
  return `p-${counter}-${Date.now()}`
}

/**
 * Generates an array of DOM Nodes
 * @param  {...any} partials   Might be anything. DOM Nodes are handled, arrays are iterated over and then handled, everything else just gets passed through
 * @return {Node[]}            An array of DOM Nodes
 */
function generateNodes (doc, ...partials) {
  // Array of placeholder IDs
  const placeholders = []
  // Generate regular HTML string first
  function reducer(carry, partial) {
    if (partial && partial.nodeType == Node.DOCUMENT_FRAGMENT_NODE) {
      partial = partial.childNodes
    }
    if (Array.isArray(partial)) {
      carry.concat(partial)
    } else if (typeof partial === 'object' && partial instanceof Node) {
      const id = generateId()
      placeholders.push({ id, node: partial })
      return carry.concat(`<${partial.nodeName} id="${id}"></${partial.nodeName}>`)
    } else if (partial && typeof partial.item == "function" && typeof partial.length == "number") {
      return carry.concat(Array.prototype.reduce.call(partial, reducer, []))
    } else {
      return carry.concat(partial)
    }
  }
  const html = partials.reduce(reducer, []).join('')

  // Wrap in temporary container node
  let template = doc.createElement('template')
  template.innerHTML = html
  let container = template.content

  // Replace placeholders with real Nodes
  placeholders.forEach(({ id, node }) => {
    const placeholder = container.querySelector(`${node.nodeName}#${id}`)
    placeholder.parentNode.replaceChild(node, placeholder)
  })

  // Get array of Nodes
  return container
}

/**
 * A function that is suitable to be used as a function for tagged template strings
 * @param  {string[]}    strings  The literal parts of the template string
 * @param  {...values}   values   The interpolated parts of the template string
 * @return {Node[]}               An array of DOM Nodes
 */
function taggedTemplateHandler (doc, strings, ...values) {
  // Create an array that puts the values back in their place
  const arr = strings.reduce((carry, current, index) => {
    return carry.concat(current, (index + 1 === strings.length) ? [] : values[index])
  }, [])

  // Generate the Node array
  return generateNodes(doc, ...arr)
}

function domify (strings, ...values) {
  var doc = document
  if (this) {
    if (this.nodeType == Node.DOCUMENT_NODE) doc = this
    else if (this.ownerDocument) doc = this.ownerDocument
  }
  let result = taggedTemplateHandler(doc, strings, ...values)
  if (result.childNodes.length == 1) {
    let child = result.firstChild
    result.removeChild(child)
    return child
  } else {
    return result
  }
}

})()
<|endoftext|>


<commit_before>'use strict';

const app = require('./app');
const port = app.get('port');
const server = app.listen(port);

server.on('listening', () =>
	console.log(`Feathers application started on ${app.get('host')}:${port}`)
);
<commit_msg>set port to process.env.PORT to make heroku happy<commit_after>'use strict';

const app = require('./app');
const port = process.env.PORT || app.get('port');
const server = app.listen(port);

server.on('listening', () =>
	console.log(`Feathers application started on ${app.get('host')}:${port}`)
);
<|endoftext|>


<commit_before>import React from 'react';

function requiredPropTypesMustExist(props, { propTypes, displayName }) {
  Object.keys(propTypes).every(propName => propTypes[propName](props, propName, displayName) == null);
}

export default function pending(NotReadyComponent) {
  return (hasLoaded = requiredPropTypesMustExist) => (ReadyComponent, ErrorComponent) => ({ error, ...props }) => {
    if (error) {
      return <ErrorComponent error={ error } { ...props } />;
    }
    else if (hasLoaded(props, ReadyComponent)) {
      return <ReadyComponent { ...props } />;
    }
    else {
      return <NotReadyComponent />;
    }
  }
}
<commit_msg>Change code to allow compiling with just ES2015<commit_after>import React from 'react';

function requiredPropTypesMustExist(props, { propTypes, displayName }) {
  Object.keys(propTypes).every(propName => propTypes[propName](props, propName, displayName) == null);
}

export default function pending(NotReadyComponent) {
  return (hasLoaded = requiredPropTypesMustExist) => (ReadyComponent, ErrorComponent) => (props) => {
    if (props.error) {
      return <ErrorComponent { ...props } />;
    }
    else if (hasLoaded(props, ReadyComponent)) {
      return <ReadyComponent { ...props } />;
    }
    else {
      return <NotReadyComponent />;
    }
  }
}
<|endoftext|>


<commit_before>var docComments = require("./doccomments")
var parseType = require("./parsetype")

exports.gather = function(text, filename, items) {
  if (!items) items = Object.create(null)
  var top = {properties: items}

  var found = docComments.parse(text, filename)

  found.comments.forEach(function(comment) {
    var data = comment.parsed.data

    if (comment.parsed.name) {
      var stack = docComments.findNodeAround(found.ast, comment.end, findPath)
      path = addNameToPath(comment.parsed.name, getPath(stack), data.$static)
    } else {
      var stack = docComments.findNodeAfter(found.ast, comment.end, findPath)
      var node = stack && stack[stack.length - 1]
      if (!node || !/^(?:[;{},\s]|\/\/.*|\/\*.*?\*\/)*$/.test(text.slice(node.end, comment.start)))
        throw new SyntaxError("Misplaced documentation block at " + filename + ":" + comment.startLoc.line)
      if (inferForNode.hasOwnProperty(node.type)) data = inferForNode[node.type](node, data, stack)
      var path = getPath(stack)
    }

    var stored = addData(top, path, data)

    comment.parsed.subcomments.forEach(function(sub) { applySubComment(stored, sub) })
  })

  // Mark locals exported with `export {a, b, c}` statements as exported
  for (var i = 0; i < found.ast.body.length; i++) {
    var node = found.ast.body[i]
    if (node.type == "ExportNamedDeclaration" && !node.source) {
      for (var j = 0; j < node.specifiers.length; j++) {
        var spec = node.specifiers[j]
        var known = items[spec.local.name]
        if (known) known.exported = true
      }
    }
  }

  return items
}

function applySubComment(parent, sub) {
  var target
  if (parent.type == "Function") {
    if (sub.name == "return")
      target = parent.returns
    else if (parent.params) for (var i = 0; i < parent.params; i++)
      if (parent.params[i].name == sub.name) target = parent.params[i]
    if (!target) raise("Unknown parameter " + sub.name, sub)
  } else if (parent.type == "class" || parent.type == "interface" || parent.type == "Object") {
    var path = splitPath(sub.name), target = parent
    for (var i = 0; i < path.length; i++) {
      if (path[i] == "prototype" && i < path.length - 1)
        target = deref(deref(target, "instanceProperties"), path[++i])
      else
        target = deref(deref(target, "properties"), path[i])
    }
  } else {
    raise("Can not add sub-fields to named type " + parent.type, sub)
  }
  var stored = extend(sub.data, target, sub.name)
  sub.subcomments.forEach(function(sub) { applySubComment(stored, sub) })
}

function getPath(ancestors) {
  var top = ancestors[ancestors.length - 1]
  return top ? findPath[top.type](top, ancestors) : []
}

var findPath = {
  // FIXME destructuring
  VariableDeclaration: function(node) { return [node.declarations[0].id.name] },

  VariableDeclarator: function(node) { return [node.id.name] },

  FunctionDeclaration: function(node) { return [node.id.name] },

  ClassDeclaration: function(node) { return [node.id.name] },

  AssignmentExpression: function(node, ancestors) {
    return lvalPath(node.left, ancestors)
  },

  Property: function(node, ancestors) {
    var path = parentPath(ancestors)
    path.push(propName(node, true))
    return path
  },

  MethodDefinition: function(node, ancestors) {
    var path = parentPath(ancestors)
    if (node.kind == "constructor") {
      path.push("#constructor")
    } else {
      if (!node.static) path.push("prototype")
      path.push(propName(node, true))
    }
    return path
  },

  ExportNamedDeclaration: function(node, ancestors) {
    return this[node.declaration.type](node.declaration, ancestors)
  },

  ExportDefaultDeclaration: function() {
    return ["default"]
  }
}

function addNameToPath(name, path, isStatic) {
  var parts = splitPath(name)
  for (var i = 0; i < parts.length; i++) {
    if (path.length && ctorName(path[path.length - 1]) && (!isStatic || i < parts.length - 1))
      path.push("prototype")
    path.push(parts[i])
  }
  return path
}

function addData(top, path, data) {
  var target = top, isCtor = false
  for (var i = 0; i < path.length; i++) {
    var cur = path[i], descend = "properties"
    if (cur == "#constructor") {
      target = deref(target, "constructor")
      break
    }
    if (isCtor) {
      if (cur == "prototype") {
        if (i == path.length - 1) raise("Can not annotate constructor prototype", data)
        cur = path[++i]
      } else {
        descend = "staticProperties"
      }
    }
    target = deref(deref(target, descend), cur)
    isCtor = target.type ? target.type == "class" : ctorName(cur)
  }
  return extend(data, target, path)
}

var inferForNode = {
  VariableDeclaration: function(node, data) {
    var decl0 = node.declarations[0]
    return inferExpr(decl0.init, data, decl0.id.name)
  },

  VariableDeclarator: function(node, data) {
    return inferExpr(node.init, data, node.id.name)
  },

  FunctionDeclaration: function(node, data) {
    return inferFn(node, data, node.id.name)
  },

  ClassDeclaration: inferClass,
  ClassExpression: inferClass,

  AssignmentExpression: function(node, data) {
    return inferExpr(node.right, data, propName(node.left))
  },

  Property: function(node, data) {
    return inferExpr(node.value, data, propName(node, true))
  },

  MethodDefinition: function(node, data) {
    return inferFn(node.value, data)
  },

  ExportNamedDeclaration: function(node, data, ancestors) {
    var inner = this[node.declaration.type](node.declaration, data, ancestors)
    inner.exported = true
    return inner
  },

  ExportDefaultDeclaration: function(node, data, ancestors) {
    var decl = node.declaration
    if (this[decl.type])
      data = this[decl.type](decl, data, ancestors)
    else
      data = inferExpr(decl, data)
    data.exported = true
    return data
  }
}

function raise(msg, node) {
  throw new SyntaxError(msg + " at " + node.loc.source.name + ":" + node.loc.start.line)
}

function propName(node, force) {
  var key = node.key || node.property
  if (!node.computed && key.type == "Identifier") return key.name
  if (key.type == "Literal") {
    if (typeof key.value == "string") return key.value
    if (typeof key.value == "number") return String(key.value)
  }
  if (node.computed && key.type == "MemberExpression" &&
      !key.computed && key.object.name == "Symbol")
    return "[Symbol." + key.property.name + "]"
  if (force) raise("Expected static property", node)
}

function inferParam(n) {
  var param = Object.create(null)
  param.type = "any"
  param.loc = n.loc.start
  param.loc.file = n.loc.source.name
  if (n.type == "RestElement") {
    param.rest = true
    n = n.argument
  }
  if (n.type == "AssignmentPattern") {
    if (n.right.end - n.right.start < 20)
      param.default = n.loc.source.text.slice(n.right.start, n.right.end)
    n = n.left
    param.optional = true
  }
  if (n.type == "Identifier") param.name = n.name
  return param
}

function ctorName(name) {
  return name && /^[A-Z]/.test(name)
}

function inferFn(node, data, name) {
  var inferredParams = node.params.map(inferParam)

  if (!data.type) {
    data.type = "Function"
    data.params = inferredParams
  } else if (data.type == "Function") {
    for (var i = 0, e = Math.min(data.params.length, node.params.length); i < e; i++) {
      var from = inferredParams[i], to = data.params[i]
      for (var prop in from) if (!(prop in to)) to[prop] = from[prop]
    }
  }
  if (node.generator) data.generator = true

  if (ctorName(name)) {
    return {constructor: data, type: "class", loc: data.loc}
  } else {
    return data
  }
}

function inferClass(node, data) {
  if (node.superClass && node.superClass.type == "Identifier") {
    var loc = node.superClass.loc
    loc.start.file = loc.source.name
    data.extends = parseType(node.superClass.name, 0, loc.start).type
  }
  if (!data.type) data.type = "class"
  return data
}

function inferExpr(node, data, name) {
  if (!node) return data
  if (node.type == "ClassExpression") {
    inferClass(node, data)
  } else if (node.type == "FunctionExpression" || node.type == "ArrowFunctionExpression") {
    inferFn(node, data, name)
  } else if (node.type == "Literal" && !data.type) {
    if (typeof node.value == "number") data.type = "number"
    else if (typeof node.value == "boolean") data.type = "bool"
    else if (typeof node.value == "string") data.type = "string"
    else if (node.value instanceof RegExp) data.type = "RegExp"
  } else if (node.type == "NewExpression" && !data.type) {
    if (node.callee.type == "Identifier" && ctorName(node.callee.name))
      data.type = node.callee.name
  }
  return data
}

// Deriving context from ancestor nodes

function extend(from, to, path) {
  for (var prop in from) {
    if (!(prop in to)) {
      to[prop] = from[prop]
    } else if (prop == "properties" || prop == "staticProperties") {
      extend(from[prop], to[prop], path.concat(prop))
    } else {
      var msg = "Conflicting information for " + path.join(".") + "." + prop
      if (to.loc) msg += " at " + to.loc.file + ":" + to.loc.line
      if (from.loc) msg += (to.loc ? " and " : " at ") + from.loc.file + ":" + from.loc.line
      throw new SyntaxError(msg)
    }
  }
  return to
}

function deref(obj, name) {
  return obj[name] || (obj[name] = Object.create(null))
}

function lvalPath(lval, ancestors) {
  var path = []
  while (lval.type == "MemberExpression") {
    path.unshift(propName(lval))
    lval = lval.object
  }

  if (lval.type == "Identifier") {
    path.unshift(lval.name)
  } else if (lval.type == "ThisExpression") {
    path = selfPath(ancestors.slice(0, ancestors.length - 1)).concat(path)
  } else {
    raise("Could not derive a target for this assignment", lval)
  }
  return path
}

function assignedPath(ancestors) {
  var top = ancestors[ancestors.length - 1]
  if (top.type == "VariableDeclarator" && top.id.type == "Identifier")
    return [top.id.name]
  else if (top.type == "AssignmentExpression")
    return lvalPath(top.left, ancestors)
  else
    raise("Could not derive a name", top)
}

function findPrototype(ancestors) {
  var assign = ancestors[ancestors.length - 1]
  if (assign.type != "AssignmentExpression") return null
  var lval = assign.left
  if (lval.type == "MemberExpression" && !lval.computed &&
      lval.object.type == "MemberExpression" && !lval.object.computed &&
      lval.object.property.name == "prototype")
    return lvalPath(lval.object, ancestors)
}

function assignedName(node) {
  if (node.type == "VariableDeclarator" && node.id.type == "Identifier")
    return node.id.name
  else if (node.type == "AssignmentExpression")
    return propName(node.left)
}

function selfPath(ancestors) {
  for (var i = ancestors.length - 1; i >= 0; i--) {
    var ancestor = ancestors[i], found
    if (ancestor.type == "ClassDeclaration" ||
        (ancestor.type == "FunctionDeclaration" && ctorName(ancestor.id.name)))
      return [ancestor.id.name, "prototype"]
    else if (i && (ancestor.type == "ClassExpression" ||
                   ancestor.type == "FunctionExpression" && ctorName(assignedName(ancestors[i - 1]))))
      return assignedPath(ancestors.slice(0, i)).concat("prototype")
    else if (i && /Function/.test(ancestor.type) && (found = findPrototype(ancestors.slice(0, i))))
      return found
  }
  raise("No context found for 'this'", ancestors[ancestors.length - 1])
}

function parentPath(ancestors) {
  for (var i = ancestors.length - 1; i >= 0; i--) {
    var ancestor = ancestors[i]
    if (ancestor.type == "ClassDeclaration")
      return [ancestor.id.name]
    else if (i && (ancestor.type == "ClassExpression" || ancestor.type == "ObjectExpression"))
      return assignedPath(ancestors.slice(0, i))
  }
}

function splitPath(path) {
  var m, parts = [], rest = path
  while (rest && (m = /^(\[.*?\]|[^\s\.#]+)(\.)?/.exec(rest))) {
    parts.push(m[1])
    rest = rest.slice(m[0].length)
    if (!m[2]) break
  }
  if (rest) throw new Error("Invalid path: " + path)
  return parts
}
<commit_msg>Fix handling of sub-comment names<commit_after>var docComments = require("./doccomments")
var parseType = require("./parsetype")

exports.gather = function(text, filename, items) {
  if (!items) items = Object.create(null)
  var top = {properties: items}

  var found = docComments.parse(text, filename)

  found.comments.forEach(function(comment) {
    var data = comment.parsed.data

    if (comment.parsed.name) {
      var stack = docComments.findNodeAround(found.ast, comment.end, findPath)
      path = addNameToPath(comment.parsed.name, getPath(stack), data.$static)
    } else {
      var stack = docComments.findNodeAfter(found.ast, comment.end, findPath)
      var node = stack && stack[stack.length - 1]
      if (!node || !/^(?:[;{},\s]|\/\/.*|\/\*.*?\*\/)*$/.test(text.slice(node.end, comment.start)))
        throw new SyntaxError("Misplaced documentation block at " + filename + ":" + comment.startLoc.line)
      if (inferForNode.hasOwnProperty(node.type)) data = inferForNode[node.type](node, data, stack)
      var path = getPath(stack)
    }

    var stored = addData(top, path, data)

    comment.parsed.subcomments.forEach(function(sub) { applySubComment(stored, sub) })
  })

  // Mark locals exported with `export {a, b, c}` statements as exported
  for (var i = 0; i < found.ast.body.length; i++) {
    var node = found.ast.body[i]
    if (node.type == "ExportNamedDeclaration" && !node.source) {
      for (var j = 0; j < node.specifiers.length; j++) {
        var spec = node.specifiers[j]
        var known = items[spec.local.name]
        if (known) known.exported = true
      }
    }
  }

  return items
}

function applySubComment(parent, sub) {
  var target
  if (parent.type == "Function") {
    if (sub.name == "return")
      target = parent.returns
    else if (parent.params) for (var i = 0; i < parent.params; i++)
      if (parent.params[i].name == sub.name) target = parent.params[i]
    if (!target) raise("Unknown parameter " + sub.name, sub)
  } else if (parent.type == "class" || parent.type == "interface" || parent.type == "Object") {
    var path = splitPath(sub.name), target = parent
    for (var i = 0; i < path.length; i++) {
      var isStatic = i == path.length - 1 && sub.data.$static
      target = deref(deref(target, isStatic ? "staticProperties" : "properties"), path[i])
    }
  } else {
    raise("Can not add sub-fields to named type " + parent.type, sub)
  }
  var stored = extend(sub.data, target, sub.name)
  sub.subcomments.forEach(function(sub) { applySubComment(stored, sub) })
}

function getPath(ancestors) {
  var top = ancestors[ancestors.length - 1]
  return top ? findPath[top.type](top, ancestors) : []
}

var findPath = {
  // FIXME destructuring
  VariableDeclaration: function(node) { return [node.declarations[0].id.name] },

  VariableDeclarator: function(node) { return [node.id.name] },

  FunctionDeclaration: function(node) { return [node.id.name] },

  ClassDeclaration: function(node) { return [node.id.name] },

  AssignmentExpression: function(node, ancestors) {
    return lvalPath(node.left, ancestors)
  },

  Property: function(node, ancestors) {
    var path = parentPath(ancestors)
    path.push(propName(node, true))
    return path
  },

  MethodDefinition: function(node, ancestors) {
    var path = parentPath(ancestors)
    if (node.kind == "constructor") {
      path.push("#constructor")
    } else {
      if (!node.static) path.push("prototype")
      path.push(propName(node, true))
    }
    return path
  },

  ExportNamedDeclaration: function(node, ancestors) {
    return this[node.declaration.type](node.declaration, ancestors)
  },

  ExportDefaultDeclaration: function() {
    return ["default"]
  }
}

function addNameToPath(name, path, isStatic) {
  var parts = splitPath(name)
  for (var i = 0; i < parts.length; i++) {
    if (path.length && ctorName(path[path.length - 1]) && (!isStatic || i < parts.length - 1))
      path.push("prototype")
    path.push(parts[i])
  }
  return path
}

function addData(top, path, data) {
  var target = top, isCtor = false
  for (var i = 0; i < path.length; i++) {
    var cur = path[i], descend = "properties"
    if (cur == "#constructor") {
      target = deref(target, "constructor")
      break
    }
    if (isCtor) {
      if (cur == "prototype") {
        if (i == path.length - 1) raise("Can not annotate constructor prototype", data)
        cur = path[++i]
      } else {
        descend = "staticProperties"
      }
    }
    target = deref(deref(target, descend), cur)
    isCtor = target.type ? target.type == "class" : ctorName(cur)
  }
  return extend(data, target, path)
}

var inferForNode = {
  VariableDeclaration: function(node, data) {
    var decl0 = node.declarations[0]
    return inferExpr(decl0.init, data, decl0.id.name)
  },

  VariableDeclarator: function(node, data) {
    return inferExpr(node.init, data, node.id.name)
  },

  FunctionDeclaration: function(node, data) {
    return inferFn(node, data, node.id.name)
  },

  ClassDeclaration: inferClass,
  ClassExpression: inferClass,

  AssignmentExpression: function(node, data) {
    return inferExpr(node.right, data, propName(node.left))
  },

  Property: function(node, data) {
    return inferExpr(node.value, data, propName(node, true))
  },

  MethodDefinition: function(node, data) {
    return inferFn(node.value, data)
  },

  ExportNamedDeclaration: function(node, data, ancestors) {
    var inner = this[node.declaration.type](node.declaration, data, ancestors)
    inner.exported = true
    return inner
  },

  ExportDefaultDeclaration: function(node, data, ancestors) {
    var decl = node.declaration
    if (this[decl.type])
      data = this[decl.type](decl, data, ancestors)
    else
      data = inferExpr(decl, data)
    data.exported = true
    return data
  }
}

function raise(msg, node) {
  throw new SyntaxError(msg + " at " + node.loc.source.name + ":" + node.loc.start.line)
}

function propName(node, force) {
  var key = node.key || node.property
  if (!node.computed && key.type == "Identifier") return key.name
  if (key.type == "Literal") {
    if (typeof key.value == "string") return key.value
    if (typeof key.value == "number") return String(key.value)
  }
  if (node.computed && key.type == "MemberExpression" &&
      !key.computed && key.object.name == "Symbol")
    return "[Symbol." + key.property.name + "]"
  if (force) raise("Expected static property", node)
}

function inferParam(n) {
  var param = Object.create(null)
  param.type = "any"
  param.loc = n.loc.start
  param.loc.file = n.loc.source.name
  if (n.type == "RestElement") {
    param.rest = true
    n = n.argument
  }
  if (n.type == "AssignmentPattern") {
    if (n.right.end - n.right.start < 20)
      param.default = n.loc.source.text.slice(n.right.start, n.right.end)
    n = n.left
    param.optional = true
  }
  if (n.type == "Identifier") param.name = n.name
  return param
}

function ctorName(name) {
  return name && /^[A-Z]/.test(name)
}

function inferFn(node, data, name) {
  var inferredParams = node.params.map(inferParam)

  if (!data.type) {
    data.type = "Function"
    data.params = inferredParams
  } else if (data.type == "Function") {
    for (var i = 0, e = Math.min(data.params.length, node.params.length); i < e; i++) {
      var from = inferredParams[i], to = data.params[i]
      for (var prop in from) if (!(prop in to)) to[prop] = from[prop]
    }
  }
  if (node.generator) data.generator = true

  if (ctorName(name)) {
    return {constructor: data, type: "class", loc: data.loc}
  } else {
    return data
  }
}

function inferClass(node, data) {
  if (node.superClass && node.superClass.type == "Identifier") {
    var loc = node.superClass.loc
    loc.start.file = loc.source.name
    data.extends = parseType(node.superClass.name, 0, loc.start).type
  }
  if (!data.type) data.type = "class"
  return data
}

function inferExpr(node, data, name) {
  if (!node) return data
  if (node.type == "ClassExpression") {
    inferClass(node, data)
  } else if (node.type == "FunctionExpression" || node.type == "ArrowFunctionExpression") {
    inferFn(node, data, name)
  } else if (node.type == "Literal" && !data.type) {
    if (typeof node.value == "number") data.type = "number"
    else if (typeof node.value == "boolean") data.type = "bool"
    else if (typeof node.value == "string") data.type = "string"
    else if (node.value instanceof RegExp) data.type = "RegExp"
  } else if (node.type == "NewExpression" && !data.type) {
    if (node.callee.type == "Identifier" && ctorName(node.callee.name))
      data.type = node.callee.name
  }
  return data
}

// Deriving context from ancestor nodes

function extend(from, to, path) {
  for (var prop in from) {
    if (!(prop in to)) {
      to[prop] = from[prop]
    } else if (prop == "properties" || prop == "staticProperties") {
      extend(from[prop], to[prop], path.concat(prop))
    } else {
      var msg = "Conflicting information for " + path.join(".") + "." + prop
      if (to.loc) msg += " at " + to.loc.file + ":" + to.loc.line
      if (from.loc) msg += (to.loc ? " and " : " at ") + from.loc.file + ":" + from.loc.line
      throw new SyntaxError(msg)
    }
  }
  return to
}

function deref(obj, name) {
  return obj[name] || (obj[name] = Object.create(null))
}

function lvalPath(lval, ancestors) {
  var path = []
  while (lval.type == "MemberExpression") {
    path.unshift(propName(lval))
    lval = lval.object
  }

  if (lval.type == "Identifier") {
    path.unshift(lval.name)
  } else if (lval.type == "ThisExpression") {
    path = selfPath(ancestors.slice(0, ancestors.length - 1)).concat(path)
  } else {
    raise("Could not derive a target for this assignment", lval)
  }
  return path
}

function assignedPath(ancestors) {
  var top = ancestors[ancestors.length - 1]
  if (top.type == "VariableDeclarator" && top.id.type == "Identifier")
    return [top.id.name]
  else if (top.type == "AssignmentExpression")
    return lvalPath(top.left, ancestors)
  else
    raise("Could not derive a name", top)
}

function findPrototype(ancestors) {
  var assign = ancestors[ancestors.length - 1]
  if (assign.type != "AssignmentExpression") return null
  var lval = assign.left
  if (lval.type == "MemberExpression" && !lval.computed &&
      lval.object.type == "MemberExpression" && !lval.object.computed &&
      lval.object.property.name == "prototype")
    return lvalPath(lval.object, ancestors)
}

function assignedName(node) {
  if (node.type == "VariableDeclarator" && node.id.type == "Identifier")
    return node.id.name
  else if (node.type == "AssignmentExpression")
    return propName(node.left)
}

function selfPath(ancestors) {
  for (var i = ancestors.length - 1; i >= 0; i--) {
    var ancestor = ancestors[i], found
    if (ancestor.type == "ClassDeclaration" ||
        (ancestor.type == "FunctionDeclaration" && ctorName(ancestor.id.name)))
      return [ancestor.id.name, "prototype"]
    else if (i && (ancestor.type == "ClassExpression" ||
                   ancestor.type == "FunctionExpression" && ctorName(assignedName(ancestors[i - 1]))))
      return assignedPath(ancestors.slice(0, i)).concat("prototype")
    else if (i && /Function/.test(ancestor.type) && (found = findPrototype(ancestors.slice(0, i))))
      return found
  }
  raise("No context found for 'this'", ancestors[ancestors.length - 1])
}

function parentPath(ancestors) {
  for (var i = ancestors.length - 1; i >= 0; i--) {
    var ancestor = ancestors[i]
    if (ancestor.type == "ClassDeclaration")
      return [ancestor.id.name]
    else if (i && (ancestor.type == "ClassExpression" || ancestor.type == "ObjectExpression"))
      return assignedPath(ancestors.slice(0, i))
  }
}

function splitPath(path) {
  var m, parts = [], rest = path
  while (rest && (m = /^(\[.*?\]|[^\s\.#]+)(\.)?/.exec(rest))) {
    parts.push(m[1])
    rest = rest.slice(m[0].length)
    if (!m[2]) break
  }
  if (rest) throw new Error("Invalid path: " + path)
  return parts
}
<|endoftext|>