<commit_before>'use strict'

const fs = require('fs')
const path = require('path')

const frontMatter = require('front-matter')
const htmlMinifier = require('html-minifier')

const paths = require('./paths.js')
const utilsArticles = require('./utils/articles.js')

function findPathToArticle (directory, articleName, searchedDepth, currentDepth = 0) {
  const list = fs.readdirSync(directory) // TODO: sync function

  for (const file of list) {
    const filePath = path.join(directory, file)
    const isDirectory = fs.statSync(filePath).isDirectory() // TODO: sync function

    if (!isDirectory) continue
    if (currentDepth === searchedDepth && file === articleName) return filePath
    if (currentDepth >= searchedDepth) continue

    const result = findPathToArticle(filePath, articleName, searchedDepth, currentDepth + 1)
    if (result) return result
  }

  return false
}

function getArticlesMetadata (directory, filename, gatheredMetadata = [], baseDirectory = directory) {
  const list = fs.readdirSync(directory) // TODO: sync function
  for (const file of list) {
    const filePath = path.join(directory, file)
    const isDirectory = fs.statSync(filePath).isDirectory() // TODO: sync function

    if (isDirectory) {
      gatheredMetadata = getArticlesMetadata(filePath, filename, gatheredMetadata, baseDirectory)
    } else if (file === filename) {
      // path to article relative to baseDirectory
      const articleDirectory = filePath
        .replace(filename, '')
        .replace(baseDirectory, '')

      const metadata = frontMatter(fs.readFileSync(filePath, 'utf8')).attributes // TODO: sync function

      metadata.directory = articleDirectory
      metadata.publication_date = utilsArticles.isoStringToUtcDate(metadata.publication_date)
      metadata.last_update = utilsArticles.isoStringToUtcDate(metadata.last_update)

      const articlePath = articleDirectory
        .split(path.sep)
        .filter(x => x)

      metadata.url = articlePath[articlePath.length - 1]
      gatheredMetadata.push(metadata)
    }
  }

  return gatheredMetadata
}

function getArticlesDirectories (directory, searchedDepth, currentDepth = 0, articlesList = []) {
  const list = fs.readdirSync(directory) // TODO: sync function

  for (const item of list) {
    const itemPath = path.join(directory, item)
    const isDirectory = fs.statSync(itemPath).isDirectory() // TODO: sync function

    if (!isDirectory) continue

    if (currentDepth === searchedDepth) {
      articlesList.push(itemPath)
    } else {
      getArticlesDirectories(itemPath, searchedDepth, currentDepth + 1, articlesList)
    }
  }

  return articlesList
}

function parseArticle (articlePath) {
  let data = fs.readFileSync(articlePath, 'utf8')
  data = frontMatter(data)

  const metadata = data.attributes

  const articleDirectory = '/static/articles/' +
    articlePath
      // c:\some\path\rootdir\article.md -> rootdir\article.md
      .replace(paths.articles, '')
      .split(path.sep)
      // remove empty values (\some\path creates ['', 'some', 'path'])
      .filter(value => value)
      // remove filename rootdir\article.md - > rootdir
      .filter((value, index, array) => index !== array.length - 1)
      // use '/' instead of path.sep, because that's what we are using in templates
      .join('/')

  // TODO: every time we make html transformation we take html
  //       string and pass it into cheerio and create cheerio
  //       object, then make transformations and then transform
  //       back to html string. We could pass around cheerio
  //       object so creation of cheerio object and transformation
  //       to html string will be done only once
  let article = data.body

  // inside articles we are using <xmp> instead of <code>, so transform it to <code> before we
  // run other transformations which might depend on <code> tag being used instead of <xmp>
  article = utilsArticles.changeXmpToCode(article)
  article = utilsArticles.trimCodeBlocks(article)
  article = utilsArticles.removeIndentationInCodeBlocks(article)
  article = utilsArticles.escapeAndHighlightCodeBlocks(article)

  article = utilsArticles.addIdsToHeadings(article)

  article = utilsArticles.relativeUrlToAbsolute(article, 'img', 'src', articleDirectory)

  // TODO: think about merging these two together, or at least share css selector?
  article = utilsArticles.enhanceSnippetLinks(article)
  article = utilsArticles.relativeUrlToAbsolute(article, 'a[href^="./snippets/"]', 'href', articleDirectory)

  article = htmlMinifier.minify(article, {
    collapseWhitespace: true,
    conservativeCollapse: true,
    minifyCSS: true,
    minifyJS: true,
    removeComments: true,
    removeRedundantAttributes: true,
    sortAttributes: true,
    sortClassName: true
  })

  return {
    metadata,
    html: article
  }
}

function getArticle (articlePath) {
  const fileData = fs.readFileSync(path.join(articlePath, 'article.md'), 'utf8')
  const data = frontMatter(fileData)

  const metadata = data.attributes
  const html = data.body

  // c:\some\path\2015\1\article-dir -> 2015\1\article-dir
  const articleFolder = articlePath
    .replace(paths.articles, '')
    .split(path.sep)
    .filter(x => x)
    .join(path.sep)

  const articleUrl = articleFolder.split(path.sep).reverse()[0]

  // TODO: move to paths?
  const articleStaticFilesPath = '/static/articles/' + articleFolder

  // TODO: every time we make html transformation we take html
  //       string and pass it into cheerio and create cheerio
  //       object, then make transformations and then transform
  //       back to html string. We could pass around cheerio
  //       object so creation of cheerio object and transformation
  //       to html string will be done only once
  let articleHtml = html

  // inside articles we are using <xmp> instead of <code>, so transform it to <code> before we
  // run other transformations which might depend on <code> tag being used instead of <xmp>
  articleHtml = utilsArticles.changeXmpToCode(articleHtml)
  articleHtml = utilsArticles.trimCodeBlocks(articleHtml)
  articleHtml = utilsArticles.removeIndentationInCodeBlocks(articleHtml)
  articleHtml = utilsArticles.escapeAndHighlightCodeBlocks(articleHtml)

  articleHtml = utilsArticles.addIdsToHeadings(articleHtml)

  articleHtml = utilsArticles.relativeUrlToAbsolute(articleHtml, 'img', 'src', articleStaticFilesPath)

  // TODO: think about merging these two together, or at least share css selector?
  articleHtml = utilsArticles.enhanceSnippetLinks(articleHtml)
  articleHtml = utilsArticles.relativeUrlToAbsolute(articleHtml, 'a[href^="./snippets/"]', 'href', articleStaticFilesPath)

  articleHtml = htmlMinifier.minify(articleHtml, {
    collapseWhitespace: true,
    conservativeCollapse: true,
    minifyCSS: true,
    minifyJS: true,
    removeComments: true,
    removeRedundantAttributes: true,
    sortAttributes: true,
    sortClassName: true
  })

  return {
    title: metadata.title,
    url: articleUrl,
    directory: articleFolder,
    description: metadata.description,
    publicationDate: utilsArticles.isoStringToUtcDate(metadata.publication_date),
    lastUpdate: utilsArticles.isoStringToUtcDate(metadata.last_update),
    visible: metadata.visible,
    html: articleHtml
  }
}

function getSnippets (articlePath) {
  const snippetsDir = path.join(articlePath, '/snippets')
  let snippetFiles = []
  try {
    snippetFiles = fs.readdirSync(snippetsDir) // TODO: sync function;
  } catch (e) {
    return {}
  }

  const snippets = {}
  for (const fileName of snippetFiles) {
    const snippetName = fileName.split('.')[0]
    const snippetPath = path.join(snippetsDir, fileName)
    const html = fs.readFileSync(snippetPath, 'utf8') // TODO: sync function
    snippets[snippetName] = utilsArticles.parseSnippet(html)
  }

  return snippets
}

function getArticleData (pathToArticle) {
  const article = getArticle(pathToArticle)
  const snippets = getSnippets(pathToArticle)

  return {
    article,
    snippets
  }
}

module.exports = {
  findPathToArticle,
  getArticlesMetadata,
  getArticlesDirectories,
  parseArticle,
  getArticleData
}
<commit_msg>articles: remove old functions which are not needed after refactoring<commit_after>'use strict'

const fs = require('fs')
const path = require('path')

const frontMatter = require('front-matter')
const htmlMinifier = require('html-minifier')

const paths = require('./paths.js')
const utilsArticles = require('./utils/articles.js')

function findPathToArticle (directory, articleName, searchedDepth, currentDepth = 0) {
  const list = fs.readdirSync(directory) // TODO: sync function

  for (const file of list) {
    const filePath = path.join(directory, file)
    const isDirectory = fs.statSync(filePath).isDirectory() // TODO: sync function

    if (!isDirectory) continue
    if (currentDepth === searchedDepth && file === articleName) return filePath
    if (currentDepth >= searchedDepth) continue

    const result = findPathToArticle(filePath, articleName, searchedDepth, currentDepth + 1)
    if (result) return result
  }

  return false
}

function getArticlesDirectories (directory, searchedDepth, currentDepth = 0, articlesList = []) {
  const list = fs.readdirSync(directory) // TODO: sync function

  for (const item of list) {
    const itemPath = path.join(directory, item)
    const isDirectory = fs.statSync(itemPath).isDirectory() // TODO: sync function

    if (!isDirectory) continue

    if (currentDepth === searchedDepth) {
      articlesList.push(itemPath)
    } else {
      getArticlesDirectories(itemPath, searchedDepth, currentDepth + 1, articlesList)
    }
  }

  return articlesList
}

function getArticle (articlePath) {
  const fileData = fs.readFileSync(path.join(articlePath, 'article.md'), 'utf8')
  const data = frontMatter(fileData)

  const metadata = data.attributes
  const html = data.body

  // c:\some\path\2015\1\article-dir -> 2015\1\article-dir
  const articleFolder = articlePath
    .replace(paths.articles, '')
    .split(path.sep)
    .filter(x => x)
    .join(path.sep)

  const articleUrl = articleFolder.split(path.sep).reverse()[0]

  // TODO: move to paths?
  const articleStaticFilesPath = '/static/articles/' + articleFolder

  // TODO: every time we make html transformation we take html
  //       string and pass it into cheerio and create cheerio
  //       object, then make transformations and then transform
  //       back to html string. We could pass around cheerio
  //       object so creation of cheerio object and transformation
  //       to html string will be done only once
  let articleHtml = html

  // inside articles we are using <xmp> instead of <code>, so transform it to <code> before we
  // run other transformations which might depend on <code> tag being used instead of <xmp>
  articleHtml = utilsArticles.changeXmpToCode(articleHtml)
  articleHtml = utilsArticles.trimCodeBlocks(articleHtml)
  articleHtml = utilsArticles.removeIndentationInCodeBlocks(articleHtml)
  articleHtml = utilsArticles.escapeAndHighlightCodeBlocks(articleHtml)

  articleHtml = utilsArticles.addIdsToHeadings(articleHtml)

  articleHtml = utilsArticles.relativeUrlToAbsolute(articleHtml, 'img', 'src', articleStaticFilesPath)

  // TODO: think about merging these two together, or at least share css selector?
  articleHtml = utilsArticles.enhanceSnippetLinks(articleHtml)
  articleHtml = utilsArticles.relativeUrlToAbsolute(articleHtml, 'a[href^="./snippets/"]', 'href', articleStaticFilesPath)

  articleHtml = htmlMinifier.minify(articleHtml, {
    collapseWhitespace: true,
    conservativeCollapse: true,
    minifyCSS: true,
    minifyJS: true,
    removeComments: true,
    removeRedundantAttributes: true,
    sortAttributes: true,
    sortClassName: true
  })

  return {
    title: metadata.title,
    url: articleUrl,
    directory: articleFolder,
    description: metadata.description,
    publicationDate: utilsArticles.isoStringToUtcDate(metadata.publication_date),
    lastUpdate: utilsArticles.isoStringToUtcDate(metadata.last_update),
    visible: metadata.visible,
    html: articleHtml
  }
}

function getSnippets (articlePath) {
  const snippetsDir = path.join(articlePath, '/snippets')
  let snippetFiles = []
  try {
    snippetFiles = fs.readdirSync(snippetsDir) // TODO: sync function;
  } catch (e) {
    return {}
  }

  const snippets = {}
  for (const fileName of snippetFiles) {
    const snippetName = fileName.split('.')[0]
    const snippetPath = path.join(snippetsDir, fileName)
    const html = fs.readFileSync(snippetPath, 'utf8') // TODO: sync function
    snippets[snippetName] = utilsArticles.parseSnippet(html)
  }

  return snippets
}

function getArticleData (pathToArticle) {
  const article = getArticle(pathToArticle)
  const snippets = getSnippets(pathToArticle)

  return {
    article,
    snippets
  }
}

module.exports = {
  findPathToArticle,
  getArticlesDirectories,
  getArticleData
}
<|endoftext|>


<commit_before>import { GamePlayers, socket } from '../sockets'
const Easystar = require('easystarjs')
import Enemy from '../constructor/Enemy'
import throttle from 'lodash.throttle'

let map
  , cursors
  , OGuy
  , xCoord = 100
  , yCoord = 100
  , monster

const localState = {
  players: [],
  enemies: {},
}

import loadMaps from './utils/loadMaps'
import buildMaps from './utils/buildMaps'
import playerMovement from './utils/playerMovement'
import mapTransition from './utils/mapTransition'

let playerObject
  , player

export const fantasyState = {
  init(character) {
    if (character) player = character
  },

  preload() {
    loadMaps.fantasy()
  },

  create() {
    this.physics.startSystem(Phaser.Physics.P2JS)

    map = buildMaps.fantasy()
    this.makeCollisionMap()

    socket.emit('setupState', player, 'fantasyState')

    playerObject = StackQuest.game.add.text(player.x, player.y, player.class, { font: '32px Arial', fill: '#ffffff' })
    localState.players.push(playerObject)

    this.spawnEnemy()

    this.physics.p2.enable(playerObject)

    // create monster test
    // this.easystar.findPath(Math.floor(monster.position.x / 60), Math.floor(monster.position.y / 60), Math.floor(xCoord / 60), Math.floor(yCoord / 60), monster.move)
    // this.easystar.calculate()

    this.camera.follow(playerObject)

    this.physics.p2.setBoundsToWorld(true, true, true, true, false)

    cursors = this.input.keyboard.createCursorKeys()

    socket.on('enemyCreated', (enemy) => {
      localState.enemies[enemy.name] = new Enemy(this.game, enemy.name, {x: enemy.x, y: enemy.y}, enemy.key)
    })

    socket.on('enemyUpdated', (enemy) => {
      localState.enemies[enemy.name].position.x = enemy.x
      localState.enemies[enemy.name].position.y = enemy.y
    })
  },

  update() {
    playerMovement(playerObject, cursors)
    mapTransition(player, playerObject, 'spaceState')
    localState.enemies.forEach(this.enemyPathFinding, this)
  },

  render() {
    this.game.debug.cameraInfo(this.camera, 32, 32)
  },

  enemyPathFinding(enemy) {
    const closestPlayer = enemy.findClosestPlayer(localState)
    this.easystar.findPath(Math.floor(enemy.position.x / map.width), Math.floor(enemy.position.y / map.height), Math.floor(closestPlayer.position.x / map.width), Math.floor(closestPlayer.position.y / map.height), enemy.move)
    this.easystar.calculate()
  },

  makeCollisionMap() {
    const collisionArray = []
    for (let rowIdx = 0; rowIdx < map.height; rowIdx++) {
      const rowArray = []
      for (let colIdx = 0; colIdx < map.width; colIdx++) {
        let collision = false
        for (const layer of map.layers) {
          if (layer.data[rowIdx][colIdx].collides) {
            collision = true
            break
          }
        }
        rowArray.push(Number(collision))
      }
      collisionArray.push(rowArray)
    }
    this.easystar = new Easystar.js()
    this.easystar.setGrid(collisionArray)
    this.easystar.setAcceptableTiles([0])
    this.easystar.enableDiagonals()
  },

  getPointFromGrid(rowIdx, colIdx) {
    const y = (rowIdx * map.height) + (map.height / 2)
    const x = (colIdx * map.width) + (map.width / 2)
    return new Phaser.Point(x, y)
  },

  spawnEnemy() {
    // localState.enemies.push(new Enemy(this.game, 'testMonster1', {x: Math.random()*1200, y: Math.random() * 800}, 'soldier'))
    // localState.enemies.push(new Enemy(this.game, 'testMonster2', {x: Math.random()*1200, y: Math.random()*800}, 'soldier'))
    const newEnemy = new Enemy(this.game, 'testMonster3', {x: Math.random()*1200, y: Math.random()*800}, 'soldier')
    localState.enemies[newEnemy.name] = newEnemy
    // socket.emit('addEnemy', {enemies: localState.enemies.map(({name, position}) => ({name, x: position.x, y: position.y}))})
    socket.emit('addEnemy', {name: newEnemy.name, key: newEnemy.key, x: newEnemy.position.x, y: newEnemy.position.y})
  }
}

export default fantasyState
<commit_msg>refactor localState on FantasyState to be a hash table instead of Array<commit_after>import { GamePlayers, socket } from '../sockets'
const Easystar = require('easystarjs')
import Enemy from '../constructor/Enemy'
import throttle from 'lodash.throttle'

let map
  , cursors
  , OGuy
  , xCoord = 100
  , yCoord = 100
  , monster

const localState = {
  players: [],
  enemies: {},
}

import loadMaps from './utils/loadMaps'
import buildMaps from './utils/buildMaps'
import playerMovement from './utils/playerMovement'
import mapTransition from './utils/mapTransition'

let playerObject
  , player

export const fantasyState = {
  init(character) {
    if (character) player = character
  },

  preload() {
    loadMaps.fantasy()
  },

  create() {
    this.physics.startSystem(Phaser.Physics.P2JS)

    map = buildMaps.fantasy()
    this.makeCollisionMap()

    socket.emit('setupState', player, 'fantasyState')

    playerObject = StackQuest.game.add.text(player.x, player.y, player.class, { font: '32px Arial', fill: '#ffffff' })
    localState.players.push(playerObject)

    this.spawnEnemy()

    this.physics.p2.enable(playerObject)

    // create monster test
    // this.easystar.findPath(Math.floor(monster.position.x / 60), Math.floor(monster.position.y / 60), Math.floor(xCoord / 60), Math.floor(yCoord / 60), monster.move)
    // this.easystar.calculate()

    this.camera.follow(playerObject)

    this.physics.p2.setBoundsToWorld(true, true, true, true, false)

    cursors = this.input.keyboard.createCursorKeys()

    socket.on('enemyCreated', (enemy) => {
      localState.enemies[enemy.name] = new Enemy(this.game, enemy.name, {x: enemy.x, y: enemy.y}, enemy.key)
    })

    socket.on('enemyUpdated', (enemy) => {
      localState.enemies[enemy.name].position.x = enemy.x
      localState.enemies[enemy.name].position.y = enemy.y
    })
  },

  update() {
    playerMovement(playerObject, cursors)
    mapTransition(player, playerObject, 'spaceState')
    // localState.enemies.forEach(this.enemyPathFinding, this)
    this.moveAllEnemies()
  },

  render() {
    this.game.debug.cameraInfo(this.camera, 32, 32)
  },

  enemyPathFinding(enemy) {
    const closestPlayer = enemy.findClosestPlayer(localState)
    this.easystar.findPath(Math.floor(enemy.position.x / map.width), Math.floor(enemy.position.y / map.height), Math.floor(closestPlayer.position.x / map.width), Math.floor(closestPlayer.position.y / map.height), enemy.move)
    this.easystar.calculate()
  },

  makeCollisionMap() {
    const collisionArray = []
    for (let rowIdx = 0; rowIdx < map.height; rowIdx++) {
      const rowArray = []
      for (let colIdx = 0; colIdx < map.width; colIdx++) {
        let collision = false
        for (const layer of map.layers) {
          if (layer.data[rowIdx][colIdx].collides) {
            collision = true
            break
          }
        }
        rowArray.push(Number(collision))
      }
      collisionArray.push(rowArray)
    }
    this.easystar = new Easystar.js()
    this.easystar.setGrid(collisionArray)
    this.easystar.setAcceptableTiles([0])
    this.easystar.enableDiagonals()
  },

  getPointFromGrid(rowIdx, colIdx) {
    const y = (rowIdx * map.height) + (map.height / 2)
    const x = (colIdx * map.width) + (map.width / 2)
    return new Phaser.Point(x, y)
  },

  spawnEnemy() {
    // localState.enemies.push(new Enemy(this.game, 'testMonster1', {x: Math.random()*1200, y: Math.random() * 800}, 'soldier'))
    // localState.enemies.push(new Enemy(this.game, 'testMonster2', {x: Math.random()*1200, y: Math.random()*800}, 'soldier'))
    const newEnemy = new Enemy(this.game, 'testMonster3', {x: Math.random()*1200, y: Math.random()*800}, 'soldier')
    localState.enemies[newEnemy.name] = newEnemy
    // socket.emit('addEnemy', {enemies: localState.enemies.map(({name, position}) => ({name, x: position.x, y: position.y}))})
    socket.emit('addEnemy', {name: newEnemy.name, key: newEnemy.key, x: newEnemy.position.x, y: newEnemy.position.y})
  },
  moveAllEnemies() {
    Object.keys(localState.enemies).forEach((enemyName) => this.enemyPathFinding(localState.enemies[enemyName]), this)
  }
}

export default fantasyState
<|endoftext|>


<commit_before>/*
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

var AWS = require('aws-sdk');
var _ = require('lodash');

/**
 * analyze the AWS AutoScalingGroups. The following tags must be in place on the Group
 *
 * nscale-system: <namespace>-<target> for example nodezoo-blue
 * Name: <nscaleId> for example awsMachine$fe-12345
 */
module.exports = function fetchAutoScalingGroups(config, result, cb) {
  var autoScaling = new AWS.AutoScaling(config);
  var filters = [
    { Name: 'key', Values: ['nscale-system']},
    { Name: 'value', Values: [result.name + '-' + result.topology.name] }
  ];

  var params = {Filters: filters};
  var containers = result.topology.containers;

  autoScaling.describeTags(params, function(err, data) {
    if (err) {
      return cb(err);
    }

    var names = _.chain(data.Tags)
                 .filter({ResourceType: 'auto-scaling-group'})
                 .pluck('ResourceId')
                 .value()

    if (names.length === 0) {
      // there is no autoscaling group
      return cb();
    }

    var params = {
      AutoScalingGroupNames: names
    };

    autoScaling.describeAutoScalingGroups(params, function(err, data) {
      if (err) {
        return cb(err);
      }

      data.AutoScalingGroups.forEach(function (autoScalingGroup) {
        var nameTag = _.find(autoScalingGroup.Tags, function(tag) {return tag.Key === 'Name'; });
        var id = nameTag.Value;
        var defName = nameTag.Value.split('-')[0];
        var instances = _.pluck(autoScalingGroup.Instances, 'InstanceId');

        var groups = _.chain(instances).map(function(instance) {
          return result.topology.containers[instance];
        }).map(function(instance) {
          return instance.specific.securityGroups;
        }).flatten().pluck('GroupName').value();

        // the parent is either the ELB or the Security Group.
        var containedBy = autoScalingGroup.LoadBalancerNames[0] || groups[0];

        // at this point we have already looked up both the security groups
        // and the elbs, so it is safe to assume the parent is there
        result.topology.containers[containedBy].contains.push(id);

        result.containerDefinitions.push({
          name: defName,
          id: id,
          type: 'aws-autoscaling',
          specific: autoScalingGroup
        });

        containers[id] = {
          id: id,
          containedBy: containedBy,
          containerDefinitionId: defName,
          type: 'aws-autoscaling',
          contains: instances,
          specific: autoScalingGroup,
          nativeId: autoScalingGroup.AutoScalingGroupName
        };

        instances.forEach(function (instance) {
          var container = containers[instance.InstanceId];
          if (container) {
            // update only the instances that are managed by nscale
            container.type = 'blank-container';
            container.containedBy = id;
          }
        });
      });

      cb();
    });
  });
};
<commit_msg>Do not crash if the elb is not missing.<commit_after>/*
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

var AWS = require('aws-sdk');
var _ = require('lodash');

/**
 * analyze the AWS AutoScalingGroups. The following tags must be in place on the Group
 *
 * nscale-system: <namespace>-<target> for example nodezoo-blue
 * Name: <nscaleId> for example awsMachine$fe-12345
 */
module.exports = function fetchAutoScalingGroups(config, result, cb) {
  var autoScaling = new AWS.AutoScaling(config);
  var filters = [
    { Name: 'key', Values: ['nscale-system']},
    { Name: 'value', Values: [result.name + '-' + result.topology.name] }
  ];

  var params = {Filters: filters};
  var containers = result.topology.containers;

  autoScaling.describeTags(params, function(err, data) {
    if (err) {
      return cb(err);
    }

    var names = _.chain(data.Tags)
                 .filter({ResourceType: 'auto-scaling-group'})
                 .pluck('ResourceId')
                 .value()

    if (names.length === 0) {
      // there is no autoscaling group
      return cb();
    }

    var params = {
      AutoScalingGroupNames: names
    };

    autoScaling.describeAutoScalingGroups(params, function(err, data) {
      if (err) {
        return cb(err);
      }

      data.AutoScalingGroups.forEach(function (autoScalingGroup) {
        var nameTag = _.find(autoScalingGroup.Tags, function(tag) {return tag.Key === 'Name'; });
        var id = nameTag.Value;
        var defName = nameTag.Value.split('-')[0];
        var instances = _.pluck(autoScalingGroup.Instances, 'InstanceId');

        var group = _.chain(instances).map(function(instance) {
          return result.topology.containers[instance];
        }).map(function(instance) {
          return instance.specific.securityGroups;
        }).flatten().pluck('GroupName').value()[0];

        var elb = _.find(autoScalingGroup.LoadBalancerNames, function(elb) {
          return result.topology.containers[elb];
        });

        // the parent is either the ELB or the Security Group.
        var containedBy = elb || group;

        // at this point we have already looked up both the security groups
        // and the elbs, so it is safe to assume the parent is there
        result.topology.containers[containedBy].contains.push(id);

        result.containerDefinitions.push({
          name: defName,
          id: id,
          type: 'aws-autoscaling',
          specific: autoScalingGroup
        });

        containers[id] = {
          id: id,
          containedBy: containedBy,
          containerDefinitionId: defName,
          type: 'aws-autoscaling',
          contains: instances,
          specific: autoScalingGroup,
          nativeId: autoScalingGroup.AutoScalingGroupName
        };

        instances.forEach(function (instance) {
          var container = containers[instance.InstanceId];
          if (container) {
            // update only the instances that are managed by nscale
            container.type = 'blank-container';
            container.containedBy = id;
          }
        });
      });

      cb();
    });
  });
};
<|endoftext|>


<commit_before>'use strict';

/*
 * Simple wrapper for the PHP action API
 */

module.exports = {
    paths: {
        '/v1/{domain}/_svc/action/query': {
            all: {
                request_handler: function(restbase, req) {
                    if (restbase._options.conf.offline) {
                        throw new Error("We are offline, you are tring to fallback to dynamic api");
                    }

                    var rp = req.params;

                    // TODO: this is a hack to allow us to test with a safe/fake
                    // domain, but still call out to a real service
                    var domain = rp.domain;
                    if (domain === 'en.wikipedia.test.local') {
                        domain = 'en.wikipedia.org';
                    }

                    req.uri = 'http://' + domain + '/w/api.php';
                    var body = req.body || req.query;
                    body.action = 'query';
                    // Always request json
                    body.format = 'json';
                    return restbase[req.method](req)
                    .then(function(res) {
                        if (res.status !== 200) {
                            return res;
                        }
                        // Rewrite res.body
                        // XXX: Rethink!
                        var pages = res.body.query.pages;
                        var newBody = [];
                        Object.keys(pages).forEach(function(key) {
                            newBody.push(pages[key]);
                        });
                        // XXX: Clean this up!
                        res.body = {
                            items: newBody,
                            next: res.body["query-continue"]
                        };
                        return res;
                    });
                }
            }
        }
    }
};
<commit_msg>Typo fix in error message<commit_after>'use strict';

/*
 * Simple wrapper for the PHP action API
 */

module.exports = {
    paths: {
        '/v1/{domain}/_svc/action/query': {
            all: {
                request_handler: function(restbase, req) {
                    if (restbase._options.conf.offline) {
                        throw new Error("We are offline, you are trying to fallback to the dynamic API.");
                    }

                    var rp = req.params;

                    // TODO: this is a hack to allow us to test with a safe/fake
                    // domain, but still call out to a real service
                    var domain = rp.domain;
                    if (domain === 'en.wikipedia.test.local') {
                        domain = 'en.wikipedia.org';
                    }

                    req.uri = 'http://' + domain + '/w/api.php';
                    var body = req.body || req.query;
                    body.action = 'query';
                    // Always request json
                    body.format = 'json';
                    return restbase[req.method](req)
                    .then(function(res) {
                        if (res.status !== 200) {
                            return res;
                        }
                        // Rewrite res.body
                        // XXX: Rethink!
                        var pages = res.body.query.pages;
                        var newBody = [];
                        Object.keys(pages).forEach(function(key) {
                            newBody.push(pages[key]);
                        });
                        // XXX: Clean this up!
                        res.body = {
                            items: newBody,
                            next: res.body["query-continue"]
                        };
                        return res;
                    });
                }
            }
        }
    }
};
<|endoftext|>


<commit_before>exports.businesses = require('./businesses');
exports.products = require('./products');
exports.suppliers = require('./suppliers');
exports.locations = require('./locations');<commit_msg>added events to exports.<commit_after>exports.businesses = require('./businesses');
exports.products = require('./products');
exports.suppliers = require('./suppliers');
exports.locations = require('./locations');
exports.events = require('./events');<|endoftext|>


<commit_before>/**
 * @class Oskari.mapframework.bundle.mapmodule.plugin.Portti2Zoombar
 *
 * Zoombar implementation with jQuery UI and refined graphics. Location can be configured,
 * but defaults on top of the map with placement details on the css-file.
 *
 * See http://www.oskari.org/trac/wiki/DocumentationBundleMapModulePluginPorttiZoombar
 */
Oskari.clazz.define('Oskari.mapframework.bundle.mapmodule.plugin.Portti2Zoombar',

/**
 * @method create called automatically on construction
 * @static
 */
function(config) {
    this.mapModule = null;
    this.pluginName = null;
    this._sandbox = null;
    this._map = null;
    this.__templates = {};
    this.__elements = {};
    this.__parent = null;
    this._slider = null;
    this._zoombar_messages = {};
    this._suppressEvents = false;
    this._conf = config;
}, {
    /**
     * @static
     * @property __name
     */
    __name : 'Portti2Zoombar',

    /**
     * @method getName
     * @return {String} the name for the component
     */
    getName : function() {
        return this.pluginName;
    },
    /**
     * @method getMapModule
     * Returns reference to map module
     * @return {Oskari.mapframework.ui.module.common.MapModule}
     */
    getMapModule : function() {
        return this.mapModule;
    },
    /**
     * @method hasUI
     * This plugin has an UI so always returns true
     * @return {Boolean} true
     */
    hasUI : function() {
        return true;
    },
    /**
     * @method setMapModule
     * @param {Oskari.mapframework.ui.module.common.MapModule} reference to map
     * module
     */
    setMapModule : function(mapModule) {
        this.mapModule = mapModule;
        if(mapModule) {
            this._map = mapModule.getMap();
            this.pluginName = mapModule.getName() + this.__name;
        }
    },
    /**
     * @method init
     * implements Module protocol init method - declares popup templates
     */
    init : function() {
        var me = this;
        // templates
        this.__templates['zoombar'] = jQuery('
' +
            '

' +
            '<input type=\'hidden\' />' +
            '

' +
            '

' +
        '
');
    },
    /**
     * @method register
     * mapmodule.Plugin protocol method - does nothing atm
     */
    register : function() {

    },
    /**
     * @method unregister
     * mapmodule.Plugin protocol method - does nothing atm
     */
    unregister : function() {
    },
    /**
     * @method startPlugin
     * @param {Oskari.mapframework.sandbox.Sandbox} sandbox
     * mapmodule.Plugin protocol method.
     * Sets sandbox and registers self to sandbox
     */
    startPlugin : function(sandbox) {
        this._sandbox = sandbox;
        sandbox.register(this);

        for(p in this.eventHandlers) {
            sandbox.registerForEventByName(this, p);
        }
        this._draw();
        this._setZoombarValue(this._map.getZoom());
    },
    /**
     * @method stopPlugin
     * mapmodule.Plugin protocol method.
     * Unregisters self from sandbox and removes plugins UI.
     * @param {Oskari.mapframework.sandbox.Sandbox} sandbox
     */
    stopPlugin : function(sandbox) {

        if(this.__elements['zoombarSlider']) {
            this.__elements['zoombarSlider'].remove();
            this._slider.remove();
            delete this.__elements['zoombarSlider'];
        }
        sandbox.unregister(this);

        //this._map = null;
        this._sandbox = null;
    },
    /**
     * @method _draw
     * @private
     *
     * Draws the zoombar on the screen.
     */
    _draw : function() {
        var me = this;
        if(!me.__parent) {
            me.__parent = this._map.div;
        }
        if(!me.__elements['zoombarSlider']) {
            me.__elements['zoombarSlider'] = me.__templates['zoombar'].clone();
        }

        var inputId = 'pzb-input-' + this.getName();
        var sliderId = 'pzb-slider-' + this.getName();
        var sliderEl = me.__elements['zoombarSlider'].find('div.slider');

        me.__elements['zoombarSlider'].find('input').attr('id', inputId);
        sliderEl.attr('id', sliderId);

        jQuery(me.__parent).append(me.__elements['zoombarSlider']);

       	var sliderEl = me.__elements['zoombarSlider'].find('div.slider');
       	sliderEl.css("height",(this._map.getNumZoomLevels()*11)+"px")
       	me._slider = sliderEl.slider({
            orientation: "vertical",
            range: "min",
            min: 0,
            max: this._map.getNumZoomLevels()-1,
            value: this._map.getZoom(),
            slide: function( event, ui ) {
                me.getMapModule().zoomTo( ui.value );
            }
        });

        var plus = me.__elements['zoombarSlider'].find('.pzbDiv-plus');
        plus.bind('click', function(event) {
            if(me._slider.slider('value') < 12) {
                me.getMapModule().zoomTo(me._slider.slider('value') + 1);
            }
        });
        var minus = me.__elements['zoombarSlider'].find('.pzbDiv-minus');
        minus.bind('click', function(event) {
            if(me._slider.slider('value') > 0) {
                me.getMapModule().zoomTo(me._slider.slider('value') - 1);
            }
        });
        // override default location if configured
        if(this._conf && this._conf.location) {
            // clear possible opposite position with 'auto' and set the one in config
            if(this._conf.location.top) {
                me.__elements['zoombarSlider'].css('bottom', 'auto');
                me.__elements['zoombarSlider'].css('top', this._conf.location.top);
            }
            if(this._conf.location.left) {
                me.__elements['zoombarSlider'].css('right', 'auto');
                me.__elements['zoombarSlider'].css('left', this._conf.location.left);
            }
            if(this._conf.location.right) {
                me.__elements['zoombarSlider'].css('left', 'auto');
                me.__elements['zoombarSlider'].css('right', this._conf.location.right);
            }
            if(this._conf.location.bottom) {
                me.__elements['zoombarSlider'].css('top', 'auto');
                me.__elements['zoombarSlider'].css('bottom', this._conf.location.bottom);
            }
        }
    },
    /**
     * @method _setZoombarValue
     * Sets the zoombar slider value
     * @private
     * @param {Number} value new Zoombar value
     */
    _setZoombarValue : function(value) {
        var me = this;
        if(me._slider) {
            // disable events in "onChange"
            this._suppressEvents = true;
            /*me._slider.setValue(value);*/
           me._slider.slider('value',value);
            this._suppressEvents = false;
        }
    },
    /**
     * @property {Object} eventHandlers
     * @static
     */
    eventHandlers : {
        'AfterMapMoveEvent' : function(event) {
            if(this._sandbox) {
                var me = this;
                me._setZoombarValue(event.getZoom());
            }
        }
    },

    /**
     * @method onEvent
     * @param {Oskari.mapframework.event.Event} event a Oskari event object
     * Event is handled forwarded to correct #eventHandlers if found or discarded
     * if not.
     */
    onEvent : function(event) {
        return this.eventHandlers[event.getName()].apply(this, [event]);
    },
    /**
     * @method start
     * @param {Oskari.mapframework.sandbox.Sandbox} sandbox
     * Module protocol method - does nothing atm
     */
    start : function(sandbox) {
    },
    /**
     * @method stop
     * @param {Oskari.mapframework.sandbox.Sandbox} sandbox
     * Module protocol method - does nothing atm
     */
    stop : function(sandbox) {
    }
}, {
    /**
     * @property {String[]} protocol
     * @static
     */
    'protocol' : ["Oskari.mapframework.module.Module", "Oskari.mapframework.ui.module.common.mapmodule.Plugin"]
});<commit_msg>Korjattu zoombar<commit_after>/**
 * @class Oskari.mapframework.bundle.mapmodule.plugin.Portti2Zoombar
 *
 * Zoombar implementation with jQuery UI and refined graphics. Location can be configured,
 * but defaults on top of the map with placement details on the css-file.
 *
 * See http://www.oskari.org/trac/wiki/DocumentationBundleMapModulePluginPorttiZoombar
 */
Oskari.clazz.define('Oskari.mapframework.bundle.mapmodule.plugin.Portti2Zoombar',

/**
 * @method create called automatically on construction
 * @static
 */
function(config) {
    this.mapModule = null;
    this.pluginName = null;
    this._sandbox = null;
    this._map = null;
    this.__templates = {};
    this.__elements = {};
    this.__parent = null;
    this._slider = null;
    this._zoombar_messages = {};
    this._suppressEvents = false;
    this._conf = config;
}, {
    /**
     * @static
     * @property __name
     */
    __name : 'Portti2Zoombar',

    /**
     * @method getName
     * @return {String} the name for the component
     */
    getName : function() {
        return this.pluginName;
    },
    /**
     * @method getMapModule
     * Returns reference to map module
     * @return {Oskari.mapframework.ui.module.common.MapModule}
     */
    getMapModule : function() {
        return this.mapModule;
    },
    /**
     * @method hasUI
     * This plugin has an UI so always returns true
     * @return {Boolean} true
     */
    hasUI : function() {
        return true;
    },
    /**
     * @method setMapModule
     * @param {Oskari.mapframework.ui.module.common.MapModule} reference to map
     * module
     */
    setMapModule : function(mapModule) {
        this.mapModule = mapModule;
        if(mapModule) {
            this._map = mapModule.getMap();
            this.pluginName = mapModule.getName() + this.__name;
        }
    },
    /**
     * @method init
     * implements Module protocol init method - declares popup templates
     */
    init : function() {
        var me = this;
        // templates
        this.__templates['zoombar'] = jQuery('
' +
            '

' +
            '<input type=\'hidden\' />' +
            '

' +
            '

' +
        '
');
    },
    /**
     * @method register
     * mapmodule.Plugin protocol method - does nothing atm
     */
    register : function() {

    },
    /**
     * @method unregister
     * mapmodule.Plugin protocol method - does nothing atm
     */
    unregister : function() {
    },
    /**
     * @method startPlugin
     * @param {Oskari.mapframework.sandbox.Sandbox} sandbox
     * mapmodule.Plugin protocol method.
     * Sets sandbox and registers self to sandbox
     */
    startPlugin : function(sandbox) {
        this._sandbox = sandbox;
        sandbox.register(this);

        for(p in this.eventHandlers) {
            sandbox.registerForEventByName(this, p);
        }
        this._draw();
        this._setZoombarValue(this._map.getZoom());
    },
    /**
     * @method stopPlugin
     * mapmodule.Plugin protocol method.
     * Unregisters self from sandbox and removes plugins UI.
     * @param {Oskari.mapframework.sandbox.Sandbox} sandbox
     */
    stopPlugin : function(sandbox) {

        if(this.__elements['zoombarSlider']) {
            this.__elements['zoombarSlider'].remove();
            this._slider.remove();
            delete this.__elements['zoombarSlider'];
        }
        sandbox.unregister(this);

        //this._map = null;
        this._sandbox = null;
    },
    /**
     * @method _draw
     * @private
     *
     * Draws the zoombar on the screen.
     */
    _draw : function() {
        var me = this;
        if(!me.__parent) {
            me.__parent = this._map.div;
        }
        if(!me.__elements['zoombarSlider']) {
            me.__elements['zoombarSlider'] = me.__templates['zoombar'].clone();
        }

        var inputId = 'pzb-input-' + this.getName();
        var sliderId = 'pzb-slider-' + this.getName();
        var sliderEl = me.__elements['zoombarSlider'].find('div.slider');

        me.__elements['zoombarSlider'].find('input').attr('id', inputId);
        sliderEl.attr('id', sliderId);

        jQuery(me.__parent).append(me.__elements['zoombarSlider']);

       	var sliderEl = me.__elements['zoombarSlider'].find('div.slider');
       	sliderEl.css("height",(this._map.getNumZoomLevels()*11)+"px")
       	me._slider = sliderEl.slider({
            orientation: "vertical",
            range: "min",
            min: 0,
            max: this._map.getNumZoomLevels()-1,
            value: this._map.getZoom(),
            slide: function( event, ui ) {
                me.getMapModule().zoomTo( ui.value );
            }
        });

        var plus = me.__elements['zoombarSlider'].find('.pzbDiv-plus');

        plus.bind('click', function(event) {
            if(me._slider.slider('value') < me._map.getNumZoomLevels()) {
                me.getMapModule().zoomTo(me._slider.slider('value') + 1);
            }
        });
        var minus = me.__elements['zoombarSlider'].find('.pzbDiv-minus');
        minus.bind('click', function(event) {
            if(me._slider.slider('value') > 0) {
                me.getMapModule().zoomTo(me._slider.slider('value') - 1);
            }
        });
        // override default location if configured
        if(this._conf && this._conf.location) {
            // clear possible opposite position with 'auto' and set the one in config
            if(this._conf.location.top) {
                me.__elements['zoombarSlider'].css('bottom', 'auto');
                me.__elements['zoombarSlider'].css('top', this._conf.location.top);
            }
            if(this._conf.location.left) {
                me.__elements['zoombarSlider'].css('right', 'auto');
                me.__elements['zoombarSlider'].css('left', this._conf.location.left);
            }
            if(this._conf.location.right) {
                me.__elements['zoombarSlider'].css('left', 'auto');
                me.__elements['zoombarSlider'].css('right', this._conf.location.right);
            }
            if(this._conf.location.bottom) {
                me.__elements['zoombarSlider'].css('top', 'auto');
                me.__elements['zoombarSlider'].css('bottom', this._conf.location.bottom);
            }
        }
    },
    /**
     * @method _setZoombarValue
     * Sets the zoombar slider value
     * @private
     * @param {Number} value new Zoombar value
     */
    _setZoombarValue : function(value) {
        var me = this;
        if(me._slider) {
            // disable events in "onChange"
            this._suppressEvents = true;
            /*me._slider.setValue(value);*/
           me._slider.slider('value',value);
            this._suppressEvents = false;
        }
    },
    /**
     * @property {Object} eventHandlers
     * @static
     */
    eventHandlers : {
        'AfterMapMoveEvent' : function(event) {
            if(this._sandbox) {
                var me = this;
                me._setZoombarValue(event.getZoom());
            }
        }
    },

    /**
     * @method onEvent
     * @param {Oskari.mapframework.event.Event} event a Oskari event object
     * Event is handled forwarded to correct #eventHandlers if found or discarded
     * if not.
     */
    onEvent : function(event) {
        return this.eventHandlers[event.getName()].apply(this, [event]);
    },
    /**
     * @method start
     * @param {Oskari.mapframework.sandbox.Sandbox} sandbox
     * Module protocol method - does nothing atm
     */
    start : function(sandbox) {
    },
    /**
     * @method stop
     * @param {Oskari.mapframework.sandbox.Sandbox} sandbox
     * Module protocol method - does nothing atm
     */
    stop : function(sandbox) {
    }
}, {
    /**
     * @property {String[]} protocol
     * @static
     */
    'protocol' : ["Oskari.mapframework.module.Module", "Oskari.mapframework.ui.module.common.mapmodule.Plugin"]
});<|endoftext|>


<commit_before>// oh iterables
import 'babel/polyfill'

import assert from 'assert'
import { dirname } from 'path'

import log from 'npmlog'

import { MultitrackAlbum as Album, Artist } from '@packard/model'

// heuristic as all get-out
function getID ({ fsAlbum, flacTags, album }) {
  if (flacTags && flacTags.ALBUMARTIST && flacTags.ALBUM) {
    return flacTags.ALBUMARTIST + ' - ' + flacTags.ALBUM
  } else if (fsAlbum && fsAlbum.artist && fsAlbum.artist.name && album) {
    return fsAlbum.artist.name + ' - ' + album.name
  } else {
    return album && album.name
  }
}

export default function albumsFromTracks (metadata, covers = new Map()) {
  assert(metadata, 'must pass metadata')

  const albums = new Map()
  const tracks = [].concat(...metadata)
  for (let track of tracks) {
    const id = getID(track)
    if (!albums.get(id)) albums.set(id, [])
    albums.get(id).push(track)
  }

  const finished = new Set()
  for (let album of albums.keys()) {
    let names = new Set()
    let artists = new Set()
    let tracks = new Set()
    let dirs = new Set()
    let dates = new Set()
    let archives = new Map()

    for (let track of albums.get(album)) {
      log.silly('albumsFromTracks', 'track', track)
      names.add(track.flacTags.ALBUM)
      artists.add(track.flacTags.ALBUMARTIST || track.flacTags.ARTIST)
      dirs.add(dirname(track.file.path))
      dates.add(track.date)
      if (track.sourceArchive) archives.set(track.sourceArchive.path, track.sourceArchive)
      tracks.add(track)
    }

    const minNames = [...names]
    if (minNames.length === 0) log.error('albumsFromTracks', 'album has no name?', minNames)
    if (minNames.length > 1) {
      log.error('albumsFromTracks', 'album has more than one name', minNames)
    }

    const minDirs = [...dirs]
    if (minDirs.length === 0) log.warn('albumsFromTracks', 'album has no path?', minDirs)
    if (minDirs.length > 1) {
      log.warn('albumsFromTracks', 'album in more than one directory', minDirs)
    }

    const minDates = [...dates.keys()]
    if (minDates.length > 1) {
      log.warn('albumsFromTracks', 'more than one date', minDates)
    }

    const minArchism = [...archives.keys()]
    if (minArchism.length > 1) {
      log.warn('albumsFromTracks', 'more than one source archive', minArchism)
      log.warn('albumsFromTracks', '--archive may not work as expected')
    }

    let artistName
    const minArtists = [...artists]
    switch (minArtists.length) {
      case 1:
        artistName = minArtists[0]
        log.verbose('albumsFromTracks', '1 artist found', artistName)
        break
      case 2:
        let [first, second] = minArtists
        if (first.indexOf(second) !== -1) {
          artistName = first
        } else if (second.indexOf(first) !== -1) {
          artistName = second
        } else {
          const sorted = [...tracks].sort((a, b) => (a.index || 0) - (b.index || 0))
          if (sorted[0].artistName === first) {
            artistName = first + ' / ' + second
          } else {
            artistName = second + ' / ' + first
          }
        }
        log.warn('albumsFromTracks', '2 artists found; assuming split', artistName)
        break
      default:
        log.warn('albumsFromTracks', 'many artists found; assuming compilation', minArtists)
        artistName = 'Various Artists'
    }
    const artist = new Artist(artistName)
    const a = new Album(minNames[0], artist, { path: minDirs[0], tracks: [...tracks] })
    a.sourceArchive = archives.get(minArchism[0])
    a.date = minDates[0]
    log.verbose('albumsFromTracks', 'looking up cover for', a.path, minDirs)
    if (covers.get(a.path)) a.pictures = covers.get(a.path)
    for (let track of tracks) {
      track.album = a
    }
    log.silly('albumsFromTracks', 'finished album', a)
    finished.add(a)
  }

  log.verbose('albumsFromTracks', 'processed', finished.size, 'albums')
  return finished
}
<commit_msg>scan: sometimes tracks don't have metadata<commit_after>// oh iterables
import 'babel/polyfill'

import assert from 'assert'
import { dirname } from 'path'

import log from 'npmlog'

import { MultitrackAlbum as Album, Artist } from '@packard/model'

// heuristic as all get-out
function getID ({ fsAlbum, flacTags, album }) {
  if (flacTags && flacTags.ALBUMARTIST && flacTags.ALBUM) {
    return flacTags.ALBUMARTIST + ' - ' + flacTags.ALBUM
  } else if (fsAlbum && fsAlbum.artist && fsAlbum.artist.name && album) {
    return fsAlbum.artist.name + ' - ' + album.name
  } else {
    return album && album.name
  }
}

export default function albumsFromTracks (metadata, covers = new Map()) {
  assert(metadata, 'must pass metadata')

  const albums = new Map()
  const tracks = [].concat(...metadata)
  for (let track of tracks) {
    const id = getID(track)
    if (!albums.get(id)) albums.set(id, [])
    albums.get(id).push(track)
  }

  const finished = new Set()
  for (let album of albums.keys()) {
    let names = new Set()
    let artists = new Set()
    let tracks = new Set()
    let dirs = new Set()
    let dates = new Set()
    let archives = new Map()

    for (let track of albums.get(album)) {
      if (!track.flacTags) continue
      log.silly('albumsFromTracks', 'track', track)
      names.add(track.flacTags.ALBUM)
      artists.add(track.flacTags.ALBUMARTIST || track.flacTags.ARTIST)
      dirs.add(dirname(track.file.path))
      dates.add(track.date)
      if (track.sourceArchive) archives.set(track.sourceArchive.path, track.sourceArchive)
      tracks.add(track)
    }

    const minNames = [...names]
    if (minNames.length === 0) log.error('albumsFromTracks', 'album has no name?', minNames)
    if (minNames.length > 1) {
      log.error('albumsFromTracks', 'album has more than one name', minNames)
    }

    const minDirs = [...dirs]
    if (minDirs.length === 0) log.warn('albumsFromTracks', 'album has no path?', minDirs)
    if (minDirs.length > 1) {
      log.warn('albumsFromTracks', 'album in more than one directory', minDirs)
    }

    const minDates = [...dates.keys()]
    if (minDates.length > 1) {
      log.warn('albumsFromTracks', 'more than one date', minDates)
    }

    const minArchism = [...archives.keys()]
    if (minArchism.length > 1) {
      log.warn('albumsFromTracks', 'more than one source archive', minArchism)
      log.warn('albumsFromTracks', '--archive may not work as expected')
    }

    let artistName
    const minArtists = [...artists]
    switch (minArtists.length) {
      case 1:
        artistName = minArtists[0]
        log.verbose('albumsFromTracks', '1 artist found', artistName)
        break
      case 2:
        let [first, second] = minArtists
        if (first.indexOf(second) !== -1) {
          artistName = first
        } else if (second.indexOf(first) !== -1) {
          artistName = second
        } else {
          const sorted = [...tracks].sort((a, b) => (a.index || 0) - (b.index || 0))
          if (sorted[0].artistName === first) {
            artistName = first + ' / ' + second
          } else {
            artistName = second + ' / ' + first
          }
        }
        log.warn('albumsFromTracks', '2 artists found; assuming split', artistName)
        break
      default:
        log.warn('albumsFromTracks', 'many artists found; assuming compilation', minArtists)
        artistName = 'Various Artists'
    }
    const artist = new Artist(artistName)
    const a = new Album(minNames[0], artist, { path: minDirs[0], tracks: [...tracks] })
    a.sourceArchive = archives.get(minArchism[0])
    a.date = minDates[0]
    log.verbose('albumsFromTracks', 'looking up cover for', a.path, minDirs)
    if (covers.get(a.path)) a.pictures = covers.get(a.path)
    for (let track of tracks) {
      track.album = a
    }
    log.silly('albumsFromTracks', 'finished album', a)
    finished.add(a)
  }

  log.verbose('albumsFromTracks', 'processed', finished.size, 'albums')
  return finished
}
<|endoftext|>


<commit_before>
/**
 * @license
 * Copyright 2018 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

 foam.CLASS({
   package: 'foam.graphics',
   name: 'TreeGraph',
   extends: 'foam.graphics.Box',

   requires: [
    'foam.graphics.TreeNode'
   ],
   exports: [
     'as graph',
     'formatNode',
     'relationship',
     'isAutoExpandedByDefault',
     'childNodesForAutoExpansion'
   ],

   properties: [
     [ 'nodeWidth',  155 ],
     [ 'nodeHeight',  60 ],
     [ 'lineWidth',   0.5 ],
     [ 'padding',     30 ],
     [ 'color', 'white'],
     {
       name: 'data'
     },
     {
       name: 'relationship'
     },
     'root',
     {
       name: 'formatNode',
       value: function() {}
     },
     {
       class: 'Boolean',
       name: 'isAutoExpandedByDefault',
       value: true
     },
     {
       class: 'Int',
       name: 'childNodesForAutoExpansion',
       value: 5
     }
   ],

   topics: [
    'onSizeChange'
   ],

   methods: [
     function initCView() {
       this.SUPER();

       if ( this.data ) {
        this.root = this.TreeNode.create({
          width: this.nodeWidth,
          height: this.nodeHeight,
          padding: this.padding,
          lineWidth: this.lineWidth,
          y: this.nodeHeight * 2,
          data: this.data
        });
        this.add(this.root);
        this.doLayout();
       }
     },
   ],

   listeners: [
     {
       name: 'doLayout',
       isMerged: true,
       code: function() {
          if ( this.root && this.root.layout() ) {
            this.invalidate();
            this.doLayout();
          } else {
            this.updateCSize();
          }
        }
     },
     {
       name: 'updateCSize',
       isFramed: true,
       code: function() {
         const maxes  = {
           maxLeft: Number.MAX_SAFE_INTEGER,
           maxRight: Number.MIN_SAFE_INTEGER
         }

         this.root.outline.forEach(level => {
           maxes.maxLeft = Math.min(level.left, maxes.maxLeft);
           maxes.maxRight = Math.max(level.right, maxes.maxRight);
         })

         var width = Math.abs(maxes.maxLeft - maxes.maxRight);
         var height = (this.nodeHeight * 2) * (this.root.outline.length + 1);

         if ( this.width != width || this.height != height ) {
           this.width = width;
           this.height = height;

           this.root.centerX = 0;
           this.root.centerX = - Math.min.apply(Math, this.root.outline.map(o => o.left));

           // IMPORTANT: Have to add the extra invalidate and doLayout to avoid a bug with
           // the tree freezing upon render sometimes during search
           this.invalidate();
           this.doLayout();

           this.onSizeChange.pub();
         }
      }
     }
   ]
 });
<commit_msg>Fixed tree search omg<commit_after>
/**
 * @license
 * Copyright 2018 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

 foam.CLASS({
   package: 'foam.graphics',
   name: 'TreeGraph',
   extends: 'foam.graphics.Box',

   requires: [
    'foam.graphics.TreeNode'
   ],
   exports: [
     'as graph',
     'formatNode',
     'relationship',
     'isAutoExpandedByDefault',
     'childNodesForAutoExpansion'
   ],

   properties: [
     [ 'nodeWidth',  155 ],
     [ 'nodeHeight',  60 ],
     [ 'lineWidth',   0.5 ],
     [ 'padding',     30 ],
     [ 'color', 'white'],
     {
       name: 'data'
     },
     {
       name: 'relationship'
     },
     'root',
     {
       name: 'formatNode',
       value: function() {}
     },
     {
       class: 'Boolean',
       name: 'isAutoExpandedByDefault',
       value: true
     },
     {
       class: 'Int',
       name: 'childNodesForAutoExpansion',
       value: 5
     }
   ],

   topics: [
    'onSizeChangeComplete'
   ],

   methods: [
     function initCView() {
       this.SUPER();

       if ( this.data ) {
        this.root = this.TreeNode.create({
          width: this.nodeWidth,
          height: this.nodeHeight,
          padding: this.padding,
          lineWidth: this.lineWidth,
          y: this.nodeHeight * 2,
          data: this.data
        });
        this.add(this.root);
        this.doLayout();
       }
     },
   ],

   listeners: [
     {
       name: 'doLayout',
       isMerged: true,
       code: function() {
          if ( this.root && this.root.layout() ) {
            this.invalidate();
            this.doLayout();
          } else {
            this.updateCSize();
          }
        }
     },
     {
       name: 'updateCSize',
       isFramed: true,
       code: function() {
         const maxes  = {
           maxLeft: Number.MAX_SAFE_INTEGER,
           maxRight: Number.MIN_SAFE_INTEGER
         }

         this.root.outline.forEach(level => {
           maxes.maxLeft = Math.min(level.left, maxes.maxLeft);
           maxes.maxRight = Math.max(level.right, maxes.maxRight);
         })

         var width = Math.abs(maxes.maxLeft - maxes.maxRight);
         var height = (this.nodeHeight * 2) * (this.root.outline.length + 1);

         if ( this.width != width || this.height != height ) {
           this.width = width;
           this.height = height;

           this.root.centerX = 0;
           this.root.centerX = - Math.min.apply(Math, this.root.outline.map(o => o.left));

           // IMPORTANT: Have to add the extra invalidate and doLayout to avoid a bug with
           // the tree freezing upon render sometimes during search
           this.invalidate();
           this.doLayout();

         } else {
          this.onSizeChangeComplete.pub();
         }
      }
     }
   ]
 });
<|endoftext|>


<commit_before>/**
 * @fileOverview
 * Main Class to config and build.
 *
 * Bao#input
 * Bao#output
 *
 * Bao#build(isWatchMode)
 */

const path = require('path')
const webpack = require('webpack')

const { genBasicConfig } = require('./config')
const { getAliasConfig, getCommonChunksConfig } = require('./runtime-config')

class Bao {

  constructor (config) {
    const { input, output } = config
    this.input = path.resolve(input || 'index.js')
    this.output = path.resolve(output || 'bundle.js')
  }

  build (isWatchMode) {
    const webpackConfig = this._genWebpackConfig()
    const compiler = webpack(webpackConfig)
    // @todo: error handling & stats info
    if (isWatchMode) {
      compiler.watch({}, (err, stats) => {
        console.log('[updated]')
      })
    } else {
      compiler.run((err, stats) => {
        console.log('[done]')
      })
    }
  }

  _genWebpackConfig () {
    const webpackConfig = genBasicConfig()
    const aliasConfig = getAliasConfig()
    const commonChunksConfig = getCommonChunksConfig()
    webpackConfig.entry = this.input
    webpackConfig.output.path = path.dirname(this.output)
    webpackConfig.output.filename = path.basename(this.output)
    if (!isEmptyObject(aliasConfig)) {
      console.log('[alias]', aliasConfig)
      webpackConfig.resolve.alias = aliasConfig
    }
    if (!isEmptyObject(commonChunksConfig)) {
      updateEntryFromStringToMap(webpackConfig)
      console.log('[shared]', commonChunksConfig)
      const outputPath = webpackConfig.output.path
      for (const name in commonChunksConfig) {
        const filepath = path.resolve(outputPath, name)
        const filename = path.relative(outputPath, filepath)
        if (!webpackConfig.entry[filename]) {
          webpackConfig.entry[filename] = commonChunksConfig[name]
          webpackConfig.plugins.push(
            new webpack.optimize.CommonsChunkPlugin({
              name: filename,
              filename,
              minChunks: Infinity
            })
          )
        } else {
          console.warn(`[Warning] Shared filename existed! ${name}`)
        }
      }
    }
    return webpackConfig
  }
}

function isEmptyObject (obj) {
  return Object.keys(obj).length === 0
}

function updateEntryFromStringToMap (webpackConfig) {
  if (typeof webpackConfig.entry === 'string') {
    const entryMap = {}
    entryMap[webpackConfig.output.filename] = webpackConfig.entry
    webpackConfig.entry = entryMap
    webpackConfig.output.filename = '[name]'
  }
}

exports.Bao = Bao
<commit_msg>Supported multi-entries.<commit_after>/**
 * @fileOverview
 * Main Class to config and build.
 *
 * Bao#input
 * Bao#output
 *
 * Bao#build(isWatchMode)
 */

const path = require('path')
const webpack = require('webpack')

const { genBasicConfig } = require('./config')
const { getAliasConfig, getCommonChunksConfig } = require('./runtime-config')

class Bao {

  constructor (config) {
    const { input, output } = config
    this.input = path.resolve(input || 'index.js')
    this.output = path.resolve(output || 'bundle.js')
  }

  build (isWatchMode) {
    const webpackConfig = this._genWebpackConfig()
    const compiler = webpack(webpackConfig)
    // @todo: error handling & stats info
    if (isWatchMode) {
      compiler.watch({}, (err, stats) => {
        console.log('[updated]')
      })
    } else {
      compiler.run((err, stats) => {
        console.log('[done]')
      })
    }
  }

  _genWebpackConfig () {
    const webpackConfig = genBasicConfig()

    const entriesConfig = getEntriesConfig()
    if (entriesConfig && !isEmptyObject(entriesConfig.entryMap)) {
      const { entryMap, outputPath } = entriesConfig
      console.log('[entries]', outputPath, entryMap)
      webpackConfig.entry = entryMap
      webpackConfig.output.path = outputPath
      webpackConfig.output.filename = '[name]'
    } else {
      webpackConfig.entry = this.input
      webpackConfig.output.path = path.dirname(this.output)
      webpackConfig.output.filename = path.basename(this.output)
    }

    const aliasConfig = getAliasConfig()
    if (!isEmptyObject(aliasConfig)) {
      console.log('[alias]', aliasConfig)
      webpackConfig.resolve.alias = aliasConfig
    }

    const commonChunksConfig = getCommonChunksConfig()
    if (!isEmptyObject(commonChunksConfig)) {
      updateEntryFromStringToMap(webpackConfig)
      console.log('[shared]', commonChunksConfig)
      const outputPath = webpackConfig.output.path
      for (const name in commonChunksConfig) {
        const filepath = path.resolve(outputPath, name)
        const filename = path.relative(outputPath, filepath)
        if (!webpackConfig.entry[filename]) {
          webpackConfig.entry[filename] = commonChunksConfig[name]
          webpackConfig.plugins.push(
            new webpack.optimize.CommonsChunkPlugin({
              name: filename,
              filename,
              minChunks: Infinity
            })
          )
        } else {
          console.warn(`[Warning] Shared filename existed! ${name}`)
        }
      }
    }
    return webpackConfig
  }
}

function isEmptyObject (obj) {
  return Object.keys(obj).length === 0
}

function updateEntryFromStringToMap (webpackConfig) {
  if (typeof webpackConfig.entry === 'string') {
    const entryMap = {}
    entryMap[webpackConfig.output.filename] = webpackConfig.entry
    webpackConfig.entry = entryMap
    webpackConfig.output.filename = '[name]'
  }
}

exports.Bao = Bao
<|endoftext|>


<commit_before>import * as TheLogManager from 'aurelia-logging';
import {ViewEngine} from 'aurelia-templating';
import {join} from 'aurelia-path';
import {Container} from 'aurelia-dependency-injection';

const logger = TheLogManager.getLogger('aurelia');
const extPattern = /\.[^/.]+$/;

function runTasks(config, tasks) {
  let current;
  let next = () => {
    current = tasks.shift();
    if (current) {
      return Promise.resolve(current(config)).then(next);
    }

    return Promise.resolve();
  };

  return next();
}

function loadPlugin(config, loader, info) {
  logger.debug(`Loading plugin ${info.moduleId}.`);
  config.resourcesRelativeTo = info.resourcesRelativeTo;

  let id = info.moduleId; // General plugins installed/configured by the end user.

  if (info.resourcesRelativeTo.length > 1 ) { // In case of bootstrapper installed plugins like `aurelia-templating-resources` or `aurelia-history-browser`.
    return loader.normalize(info.moduleId, info.resourcesRelativeTo[1])
      .then(normalizedId => _loadPlugin(normalizedId));
  }

  return _loadPlugin(id);

  function _loadPlugin(moduleId) {
    return loader.loadModule(moduleId).then(m => { // eslint-disable-line consistent-return
      if ('configure' in m) {
        return Promise.resolve(m.configure(config, info.config || {})).then(() => {
          config.resourcesRelativeTo = null;
          logger.debug(`Configured plugin ${info.moduleId}.`);
        });
      }

      config.resourcesRelativeTo = null;
      logger.debug(`Loaded plugin ${info.moduleId}.`);
    });
  }
}

function loadResources(aurelia, resourcesToLoad, appResources) {
  let viewEngine = aurelia.container.get(ViewEngine);

  return Promise.all(Object.keys(resourcesToLoad).map(n => _normalize(resourcesToLoad[n])))
    .then(loads => {
      let names = [];
      let importIds = [];

      loads.forEach(l => {
        names.push(undefined);
        importIds.push(l.importId);
      });

      return viewEngine.importViewResources(importIds, names, appResources);
    });

  function _normalize(load) {
    let moduleId = load.moduleId;
    let ext = getExt(moduleId);

    if (isOtherResource(moduleId)) {
      moduleId = removeExt(moduleId);
    }

    return aurelia.loader.normalize(moduleId, load.relativeTo)
      .then(normalized => {
        return {
          name: load.moduleId,
          importId: isOtherResource(load.moduleId) ? addOriginalExt(normalized, ext) : normalized
        };
      });
  }

  function isOtherResource(name) {
    let ext = getExt(name);
    if (!ext) return false;
    if (ext === '') return false;
    if (ext === '.js' || ext === '.ts') return false;
    return true;
  }

  function removeExt(name) {
    return name.replace(extPattern, '');
  }

  function addOriginalExt(normalized, ext) {
    return removeExt(normalized) + '.' + ext;
  }
}

function getExt(name) { // eslint-disable-line consistent-return
  let match = name.match(extPattern);
  if (match && match.length > 0) {
    return (match[0].split('.'))[1];
  }
}

function assertProcessed(plugins) {
  if (plugins.processed) {
    throw new Error('This config instance has already been applied. To load more plugins or global resources, create a new FrameworkConfiguration instance.');
  }
}

/**
 * Manages configuring the aurelia framework instance.
 */
export class FrameworkConfiguration {
  /**
   * The root DI container used by the application.
   */
  container: Container;

  /**
   * The aurelia instance.
   */
  aurelia: Aurelia;

  /**
   * Creates an instance of FrameworkConfiguration.
   * @param aurelia An instance of Aurelia.
   */
  constructor(aurelia: Aurelia) {
    this.aurelia = aurelia;
    this.container = aurelia.container;
    this.info = [];
    this.processed = false;
    this.preTasks = [];
    this.postTasks = [];
    this.resourcesToLoad = {};
    this.preTask(() => aurelia.loader.normalize('aurelia-bootstrapper').then(name => this.bootstrapperName = name));
    this.postTask(() => loadResources(aurelia, this.resourcesToLoad, aurelia.resources));
  }

  /**
   * Adds an existing object to the framework's dependency injection container.
   * @param type The object type of the dependency that the framework will inject.
   * @param instance The existing instance of the dependency that the framework will inject.
   * @return Returns the current FrameworkConfiguration instance.
   */
  instance(type: any, instance: any): FrameworkConfiguration {
    this.container.registerInstance(type, instance);
    return this;
  }

  /**
   * Adds a singleton to the framework's dependency injection container.
   * @param type The object type of the dependency that the framework will inject.
   * @param implementation The constructor function of the dependency that the framework will inject.
   * @return Returns the current FrameworkConfiguration instance.
   */
  singleton(type: any, implementation?: Function): FrameworkConfiguration {
    this.container.registerSingleton(type, implementation);
    return this;
  }

  /**
   * Adds a transient to the framework's dependency injection container.
   * @param type The object type of the dependency that the framework will inject.
   * @param implementation The constructor function of the dependency that the framework will inject.
   * @return Returns the current FrameworkConfiguration instance.
   */
  transient(type: any, implementation?: Function): FrameworkConfiguration {
    this.container.registerTransient(type, implementation);
    return this;
  }

  /**
   * Adds an async function that runs before the plugins are run.
   * @param task The function to run before start.
   * @return Returns the current FrameworkConfiguration instance.
   */
  preTask(task: Function): FrameworkConfiguration {
    assertProcessed(this);
    this.preTasks.push(task);
    return this;
  }

  /**
   * Adds an async function that runs after the plugins are run.
   * @param task The function to run after start.
   * @return Returns the current FrameworkConfiguration instance.
   */
  postTask(task: Function): FrameworkConfiguration {
    assertProcessed(this);
    this.postTasks.push(task);
    return this;
  }

  /**
   * Configures an internal feature plugin before Aurelia starts.
   * @param plugin The folder for the internal plugin to configure (expects an index.js in that folder).
   * @param config The configuration for the specified plugin.
   * @return Returns the current FrameworkConfiguration instance.
   */
  feature(plugin: string, config?: any): FrameworkConfiguration {
    if (getExt(plugin)) {
      return this.plugin({ moduleId: plugin, resourcesRelativeTo: [plugin, ''], config: config || {} });
    }

    return this.plugin({ moduleId: plugin + '/index', resourcesRelativeTo: [plugin, ''], config: config || {} });
  }

  /**
   * Adds globally available view resources to be imported into the Aurelia framework.
   * @param resources The relative module id to the resource. (Relative to the plugin's installer.)
   * @return Returns the current FrameworkConfiguration instance.
   */
  globalResources(resources: string|string[]): FrameworkConfiguration {
    assertProcessed(this);

    let toAdd = Array.isArray(resources) ? resources : arguments;
    let resource;
    let resourcesRelativeTo = this.resourcesRelativeTo || ['', ''];

    for (let i = 0, ii = toAdd.length; i < ii; ++i) {
      resource = toAdd[i];
      if (typeof resource !== 'string') {
        throw new Error(`Invalid resource path [${resource}]. Resources must be specified as relative module IDs.`);
      }

      let parent = resourcesRelativeTo[0];
      let grandParent = resourcesRelativeTo[1];
      let name = resource;

      if ((resource.startsWith('./') || resource.startsWith('../')) && parent !== '') {
        name = join(parent, resource);
      }

      this.resourcesToLoad[name] = { moduleId: name, relativeTo: grandParent };
    }

    return this;
  }

  /**
   * Renames a global resource that was imported.
   * @param resourcePath The path to the resource.
   * @param newName The new name.
   * @return Returns the current FrameworkConfiguration instance.
   */
  globalName(resourcePath: string, newName: string): FrameworkConfiguration {
    assertProcessed(this);
    this.resourcesToLoad[resourcePath] = { moduleId: newName, relativeTo: '' };
    return this;
  }

  /**
   * Configures an external, 3rd party plugin before Aurelia starts.
   * @param plugin The ID of the 3rd party plugin to configure.
   * @param config The configuration for the specified plugin.
   * @return Returns the current FrameworkConfiguration instance.
 */
  plugin(plugin: string, config?: any): FrameworkConfiguration {
    assertProcessed(this);

    if (typeof (plugin) === 'string') {
      return this.plugin({ moduleId: plugin, resourcesRelativeTo: [plugin, ''], config: config || {} });
    }

    this.info.push(plugin);
    return this;
  }

  _addNormalizedPlugin(name, config) {
    let plugin = { moduleId: name, resourcesRelativeTo: [name, ''], config: config || {} };
    this.plugin(plugin);

    this.preTask(() => {
      let relativeTo = [name, this.bootstrapperName];
      plugin.moduleId = name;
      plugin.resourcesRelativeTo = relativeTo;
      return Promise.resolve();
    });

    return this;
  }

  /**
   * Plugs in the default binding language from aurelia-templating-binding.
   * @return Returns the current FrameworkConfiguration instance.
  */
  defaultBindingLanguage(): FrameworkConfiguration {
    return this._addNormalizedPlugin('aurelia-templating-binding');
  }

  /**
   * Plugs in the router from aurelia-templating-router.
   * @return Returns the current FrameworkConfiguration instance.
  */
  router(): FrameworkConfiguration {
    return this._addNormalizedPlugin('aurelia-templating-router');
  }

  /**
   * Plugs in the default history implementation from aurelia-history-browser.
   * @return Returns the current FrameworkConfiguration instance.
  */
  history(): FrameworkConfiguration {
    return this._addNormalizedPlugin('aurelia-history-browser');
  }

  /**
   * Plugs in the default templating resources (if, repeat, show, compose, etc.) from aurelia-templating-resources.
   * @return Returns the current FrameworkConfiguration instance.
  */
  defaultResources(): FrameworkConfiguration {
    return this._addNormalizedPlugin('aurelia-templating-resources');
  }

  /**
   * Plugs in the event aggregator from aurelia-event-aggregator.
   * @return Returns the current FrameworkConfiguration instance.
  */
  eventAggregator(): FrameworkConfiguration {
    return this._addNormalizedPlugin('aurelia-event-aggregator');
  }

  /**
   * Sets up a basic Aurelia configuration. This is equivalent to calling `.defaultBindingLanguage().defaultResources().eventAggregator();`
   * @return Returns the current FrameworkConfiguration instance.
  */
  basicConfiguration(): FrameworkConfiguration {
    return this.defaultBindingLanguage().defaultResources().eventAggregator();
  }

  /**
   * Sets up the standard Aurelia configuration. This is equivalent to calling `.defaultBindingLanguage().defaultResources().eventAggregator().history().router();`
   * @return Returns the current FrameworkConfiguration instance.
  */
  standardConfiguration(): FrameworkConfiguration {
    return this.basicConfiguration().history().router();
  }

  /**
   * Plugs in the ConsoleAppender and sets the log level to debug.
   * @return {FrameworkConfiguration} Returns the current FrameworkConfiguration instance.
  */
  developmentLogging(): FrameworkConfiguration {
    this.preTask(() => {
      return this.aurelia.loader.normalize('aurelia-logging-console', this.bootstrapperName).then(name => {
        return this.aurelia.loader.loadModule(name).then(m => {
          TheLogManager.addAppender(new m.ConsoleAppender());
          TheLogManager.setLevel(TheLogManager.logLevel.debug);
        });
      });
    });

    return this;
  }

  /**
   * Loads and configures the plugins registered with this instance.
   * @return Returns a promise which resolves when all plugins are loaded and configured.
  */
  apply(): Promise<void> {
    if (this.processed) {
      return Promise.resolve();
    }

    return runTasks(this, this.preTasks).then(() => {
      let loader = this.aurelia.loader;
      let info = this.info;
      let current;

      let next = () => {
        current = info.shift();
        if (current) {
          return loadPlugin(this, loader, current).then(next);
        }

        this.processed = true;
        return Promise.resolve();
      };

      return next().then(() => runTasks(this, this.postTasks));
    });
  }
}
<commit_msg>style(comments): added comments about moduleName<commit_after>import * as TheLogManager from 'aurelia-logging';
import {ViewEngine} from 'aurelia-templating';
import {join} from 'aurelia-path';
import {Container} from 'aurelia-dependency-injection';

const logger = TheLogManager.getLogger('aurelia');
const extPattern = /\.[^/.]+$/;

function runTasks(config, tasks) {
  let current;
  let next = () => {
    current = tasks.shift();
    if (current) {
      return Promise.resolve(current(config)).then(next);
    }

    return Promise.resolve();
  };

  return next();
}

function loadPlugin(config, loader, info) {
  logger.debug(`Loading plugin ${info.moduleId}.`);
  config.resourcesRelativeTo = info.resourcesRelativeTo;

  let id = info.moduleId; // General plugins installed/configured by the end user.

  if (info.resourcesRelativeTo.length > 1 ) { // In case of bootstrapper installed plugins like `aurelia-templating-resources` or `aurelia-history-browser`.
    return loader.normalize(info.moduleId, info.resourcesRelativeTo[1])
      .then(normalizedId => _loadPlugin(normalizedId));
  }

  return _loadPlugin(id);

  function _loadPlugin(moduleId) {
    return loader.loadModule(moduleId).then(m => { // eslint-disable-line consistent-return
      if ('configure' in m) {
        return Promise.resolve(m.configure(config, info.config || {})).then(() => {
          config.resourcesRelativeTo = null;
          logger.debug(`Configured plugin ${info.moduleId}.`);
        });
      }

      config.resourcesRelativeTo = null;
      logger.debug(`Loaded plugin ${info.moduleId}.`);
    });
  }
}

function loadResources(aurelia, resourcesToLoad, appResources) {
  let viewEngine = aurelia.container.get(ViewEngine);

  return Promise.all(Object.keys(resourcesToLoad).map(n => _normalize(resourcesToLoad[n])))
    .then(loads => {
      let names = [];
      let importIds = [];

      loads.forEach(l => {
        names.push(undefined);
        importIds.push(l.importId);
      });

      return viewEngine.importViewResources(importIds, names, appResources);
    });

  function _normalize(load) {
    let moduleId = load.moduleId;
    let ext = getExt(moduleId);

    if (isOtherResource(moduleId)) {
      moduleId = removeExt(moduleId);
    }

    return aurelia.loader.normalize(moduleId, load.relativeTo)
      .then(normalized => {
        return {
          name: load.moduleId,
          importId: isOtherResource(load.moduleId) ? addOriginalExt(normalized, ext) : normalized
        };
      });
  }

  function isOtherResource(name) {
    let ext = getExt(name);
    if (!ext) return false;
    if (ext === '') return false;
    if (ext === '.js' || ext === '.ts') return false;
    return true;
  }

  function removeExt(name) {
    return name.replace(extPattern, '');
  }

  function addOriginalExt(normalized, ext) {
    return removeExt(normalized) + '.' + ext;
  }
}

function getExt(name) { // eslint-disable-line consistent-return
  let match = name.match(extPattern);
  if (match && match.length > 0) {
    return (match[0].split('.'))[1];
  }
}

function assertProcessed(plugins) {
  if (plugins.processed) {
    throw new Error('This config instance has already been applied. To load more plugins or global resources, create a new FrameworkConfiguration instance.');
  }
}

/**
 * Manages configuring the aurelia framework instance.
 */
export class FrameworkConfiguration {
  /**
   * The root DI container used by the application.
   */
  container: Container;

  /**
   * The aurelia instance.
   */
  aurelia: Aurelia;

  /**
   * Creates an instance of FrameworkConfiguration.
   * @param aurelia An instance of Aurelia.
   */
  constructor(aurelia: Aurelia) {
    this.aurelia = aurelia;
    this.container = aurelia.container;
    this.info = [];
    this.processed = false;
    this.preTasks = [];
    this.postTasks = [];
    this.resourcesToLoad = {};
    this.preTask(() => aurelia.loader.normalize('aurelia-bootstrapper').then(name => this.bootstrapperName = name));
    this.postTask(() => loadResources(aurelia, this.resourcesToLoad, aurelia.resources));
  }

  /**
   * Adds an existing object to the framework's dependency injection container.
   * @param type The object type of the dependency that the framework will inject.
   * @param instance The existing instance of the dependency that the framework will inject.
   * @return Returns the current FrameworkConfiguration instance.
   */
  instance(type: any, instance: any): FrameworkConfiguration {
    this.container.registerInstance(type, instance);
    return this;
  }

  /**
   * Adds a singleton to the framework's dependency injection container.
   * @param type The object type of the dependency that the framework will inject.
   * @param implementation The constructor function of the dependency that the framework will inject.
   * @return Returns the current FrameworkConfiguration instance.
   */
  singleton(type: any, implementation?: Function): FrameworkConfiguration {
    this.container.registerSingleton(type, implementation);
    return this;
  }

  /**
   * Adds a transient to the framework's dependency injection container.
   * @param type The object type of the dependency that the framework will inject.
   * @param implementation The constructor function of the dependency that the framework will inject.
   * @return Returns the current FrameworkConfiguration instance.
   */
  transient(type: any, implementation?: Function): FrameworkConfiguration {
    this.container.registerTransient(type, implementation);
    return this;
  }

  /**
   * Adds an async function that runs before the plugins are run.
   * @param task The function to run before start.
   * @return Returns the current FrameworkConfiguration instance.
   */
  preTask(task: Function): FrameworkConfiguration {
    assertProcessed(this);
    this.preTasks.push(task);
    return this;
  }

  /**
   * Adds an async function that runs after the plugins are run.
   * @param task The function to run after start.
   * @return Returns the current FrameworkConfiguration instance.
   */
  postTask(task: Function): FrameworkConfiguration {
    assertProcessed(this);
    this.postTasks.push(task);
    return this;
  }

  /**
   * Configures an internal feature plugin before Aurelia starts.
   * @param plugin The folder for the internal plugin to configure (expects an index.js in that folder).
   * @param config The configuration for the specified plugin.
   * @return Returns the current FrameworkConfiguration instance.
   */
  feature(plugin: string, config?: any): FrameworkConfiguration {
    if (getExt(plugin)) {
      return this.plugin({ moduleId: plugin, resourcesRelativeTo: [plugin, ''], config: config || {} });
    }

    return this.plugin({ moduleId: plugin + '/index', resourcesRelativeTo: [plugin, ''], config: config || {} });
  }

  /**
   * Adds globally available view resources to be imported into the Aurelia framework.
   * @param resources The relative module id to the resource. (Relative to the plugin's installer.)
   * @return Returns the current FrameworkConfiguration instance.
   */
  globalResources(resources: string|string[]): FrameworkConfiguration {
    assertProcessed(this);

    let toAdd = Array.isArray(resources) ? resources : arguments;
    let resource;
    let resourcesRelativeTo = this.resourcesRelativeTo || ['', ''];

    for (let i = 0, ii = toAdd.length; i < ii; ++i) {
      resource = toAdd[i];
      if (typeof resource !== 'string') {
        throw new Error(`Invalid resource path [${resource}]. Resources must be specified as relative module IDs.`);
      }

      let parent = resourcesRelativeTo[0];
      let grandParent = resourcesRelativeTo[1];
      let name = resource;

      if ((resource.startsWith('./') || resource.startsWith('../')) && parent !== '') {
        name = join(parent, resource);
      }

      this.resourcesToLoad[name] = { moduleId: name, relativeTo: grandParent };
    }

    return this;
  }

  /**
   * Renames a global resource that was imported.
   * @param resourcePath The path to the resource.
   * @param newName The new name.
   * @return Returns the current FrameworkConfiguration instance.
   */
  globalName(resourcePath: string, newName: string): FrameworkConfiguration {
    assertProcessed(this);
    this.resourcesToLoad[resourcePath] = { moduleId: newName, relativeTo: '' };
    return this;
  }

  /**
   * Configures an external, 3rd party plugin before Aurelia starts.
   * @param plugin The ID of the 3rd party plugin to configure.
   * @param config The configuration for the specified plugin.
   * @return Returns the current FrameworkConfiguration instance.
 */
  plugin(plugin: string, config?: any): FrameworkConfiguration {
    assertProcessed(this);

    if (typeof (plugin) === 'string') {
      return this.plugin({ moduleId: plugin, resourcesRelativeTo: [plugin, ''], config: config || {} });
    }

    this.info.push(plugin);
    return this;
  }

  _addNormalizedPlugin(name, config) {
    let plugin = { moduleId: name, resourcesRelativeTo: [name, ''], config: config || {} };
    this.plugin(plugin);

    this.preTask(() => {
      let relativeTo = [name, this.bootstrapperName];
      plugin.moduleId = name;
      plugin.resourcesRelativeTo = relativeTo;
      return Promise.resolve();
    });

    return this;
  }

  // Default configuration helpers
  // Note: Please do NOT add PLATFORM.moduleName() around those module names.
  //       Those functions are not guaranteed to be called, they are here to faciliate
  //       common configurations. If they are not called, we don't want to include a
  //       static dependency on those modules.
  //       Including those modules in the bundle or not is a decision that must be
  //       taken by the bundling tool, at build time.

  /**
   * Plugs in the default binding language from aurelia-templating-binding.
   * @return Returns the current FrameworkConfiguration instance.
  */
  defaultBindingLanguage(): FrameworkConfiguration {
    return this._addNormalizedPlugin('aurelia-templating-binding');
  }

  /**
   * Plugs in the router from aurelia-templating-router.
   * @return Returns the current FrameworkConfiguration instance.
  */
  router(): FrameworkConfiguration {
    return this._addNormalizedPlugin('aurelia-templating-router');
  }

  /**
   * Plugs in the default history implementation from aurelia-history-browser.
   * @return Returns the current FrameworkConfiguration instance.
  */
  history(): FrameworkConfiguration {
    return this._addNormalizedPlugin('aurelia-history-browser');
  }

  /**
   * Plugs in the default templating resources (if, repeat, show, compose, etc.) from aurelia-templating-resources.
   * @return Returns the current FrameworkConfiguration instance.
  */
  defaultResources(): FrameworkConfiguration {
    return this._addNormalizedPlugin('aurelia-templating-resources');
  }

  /**
   * Plugs in the event aggregator from aurelia-event-aggregator.
   * @return Returns the current FrameworkConfiguration instance.
  */
  eventAggregator(): FrameworkConfiguration {
    return this._addNormalizedPlugin('aurelia-event-aggregator');
  }

  /**
   * Sets up a basic Aurelia configuration. This is equivalent to calling `.defaultBindingLanguage().defaultResources().eventAggregator();`
   * @return Returns the current FrameworkConfiguration instance.
  */
  basicConfiguration(): FrameworkConfiguration {
    return this.defaultBindingLanguage().defaultResources().eventAggregator();
  }

  /**
   * Sets up the standard Aurelia configuration. This is equivalent to calling `.defaultBindingLanguage().defaultResources().eventAggregator().history().router();`
   * @return Returns the current FrameworkConfiguration instance.
  */
  standardConfiguration(): FrameworkConfiguration {
    return this.basicConfiguration().history().router();
  }

  /**
   * Plugs in the ConsoleAppender and sets the log level to debug.
   * @return {FrameworkConfiguration} Returns the current FrameworkConfiguration instance.
  */
  developmentLogging(): FrameworkConfiguration {
    this.preTask(() => {
      return this.aurelia.loader.normalize('aurelia-logging-console', this.bootstrapperName).then(name => {
        return this.aurelia.loader.loadModule(name).then(m => {
          TheLogManager.addAppender(new m.ConsoleAppender());
          TheLogManager.setLevel(TheLogManager.logLevel.debug);
        });
      });
    });

    return this;
  }

  /**
   * Loads and configures the plugins registered with this instance.
   * @return Returns a promise which resolves when all plugins are loaded and configured.
  */
  apply(): Promise<void> {
    if (this.processed) {
      return Promise.resolve();
    }

    return runTasks(this, this.preTasks).then(() => {
      let loader = this.aurelia.loader;
      let info = this.info;
      let current;

      let next = () => {
        current = info.shift();
        if (current) {
          return loadPlugin(this, loader, current).then(next);
        }

        this.processed = true;
        return Promise.resolve();
      };

      return next().then(() => runTasks(this, this.postTasks));
    });
  }
}
<|endoftext|>


<commit_before>import loadScript from 'load-script'

const BEEJS_URL = 'https://app-rsrc.getbee.io/plugin/BeePlugin.js'
const API_AUTH_URL = 'https://auth.getbee.io/apiauth'

const load = (bee) => {
  loadScript(BEEJS_URL, (err) => {
    if (err) {
      throw new Error('BeePlugin.js is not reachable')
    }
    return bee()
  })
}

const beeExisist = (instance) => {
  if (!instance) {
    throw new Error('Bee is not started')
  }
}

export default class Bee {
  constructor(token) {
    this.bee = (call) => load(() => call())
    this.token = token || null
    this.config = null
    this.instance = null
  }

  getToken(clientId, clientSecret) {
    const config = {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
      body: `grant_type=password&client_id=${clientId}&client_secret=${clientSecret}`,
      mode: 'cors',
    }
    if (this.token) {
      throw new Error('Toker already declared')
    }
    return fetch(new Request(API_AUTH_URL, config))
    .then(res => res.json())
    .then(token => {
      this.token = token
      return token
    })
  }

  start(config, template) {
    const { bee, token } = this
    if (!config || !template) {
      throw new Error('Config or template are missing')
    }
    if (!this.token) {
      throw new Error('Toker NOT declared, call getToken or pass token on new BEE')
    }
    return bee(() =>
      BeePlugin.create(token, config, (instance) => {
        this.instance = instance
        return instance.start(template)
      })
    )
  }

  load(template) {
    const { instance } = this
    beeExisist(instance)
    return instance.laod(template)
  }

  save() {
    const { instance } = this
    beeExisist(instance)
    return instance.save()
  }

  saveAsTemplate() {
    const { instance } = this
    beeExisist(instance)
    return instance.saveAsTemplate()
  }
}
<commit_msg>Small adjustements and a bug fix<commit_after>import loadScript from 'load-script'

const BEEJS_URL = 'https://app-rsrc.getbee.io/plugin/BeePlugin.js'
const API_AUTH_URL = 'https://auth.getbee.io/apiauth'

const load = (bee) => {
  loadScript(BEEJS_URL, err => {
    if (err) {
      throw new Error('BeePlugin.js is not reachable')
    }
    return bee()
  })
}

const beeExists = instance => {
  if (!instance) {
    throw new Error('Bee is not started')
  }
}

export default class Bee {
  constructor(token) {
    this.bee = (call) => load(() => call())
    this.token = token || null
    this.config = null
    this.instance = null
  }

  getToken(clientId, clientSecret) {
    const config = {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
      body: `grant_type=password&client_id=${clientId}&client_secret=${clientSecret}`,
      mode: 'cors',
    }
    if (this.token) {
      throw new Error('Toker already declared')
    }
    return fetch(new Request(API_AUTH_URL, config))
    .then(res => res.json())
    .then(token => {
      this.token = token
      return token
    })
  }

  start(config, template) {
    const { bee, token } = this
    if (!config || !template) {
      throw new Error('Config or template are missing')
    }
    if (!this.token) {
      throw new Error('Toker NOT declared, call getToken or pass token on new BEE')
    }
    return bee(() =>
      BeePlugin.create(token, config, instance => {
        this.instance = instance
        return instance.start(template)
      })
    )
  }

  load(template) {
    const { instance } = this
    beeExists(instance)
    return instance.load(template)
  }

  save() {
    const { instance } = this
    beeExists(instance)
    return instance.save()
  }

  saveAsTemplate() {
    const { instance } = this
    beeExists(instance)
    return instance.saveAsTemplate()
  }
}
<|endoftext|>