<commit_before>import path from 'path';

import State from './state';

import {deleteFileOrFolder} from '../../helpers';
import FilePatch from '../file-patch';
import Hunk from '../hunk';
import HunkLine from '../hunk-line';
import DiscardHistory from '../discard-history';

class Cache {
  constructor() {
    this.storage = new Map();
  }

  getOrSet(key, operation) {
    const existing = this.storage.get(key);
    if (existing !== undefined) {
      return existing;
    }

    const created = operation();
    this.storage.set(key, created);
    return created;
  }

  clear() {
    this.storage.clear();
  }
}

export default class Present extends State {
  constructor(repository, history) {
    super(repository);

    this.cache = new Cache();

    this.discardHistory = new DiscardHistory(
      this.createBlob.bind(this),
      this.expandBlobToFile.bind(this),
      this.mergeFile.bind(this),
      this.workdir.bind(this),
      {maxHistoryLength: 60},
    );
  }

  isPresent() {
    return true;
  }

  destroy() {
    this.transitionTo('Destroyed');
  }

  refresh() {
    this.cache.clear();
    this.didUpdate();
  }

  // Git operations ////////////////////////////////////////////////////////////////////////////////////////////////////

  // Staging and unstaging

  stageFiles(paths) {
    return this.git().stageFiles(paths);
  }

  unstageFiles(paths) {
    return this.git().unstageFiles(paths);
  }

  stageFilesFromParentCommit(paths) {
    return this.git().unstageFiles(paths, 'HEAD~');
  }

  applyPatchToIndex(filePatch) {
    const patchStr = filePatch.getHeaderString() + filePatch.toString();
    return this.git().applyPatch(patchStr, {index: true});
  }

  applyPatchToWorkdir(filePatch) {
    const patchStr = filePatch.getHeaderString() + filePatch.toString();
    return this.git().applyPatch(patchStr);
  }

  // Committing

  commit(message, options) {
    return this.git().commit(formatCommitMessage(message), options);
  }

  // Merging

  merge(branchName) {
    return this.git().merge(branchName);
  }

  async abortMerge() {
    await this.git().abortMerge();
    this.refresh();
  }

  checkoutSide(side, paths) {
    return this.git().checkoutSide(side, paths);
  }

  mergeFile(oursPath, commonBasePath, theirsPath, resultPath) {
    return this.git().mergeFile(oursPath, commonBasePath, theirsPath, resultPath);
  }

  writeMergeConflictToIndex(filePath, commonBaseSha, oursSha, theirsSha) {
    return this.git().writeMergeConflictToIndex(filePath, commonBaseSha, oursSha, theirsSha);
  }

  // Checkout

  checkout(revision, options = {}) {
    return this.git().checkout(revision, options);
  }

  checkoutPathsAtRevision(paths, revision = 'HEAD') {
    return this.git().checkoutFiles(paths, revision);
  }

  // Remote interactions

  fetch(branchName) {
    return this.git().fetch(branchName);
  }

  pull(branchName) {
    return this.git().pull(branchName);
  }

  push(branchName, options) {
    return this.git().push(branchName, options);
  }

  // Configuration

  setConfig(option, value, options) {
    return this.git().setConfig(option, value, options);
  }

  // Direct blob interactions

  createBlob(options) {
    return this.git().createBlob(options);
  }

  expandBlobToFile(absFilePath, sha) {
    return this.git().expandBlobToFile(absFilePath, sha);
  }

  // Discard history

  createDiscardHistoryBlob() {
    return this.discardHistory.createHistoryBlob();
  }

  async updateDiscardHistory() {
    const history = await this.loadHistoryPayload();
    this.discardHistory.updateHistory(history);
  }

  async storeBeforeAndAfterBlobs(filePaths, isSafe, destructiveAction, partialDiscardFilePath = null) {
    const snapshots = await this.discardHistory.storeBeforeAndAfterBlobs(
      filePaths,
      isSafe,
      destructiveAction,
      partialDiscardFilePath,
    );
    if (snapshots) { await this.saveDiscardHistory(); }
    return snapshots;
  }

  restoreLastDiscardInTempFiles(isSafe, partialDiscardFilePath = null) {
    return this.discardHistory.restoreLastDiscardInTempFiles(isSafe, partialDiscardFilePath);
  }

  async popDiscardHistory(partialDiscardFilePath = null) {
    const removed = await this.discardHistory.popHistory(partialDiscardFilePath);
    if (removed) { await this.saveDiscardHistory(); }
  }

  clearDiscardHistory(partialDiscardFilePath = null) {
    this.discardHistory.clearHistory(partialDiscardFilePath);
    return this.saveDiscardHistory();
  }

  async discardWorkDirChangesForPaths(paths) {
    const untrackedFiles = await this.git.getUntrackedFiles();
    const [filesToRemove, filesToCheckout] = partition(paths, f => untrackedFiles.includes(f));
    await this.git.checkoutFiles(filesToCheckout);
    await Promise.all(filesToRemove.map(filePath => {
      const absPath = path.join(this.getWorkingDirectoryPath(), filePath);
      return deleteFileOrFolder(absPath);
    }));
  }

  // Accessors /////////////////////////////////////////////////////////////////////////////////////////////////////////

  // Index queries

  getStatusesForChangedFiles() {
    return this.cache.getOrSet('changed-files', () => {
      return this.git().getStatusesForChangedFiles();
    });
  }

  getStagedChangesSinceParentCommit() {
    return this.cache.getOrSet('staged-changes-since-parent-commit', async () => {
      try {
        const stagedFiles = await this.git().diffFileStatus({staged: true, target: 'HEAD~'});
        return Object.keys(stagedFiles).map(filePath => ({filePath, status: stagedFiles[filePath]}));
      } catch (e) {
        if (e.message.includes('ambiguous argument \'HEAD~\'')) {
          return [];
        } else {
          throw e;
        }
      }
    });
  }

  isPartiallyStaged(fileName) {
    return this.cache.getOrSet(`is-partially-staged:${fileName}`, () => {
      return this.git().isPartiallyStaged(fileName);
    });
  }

  getFilePatchForPath(filePath, {staged, amending} = {staged: false, amending: false}) {
    let desc = '';
    if (staged && amending) {
      desc = 'p';
    } else if (staged) {
      desc = 's';
    } else {
      desc = 'u';
    }

    return this.cache.getOrSet(`file-patch:${desc}:${filePath}`, async () => {
      const options = {staged, amending};
      if (amending) {
        options.baseCommit = 'HEAD~';
      }

      const rawDiff = await this.git().getDiffForFilePath(filePath, options);
      if (rawDiff) {
        const [filePatch] = buildFilePatchesFromRawDiffs([rawDiff]);
        return filePatch;
      } else {
        return null;
      }
    });
  }

  readFileFromIndex(filePath) {
    return this.cache.getOrSet(`index:${filePath}`, () => {
      return this.git().readFileFromIndex(filePath);
    });
  }

  // Commit access

  getLastCommit() {
    return this.cache.getOrSet('last-commit', () => {
      return this.git().getHeadCommit();
    });
  }

  // Branches

  getBranches() {
    return this.cache.getOrSet('branches', () => {
      return this.git().getBranches();
    });
  }

  getCurrentBranch() {
    return this.cache.getOrSet('current-branch', () => {
      return this.git().getCurrentBranch();
    });
  }

  // Merging and rebasing status

  isMerging() {
    return this.git().isMerging();
  }

  isRebasing() {
    return this.git().isRebasing();
  }

  // Remotes

  getRemotes() {
    return this.cache.getOrSet('remotes', () => {
      return this.git().getRemotes();
    });
  }

  getRemoteForBranch(branchName) {
    return this.cache.getOrSet(`remote-for-branch:${branchName}`, () => {
      return this.git().getRemoteForBranch(branchName);
    });
  }

  getAheadCount(branchName) {
    return this.cache.getOrSet(`ahead-count:${branchName}`, () => {
      return this.git().getAheadCount(branchName);
    });
  }

  getBehindCount(branchName) {
    return this.cache.getOrSet(`behind-count:${branchName}`, () => {
      return this.git().getBehindCount(branchName);
    });
  }

  getConfig(option, {local} = {local: false}) {
    const desc = local ? 'l' : '';
    return this.cache.getOrSet(`config:${desc}:${option}`, () => {
      return this.git().getConfig(option, {local});
    });
  }

  // Direct blob access

  getBlobContents(sha) {
    return this.cache.getOrSet(`blob:${sha}`, () => {
      return this.git().getBlobContents(sha);
    });
  }

  // Discard history

  hasDiscardHistory(partialDiscardFilePath = null) {
    return this.discardHistory.hasHistory(partialDiscardFilePath);
  }

  getDiscardHistory(partialDiscardFilePath = null) {
    return this.discardHistory.getHistory(partialDiscardFilePath);
  }

  getLastHistorySnapshots(partialDiscardFilePath = null) {
    return this.discardHistory.getLastSnapshots(partialDiscardFilePath);
  }
}

State.register(Present);

function partition(array, predicate) {
  const matches = [];
  const nonmatches = [];
  array.forEach(item => {
    if (predicate(item)) {
      matches.push(item);
    } else {
      nonmatches.push(item);
    }
  });
  return [matches, nonmatches];
}

function formatCommitMessage(message) {
  // strip out comments
  const messageWithoutComments = message.replace(/^#.*$/mg, '').trim();

  // hard wrap message (except for first line) at 72 characters
  let results = [];
  messageWithoutComments.split('\n').forEach((line, index) => {
    if (line.length <= 72 || index === 0) {
      results.push(line);
    } else {
      const matches = line.match(/.{1,72}(\s|$)|\S+?(\s|$)/g)
        .map(match => {
          return match.endsWith('\n') ? match.substr(0, match.length - 1) : match;
        });
      results = results.concat(matches);
    }
  });

  return results.join('\n');
}

function buildFilePatchesFromRawDiffs(rawDiffs) {
  let diffLineNumber = 0;
  return rawDiffs.map(patch => {
    const hunks = patch.hunks.map(hunk => {
      let oldLineNumber = hunk.oldStartLine;
      let newLineNumber = hunk.newStartLine;
      const hunkLines = hunk.lines.map(line => {
        const status = HunkLine.statusMap[line[0]];
        const text = line.slice(1);
        let hunkLine;
        if (status === 'unchanged') {
          hunkLine = new HunkLine(text, status, oldLineNumber, newLineNumber, diffLineNumber++);
          oldLineNumber++;
          newLineNumber++;
        } else if (status === 'added') {
          hunkLine = new HunkLine(text, status, -1, newLineNumber, diffLineNumber++);
          newLineNumber++;
        } else if (status === 'deleted') {
          hunkLine = new HunkLine(text, status, oldLineNumber, -1, diffLineNumber++);
          oldLineNumber++;
        } else if (status === 'nonewline') {
          hunkLine = new HunkLine(text.substr(1), status, -1, -1, diffLineNumber++);
        } else {
          throw new Error(`unknow status type: ${status}`);
        }
        return hunkLine;
      });
      return new Hunk(
        hunk.oldStartLine,
        hunk.newStartLine,
        hunk.oldLineCount,
        hunk.newLineCount,
        hunk.heading,
        hunkLines,
      );
    });
    return new FilePatch(patch.oldPath, patch.newPath, patch.status, hunks);
  });
}
<commit_msg>Cache invalidation with a fancy decorator<commit_after>import path from 'path';

import State from './state';

import {deleteFileOrFolder} from '../../helpers';
import FilePatch from '../file-patch';
import Hunk from '../hunk';
import HunkLine from '../hunk-line';
import DiscardHistory from '../discard-history';

class Cache {
  constructor() {
    this.storage = new Map();
  }

  getOrSet(key, operation) {
    const existing = this.storage.get(key);
    if (existing !== undefined) {
      return existing;
    }

    const created = operation();
    this.storage.set(key, created);
    return created;
  }

  invalidate(...keys) {
    for (let i = 0; i < keys.length; i++) {
      this.storage.delete(keys[i]);
    }
  }

  clear() {
    this.storage.clear();
  }
}

/**
 * Decorator for an async method that invalidates the cache after execution (regardless of success or failure.)
 */
function invalidate() {
  return function(target, name, descriptor) {
    const original = descriptor.value;
    descriptor.value = function(...args) {
      const cache = this.cache;

      return original.apply(this, args).then(
        result => {
          cache.clear();
          return result;
        },
        err => {
          return Promise.reject(err);
        },
      );
    };
    return descriptor;
  };
}

export default class Present extends State {
  constructor(repository, history) {
    super(repository);

    this.cache = new Cache();

    this.discardHistory = new DiscardHistory(
      this.createBlob.bind(this),
      this.expandBlobToFile.bind(this),
      this.mergeFile.bind(this),
      this.workdir.bind(this),
      {maxHistoryLength: 60},
    );
  }

  isPresent() {
    return true;
  }

  destroy() {
    this.transitionTo('Destroyed');
  }

  refresh() {
    this.cache.clear();
    this.didUpdate();
  }

  // Git operations ////////////////////////////////////////////////////////////////////////////////////////////////////

  // Staging and unstaging

  @invalidate()
  stageFiles(paths) {
    return this.git().stageFiles(paths);
  }

  @invalidate()
  unstageFiles(paths) {
    return this.git().unstageFiles(paths);
  }

  @invalidate()
  stageFilesFromParentCommit(paths) {
    return this.git().unstageFiles(paths, 'HEAD~');
  }

  @invalidate()
  applyPatchToIndex(filePatch) {
    const patchStr = filePatch.getHeaderString() + filePatch.toString();
    return this.git().applyPatch(patchStr, {index: true});
  }

  @invalidate()
  applyPatchToWorkdir(filePatch) {
    const patchStr = filePatch.getHeaderString() + filePatch.toString();
    return this.git().applyPatch(patchStr);
  }

  // Committing

  @invalidate()
  commit(message, options) {
    return this.git().commit(formatCommitMessage(message), options);
  }

  // Merging

  @invalidate()
  merge(branchName) {
    return this.git().merge(branchName);
  }

  @invalidate()
  abortMerge() {
    return this.git().abortMerge();
  }

  @invalidate()
  checkoutSide(side, paths) {
    return this.git().checkoutSide(side, paths);
  }

  @invalidate()
  mergeFile(oursPath, commonBasePath, theirsPath, resultPath) {
    return this.git().mergeFile(oursPath, commonBasePath, theirsPath, resultPath);
  }

  @invalidate()
  writeMergeConflictToIndex(filePath, commonBaseSha, oursSha, theirsSha) {
    return this.git().writeMergeConflictToIndex(filePath, commonBaseSha, oursSha, theirsSha);
  }

  // Checkout

  @invalidate()
  checkout(revision, options = {}) {
    return this.git().checkout(revision, options);
  }

  @invalidate()
  checkoutPathsAtRevision(paths, revision = 'HEAD') {
    return this.git().checkoutFiles(paths, revision);
  }

  // Remote interactions

  @invalidate()
  async fetch(branchName) {
    const remote = await this.getRemoteForBranch(branchName);
    if (!remote.isPresent()) {
      return;
    }
    await this.git().fetch(remote.getName(), branchName);
  }

  @invalidate()
  async pull(branchName) {
    const remote = await this.getRemoteForBranch(branchName);
    if (!remote.isPresent()) {
      return;
    }
    await this.git().pull(remote.getName(), branchName);
  }

  @invalidate()
  async push(branchName, options) {
    const remote = await this.getRemoteForBranch(branchName);
    return this.git().push(remote.getNameOr('origin'), branchName, options);
  }

  // Configuration

  @invalidate()
  setConfig(option, value, options) {
    return this.git().setConfig(option, value, options);
  }

  // Direct blob interactions

  @invalidate()
  createBlob(options) {
    return this.git().createBlob(options);
  }

  expandBlobToFile(absFilePath, sha) {
    return this.git().expandBlobToFile(absFilePath, sha);
  }

  // Discard history

  createDiscardHistoryBlob() {
    return this.discardHistory.createHistoryBlob();
  }

  async updateDiscardHistory() {
    const history = await this.loadHistoryPayload();
    this.discardHistory.updateHistory(history);
  }

  async storeBeforeAndAfterBlobs(filePaths, isSafe, destructiveAction, partialDiscardFilePath = null) {
    const snapshots = await this.discardHistory.storeBeforeAndAfterBlobs(
      filePaths,
      isSafe,
      destructiveAction,
      partialDiscardFilePath,
    );
    if (snapshots) {
      await this.saveDiscardHistory();
    }
    return snapshots;
  }

  restoreLastDiscardInTempFiles(isSafe, partialDiscardFilePath = null) {
    return this.discardHistory.restoreLastDiscardInTempFiles(isSafe, partialDiscardFilePath);
  }

  async popDiscardHistory(partialDiscardFilePath = null) {
    const removed = await this.discardHistory.popHistory(partialDiscardFilePath);
    if (removed) {
      await this.saveDiscardHistory();
    }
  }

  clearDiscardHistory(partialDiscardFilePath = null) {
    this.discardHistory.clearHistory(partialDiscardFilePath);
    return this.saveDiscardHistory();
  }

  @invalidate()
  async discardWorkDirChangesForPaths(paths) {
    const untrackedFiles = await this.git().getUntrackedFiles();
    const [filesToRemove, filesToCheckout] = partition(paths, f => untrackedFiles.includes(f));
    await this.git().checkoutFiles(filesToCheckout);
    await Promise.all(filesToRemove.map(filePath => {
      const absPath = path.join(this.workdir(), filePath);
      return deleteFileOrFolder(absPath);
    }));
  }

  // Accessors /////////////////////////////////////////////////////////////////////////////////////////////////////////

  // Index queries

  getStatusesForChangedFiles() {
    return this.cache.getOrSet('changed-files', () => {
      return this.git().getStatusesForChangedFiles();
    });
  }

  getStagedChangesSinceParentCommit() {
    return this.cache.getOrSet('staged-changes-since-parent-commit', async () => {
      try {
        const stagedFiles = await this.git().diffFileStatus({staged: true, target: 'HEAD~'});
        return Object.keys(stagedFiles).map(filePath => ({filePath, status: stagedFiles[filePath]}));
      } catch (e) {
        if (e.message.includes('ambiguous argument \'HEAD~\'')) {
          return [];
        } else {
          throw e;
        }
      }
    });
  }

  isPartiallyStaged(fileName) {
    return this.cache.getOrSet(`is-partially-staged:${fileName}`, () => {
      return this.git().isPartiallyStaged(fileName);
    });
  }

  getFilePatchForPath(filePath, {staged, amending} = {staged: false, amending: false}) {
    let desc = '';
    if (staged && amending) {
      desc = 'p';
    } else if (staged) {
      desc = 's';
    } else {
      desc = 'u';
    }

    return this.cache.getOrSet(`file-patch:${desc}:${filePath}`, async () => {
      const options = {staged, amending};
      if (amending) {
        options.baseCommit = 'HEAD~';
      }

      const rawDiff = await this.git().getDiffForFilePath(filePath, options);
      if (rawDiff) {
        const [filePatch] = buildFilePatchesFromRawDiffs([rawDiff]);
        return filePatch;
      } else {
        return null;
      }
    });
  }

  readFileFromIndex(filePath) {
    return this.cache.getOrSet(`index:${filePath}`, () => {
      return this.git().readFileFromIndex(filePath);
    });
  }

  // Commit access

  getLastCommit() {
    return this.cache.getOrSet('last-commit', () => {
      return this.git().getHeadCommit();
    });
  }

  // Branches

  getBranches() {
    return this.cache.getOrSet('branches', () => {
      return this.git().getBranches();
    });
  }

  getCurrentBranch() {
    return this.cache.getOrSet('current-branch', () => {
      return this.git().getCurrentBranch();
    });
  }

  // Merging and rebasing status

  isMerging() {
    return this.git().isMerging();
  }

  isRebasing() {
    return this.git().isRebasing();
  }

  // Remotes

  getRemotes() {
    return this.cache.getOrSet('remotes', () => {
      return this.git().getRemotes();
    });
  }

  getAheadCount(branchName) {
    return this.cache.getOrSet(`ahead-count:${branchName}`, () => {
      return this.git().getAheadCount(branchName);
    });
  }

  getBehindCount(branchName) {
    return this.cache.getOrSet(`behind-count:${branchName}`, () => {
      return this.git().getBehindCount(branchName);
    });
  }

  getConfig(option, {local} = {local: false}) {
    const desc = local ? 'l' : '';
    return this.cache.getOrSet(`config:${desc}:${option}`, () => {
      return this.git().getConfig(option, {local});
    });
  }

  // Direct blob access

  getBlobContents(sha) {
    return this.cache.getOrSet(`blob:${sha}`, () => {
      return this.git().getBlobContents(sha);
    });
  }

  // Discard history

  hasDiscardHistory(partialDiscardFilePath = null) {
    return this.discardHistory.hasHistory(partialDiscardFilePath);
  }

  getDiscardHistory(partialDiscardFilePath = null) {
    return this.discardHistory.getHistory(partialDiscardFilePath);
  }

  getLastHistorySnapshots(partialDiscardFilePath = null) {
    return this.discardHistory.getLastSnapshots(partialDiscardFilePath);
  }
}

State.register(Present);

function partition(array, predicate) {
  const matches = [];
  const nonmatches = [];
  array.forEach(item => {
    if (predicate(item)) {
      matches.push(item);
    } else {
      nonmatches.push(item);
    }
  });
  return [matches, nonmatches];
}

function formatCommitMessage(message) {
  // strip out comments
  const messageWithoutComments = message.replace(/^#.*$/mg, '').trim();

  // hard wrap message (except for first line) at 72 characters
  let results = [];
  messageWithoutComments.split('\n').forEach((line, index) => {
    if (line.length <= 72 || index === 0) {
      results.push(line);
    } else {
      const matches = line.match(/.{1,72}(\s|$)|\S+?(\s|$)/g)
        .map(match => {
          return match.endsWith('\n') ? match.substr(0, match.length - 1) : match;
        });
      results = results.concat(matches);
    }
  });

  return results.join('\n');
}

function buildFilePatchesFromRawDiffs(rawDiffs) {
  let diffLineNumber = 0;
  return rawDiffs.map(patch => {
    const hunks = patch.hunks.map(hunk => {
      let oldLineNumber = hunk.oldStartLine;
      let newLineNumber = hunk.newStartLine;
      const hunkLines = hunk.lines.map(line => {
        const status = HunkLine.statusMap[line[0]];
        const text = line.slice(1);
        let hunkLine;
        if (status === 'unchanged') {
          hunkLine = new HunkLine(text, status, oldLineNumber, newLineNumber, diffLineNumber++);
          oldLineNumber++;
          newLineNumber++;
        } else if (status === 'added') {
          hunkLine = new HunkLine(text, status, -1, newLineNumber, diffLineNumber++);
          newLineNumber++;
        } else if (status === 'deleted') {
          hunkLine = new HunkLine(text, status, oldLineNumber, -1, diffLineNumber++);
          oldLineNumber++;
        } else if (status === 'nonewline') {
          hunkLine = new HunkLine(text.substr(1), status, -1, -1, diffLineNumber++);
        } else {
          throw new Error(`unknow status type: ${status}`);
        }
        return hunkLine;
      });
      return new Hunk(
        hunk.oldStartLine,
        hunk.newStartLine,
        hunk.oldLineCount,
        hunk.newLineCount,
        hunk.heading,
        hunkLines,
      );
    });
    return new FilePatch(patch.oldPath, patch.newPath, patch.status, hunks);
  });
}
<|endoftext|>


<commit_before>"use strict";

var util = require ("util");
var packets = require ("../packets");
var opcodes = require ("../opcodes");
var errors = require ("../errors");
var Request = require ("../request");
var knownExtensions = require ("../known-extensions");

var IncomingRequest = module.exports = function (args){
	Request.call (this, args.helper._rinfo.address, args.helper._rinfo.port,
			args.globalOptions.retries, args.globalOptions.extensions.timeout);

	this._isRRQ = !args.reader;
	this._globalOptions = args.globalOptions;
	this._maxDataLength = 4;
	this._file = args.message.file;
	this._size = args.size || null;
	this._requestUserExtensions = args.message.userExtensions;
	this._responseUserExtensions = null;
	this._oackSent = false;
	this._firstPacket = true;
	this._requestExtensions = args.message.extensions;
	this._responseExtensions = null;

	var me = this;
	this._initSocket (args.helper._socket, function (message){
		if (me._firstPacket){
			me._firstPacket = false;
			me._requestTimer.reset ();
		}
		me._onMessage (message);
	});

	//The socket is still not bound to an address and port because no packet has
	//been still sent, so the stats cannot be emitted yet (the call to address()
	//fails)
	//The socket must be manually bound
	this._socket.bind (0, null, function (){
		if (me._requestExtensions === null){
			//The client doesn't send extensions, it's probably that it doesn't
			//rollover automatically (old client), so we can abort the transfer
			//prematurely
			//Check the size (65535x512)
			if (me._isRRQ && me._size > 33553920){
				return me._sendErrorAndClose (errors.EFBIG);
			}
			me._onReady (me._createStats (512, 1), 0);
			if (!me._isRRQ){
				//The ACK of the block 0 is retransmitted from the reader
				me._sendAck (0);
			}
		}else{
			//Send OACK
			me._sendOackMessage (me._requestExtensions);
		}
	});
};

util.inherits (IncomingRequest, Request);

IncomingRequest.prototype.continueRequest = function (size){
	this._setSize (size);

	if (this._requestExtensions === null){
		this.onContinue ();
	}else{
		this._responseExtensions.tsize = size;

		//Set the user extensions
		for (var p in this._responseUserExtensions){
			//Ignore invalid extensions
			if (knownExtensions[p]) continue;
			if (this._requestUserExtensions[p] === undefined) continue;
			this._responseExtensions[p] = this._responseUserExtensions[p];
		}

		this._sendAndRetransmit (packets.oack.serialize (this._responseExtensions));
	}
};

IncomingRequest.prototype._createStats = function (blockSize, windowSize){
	//Save max data length
	this._maxDataLength += blockSize;

	var address = this._socket.address ();

	return {
		blockSize: blockSize,
		windowSize: windowSize,
		size: this._size,
		userExtensions: this._requestUserExtensions,
		retries: this._globalOptions.retries,
		timeout: this._timeout,
		localAddress: address.address,
		localPort: address.port,
		remoteAddress: this._address,
		remotePort: this._port,
	};
};

IncomingRequest.prototype._onMessage = function (buffer){
	var op = buffer.readUInt16BE (0);

	if (op === opcodes.DATA){
		if (!this._isRRQ){
			if (buffer.length < 4 || buffer.length > this._maxDataLength){
				return this._sendErrorAndClose (errors.EBADMSG);
			}
			try{
				this._onData (packets.data.deserialize (buffer));
			}catch (error){
				this._sendErrorAndClose (error);
			}
		}else{
			this._sendErrorAndClose (errors.EBADOP);
		}
	}else if (op === opcodes.ACK){
		if (this._isRRQ){
			if (buffer.length !== 4){
				return this._sendErrorAndClose (errors.EBADMSG);
			}
			if (this._oackSent){
				this._oackSent = false;
				if (buffer.readUInt16BE (2) !== 0){
					this._sendErrorAndClose (errors.EBADMSG);
				}else{
					this.onContinue ();
				}
			}else{
				try{
					this._onAck (packets.ack.deserialize (buffer));
				}catch (error){
					this._sendErrorAndClose (error);
				}
			}
		}else{
			this._sendErrorAndClose (errors.EBADOP);
		}
	}else if (op === opcodes.ERROR){
		if (buffer.length < 4) return this._closeWithError (errors.EBADMSG);
		try{
			this._close (new Error (packets.error.deserialize (buffer).message));
		}catch (error){
			return this._closeWithError (error);
		}
	}else{
		this._sendErrorAndClose (errors.EBADOP);
	}
};

IncomingRequest.prototype._sendOackMessage = function (extensions){
	var ext = {};
	if (extensions.blksize !== undefined){
		var blksize = this._globalOptions.extensions.blksize;
		ext.blksize = extensions.blksize > blksize ? blksize : extensions.blksize;
	}
	if (extensions.windowsize !== undefined){
		var windowsize = this._globalOptions.extensions.windowsize;
		ext.windowsize = extensions.windowsize > windowsize
				? windowsize
				: extensions.windowsize;
	}
	if (extensions.tsize !== undefined){
		if (!this._isRRQ) this._size = extensions.tsize;
		//ext.tsize is set later, when the request continues (RRQ)
	}
	if (extensions.rollover !== undefined){
		ext.rollover = 0;
	}

	this._oackSent = true;
	var me = this;
	var ready = function (){
		me._onReady (me._createStats (ext.blksize || 512, ext.windowsize || 1),
			0, true);
	};

	if (this._isRRQ){
		//Save the extensions
		this._responseExtensions = ext;
		ready ();
	}else{
		ready ();
		this._sendAndRetransmit (packets.oack.serialize (ext));
	}
};<commit_msg>Check tsize option in request extension<commit_after>"use strict";

var util = require ("util");
var packets = require ("../packets");
var opcodes = require ("../opcodes");
var errors = require ("../errors");
var Request = require ("../request");
var knownExtensions = require ("../known-extensions");

var IncomingRequest = module.exports = function (args){
	Request.call (this, args.helper._rinfo.address, args.helper._rinfo.port,
			args.globalOptions.retries, args.globalOptions.extensions.timeout);

	this._isRRQ = !args.reader;
	this._globalOptions = args.globalOptions;
	this._maxDataLength = 4;
	this._file = args.message.file;
	this._size = args.size || null;
	this._requestUserExtensions = args.message.userExtensions;
	this._responseUserExtensions = null;
	this._oackSent = false;
	this._firstPacket = true;
	this._requestExtensions = args.message.extensions;
	this._responseExtensions = null;

	var me = this;
	this._initSocket (args.helper._socket, function (message){
		if (me._firstPacket){
			me._firstPacket = false;
			me._requestTimer.reset ();
		}
		me._onMessage (message);
	});

	//The socket is still not bound to an address and port because no packet has
	//been still sent, so the stats cannot be emitted yet (the call to address()
	//fails)
	//The socket must be manually bound
	this._socket.bind (0, null, function (){
		if (me._requestExtensions === null){
			//The client doesn't send extensions, it's probably that it doesn't
			//rollover automatically (old client), so we can abort the transfer
			//prematurely
			//Check the size (65535x512)
			if (me._isRRQ && me._size > 33553920){
				return me._sendErrorAndClose (errors.EFBIG);
			}
			me._onReady (me._createStats (512, 1), 0);
			if (!me._isRRQ){
				//The ACK of the block 0 is retransmitted from the reader
				me._sendAck (0);
			}
		}else{
			//Send OACK
			me._sendOackMessage (me._requestExtensions);
		}
	});
};

util.inherits (IncomingRequest, Request);

IncomingRequest.prototype.continueRequest = function (size){
	this._setSize (size);

	if (this._requestExtensions === null){
		this.onContinue ();
	}else{
        if( this._responseExtensions.tsize !== undefined){
            this._responseExtensions.tsize = size;
        }

		//Set the user extensions
		for (var p in this._responseUserExtensions){
			//Ignore invalid extensions
			if (knownExtensions[p]) continue;
			if (this._requestUserExtensions[p] === undefined) continue;
			this._responseExtensions[p] = this._responseUserExtensions[p];
		}

		this._sendAndRetransmit (packets.oack.serialize (this._responseExtensions));
	}
};

IncomingRequest.prototype._createStats = function (blockSize, windowSize){
	//Save max data length
	this._maxDataLength += blockSize;

	var address = this._socket.address ();

	return {
		blockSize: blockSize,
		windowSize: windowSize,
		size: this._size,
		userExtensions: this._requestUserExtensions,
		retries: this._globalOptions.retries,
		timeout: this._timeout,
		localAddress: address.address,
		localPort: address.port,
		remoteAddress: this._address,
		remotePort: this._port,
	};
};

IncomingRequest.prototype._onMessage = function (buffer){
	var op = buffer.readUInt16BE (0);

	if (op === opcodes.DATA){
		if (!this._isRRQ){
			if (buffer.length < 4 || buffer.length > this._maxDataLength){
				return this._sendErrorAndClose (errors.EBADMSG);
			}
			try{
				this._onData (packets.data.deserialize (buffer));
			}catch (error){
				this._sendErrorAndClose (error);
			}
		}else{
			this._sendErrorAndClose (errors.EBADOP);
		}
	}else if (op === opcodes.ACK){
		if (this._isRRQ){
			if (buffer.length !== 4){
				return this._sendErrorAndClose (errors.EBADMSG);
			}
			if (this._oackSent){
				this._oackSent = false;
				if (buffer.readUInt16BE (2) !== 0){
					this._sendErrorAndClose (errors.EBADMSG);
				}else{
					this.onContinue ();
				}
			}else{
				try{
					this._onAck (packets.ack.deserialize (buffer));
				}catch (error){
					this._sendErrorAndClose (error);
				}
			}
		}else{
			this._sendErrorAndClose (errors.EBADOP);
		}
	}else if (op === opcodes.ERROR){
		if (buffer.length < 4) return this._closeWithError (errors.EBADMSG);
		try{
			this._close (new Error (packets.error.deserialize (buffer).message));
		}catch (error){
			return this._closeWithError (error);
		}
	}else{
		this._sendErrorAndClose (errors.EBADOP);
	}
};

IncomingRequest.prototype._sendOackMessage = function (extensions){
	var ext = {};
	if (extensions.blksize !== undefined){
		var blksize = this._globalOptions.extensions.blksize;
		ext.blksize = extensions.blksize > blksize ? blksize : extensions.blksize;
	}
	if (extensions.windowsize !== undefined){
		var windowsize = this._globalOptions.extensions.windowsize;
		ext.windowsize = extensions.windowsize > windowsize
				? windowsize
				: extensions.windowsize;
	}
	if (extensions.tsize !== undefined){
		if (!this._isRRQ) this._size = extensions.tsize;
		ext.tsize = extensions.tsize;
		//ext.tsize is set later, when the request continues (RRQ)
	}
	if (extensions.rollover !== undefined){
		ext.rollover = 0;
	}

	this._oackSent = true;
	var me = this;
	var ready = function (){
		me._onReady (me._createStats (ext.blksize || 512, ext.windowsize || 1),
			0, true);
	};

	if (this._isRRQ){
		//Save the extensions
		this._responseExtensions = ext;
		ready ();
	}else{
		ready ();
		this._sendAndRetransmit (packets.oack.serialize (ext));
	}
};<|endoftext|>


<commit_before>// Concats js/css resources per-bundle
//

'use strict';

// stdlib
var fs      = require('fs');
var path    = require('path');
var format  = require('util').format;

// 3rd-party
var _       = require('lodash');
var fstools = require('fs-tools');

// internal
var stopwatch = require('../utils/stopwatch');

////////////////////////////////////////////////////////////////////////////////

// Returns package styesheet string
//
function readPackageStylesheet(pkgName, sandbox) {
  var filename = path.join(sandbox.tmpdir, "styles", pkgName + ".css");
  return fs.existsSync(filename) ? fs.readFileSync(filename, "utf8") : "";
}

// Returns map of { <locale> : <source>, ... }
//
function readPackageJavascripts(pkgName, sandbox, withLocales) {
  var data = { "*" : "" }
    , tmpdir = sandbox.tmpdir
    , N = sandbox.N;

  //
  // join locale-independent data
  //

  [ "vendor", "views", "client" ].forEach(function (part) {
    var filename = path.join(tmpdir, part, pkgName + '.js');

    if (fs.existsSync(filename)) {
      data["*"] += ';' + fs.readFileSync(filename, "utf8");
    }
  });

  //
  // prepare localized data if required
  //

  if (withLocales) {
    N.config.locales['enabled'].forEach(function (locale) {
      var filename = path.join(tmpdir, 'i18n', pkgName, locale + '.js');

      if (fs.existsSync(filename)) {
        data[locale] = data["*"] + ';' + fs.readFileSync(filename, "utf8");
      }
    });
  }

  return data;
}

// Reads stylesheets and javascripts for each package. Concatenate javascripts
// per-language if needed.
//
// Returns hash with package -> assets:
//
//    {
//      forum: {
//        stylesheet: <String>,
//        javascripts: {
//          "en-US": <String>,
//          ...
//        }
//      },
//
//      lib: {
//        stylesheet: <String>,
//        javascripts: {
//          "*": <String>
//        }
//      },
//
//      ...
//    }
//
function concatFilesPerPackage(sandbox) {
  var assets    = {}
    , i18nTree  = sandbox.i18nTree || {};

  _.each(sandbox.config.packages, function (pkgConfig, pkgName) {
    var withLocales = !_.isEmpty(i18nTree[pkgName] && i18nTree[pkgName].client);

    assets[pkgName] = {
      stylesheet:   readPackageStylesheet(pkgName, sandbox),
      javascripts:  readPackageJavascripts(pkgName, sandbox, withLocales)
    };
  });

  return assets;
}

// Concat builded stylesheets from different packages and writes bundled
// stylesheet if it's non-empty
//
function writeBundleStylesheet(bndlName, sandbox, assets) {
  var stylesheet  = ""
    , filename    = path.join(sandbox.tmpdir, "bundle", "bundle-" + bndlName + ".css");

  _.each(assets, function (data) {
    stylesheet += data.stylesheet;
  });

  if (!stylesheet) {
    return null;
  }

  fs.writeFileSync(filename, stylesheet, "utf8");
  return filename;
}

// Concat all javascripts per-bundle / locale
//
function writeBundleJavascripts(bndlName, sandbox, assets) {
  var withLocales = _.any(assets, function (data) {
        // data always contains at least one key `*`.
        // if it has more than one key, then it contains locales
        return data.javascripts && 1 < _.keys(data.javascripts).length;
      })
    , N = sandbox.N
    , tmpdir = sandbox.tmpdir;

  function writeFile(locale) {
    var
    javascript  = "",
    suffix      = (locale ? ("." + locale) : "") + ".js",
    filename    = path.join(tmpdir, "bundle", "bundle-" + bndlName + suffix);

    _.each(assets, function (data) {
      javascript += data.javascripts[locale] || data.javascripts['*'] || '';
    });

    if (!javascript) {
      return null;
    }

    fs.writeFileSync(filename, javascript, "utf8");
    return filename;
  }

  var data = {};

  if (!withLocales) {
    data["*"] = writeFile();
  } else {
    N.config.locales["enabled"].forEach(function (locale) {
      data[locale] = writeFile(locale);
    });
  }

  return data;
}

// Write bundle files
//
//    bundle/<name>.css
//    bundle/<name>.<locale>.js
//
function writeAssetsPerBundle(sandbox, assets) {
  var data = {};

  _.each(sandbox.config.bundles, function (packages, bndlName) {
    var bndlAssets = _.pick(assets, packages);

    data[bndlName] = {
      stylesheet:   writeBundleStylesheet(bndlName, sandbox, bndlAssets),
      javascripts:  writeBundleJavascripts(bndlName, sandbox, bndlAssets)
    };
  });

  return data;
}

// Walks over all packages in the given distibution and composes queues of
// packages and stylesheets on each.
//
// The resulting package queues are full lists of packages are needed to load in
// order to load the given package. In the exact order. These include also the
// concerned package ifself at the last position.
//
// The resulting stylesheet queues are intended to use in the view layouts for
// linking initial page stylesheets.
//
function composeLoadingQueues(sandbox, distribution) {
  _.each(distribution, function (localeDist) {
    _.each(localeDist, function (pkgDist, pkgName) {
      var packagesQueue = []
        , stylesQueue  = [];

      // This function is used to recursively populate the loading queue.
      function processPackage(processPkgName) {
        var processPkgConfig = sandbox.config.packages[processPkgName];

        // Yield dependences of the current package first.
        if (processPkgConfig.depends) {
          processPkgConfig.depends.forEach(function (dependency) {
            if (-1 === packagesQueue.indexOf(dependency)) {
              processPackage(dependency);
            }
          });
        }

        // Yield the current package itself at the last.
        packagesQueue.push(processPkgName);
      }

      // Compose the loading queue.
      processPackage(pkgName);

      // Compose the styles queue.
      packagesQueue.forEach(function (depName) {
        var depDist = localeDist[depName];

        if (depDist && depDist.stylesheet) {
          if (-1 === stylesQueue.indexOf(depDist.stylesheet)) {
            stylesQueue.push(depDist.stylesheet);
          }
        }
      });

      // Expose the queues to the package distribution.
      pkgDist.packagesQueue = packagesQueue;
      pkgDist.stylesQueue   = stylesQueue;
    });
  });
}

// Returns assets distribution map that will be used by loader
//
//    <locale>:
//      <pkgName>:
//        stylesheets:
//          - <file>
//          - ...
//        javascripts:
//          - <file>
//          - ...
//
function createLoaderPkgsMap(sandbox, bundleAssets) {
  var // map of { <packageName>: <bundleName>, ... }
      pkgBundle = {}
      // map of { <packageName>: { <locale>: { <assets> }, ... } }
    , distribution = {}
    , N = sandbox.N;

  //
  // get bundles list
  //

  _.each(sandbox.config.bundles, function (packages, bndlName) {
    _.each(packages, function (pkgName) {
      pkgBundle[pkgName] = bndlName;
    });
  });

  //
  // collect assets for each package per locale
  //

  N.config.locales["enabled"].forEach(function (locale) {
    distribution[locale] = {};

    _.each(sandbox.config.packages, function (pkgConfig, pkgName) {
      var assets, stylesheet, javascript;

      assets = bundleAssets[pkgBundle[pkgName]];

      if(!assets) {
        throw new Error(format(
              'Package `%s` defined, but not assigned to any bundle, ' +
              'check `bundle.yml` in appropriate application root', pkgName));
      }

      distribution[locale][pkgName] = {
        packagesQueue: null,
        stylesQueue:   null,
        stylesheet:    null,
        javascript:    null
      };

      stylesheet = assets.stylesheet;
      javascript = assets.javascripts[locale] || assets.javascripts["*"];

      if (stylesheet) {
        stylesheet = sandbox.assets.environment.findAsset(stylesheet);

        distribution[locale][pkgName].stylesheet = stylesheet.logicalPath;
        sandbox.assets.files.push(stylesheet.logicalPath);
      }

      if (javascript) {
        javascript = sandbox.assets.environment.findAsset(javascript);

        distribution[locale][pkgName].javascript = javascript.logicalPath;
        sandbox.assets.files.push(javascript.logicalPath);
      }
    });
  });

  return distribution;
}

////////////////////////////////////////////////////////////////////////////////

module.exports = function (sandbox, callback) {
  var timer   = stopwatch()
    , N = sandbox.N
    , bndlDir = path.join(sandbox.tmpdir, 'bundle');

  fstools.mkdir(bndlDir, function (err) {
    var compiledPkgs, compiledBndls;

    if (err) {
      callback(err);
      return;
    }

    try {
      compiledPkgs  = concatFilesPerPackage(sandbox);
      compiledBndls = writeAssetsPerBundle(sandbox, compiledPkgs);

      sandbox.assets.environment.appendPath(bndlDir);

      sandbox.assets.distribution = createLoaderPkgsMap(sandbox, compiledBndls);
      composeLoadingQueues(sandbox, sandbox.assets.distribution);
    } catch (err) {
      callback(err);
      return;
    }

    N.logger.debug('Concatenated dynamic assets %s', timer.elapsed);
    callback();
  });
};
<commit_msg>Refactor `join_packages` bundle processor.<commit_after>// Concats js/css resources per-bundle
//

'use strict';

// stdlib
var fs      = require('fs');
var path    = require('path');
var format  = require('util').format;

// 3rd-party
var _       = require('lodash');
var fstools = require('fs-tools');

// internal
var stopwatch = require('../utils/stopwatch');

////////////////////////////////////////////////////////////////////////////////

// Returns package styesheet string
//
function readPackageStylesheet(pkgName, sandbox) {
  var filename = path.join(sandbox.tmpdir, "styles", pkgName + ".css");
  return fs.existsSync(filename) ? fs.readFileSync(filename, "utf8") : "";
}

// Returns map of { <locale> : <source>, ... }
//
function readPackageJavascripts(pkgName, sandbox, withLocales) {
  var data = { "*" : "" }
    , tmpdir = sandbox.tmpdir
    , N = sandbox.N;

  //
  // join locale-independent data
  //

  [ "vendor", "views", "client" ].forEach(function (part) {
    var filename = path.join(tmpdir, part, pkgName + '.js');

    if (fs.existsSync(filename)) {
      data["*"] += ';' + fs.readFileSync(filename, "utf8");
    }
  });

  //
  // prepare localized data if required
  //

  if (withLocales) {
    N.config.locales['enabled'].forEach(function (locale) {
      var filename = path.join(tmpdir, 'i18n', pkgName, locale + '.js');

      if (fs.existsSync(filename)) {
        data[locale] = data["*"] + ';' + fs.readFileSync(filename, "utf8");
      }
    });
  }

  return data;
}

// Reads stylesheets and javascripts for each package. Concatenate javascripts
// per-language if needed.
//
// Returns hash with package -> assets:
//
//    {
//      forum: {
//        stylesheet: <String>,
//        javascripts: {
//          "en-US": <String>,
//          ...
//        }
//      },
//
//      lib: {
//        stylesheet: <String>,
//        javascripts: {
//          "*": <String>
//        }
//      },
//
//      ...
//    }
//
function concatFilesPerPackage(sandbox) {
  var assets    = {}
    , i18nTree  = sandbox.i18nTree || {};

  _.each(sandbox.config.packages, function (pkgConfig, pkgName) {
    var withLocales = !_.isEmpty(i18nTree[pkgName] && i18nTree[pkgName].client);

    assets[pkgName] = {
      stylesheet:   readPackageStylesheet(pkgName, sandbox),
      javascripts:  readPackageJavascripts(pkgName, sandbox, withLocales)
    };
  });

  return assets;
}

// Concat builded stylesheets from different packages and writes bundled
// stylesheet if it's non-empty
//
function writeBundleStylesheet(bndlName, sandbox, assets) {
  var stylesheet  = ""
    , filename    = path.join(sandbox.tmpdir, "bundle", "bundle-" + bndlName + ".css");

  _.each(assets, function (data) {
    stylesheet += data.stylesheet;
  });

  if (!stylesheet) {
    return null;
  }

  fs.writeFileSync(filename, stylesheet, "utf8");
  return filename;
}

// Concat all javascripts per-bundle / locale
//
function writeBundleJavascripts(bndlName, sandbox, assets) {
  var withLocales = _.any(assets, function (data) {
        // data always contains at least one key `*`.
        // if it has more than one key, then it contains locales
        return data.javascripts && 1 < _.keys(data.javascripts).length;
      })
    , N = sandbox.N
    , tmpdir = sandbox.tmpdir;

  function writeFile(locale) {
    var
    javascript  = "",
    suffix      = (locale ? ("." + locale) : "") + ".js",
    filename    = path.join(tmpdir, "bundle", "bundle-" + bndlName + suffix);

    _.each(assets, function (data) {
      javascript += data.javascripts[locale] || data.javascripts['*'] || '';
    });

    if (!javascript) {
      return null;
    }

    fs.writeFileSync(filename, javascript, "utf8");
    return filename;
  }

  var data = {};

  if (!withLocales) {
    data["*"] = writeFile();
  } else {
    N.config.locales["enabled"].forEach(function (locale) {
      data[locale] = writeFile(locale);
    });
  }

  return data;
}

// Write bundle files
//
//    bundle/<name>.css
//    bundle/<name>.<locale>.js
//
function writeAssetsPerBundle(sandbox, assets) {
  var data = {};

  _.each(sandbox.config.bundles, function (packages, bndlName) {
    var bndlAssets = _.pick(assets, packages);

    data[bndlName] = {
      stylesheet:   writeBundleStylesheet(bndlName, sandbox, bndlAssets),
      javascripts:  writeBundleJavascripts(bndlName, sandbox, bndlAssets)
    };
  });

  return data;
}

// Walks over all packages in the given distibution and composes queues of
// packages and stylesheets on each.
//
// The resulting package queues are full lists of packages are needed to load in
// order to load the given package. In the exact order. These include also the
// concerned package ifself at the last position.
//
// The resulting stylesheet queues are intended to use in the view layouts for
// linking initial page stylesheets.
//
function composeLoadingQueues(sandbox, distribution) {
  _.each(distribution, function (localeDist) {
    _.each(localeDist, function (pkgDist, pkgName) {
      var packagesQueue = []
        , stylesQueue  = [];

      // This function is used to recursively populate the loading queue.
      function processPackage(processPkgName) {
        var processPkgConfig = sandbox.config.packages[processPkgName];

        // Yield dependences of the current package first.
        if (processPkgConfig.depends) {
          processPkgConfig.depends.forEach(function (dependency) {
            if (-1 === packagesQueue.indexOf(dependency)) {
              processPackage(dependency);
            }
          });
        }

        // Yield the current package itself at the last.
        packagesQueue.push(processPkgName);
      }

      // Compose the loading queue.
      processPackage(pkgName);

      // Compose the styles queue.
      packagesQueue.forEach(function (depName) {
        var depDist = localeDist[depName];

        if (depDist && depDist.stylesheet) {
          if (-1 === stylesQueue.indexOf(depDist.stylesheet)) {
            stylesQueue.push(depDist.stylesheet);
          }
        }
      });

      // Expose the queues to the package distribution.
      pkgDist.packagesQueue = packagesQueue;
      pkgDist.stylesQueue   = stylesQueue;
    });
  });
}

// Returns assets distribution map that will be used by loader
//
//    <locale>:
//      <pkgName>:
//        stylesheets:
//          - <file>
//          - ...
//        javascripts:
//          - <file>
//          - ...
//
function createLoaderPkgsMap(sandbox, bundleAssets) {
  var // map of { <packageName>: <bundleName>, ... }
      pkgBundle = {}
      // map of { <packageName>: { <locale>: { <assets> }, ... } }
    , distribution = {}
    , N = sandbox.N;

  //
  // get bundles list
  //

  _.each(sandbox.config.bundles, function (packages, bndlName) {
    _.each(packages, function (pkgName) {
      pkgBundle[pkgName] = bndlName;
    });
  });

  //
  // collect assets for each package per locale
  //

  N.config.locales["enabled"].forEach(function (locale) {
    distribution[locale] = {};

    _.each(sandbox.config.packages, function (pkgConfig, pkgName) {
      var assets, stylesheet, javascript;

      assets = bundleAssets[pkgBundle[pkgName]];

      if(!assets) {
        throw new Error(format(
              'Package `%s` defined, but not assigned to any bundle, ' +
              'check `bundle.yml` in appropriate application root', pkgName));
      }

      distribution[locale][pkgName] = {
        packagesQueue: null,
        stylesQueue:   null,
        stylesheet:    null,
        javascript:    null
      };

      stylesheet = assets.stylesheet;
      javascript = assets.javascripts[locale] || assets.javascripts["*"];

      if (stylesheet) {
        stylesheet = sandbox.assets.environment.findAsset(stylesheet);

        distribution[locale][pkgName].stylesheet = stylesheet.logicalPath;
        sandbox.assets.files.push(stylesheet.logicalPath);
      }

      if (javascript) {
        javascript = sandbox.assets.environment.findAsset(javascript);

        distribution[locale][pkgName].javascript = javascript.logicalPath;
        sandbox.assets.files.push(javascript.logicalPath);
      }
    });
  });

  return distribution;
}

////////////////////////////////////////////////////////////////////////////////

module.exports = function (sandbox) {
  var timer   = stopwatch()
    , N       = sandbox.N
    , bndlDir = path.join(sandbox.tmpdir, 'bundle')
    , compiledPkgs
    , compiledBndls;

  fstools.mkdirSync(bndlDir);

  compiledPkgs  = concatFilesPerPackage(sandbox);
  compiledBndls = writeAssetsPerBundle(sandbox, compiledPkgs);

  sandbox.assets.environment.appendPath(bndlDir);

  sandbox.assets.distribution = createLoaderPkgsMap(sandbox, compiledBndls);
  composeLoadingQueues(sandbox, sandbox.assets.distribution);

  N.logger.debug('Concatenated dynamic assets %s', timer.elapsed);
};
<|endoftext|>


<commit_before>module.exports = ( input ) =>
{
	input = input
		.split( '\n' )
		.map( x => x.match( /^(?<color>[a-z]+ [a-z]+) bags contain (?<contains>.+)\./ ) )
		.map( x =>
		{
			const colors = new Map();

			if( x.groups.contains !== 'no other bags' )
			{
				const contains = x.groups.contains.matchAll( /(?<count>[0-9]+) (?<color>[a-z]+ [a-z]+) bag/g );

				for( const color of contains )
				{
					colors.set( color.groups.color, parseInt( color.groups.count, 10 ) );
				}
			}

			return [ x.groups.color, colors ];
		} );

	input = new Map( input );

	function containsShinyGoldBag( [ color, contains ] )
	{
		if( typeof contains === 'number' )
		{
			return containsShinyGoldBag( [ color, input.get( color ) ] );
		}

		if( contains.has( 'shiny gold' ) )
		{
			return true;
		}

		return [ ...contains ].some( containsShinyGoldBag );
	}

	function countBags( [ color, contains ] )
	{
		if( typeof contains === 'number' )
		{
			const bags = countBags( [ color, input.get( color ) ] );
			return bags > 0 ? contains + bags * contains : contains;
		}

		return [ ...contains ].reduce( ( a, b ) => a + countBags( b ), 0 );
	}

	const part1 = [ ...input ].reduce( ( a, b ) => a + containsShinyGoldBag( b ), 0 );
	const part2 = countBags( [ null, input.get( 'shiny gold' ) ] );

	return [ part1, part2 ];
};
<commit_msg>Pointless ternary<commit_after>module.exports = ( input ) =>
{
	input = input
		.split( '\n' )
		.map( x => x.match( /^(?<color>[a-z]+ [a-z]+) bags contain (?<contains>.+)\./ ) )
		.map( x =>
		{
			const colors = new Map();

			if( x.groups.contains !== 'no other bags' )
			{
				const contains = x.groups.contains.matchAll( /(?<count>[0-9]+) (?<color>[a-z]+ [a-z]+) bag/g );

				for( const color of contains )
				{
					colors.set( color.groups.color, parseInt( color.groups.count, 10 ) );
				}
			}

			return [ x.groups.color, colors ];
		} );

	input = new Map( input );

	function containsShinyGoldBag( [ color, contains ] )
	{
		if( typeof contains === 'number' )
		{
			return containsShinyGoldBag( [ color, input.get( color ) ] );
		}

		if( contains.has( 'shiny gold' ) )
		{
			return true;
		}

		return [ ...contains ].some( containsShinyGoldBag );
	}

	function countBags( [ color, contains ] )
	{
		if( typeof contains === 'number' )
		{
			return contains + countBags( [ color, input.get( color ) ] ) * contains;
		}

		return [ ...contains ].reduce( ( a, b ) => a + countBags( b ), 0 );
	}

	const part1 = [ ...input ].reduce( ( a, b ) => a + containsShinyGoldBag( b ), 0 );
	const part2 = countBags( [ null, input.get( 'shiny gold' ) ] );

	return [ part1, part2 ];
};
<|endoftext|>


<commit_before>/**
 * Will pass the state of a store as properties to the nested components.
 * If there is more than one store, the stores themselves will be passed
 */
import React from 'react'

export default class AltContainer extends React.Component {

  constructor(props) {
    super(props)
    const { store, stores } = props;

    if (!stores && !store) {
      throw new Error('Must define either store or stores')
    }
    if (stores && store) {
      throw new Error('Cannot define both store and stores')
    }
    this.onStoreChange = this.onStoreChange.bind(this);
    this.state = this._reduceState();
    this.storeListeners = []
  }

  componentWillReceiveProps(nextProps) {
    this._destroySubscriptions()
    this.setState(this._reduceState(nextProps))
    this._registerStores(nextProps)
  }

  componentDidMount() {
    this._registerStores(this.props)
  }

  componentWillUnmount() {
    this._destroySubscriptions()
  }

  _reduceState() {
    const { store, stores, mergeFunc } = this.props;
    return store? store.getState() : mergeFunc(stores);
  }

  _registerStores({ store, stores }) {
    stores = stores || [ store ];
    stores.forEach(this._addSubscription.bind(this));
  }

  _destroySubscriptions() {
    this.storeListeners.forEach(storeListener => storeListener.dispose())
    this.storeListeners = []
  }

  _addSubscription(store) {
    this.storeListeners.push(store.subscribe(this.onStoreChange))
  }

  onStoreChange() {
    this.setState(this._reduceState(this.props))
  }

  render() {
    const { element, children, store, stores, mergeFunc, ...nodeProps } = this.props;
    return React.createElement(element, nodeProps, React.Children.map(children, child => {
      const childrenProps = Object.assign({}, this.state, child.props);
      return React.cloneElement(child, childrenProps);
    }))
  }

}

const PropTypes = React.PropTypes;
const storeShape = PropTypes.shape({
  getState: React.PropTypes.func.isRequired
});
AltContainer.propTypes = {

  // specify either store (a single store) or stores (array of stores)
  store: storeShape,
  stores: PropTypes.arrayOf(storeShape),

  // when specifying multiple stores, this is the function that will be called to merge the states of all stores.
  // By default, Object.assign() will be invoked with all the states.
  mergeFunc: PropTypes.func,

  // when rendering multiple children, a parent element will be created. Default is "div"
  element: PropTypes.string
}

AltContainer.defaultProps = {
  element: 'div',
  mergeFunc: function(stores) {
    return stores.reduce(function(state, store) {
      return Object.assign(state, store.getState());
    }, {});
  }
}
<commit_msg>Move validations to dev stage only<commit_after>/**
 * Will pass the state of a store as properties to the nested components.
 * If there is more than one store, the stores themselves will be passed
 */
import React from 'react'

export default class AltContainer extends React.Component {

  constructor(props) {
    super(props)
    const { store, stores } = props;

    if (process.env.NODE_ENV !== 'production') {
      if (!stores && !store) {
        throw new Error('Must define either store or stores')
      }
      if (stores && store) {
        throw new Error('Cannot define both store and stores')
      }
    }
    this.onStoreChange = this.onStoreChange.bind(this);
    this.state = this._reduceState();
    this.storeListeners = []
  }

  componentWillReceiveProps(nextProps) {
    this._destroySubscriptions()
    this.setState(this._reduceState(nextProps))
    this._registerStores(nextProps)
  }

  componentDidMount() {
    this._registerStores(this.props)
  }

  componentWillUnmount() {
    this._destroySubscriptions()
  }

  _reduceState() {
    const { store, stores, mergeFunc } = this.props;
    return store? store.getState() : mergeFunc(stores);
  }

  _registerStores({ store, stores }) {
    stores = stores || [ store ];
    stores.forEach(this._addSubscription.bind(this));
  }

  _destroySubscriptions() {
    this.storeListeners.forEach(storeListener => storeListener.dispose())
    this.storeListeners = []
  }

  _addSubscription(store) {
    this.storeListeners.push(store.subscribe(this.onStoreChange))
  }

  onStoreChange() {
    this.setState(this._reduceState(this.props))
  }

  render() {
    const { element, children, store, stores, mergeFunc, ...nodeProps } = this.props;
    return React.createElement(element, nodeProps, React.Children.map(children, child => {
      const childrenProps = Object.assign({}, this.state, child.props);
      return React.cloneElement(child, childrenProps);
    }))
  }

}

const PropTypes = React.PropTypes;
const storeShape = PropTypes.shape({
  getState: React.PropTypes.func.isRequired
});
AltContainer.propTypes = {

  // specify either store (a single store) or stores (array of stores)
  store: storeShape,
  stores: PropTypes.arrayOf(storeShape),

  // when specifying multiple stores, this is the function that will be called to merge the states of all stores.
  // By default, Object.assign() will be invoked with all the states.
  mergeFunc: PropTypes.func,

  // when rendering multiple children, a parent element will be created. Default is "div"
  element: PropTypes.string
}

AltContainer.defaultProps = {
  element: 'div',
  mergeFunc: function(stores) {
    return stores.reduce(function(state, store) {
      return Object.assign(state, store.getState());
    }, {});
  }
}
<|endoftext|>


<commit_before>import {Form} from 'widget/form/form'
import {Layout} from 'widget/layout'
import {PreviewMap} from './previewMap'
import {Subject, map, takeUntil} from 'rxjs'
import {compose} from 'compose'
import {connect, select} from 'store'
import {countryEETable, countryToEETable} from 'app/home/map/aoiLayer'
import {msg} from 'translate'
import {selectFrom} from 'stateUtils'
import {withRecipe} from '../../../../recipeContext'
import Notifications from 'widget/notifications'
import PropTypes from 'prop-types'
import React from 'react'
import _ from 'lodash'
import actionBuilder from 'action-builder'
import api from 'api'

const mapRecipeToProps = recipe => ({
    overlay: selectFrom(recipe, 'layers.overlay'),
    featureLayerSources: selectFrom(recipe, 'ui.featureLayerSources'),
})

const loadCountries$ = () => {
    return api.gee.queryEETable$({
        select: ['id', 'label'],
        from: countryEETable,
        where: [['parent_id', 'equals', '']],
        orderBy: ['label']
    }).pipe(
        map(countries => countries.map(({id, label}) => ({value: id, label}))),
        map(countries =>
            actionBuilder('SET_COUNTRIES', {countries})
                .set('countries', countries)
                .dispatch()
        )
    )
}

const loadCountryForArea$ = areaId => {
    return api.gee.queryEETable$({
        select: ['parent_id'],
        from: countryEETable,
        where: [['id', 'equals', areaId]],
        orderBy: ['label']
    })
}

const loadCountryAreas$ = countryId => {
    return api.gee.queryEETable$({
        select: ['id', 'label'],
        from: countryEETable,
        where: [['parent_id', 'equals', countryId]],
        orderBy: ['label']
    }).pipe(
        map(countries => countries.map(({id, label}) => ({value: id, label}))),
        map(areas =>
            actionBuilder('SET_COUNTRY_AREA', {countryId, areas})
                .set(['areasByCountry', countryId], areas)
                .dispatch()
        )
    )
}

const mapStateToProps = (state, ownProps) => {
    return {
        countries: select('countries'),
        countryAreas: select(['areasByCountry', ownProps.inputs.country.value]),
    }
}

class _CountrySection extends React.Component {
    constructor(props) {
        super(props)
        this.aoiChanged$ = new Subject()
    }

    loadCountryAreas(countryId) {
        if (!select(['areasByCountry', countryId]))
            this.props.stream('LOAD_COUNTRY_AREAS',
                loadCountryAreas$(countryId).pipe(
                    takeUntil(this.aoiChanged$)
                ))
    }

    render() {
        const {stream, countries, countryAreas, inputs: {country, area, buffer}} = this.props
        const loadCountries = stream('LOAD_COUNTRIES')
        const loadCountryAreas = stream('LOAD_COUNTRY_AREAS')
        const countriesState = loadCountries.active
            ? 'loading'
            : 'loaded'
        const areasState = loadCountryAreas.active
            ? 'loading'
            : country.value
                ? countryAreas && countryAreas.length > 0 ? 'loaded' : 'noAreas'
                : 'noCountry'
        const countryPlaceholder = msg(`process.mosaic.panel.areaOfInterest.form.country.country.placeholder.${countriesState}`)
        const areaPlaceholder = msg(`process.mosaic.panel.areaOfInterest.form.country.area.placeholder.${areasState}`)
        return (
            <Layout>
                <Form.Combo
                    label={msg('process.mosaic.panel.areaOfInterest.form.country.country.label')}
                    input={country}
                    placement='below'
                    options={countries || []}
                    placeholder={countryPlaceholder}
                    busyMessage={loadCountries.active && msg('widget.loading')}
                    disabled={loadCountries.failed}
                    autoFocus
                    onChange={option => {
                        area.set('')
                        this.aoiChanged$.next()
                        this.loadCountryAreas(option.value)
                    }}
                />
                <Form.Combo
                    label={msg('process.mosaic.panel.areaOfInterest.form.country.area.label')}
                    input={area}
                    placement='below'
                    options={(countryAreas || [])}
                    placeholder={areaPlaceholder}
                    busyMessage={loadCountryAreas.active && msg('widget.loading')}
                    disabled={loadCountryAreas.failed || !countryAreas || countryAreas.length === 0}
                    autoFocus
                    onChange={() => this.aoiChanged$.next()}
                    allowClear
                />
                <Form.Slider
                    label={msg('process.mosaic.panel.areaOfInterest.form.buffer.label')}
                    tooltip={msg('process.mosaic.panel.areaOfInterest.form.buffer.tooltip')}
                    info={buffer => msg('process.mosaic.panel.areaOfInterest.form.buffer.info', {buffer})}
                    input={buffer}
                    minValue={0}
                    maxValue={100}
                    scale={'log'}
                    ticks={[0, 1, 2, 5, 10, 20, 50, 100]}
                    snap
                    range='none'
                />
                <PreviewMap/>
            </Layout>
        )
    }

    componentDidMount() {
        const {stream, inputs: {country, area}} = this.props
        if (area.value && !country.value)
            stream('LOAD_COUNTRY_FOR_AREA',
                loadCountryForArea$(area.value).pipe(
                    map(countryId => {
                        country.setInitialValue(countryId)
                        this.loadCountryAreas(countryId)
                    })
                ))
        if (country.value) {
            this.loadCountryAreas(country.value)
        }
        this.update()
    }

    componentDidUpdate(prevProps) {
        if (!prevProps || prevProps.inputs !== this.props.inputs) {
            this.update(prevProps)
        }
    }

    update() {
        const {countries, stream, inputs: {buffer}} = this.props
        if (!countries && !stream('LOAD_COUNTRIES').active && !stream('LOAD_COUNTRIES').failed) {
            this.props.stream('LOAD_COUNTRIES',
                loadCountries$(),
                null,
                () => Notifications.error({
                    message: msg('process.mosaic.panel.areaOfInterest.form.country.country.loadFailed'),
                    timeout: 10
                })
            )
        }
        this.setOverlay()
        if (!_.isFinite(buffer.value)) {
            buffer.set(0)
        }
    }

    setOverlay() {
        const {stream, inputs: {country, area, buffer}} = this.props
        if (!country.value && !area.value) {
            return
        }
        const aoi = {
            type: 'COUNTRY',
            countryCode: country.value,
            areaCode: area.value,
            buffer: buffer.value
        }
        const {overlay: prevOverlay, featureLayerSources, recipeActionBuilder} = this.props
        const aoiLayerSource = featureLayerSources.find(({type}) => type === 'Aoi')
        const overlay = {
            featureLayers: [
                {
                    sourceId: aoiLayerSource.id,
                    layerConfig: {aoi}
                }
            ]
        }
        if (!_.isEqual(overlay, prevOverlay) && !stream('LOAD_BOUNDS').active) {
            recipeActionBuilder('DELETE_MAP_OVERLAY_BOUNDS')
                .del('ui.overlay.bounds')
                .dispatch()
            stream('LOAD_MAP_OVERLAY_BOUNDS',
                api.gee.aoiBounds$(countryToEETable(aoi)),
                bounds => {
                    recipeActionBuilder('SET_MAP_OVERLAY_BOUNDS')
                        .set('ui.overlay.bounds', bounds)
                        .dispatch()
                }
            )
            recipeActionBuilder('SET_MAP_OVERLAY')
                .set('layers.overlay', overlay)
                .dispatch()
        }
    }

    componentWillUnmount() {
        const {recipeActionBuilder} = this.props
        recipeActionBuilder('REMOVE_MAP_OVERLAY')
            .del('layers.overlay')
            .dispatch()
    }
}

export const CountrySection = compose(
    _CountrySection,
    connect(mapStateToProps),
    withRecipe(mapRecipeToProps)
)

CountrySection.propTypes = {
    inputs: PropTypes.object.isRequired,
    recipeId: PropTypes.string.isRequired,
    layerIndex: PropTypes.number
}
<commit_msg>Fixed initialization of country when area is selected.<commit_after>import {Form} from 'widget/form/form'
import {Layout} from 'widget/layout'
import {PreviewMap} from './previewMap'
import {Subject, map, takeUntil} from 'rxjs'
import {compose} from 'compose'
import {connect, select} from 'store'
import {countryEETable, countryToEETable} from 'app/home/map/aoiLayer'
import {msg} from 'translate'
import {selectFrom} from 'stateUtils'
import {withRecipe} from '../../../../recipeContext'
import Notifications from 'widget/notifications'
import PropTypes from 'prop-types'
import React from 'react'
import _ from 'lodash'
import actionBuilder from 'action-builder'
import api from 'api'

const mapRecipeToProps = recipe => ({
    overlay: selectFrom(recipe, 'layers.overlay'),
    featureLayerSources: selectFrom(recipe, 'ui.featureLayerSources'),
})

const loadCountries$ = () => {
    return api.gee.queryEETable$({
        select: ['id', 'label'],
        from: countryEETable,
        where: [['parent_id', 'equals', '']],
        orderBy: ['label']
    }).pipe(
        map(countries => countries.map(({id, label}) => ({value: id, label}))),
        map(countries =>
            actionBuilder('SET_COUNTRIES', {countries})
                .set('countries', countries)
                .dispatch()
        )
    )
}

const loadCountryForArea$ = areaId => {
    return api.gee.queryEETable$({
        select: ['parent_id'],
        from: countryEETable,
        where: [['id', 'equals', areaId]],
        orderBy: ['label']
    }).pipe(
        map(rows => rows.length ? rows[0]['parent_id'] : null)
    )
}

const loadCountryAreas$ = countryId => {
    return api.gee.queryEETable$({
        select: ['id', 'label'],
        from: countryEETable,
        where: [['parent_id', 'equals', countryId]],
        orderBy: ['label']
    }).pipe(
        map(countries => countries.map(({id, label}) => ({value: id, label}))),
        map(areas =>
            actionBuilder('SET_COUNTRY_AREA', {countryId, areas})
                .set(['areasByCountry', countryId], areas)
                .dispatch()
        )
    )
}

const mapStateToProps = (state, ownProps) => {
    return {
        countries: select('countries'),
        countryAreas: select(['areasByCountry', ownProps.inputs.country.value]),
    }
}

class _CountrySection extends React.Component {
    constructor(props) {
        super(props)
        this.aoiChanged$ = new Subject()
    }

    loadCountryAreas(countryId) {
        if (!select(['areasByCountry', countryId]))
            this.props.stream('LOAD_COUNTRY_AREAS',
                loadCountryAreas$(countryId).pipe(
                    takeUntil(this.aoiChanged$)
                ))
    }

    render() {
        const {stream, countries, countryAreas, inputs: {country, area, buffer}} = this.props
        const loadCountries = stream('LOAD_COUNTRIES')
        const loadCountryAreas = stream('LOAD_COUNTRY_AREAS')
        const countriesState = loadCountries.active
            ? 'loading'
            : 'loaded'
        const areasState = loadCountryAreas.active
            ? 'loading'
            : country.value
                ? countryAreas && countryAreas.length > 0 ? 'loaded' : 'noAreas'
                : 'noCountry'
        const countryPlaceholder = msg(`process.mosaic.panel.areaOfInterest.form.country.country.placeholder.${countriesState}`)
        const areaPlaceholder = msg(`process.mosaic.panel.areaOfInterest.form.country.area.placeholder.${areasState}`)
        return (
            <Layout>
                <Form.Combo
                    label={msg('process.mosaic.panel.areaOfInterest.form.country.country.label')}
                    input={country}
                    placement='below'
                    options={countries || []}
                    placeholder={countryPlaceholder}
                    busyMessage={loadCountries.active && msg('widget.loading')}
                    disabled={loadCountries.failed}
                    autoFocus
                    onChange={option => {
                        area.set('')
                        this.aoiChanged$.next()
                        this.loadCountryAreas(option.value)
                    }}
                />
                <Form.Combo
                    label={msg('process.mosaic.panel.areaOfInterest.form.country.area.label')}
                    input={area}
                    placement='below'
                    options={(countryAreas || [])}
                    placeholder={areaPlaceholder}
                    busyMessage={loadCountryAreas.active && msg('widget.loading')}
                    disabled={loadCountryAreas.failed || !countryAreas || countryAreas.length === 0}
                    autoFocus
                    onChange={() => this.aoiChanged$.next()}
                    allowClear
                />
                <Form.Slider
                    label={msg('process.mosaic.panel.areaOfInterest.form.buffer.label')}
                    tooltip={msg('process.mosaic.panel.areaOfInterest.form.buffer.tooltip')}
                    info={buffer => msg('process.mosaic.panel.areaOfInterest.form.buffer.info', {buffer})}
                    input={buffer}
                    minValue={0}
                    maxValue={100}
                    scale={'log'}
                    ticks={[0, 1, 2, 5, 10, 20, 50, 100]}
                    snap
                    range='none'
                />
                <PreviewMap/>
            </Layout>
        )
    }

    componentDidMount() {
        const {stream, inputs: {country, area}} = this.props
        if (area.value && !country.value) {
            stream('LOAD_COUNTRY_FOR_AREA',
                loadCountryForArea$(area.value).pipe(
                    map(countryId => {
                        country.setInitialValue(countryId)
                        this.loadCountryAreas(countryId)
                    })
                ))
        }
        if (country.value) {
            this.loadCountryAreas(country.value)
        }
        this.update()
    }

    componentDidUpdate(prevProps) {
        if (!prevProps || prevProps.inputs !== this.props.inputs) {
            this.update(prevProps)
        }
    }

    update() {
        const {countries, stream, inputs: {buffer}} = this.props
        if (!countries && !stream('LOAD_COUNTRIES').active && !stream('LOAD_COUNTRIES').failed) {
            this.props.stream('LOAD_COUNTRIES',
                loadCountries$(),
                null,
                () => Notifications.error({
                    message: msg('process.mosaic.panel.areaOfInterest.form.country.country.loadFailed'),
                    timeout: 10
                })
            )
        }
        this.setOverlay()
        if (!_.isFinite(buffer.value)) {
            buffer.set(0)
        }
    }

    setOverlay() {
        const {stream, inputs: {country, area, buffer}} = this.props
        if (!country.value && !area.value) {
            return
        }
        const aoi = {
            type: 'COUNTRY',
            countryCode: country.value,
            areaCode: area.value,
            buffer: buffer.value
        }
        const {overlay: prevOverlay, featureLayerSources, recipeActionBuilder} = this.props
        const aoiLayerSource = featureLayerSources.find(({type}) => type === 'Aoi')
        const overlay = {
            featureLayers: [
                {
                    sourceId: aoiLayerSource.id,
                    layerConfig: {aoi}
                }
            ]
        }
        if (!_.isEqual(overlay, prevOverlay) && !stream('LOAD_BOUNDS').active) {
            recipeActionBuilder('DELETE_MAP_OVERLAY_BOUNDS')
                .del('ui.overlay.bounds')
                .dispatch()
            stream('LOAD_MAP_OVERLAY_BOUNDS',
                api.gee.aoiBounds$(countryToEETable(aoi)),
                bounds => {
                    recipeActionBuilder('SET_MAP_OVERLAY_BOUNDS')
                        .set('ui.overlay.bounds', bounds)
                        .dispatch()
                }
            )
            recipeActionBuilder('SET_MAP_OVERLAY')
                .set('layers.overlay', overlay)
                .dispatch()
        }
    }

    componentWillUnmount() {
        const {recipeActionBuilder} = this.props
        recipeActionBuilder('REMOVE_MAP_OVERLAY')
            .del('layers.overlay')
            .dispatch()
    }
}

export const CountrySection = compose(
    _CountrySection,
    connect(mapStateToProps),
    withRecipe(mapRecipeToProps)
)

CountrySection.propTypes = {
    inputs: PropTypes.object.isRequired,
    recipeId: PropTypes.string.isRequired,
    layerIndex: PropTypes.number
}
<|endoftext|>


<commit_before>'use strict';

function Account(account_name, account_type, floating_wall) {
    this.__init__(account_name, account_type, floating_wall);
}

Account.prototype = {
    __init__: function(account_name, account_type, floating_wall) {
        this.account_name = account_name;
        this.account_type = account_type;
        this.floating_wall = floating_wall;

        this._button = null;
    },

    set_button: function(b) {
        this._button = b;
    },

    is_listed_on_floating_wall: function() {
        if (!is_defined(this.floating_wall)) {
            return false;
        }
        for (var f = 0; f < this.floating_wall._2D_rows.length; f++) {
            // 3 is the index of the floating_2D_text
            if (this.floating_wall._2D_rows[f][3].get_text() === this.account_name) {
                return true;
            }
        }
        return false;
    }
};

function AdminWorld() {
    this.__init__();
}

AdminWorld.prototype = {

    __init__: function () {
        World.call(this);
    },

    create: function() {
        // POST calls.
        this.post_get_all_accounts_information = new PostHelper('sudo_command');

        var p = new THREE.Vector3(1000, 1000, 1500);
        var n = new THREE.Vector3(-.56, 0, -.8);
        this.wall_all_accounts = new FloatingWall(1000, 1000, p, n, this, false);
        this.wall_all_accounts.add_row_3D_text(false, -1, 'All Accounts', TYPE_TITLE, COLOR_BLUE);

        this.button_load_all_accounts = this.wall_all_accounts.add_row_2D_text([0, 1], 0, 'Refresh/Load All Accounts', TYPE_BUTTON, null, COLOR_YELLOW);
        this.button_load_all_accounts.set_engage_function(this._load_all_accounts_action.bind(this));

        this.wall_all_accounts.add_row_2D_text([0, ONE_THIRD], 2, 'Name', TYPE_CONSTANT);
        this.wall_all_accounts.add_row_2D_text([ONE_THIRD, TWO_THIRDS], 2, 'Type', TYPE_CONSTANT);
        this.wall_all_accounts.add_row_2D_text([TWO_THIRDS, 1], 2, 'Actions', TYPE_CONSTANT);

        //this.wall_all_accounts.add_row_2D_text(row, text, type, sc, c)

        this.wall_all_accounts.refresh_position_and_look_at();

        this.accounts = [];
        this.number_of_accounts_listed = 0;

        // Account actions list.
        this.wall_account_actions = new FloatingWall(200, 300, null, null, this, false);
        this.wall_account_actions.add_row_3D_text(false, -1, 'Account Actions', TYPE_TITLE);
        // TODO : Add the actions here

        this.wall_account_actions.set_to_invisible();
    },

    _load_all_accounts_action: function() {
        var data = {};
        data[ENTITY_PROPERTY_USERNAME]      = ENTITY_OWNER.get_username();
        data[ENTITY_PROPERTY_PASSWORD]      = ENTITY_OWNER.get_password();
        data[SERVER_COMMAND_SUDO_OPERATION] = SERVER_COMMAND_GET_ALL_ACCOUNTS_INFORMATION;
        data[POST_KEY_GENERIC_DATA]         = 'not_needed';
        this.post_get_all_accounts_information.perform_post(data, this._all_accounts_information_result.bind(this));
    },

    _all_accounts_information_result: function(result) {
        l('ALL ACCOUNTS INFORMATION RESULT');
        l(result);

        var all_accounts = result.split('-');
        for (var a = 0; a < all_accounts.length; a++) {
            var account_data = all_accounts[a].split('+');
            if (is_defined(account_data[0] && is_defined(account_data[1]))) {
                if (!this._have_account(account_data[0])) {
                    this.accounts.push(new Account(account_data[0], account_data[1]));
                }
            }
        }

        this._refresh_account_list();
    },

    _refresh_account_list: function() {
        var account_added_or_removed = false;

        for (var a = 0; a < this.accounts.length; a++) {
            if (!this.accounts[a].is_listed_on_floating_wall()) {
                this.wall_all_accounts.add_row_2D_text([0, ONE_THIRD], 2 + this.number_of_accounts_listed, this.accounts[a].account_name, TYPE_CONSTANT);
                this.wall_all_accounts.add_row_2D_text([ONE_THIRD, TWO_THIRDS], 2 + this.number_of_accounts_listed, this.accounts[a].account_type, TYPE_CONSTANT);
                var perform_account_actions_button =  this.wall_all_accounts.add_row_2D_text([TWO_THIRDS, 1], 2 + this.number_of_accounts_listed, 'actions', TYPE_BUTTON);
                perform_account_actions_button.set_engage_function(this._perform_account_actions_button_pressed.bind(this, this.accounts[a]));

                this.accounts[a].set_button(perform_account_actions_button);

                this.number_of_accounts_listed += 1;

                account_added_or_removed = true;
            }
        }

        if (account_added_or_removed) {
            this.wall_all_accounts.refresh_position_and_look_at();
        }
    },

    _have_account: function(account_name) {
        for (var a = 0; a < this.accounts.length; a++) {
            if (this.accounts[a].account_name === account_name) {
                return true;
            }
        }
        return false;
    },

    _perform_account_actions_button_pressed: function(account) {
        l('TODO : Perform account actions!');
    },

    enter_world: function() {
        CURRENT_PLAYER.disengage();
        if (!GUI_PAUSED_MENU.currently_displayed) {
            CURRENT_PLAYER.enable_controls();
        }
        CURRENT_PLAYER.set_position_xyz(0, 100, 0);
    },

    exit_world: function() {
    }
};

// utarsuno+sudo-aaaa+not_verified-bbbb+not_verified-Ryan+not_verified-cccc+not_verified-zzzz+not_verified-
<commit_msg>general updating 117<commit_after>'use strict';

function Account(account_name, account_type, floating_wall) {
    this.__init__(account_name, account_type, floating_wall);
}

Account.prototype = {
    __init__: function(account_name, account_type, floating_wall) {
        this.account_name = account_name;
        this.account_type = account_type;
        this.floating_wall = floating_wall;

        this._button = null;
    },

    set_button: function(b) {
        this._button = b;
    },

    is_listed_on_floating_wall: function() {
        if (!is_defined(this.floating_wall)) {
            return false;
        }
        for (var f = 0; f < this.floating_wall._2D_rows.length; f++) {
            // 3 is the index of the floating_2D_text
            if (this.floating_wall._2D_rows[f][3].get_text() === this.account_name) {
                return true;
            }
        }
        return false;
    }
};

function AdminWorld() {
    this.__init__();
}

AdminWorld.prototype = {

    __init__: function () {
        World.call(this);
    },

    create: function() {
        // POST calls.
        this.post_get_all_accounts_information = new PostHelper('sudo_command');

        var p = new THREE.Vector3(1000, 1000, 1500);
        var n = new THREE.Vector3(-.56, 0, -.8);
        this.wall_all_accounts = new FloatingWall(600, 1000, p, n, this, false);
        this.wall_all_accounts.add_row_3D_text(false, -1, 'All Accounts', TYPE_TITLE, COLOR_BLUE);

        this.button_load_all_accounts = this.wall_all_accounts.add_row_2D_text([0, 1], 0, 'Refresh/Load All Accounts', TYPE_BUTTON, null, COLOR_YELLOW);
        this.button_load_all_accounts.set_engage_function(this._load_all_accounts_action.bind(this));

        this.wall_all_accounts.add_row_2D_text([0, ONE_THIRD], 2, 'Name', TYPE_CONSTANT);
        this.wall_all_accounts.add_row_2D_text([ONE_THIRD, TWO_THIRDS], 2, 'Type', TYPE_CONSTANT);
        this.wall_all_accounts.add_row_2D_text([TWO_THIRDS, 1], 2, 'Actions', TYPE_CONSTANT);

        //this.wall_all_accounts.add_row_2D_text(row, text, type, sc, c)

        this.wall_all_accounts.refresh_position_and_look_at();

        this.accounts = [];
        this.number_of_accounts_listed = 0;

        // Account actions list.
        this.wall_account_actions = new FloatingWall(200, 300, null, null, this, false);
        this.wall_account_actions.add_row_3D_text(false, -1, 'Account Actions', TYPE_TITLE);
        // TODO : Add the actions here

        this.wall_account_actions.set_to_invisible();
    },

    _load_all_accounts_action: function() {
        var data = {};
        data[ENTITY_PROPERTY_USERNAME]      = ENTITY_OWNER.get_username();
        data[ENTITY_PROPERTY_PASSWORD]      = ENTITY_OWNER.get_password();
        data[SERVER_COMMAND_SUDO_OPERATION] = SERVER_COMMAND_GET_ALL_ACCOUNTS_INFORMATION;
        data[POST_KEY_GENERIC_DATA]         = 'not_needed';
        this.post_get_all_accounts_information.perform_post(data, this._all_accounts_information_result.bind(this));
    },

    _all_accounts_information_result: function(result) {
        var all_accounts = result.split('-');
        for (var a = 0; a < all_accounts.length; a++) {
            var account_data = all_accounts[a].split('+');
            if (is_defined(account_data[0] && is_defined(account_data[1]))) {
                if (account_data[0] !== '') {
                    if (!this._have_account(account_data[0])) {
                        this.accounts.push(new Account(account_data[0], account_data[1]));
                    }
                }
            }
        }

        this._refresh_account_list();
    },

    _refresh_account_list: function() {
        var account_added_or_removed = false;

        for (var a = 0; a < this.accounts.length; a++) {
            if (!this.accounts[a].is_listed_on_floating_wall()) {
                this.wall_all_accounts.add_row_2D_text([0, ONE_THIRD], 3 + this.number_of_accounts_listed, this.accounts[a].account_name, TYPE_CONSTANT);
                this.wall_all_accounts.add_row_2D_text([ONE_THIRD, TWO_THIRDS], 3 + this.number_of_accounts_listed, this.accounts[a].account_type, TYPE_CONSTANT);
                var perform_account_actions_button =  this.wall_all_accounts.add_row_2D_text([TWO_THIRDS, 1], 3 + this.number_of_accounts_listed, 'actions', TYPE_BUTTON);
                perform_account_actions_button.set_engage_function(this._perform_account_actions_button_pressed.bind(this, this.accounts[a]));

                this.accounts[a].set_button(perform_account_actions_button);

                this.number_of_accounts_listed += 1;

                account_added_or_removed = true;
            }
        }

        if (account_added_or_removed) {
            this.wall_all_accounts.refresh_position_and_look_at();
        }
    },

    _have_account: function(account_name) {
        for (var a = 0; a < this.accounts.length; a++) {
            if (this.accounts[a].account_name === account_name) {
                return true;
            }
        }
        return false;
    },

    _perform_account_actions_button_pressed: function(account) {
        l('TODO : Perform account actions!');
    },

    enter_world: function() {
        CURRENT_PLAYER.disengage();
        if (!GUI_PAUSED_MENU.currently_displayed) {
            CURRENT_PLAYER.enable_controls();
        }
        CURRENT_PLAYER.set_position_xyz(0, 100, 0);
    },

    exit_world: function() {
    }
};

// utarsuno+sudo-aaaa+not_verified-bbbb+not_verified-Ryan+not_verified-cccc+not_verified-zzzz+not_verified-
<|endoftext|>