<commit_before>// Aggregates X number of Seadragon Viewers a set of switchable buffers
// Syncing of zooming and position is always set to the currently
// viewed buffer

if (OCRJS === undefined) {
    var OCRJS = {};
}

OCRJS.ImageViewer = OCRJS.OcrBaseWidget.extend({
    constructor: function(parent, options) {
        this.base();
        this.parent = parent;
        this.options = {
            id: new Date().getTime(),
            numBuffers: 2,
            width: -1,
            height: 500,
            log: false,
            dashboard: true,
        };
        $.extend(this.options, options);

        if (this.options.width == -1) {
            this.options.width = $(this.parent).width();
        }

        this._listeners = {
            onBufferRefresh: [],

        };

        this._cbuf = 0;
        this._syncfunc = null;
        this._viewport = null;
        this._overlay = null;
        this._portals = [];
        this._buffers = [];
        this._rects = [];
        this._paths = [];

        // set up UI and init events
        this.init();
    },

    init: function() {
        this._viewport = $("

")
            .addClass("imageviewer_viewport")
            .attr("id", this.options.id + "_viewport")
            .height(this.options.height);
        this._portalholder = $("

");
        this._overlay = $("

")
            .addClass("imageviewer_overlay")
            .attr("id", this.options.id + "_overlay")
            .hide();
        $(this.parent)
            .append(this._overlay)
            .append(this._viewport.append(this._portalholder));

        for (var i = 0; i < this.options.numBuffers; i++) {
            var pid = this.options.id + "_portal" + i;
            var portal = $("

")
                .height(this.options.height)
                .addClass("imageviewer_portal")
                .attr("id", pid)
                .appendTo(this._portalholder);
            this._portals.push(portal);
            this._buffers.push(new Seadragon.Viewer(pid));
            this._rects.push(null);
        }

        // cache function for wiring up syncing across
        // each buffer - this is a little gross.  We store
        // a ref to the function to easily add/remove it
        // as an event listeners running with the correct
        // scope.
        var self = this;
        this._syncfunc = function() {
            self.syncToActiveBuffer();
        }
        this.refreshDashboard();
        this.setMasterBuffer();
    },

    container: function() {
        return this.containerWidget();
    },

    setDashboardVisible: function(show) {
        this.options.dashboard = show;
        this.refreshDashboard(show);
    },

    refreshDashboard: function() {
        for (var i in this._buffers) {
            this._buffers[i].setDashboardEnabled(
                this.options.dashboard);
        }
    },

    close: function() {
        for (var i in this._buffers) {
            if (this._buffers[i].isOpen()) {
                this._buffers[i].close();
            }
        }
    },

    updateSize: function(width, height) {
        this._logger(width + "   " + height);
        this._viewport
            .height(height)
            .width(width);
        this._overlay
            .height(height)
            .width(width);
        for (var i in this._portals) {
            this._portals[i]
                .width(width)
                .height(height);
        }
        this.options.height = height;
        this.options.width = width;
        this.refreshBuffers();
    },

    setWidth: function(width) {
        this.updateSize(width, this.options.height);
    },

    setHeight: function(height) {
        this.updateSize(this.options.width, height);
    },

    isReady: function() {
        for (var i in this._buffers) {
            if (!this._buffers[i].viewport) {
                return false;
            }
        }
        return true;
    },

    setWaiting: function(wait) {
        this._overlay.css({
            top: this._viewport.position().top,
            left: this._viewport.position().left,
            width: this._viewport.width(),
            height: this._viewport.height(),
        }).toggle(wait);
    },

    // set the path to a viewer and wire it to switch back
    // to the original position and zoom...
    setBufferPath: function(bufnum, dzipath) {
        this._paths[bufnum] = dzipath;
        var buffer = this._buffers[bufnum];
        if (buffer.isOpen()) {
            var center = buffer.viewport.getCenter();
            var zoom = buffer.viewport.getZoom();
            buffer.addEventListener("open", function(e) {
                buffer.viewport.panTo(center, true);
                buffer.viewport.zoomTo(zoom, true);
            });
        }

        buffer.openDzi(dzipath);
        this.callListeners("onBufferRefresh", bufnum);
    },

    bufferPath: function(bufnum) {
        return this._paths[bufnum];
    },

    activeBufferPath: function() {
        return this._paths[this._cbuf];
    },

    activeBuffer: function() {
        return this._cbuf;
    },

    setActiveBuffer: function(bufnum) {
        if (bufnum + 1 > this.options.numBuffers)
            throw "Can't set buffer to " + bufnum + ".  Only "
                + this.options.numBuffers + " available";
        this._cbuf = bufnum;
        this.refreshBuffers();
    },

    bufferOverlays: function(bufnum) {
        return this._rects[bufnum];
    },

    activeBufferOverlays: function(bufnum) {
        return this._rects[this._cbuf];
    },

    activeViewer: function() {
        return this._buffers[this._cbuf];
    },

    // set overlay elements for a given buffer, or if
    // no nuffer number given, all buffers
    setBufferOverlays: function(rects, bufnum) {
        if (bufnum && typeof bufnum == "object") {
            for (var i in bufnum) {
                this._rects[bufnum[i]] = rects;
            }
        } else if (bufnum) {
            this._rects[bufnum] = rects;
        } else {
            for (var i in this._rects) {
                this._rects[i] = rects;
            }
        }

        // FIXME: hack around the viewport not
        // being built the first time we try to
        // draw overlays
        var self = this;
        if (this.isReady()) {
            self.drawBufferOverlays();
        } else {
            setTimeout(function() {
                self.drawBufferOverlays();
            }, 200);
        }
    },

    drawBufferOverlays: function() {
        for (var i in this._buffers) {
            var portal = this._portals[i];
            var viewer = this._buffers[i];
            var overlays = this._rects[i];
            if (!viewer || !viewer.drawer || !overlays)
                continue;
            //viewer.drawer.clearOverlays();
            $(".viewer_highlight", portal).each(function(i, elem) {
                viewer.drawer.removeOverlay(elem);
            });
            var overlaydiv;
            $.each(overlays, function(class, rects) {
                for (var r in rects) {
                    overlaydiv = document.createElement("div");
                    $(overlaydiv).addClass("viewer_highlight " + class);
                    viewer.drawer.addOverlay(overlaydiv, rects[r]);
                }
            });
        }
    },

    nextBuffer: function() {
        if (this._cbuf < this.options.numBuffers - 1) {
            this._cbuf++;
        } else {
            this._cbuf = 0;
        }
        this.refreshBuffers();
    },

    refreshBuffers: function() {
        this._portalholder.css("margin-top", "-"
                + (this._cbuf * this.options.height) + "px");
        this.setMasterBuffer();
    },

    // Sync background buffers to the foreground one
    syncToActiveBuffer: function() {
        //this._logger("Syncing others to: " + this._cbuf);
        var active = this._buffers[this._cbuf];
        for (i in this._buffers) {
            if (i != this._cbuf) {
                this._buffers[i].viewport.zoomTo(active.viewport.getZoom(), true);
                this._buffers[i].viewport.panTo(active.viewport.getCenter(), true);
            }
        }
    },

    // sync movement of inactive viewers to the
    // active one
    setMasterBuffer: function() {
        for (var i in this._buffers) {
            this._buffers[i].removeEventListener("animation", this._syncfunc);
        }
        this._logger("Setting sync to: " + this._cbuf);
        this._buffers[this._cbuf].addEventListener("animation", this._syncfunc);
    },

    // wrappers for viewport functions
    zoomBy: function(zoom, immediately) {
        this._buffers[this._cbuf].viewport.zoomBy(zoom, immediately);
    },

    goHome: function() {
        this._buffers[this._cbuf].viewport.goHome();
    },

    setFullPage: function(fullpage) {
        this._buffers[this._cbuf].setFullPage(fullpage);
    },

    fitBounds: function(rect, immediately) {
        this._buffers[this._cbuf].viewport.fitBounds(rect, immediately);
    },
});

<commit_msg>Turn of animation<commit_after>// Aggregates X number of Seadragon Viewers a set of switchable buffers
// Syncing of zooming and position is always set to the currently
// viewed buffer

if (OCRJS === undefined) {
    var OCRJS = {};
}

Seadragon.Config.blendTime = 0;
Seadragon.Config.animationTime = 0;

OCRJS.ImageViewer = OCRJS.OcrBaseWidget.extend({
    constructor: function(parent, options) {
        this.base();
        this.parent = parent;
        this.options = {
            id: new Date().getTime(),
            numBuffers: 2,
            width: -1,
            height: 500,
            log: false,
            dashboard: true,
        };
        $.extend(this.options, options);

        if (this.options.width == -1) {
            this.options.width = $(this.parent).width();
        }

        this._listeners = {
            onBufferRefresh: [],

        };

        this._cbuf = 0;
        this._syncfunc = null;
        this._viewport = null;
        this._overlay = null;
        this._portals = [];
        this._buffers = [];
        this._rects = [];
        this._paths = [];

        // set up UI and init events
        this.init();
    },

    init: function() {
        this._viewport = $("

")
            .addClass("imageviewer_viewport")
            .attr("id", this.options.id + "_viewport")
            .height(this.options.height);
        this._portalholder = $("

");
        this._overlay = $("

")
            .addClass("imageviewer_overlay")
            .attr("id", this.options.id + "_overlay")
            .hide();
        $(this.parent)
            .append(this._overlay)
            .append(this._viewport.append(this._portalholder));

        for (var i = 0; i < this.options.numBuffers; i++) {
            var pid = this.options.id + "_portal" + i;
            var portal = $("

")
                .height(this.options.height)
                .addClass("imageviewer_portal")
                .attr("id", pid)
                .appendTo(this._portalholder);
            this._portals.push(portal);
            this._buffers.push(new Seadragon.Viewer(pid));
            this._rects.push(null);
        }

        // cache function for wiring up syncing across
        // each buffer - this is a little gross.  We store
        // a ref to the function to easily add/remove it
        // as an event listeners running with the correct
        // scope.
        var self = this;
        this._syncfunc = function() {
            self.syncToActiveBuffer();
        }
        this.refreshDashboard();
        this.setMasterBuffer();
    },

    container: function() {
        return this.containerWidget();
    },

    setDashboardVisible: function(show) {
        this.options.dashboard = show;
        this.refreshDashboard(show);
    },

    refreshDashboard: function() {
        for (var i in this._buffers) {
            this._buffers[i].setDashboardEnabled(
                this.options.dashboard);
        }
    },

    close: function() {
        for (var i in this._buffers) {
            if (this._buffers[i].isOpen()) {
                this._buffers[i].close();
            }
        }
    },

    updateSize: function(width, height) {
        this._logger(width + "   " + height);
        this._viewport
            .height(height)
            .width(width);
        this._overlay
            .height(height)
            .width(width);
        for (var i in this._portals) {
            this._portals[i]
                .width(width)
                .height(height);
        }
        this.options.height = height;
        this.options.width = width;
        this.refreshBuffers();
    },

    setWidth: function(width) {
        this.updateSize(width, this.options.height);
    },

    setHeight: function(height) {
        this.updateSize(this.options.width, height);
    },

    isReady: function() {
        for (var i in this._buffers) {
            if (!this._buffers[i].viewport) {
                return false;
            }
        }
        return true;
    },

    setWaiting: function(wait) {
        this._overlay.css({
            top: this._viewport.position().top,
            left: this._viewport.position().left,
            width: this._viewport.width(),
            height: this._viewport.height(),
        }).toggle(wait);
    },

    // set the path to a viewer and wire it to switch back
    // to the original position and zoom...
    setBufferPath: function(bufnum, dzipath) {
        this._paths[bufnum] = dzipath;
        var buffer = this._buffers[bufnum];
        if (buffer.isOpen()) {
            var center = buffer.viewport.getCenter();
            var zoom = buffer.viewport.getZoom();
            buffer.addEventListener("open", function(e) {
                buffer.viewport.panTo(center, true);
                buffer.viewport.zoomTo(zoom, true);
            });
        }

        buffer.openDzi(dzipath);
        this.callListeners("onBufferRefresh", bufnum);
    },

    bufferPath: function(bufnum) {
        return this._paths[bufnum];
    },

    activeBufferPath: function() {
        return this._paths[this._cbuf];
    },

    activeBuffer: function() {
        return this._cbuf;
    },

    setActiveBuffer: function(bufnum) {
        if (bufnum + 1 > this.options.numBuffers)
            throw "Can't set buffer to " + bufnum + ".  Only "
                + this.options.numBuffers + " available";
        this._cbuf = bufnum;
        this.refreshBuffers();
    },

    bufferOverlays: function(bufnum) {
        return this._rects[bufnum];
    },

    activeBufferOverlays: function(bufnum) {
        return this._rects[this._cbuf];
    },

    activeViewer: function() {
        return this._buffers[this._cbuf];
    },

    // set overlay elements for a given buffer, or if
    // no nuffer number given, all buffers
    setBufferOverlays: function(rects, bufnum) {
        if (bufnum && typeof bufnum == "object") {
            for (var i in bufnum) {
                this._rects[bufnum[i]] = rects;
            }
        } else if (bufnum) {
            this._rects[bufnum] = rects;
        } else {
            for (var i in this._rects) {
                this._rects[i] = rects;
            }
        }

        // FIXME: hack around the viewport not
        // being built the first time we try to
        // draw overlays
        var self = this;
        if (this.isReady()) {
            self.drawBufferOverlays();
        } else {
            setTimeout(function() {
                self.drawBufferOverlays();
            }, 200);
        }
    },

    drawBufferOverlays: function() {
        for (var i in this._buffers) {
            var portal = this._portals[i];
            var viewer = this._buffers[i];
            var overlays = this._rects[i];
            if (!viewer || !viewer.drawer || !overlays)
                continue;
            //viewer.drawer.clearOverlays();
            $(".viewer_highlight", portal).each(function(i, elem) {
                viewer.drawer.removeOverlay(elem);
            });
            var overlaydiv;
            $.each(overlays, function(class, rects) {
                for (var r in rects) {
                    overlaydiv = document.createElement("div");
                    $(overlaydiv).addClass("viewer_highlight " + class);
                    viewer.drawer.addOverlay(overlaydiv, rects[r]);
                }
            });
        }
    },

    nextBuffer: function() {
        if (this._cbuf < this.options.numBuffers - 1) {
            this._cbuf++;
        } else {
            this._cbuf = 0;
        }
        this.refreshBuffers();
    },

    refreshBuffers: function() {
        this._portalholder.css("margin-top", "-"
                + (this._cbuf * this.options.height) + "px");
        this.setMasterBuffer();
    },

    // Sync background buffers to the foreground one
    syncToActiveBuffer: function() {
        //this._logger("Syncing others to: " + this._cbuf);
        var active = this._buffers[this._cbuf];
        for (i in this._buffers) {
            if (i != this._cbuf) {
                this._buffers[i].viewport.zoomTo(active.viewport.getZoom(), true);
                this._buffers[i].viewport.panTo(active.viewport.getCenter(), true);
            }
        }
    },

    // sync movement of inactive viewers to the
    // active one
    setMasterBuffer: function() {
        for (var i in this._buffers) {
            this._buffers[i].removeEventListener("animation", this._syncfunc);
        }
        this._logger("Setting sync to: " + this._cbuf);
        this._buffers[this._cbuf].addEventListener("animation", this._syncfunc);
    },

    // wrappers for viewport functions
    zoomBy: function(zoom, immediately) {
        this._buffers[this._cbuf].viewport.zoomBy(zoom, immediately);
    },

    goHome: function() {
        this._buffers[this._cbuf].viewport.goHome();
    },

    setFullPage: function(fullpage) {
        this._buffers[this._cbuf].setFullPage(fullpage);
    },

    fitBounds: function(rect, immediately) {
        this._buffers[this._cbuf].viewport.fitBounds(rect, immediately);
    },
});

<|endoftext|>


<commit_before><commit_msg>Adds script to find  customers using 'Link' custom metadata<commit_after><|endoftext|>


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

/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//   /!\ DO NOT MODIFY THIS FILE /!\
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// create-react-app is installed globally on people's computers. This means
// that it is extremely difficult to have them upgrade the version and
// because there's only one global version installed, it is very prone to
// breaking changes.
//
// The only job of create-react-app is to init the repository and then
// forward all the commands to the local version of create-react-app.
//
// If you need to add a new command, please add it to the scripts/ folder.
//
// The only reason to modify this file is to add more warnings and
// troubleshooting information for the `create-react-app` command.
//
// Do not make breaking changes! We absolutely don't want to have to
// tell people to update their global version of create-react-app.
//
// Also be careful with new language features.
// This file must work on Node 0.10+.
//
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//   /!\ DO NOT MODIFY THIS FILE /!\
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

'use strict';

var chalk = require('chalk');
var validateProjectName = require("validate-npm-package-name");

var currentNodeVersion = process.versions.node;
if (currentNodeVersion.split('.')[0] < 4) {
  console.error(
    chalk.red(
      'You are running Node ' + currentNodeVersion + '.\n' +
      'Create React App requires Node 4 or higher. \n' +
      'Please update your version of Node.'
    )
  );
  process.exit(1);
}

var commander = require('commander');
var fs = require('fs-extra');
var path = require('path');
var execSync = require('child_process').execSync;
var spawn = require('cross-spawn');
var semver = require('semver');
var dns = require('dns');

var projectName;

var program = commander
  .version(require('./package.json').version)
  .arguments('<project-directory>')
  .usage(chalk.green('<project-directory>') + ' [options]')
  .action(function (name) {
    projectName = name;
  })
  .option('--verbose', 'print additional logs')
  .option('--scripts-version <alternative-package>', 'use a non-standard version of react-scripts')
  .allowUnknownOption()
  .on('--help', function () {
    console.log('    Only ' + chalk.green('<project-directory>') + ' is required.');
    console.log();
    console.log('    A custom ' + chalk.cyan('--scripts-version') + ' can be one of:');
    console.log('      - a specific npm version: ' + chalk.green('0.8.2'));
    console.log('      - a custom fork published on npm: ' + chalk.green('my-react-scripts'));
    console.log('      - a .tgz archive: ' + chalk.green('https://mysite.com/my-react-scripts-0.8.2.tgz'));
    console.log('    It is not needed unless you specifically want to use a fork.');
    console.log();
    console.log('    If you have any problems, do not hesitate to file an issue:');
    console.log('      ' + chalk.cyan('https://github.com/facebookincubator/create-react-app/issues/new'));
    console.log();
  })
  .parse(process.argv);

if (typeof projectName === 'undefined') {
  console.error('Please specify the project directory:');
  console.log('  ' + chalk.cyan(program.name()) + chalk.green(' <project-directory>'));
  console.log();
  console.log('For example:');
  console.log('  ' + chalk.cyan(program.name()) + chalk.green(' my-react-app'));
  console.log();
  console.log('Run ' + chalk.cyan(program.name() + ' --help') + ' to see all options.');
  process.exit(1);
}

function printValidationResults(results) {
  if (typeof results !== 'undefined') {
    results.forEach(function (error) {
      console.error(chalk.red('  * ' + error));
    });
  }
}

var hiddenProgram = new commander.Command()
  .option('--internal-testing-template <path-to-template>', '(internal usage only, DO NOT RELY ON THIS) ' +
    'use a non-standard application template')
  .parse(process.argv)

createApp(projectName, program.verbose, program.scriptsVersion, hiddenProgram.internalTestingTemplate);

function createApp(name, verbose, version, template) {
  var root = path.resolve(name);
  var appName = path.basename(root);

  checkAppName(appName);
  fs.ensureDirSync(name);
  if (!isSafeToCreateProjectIn(root)) {
    console.log('The directory ' + chalk.green(name) + ' contains files that could conflict.');
    console.log('Try using a new directory name.');
    process.exit(1);
  }

  console.log(
    'Creating a new React app in ' + chalk.green(root) + '.'
  );
  console.log();

  var packageJson = {
    name: appName,
    version: '0.1.0',
    private: true
  };
  fs.writeFileSync(
    path.join(root, 'package.json'),
    JSON.stringify(packageJson, null, 2)
  );
  var originalDirectory = process.cwd();
  process.chdir(root);

  run(root, appName, version, verbose, originalDirectory, template);
}

function shouldUseYarn() {
  try {
    execSync('yarnpkg --version', {stdio: 'ignore'});
    return true;
  } catch (e) {
    return false;
  }
}

function install(useYarn, dependencies, verbose, isOnline) {
  return new Promise(function(resolve, reject) {
    var command;
    var args;
    if (useYarn) {
      command = 'yarnpkg';
      args = [
        'add',
        '--exact',
        isOnline === false && '--offline'
      ].concat(dependencies);

      if (!isOnline) {
        console.log(chalk.yellow('You appear to be offline.'));
        console.log(chalk.yellow('Falling back to the local Yarn cache.'));
        console.log();
      }

    } else {
      checkNpmVersion();
      command = 'npm';
      args = ['install', '--save', '--save-exact'].concat(dependencies);
    }

    if (verbose) {
      args.push('--verbose');
    }

    var child = spawn(command, args, {stdio: 'inherit'});
    child.on('close', function(code) {
      if (code !== 0) {
        reject({
          command: command + ' ' + args.join(' ')
        });
        return;
      }
      resolve();
    });
  });
}

function run(root, appName, version, verbose, originalDirectory, template) {
  var packageToInstall = getInstallPackage(version);
  var packageName = getPackageName(packageToInstall);

  var allDependencies = ['react', 'react-dom', packageToInstall];

  console.log('Installing packages. This might take a couple minutes.');
  console.log(
    'Installing ' + chalk.cyan('react') + ', ' + chalk.cyan('react-dom') +
    ', and ' + chalk.cyan(packageName) + '...'
  );
  console.log();

  var useYarn = shouldUseYarn();
  checkIfOnline(useYarn)
    .then(function(isOnline) {
      return install(useYarn, allDependencies, verbose, isOnline);
    })
    .then(function() {
      checkNodeVersion(packageName);

      // Since react-scripts has been installed with --save
      // we need to move it into devDependencies and rewrite package.json
      // also ensure react dependencies have caret version range
      fixDependencies(packageName);

      var scriptsPath = path.resolve(
        process.cwd(),
        'node_modules',
        packageName,
        'scripts',
        'init.js'
      );
      var init = require(scriptsPath);
      init(root, appName, verbose, originalDirectory, template);
    })
    .catch(function(reason) {
      console.log();
      console.log('Aborting installation.');
      if (reason.command) {
        console.log('  ' + chalk.cyan(reason.command), 'has failed.')
      }
      console.log();

      // On 'exit' we will delete these files from target directory.
      var knownGeneratedFiles = [
        'package.json', 'npm-debug.log', 'yarn-error.log', 'yarn-debug.log', 'node_modules'
      ];
      var currentFiles = fs.readdirSync(path.join(root));
      currentFiles.forEach(function (file) {
        knownGeneratedFiles.forEach(function (fileToMatch) {
          // This will catch `(npm-debug|yarn-error|yarn-debug).log*` files
          // and the rest of knownGeneratedFiles.
          if ((fileToMatch.match(/.log/g) && file.indexOf(fileToMatch) === 0) || file === fileToMatch) {
            console.log('Deleting generated file...', chalk.cyan(file));
            fs.removeSync(path.join(root, file));
          }
        });
      });
      var remainingFiles = fs.readdirSync(path.join(root));
      if (!remainingFiles.length) {
        // Delete target folder if empty
        console.log('Deleting', chalk.cyan(appName + '/'), 'from', chalk.cyan(path.resolve(root, '..')));
        process.chdir(path.resolve(root, '..'));
        fs.removeSync(path.join(root));
      }
      console.log('Done.');
      process.exit(1);
    });
}

function getInstallPackage(version) {
  var packageToInstall = 'react-scripts';
  var validSemver = semver.valid(version);
  if (validSemver) {
    packageToInstall += '@' + validSemver;
  } else if (version) {
    // for tar.gz or alternative paths
    packageToInstall = version;
  }
  return packageToInstall;
}

// Extract package name from tarball url or path.
function getPackageName(installPackage) {
  if (installPackage.indexOf('.tgz') > -1) {
    // The package name could be with or without semver version, e.g. react-scripts-0.2.0-alpha.1.tgz
    // However, this function returns package name only without semver version.
    return installPackage.match(/^.+\/(.+?)(?:-\d+.+)?\.tgz$/)[1];
  } else if (installPackage.indexOf('git+') === 0) {
    // Pull package name out of git urls e.g:
    // git+https://github.com/mycompany/react-scripts.git
    // git+ssh://github.com/mycompany/react-scripts.git#v1.2.3
    return installPackage.match(/([^\/]+)\.git(#.*)?$/)[1];
  } else if (installPackage.indexOf('@') > 0) {
    // Do not match @scope/ when stripping off @version or @tag
    return installPackage.charAt(0) + installPackage.substr(1).split('@')[0];
  }
  return installPackage;
}

function checkNpmVersion() {
  var isNpm2 = false;
  try {
    var npmVersion = execSync('npm --version').toString();
    isNpm2 = semver.lt(npmVersion, '3.0.0');
  } catch (err) {
    return;
  }
  if (!isNpm2) {
    return;
  }
  console.log(chalk.yellow('It looks like you are using npm 2.'));
  console.log(chalk.yellow(
    'We suggest using npm 3 or Yarn for faster install times ' +
    'and less disk space usage.'
  ));
  console.log();
}

function checkNodeVersion(packageName) {
  var packageJsonPath = path.resolve(
    process.cwd(),
    'node_modules',
    packageName,
    'package.json'
  );
  var packageJson = require(packageJsonPath);
  if (!packageJson.engines || !packageJson.engines.node) {
    return;
  }

  if (!semver.satisfies(process.version, packageJson.engines.node)) {
    console.error(
      chalk.red(
        'You are running Node %s.\n' +
        'Create React App requires Node %s or higher. \n' +
        'Please update your version of Node.'
      ),
      process.version,
      packageJson.engines.node
    );
    process.exit(1);
  }
}

function checkAppName(appName) {
  var validationResult = validateProjectName(appName);
  if (!validationResult.validForNewPackages) {
    console.error('Could not create a project called ' + chalk.red('"' + appName + '"') + ' because of npm naming restrictions:');
    printValidationResults(validationResult.errors);
    printValidationResults(validationResult.warnings);
    process.exit(1);
  }

  // TODO: there should be a single place that holds the dependencies
  var dependencies = ['react', 'react-dom'];
  var devDependencies = ['react-scripts'];
  var allDependencies = dependencies.concat(devDependencies).sort();
  if (allDependencies.indexOf(appName) >= 0) {
    console.error(
      chalk.red(
        'We cannot create a project called ' + chalk.green(appName) + ' because a dependency with the same name exists.\n' +
        'Due to the way npm works, the following names are not allowed:\n\n'
      ) +
      chalk.cyan(
        allDependencies.map(function(depName) {
          return '  ' + depName;
        }).join('\n')
      ) +
      chalk.red('\n\nPlease choose a different project name.')
    );
    process.exit(1);
  }
}

function makeCaretRange(dependencies, name) {
  var version = dependencies[name];

  if (typeof version === 'undefined') {
    console.error(
      chalk.red('Missing ' + name + ' dependency in package.json')
    );
    process.exit(1);
  }

  var patchedVersion = '^' + version;

  if (!semver.validRange(patchedVersion)) {
    console.error(
      'Unable to patch ' + name + ' dependency version because version ' + chalk.red(version) + ' will become invalid ' + chalk.red(patchedVersion)
    );
    patchedVersion = version;
  }

  dependencies[name] = patchedVersion;
}

function fixDependencies(packageName) {
  var packagePath = path.join(process.cwd(), 'package.json');
  var packageJson = require(packagePath);

  if (typeof packageJson.dependencies === 'undefined') {
    console.error(
      chalk.red('Missing dependencies in package.json')
    );
    process.exit(1);
  }

  var packageVersion = packageJson.dependencies[packageName];

  if (typeof packageVersion === 'undefined') {
    console.error(
      chalk.red('Unable to find ' + packageName + ' in package.json')
    );
    process.exit(1);
  }

  packageJson.devDependencies = packageJson.devDependencies || {};
  packageJson.devDependencies[packageName] = packageVersion;
  delete packageJson.dependencies[packageName];

  makeCaretRange(packageJson.dependencies, 'react');
  makeCaretRange(packageJson.dependencies, 'react-dom');

  fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2));
}

// If project only contains files generated by GH, it’s safe.
// We also special case IJ-based products .idea because it integrates with CRA:
// https://github.com/facebookincubator/create-react-app/pull/368#issuecomment-243446094
function isSafeToCreateProjectIn(root) {
  var validFiles = [
    '.DS_Store', 'Thumbs.db', '.git', '.gitignore', '.idea', 'README.md', 'LICENSE', 'web.iml'
  ];
  return fs.readdirSync(root)
    .every(function(file) {
      return validFiles.indexOf(file) >= 0;
    });
}

function checkIfOnline(useYarn) {
  if (!useYarn) {
    // Don't ping the Yarn registry.
    // We'll just assume the best case.
    return Promise.resolve(true);
  }

  return new Promise(function(resolve) {
    dns.resolve('registry.yarnpkg.com', function(err) {
      resolve(err === null);
    });
  });
}
<commit_msg>False expression should not be in dependencies<commit_after>#!/usr/bin/env node

/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//   /!\ DO NOT MODIFY THIS FILE /!\
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// create-react-app is installed globally on people's computers. This means
// that it is extremely difficult to have them upgrade the version and
// because there's only one global version installed, it is very prone to
// breaking changes.
//
// The only job of create-react-app is to init the repository and then
// forward all the commands to the local version of create-react-app.
//
// If you need to add a new command, please add it to the scripts/ folder.
//
// The only reason to modify this file is to add more warnings and
// troubleshooting information for the `create-react-app` command.
//
// Do not make breaking changes! We absolutely don't want to have to
// tell people to update their global version of create-react-app.
//
// Also be careful with new language features.
// This file must work on Node 0.10+.
//
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//   /!\ DO NOT MODIFY THIS FILE /!\
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

'use strict';

var chalk = require('chalk');
var validateProjectName = require("validate-npm-package-name");

var currentNodeVersion = process.versions.node;
if (currentNodeVersion.split('.')[0] < 4) {
  console.error(
    chalk.red(
      'You are running Node ' + currentNodeVersion + '.\n' +
      'Create React App requires Node 4 or higher. \n' +
      'Please update your version of Node.'
    )
  );
  process.exit(1);
}

var commander = require('commander');
var fs = require('fs-extra');
var path = require('path');
var execSync = require('child_process').execSync;
var spawn = require('cross-spawn');
var semver = require('semver');
var dns = require('dns');

var projectName;

var program = commander
  .version(require('./package.json').version)
  .arguments('<project-directory>')
  .usage(chalk.green('<project-directory>') + ' [options]')
  .action(function (name) {
    projectName = name;
  })
  .option('--verbose', 'print additional logs')
  .option('--scripts-version <alternative-package>', 'use a non-standard version of react-scripts')
  .allowUnknownOption()
  .on('--help', function () {
    console.log('    Only ' + chalk.green('<project-directory>') + ' is required.');
    console.log();
    console.log('    A custom ' + chalk.cyan('--scripts-version') + ' can be one of:');
    console.log('      - a specific npm version: ' + chalk.green('0.8.2'));
    console.log('      - a custom fork published on npm: ' + chalk.green('my-react-scripts'));
    console.log('      - a .tgz archive: ' + chalk.green('https://mysite.com/my-react-scripts-0.8.2.tgz'));
    console.log('    It is not needed unless you specifically want to use a fork.');
    console.log();
    console.log('    If you have any problems, do not hesitate to file an issue:');
    console.log('      ' + chalk.cyan('https://github.com/facebookincubator/create-react-app/issues/new'));
    console.log();
  })
  .parse(process.argv);

if (typeof projectName === 'undefined') {
  console.error('Please specify the project directory:');
  console.log('  ' + chalk.cyan(program.name()) + chalk.green(' <project-directory>'));
  console.log();
  console.log('For example:');
  console.log('  ' + chalk.cyan(program.name()) + chalk.green(' my-react-app'));
  console.log();
  console.log('Run ' + chalk.cyan(program.name() + ' --help') + ' to see all options.');
  process.exit(1);
}

function printValidationResults(results) {
  if (typeof results !== 'undefined') {
    results.forEach(function (error) {
      console.error(chalk.red('  * ' + error));
    });
  }
}

var hiddenProgram = new commander.Command()
  .option('--internal-testing-template <path-to-template>', '(internal usage only, DO NOT RELY ON THIS) ' +
    'use a non-standard application template')
  .parse(process.argv)

createApp(projectName, program.verbose, program.scriptsVersion, hiddenProgram.internalTestingTemplate);

function createApp(name, verbose, version, template) {
  var root = path.resolve(name);
  var appName = path.basename(root);

  checkAppName(appName);
  fs.ensureDirSync(name);
  if (!isSafeToCreateProjectIn(root)) {
    console.log('The directory ' + chalk.green(name) + ' contains files that could conflict.');
    console.log('Try using a new directory name.');
    process.exit(1);
  }

  console.log(
    'Creating a new React app in ' + chalk.green(root) + '.'
  );
  console.log();

  var packageJson = {
    name: appName,
    version: '0.1.0',
    private: true
  };
  fs.writeFileSync(
    path.join(root, 'package.json'),
    JSON.stringify(packageJson, null, 2)
  );
  var originalDirectory = process.cwd();
  process.chdir(root);

  run(root, appName, version, verbose, originalDirectory, template);
}

function shouldUseYarn() {
  try {
    execSync('yarnpkg --version', {stdio: 'ignore'});
    return true;
  } catch (e) {
    return false;
  }
}

function install(useYarn, dependencies, verbose, isOnline) {
  return new Promise(function(resolve, reject) {
    var command;
    var args;
    if (useYarn) {
      command = 'yarnpkg';
      args = [
        'add',
        '--exact',
      ];
      if (!isOnline) {
        args.push('--offline');
      }
      [].push.apply(args, dependencies);

      if (!isOnline) {
        console.log(chalk.yellow('You appear to be offline.'));
        console.log(chalk.yellow('Falling back to the local Yarn cache.'));
        console.log();
      }

    } else {
      checkNpmVersion();
      command = 'npm';
      args = ['install', '--save', '--save-exact'].concat(dependencies);
    }

    if (verbose) {
      args.push('--verbose');
    }

    var child = spawn(command, args, {stdio: 'inherit'});
    child.on('close', function(code) {
      if (code !== 0) {
        reject({
          command: command + ' ' + args.join(' ')
        });
        return;
      }
      resolve();
    });
  });
}

function run(root, appName, version, verbose, originalDirectory, template) {
  var packageToInstall = getInstallPackage(version);
  var packageName = getPackageName(packageToInstall);

  var allDependencies = ['react', 'react-dom', packageToInstall];

  console.log('Installing packages. This might take a couple minutes.');
  console.log(
    'Installing ' + chalk.cyan('react') + ', ' + chalk.cyan('react-dom') +
    ', and ' + chalk.cyan(packageName) + '...'
  );
  console.log();

  var useYarn = shouldUseYarn();
  checkIfOnline(useYarn)
    .then(function(isOnline) {
      return install(useYarn, allDependencies, verbose, isOnline);
    })
    .then(function() {
      checkNodeVersion(packageName);

      // Since react-scripts has been installed with --save
      // we need to move it into devDependencies and rewrite package.json
      // also ensure react dependencies have caret version range
      fixDependencies(packageName);

      var scriptsPath = path.resolve(
        process.cwd(),
        'node_modules',
        packageName,
        'scripts',
        'init.js'
      );
      var init = require(scriptsPath);
      init(root, appName, verbose, originalDirectory, template);
    })
    .catch(function(reason) {
      console.log();
      console.log('Aborting installation.');
      if (reason.command) {
        console.log('  ' + chalk.cyan(reason.command), 'has failed.')
      }
      console.log();

      // On 'exit' we will delete these files from target directory.
      var knownGeneratedFiles = [
        'package.json', 'npm-debug.log', 'yarn-error.log', 'yarn-debug.log', 'node_modules'
      ];
      var currentFiles = fs.readdirSync(path.join(root));
      currentFiles.forEach(function (file) {
        knownGeneratedFiles.forEach(function (fileToMatch) {
          // This will catch `(npm-debug|yarn-error|yarn-debug).log*` files
          // and the rest of knownGeneratedFiles.
          if ((fileToMatch.match(/.log/g) && file.indexOf(fileToMatch) === 0) || file === fileToMatch) {
            console.log('Deleting generated file...', chalk.cyan(file));
            fs.removeSync(path.join(root, file));
          }
        });
      });
      var remainingFiles = fs.readdirSync(path.join(root));
      if (!remainingFiles.length) {
        // Delete target folder if empty
        console.log('Deleting', chalk.cyan(appName + '/'), 'from', chalk.cyan(path.resolve(root, '..')));
        process.chdir(path.resolve(root, '..'));
        fs.removeSync(path.join(root));
      }
      console.log('Done.');
      process.exit(1);
    });
}

function getInstallPackage(version) {
  var packageToInstall = 'react-scripts';
  var validSemver = semver.valid(version);
  if (validSemver) {
    packageToInstall += '@' + validSemver;
  } else if (version) {
    // for tar.gz or alternative paths
    packageToInstall = version;
  }
  return packageToInstall;
}

// Extract package name from tarball url or path.
function getPackageName(installPackage) {
  if (installPackage.indexOf('.tgz') > -1) {
    // The package name could be with or without semver version, e.g. react-scripts-0.2.0-alpha.1.tgz
    // However, this function returns package name only without semver version.
    return installPackage.match(/^.+\/(.+?)(?:-\d+.+)?\.tgz$/)[1];
  } else if (installPackage.indexOf('git+') === 0) {
    // Pull package name out of git urls e.g:
    // git+https://github.com/mycompany/react-scripts.git
    // git+ssh://github.com/mycompany/react-scripts.git#v1.2.3
    return installPackage.match(/([^\/]+)\.git(#.*)?$/)[1];
  } else if (installPackage.indexOf('@') > 0) {
    // Do not match @scope/ when stripping off @version or @tag
    return installPackage.charAt(0) + installPackage.substr(1).split('@')[0];
  }
  return installPackage;
}

function checkNpmVersion() {
  var isNpm2 = false;
  try {
    var npmVersion = execSync('npm --version').toString();
    isNpm2 = semver.lt(npmVersion, '3.0.0');
  } catch (err) {
    return;
  }
  if (!isNpm2) {
    return;
  }
  console.log(chalk.yellow('It looks like you are using npm 2.'));
  console.log(chalk.yellow(
    'We suggest using npm 3 or Yarn for faster install times ' +
    'and less disk space usage.'
  ));
  console.log();
}

function checkNodeVersion(packageName) {
  var packageJsonPath = path.resolve(
    process.cwd(),
    'node_modules',
    packageName,
    'package.json'
  );
  var packageJson = require(packageJsonPath);
  if (!packageJson.engines || !packageJson.engines.node) {
    return;
  }

  if (!semver.satisfies(process.version, packageJson.engines.node)) {
    console.error(
      chalk.red(
        'You are running Node %s.\n' +
        'Create React App requires Node %s or higher. \n' +
        'Please update your version of Node.'
      ),
      process.version,
      packageJson.engines.node
    );
    process.exit(1);
  }
}

function checkAppName(appName) {
  var validationResult = validateProjectName(appName);
  if (!validationResult.validForNewPackages) {
    console.error('Could not create a project called ' + chalk.red('"' + appName + '"') + ' because of npm naming restrictions:');
    printValidationResults(validationResult.errors);
    printValidationResults(validationResult.warnings);
    process.exit(1);
  }

  // TODO: there should be a single place that holds the dependencies
  var dependencies = ['react', 'react-dom'];
  var devDependencies = ['react-scripts'];
  var allDependencies = dependencies.concat(devDependencies).sort();
  if (allDependencies.indexOf(appName) >= 0) {
    console.error(
      chalk.red(
        'We cannot create a project called ' + chalk.green(appName) + ' because a dependency with the same name exists.\n' +
        'Due to the way npm works, the following names are not allowed:\n\n'
      ) +
      chalk.cyan(
        allDependencies.map(function(depName) {
          return '  ' + depName;
        }).join('\n')
      ) +
      chalk.red('\n\nPlease choose a different project name.')
    );
    process.exit(1);
  }
}

function makeCaretRange(dependencies, name) {
  var version = dependencies[name];

  if (typeof version === 'undefined') {
    console.error(
      chalk.red('Missing ' + name + ' dependency in package.json')
    );
    process.exit(1);
  }

  var patchedVersion = '^' + version;

  if (!semver.validRange(patchedVersion)) {
    console.error(
      'Unable to patch ' + name + ' dependency version because version ' + chalk.red(version) + ' will become invalid ' + chalk.red(patchedVersion)
    );
    patchedVersion = version;
  }

  dependencies[name] = patchedVersion;
}

function fixDependencies(packageName) {
  var packagePath = path.join(process.cwd(), 'package.json');
  var packageJson = require(packagePath);

  if (typeof packageJson.dependencies === 'undefined') {
    console.error(
      chalk.red('Missing dependencies in package.json')
    );
    process.exit(1);
  }

  var packageVersion = packageJson.dependencies[packageName];

  if (typeof packageVersion === 'undefined') {
    console.error(
      chalk.red('Unable to find ' + packageName + ' in package.json')
    );
    process.exit(1);
  }

  packageJson.devDependencies = packageJson.devDependencies || {};
  packageJson.devDependencies[packageName] = packageVersion;
  delete packageJson.dependencies[packageName];

  makeCaretRange(packageJson.dependencies, 'react');
  makeCaretRange(packageJson.dependencies, 'react-dom');

  fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2));
}

// If project only contains files generated by GH, it’s safe.
// We also special case IJ-based products .idea because it integrates with CRA:
// https://github.com/facebookincubator/create-react-app/pull/368#issuecomment-243446094
function isSafeToCreateProjectIn(root) {
  var validFiles = [
    '.DS_Store', 'Thumbs.db', '.git', '.gitignore', '.idea', 'README.md', 'LICENSE', 'web.iml'
  ];
  return fs.readdirSync(root)
    .every(function(file) {
      return validFiles.indexOf(file) >= 0;
    });
}

function checkIfOnline(useYarn) {
  if (!useYarn) {
    // Don't ping the Yarn registry.
    // We'll just assume the best case.
    return Promise.resolve(true);
  }

  return new Promise(function(resolve) {
    dns.resolve('registry.yarnpkg.com', function(err) {
      resolve(err === null);
    });
  });
}
<|endoftext|>


<commit_before>/*global $, _, ko, N*/

'use strict';

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

var
render          = require('../../../lib/render/client'),
embedded_fonts  = require('../../../lib/shared/embedded_fonts'),
glyphs_map      = require('../../../lib/shared/glyphs_map');

// Int to char, with fix for big numbers
// see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/fromCharCode
function fixedFromCharCode(code) {
  /*jshint bitwise: false*/
  if (code > 0xffff) {
    code -= 0x10000;
    var surrogate1 = 0xd800 + (code >> 10),
        surrogate2 = 0xdc00 + (code & 0x3ff);
    return String.fromCharCode(surrogate1, surrogate2);
  } else {
    return String.fromCharCode(code);
  }
}

// Char to Int, with fix for big numbers
function fixedCharCodeAt(char) {
  /*jshint bitwise: false*/
  var char1 = char.charCodeAt(0),
      char2 = char.charCodeAt(1);

  if ((char.length >= 2) &&
      ((char1 & 0xfc00) === 0xd800) &&
      ((char2 & 0xfc00) === 0xdc00)) {
    return 0x10000 + ((char1 - 0xd800) << 10) + (char2 - 0xdc00);
  } else {
    return char1;
  }
}

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

function GlyphModel(font, data) {

  //
  // Essential properties
  //

  this.uid              = data.uid;
  this.originalName     = data.css;
  this.originalCode     = data.code;

  //
  // Helper properties
  //

  this.font             = font;
  this.keywords         = (data.search || []).join(',');
  this.charRef          = fixedFromCharCode(glyphs_map[font.fontname][data.uid]);

  //
  // Actual properties state
  //

  this.selected         = ko.observable(false);
  this.name             = ko.observable(this.originalName);
  this.code             = ko.observable(this.originalCode);

  //
  // Helpers
  //

  this.toggleSelection  = function () {
    this.selected(!this.selected());
  }.bind(this);

  //
  // Whenever or not glyph should be treaten as "modified"
  //

  this.isModified       = ko.computed(function () {
    return  !!this.selected() ||
            this.name() !== this.originalName ||
            this.code() !== this.originalCode;
  }, this);

  //
  // code value as character (for code editor)
  //

  this.customChar       = ko.computed({
    read: function () {
      return fixedFromCharCode(this.code());
    },
    write: function (value) {
      this.code(fixedCharCodeAt(value));
    },
    owner: this
  });

  //
  // code value as hex-string (for code editor)
  //

  this.customHex        = ko.computed({
    read: function () {
      var code = this.code().toString(16).toUpperCase();
      return "0000".substr(0, Math.max(4 - code.length, 0)) + code;
    },
    write: function (value) {
      // value must be HEX string - omit invalid chars
      value = 0 + value.replace(/[^0-9a-f]+/g, '');
      this.code(parseInt(value, 16));
    },
    owner: this
  });

}

function FontModel(data) {

  //
  // Essential properties
  //

  this.id         = data.id;
  this.fullname   = data.font.fullname;
  this.fontname   = data.font.fontname;

  this.author     = data.meta.author;
  this.license    = data.meta.license;
  this.homepage   = data.meta.homepage;
  this.email      = data.meta.email;
  this.twitter    = data.meta.twitter;
  this.github     = data.meta.github;

  //
  // View state properties
  //

  this.collapsed  = ko.observable(false);

  //
  // Helpers
  //

  this.toggleCollapsed = function () {
    this.collapsed(!this.collapsed());
  }.bind(this);

  //
  // Array of font glyphs
  //

  this.glyphs     = _.map(data.glyphs, function (data) {
    return new GlyphModel(this, data);
  }, this);

  //
  // Returns array of selected glyphs of a font
  // throttling compensates mass reflows on multiselect
  //

  this.selectedGlyphs = ko.computed(function () {
    var glyphs = [];

    _.each(this.glyphs, function (glyph) {
      if (glyph.selected()) {
        glyphs.push(glyph);
      }
    });

    return glyphs;
  }, this).extend({ throttle: 100 });

  //
  // Returns array of modified glyphs of a font
  // throttling compensates mass reflows on multiselect
  //

  this.modifiedGlyphs = ko.computed(function () {
    var glyphs = [];

    _.each(this.glyphs, function (glyph) {
      if (glyph.isModified()) {
        glyphs.push(glyph);
      }
    });

    return glyphs;
  }, this).extend({ throttle: 100 });
}

function FontsList() {
  this.fonts = _.map(embedded_fonts, function (data) {
    return new FontModel(data);
  });

  //
  // Returns array of selected glyphs from all fonts
  // throttling compensates mass reflows on multiselect
  //

  this.selectedGlyphs = ko.computed(function () {
    var glyphs = [];

    _.each(this.fonts, function (font) {
      glyphs = glyphs.concat(font.selectedGlyphs());
    });

    return glyphs;
  }, this).extend({ throttle: 100 });

  //
  // Returns amount of selected glyphs from all fonts
  //

  this.selectedCount = ko.computed(function () {
    return this.selectedGlyphs().length;
  }, this);

  //
  // Returns array of modified glyphs from all fonts
  // throttling compensates mass reflows on multiselect
  //

  this.modifiedGlyphs = ko.computed(function () {
    var glyphs = [];

    _.each(this.fonts, function (font) {
      glyphs = glyphs.concat(font.modifiedGlyphs());
    });

    return glyphs;
  }, this).extend({ throttle: 100 });
}

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

var fontsList = new FontsList();
var fontSize  = ko.observable(16);

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

N.on('font_size_change', fontSize);

var autoSaveSession = _.debounce(function () {
  var session = { fonts: {} };

  _.each(fontsList.fonts, function (font) {
    var font_data = { collapsed: false, glyphs: [] };

    _.each(font.glyphs, function (glyph) {
      if (glyph.isModified()) {
        font_data.glyphs.push({
          uid:        glyph.uid,
          orig_code:  glyph.originalCode,
          orig_css:   glyph.originalName,
          code:       glyph.code(),
          css:        glyph.name()
        });
      }
    });

    session.fonts[font.id] = font_data;
  });

  N.emit('session_save', session);
}, 500);

fontsList.modifiedGlyphs.subscribe(autoSaveSession);

N.on('session_load', function (session) {
  var fonts = session.fonts || [];

  _.each(fontsList.fonts, function (font) {
    if (!fonts[font.id]) {
      font.collapsed(false);
      return;
    }

    font.collapsed(!!fonts[font.id].collapsed);

    _.each(fonts[font.id].glyphs, function (glyph) {
      _.each(font.glyphs, function (g) {
        if (g.uid === glyph.uid) {
          g.selected(true);
          g.code(glyph.code || glyph.orig_code || g.originalCode);
          g.name(glyph.css || glyph.orig_css || g.originalName);
        }
      });
    });
  });
});

N.on('reset_selected', function () {
  _.each(fontsList.selectedGlyphs(), function (glyph) {
    glyph.selected(false);
  });
});

N.on('reset_all', function () {
  _.each(fontsList.modifiedGlyphs(), function (glyph) {
    glyph.selected(false);
    glyph.code(glyph.originalCode);
    glyph.name(glyph.originalName);
  });
});

$(function () {
  var $view = $(render('app.selector')).appendTo('#selector');

  //
  // Bind model and view
  //

  ko.applyBindings({
    fonts:    fontsList.fonts,
    fontSize: fontSize
  }, $view.get(0));

  //
  // Init multi-select of glyphs
  //

  $view.selectable({
    filter: 'li.glyph:visible',
    distance: 5,
    stop: function () {
      var $els = $view.find('.glyph.ui-selected');

      // prevent from double-triggering event,
      // otherwise click event will be fired as well
      if (1 === $els.length) {
        return;
      }

      $els.each(function () {
        ko.dataFor(this).toggleSelection();
      });
    }
  });
});

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

N.once('init_complete', function () {
  N.emit('fonts_ready', fontsList);
});
<commit_msg>Save collapsed state into session<commit_after>/*global $, _, ko, N*/

'use strict';

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

var
render          = require('../../../lib/render/client'),
embedded_fonts  = require('../../../lib/shared/embedded_fonts'),
glyphs_map      = require('../../../lib/shared/glyphs_map');

// Int to char, with fix for big numbers
// see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/fromCharCode
function fixedFromCharCode(code) {
  /*jshint bitwise: false*/
  if (code > 0xffff) {
    code -= 0x10000;
    var surrogate1 = 0xd800 + (code >> 10),
        surrogate2 = 0xdc00 + (code & 0x3ff);
    return String.fromCharCode(surrogate1, surrogate2);
  } else {
    return String.fromCharCode(code);
  }
}

// Char to Int, with fix for big numbers
function fixedCharCodeAt(char) {
  /*jshint bitwise: false*/
  var char1 = char.charCodeAt(0),
      char2 = char.charCodeAt(1);

  if ((char.length >= 2) &&
      ((char1 & 0xfc00) === 0xd800) &&
      ((char2 & 0xfc00) === 0xdc00)) {
    return 0x10000 + ((char1 - 0xd800) << 10) + (char2 - 0xdc00);
  } else {
    return char1;
  }
}

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

function GlyphModel(font, data) {

  //
  // Essential properties
  //

  this.uid              = data.uid;
  this.originalName     = data.css;
  this.originalCode     = data.code;

  //
  // Helper properties
  //

  this.font             = font;
  this.keywords         = (data.search || []).join(',');
  this.charRef          = fixedFromCharCode(glyphs_map[font.fontname][data.uid]);

  //
  // Actual properties state
  //

  this.selected         = ko.observable(false);
  this.name             = ko.observable(this.originalName);
  this.code             = ko.observable(this.originalCode);

  //
  // Helpers
  //

  this.toggleSelection  = function () {
    this.selected(!this.selected());
  }.bind(this);

  //
  // Whenever or not glyph should be treaten as "modified"
  //

  this.isModified       = ko.computed(function () {
    return  !!this.selected() ||
            this.name() !== this.originalName ||
            this.code() !== this.originalCode;
  }, this);

  //
  // code value as character (for code editor)
  //

  this.customChar       = ko.computed({
    read: function () {
      return fixedFromCharCode(this.code());
    },
    write: function (value) {
      this.code(fixedCharCodeAt(value));
    },
    owner: this
  });

  //
  // code value as hex-string (for code editor)
  //

  this.customHex        = ko.computed({
    read: function () {
      var code = this.code().toString(16).toUpperCase();
      return "0000".substr(0, Math.max(4 - code.length, 0)) + code;
    },
    write: function (value) {
      // value must be HEX string - omit invalid chars
      value = 0 + value.replace(/[^0-9a-f]+/g, '');
      this.code(parseInt(value, 16));
    },
    owner: this
  });

}

function FontModel(data) {

  //
  // Essential properties
  //

  this.id         = data.id;
  this.fullname   = data.font.fullname;
  this.fontname   = data.font.fontname;

  this.author     = data.meta.author;
  this.license    = data.meta.license;
  this.homepage   = data.meta.homepage;
  this.email      = data.meta.email;
  this.twitter    = data.meta.twitter;
  this.github     = data.meta.github;

  //
  // View state properties
  //

  this.collapsed  = ko.observable(false);

  //
  // Helpers
  //

  this.toggleCollapsed = function () {
    this.collapsed(!this.collapsed());
  }.bind(this);

  //
  // Array of font glyphs
  //

  this.glyphs     = _.map(data.glyphs, function (data) {
    return new GlyphModel(this, data);
  }, this);

  //
  // Returns array of selected glyphs of a font
  // throttling compensates mass reflows on multiselect
  //

  this.selectedGlyphs = ko.computed(function () {
    var glyphs = [];

    _.each(this.glyphs, function (glyph) {
      if (glyph.selected()) {
        glyphs.push(glyph);
      }
    });

    return glyphs;
  }, this).extend({ throttle: 100 });

  //
  // Returns array of modified glyphs of a font
  // throttling compensates mass reflows on multiselect
  //

  this.modifiedGlyphs = ko.computed(function () {
    var glyphs = [];

    _.each(this.glyphs, function (glyph) {
      if (glyph.isModified()) {
        glyphs.push(glyph);
      }
    });

    return glyphs;
  }, this).extend({ throttle: 100 });
}

function FontsList() {
  this.fonts = _.map(embedded_fonts, function (data) {
    return new FontModel(data);
  });

  //
  // Returns array of selected glyphs from all fonts
  // throttling compensates mass reflows on multiselect
  //

  this.selectedGlyphs = ko.computed(function () {
    var glyphs = [];

    _.each(this.fonts, function (font) {
      glyphs = glyphs.concat(font.selectedGlyphs());
    });

    return glyphs;
  }, this).extend({ throttle: 100 });

  //
  // Returns amount of selected glyphs from all fonts
  //

  this.selectedCount = ko.computed(function () {
    return this.selectedGlyphs().length;
  }, this);

  //
  // Returns array of modified glyphs from all fonts
  // throttling compensates mass reflows on multiselect
  //

  this.modifiedGlyphs = ko.computed(function () {
    var glyphs = [];

    _.each(this.fonts, function (font) {
      glyphs = glyphs.concat(font.modifiedGlyphs());
    });

    return glyphs;
  }, this).extend({ throttle: 100 });

  //
  // Returns whenever font list has modified glyphs or collapsed fonts
  // throttling compensates mass reflows on multiselect
  //

  this.modified = ko.computed(function () {
    var value = false;

    _.each(this.fonts, function (font) {
      value = font.collapsed() || value;
    });

    value = !!this.modifiedGlyphs().length || value;

    return value;
  }, this).extend({ throttle: 100 });
}

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

var fontsList = new FontsList();
var fontSize  = ko.observable(16);

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

N.on('font_size_change', fontSize);

var autoSaveSession = _.debounce(function () {
  var session = { fonts: {} };

  _.each(fontsList.fonts, function (font) {
    var font_data = { collapsed: font.collapsed(), glyphs: [] };

    _.each(font.glyphs, function (glyph) {
      if (glyph.isModified()) {
        font_data.glyphs.push({
          uid:        glyph.uid,
          orig_code:  glyph.originalCode,
          orig_css:   glyph.originalName,
          code:       glyph.code(),
          css:        glyph.name()
        });
      }
    });

    session.fonts[font.id] = font_data;
  });

  N.emit('session_save', session);
}, 500);

fontsList.modified.subscribe(autoSaveSession);

N.on('session_load', function (session) {
  var fonts = session.fonts || [];

  _.each(fontsList.fonts, function (font) {
    if (!fonts[font.id]) {
      font.collapsed(false);
      return;
    }

    font.collapsed(!!fonts[font.id].collapsed);

    _.each(fonts[font.id].glyphs, function (glyph) {
      _.each(font.glyphs, function (g) {
        if (g.uid === glyph.uid) {
          g.selected(true);
          g.code(glyph.code || glyph.orig_code || g.originalCode);
          g.name(glyph.css || glyph.orig_css || g.originalName);
        }
      });
    });
  });
});

N.on('reset_selected', function () {
  _.each(fontsList.selectedGlyphs(), function (glyph) {
    glyph.selected(false);
  });
});

N.on('reset_all', function () {
  _.each(fontsList.modifiedGlyphs(), function (glyph) {
    glyph.selected(false);
    glyph.code(glyph.originalCode);
    glyph.name(glyph.originalName);
  });
});

$(function () {
  var $view = $(render('app.selector')).appendTo('#selector');

  //
  // Bind model and view
  //

  ko.applyBindings({
    fonts:    fontsList.fonts,
    fontSize: fontSize
  }, $view.get(0));

  //
  // Init multi-select of glyphs
  //

  $view.selectable({
    filter: 'li.glyph:visible',
    distance: 5,
    stop: function () {
      var $els = $view.find('.glyph.ui-selected');

      // prevent from double-triggering event,
      // otherwise click event will be fired as well
      if (1 === $els.length) {
        return;
      }

      $els.each(function () {
        ko.dataFor(this).toggleSelection();
      });
    }
  });
});

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

N.once('init_complete', function () {
  N.emit('fonts_ready', fontsList);
});
<|endoftext|>


<commit_before>import { Meteor } from 'meteor/meteor';
import React from 'react';
import Subheader from 'material-ui/Subheader';
import Paper from 'material-ui/Paper';
import PaperCard from '../../layouts/paper-card';
import FlatButton from 'material-ui/FlatButton';

import ConfirmDialog from '../../layouts/confirm-dialog';
import RemoveGiveawayDialog from '../form/remove-giveaway-dialog';
import Link from '../../layouts/link';

import * as Colors from 'material-ui/styles/colors';
import { makeLink, pluralizer } from '../../../util/helper';
import * as GiveawaysHelper from '../../../util/giveaways';
import * as AvatarHelper from '../../../util/avatar';
import * as IconsHelper from '../../../util/icons';
import * as RolesHelper from '../../../util/roles';
import * as UsersHelper from '../../../util/users';

import { flagGiveaway, unflagGiveaway, removeFlaggedGiveaway, restoreGiveaway } from '../../../api/giveaways/methods';

let giveaway = null;

const iconRow = (icon, content) => {
  if (content)
    return (

          { IconsHelper.icon(icon, { fontSize: 18, lineHeight: "25px" }) }

{ content }

    );
};

const OwnerActions = ({ self }) => (

      <Link className="button" to={ "/my-giveaways/" + ga._id }>
        <FlatButton label="Edit" />
      </Link>

);

const NonOwnerActions = ({ self }) => {
  const userHasFlagged = GiveawaysHelper.userHasFlagged(ga, Meteor.user());

  return (

      { userHasFlagged ?

          You have flagged this giveaway for moderators' attention. We will be reviewing this giveaway shortly.

 : null }

        { Meteor.userId() && ga.userId !== Meteor.userId() && !userHasFlagged ?
          <FlatButton label="Flag" onTouchTap={ event => self.setState({ confirmFlagDialogOpen: true }) } />
          : null }

  );
};

const ModActions = ({ self }) => {
  const countFlags = GiveawaysHelper.countTotalFlags(ga);
  const isRemoved = ga.isRemoved;

  const FlagCounts = () => (

      This giveaway has been flagged {countFlags} { pluralizer(countFlags, 'time', 'times') }.

  );

  const DeletedBy = () => {
    const deleter = Meteor.users.findOne(ga.removeUserId);

    return (

        This giveaway was deleted by { UsersHelper.getFullName(deleter) } { moment(ga.removeDate).fromNow() }.

    );
  };

  const buttonsIfRemoved = <FlatButton label="Restore" onTouchTap={ event => self.setState({ confirmRestoreDialogOpen: true }) } />;

  const buttonsIfNotRemoved = [
    <Link className="button" to={ "/my-giveaways/" + ga._id }><FlatButton label="Edit" /></Link>
  ];

  if (countFlags > 0) {
    buttonsIfNotRemoved.push(<FlatButton label="Unflag" onTouchTap={ event => handleAction(unflagGiveaway, ga._id, 'Successfully removed all flags.') } />);
    buttonsIfNotRemoved.push(<FlatButton label="Delete" onTouchTap={ event => self.setState({ confirmRemoveDialogOpen: true }) } />);
  }

  return (

      { isRemoved ? <DeletedBy /> : countFlags > 0 ? <FlagCounts /> : null }

        { isRemoved ? buttonsIfRemoved : buttonsIfNotRemoved }

  );
};

const ConfirmFlagDialog = ({ open, handleSubmit, handleClose }) => (
  <ConfirmDialog
    title="Flag Giveaway"
    open={ open }
    handleClose={ handleClose }
    handleSubmit={ handleSubmit }>

You can bring this giveaway to a moderator's attention by flagging it. Flagging a giveaway is usually done for the following reasons:

Spam/Fakes: Giveaway does not exist and should be removed from the site.

Abusive: Giveaway contains abusive content and should be removed.

Duplicate: This giveaway is a duplicate of another one, and content should be merged.

Do leave a comment on the reason for flagging this giveaway.

Please confirm if you would like to flag this giveaway by clicking Submit.

  </ConfirmDialog>
);

const ConfirmRestoreDialog = ({ open, handleSubmit, handleClose }) => (
  <ConfirmDialog
    title="Restore this giveaway?"
    open={ open }
    handleClose={ handleClose }
    handleSubmit={ handleSubmit }>

Are you sure you wish to restore this giveaway from deletion?

  </ConfirmDialog>
);

const handleAction = (action, _id, notification) => {
  const userId = Meteor.userId();

  if (!_id)
    return false;

  action.call({ _id, userId }, (error) => {
    if (error) {
      Bert.alert(error.reason, 'danger');
    } else {
      Bert.alert(notification, 'success');
    }
  });
};

export class GiveawaySingleCard extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      confirmFlagDialogOpen: false,
      confirmRemoveDialogOpen: false,
      confirmRestoreDialogOpen: false,
    };
  }

  handleFlagGiveaway(event) {
    this.setState({ confirmFlagDialogOpen: false });
    handleAction(flagGiveaway, this.props.ga._id, 'Thanks for flagging, we will be reviewing this shortly.');
  }

  handleRemoveFlaggedGiveaway(event) {
    this.setState({ confirmRemoveDialogOpen: false });
    handleAction(removeFlaggedGiveaway, this.props.ga._id, 'Removed flagged giveaway.');
  }

  handleRestoreGiveaway(event) {
    this.setState({ confirmRestoreDialogOpen: false });
    handleAction(restoreGiveaway, this.props.ga._id, 'Successfully restored giveaway.');
  }

  render() {
    ga = this.props.ga;

    return (
      <PaperCard className="giveaway giveaway-single">

              { GiveawaysHelper.makeAvatarLegacy(ga, 350) }

{ ga.title }

{ GiveawaysHelper.categoryBreadcrumbs(ga) }

{ GiveawaysHelper.description(ga) }

            { iconRow("date_range", GiveawaysHelper.dateRange(ga)) }
            { iconRow("location_on", ga.location ) }
            { GiveawaysHelper.is_ongoing(ga) ? iconRow("info_outline", "Status: " + GiveawaysHelper.getLastOwnerStatusType(ga).label ) : null }
            { iconRow("link", makeLink(ga.website, "Website")) }

            { RolesHelper.modsOrAdmins(Meteor.user()) ? <ModActions self={this} /> :
              GiveawaysHelper.isCurrentUserOwner(ga)  ? <OwnerActions self={this} /> : <NonOwnerActions self={this} /> }

        <ConfirmFlagDialog
          open={ this.state.confirmFlagDialogOpen }
          handleClose={ event => this.setState({ confirmFlagDialogOpen: false }) }
          handleSubmit={ this.handleFlagGiveaway.bind(this) } />

        <RemoveGiveawayDialog
          open={ this.state.confirmRemoveDialogOpen }
          handleClose={ event => this.setState({ confirmRemoveDialogOpen: false }) }
          handleSubmit={ this.handleRemoveFlaggedGiveaway.bind(this) } />

        <ConfirmRestoreDialog
          open={ this.state.confirmRestoreDialogOpen }
          handleClose={ event => this.setState({ confirmRestoreDialogOpen: false }) }
          handleSubmit={ this.handleRestoreGiveaway.bind(this) } />
      </PaperCard>
    );
  }
}
<commit_msg>Fix unique key warnings for ModActions<commit_after>import { Meteor } from 'meteor/meteor';
import React from 'react';
import Subheader from 'material-ui/Subheader';
import Paper from 'material-ui/Paper';
import PaperCard from '../../layouts/paper-card';
import FlatButton from 'material-ui/FlatButton';

import ConfirmDialog from '../../layouts/confirm-dialog';
import RemoveGiveawayDialog from '../form/remove-giveaway-dialog';
import Link from '../../layouts/link';

import * as Colors from 'material-ui/styles/colors';
import { makeLink, pluralizer } from '../../../util/helper';
import * as GiveawaysHelper from '../../../util/giveaways';
import * as AvatarHelper from '../../../util/avatar';
import * as IconsHelper from '../../../util/icons';
import * as RolesHelper from '../../../util/roles';
import * as UsersHelper from '../../../util/users';

import { flagGiveaway, unflagGiveaway, removeFlaggedGiveaway, restoreGiveaway } from '../../../api/giveaways/methods';

let giveaway = null;

const iconRow = (icon, content) => {
  if (content)
    return (

          { IconsHelper.icon(icon, { fontSize: 18, lineHeight: "25px" }) }

{ content }

    );
};

const OwnerActions = ({ self }) => (

      <Link className="button" to={ "/my-giveaways/" + ga._id }>
        <FlatButton label="Edit" />
      </Link>

);

const NonOwnerActions = ({ self }) => {
  const userHasFlagged = GiveawaysHelper.userHasFlagged(ga, Meteor.user());

  return (

      { userHasFlagged ?

          You have flagged this giveaway for moderators' attention. We will be reviewing this giveaway shortly.

 : null }

        { Meteor.userId() && ga.userId !== Meteor.userId() && !userHasFlagged ?
          <FlatButton label="Flag" onTouchTap={ event => self.setState({ confirmFlagDialogOpen: true }) } />
          : null }

  );
};

const ModActions = ({ self }) => {
  const countFlags = GiveawaysHelper.countTotalFlags(ga);
  const isRemoved = ga.isRemoved;

  const FlagCounts = () => (

      This giveaway has been flagged {countFlags} { pluralizer(countFlags, 'time', 'times') }.

  );

  const DeletedBy = () => {
    const deleter = Meteor.users.findOne(ga.removeUserId);

    return (

        This giveaway was deleted by { UsersHelper.getFullName(deleter) } { moment(ga.removeDate).fromNow() }.

    );
  };

  const buttonsIfRemoved = <FlatButton label="Restore" onTouchTap={ event => self.setState({ confirmRestoreDialogOpen: true }) } />;

  const buttonsIfNotRemoved = [
    <Link key="edit" className="button" to={ "/my-giveaways/" + ga._id }><FlatButton label="Edit" /></Link>
  ];

  if (countFlags > 0) {
    buttonsIfNotRemoved.push(<FlatButton key="unflag" label="Unflag" onTouchTap={ event => handleAction(unflagGiveaway, ga._id, 'Successfully removed all flags.') } />);
    buttonsIfNotRemoved.push(<FlatButton key="delete" label="Delete" onTouchTap={ event => self.setState({ confirmRemoveDialogOpen: true }) } />);
  }

  return (

      { isRemoved ? <DeletedBy /> : countFlags > 0 ? <FlagCounts /> : null }

        { isRemoved ? buttonsIfRemoved : buttonsIfNotRemoved }

  );
};

const ConfirmFlagDialog = ({ open, handleSubmit, handleClose }) => (
  <ConfirmDialog
    title="Flag Giveaway"
    open={ open }
    handleClose={ handleClose }
    handleSubmit={ handleSubmit }>

You can bring this giveaway to a moderator's attention by flagging it. Flagging a giveaway is usually done for the following reasons:

Spam/Fakes: Giveaway does not exist and should be removed from the site.

Abusive: Giveaway contains abusive content and should be removed.

Duplicate: This giveaway is a duplicate of another one, and content should be merged.

Do leave a comment on the reason for flagging this giveaway.

Please confirm if you would like to flag this giveaway by clicking Submit.

  </ConfirmDialog>
);

const ConfirmRestoreDialog = ({ open, handleSubmit, handleClose }) => (
  <ConfirmDialog
    title="Restore this giveaway?"
    open={ open }
    handleClose={ handleClose }
    handleSubmit={ handleSubmit }>

Are you sure you wish to restore this giveaway from deletion?

  </ConfirmDialog>
);

const handleAction = (action, _id, notification) => {
  const userId = Meteor.userId();

  if (!_id)
    return false;

  action.call({ _id, userId }, (error) => {
    if (error) {
      Bert.alert(error.reason, 'danger');
    } else {
      Bert.alert(notification, 'success');
    }
  });
};

export class GiveawaySingleCard extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      confirmFlagDialogOpen: false,
      confirmRemoveDialogOpen: false,
      confirmRestoreDialogOpen: false,
    };
  }

  handleFlagGiveaway(event) {
    this.setState({ confirmFlagDialogOpen: false });
    handleAction(flagGiveaway, this.props.ga._id, 'Thanks for flagging, we will be reviewing this shortly.');
  }

  handleRemoveFlaggedGiveaway(event) {
    this.setState({ confirmRemoveDialogOpen: false });
    handleAction(removeFlaggedGiveaway, this.props.ga._id, 'Removed flagged giveaway.');
  }

  handleRestoreGiveaway(event) {
    this.setState({ confirmRestoreDialogOpen: false });
    handleAction(restoreGiveaway, this.props.ga._id, 'Successfully restored giveaway.');
  }

  render() {
    ga = this.props.ga;

    return (
      <PaperCard className="giveaway giveaway-single">

              { GiveawaysHelper.makeAvatarLegacy(ga, 350) }

{ ga.title }

{ GiveawaysHelper.categoryBreadcrumbs(ga) }

{ GiveawaysHelper.description(ga) }

            { iconRow("date_range", GiveawaysHelper.dateRange(ga)) }
            { iconRow("location_on", ga.location ) }
            { GiveawaysHelper.is_ongoing(ga) ? iconRow("info_outline", "Status: " + GiveawaysHelper.getLastOwnerStatusType(ga).label ) : null }
            { iconRow("link", makeLink(ga.website, "Website")) }

            { RolesHelper.modsOrAdmins(Meteor.user()) ? <ModActions self={this} /> :
              GiveawaysHelper.isCurrentUserOwner(ga)  ? <OwnerActions self={this} /> : <NonOwnerActions self={this} /> }

        <ConfirmFlagDialog
          open={ this.state.confirmFlagDialogOpen }
          handleClose={ event => this.setState({ confirmFlagDialogOpen: false }) }
          handleSubmit={ this.handleFlagGiveaway.bind(this) } />

        <RemoveGiveawayDialog
          open={ this.state.confirmRemoveDialogOpen }
          handleClose={ event => this.setState({ confirmRemoveDialogOpen: false }) }
          handleSubmit={ this.handleRemoveFlaggedGiveaway.bind(this) } />

        <ConfirmRestoreDialog
          open={ this.state.confirmRestoreDialogOpen }
          handleClose={ event => this.setState({ confirmRestoreDialogOpen: false }) }
          handleSubmit={ this.handleRestoreGiveaway.bind(this) } />
      </PaperCard>
    );
  }
}
<|endoftext|>


<commit_before>var hbs = require('handlebars')
var fs = require('fs')
var Github = require('github-api')

module.exports = function(callback) {

  fs.readFile( process.env['CONTRIBUTORS'], function (err, data) {
    if (err) return callback(err, "Error reading contribs file for building page.")

    var everyone = JSON.parse(data)
    var newest = everyone[everyone.length - 1]

    var stats = {featured: newest, everyone: everyone.reverse(), total: everyone.length}
    return getTemplate(stats, everyone)
  })

  function getTemplate(stats, everyone) {
    fs.readFile('template.hbs', function (err, data) {
      if (err) return callback(err, "Error reading template file.")

      data = data.toString()
      var template = hbs.compile(data)
      var HTML = template(stats)
      return writeRepo(HTML, stats, everyone)
    })
  }

  function writeRepo(HTML, stats, everyone) {
    var github = new Github({
      auth: "oauth",
      token: process.env['REPOROBOT_TOKEN']
    })

    var repo = github.getRepo('jlord', 'patchwork')
    var username = stats.featured.username
    var commitMes = "rebuilt with @" + username + " added\!"

    repo.write('gh-pages', 'index.html', HTML, commitMes, function(err) {
      if (err) return callback(err, "Error writing new index to Patchwork.")
      console.log(new Date(), "Rebuilt index with " + username)

      repo.write('gh-pages', 'contributors.json', JSON.stringify(everyone, null, " ") , "Added @" + username, function(err) {
        if (err) return callback(err, "Error writing contribs backup to Patchwork.")
        console.log(new Date(), "Wrote contributors backup with " + username)
        return callback(null, "Done!")
      })
    })
  }
}<commit_msg>this seems to work<commit_after>var hbs = require('handlebars')
var fs = require('fs')
var Github = require('github-api')
var request = require('request')
var btoa =  require('btoa')

module.exports = function(callback) {

  var github = new Github({
    auth: "oauth",
    token: process.env['REPOROBOT_TOKEN']
  })

  var repo = github.getRepo('jlord', 'patchwork')

  if (process.env['CONTRIBUTORS']) {
    fs.readFile( process.env['CONTRIBUTORS'], function (err, data) {
      if (err) return callback(err, "Error reading contribs file for building page.")
      organizeData(data)
    })
  } else {
    console.log("Making request for data...")
    var uri = "http://reporobot.jlord.us/data"
    request({url: uri, json: true}, function (err, res, body) {
      if (err) return callback(err, "Fetching latest data for building page")
      organizeData(JSON.stringify(body))
    })
  }

  function organizeData(data) {
    var everyone = JSON.parse(data)
    var newest = everyone[everyone.length - 1]
    var stats = {featured: newest, everyone: everyone.reverse(), total: everyone.length}
    return getTemplate(stats, everyone)
  }

  function getTemplate(stats, everyone) {
    fs.readFile('template.hbs', function (err, file) {
      if (err) return callback(err, "Error reading template file.")

      file = file.toString()
      var template = hbs.compile(file)
      var HTML = template(stats)
      return writeRepo(HTML, stats, everyone)
    })
  }

  function writeRepo(HTML, stats, everyone) {
    var username = stats.featured.user
    var commitMes = "rebuilt with @" + username + " added\!"
    var baseURL = 'https://api.github.com/repos/'

    var reqHeaders = {
      'User-Agent': 'request',
      'Authorization': 'token ' + process.env['REPOROBOT_TOKEN']
    }

    var options = {
      headers: reqHeaders,
      url: baseURL + "jlord/patchwork/contents/index.html",
      json: true,
      body: {
        "branch": "gh-pages",
        "committer": {
          "name": "reporobot",
          "email": "60ebe73fdad8ee59d45c@cloudmailin.net"
        },
        "sha": "",
        "content": btoa(HTML),
        "message": "Rebuilt index with " + username
      }
    }

    request.get(options, function(err, res, body) {
      if (err) return callback(err, "Error fetching SHA")
      options.body.sha = body.sha
      request.put(options, function(err, res, body) {
        if (err) return callback(err, "Error writing new index to Patchwork")
        console.log(new Date(), "Rebuilt index with " + username)
        })
    })
  }
}
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router';
import { push } from 'react-router-redux';

import actions from '../actions/index.js';
import { forkRecipe, fetchRecipes, fetchRecipe } from '../utils/utils';
import RecipeContainer from './RecipeContainer';
import '../scss/_main.scss';
import '../scss/_mainRecipe.scss';

import Like from './Like.js';
class MainRecipe extends Component {
  componentDidMount() {
    const { getRecipe, id } = this.props;
    if (id) {
      getRecipe(id);
    }
  }

  componentWillUpdate(nextProps) {
    const currId = this.props.id;
    const { getRecipe, id } = nextProps;
    if (+currId !== +id) {
      getRecipe(id);
    }
  }

  render() {
    const { user, toggleEdit, handleToggleEdit, recipe, onForkClick, historyRecipes, } = this.props;
    let forkButton;
    if (user.id) {
      forkButton = <button className="btn-fork" onClick={ onForkClick.bind(null, recipe.id, user.id) }>Fork</button>;
    } else {
      forkButton = <button className="btn-fork" disabled>Fork</button>;
    }

    let editButton;
    if (user.id === recipe.author && user.id !== null) {
      editButton = <button className="btn-toggle-edit" onClick={ handleToggleEdit }>Toggle Edit</button>;
    } else {
      editButton = <button className="btn-toggle-edit" disabled>Toggle Edit</button>
    }

    return (

        {/*
THIS IS THE USER: {user.displayName}

THIS IS THE RECIPE AUTHOR: {recipe.author}

THIS IS THE RECIPE PARENT: {recipe.parent}

THIS IS THE RECIPE HISTORY: {recipe.fork_history}

THIS IS THE RECIPE ID: {recipe.id}
*/}
        {editButton}
        {forkButton}
        <Like recipeID={recipe.id} userID={user.id} />
        {/*<Link to="/recipe/15">GO TO RECIPE 15</Link>*/}

{recipe.title}

                {recipe.images.map((image) =>

)}

                <RecipeContainer
                  className="fork-history"
                  type="Recipe History"
                  recipes={historyRecipes}
                />

Servings: {recipe.yield + ' ' + recipe.yield_unit}

 tags: {recipe.tags.map(t =>  {t} )}

 Ingredients

                {recipe.ingredients.map((i) =>
 {i}
)}

 Prep | Time: {recipe.prep_time}

              {recipe.prep_steps.map((s) =>
 {s}
)}

 Cook | Time: {recipe.cook_time}

              {recipe.cook_steps.map((s) =>
 {s}
)}

 Finish

              {recipe.finish_steps.map((s) =>
 {s}
)}

 Documentation

    );
  }
}

const mapStateToProps = (state, ownProps) => {
  return {
    toggleEdit: state.toggleEdit,
    user: state.user,
    recipe: state.recipe,
    fork_history: state.recipe.fork_history || [],
    historyRecipes: state.recipe.historyRecipes,
    id: ownProps.params.id,
  };
};

const mapDispatchToProps = (dispatch) => {
  return {
    onForkClick: (recipeID, userID) => {
      forkRecipe(recipeID, userID, (newRecipe) => {
        dispatch(actions.forkRecipe(newRecipe));
        dispatch(push(`/recipe/${newRecipe.id}`));
      });
    },
    handleToggleEdit: () => dispatch(actions.toggleEdit()),
    getRecipe: (recipeID) => {
      fetchRecipe(recipeID, (recipe) => {
        dispatch(actions.setRecipe(recipe));
        fetchRecipes(recipe.fork_history, (recipes) => {
          dispatch(actions.setRecipeHistory(recipes));
        });
      });
    },
  };
};

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(MainRecipe);
<commit_msg>change toggleEdit buttons to dispatch redirect to createRecipe page<commit_after>import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router';
import { push } from 'react-router-redux';

import actions from '../actions/index.js';
import { forkRecipe, fetchRecipes, fetchRecipe } from '../utils/utils';
import RecipeContainer from './RecipeContainer';
import '../scss/_main.scss';
import '../scss/_mainRecipe.scss';

import Like from './Like.js';
class MainRecipe extends Component {
  componentDidMount() {
    const { getRecipe, id } = this.props;
    if (id) {
      getRecipe(id);
    }
  }

  componentWillUpdate(nextProps) {
    const currId = this.props.id;
    const { getRecipe, id } = nextProps;
    if (+currId !== +id) {
      getRecipe(id);
    }
  }

  render() {
    const { user, navToEdit, recipe, onForkClick, historyRecipes, } = this.props;
    let forkButton;
    if (user.id) {
      forkButton = <button className="btn-fork" onClick={ onForkClick.bind(null, recipe.id, user.id) }>Fork</button>;
    } else {
      forkButton = <button className="btn-fork" disabled>Fork</button>;
    }

    let editButton;
    if (user.id === recipe.author && user.id !== null) {
      editButton = <button
      className="btn-toggle-edit"
      onClick={(e) => {
        e.preventDefault();
        e.stopPropagation();
        navToEdit(user);
      }}>Edit Recipe</button>;
    } else {
      editButton = <button className="btn-toggle-edit" disabled>Edit Recipe</button>
    }

    return (

        {/*
THIS IS THE USER: {user.displayName}

THIS IS THE RECIPE AUTHOR: {recipe.author}

THIS IS THE RECIPE PARENT: {recipe.parent}

THIS IS THE RECIPE HISTORY: {recipe.fork_history}

THIS IS THE RECIPE ID: {recipe.id}
*/}
        {editButton}
        {forkButton}
        <Like recipeID={recipe.id} userID={user.id} />
        {/*<Link to="/recipe/15">GO TO RECIPE 15</Link>*/}

{recipe.title}

                {recipe.images.map((image) =>

)}

                <RecipeContainer
                  className="fork-history"
                  type="Recipe History"
                  recipes={historyRecipes}
                />

Servings: {recipe.yield + ' ' + recipe.yield_unit}

 tags: {recipe.tags.map(t =>  {t} )}

 Ingredients

                {recipe.ingredients.map((i) =>
 {i}
)}

 Prep | Time: {recipe.prep_time}

              {recipe.prep_steps.map((s) =>
 {s}
)}

 Cook | Time: {recipe.cook_time}

              {recipe.cook_steps.map((s) =>
 {s}
)}

 Finish

              {recipe.finish_steps.map((s) =>
 {s}
)}

 Documentation

    );
  }
}

const mapStateToProps = (state, ownProps) => {
  return {

    user: state.user,
    recipe: state.recipe,
    fork_history: state.recipe.fork_history || [],
    historyRecipes: state.recipe.historyRecipes,
    id: ownProps.params.id,
  };
};

const mapDispatchToProps = (dispatch) => {
  return {
    onForkClick: (recipeID, userID) => {
      forkRecipe(recipeID, userID, (newRecipe) => {
        dispatch(actions.forkRecipe(newRecipe));
        dispatch(push(`/recipe/${newRecipe.id}`));
      });
    },

    navToEdit: (user) => {
      dispatch(push('/search'));
    },

    getRecipe: (recipeID) => {
      fetchRecipe(recipeID, (recipe) => {
        dispatch(actions.setRecipe(recipe));
        fetchRecipes(recipe.fork_history, (recipes) => {
          dispatch(actions.setRecipeHistory(recipes));
        });
      });
    },
  };
};

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(MainRecipe);
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

module.exports = function (BLOCK_INTERVAL_MS, MAX_BAD_LOGINS, now) {

  now = now || Date.now

  function IpEmailRecord() {
    this.xs = []
  }

  IpEmailRecord.parse = function (object) {
    var rec = new IpEmailRecord()
    object = object || {}
    rec.bk = object.bk
    rec.xs = object.xs || []
    return rec
  }

  IpEmailRecord.prototype.isOverBadLogins = function () {
    this.trimBadLogins(now())
    return this.xs.length > MAX_BAD_LOGINS
  }

  IpEmailRecord.prototype.addBadLogin = function () {
    this.trimBadLogins(now())
    this.xs.push(now())
  }

  IpEmailRecord.prototype.trimBadLogins = function (now) {
    if (this.xs.length === 0) { return }
    // xs is naturally ordered from oldest to newest
    // and we only need to keep up to MAX_BAD_LOGINS + 1

    // start at the end and go backwards until login is old
    // or we have
    var i = this.xs.length - 1
    var n = 0
    var login = this.xs[i]
    while (login > (now - BLOCK_INTERVAL_MS) && n <= MAX_BAD_LOGINS) {
      login = this.xs[--i]
      n++
    }
    this.xs = this.xs.slice(i + 1)
  }

  IpEmailRecord.prototype.isBlocked = function () {
    return !!(this.bk && (now() - this.bk < BLOCK_INTERVAL_MS))
  }

  IpEmailRecord.prototype.block = function () {
    this.bk = now()
    this.xs = []
  }

  IpEmailRecord.prototype.unblockIfReset = function (resetAt) {
    if (resetAt > this.bk) {
      this.xs = []
      delete this.bk
    }
  }

  IpEmailRecord.prototype.retryAfter = function () {
    return Math.max(0, Math.floor(((this.bk || 0) + BLOCK_INTERVAL_MS - now()) / 1000))
  }

  IpEmailRecord.prototype.update = function (action) {
    if (!(
      action === 'accountLogin' ||
      action === 'accountDestroy' ||
      action === 'passwordChange'
      )) { return 0 }
    if (!this.isBlocked()) {
      if (this.isOverBadLogins()) {
        this.block()
      }
      else {
        return 0
      }
    }
    return this.retryAfter()
  }

  return IpEmailRecord
}
<commit_msg>Move allowed actions to an array<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

module.exports = function (BLOCK_INTERVAL_MS, MAX_BAD_LOGINS, now) {

  now = now || Date.now

  var IP_EMAIL_ACTIONS = [
    'accountLogin',
    'accountDestroy',
    'passwordChange'
  ]

  function IpEmailRecord() {
    this.xs = []
  }

  IpEmailRecord.parse = function (object) {
    var rec = new IpEmailRecord()
    object = object || {}
    rec.bk = object.bk
    rec.xs = object.xs || []
    return rec
  }

  IpEmailRecord.prototype.isOverBadLogins = function () {
    this.trimBadLogins(now())
    return this.xs.length > MAX_BAD_LOGINS
  }

  IpEmailRecord.prototype.addBadLogin = function () {
    this.trimBadLogins(now())
    this.xs.push(now())
  }

  IpEmailRecord.prototype.trimBadLogins = function (now) {
    if (this.xs.length === 0) { return }
    // xs is naturally ordered from oldest to newest
    // and we only need to keep up to MAX_BAD_LOGINS + 1

    // start at the end and go backwards until login is old
    // or we have
    var i = this.xs.length - 1
    var n = 0
    var login = this.xs[i]
    while (login > (now - BLOCK_INTERVAL_MS) && n <= MAX_BAD_LOGINS) {
      login = this.xs[--i]
      n++
    }
    this.xs = this.xs.slice(i + 1)
  }

  IpEmailRecord.prototype.isBlocked = function () {
    return !!(this.bk && (now() - this.bk < BLOCK_INTERVAL_MS))
  }

  IpEmailRecord.prototype.block = function () {
    this.bk = now()
    this.xs = []
  }

  IpEmailRecord.prototype.unblockIfReset = function (resetAt) {
    if (resetAt > this.bk) {
      this.xs = []
      delete this.bk
    }
  }

  IpEmailRecord.prototype.retryAfter = function () {
    return Math.max(0, Math.floor(((this.bk || 0) + BLOCK_INTERVAL_MS - now()) / 1000))
  }

  IpEmailRecord.prototype.update = function (action) {
    if (IP_EMAIL_ACTIONS.indexOf(action) === -1) {
      return 0
    }

    if (!this.isBlocked()) {
      if (this.isOverBadLogins()) {
        this.block()
      }
      else {
        return 0
      }
    }
    return this.retryAfter()
  }

  return IpEmailRecord
}
<|endoftext|>


<commit_before>import Vue from 'vue'
import VueRouter from 'vue-router'
import VueMeta from 'vue-meta'

Vue.use(VueRouter)
Vue.use(VueMeta)

import Footer from '../components/Footer'
Vue.component('my-footer', Footer)

// Can't make a array reduce or the number of chunks will increase twice,
// because System.import is ES6 module, which requires a static module imports
const BlogPager = process.BROWSER
  ? () => System.import('../components/BlogPager')
  : require('../components/BlogPager')
const Archive = process.BROWSER
  ? () => System.import('../components/Archive')
  : require('../components/Archive')
const Tag = process.BROWSER
  ? () => System.import('../components/Tag')
  : require('../components/Tag')
const PostContainer = process.BROWSER
  ? () => System.import('../components/PostContainer')
  : require('../components/PostContainer')
const TagPager = process.BROWSER
  ? () => System.import('../components/TagPager')
  : require('../components/TagPager')
const PageContainer = process.BROWSER
  ? () => System.import('../components/PageContainer')
  : require('../components/PageContainer')

export default new VueRouter({
  mode: 'history',
  scrollBehavior: function (to, from, savedPosition) {
    if (savedPosition) {
      return savedPosition
    } else {
      let position = {x: 0, y: 0}
      if (to.hash) {
        position = { selector: to.hash }
      }
      return position
    }
  },
  routes: [
    {
      path: '/',
      name: 'main',
      component: BlogPager
    },
    {
      path: '/archive',
      name: 'archive',
      component: Archive
    },
    {
      path: '/tag',
      name: 'tag',
      component: Tag
    },
    {
      path: '/post/:pathName',
      name: 'post',
      component: PostContainer
    },
    {
      path: '/tag/:tagName',
      name: 'tagPager',
      component: TagPager
    },
    {
      path: '/:page',
      name: 'page',
      component: PageContainer
    }
  ]
})
<commit_msg>[前端-前台] 按需加载标签详情页<commit_after>import Vue from 'vue'
import VueRouter from 'vue-router'
import VueMeta from 'vue-meta'

Vue.use(VueRouter)
Vue.use(VueMeta)

import Footer from '../components/Footer'
Vue.component('my-footer', Footer)

const BlogPager = require('../components/BlogPager')
const Archive = require('../components/Archive')
const Tag = require('../components/Tag')
const PostContainer = require('../components/PostContainer')
const PageContainer = require('../components/PageContainer')

// System.import is of ES6 module, which is designed to be statically analyzable
// It means that you can't write `let path = './main.js'; System.import(path)`
// Must use it by `System.import('./main.js')`
const TagPager = process.BROWSER
  ? () => System.import('../components/TagPager')
  : require('../components/TagPager')

export default new VueRouter({
  mode: 'history',
  scrollBehavior: function (to, from, savedPosition) {
    if (savedPosition) {
      return savedPosition
    } else {
      let position = {x: 0, y: 0}
      if (to.hash) {
        position = { selector: to.hash }
      }
      return position
    }
  },
  routes: [
    {
      path: '/',
      name: 'main',
      component: BlogPager
    },
    {
      path: '/archive',
      name: 'archive',
      component: Archive
    },
    {
      path: '/tag',
      name: 'tag',
      component: Tag
    },
    {
      path: '/post/:pathName',
      name: 'post',
      component: PostContainer
    },
    {
      path: '/tag/:tagName',
      name: 'tagPager',
      component: TagPager
    },
    {
      path: '/:page',
      name: 'page',
      component: PageContainer
    }
  ]
})
<|endoftext|>


<commit_before>s.Turret = new Class({
	extend: s.Projectile,

	construct: function(options){
        // Handle parameters
        this.color = s.config.weapons.turret.color[options.team];
        this.velocity = s.config.weapons.turret.velocity;

        // Add a collision mesh
        this.addCollisionMesh(new THREE.SphereGeometry(16));

        // Draw the projectile to the screen
        this.geometry = new THREE.SphereGeometry(12,20,20);
        this.material = new THREE.MeshBasicMaterial({color: this.color});
        this.root.add(new THREE.Mesh(this.geometry, this.material, 0.1));

        // Position the projectile relative to the ship
        this.root.position.copy(options.position);
        this.root.rotation.copy(options.rotation);
	}
});
<commit_msg>Using sprites for turrets<commit_after>s.Turret = new Class({
	extend: s.Projectile,

	construct: function(options){
        // Handle parameters
        this.color = s.config.weapons.turret.color[options.team];
        this.velocity = s.config.weapons.turret.velocity;

        // Add a collision mesh
        this.addCollisionMesh(new THREE.SphereGeometry(16));

        // Draw the projectile to the screen
        var spriteImg = new THREE.ImageUtils.loadTexture("game/textures/particle.png");
        var sprite = new THREE.Sprite(new THREE.SpriteMaterial({
            map: spriteImg,
            useScreenCoordinates: false,
            blending: THREE.AdditiveBlending,
            color: this.color
        }));
        // sprite.position.set(10,10,0);
        sprite.scale.set(50,50,1.0);
        this.root.add(sprite);

        // add point lights
        var pointLight = new THREE.SpotLight(0xffffff);
        this.root.add(pointLight);

        // Position the projectile relative to the ship
        this.root.position.copy(options.position);
        this.root.rotation.copy(options.rotation);
	}
});
<|endoftext|>