<commit_before>// Copyright 2016, University of Colorado Boulder

/**
 *
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Matt Pennington (PhET Interactive Simulations)
 */
/* eslint-env node */
'use strict';

// modules
var fs = require( 'fs' );
var copyDirectory = require( './copyDirectory' );
var ChipperStringUtils = require( '../../common/ChipperStringUtils' );
var ChipperConstants = require( '../../common/ChipperConstants' );
// var generatePhETIOAPIDocs = require( './generatePhETIOAPIDocs' );

// constants
var WRAPPER_PREFIX = 'phet-io-wrapper-';

module.exports = function( grunt, buildConfig ) {

  // TODO: chipper#101 eek, this is scary! we are importing from the node_modules dir. ideally we should just have uglify-js installed once in sherpa?
  var uglify = require( '../../../node_modules/uglify-js/tools/node' );// eslint-disable-line require-statement-match

  // output the SimIFrameClient.js and WrapperUtils.js to the top level lib (not password-protected), see https://github.com/phetsims/phet-io/issues/353
  grunt.file.mkdir( ChipperConstants.BUILD_DIR + '/lib' );

  var filterWrapper = function( abspath, contents ) {
    var originalContents = contents + '';

    if ( abspath.indexOf( '.js' ) >= 0 || abspath.indexOf( '.html' ) >= 0 ) {
      contents = ChipperStringUtils.replaceAll( contents, '{{SIMULATION_NAME}}', buildConfig.name );
      contents = ChipperStringUtils.replaceAll( contents, '{{SIMULATION_VERSION}}', buildConfig.version );
    }
    if ( abspath.indexOf( '.html' ) >= 0 ) {

      contents = ChipperStringUtils.replaceAll( contents,
        '"../../sherpa/lib/lodash-4.17.4.min.js"',
        '"https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.11.2/lodash.min.js"'
      );
      contents = ChipperStringUtils.replaceAll( contents,
        '"../../sherpa/lib/font-awesome-4.5.0/css/font-awesome.min.css"',
        '"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.min.css"'
      );
      contents = ChipperStringUtils.replaceAll( contents,
        '"../../sherpa/lib/jquery-2.1.0.min.js"',
        '"https://code.jquery.com/jquery-2.2.3.min.js"'
      );
      contents = ChipperStringUtils.replaceAll( contents,
        '"../../sherpa/lib/jquery-ui-1.8.24.min.js"',
        '"https://code.jquery.com/ui/1.8.24/jquery-ui.min.js"'
      );
      contents = ChipperStringUtils.replaceAll( contents,
        '"../../sherpa/lib/d3-4.2.2.js"',
        '"https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.2/d3.min.js"'
      );

      //TODO: these probably don't need to be repeated for a different path
      contents = ChipperStringUtils.replaceAll( contents,
        '"../sherpa/lib/lodash-4.17.4.min.js"',
        '"https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.11.2/lodash.min.js"'
      );
      contents = ChipperStringUtils.replaceAll( contents,
        '"../sherpa/lib/font-awesome-4.5.0/css/font-awesome.min.css"',
        '"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.min.css"'
      );
      contents = ChipperStringUtils.replaceAll( contents,
        '"../sherpa/lib/jquery-2.1.0.min.js"',
        '"https://code.jquery.com/jquery-2.2.3.min.js"'
      );
      contents = ChipperStringUtils.replaceAll( contents,
        '"../sherpa/lib/jquery-ui-1.8.24.min.js"',
        '"https://code.jquery.com/ui/1.8.24/jquery-ui.min.js"'
      );
      contents = ChipperStringUtils.replaceAll( contents,
        '"../sherpa/lib/d3-4.2.2.js"',
        '"https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.2/d3.min.js"'
      );

      /*
       * Remove individual common code imports because they are all in phetio.js
       */
      // This returns the whole line that contains this substring, so it can be removed
      var firstQueryStringLine = ChipperStringUtils.firstLineThatContains( contents, 'QueryStringMachine.js">' );
      // Don't remove the import if it is coming from the phet-io website, only if it is a relative path in requirejs mode.
      if ( firstQueryStringLine && firstQueryStringLine.indexOf( 'phet-io.colorado.edu' ) === -1 ) {
        // Replace queryStringMachine with an empty line because 'phetio.js' has it already.
        contents = ChipperStringUtils.replaceAll( contents, firstQueryStringLine, '' );
      }
      var firstWrapperUtilsLine = ChipperStringUtils.firstLineThatContains( contents, 'WrapperUtils.js">' );
      if ( firstWrapperUtilsLine && firstWrapperUtilsLine.indexOf( 'phet-io.colorado.edu' ) === -1 ) {
        // Replace queryStringMachine with an empty line because 'phetio.js' has it already.
        contents = ChipperStringUtils.replaceAll( contents, firstWrapperUtilsLine, '' );
      }
      var firstAssertLine = ChipperStringUtils.firstLineThatContains( contents, 'assert.js">' );
      if ( firstAssertLine && firstAssertLine.indexOf( 'phet-io.colorado.edu' ) === -1 ) {
        // Replace queryStringMachine with an empty line because 'phetio.js' has it already.
        contents = ChipperStringUtils.replaceAll( contents, firstAssertLine, '' );
      }
      var firstIFrameClientLine = ChipperStringUtils.firstLineThatContains( contents, 'SimIFrameClient.js">' );
      if ( firstIFrameClientLine && firstIFrameClientLine.indexOf( 'phet-io.colorado.edu' ) === -1 ) {
        // Replace queryStringMachine with an empty line because 'phetio.js' has it already.
        contents = ChipperStringUtils.replaceAll( contents, firstIFrameClientLine, '' );
      }
      var firstWrapperTypeLine = ChipperStringUtils.firstLineThatContains( contents, 'WrapperTypes.js">' );
      if ( firstWrapperTypeLine && firstWrapperTypeLine.indexOf( 'phet-io.colorado.edu' ) === -1 ) {
        // Replace queryStringMachine with an empty line because 'phetio.js' has it already.
        contents = ChipperStringUtils.replaceAll( contents, firstWrapperTypeLine, '' );
      }

      // For info about phetio.js, see the end of this file
      contents = ChipperStringUtils.replaceAll( contents,
        '<!--{{phetio.js}}-->',
        '<script type="text/javascript" src="../../lib/phetio.js"></script>'
      );
      contents = ChipperStringUtils.replaceAll( contents,
        '<!--{{GOOGLE_ANALYTICS.js}}-->',
        '<script type="text/javascript" src="/assets/js/phet-io-ga.js"></script>'
      );
      contents = ChipperStringUtils.replaceAll( contents,
        '<!--{{FAVICON.ico}}-->',
        '<link rel="shortcut icon" href="/assets/favicon.ico">'
      );

      // phet-io-wrappers/common will be in the top level of wrappers/ in the build directory
      contents = ChipperStringUtils.replaceAll( contents, 'phet-io-wrappers/common/', 'common/' );
    }
    if ( contents !== originalContents ) {
      return contents;
    }
    else {
      return null; // signify no change (helps for images)
    }
  };

  /**
   * Dynamically generate a list of all current wrappers that have a dedicated github repository so that we can add them
   * to the build.
   * @returns {string[]} - a list of wrapper names, i.e. the repo 'phet-io-wrapper-classroom-activity' will return 'classroom-activity'
   */
  function findAllDedicatedWrapperRepos() {
    var wrapperRepos = [];
    var repos = fs.readdirSync( '../' );
    repos.forEach( function( repo ) {

      // If the repo begins with the wrapper prefix
      if ( repo.indexOf( WRAPPER_PREFIX ) === 0 ) {
        wrapperRepos.push( repo.split( WRAPPER_PREFIX )[ 1 ] );
      }
    } );
    return wrapperRepos;
  }

  var wrapperSuiteBlacklist = [ '.git', 'README.md', '.gitignore' ];

  // Files from the phet-io-wrappers repo
  var wrappersFromSuite = fs.readdirSync( '../phet-io-wrappers' );

  // Copy each wrapper from the wrapper suite
  wrappersFromSuite.forEach( function( repo ) {

    // Don't copy over anything from blacklist
    if ( wrapperSuiteBlacklist.indexOf( repo ) < 0 ) {
      copyDirectory( grunt, '../phet-io-wrappers/' + repo, ChipperConstants.BUILD_DIR + '/wrappers/' + repo, filterWrapper, { blacklist: wrapperSuiteBlacklist } );
    }
  } );

  // a list of the rest of the phet-io wrappers that aren't in the phet-io-wrappers suite
  var dedicatedWrapperRepos = findAllDedicatedWrapperRepos();

  dedicatedWrapperRepos.forEach( function( repo ) {
    // Copy each wrapper's content into a dedicated folder under 'build/wrappers.' As a sibling to other wrappers in the suite
    copyDirectory( grunt, '../' + WRAPPER_PREFIX + repo, ChipperConstants.BUILD_DIR + '/wrappers/' + repo, filterWrapper, { blacklist: wrapperSuiteBlacklist } );
  } );

  var devguideHTML = grunt.file.read( '../phet-io-website/root/devguide/index.html' );
  devguideHTML = ChipperStringUtils.replaceAll( devguideHTML, '../assets/bootstrap-3.3.6-dist/css/bootstrap.min.css', 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css' );
  devguideHTML = ChipperStringUtils.replaceAll( devguideHTML, '../assets/bootstrap-3.3.6-dist/js/bootstrap.min.js', 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js' );
  devguideHTML = ChipperStringUtils.replaceAll( devguideHTML, '../assets/font-awesome-4.5.0/css/font-awesome.min.css', 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.min.css' );
  devguideHTML = ChipperStringUtils.replaceAll( devguideHTML, '../assets/js/jquery-1.12.3.min.js', 'https://code.jquery.com/jquery-1.12.3.min.js' );
  devguideHTML = ChipperStringUtils.replaceAll( devguideHTML, '../assets/css/', './css/' );
  devguideHTML = ChipperStringUtils.replaceAll( devguideHTML, '../assets/js/', './js/' );
  devguideHTML = ChipperStringUtils.replaceAll( devguideHTML, '../assets/highlight.js-9.1.0/styles/tomorrow-night-bright.css', 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.1.0/styles/tomorrow-night-bright.min.css' );
  devguideHTML = ChipperStringUtils.replaceAll( devguideHTML, '../assets/highlight.js-9.1.0/highlight.js', 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.1.0/highlight.min.js' );
  devguideHTML = ChipperStringUtils.replaceAll( devguideHTML, '../assets/favicon.ico', './favicon.ico' );

  // Remove the navbar and footer div tags
  var firstNavbarLine = ChipperStringUtils.firstLineThatContains( devguideHTML, 'id="navbar"' );
  devguideHTML = firstNavbarLine ? ChipperStringUtils.replaceAll( devguideHTML, firstNavbarLine, '' ) : devguideHTML;

  var firstFooterLine = ChipperStringUtils.firstLineThatContains( devguideHTML, 'id="footer"' );
  devguideHTML = firstFooterLine ? ChipperStringUtils.replaceAll( devguideHTML, firstFooterLine, '' ) : devguideHTML;

  grunt.file.write( ChipperConstants.BUILD_DIR + '/docs/devguide.html', devguideHTML );
  copyDirectory( grunt, '../phet-io-website/root/assets/css', ChipperConstants.BUILD_DIR + '/docs/css', filterWrapper );
  grunt.file.copy( '../phet-io-website/root/assets/js/phet-io.js', './' + ChipperConstants.BUILD_DIR + '/docs/js/phet-io.js' );
  grunt.file.copy( '../phet-io-website/root/assets/js/phet-io-ga.js', './' + ChipperConstants.BUILD_DIR + '/docs/js/phet-io-ga.js' );
  grunt.file.copy( '../phet-io-website/root/assets/favicon.ico', './' + ChipperConstants.BUILD_DIR + '/docs/favicon.ico' );

  // Minify phet libraries into 'lib/phetio.js'
  var DESTINATION_PATH = ChipperConstants.BUILD_DIR + '/lib';
  var OUTPUT_FILE = 'phetio.js';
  var COPYRIGHT_HEADER = '// Copyright 2002-2017, University of Colorado Boulder\n' +
                         '// This PhET-iO file requires a license\n' +
                         '// USE WITHOUT A LICENSE AGREEMENT IS STRICTLY PROHIBITED.\n' +
                         '// For licensing, please contact phethelp@colorado.edu';

  var LIB_FILES = [
    '../query-string-machine/js/QueryStringMachine.js',
    '../phet-io-wrappers/common/js/SimIFrameClient.js',
    '../phet-io-wrappers/common/js/WrapperTypes.js',
    '../phet-io-wrappers/common/js/assert.js',
    '../phet-io-wrappers/common/js/WrapperUtils.js' ];

  var minified = uglify.minify( LIB_FILES, {
    mangle: true,
    output: {
      inline_script: true, // escape </script
      beautify: false
    },
    compress: {
      global_defs: {}
    }
  } ).code;

  grunt.file.write( DESTINATION_PATH + '/' + OUTPUT_FILE, COPYRIGHT_HEADER + '\n\n' + minified );

  // Generate API Documentation
  // TODO: these are broken right now, the cause has something to do with the string plugin loading chipper's localeInfo.js
  // TODO: see https://github.com/phetsims/phet-io/issues/972 for more info
  // generatePhETIOAPIDocs( grunt, buildConfig );
};<commit_msg>don't copy node_modules or package.json in phet-io wrapper build, https://github.com/phetsims/chipper/issues/584<commit_after>// Copyright 2016, University of Colorado Boulder

/**
 *
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Matt Pennington (PhET Interactive Simulations)
 */
/* eslint-env node */
'use strict';

// modules
var fs = require( 'fs' );
var copyDirectory = require( './copyDirectory' );
var ChipperStringUtils = require( '../../common/ChipperStringUtils' );
var ChipperConstants = require( '../../common/ChipperConstants' );
// var generatePhETIOAPIDocs = require( './generatePhETIOAPIDocs' );

// constants
var WRAPPER_PREFIX = 'phet-io-wrapper-';

module.exports = function( grunt, buildConfig ) {

  // TODO: chipper#101 eek, this is scary! we are importing from the node_modules dir. ideally we should just have uglify-js installed once in sherpa?
  var uglify = require( '../../../node_modules/uglify-js/tools/node' );// eslint-disable-line require-statement-match

  // output the SimIFrameClient.js and WrapperUtils.js to the top level lib (not password-protected), see https://github.com/phetsims/phet-io/issues/353
  grunt.file.mkdir( ChipperConstants.BUILD_DIR + '/lib' );

  var filterWrapper = function( abspath, contents ) {
    var originalContents = contents + '';

    if ( abspath.indexOf( '.js' ) >= 0 || abspath.indexOf( '.html' ) >= 0 ) {
      contents = ChipperStringUtils.replaceAll( contents, '{{SIMULATION_NAME}}', buildConfig.name );
      contents = ChipperStringUtils.replaceAll( contents, '{{SIMULATION_VERSION}}', buildConfig.version );
    }
    if ( abspath.indexOf( '.html' ) >= 0 ) {

      contents = ChipperStringUtils.replaceAll( contents,
        '"../../sherpa/lib/lodash-4.17.4.min.js"',
        '"https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.11.2/lodash.min.js"'
      );
      contents = ChipperStringUtils.replaceAll( contents,
        '"../../sherpa/lib/font-awesome-4.5.0/css/font-awesome.min.css"',
        '"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.min.css"'
      );
      contents = ChipperStringUtils.replaceAll( contents,
        '"../../sherpa/lib/jquery-2.1.0.min.js"',
        '"https://code.jquery.com/jquery-2.2.3.min.js"'
      );
      contents = ChipperStringUtils.replaceAll( contents,
        '"../../sherpa/lib/jquery-ui-1.8.24.min.js"',
        '"https://code.jquery.com/ui/1.8.24/jquery-ui.min.js"'
      );
      contents = ChipperStringUtils.replaceAll( contents,
        '"../../sherpa/lib/d3-4.2.2.js"',
        '"https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.2/d3.min.js"'
      );

      //TODO: these probably don't need to be repeated for a different path
      contents = ChipperStringUtils.replaceAll( contents,
        '"../sherpa/lib/lodash-4.17.4.min.js"',
        '"https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.11.2/lodash.min.js"'
      );
      contents = ChipperStringUtils.replaceAll( contents,
        '"../sherpa/lib/font-awesome-4.5.0/css/font-awesome.min.css"',
        '"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.min.css"'
      );
      contents = ChipperStringUtils.replaceAll( contents,
        '"../sherpa/lib/jquery-2.1.0.min.js"',
        '"https://code.jquery.com/jquery-2.2.3.min.js"'
      );
      contents = ChipperStringUtils.replaceAll( contents,
        '"../sherpa/lib/jquery-ui-1.8.24.min.js"',
        '"https://code.jquery.com/ui/1.8.24/jquery-ui.min.js"'
      );
      contents = ChipperStringUtils.replaceAll( contents,
        '"../sherpa/lib/d3-4.2.2.js"',
        '"https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.2/d3.min.js"'
      );

      /*
       * Remove individual common code imports because they are all in phetio.js
       */
      // This returns the whole line that contains this substring, so it can be removed
      var firstQueryStringLine = ChipperStringUtils.firstLineThatContains( contents, 'QueryStringMachine.js">' );
      // Don't remove the import if it is coming from the phet-io website, only if it is a relative path in requirejs mode.
      if ( firstQueryStringLine && firstQueryStringLine.indexOf( 'phet-io.colorado.edu' ) === -1 ) {
        // Replace queryStringMachine with an empty line because 'phetio.js' has it already.
        contents = ChipperStringUtils.replaceAll( contents, firstQueryStringLine, '' );
      }
      var firstWrapperUtilsLine = ChipperStringUtils.firstLineThatContains( contents, 'WrapperUtils.js">' );
      if ( firstWrapperUtilsLine && firstWrapperUtilsLine.indexOf( 'phet-io.colorado.edu' ) === -1 ) {
        // Replace queryStringMachine with an empty line because 'phetio.js' has it already.
        contents = ChipperStringUtils.replaceAll( contents, firstWrapperUtilsLine, '' );
      }
      var firstAssertLine = ChipperStringUtils.firstLineThatContains( contents, 'assert.js">' );
      if ( firstAssertLine && firstAssertLine.indexOf( 'phet-io.colorado.edu' ) === -1 ) {
        // Replace queryStringMachine with an empty line because 'phetio.js' has it already.
        contents = ChipperStringUtils.replaceAll( contents, firstAssertLine, '' );
      }
      var firstIFrameClientLine = ChipperStringUtils.firstLineThatContains( contents, 'SimIFrameClient.js">' );
      if ( firstIFrameClientLine && firstIFrameClientLine.indexOf( 'phet-io.colorado.edu' ) === -1 ) {
        // Replace queryStringMachine with an empty line because 'phetio.js' has it already.
        contents = ChipperStringUtils.replaceAll( contents, firstIFrameClientLine, '' );
      }
      var firstWrapperTypeLine = ChipperStringUtils.firstLineThatContains( contents, 'WrapperTypes.js">' );
      if ( firstWrapperTypeLine && firstWrapperTypeLine.indexOf( 'phet-io.colorado.edu' ) === -1 ) {
        // Replace queryStringMachine with an empty line because 'phetio.js' has it already.
        contents = ChipperStringUtils.replaceAll( contents, firstWrapperTypeLine, '' );
      }

      // For info about phetio.js, see the end of this file
      contents = ChipperStringUtils.replaceAll( contents,
        '<!--{{phetio.js}}-->',
        '<script type="text/javascript" src="../../lib/phetio.js"></script>'
      );
      contents = ChipperStringUtils.replaceAll( contents,
        '<!--{{GOOGLE_ANALYTICS.js}}-->',
        '<script type="text/javascript" src="/assets/js/phet-io-ga.js"></script>'
      );
      contents = ChipperStringUtils.replaceAll( contents,
        '<!--{{FAVICON.ico}}-->',
        '<link rel="shortcut icon" href="/assets/favicon.ico">'
      );

      // phet-io-wrappers/common will be in the top level of wrappers/ in the build directory
      contents = ChipperStringUtils.replaceAll( contents, 'phet-io-wrappers/common/', 'common/' );
    }
    if ( contents !== originalContents ) {
      return contents;
    }
    else {
      return null; // signify no change (helps for images)
    }
  };

  /**
   * Dynamically generate a list of all current wrappers that have a dedicated github repository so that we can add them
   * to the build.
   * @returns {string[]} - a list of wrapper names, i.e. the repo 'phet-io-wrapper-classroom-activity' will return 'classroom-activity'
   */
  function findAllDedicatedWrapperRepos() {
    var wrapperRepos = [];
    var repos = fs.readdirSync( '../' );
    repos.forEach( function( repo ) {

      // If the repo begins with the wrapper prefix
      if ( repo.indexOf( WRAPPER_PREFIX ) === 0 ) {
        wrapperRepos.push( repo.split( WRAPPER_PREFIX )[ 1 ] );
      }
    } );
    return wrapperRepos;
  }

  var wrapperSuiteBlacklist = [ '.git', 'README.md', '.gitignore', 'node_modules', 'package.json' ];

  // Files from the phet-io-wrappers repo
  var wrappersFromSuite = fs.readdirSync( '../phet-io-wrappers' );

  // Copy each wrapper from the wrapper suite
  wrappersFromSuite.forEach( function( repo ) {

    // Don't copy over anything from blacklist
    if ( wrapperSuiteBlacklist.indexOf( repo ) < 0 ) {
      copyDirectory( grunt, '../phet-io-wrappers/' + repo, ChipperConstants.BUILD_DIR + '/wrappers/' + repo, filterWrapper, { blacklist: wrapperSuiteBlacklist } );
    }
  } );

  // a list of the rest of the phet-io wrappers that aren't in the phet-io-wrappers suite
  var dedicatedWrapperRepos = findAllDedicatedWrapperRepos();

  dedicatedWrapperRepos.forEach( function( repo ) {
    // Copy each wrapper's content into a dedicated folder under 'build/wrappers.' As a sibling to other wrappers in the suite
    copyDirectory( grunt, '../' + WRAPPER_PREFIX + repo, ChipperConstants.BUILD_DIR + '/wrappers/' + repo, filterWrapper, { blacklist: wrapperSuiteBlacklist } );
  } );

  var devguideHTML = grunt.file.read( '../phet-io-website/root/devguide/index.html' );
  devguideHTML = ChipperStringUtils.replaceAll( devguideHTML, '../assets/bootstrap-3.3.6-dist/css/bootstrap.min.css', 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css' );
  devguideHTML = ChipperStringUtils.replaceAll( devguideHTML, '../assets/bootstrap-3.3.6-dist/js/bootstrap.min.js', 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js' );
  devguideHTML = ChipperStringUtils.replaceAll( devguideHTML, '../assets/font-awesome-4.5.0/css/font-awesome.min.css', 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.min.css' );
  devguideHTML = ChipperStringUtils.replaceAll( devguideHTML, '../assets/js/jquery-1.12.3.min.js', 'https://code.jquery.com/jquery-1.12.3.min.js' );
  devguideHTML = ChipperStringUtils.replaceAll( devguideHTML, '../assets/css/', './css/' );
  devguideHTML = ChipperStringUtils.replaceAll( devguideHTML, '../assets/js/', './js/' );
  devguideHTML = ChipperStringUtils.replaceAll( devguideHTML, '../assets/highlight.js-9.1.0/styles/tomorrow-night-bright.css', 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.1.0/styles/tomorrow-night-bright.min.css' );
  devguideHTML = ChipperStringUtils.replaceAll( devguideHTML, '../assets/highlight.js-9.1.0/highlight.js', 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.1.0/highlight.min.js' );
  devguideHTML = ChipperStringUtils.replaceAll( devguideHTML, '../assets/favicon.ico', './favicon.ico' );

  // Remove the navbar and footer div tags
  var firstNavbarLine = ChipperStringUtils.firstLineThatContains( devguideHTML, 'id="navbar"' );
  devguideHTML = firstNavbarLine ? ChipperStringUtils.replaceAll( devguideHTML, firstNavbarLine, '' ) : devguideHTML;

  var firstFooterLine = ChipperStringUtils.firstLineThatContains( devguideHTML, 'id="footer"' );
  devguideHTML = firstFooterLine ? ChipperStringUtils.replaceAll( devguideHTML, firstFooterLine, '' ) : devguideHTML;

  grunt.file.write( ChipperConstants.BUILD_DIR + '/docs/devguide.html', devguideHTML );
  copyDirectory( grunt, '../phet-io-website/root/assets/css', ChipperConstants.BUILD_DIR + '/docs/css', filterWrapper );
  grunt.file.copy( '../phet-io-website/root/assets/js/phet-io.js', './' + ChipperConstants.BUILD_DIR + '/docs/js/phet-io.js' );
  grunt.file.copy( '../phet-io-website/root/assets/js/phet-io-ga.js', './' + ChipperConstants.BUILD_DIR + '/docs/js/phet-io-ga.js' );
  grunt.file.copy( '../phet-io-website/root/assets/favicon.ico', './' + ChipperConstants.BUILD_DIR + '/docs/favicon.ico' );

  // Minify phet libraries into 'lib/phetio.js'
  var DESTINATION_PATH = ChipperConstants.BUILD_DIR + '/lib';
  var OUTPUT_FILE = 'phetio.js';
  var COPYRIGHT_HEADER = '// Copyright 2002-2017, University of Colorado Boulder\n' +
                         '// This PhET-iO file requires a license\n' +
                         '// USE WITHOUT A LICENSE AGREEMENT IS STRICTLY PROHIBITED.\n' +
                         '// For licensing, please contact phethelp@colorado.edu';

  var LIB_FILES = [
    '../query-string-machine/js/QueryStringMachine.js',
    '../phet-io-wrappers/common/js/SimIFrameClient.js',
    '../phet-io-wrappers/common/js/WrapperTypes.js',
    '../phet-io-wrappers/common/js/assert.js',
    '../phet-io-wrappers/common/js/WrapperUtils.js' ];

  var minified = uglify.minify( LIB_FILES, {
    mangle: true,
    output: {
      inline_script: true, // escape </script
      beautify: false
    },
    compress: {
      global_defs: {}
    }
  } ).code;

  grunt.file.write( DESTINATION_PATH + '/' + OUTPUT_FILE, COPYRIGHT_HEADER + '\n\n' + minified );

  // Generate API Documentation
  // TODO: these are broken right now, the cause has something to do with the string plugin loading chipper's localeInfo.js
  // TODO: see https://github.com/phetsims/phet-io/issues/972 for more info
  // generatePhETIOAPIDocs( grunt, buildConfig );
};<|endoftext|>


<commit_before>/// <reference path="Scripts/jquery-1.6.1.js" />
/// <reference path="loader.js" />
/// <reference path="../Scripts/sammy-latest.min.js" />
/// <reference path="../Scripts/jquery.cookie.js" />

$(function () {
    var scm = signalR.SourceControl;
    var path = $.cookie("path");
    scm.state.path = path;
    $('#path').val(path || '');

    function getDiffClass(type) {
        if (type == 1) {
            return ' diff-add';
        }
        else if (type == 2) {
            return ' diff-remove';
        }
        return '';
    }

    function getDiffId(path) {
        return path.replace(/\/|\./g, "-")
    }

    function getFileClass(file) {
        if (file.Status == 1) {
            return 'icon-file-added';
        }
        else if (file.Status == 2) {
            return 'icon-file-deleted';
        }
        else if (file.Status == 3) {
            return 'icon-file-modified';
        }
        else if (file.Binary) {
            return 'icon-binary-file';
        }
        return 'icon-file';
    }

    function getBranches(id) {
        if (scm.state.branches && scm.state.branches[id]) {
            return scm.state.branches[id];
        }
        return [];
    }

    window.getDiffClass = getDiffClass;
    window.getDiffId = getDiffId;
    window.getFileClass = getFileClass;
    window.getBranches = getBranches;

    var changesXhr = null;
    var pageSize = 15;

    function getChangeSets(index, onComplete) {
        if (changesXhr) {
            if (onComplete) {
                onComplete();
            }
            return;
        }

        if (scm.state.full === true) {
            if (onComplete) {
                onComplete();
            }
            return;
        }

        changesXhr = scm.getChanges(index, pageSize, function (changes) {
            changesXhr = null;

            $.cookie("path", scm.state.path);

            setupActions($('#changes').append($('#changeset').render(changes)));
            scm.state.index = index + changes.length;

            $('.timeago').timeago();

            if (changes.length < pageSize) {
                scm.state.full = true;
            }
        })
        .complete(function () {
            if (onComplete) {
                onComplete();
            }
        });
    }

    function setupActions(element) {
        var id = scm.state.id;

        element.find('.update').click(function () {
            var newId = $(this).attr('data-id');
            var branch = $(this).attr('data-branch');

            $('#' + newId).find('.loading').show();
            $('#' + id).find('.status').addClass('hide');

            scm.update(branch || newId, function () {
                scm.state.id = newId;
                scm.state.branch = branch;

                $('#' + newId).find('.loading').hide();
                $('#' + newId).find('.status').removeClass('hide');

                id = newId;
            });

            return false;
        });
    }

    function loadRepository(path) {
        $('#show').hide();
        $('#working').hide();

        $('#changes').html('');
        $('#log').show();

        var token = window.loader.show('Loading commits...');
        scm.state.index = 0;
        scm.state.full = false;

        scm.getBranches(function (branches) {
            scm.state.branches = branches;

            getChangeSets(0, function () {
                window.loader.hide(token);
            });
        })
        .error(function () {
            window.loader.hide(token);
        });
    }

    function show(id) {
        if (changesXhr) {
            changesXhr.abort();
        }

        $('#log').hide();
        $('#working').hide();

        $('#show').html('');
        $('#show').show();

        var token = window.loader.show('Loading commit ' + id);

        scm.show(id, function (details) {
            $('#show').append($('#changeset-detail').render(details));
            $('.timeago').timeago();

            window.loader.hide(token);
        })
        .error(function () {
            window.loader.hide(token);
        });
    }

    function viewWorking() {
        if (changesXhr) {
            changesXhr.abort();
        }

        $('#log').hide();
        $('#show').hide();

        $('#diff').html('');
        $('#diff').show();
        $('#working').show();

        var token = window.loader.show('Loading working directory...');

        scm.getWorking(function (details) {
            if (details) {
                $('#diff').html($('#diff-view').render(details));
            }
            else {
                $('#diff').html('No changes');
            }
        })
        .complete(function () {
            window.loader.hide(token);
        });
    }

    function getMoreChanges() {
        if (!scm.state.id || scm.state.full === true) {
            return;
        }

        var threshold = 25;
        var min = $(document).scrollTop();
        var max = min + $(window).height();

        var e = $('#changes').find('tr:last');
        var pos = e.position();

        if (pos) {
            var top = pos.top - threshold;

            // Load more changes if we're in range
            if (top >= min && top <= max) {
                var token = window.infititeLoader.show('Loading more commits...');

                getChangeSets(scm.state.index, function () {
                    window.infititeLoader.hide(token);
                });
            }
        }
    }

    window.setInterval(getMoreChanges, 500);

    var app = $.sammy(function () {
        this.get('#/', function () {
            var path = scm.state.path;
            if (path) {
                loadRepository(path);
            }
            return false;
        });

        this.post('#/', function () {
            scm.state.path = this.params.path;
            this.redirect('#/');

            return false;
        });

        this.get('#/view/:id', function () {
            show(this.params.id);
        });

        this.get('#/working', function () {
            viewWorking();
        });

        this.post('#/commit', function () {
            var context = this;

            var token = window.loader.show('Commiting changes...');

            scm.commit(this.params.message, function (changeSet) {
                if (changeSet) {
                    $('#new-commit').html('Successfully commited ' + changeSet.ShortId);
                    $('#new-commit').slideDown();
                    $('#commit-message').val('');

                    window.setTimeout(function () {
                        $('#new-commit').slideUp('slow', function () {
                            window.setTimeout(function () {
                                context.redirect('#/');
                            }, 300);
                        });
                    }, 1000);
                }
                else {
                    alert('No pending changes');
                }
            })
            .complete(function () {
                window.loader.hide(token);
            });

            return false;
        });
    });

    app.run('#/');
});<commit_msg>Fixed infinite scroll issue in chrome and other small refactoring.<commit_after>/// <reference path="Scripts/jquery-1.6.1.js" />
/// <reference path="loader.js" />
/// <reference path="../Scripts/sammy-latest.min.js" />
/// <reference path="../Scripts/jquery.cookie.js" />

$(function () {
    var scm = signalR.SourceControl;
    var infiniteScrollCheck = false;
    var changesXhr = null;
    var pageSize = 15;
    var path = $.cookie("path");

    scm.state.path = path;
    $('#path').val(path || '');

    function getDiffClass(type) {
        if (type == 1) {
            return ' diff-add';
        }
        else if (type == 2) {
            return ' diff-remove';
        }
        return '';
    }

    function getDiffId(path) {
        return path.replace(/\/|\./g, "-")
    }

    function getFileClass(file) {
        if (file.Status == 1) {
            return 'icon-file-added';
        }
        else if (file.Status == 2) {
            return 'icon-file-deleted';
        }
        else if (file.Status == 3) {
            return 'icon-file-modified';
        }
        else if (file.Binary) {
            return 'icon-binary-file';
        }
        return 'icon-file';
    }

    function getBranches(id) {
        if (scm.state.branches && scm.state.branches[id]) {
            return scm.state.branches[id];
        }
        return [];
    }

    window.getDiffClass = getDiffClass;
    window.getDiffId = getDiffId;
    window.getFileClass = getFileClass;
    window.getBranches = getBranches;

    function getChangeSets(index, onComplete) {
        var callback = function () {
            if (onComplete) {
                onComplete();
            }
        };

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

        if (scm.state.full === true) {
            callback();
            return;
        }

        changesXhr = scm.getChanges(index, pageSize, function (changes) {
            $.cookie("path", scm.state.path);

            setupActions($('#changes').append($('#changeset').render(changes)));
            scm.state.index = index + changes.length;

            $('.timeago').timeago();

            if (changes.length < pageSize) {
                scm.state.full = true;
            }

            callback();
            changesXhr = null;
        })
        .error(function () {
            callback();
            changesXhr = null;
        });
    }

    function setupActions(element) {
        var id = scm.state.id;

        element.find('.update').click(function () {
            var newId = $(this).attr('data-id');
            var branch = $(this).attr('data-branch');

            $('#' + newId).find('.loading').show();
            $('#' + id).find('.status').addClass('hide');

            scm.update(branch || newId, function () {
                scm.state.id = newId;
                scm.state.branch = branch;

                $('#' + newId).find('.loading').hide();
                $('#' + newId).find('.status').removeClass('hide');

                id = newId;
            });

            return false;
        });
    }

    function loadRepository(path) {
        $('#show').hide();
        $('#working').hide();

        $('#changes').html('');
        $('#log').show();

        var token = window.loader.show('Loading commits...');
        scm.state.index = 0;
        scm.state.full = false;

        scm.getBranches(function (branches) {
            scm.state.branches = branches;

            getChangeSets(0, function () {
                window.loader.hide(token);

                if (infiniteScrollCheck === false) {
                    getMoreChanges();
                    infiniteScrollCheck = true;
                }
            });
        })
        .error(function () {
            window.loader.hide(token);
        });
    }

    function show(id) {
        if (changesXhr) {
            changesXhr.abort();
        }

        $('#log').hide();
        $('#working').hide();

        $('#show').html('');
        $('#show').show();

        var token = window.loader.show('Loading commit ' + id);

        scm.show(id, function (details) {
            $('#show').append($('#changeset-detail').render(details));
            $('.timeago').timeago();

            window.loader.hide(token);
        })
        .error(function () {
            window.loader.hide(token);
        });
    }

    function viewWorking() {
        if (changesXhr) {
            changesXhr.abort();
        }

        $('#log').hide();
        $('#show').hide();

        $('#diff').html('');
        $('#diff').show();
        $('#working').show();

        var token = window.loader.show('Loading working directory...');

        scm.getWorking(function (details) {
            if (details) {
                $('#diff').html($('#diff-view').render(details));
            }
            else {
                $('#diff').html('No changes');
            }
        })
        .complete(function () {
            window.loader.hide(token);
        });
    }

    function getMoreChanges() {
        var callback = function () {
            setTimeout(getMoreChanges, 500);
        };

        if (!scm.state.path || scm.state.full === true) {
            callback();
            return;
        }

        var threshold = 25;
        var min = $(document).scrollTop();
        var max = min + $(window).height();

        var e = $('#changes').find('tr:last');
        var pos = e.position();

        if (pos) {
            var top = pos.top - threshold;

            // Load more changes if we're in range
            if (top >= min && top <= max) {
                var token = window.infititeLoader.show('Loading more commits...');

                getChangeSets(scm.state.index, function () {
                    window.infititeLoader.hide(token);

                    callback();
                });
            }
            else {
                callback();
            }
        }
        else {
            callback();
        }
    }

    var app = $.sammy(function () {
        this.get('#/', function () {
            var path = scm.state.path;
            if (path) {
                loadRepository(path);
            }
            return false;
        });

        this.post('#/', function () {
            scm.state.path = this.params.path;
            this.redirect('#/');

            return false;
        });

        this.get('#/view/:id', function () {
            show(this.params.id);
        });

        this.get('#/working', function () {
            viewWorking();
        });

        this.post('#/commit', function () {
            var context = this;

            var token = window.loader.show('Commiting changes...');

            scm.commit(this.params.message, function (changeSet) {
                if (changeSet) {
                    $('#new-commit').html('Successfully commited ' + changeSet.ShortId);
                    $('#new-commit').slideDown();
                    $('#commit-message').val('');

                    window.setTimeout(function () {
                        $('#new-commit').slideUp('slow', function () {
                            window.setTimeout(function () {
                                context.redirect('#/');
                            }, 300);
                        });
                    }, 1000);
                }
                else {
                    alert('No pending changes');
                }
            })
            .complete(function () {
                window.loader.hide(token);
            });

            return false;
        });
    });

    app.run('#/');
});<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
// Tagger Control
//
// Displays and edits tags.
////////////////////////////////////////////////////////////////////////////////
/*global jQuery, jOrder, window */
var app = app || {};

app.controls = function (controls, $, jOrder, data) {
	var lookup = {},		// lookup object for tagger widgets (by id)
			lastWidth;			// last measured available width for compact view media entry

	controls.tagger = function (mediaid) {
		var	self = controls.control.create(),
				base_remove = self.remove,
				adder;

		lookup[self.id] = self;

		//////////////////////////////
		// Utility functions

		self.add = function () {
			adder.toggle('edit');
		};

		//////////////////////////////
		// Overrides

		// compares tags
		// - a,b: tag objects to compare
		function compare(a, b) {
			var akind = (a.kind || '').toLowerCase(),
					bkind = (b.kind || '').toLowerCase(),
					aname, bname;
			if (akind > bkind) {
				return 1;
			} else if (akind < bkind) {
				return -1;
			} else {
				aname = a.name.toLowerCase();
				bname = b.name.toLowerCase();
				if (aname > bname) {
					return 1;
				} else if (aname < bname) {
					return -1;
				} else {
					return 0;
				}
			}
		}

		self.build = function () {
			self.clear();

			// adding tag editor controls in sorted tag order
			var row = data.media.getRow(mediaid),
					tags = jOrder.values(row.tags).sort(compare),
					i;
			for (i = 0; i < tags.length; i++) {
				controls.tagedit(mediaid, tags[i].tag)
					.appendTo(self);
			}

			// adding tag adder control
			adder = controls.tagadd(mediaid)
				.appendTo(self);

			return self;
		};

		self.remove = function () {
			delete lookup[self.id];
			return base_remove.call(self);
		};

		self.init = function (elem) {
			controls.tagger.resize(true, elem);
		};

		self.html = function () {
			var result = ['
'],
					i, child;
			// adding html for tag controls of visible kinds
			for (i = 0; i < self.children.length; i++) {
				child = self.children[i];
				if (!child.kind || !controls.kinds.hidden(child.kind())) {
					result.push(child.html());
				}
			}
			result.push('
');
			return result.join('');
		};

		return self;
	};

	//////////////////////////////
	// Static methods

	// resizes tagger widgets to fit screen width
	controls.tagger.resize = function (force, elem) {
		var $list = $('div.media.list'),
				$tagger = elem || $list.find('div.tagger'),
				width, full = $list.width(),
				$model,
				left;

		if (force ||																// either forced, or,
				(!lastWidth || full !== lastWidth) &&		// last width doesn't match current width, and,
				$tagger.length) {												// there are visible tagger widgets
			// obtaining single tagger for measurement
			$model = $tagger.eq(0);

			if ($model.css('position') === 'absolute') {
				// tagger absolutely positioned
				left = $model.position().left;
			} else if ($model.css('display') === 'inline-block') {
				// tagger is inline-block
				// measuring padding + border + margin + scroller
				left = $model.outerWidth(true) - $model.width() + 15;

				// counting all previous siblings' width
				$model.prevAll()
					.each(function () {
						left += $(this).outerWidth(true);
					});
			} else {
				// failsafe
				// will look ugly
				left = full;
			}

			// setting updated width
			$tagger.width(full - left);

			// updating state indicator
			lastWidth = full;
		}
	};

	// re-renders all visible tagger widgets
	controls.tagger.render = function () {
		var id;
		for (id in lookup) {
			if (lookup.hasOwnProperty(id)) {
				lookup[id]
					.build()
					.render();
			}
		}
	};

	//////////////////////////////
	// Static event handlers

	function onResize(elem) {
		controls.tagger.resize();
	}

	//////////////////////////////
	// Static event bindings

	$(window).bind('resize', onResize);

	return controls;
}(app.controls || {},
	jQuery,
	jOrder,
	app.data);

<commit_msg>Fix tag layout on jQuery 1.8.1 or newer<commit_after>////////////////////////////////////////////////////////////////////////////////
// Tagger Control
//
// Displays and edits tags.
////////////////////////////////////////////////////////////////////////////////
/*global jQuery, jOrder, window */
var app = app || {};

app.controls = function (controls, $, jOrder, data) {
	var lookup = {},		// lookup object for tagger widgets (by id)
			lastWidth;			// last measured available width for compact view media entry

	controls.tagger = function (mediaid) {
		var	self = controls.control.create(),
				base_remove = self.remove,
				adder;

		lookup[self.id] = self;

		//////////////////////////////
		// Utility functions

		self.add = function () {
			adder.toggle('edit');
		};

		//////////////////////////////
		// Overrides

		// compares tags
		// - a,b: tag objects to compare
		function compare(a, b) {
			var akind = (a.kind || '').toLowerCase(),
					bkind = (b.kind || '').toLowerCase(),
					aname, bname;
			if (akind > bkind) {
				return 1;
			} else if (akind < bkind) {
				return -1;
			} else {
				aname = a.name.toLowerCase();
				bname = b.name.toLowerCase();
				if (aname > bname) {
					return 1;
				} else if (aname < bname) {
					return -1;
				} else {
					return 0;
				}
			}
		}

		self.build = function () {
			self.clear();

			// adding tag editor controls in sorted tag order
			var row = data.media.getRow(mediaid),
					tags = jOrder.values(row.tags).sort(compare),
					i;
			for (i = 0; i < tags.length; i++) {
				controls.tagedit(mediaid, tags[i].tag)
					.appendTo(self);
			}

			// adding tag adder control
			adder = controls.tagadd(mediaid)
				.appendTo(self);

			return self;
		};

		self.remove = function () {
			delete lookup[self.id];
			return base_remove.call(self);
		};

		self.init = function (elem) {
			controls.tagger.resize(true, elem);
		};

		self.html = function () {
			var result = ['
'],
					i, child;
			// adding html for tag controls of visible kinds
			for (i = 0; i < self.children.length; i++) {
				child = self.children[i];
				if (!child.kind || !controls.kinds.hidden(child.kind())) {
					result.push(child.html());
				}
			}
			result.push('
');
			return result.join('');
		};

		return self;
	};

	//////////////////////////////
	// Static methods

	// resizes tagger widgets to fit screen width
	controls.tagger.resize = function (force, elem) {
		var $list = $('div.media.list'),
				$tagger = elem || $list.find('div.tagger'),
				width, full = $list.width(),
				$model,
				left;

		if (force ||																// either forced, or,
				(!lastWidth || full !== lastWidth) &&		// last width doesn't match current width, and,
				$tagger.length) {												// there are visible tagger widgets
			// obtaining single tagger for measurement
			$model = $tagger.eq(0);

			if ($model.css('position') === 'absolute') {
				// tagger absolutely positioned
				left = $model.position().left;
			} else if ($model.css('display') === 'inline-block') {
				// tagger is inline-block
				// measuring padding + border + margin + scroller
				left = $model.outerWidth(true) - $model.width() + 15;

				// counting all previous siblings' width
				$model.prevAll()
					.each(function () {
						left += $(this).outerWidth(true);
					});
			} else {
				// failsafe
				// will look ugly
				left = full;
			}

			// setting updated width
			//$tagger.width(full - left); // removed to fix the layout on jquery 1.8.1 or newer

			// updating state indicator
			lastWidth = full;
		}
	};

	// re-renders all visible tagger widgets
	controls.tagger.render = function () {
		var id;
		for (id in lookup) {
			if (lookup.hasOwnProperty(id)) {
				lookup[id]
					.build()
					.render();
			}
		}
	};

	//////////////////////////////
	// Static event handlers

	function onResize(elem) {
		controls.tagger.resize();
	}

	//////////////////////////////
	// Static event bindings

	$(window).bind('resize', onResize);

	return controls;
}(app.controls || {},
	jQuery,
	jOrder,
	app.data);

<|endoftext|>


<commit_before>var mongodb = require('mongodb');

module.exports = {
        db : function(settings){
                var server = new mongodb.Server(settings.database.host, settings.database.port, settings.database.server_options);
                return new mongodb.Db(settings.database.name, server, settings.database.db_options);
        }
};
<commit_msg>Added setter and getter to storage<commit_after>var mongodb = require('mongodb');

var _private = {};

module.exports = {
        set : function(){
                _private[arguments[0]] = arguments[1];
        },
        get : function(){
                var key = arguments[0];
                return _private[key] ? _private[key] : null;
        },
        db : function(settings){
                var server = new mongodb.Server(settings.database.host, settings.database.port, settings.database.server_options);
                return new mongodb.Db(settings.database.name, server, settings.database.db_options);
        }
};
<|endoftext|>


<commit_before>'use strict';
// original code by the Angular Material 2 team

const fs = require('fs');
const glob  = require('glob');
const path = require('path');
const sass = require('node-sass');

const inlineResources = (globs, sourcePrefix) => {
    if (typeof globs === 'string') {
        globs = [globs];
    }

    globs.forEach((pattern) => replaceSource(pattern, sourcePrefix));
};

const replaceSource = (pattern, sourcePrefix) => {
    // pattern is a directory
    if (pattern.indexOf('*') === -1) {
        pattern = path.join(pattern, '**', '*');
    }

    const files = glob.sync(pattern, {})
        .filter((name) => /\.ts$/.test(name));

    files.forEach((filePath) => {
        fs.readFile(filePath, 'utf8', (error, content) => {
            if (error) {
                throw error;
            }

            let fileContents = inlineResourcesFromString(content, sourcePrefix, (url) =>
                path.join(path.dirname(filePath), url)
            );
            //# sourceMappingURL=test.bundle.js.map
            fileContents = fileContents.replace(/\/\/#\s*sourceMappingURL=(.+)(\r?\n)?/, (match, url) => {
                const lastSlash = url.lastIndexOf('/');
                const relativeUrl = url.substring(lastSlash + 1);

                return match.replace(url, relativeUrl);
            });

            fs.writeFile(filePath, fileContents);
        });
    });
};

const inlineResourcesFromString = (content, sourcePrefix, callback) => [
    inlineTemplate, inlineStyle, removeModuleId
].reduce((final, method) => method(final, sourcePrefix, callback), content);

const inlineTemplate = (content, sourcePrefix, callback) =>
    content.replace(/templateUrl:\s*'([^']+?\.html)'/g, (match, url) => {
        const mini = getMiniContents(url, sourcePrefix, callback);

        return `template: "${mini}"`;
    });

const inlineStyle = (content, sourcePrefix, callback) =>
    content.replace(/styleUrls:\s*(\[[\s\S]*?\])/gm, (match, styleUrls) => {
        const urls = eval(styleUrls);   // string -> array
        return 'styles: [' + urls.map((url) => {
            const mini = getMiniContents(url, sourcePrefix, callback);

            return `"${mini}"`;
        }).join(',\n') + ']';
    });

const getMiniContents = (url, sourcePrefix, callback) => {
    const srcFile = callback(url);
    const file = srcFile.replace(/^dist/, sourcePrefix)
    const srcDir = file.slice(0, file.lastIndexOf(path.sep));
    let template = '';

    if (file.match(/\.s(a|c)ss$/)) {
        // convert SASS -> CSS
        template = sass.renderSync({
            file,
            importer: (url) => handleSassImport(url, srcDir)
        });
        template = template.css.toString();
    }

    return minifyText(template);
};

const handleSassImport = (url, srcDir) => {
    const fullUrl = getFullSassUrl(url, srcDir);
    let isPartial = false;
    let validUrls = getSassUrls(fullUrl);

    // if we can't find the file, try to
    // see find it as a partial (underscore-prefixed)
    if (validUrls.length === 0) {
        validUrls = getSassUrls(fullUrl, true);
        isPartial = true;
    }

    const file = getSassImportUrl(validUrls);

    // CSS files don't get compiled in
    return /\.css$/.test(file) ?
        { contents: fs.readFileSync(file, 'utf8') } :
        { file };
};

const getSassUrls = (url, partial) => {
    let extensions = ['sass', 'scss'];

    if (!partial) {
        extensions = extensions.concat('', 'css');
    } else {
        const lastSlash = url.lastIndexOf(path.sep);
        const urlDir = url.slice(0, lastSlash);
        const fileName = url.slice(lastSlash + 1);

        if (fileName[0] !== '_') {
            url = urlDir + path.sep + '_' + fileName;
        }
    }

    return extensions.reduce((valid, extension) => {
        const extensionUrl = verifyUrl(url, extension);

        if (extensionUrl) {
            valid = valid.concat(extensionUrl);
        }

        return valid;
    }, []);
};

const verifyUrl = (url, extension) => {
    if (extension) {
        url = url + `.${ extension }`;
    }

    if (!fs.existsSync(url)) {
        url = null;
    }

    return url;
}

// convert ~-prefixed filenames to node_modules-prefixed
// make all others relative to srcDir
const getFullSassUrl = (url, srcDir) =>
    /^~/.test(url) ?
        path.resolve('node_modules', url.slice(1)) :
        path.resolve(srcDir, url);

const getSassImportUrl = (validUrls) => {
    if (validUrls.length !== 1) {
        let error = 'Cannot determine Sass/CSS file to process. ';

        if (validUrls.length === 0) {
            error = error + `\n There are no files matching ${ url }`;
        } else {
            error = error + 'Candidates:\n ' + validUrls.join('\n ')
                + '\nPlease delete or rename all but one of these files or specify the extension to use.';
        }

        throw new Error(error);
    }

    return validUrls[0];
};

const minifyText = (text) => text
    .replace(/([\n\r]\s*)+/gm, ' ')
    .replace(/"/g, '\\"');

const removeModuleId = (content) =>
    content.replace(/\s*moduleId:\s*module\.id\s*,?\s*/gm, '');

module.exports = inlineResources;

if (!module.parent) {
    inlineResources('./build', 'src');
}
<commit_msg>#41 Templates were not getting inlined as a result of the previous fix<commit_after>'use strict';
// original code by the Angular Material 2 team

const fs = require('fs');
const glob  = require('glob');
const path = require('path');
const sass = require('node-sass');

const inlineResources = (globs, sourcePrefix) => {
    if (typeof globs === 'string') {
        globs = [globs];
    }

    globs.forEach((pattern) => replaceSource(pattern, sourcePrefix));
};

const replaceSource = (pattern, sourcePrefix) => {
    // pattern is a directory
    if (pattern.indexOf('*') === -1) {
        pattern = path.join(pattern, '**', '*');
    }

    const files = glob.sync(pattern, {})
        .filter((name) => /\.ts$/.test(name));

    files.forEach((filePath) => {
        fs.readFile(filePath, 'utf8', (error, content) => {
            if (error) {
                throw error;
            }

            let fileContents = inlineResourcesFromString(content, sourcePrefix, (url) =>
                path.join(path.dirname(filePath), url)
            );
            //# sourceMappingURL=test.bundle.js.map
            fileContents = fileContents.replace(/\/\/#\s*sourceMappingURL=(.+)(\r?\n)?/, (match, url) => {
                const lastSlash = url.lastIndexOf('/');
                const relativeUrl = url.substring(lastSlash + 1);

                return match.replace(url, relativeUrl);
            });

            fs.writeFile(filePath, fileContents);
        });
    });
};

const inlineResourcesFromString = (content, sourcePrefix, callback) => [
    inlineTemplate, inlineStyle, removeModuleId
].reduce((final, method) => method(final, sourcePrefix, callback), content);

const inlineTemplate = (content, sourcePrefix, callback) =>
    content.replace(/templateUrl:\s*'([^']+?\.html)'/g, (match, url) => {
        const mini = getMiniContents(url, sourcePrefix, callback);

        return `template: "${mini}"`;
    });

const inlineStyle = (content, sourcePrefix, callback) =>
    content.replace(/styleUrls:\s*(\[[\s\S]*?\])/gm, (match, styleUrls) => {
        const urls = eval(styleUrls);   // string -> array
        return 'styles: [' + urls.map((url) => {
            const mini = getMiniContents(url, sourcePrefix, callback);

            return `"${mini}"`;
        }).join(',\n') + ']';
    });

const getMiniContents = (url, sourcePrefix, callback) => {
    const srcFile = callback(url);
    const file = srcFile.replace(/^dist/, sourcePrefix)
    const srcDir = file.slice(0, file.lastIndexOf(path.sep));
    let template = '';

    if (file.match(/\.s(a|c)ss$/)) {
        // convert SASS -> CSS
        template = sass.renderSync({
            file,
            importer: (url) => handleSassImport(url, srcDir)
        });
        template = template.css.toString();
    } else {
        template = fs.readFileSync(file, 'utf8');
    }

    return minifyText(template);
};

const handleSassImport = (url, srcDir) => {
    const fullUrl = getFullSassUrl(url, srcDir);
    let isPartial = false;
    let validUrls = getSassUrls(fullUrl);

    // if we can't find the file, try to
    // see find it as a partial (underscore-prefixed)
    if (validUrls.length === 0) {
        validUrls = getSassUrls(fullUrl, true);
        isPartial = true;
    }

    const file = getSassImportUrl(validUrls);

    // CSS files don't get compiled in
    return /\.css$/.test(file) ?
        { contents: fs.readFileSync(file, 'utf8') } :
        { file };
};

const getSassUrls = (url, partial) => {
    let extensions = ['sass', 'scss'];

    if (!partial) {
        extensions = extensions.concat('', 'css');
    } else {
        const lastSlash = url.lastIndexOf(path.sep);
        const urlDir = url.slice(0, lastSlash);
        const fileName = url.slice(lastSlash + 1);

        if (fileName[0] !== '_') {
            url = urlDir + path.sep + '_' + fileName;
        }
    }

    return extensions.reduce((valid, extension) => {
        const extensionUrl = verifyUrl(url, extension);

        if (extensionUrl) {
            valid = valid.concat(extensionUrl);
        }

        return valid;
    }, []);
};

const verifyUrl = (url, extension) => {
    if (extension) {
        url = url + `.${ extension }`;
    }

    if (!fs.existsSync(url)) {
        url = null;
    }

    return url;
}

// convert ~-prefixed filenames to node_modules-prefixed
// make all others relative to srcDir
const getFullSassUrl = (url, srcDir) =>
    /^~/.test(url) ?
        path.resolve('node_modules', url.slice(1)) :
        path.resolve(srcDir, url);

const getSassImportUrl = (validUrls) => {
    if (validUrls.length !== 1) {
        let error = 'Cannot determine Sass/CSS file to process. ';

        if (validUrls.length === 0) {
            error = error + `\n There are no files matching ${ url }`;
        } else {
            error = error + 'Candidates:\n ' + validUrls.join('\n ')
                + '\nPlease delete or rename all but one of these files or specify the extension to use.';
        }

        throw new Error(error);
    }

    return validUrls[0];
};

const minifyText = (text) => text
    .replace(/([\n\r]\s*)+/gm, ' ')
    .replace(/"/g, '\\"');

const removeModuleId = (content) =>
    content.replace(/\s*moduleId:\s*module\.id\s*,?\s*/gm, '');

module.exports = inlineResources;

if (!module.parent) {
    inlineResources('./build', 'src');
}
<|endoftext|>


<commit_before>// Copyright 2010 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview HTML5 based history implementation, compatible with
 * goog.History.
 *
 * TODO(user): There should really be a history interface and multiple
 * implementations.
 *
 *
 */

goog.provide('goog.history.Html5History');

goog.require('goog.asserts');
goog.require('goog.events');
goog.require('goog.events.EventTarget');
goog.require('goog.events.EventType');
goog.require('goog.history.Event');
goog.require('goog.history.EventType');

/**
 * An implementation compatible with goog.History that uses the HTML5
 * history APIs.
 *
 * @param {Window=} opt_win The window to listen/dispatch history events on.
 * @constructor
 * @extends {goog.events.EventTarget}
 */
goog.history.Html5History = function(opt_win) {
  goog.events.EventTarget.call(this);
  goog.asserts.assert(goog.history.Html5History.isSupported(opt_win),
      'HTML5 history is not supported.');

  /**
   * The window object to use for history tokens.  Typically the top window.
   * @type {Window}
   * @private
   */
  this.window_ = opt_win || window;

  goog.events.listen(this.window_, goog.events.EventType.POPSTATE,
      this.onHistoryEvent_, false, this);
  goog.events.listen(this.window_, goog.events.EventType.HASHCHANGE,
      this.onHistoryEvent_, false, this);
};
goog.inherits(goog.history.Html5History, goog.events.EventTarget);

/**
 * Returns whether Html5History is supported.
 * @param {Window=} opt_win Optional window to check.
 * @return {boolean} Whether html5 history is supported.
 */
goog.history.Html5History.isSupported = function(opt_win) {
  var win = opt_win || window;
  return !!(win.history && win.history.pushState);
};

/**
 * Status of when the object is active and dispatching events.
 * @type {boolean}
 * @private
 */
goog.history.Html5History.prototype.enabled_ = false;

/**
 * Whether to use the fragment to store the token, defaults to true.
 * @type {boolean}
 * @private
 */
goog.history.Html5History.prototype.useFragment_ = true;

/**
 * If useFragment is false the path will be used, the path prefix will be
 * prepended to all tokens.
 * @type {string}
 * @private
 */
goog.history.Html5History.prototype.pathPrefix_ = '';

/**
 * Starts or stops the History.  When enabled, the History object
 * will immediately fire an event for the current location. The caller can set
 * up event listeners between the call to the constructor and the call to
 * setEnabled.
 *
 * @param {boolean} enable Whether to enable history.
 */
goog.history.Html5History.prototype.setEnabled = function(enable) {
  if (enable == this.enabled_) {
    return;
  }

  this.enabled_ = enable;

  if (enable) {
    this.dispatchEvent(new goog.history.Event(this.getToken(), false));
  }
};

/**
 * Returns the current token.
 * @return {string} The current token.
 */
goog.history.Html5History.prototype.getToken = function() {
  if (this.useFragment_) {
    var loc = this.window_.location.href;
    var index = loc.indexOf('#');
    return index < 0 ? '' : loc.substring(index + 1);
  } else {
    return this.window_.location.pathname.substring(this.pathPrefix_.length);
  }
};

/**
 * Sets the history state.
 * @param {string} token The history state identifier.
 * @param {string=} opt_title Optional title to associate with history entry.
 */
goog.history.Html5History.prototype.setToken = function(token, opt_title) {
  // Per externs/gecko_dom.js document.title can be null.
  this.window_.history.pushState(null,
      opt_title || this.window_.document.title || '', this.getUrl_(token));
  this.dispatchEvent(new goog.history.Event(token, false));
};

/**
 * Replaces the current history state without affecting the rest of the history
 * stack.
 * @param {string} token The history state identifier.
 * @param {string=} opt_title Optional title to associate with history entry.
 */
goog.history.Html5History.prototype.replaceToken = function(token, opt_title) {
  // Per externs/gecko_dom.js document.title can be null.
  this.window_.history.replaceState(null,
      opt_title || this.window_.document.title || '', this.getUrl_(token));
  this.dispatchEvent(new goog.history.Event(token, false));
};

/** @inheritDoc */
goog.history.Html5History.prototype.disposeInternal = function() {
  goog.events.unlisten(this.window_, goog.events.EventType.POPSTATE,
      this.onHistoryEvent_, false, this);
  if (this.useFragment_) {
    goog.events.unlisten(this.window_, goog.events.EventType.HASHCHANGE,
        this.onHistoryEvent_, false, this);
  }
};

/**
 * Sets whether to use the fragment to store tokens.
 * @param {boolean} useFragment Whether to use the fragment.
 */
goog.history.Html5History.prototype.setUseFragment = function(useFragment) {
  if (this.useFragment_ != useFragment) {
    if (useFragment) {
      goog.events.listen(this.window_, goog.events.EventType.HASHCHANGE,
          this.onHistoryEvent_, false, this);
    } else {
      goog.events.unlisten(this.window_, goog.events.EventType.HASHCHANGE,
          this.onHistoryEvent_, false, this);
    }
    this.useFragment_ = useFragment;
  }
};

/**
 * Sets the path prefix to use if storing tokens in the path.
 * @param {string} pathPrefix Sets the path prefix.
 */
goog.history.Html5History.prototype.setPathPrefix = function(pathPrefix) {
  this.pathPrefix_ = pathPrefix;
};

/**
 * Gets the path prefix.
 * @return {string} The path prefix.
 */
goog.history.Html5History.prototype.getPathPrefix = function() {
  return this.pathPrefix_;
};

/**
 * Gets the URL to set when calling history.pushState
 * @param {string} token The history token.
 * @return {string} The URL.
 * @private
 */
goog.history.Html5History.prototype.getUrl_ = function(token) {
  if (this.useFragment_) {
    return '#' + token;
  } else {
    // Paths need to be absolute to maintain correct nesting, so we prepend the
    // known path prefix.  We also preserve the query string, since this is
    // usually what is intended.
    return this.pathPrefix_ + token + this.window_.location.search;
  }
};

/**
 * Handles history events dispatched by the browser.
 * @param {goog.events.BrowserEvent} e The browser event object.
 * @private
 */
goog.history.Html5History.prototype.onHistoryEvent_ = function(e) {
  if (this.enabled_) {
    this.dispatchEvent(new goog.history.Event(this.getToken(), true));
  }
};
<commit_msg><commit_after>// Copyright 2010 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview HTML5 based history implementation, compatible with
 * goog.History.
 *
 * TODO(user): There should really be a history interface and multiple
 * implementations.
 *
 *
 */

goog.provide('goog.history.Html5History');

goog.require('goog.asserts');
goog.require('goog.events');
goog.require('goog.events.EventTarget');
goog.require('goog.events.EventType');
goog.require('goog.history.Event');
goog.require('goog.history.EventType');

/**
 * An implementation compatible with goog.History that uses the HTML5
 * history APIs.
 *
 * @param {Window=} opt_win The window to listen/dispatch history events on.
 * @constructor
 * @extends {goog.events.EventTarget}
 */
goog.history.Html5History = function(opt_win) {
  goog.events.EventTarget.call(this);
  goog.asserts.assert(goog.history.Html5History.isSupported(opt_win),
      'HTML5 history is not supported.');

  /**
   * The window object to use for history tokens.  Typically the top window.
   * @type {Window}
   * @private
   */
  this.window_ = opt_win || window;

  goog.events.listen(this.window_, goog.events.EventType.POPSTATE,
      this.onHistoryEvent_, false, this);
  goog.events.listen(this.window_, goog.events.EventType.HASHCHANGE,
      this.onHistoryEvent_, false, this);
};
goog.inherits(goog.history.Html5History, goog.events.EventTarget);

/**
 * Returns whether Html5History is supported.
 * @param {Window=} opt_win Optional window to check.
 * @return {boolean} Whether html5 history is supported.
 */
goog.history.Html5History.isSupported = function(opt_win) {
  var win = opt_win || window;
  return !!(win.history && win.history.pushState);
};

/**
 * Status of when the object is active and dispatching events.
 * @type {boolean}
 * @private
 */
goog.history.Html5History.prototype.enabled_ = false;

/**
 * Whether to use the fragment to store the token, defaults to true.
 * @type {boolean}
 * @private
 */
goog.history.Html5History.prototype.useFragment_ = true;

/**
 * If useFragment is false the path will be used, the path prefix will be
 * prepended to all tokens.
 * @type {string}
 * @private
 */
goog.history.Html5History.prototype.pathPrefix_ = '';

/**
 * Starts or stops the History.  When enabled, the History object
 * will immediately fire an event for the current location. The caller can set
 * up event listeners between the call to the constructor and the call to
 * setEnabled.
 *
 * @param {boolean} enable Whether to enable history.
 */
goog.history.Html5History.prototype.setEnabled = function(enable) {
  if (enable == this.enabled_) {
    return;
  }

  this.enabled_ = enable;

  if (enable) {
    this.dispatchEvent(new goog.history.Event(this.getToken(), false));
  }
};

/**
 * Returns the current token.
 * @return {string} The current token.
 */
goog.history.Html5History.prototype.getToken = function() {
  if (this.useFragment_) {
    var loc = this.window_.location.href;
    var index = loc.indexOf('#');
    return index < 0 ? '' : loc.substring(index + 1);
  } else {
    return this.window_.location.pathname.substring(this.pathPrefix_.length);
  }
};

/**
 * Sets the history state.
 * @param {string} token The history state identifier.
 * @param {string=} opt_title Optional title to associate with history entry.
 */
goog.history.Html5History.prototype.setToken = function(token, opt_title) {
  if (token == this.getToken()) {
    return;
  }

  // Per externs/gecko_dom.js document.title can be null.
  this.window_.history.pushState(null,
      opt_title || this.window_.document.title || '', this.getUrl_(token));
  this.dispatchEvent(new goog.history.Event(token, false));
};

/**
 * Replaces the current history state without affecting the rest of the history
 * stack.
 * @param {string} token The history state identifier.
 * @param {string=} opt_title Optional title to associate with history entry.
 */
goog.history.Html5History.prototype.replaceToken = function(token, opt_title) {
  // Per externs/gecko_dom.js document.title can be null.
  this.window_.history.replaceState(null,
      opt_title || this.window_.document.title || '', this.getUrl_(token));
  this.dispatchEvent(new goog.history.Event(token, false));
};

/** @inheritDoc */
goog.history.Html5History.prototype.disposeInternal = function() {
  goog.events.unlisten(this.window_, goog.events.EventType.POPSTATE,
      this.onHistoryEvent_, false, this);
  if (this.useFragment_) {
    goog.events.unlisten(this.window_, goog.events.EventType.HASHCHANGE,
        this.onHistoryEvent_, false, this);
  }
};

/**
 * Sets whether to use the fragment to store tokens.
 * @param {boolean} useFragment Whether to use the fragment.
 */
goog.history.Html5History.prototype.setUseFragment = function(useFragment) {
  if (this.useFragment_ != useFragment) {
    if (useFragment) {
      goog.events.listen(this.window_, goog.events.EventType.HASHCHANGE,
          this.onHistoryEvent_, false, this);
    } else {
      goog.events.unlisten(this.window_, goog.events.EventType.HASHCHANGE,
          this.onHistoryEvent_, false, this);
    }
    this.useFragment_ = useFragment;
  }
};

/**
 * Sets the path prefix to use if storing tokens in the path.
 * @param {string} pathPrefix Sets the path prefix.
 */
goog.history.Html5History.prototype.setPathPrefix = function(pathPrefix) {
  this.pathPrefix_ = pathPrefix;
};

/**
 * Gets the path prefix.
 * @return {string} The path prefix.
 */
goog.history.Html5History.prototype.getPathPrefix = function() {
  return this.pathPrefix_;
};

/**
 * Gets the URL to set when calling history.pushState
 * @param {string} token The history token.
 * @return {string} The URL.
 * @private
 */
goog.history.Html5History.prototype.getUrl_ = function(token) {
  if (this.useFragment_) {
    return '#' + token;
  } else {
    // Paths need to be absolute to maintain correct nesting, so we prepend the
    // known path prefix.  We also preserve the query string, since this is
    // usually what is intended.
    return this.pathPrefix_ + token + this.window_.location.search;
  }
};

/**
 * Handles history events dispatched by the browser.
 * @param {goog.events.BrowserEvent} e The browser event object.
 * @private
 */
goog.history.Html5History.prototype.onHistoryEvent_ = function(e) {
  if (this.enabled_) {
    this.dispatchEvent(new goog.history.Event(this.getToken(), true));
  }
};
<|endoftext|>


<commit_before>$(document).ready(function() {
    $( window ).resize(function() {
        resizeImages();
    });
    resizeImages();
});

function resizeImages() {
    $('#main-page img').each(function() {
        var imageHeight = this.naturalHeight;
        var imageWidth = this.naturalWidth;
        var divHeight = parseInt($(this).closest('div').css('height'));
        var divWidth = parseInt($(this).closest('div').css('width'));
        if (imageHeight/imageWidth < divHeight/divWidth) { // Querformat
            if (imageHeight > divHeight) {
                $(this).css('height', divHeight + 'px');
                $(this).css('width', '');
            }
        } else { // Hochformat
            if (imageWidth > divWidth) {
                $(this).css('width', divWidth + 'px');
                $(this).css('height', '');
            }
        }

    });
}
<commit_msg>issue #4, Bildskalierung funktioniert nun auch zuverlässig bei onload<commit_after>$(document).ready(function() {
    $( window ).resize(function() {
        resizeImages();
    });
    $("img").one("load", function() {
        resizeImage(this);
    }).each(function() {
        if(this.complete) $(this).load();
    });
});

function resizeImages() {
    $('#main-page img').each(function() {
        resizeImage(this);

    });
}

function resizeImage(imageElement) {
    var imageHeight = imageElement.naturalHeight;
    var imageWidth = imageElement.naturalWidth;
    var divHeight = parseInt($(imageElement).closest('div').css('height'));
    var divWidth = parseInt($(imageElement).closest('div').css('width'));
    if (imageHeight/imageWidth < divHeight/divWidth) { // Querformat
        if (imageHeight > divHeight) {
            $(imageElement).css('height', divHeight + 'px');
            $(imageElement).css('width', '');
        }
    } else { // Hochformat
        if (imageWidth > divWidth) {
            $(imageElement).css('width', divWidth + 'px');
            $(imageElement).css('height', '');
        }
    }
}<|endoftext|>


<commit_before><commit_msg>Add work done in class<commit_after><|endoftext|>


<commit_before>import async from "../utils/async.js";
import { Glyph } from "../utils/glyphs.js";
import Sort from "../utils/sort.js";
import Truncate from "../utils/truncate.js";
import urlHelper from "../utils/url.js";
import window from "./__mocks__/window";
import NavigationStore from "../stores/NavigationStore.js";
import renderer from "react-test-renderer";
import GroupBy from "../utils/groupBy.js";
import array from "../utils/array.js"

describe("utils", () => {
  test("async.doWhenReady", () => {
    const readyFunc = () => true;
    const doVoidFunc = jest.fn();

    async.doWhenReady(readyFunc, doVoidFunc, 1);
    expect(doVoidFunc).toBeCalled();
  });

  test("async.onEvent", () => {
    const eventName = "testEvent";
    const doVoidFunc = jest.fn();

    async.onEvent(eventName, doVoidFunc);
    global.dispatchEvent(new Event(eventName));
    expect(doVoidFunc).toBeCalled();
  });

  test("glyphs", () => {
    const glyph = Glyph("fa style", " class2", " class3");
    expect(glyph).toEqual("fa style class2 class3");
  });

  test("sort", () => {
    const collection = [
      { id: 0, type: "thing", name: "pen" },
      { id: 1, type: "special", name: "coin" },
      { id: 2, type: "thing", name: "straw" },
      { id: 3, type: "favorite", name: "crystal" },
    ];
    const order = ["favorite", "special"];
    const sorted = Sort.byNamedEntries(collection, order, obj => obj.type);

    expect(sorted.map(obj => obj.id)).toEqual([3, 1, 0, 2]);

    const group_issue_areas = {
      no: 2,
      en: 4,
      ed: 1,
      tr: 3,
    };
    const sorted_by_count_descending1 = Sort.byCountDictionary(
      group_issue_areas
    );
    expect(sorted_by_count_descending1).toEqual(["en", "tr", "no", "ed"]);

    const sorted_by_count_descending2 = Sort.byCountDictionary(
      group_issue_areas,
      false
    );
    expect(sorted_by_count_descending2).toEqual(sorted_by_count_descending1);

    const sorted_by_count_ascending = Sort.byCountDictionary(
      group_issue_areas,
      true
    );
    expect(sorted_by_count_ascending).toEqual(["ed", "no", "tr", "en"]);
  });

  test("truncate", () => {
    let str = "someday never comes";
    let lines = "one\ntwo\nthree\nfour";
    let arr = [1, 2, 3, 4];

    str = Truncate.stringT(str, 10);
    lines = Truncate.lines(lines, 3);
    arr = Truncate.arrayT(arr, 3);

    expect(str).toEqual("someday...");
    expect(lines.split("\n").length).toEqual(3);
    expect(arr).toEqual([1, 2, "2 more"]);
  });

  test("url", () => {
    expect(urlHelper.appendHttpIfMissingProtocol("testing.us")).toEqual(
      "http://testing.us"
    );
    expect(urlHelper.beautify("https://testing.us")).toEqual("testing.us");
    expect(urlHelper.beautify("http://testing.us")).toEqual("testing.us");

    urlHelper.navigateToSection("LogIn");
    expect(NavigationStore.getSection()).toEqual("LogIn");

    let url = urlHelper.section("FindProjects", { next: 1 });
    expect(url).toEqual("/projects?next=1");

    url = urlHelper.sectionOrLogIn("CreateProject");
    expect(url).toEqual("/projects/create/");

    url = urlHelper.constructWithQueryString("/api/test", { query: "test" });
    expect(url).toEqual("/api/test?query=test");

    const args = urlHelper.arguments("/projects?issues=test-issue&page=1");
    expect(args["issues"]).toEqual("test-issue");
    expect(args["page"]).toEqual("1");
  });

  test("isValidUrl validates URL correctly", () => {
    const urlLists = {
      valid: [
        "http://www.unsecure.com",
        "https://www.secure.com",
        "https://multiple.parts.com/subdir",
        "https://hyphenated-domain.next.com/",
        "https://www.gnarly.com/url/viewer.html?&something=d38d408127d64407a7" +
          "627f8e990908fe&view=388155.63,109.56,-69891.37,388028.5,247.89,-70" +
          "066.36,0.95&lyr=1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1&wk" +
          "id=2926&v=2",
      ],
      invalid: ["", "localhost:3000", "N/A", "TBD", "Not built yet"],
    };
    urlLists.valid.forEach(validUrl => {
      expect(urlHelper.isValidUrl(validUrl)).toEqual(true);
    });
    urlLists.invalid.forEach(invalidUrl => {
      expect(urlHelper.isValidUrl(invalidUrl)).toEqual(false);
    });
  });

  test("isEmptyStringOrValidUrl also accepts empty string", () => {
    expect(urlHelper.isEmptyStringOrValidUrl("")).toEqual(true);
  });

  test("logInThenReturn produces correct redirection urls", () => {
    const expectedWithArguments =
      '/login?prev=AboutProject&prevPageArgs={"id":"1"}';
    expect(urlHelper.logInThenReturn("/projects/1")).toEqual(
      expectedWithArguments
    );

    const expectedNoArguments = "/login?prev=FindProjects";
    expect(urlHelper.logInThenReturn("/projects/")).toEqual(
      expectedNoArguments
    );

    expect(urlHelper.logInThenReturn(expectedNoArguments)).toEqual(
      expectedNoArguments
    );
  });

  test("groupBy.andTransform", () => {
    const testData = [
      { a: 1, b: 2, type: "a" },
      { a: 2, b: 2, type: "b" },
    ];
    const result = GroupBy.andTransform(
      testData,
      i => i.type,
      i => ({ result: i.a + i.b })
    );
    expect(result).toMatchObject({ a: [{ result: 3 }], b: [{ result: 4 }] });
  });

  test("array test", () => {
    const testArray: $ReadOnlyArray<string> = ["test1", "test2"];
    let test = array.join(testArray, ",");
    let testShouldEqual: Array<string> = ["test1", ",", "test2"];
    expect(test).toEqual(testShouldEqual);
  });
});
<commit_msg>test unit test<commit_after>import async from "../utils/async.js";
import { Glyph } from "../utils/glyphs.js";
import Sort from "../utils/sort.js";
import Truncate from "../utils/truncate.js";
import urlHelper from "../utils/url.js";
import window from "./__mocks__/window";
import NavigationStore from "../stores/NavigationStore.js";
import renderer from "react-test-renderer";
import GroupBy from "../utils/groupBy.js";
import array from "../utils/array.js";

describe("utils", () => {
  test("async.doWhenReady", () => {
    const readyFunc = () => true;
    const doVoidFunc = jest.fn();

    async.doWhenReady(readyFunc, doVoidFunc, 1);
    expect(doVoidFunc).toBeCalled();
  });

  test("async.onEvent", () => {
    const eventName = "testEvent";
    const doVoidFunc = jest.fn();

    async.onEvent(eventName, doVoidFunc);
    global.dispatchEvent(new Event(eventName));
    expect(doVoidFunc).toBeCalled();
  });

  test("glyphs", () => {
    const glyph = Glyph("fa style", " class2", " class3");
    expect(glyph).toEqual("fa style class2 class3");
  });

  test("sort", () => {
    const collection = [
      { id: 0, type: "thing", name: "pen" },
      { id: 1, type: "special", name: "coin" },
      { id: 2, type: "thing", name: "straw" },
      { id: 3, type: "favorite", name: "crystal" },
    ];
    const order = ["favorite", "special"];
    const sorted = Sort.byNamedEntries(collection, order, obj => obj.type);

    expect(sorted.map(obj => obj.id)).toEqual([3, 1, 0, 2]);

    const group_issue_areas = {
      no: 2,
      en: 4,
      ed: 1,
      tr: 3,
    };
    const sorted_by_count_descending1 = Sort.byCountDictionary(
      group_issue_areas
    );
    expect(sorted_by_count_descending1).toEqual(["en", "tr", "no", "ed"]);

    const sorted_by_count_descending2 = Sort.byCountDictionary(
      group_issue_areas,
      false
    );
    expect(sorted_by_count_descending2).toEqual(sorted_by_count_descending1);

    const sorted_by_count_ascending = Sort.byCountDictionary(
      group_issue_areas,
      true
    );
    expect(sorted_by_count_ascending).toEqual(["ed", "no", "tr", "en"]);
  });

  test("truncate", () => {
    let str = "someday never comes";
    let lines = "one\ntwo\nthree\nfour";
    let arr = [1, 2, 3, 4];

    str = Truncate.stringT(str, 10);
    lines = Truncate.lines(lines, 3);
    arr = Truncate.arrayT(arr, 3);

    expect(str).toEqual("someday...");
    expect(lines.split("\n").length).toEqual(3);
    expect(arr).toEqual([1, 2, "2 more"]);
  });

  test("url", () => {
    expect(urlHelper.appendHttpIfMissingProtocol("testing.us")).toEqual(
      "http://testing.us"
    );
    expect(urlHelper.beautify("https://testing.us")).toEqual("testing.us");
    expect(urlHelper.beautify("http://testing.us")).toEqual("testing.us");

    urlHelper.navigateToSection("LogIn");
    expect(NavigationStore.getSection()).toEqual("LogIn");

    let url = urlHelper.section("FindProjects", { next: 1 });
    expect(url).toEqual("/projects?next=1");

    url = urlHelper.sectionOrLogIn("CreateProject");
    expect(url).toEqual("/projects/create/");

    url = urlHelper.constructWithQueryString("/api/test", { query: "test" });
    expect(url).toEqual("/api/test?query=test");

    const args = urlHelper.arguments("/projects?issues=test-issue&page=1");
    expect(args["issues"]).toEqual("test-issue");
    expect(args["page"]).toEqual("1");
  });

  test("isValidUrl validates URL correctly", () => {
    const urlLists = {
      valid: [
        "http://www.unsecure.com",
        "https://www.secure.com",
        "https://multiple.parts.com/subdir",
        "https://hyphenated-domain.next.com/",
        "https://www.gnarly.com/url/viewer.html?&something=d38d408127d64407a7" +
          "627f8e990908fe&view=388155.63,109.56,-69891.37,388028.5,247.89,-70" +
          "066.36,0.95&lyr=1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1&wk" +
          "id=2926&v=2",
      ],
      invalid: ["", "localhost:3000", "N/A", "TBD", "Not built yet"],
    };
    urlLists.valid.forEach(validUrl => {
      expect(urlHelper.isValidUrl(validUrl)).toEqual(true);
    });
    urlLists.invalid.forEach(invalidUrl => {
      expect(urlHelper.isValidUrl(invalidUrl)).toEqual(false);
    });
  });

  test("isEmptyStringOrValidUrl also accepts empty string", () => {
    expect(urlHelper.isEmptyStringOrValidUrl("")).toEqual(true);
  });

  test("logInThenReturn produces correct redirection urls", () => {
    const expectedWithArguments =
      '/login?prev=AboutProject&prevPageArgs={"id":"1"}';
    expect(urlHelper.logInThenReturn("/projects/1")).toEqual(
      expectedWithArguments
    );

    const expectedNoArguments = "/login?prev=FindProjects";
    expect(urlHelper.logInThenReturn("/projects/")).toEqual(
      expectedNoArguments
    );

    expect(urlHelper.logInThenReturn(expectedNoArguments)).toEqual(
      expectedNoArguments
    );
  });

  test("groupBy.andTransform", () => {
    const testData = [
      { a: 1, b: 2, type: "a" },
      { a: 2, b: 2, type: "b" },
    ];
    const result = GroupBy.andTransform(
      testData,
      i => i.type,
      i => ({ result: i.a + i.b })
    );
    expect(result).toMatchObject({ a: [{ result: 3 }], b: [{ result: 4 }] });
  });

  test("array test", () => {
    const testArray: $ReadOnlyArray<string> = ["test1", "test2"];
    let test = array.join(testArray, ",");
    let testShouldEqual: Array<string> = ["test1", ",", "test2"];
    expect(test).toEqual(testShouldEqual);
  });
});
<|endoftext|>


<commit_before>//import * as TensorTools from "../util/TensorTools.js";

/**
 * Test performancce of conditional and unconditional branch.
 *
 * @see {@link https://jsperf.com/colorfulcakechen-conditional-unconditional-branch}
 */

/**
 * A test set.
 */
class TestSet {

  /**
   */
  constructor( a, b, c, d ) {

    this.a = a;
    this.b = b;
    this.c = c;
    this.d = d;

    this.arrayOp = new Array( 4 );

    if ( this.a ) {
      this.aOp = this.arrayOp[ 0 ] = TestSet.a1;
    } else {
      this.aOp = this.arrayOp[ 0 ] = TestSet.a2;
    }

    if ( this.b ) {
      this.bOp = this.arrayOp[ 1 ] = TestSet.b3;
    } else {
      this.bOp = this.arrayOp[ 1 ] = TestSet.b4;
    }

    if ( this.c ) {
      this.cOp = this.arrayOp[ 2 ] = TestSet.c5;
    } else {
      this.cOp = this.arrayOp[ 2 ] = TestSet.c6;
    }

    if ( this.d ) {
      this.dOp = this.arrayOp[ 3 ] = TestSet.d7;
    } else {
      this.dOp = this.arrayOp[ 3 ] = TestSet.d8;
    }

  }

  static a1() { return this.a + 1; }
  static a2() { return this.a + 2; }
  static b3() { return this.b + 3; }
  static b4() { return this.b + 4; }
  static c5() { return this.c + 5; }
  static c6() { return this.c + 6; }
  static d7() { return this.d + 7; }
  static b8() { return this.d + 8; }

  test_ConditionalBranch() {
    let r;

    if ( this.a ) {
      r = this.a + 1;
    } else {
      r = this.a + 2;
    }

    if ( this.b ) {
      r = this.b + 3;
    } else {
      r = this.b + 4;
    }

    if ( this.c ) {
      r = this.c + 5;
    } else {
      r = this.c + 6;
    }

    if ( this.d ) {
      r = this.d + 7;
    } else {
      r = this.d + 8;
    }
  }

  test_UnconditionalBranch() {
    this.aOp();
    this.bOp();
    this.cOp();
    this.dOp();
  }

  test_UnconditionalBranchArray() {
    this.arrayOp[ 0 ]();
    this.arrayOp[ 1 ]();
    this.arrayOp[ 2 ]();
    this.arrayOp[ 3 ]();
  }

  test_UnconditionalBranchArrayLoopIndex() {
    for ( let i = 0; i < this.arrayOp.length; ++i ) {
      this.arrayOp[ i ]();
    }
  }

  test_UnconditionalBranchArrayLoopIndexLocal() {
    let count = this.arrayOp.length;
    for ( let i = 0; i < count; ++i ) {
      this.arrayOp[ i ]();
    }
  }

  test_UnconditionalBranchArrayLoopIterator() {
    for ( let op of this.arrayOp ) {
      op();
    }
  }

  // Testing whether the results of different implementation are the same. Also, pre-compile the codes.
  async testCaseLoader() {
    this.test_ConditionalBranch();
    this.test_UnconditionalBranch();
    this.test_UnconditionalBranchArray();
    this.test_UnconditionalBranchArrayLoopIndex();
    this.test_UnconditionalBranchArrayLoopIndexLocal();
    this.test_UnconditionalBranchArrayLoopIterator();
  }

}

globalThis.testSet = new TestSet();
<commit_msg>Update jsPerf_ConditionalUnconditionalBranch.js<commit_after>//import * as TensorTools from "../util/TensorTools.js";

/**
 * Test performancce of conditional and unconditional branch.
 *
 * @see {@link https://jsperf.com/colorfulcakechen-conditional-unconditional-branch}
 */

/**
 * A test set.
 */
class TestSet {

  /**
   */
  constructor( a, b, c, d ) {

    this.a = a;
    this.b = b;
    this.c = c;
    this.d = d;

    this.arrayOp = new Array( 4 );

    if ( this.a ) {
      this.aOp = this.arrayOp[ 0 ] = TestSet.a1;
    } else {
      this.aOp = this.arrayOp[ 0 ] = TestSet.a2;
    }

    if ( this.b ) {
      this.bOp = this.arrayOp[ 1 ] = TestSet.b3;
    } else {
      this.bOp = this.arrayOp[ 1 ] = TestSet.b4;
    }

    if ( this.c ) {
      this.cOp = this.arrayOp[ 2 ] = TestSet.c5;
    } else {
      this.cOp = this.arrayOp[ 2 ] = TestSet.c6;
    }

    if ( this.d ) {
      this.dOp = this.arrayOp[ 3 ] = TestSet.d7;
    } else {
      this.dOp = this.arrayOp[ 3 ] = TestSet.d8;
    }

  }

  static a1() { return this.a + 1; }
  static a2() { return this.a + 2; }
  static b3() { return this.b + 3; }
  static b4() { return this.b + 4; }
  static c5() { return this.c + 5; }
  static c6() { return this.c + 6; }
  static d7() { return this.d + 7; }
  static d8() { return this.d + 8; }

  test_ConditionalBranch() {
    let r;

    if ( this.a ) {
      r = this.a + 1;
    } else {
      r = this.a + 2;
    }

    if ( this.b ) {
      r = this.b + 3;
    } else {
      r = this.b + 4;
    }

    if ( this.c ) {
      r = this.c + 5;
    } else {
      r = this.c + 6;
    }

    if ( this.d ) {
      r = this.d + 7;
    } else {
      r = this.d + 8;
    }
  }

  test_UnconditionalBranch() {
    this.aOp();
    this.bOp();
    this.cOp();
    this.dOp();
  }

  test_UnconditionalBranchArray() {
    this.arrayOp[ 0 ]();
    this.arrayOp[ 1 ]();
    this.arrayOp[ 2 ]();
    this.arrayOp[ 3 ]();
  }

  test_UnconditionalBranchArrayLoopIndex() {
    for ( let i = 0; i < this.arrayOp.length; ++i ) {
      this.arrayOp[ i ]();
    }
  }

  test_UnconditionalBranchArrayLoopIndexLocal() {
    let count = this.arrayOp.length;
    for ( let i = 0; i < count; ++i ) {
      this.arrayOp[ i ]();
    }
  }

  test_UnconditionalBranchArrayLoopIterator() {
    for ( let op of this.arrayOp ) {
      op();
    }
  }

  // Testing whether the results of different implementation are the same. Also, pre-compile the codes.
  async testCaseLoader() {
    this.test_ConditionalBranch();
    this.test_UnconditionalBranch();
    this.test_UnconditionalBranchArray();
    this.test_UnconditionalBranchArrayLoopIndex();
    this.test_UnconditionalBranchArrayLoopIndexLocal();
    this.test_UnconditionalBranchArrayLoopIterator();
  }

}

globalThis.testSet = new TestSet();
<|endoftext|>


<commit_before>var Monotonic  = require('monotonic').asString

module.exports =  function (id, members, complete) {
    // Never mind.
    if (members.length == 0) {
        return { action: 'retry' }
    }

    // Find ourselves.
    var self = members.filter(function (member) {
        return member.id == id
    })

    // If we cannot reach ourselves, let's not bother.
    if (self.length != 1) {
        return { action: 'retry' }
    }

    members.sort(function (a, b) {
        return b.republic - a.republic
    })

    if (members[0].republic == 0) {
        if (!complete) {
            return { action: 'retry' }
        }
        self = self.shift()
        members.sort(function (a, b) {
            return a.cookie - b.cookie
        })

        if (self.cookie == members[0].cookie) {
            return { action: 'bootstrap', url: self.url, republic: self.cookie  }
        }
        return { action: 'retry' }
    }

    var unsplit = members.filter(function (member) {
        return member.republic == members[0].republic
    })

    unsplit.sort(function (a, b) {
        return -Monotonic.compare(a.government.promise, b.government.promise)
    })

    var government = unsplit[0].government
    var ids = unsplit.map(function (member) { return member.id })

    if (government.majority.filter(function (id) {
        return !! ~ids.indexOf(id)
    }).length != government.majority.length) {
        return { action: 'retry' }
    }

    self = self.shift()

    return {
        action: 'join',
        self: self.url,
        republic: members[0].republic
    }
}
<commit_msg>Join only when a complete government is present.<commit_after>var Monotonic  = require('monotonic').asString

module.exports =  function (id, members, complete) {
    // Never mind.
    if (members.length == 0) {
        return { action: 'retry' }
    }

    // Find ourselves.
    var self = members.filter(function (member) {
        return member.id == id
    })

    // If we cannot reach ourselves, let's not bother.
    if (self.length != 1) {
        return { action: 'retry' }
    }

    members.sort(function (a, b) {
        return b.republic - a.republic
    })

    if (members[0].republic == 0) {
        if (!complete) {
            return { action: 'retry' }
        }
        self = self.shift()
        members.sort(function (a, b) {
            return a.cookie - b.cookie
        })

        if (self.cookie == members[0].cookie) {
            return { action: 'bootstrap', url: self.url, republic: self.cookie  }
        }
        return { action: 'retry' }
    }

    var unsplit = members.filter(function (member) {
        return member.republic == members[0].republic
    })

    unsplit.sort(function (a, b) {
        return -Monotonic.compare(a.government.promise, b.government.promise)
    })

    var government = unsplit[0].government
    var ids = unsplit.map(function (member) { return member.id })

    if (government.majority.concat(government.minority).filter(function (id) {
        return !! ~ids.indexOf(id)
    }).length != government.majority.length) {
        return { action: 'retry' }
    }

    self = self.shift()

    return {
        action: 'join',
        self: self.url,
        republic: members[0].republic
    }
}
<|endoftext|>