<commit_before>// Copyright 2002-2015, University of Colorado Boulder

/**
 * Grunt configuration file for PhET projects.
 * Requires a package.json file containing project settings.
 * Tasks share information via global.phet, see individual tasks for details.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 * @author Jon Olson
 * @author Sam Reid
 * @author John Blanco
 */

// built-in node APIs
var assert = require( 'assert' );
var fs = require( 'fs' );

// 3rd-party packages
/* jshint -W079 */
var _ = require( '../../../sherpa/lib/lodash-2.4.1.min' ); // allow _ to be redefined, contrary to jshintOptions.js
/* jshint +W079 */

// PhET custom grunt tasks
var afterRequirejsBuild = require( '../../../chipper/js/grunt/afterRequirejsBuild' );
var beforeRequirejsBuild = require( '../../../chipper/js/grunt/beforeRequirejsBuild' );
var checkoutShas = require( '../../../chipper/js/grunt/checkoutShas' );
var createSim = require( '../../../chipper/js/grunt/createSim' );
var deploySimulation = require( '../../../chipper/js/grunt/deploySimulation' );
var deployDev = require( '../../../chipper/js/grunt/deployDev' );
var generateREADME = require( '../../../chipper/js/grunt/generateREADME' );
var generateThumbnails = require( '../../../chipper/js/grunt/generateThumbnails' );
var pullAll = require( '../../../chipper/js/grunt/pullAll' );
var setPreload = require( '../../../chipper/js/grunt/setPreload' );
var stringReport = require( '../../../chipper/js/grunt/stringReport' );
var reportMedia = require( '../../../chipper/js/grunt/reportMedia' );
var reportThirdParty = require( '../../../chipper/js/grunt/reportThirdParty' );

//TODO look at why this is necessary
/*
 * In Node, global is the global namespace object.
 * Register fs as a global so it can be accessed through the requirejs build system. Text.js plugin
 * may have a superior way to handle this but I (SR) couldn't get it working after a small amount of effort.
 */
global.fs = fs;

module.exports = function( grunt ) {
  'use strict';

  var FALLBACK_LOCAL = 'en';

  // Read package.json, verify that it contains properties required by all PhET repositories
  assert( fs.existsSync( 'package.json' ), 'repository must have a package.json' );
  var pkg = grunt.file.readJSON( 'package.json' );
  assert( pkg.name, 'name missing from package.json' );
  assert( pkg.version, 'version missing from package.json' );
  assert( pkg.license, 'license missing from package.json' );

  // For sims, read common phetLibs from chipper/build.json. We'll do this here since several grunt tasks (including some utilities) need phetLibs.
  if ( pkg.phetLibs ) {
    assert( fs.existsSync( '../chipper/build.json' ), 'missing build.json' );
    var buildInfo = grunt.file.readJSON( '../chipper/build.json' );
    pkg.phetLibs = _.uniq( pkg.phetLibs.concat( buildInfo.common.phetLibs ).sort() );
    grunt.log.debug( 'phetLibs = ' + pkg.phetLibs );
  }

  // TODO: As a temporary means of keeping track of "together" versions, replace "-dev" with "-together" in the version
  // string. This approach has a lot of problems and should be replaced as soon as we work out a more all encompassing
  // way of tracking together-enhanced versions.  See https://github.com/phetsims/special-ops/issues/3 for more info.
  if ( grunt.option( 'together' ) && pkg.version.indexOf( '-dev' ) > -1 ) {
    pkg.version = pkg.version.replace( '-dev', '-together' );
  }

  var globalDefs = {
    // global assertions
    assert: false,
    assertSlow: false,
    // scenery logging
    sceneryLog: false,
    sceneryLayerLog: false,
    sceneryEventLog: false,
    sceneryAccessibilityLog: false,
    phetAllocation: false
  };

  // Enumerate the list of files to be linted for the jshint:allFiles task
  var allFilesToLint = _.map( pkg.phetLibs, function( repo ) {
    return '../' + repo + '/js/**/*.js';
  } );

  // The brand repo has a different structure, so add it explicitly
  allFilesToLint.push( '../brand/*/js/**/*.js' );

  // Don't try to lint the svgPath.js.  It was automatically generated and doesn't match convention
  allFilesToLint.push( '!../kite/js/parser/svgPath.js' );

  // Identify the repo files to lint for the single repo.
  // The brand repo is a special case since it has nested subdirectories instead of a top level js/ directory
  var repoFilesToLint = pkg.name === 'brand' ? [ '*/js/**/*.js' ] :

    // Default case for repo files to lint
    [ 'js/**/*.js' ];
  grunt.initConfig( {
    /*
     * Read in the project settings from the package.json file into the pkg property.
     * This allows us to refer to project settings from within this config file.
     */
    pkg: pkg,

    // configure the RequireJS plugin
    requirejs: {

      // builds the minified script
      build: {
        options: {
          almond: true,
          mainConfigFile: 'js/<%= pkg.name %>-config.js',
          out: 'build/<%= pkg.name %>.min.js',
          name: '<%= pkg.name %>-config',

          // Minification strategy.  Put this to none if you want to debug a non-minified but compiled version
          optimize: 'uglify2',
          wrap: true,
//            generateSourceMaps: true, //#42 commented out this line until source maps are fixed
          preserveLicenseComments: false,
          uglify2: {
            output: {
              inline_script: true // escape </script
            },
            compress: {
              global_defs: globalDefs,
              dead_code: true
            }
          },

          //stub out the plugins so their source code won't be included in the minified file
          stubModules: [ 'string', 'audio', 'image' ]
        }
      }
    },

    // configure the JSHint plugin
    jshint: {

      // source files that are specific to this repository
      repoFiles: repoFilesToLint,

      // All source files for this repository (repository-specific and dependencies).
      // Excludes kite/js/parser/svgPath.js, which is auto-generated.
      allFiles: allFilesToLint,

      // reference external JSHint options in jshintOptions.js
      options: require( './jshintOptions' )
    }
  } );

  //---------------------------------------------------------------------------------------------------------------
  // Primary tasks
  //---------------------------------------------------------------------------------------------------------------

  // Default task ('grunt')

  grunt.registerTask( 'default', 'Builds the English HTML', [ 'build' ] );

  grunt.registerTask( 'build',
    'Builds the simulation:\n' +
    'with no options, builds HTML for English only\n' +
    '--locales=* : all locales in strings/ directory\n' +
    '--locales=fr : French\n' +
    '--locales=ar,fr,es : Arabic, French and Spanish (comma separated locales)\n' +
    '--localesRepo=$repo : all locales in another repository\'s strings/ directory, ignored if --locales is present\n' +
    '--together : adds additional preload files needed to support together.js',
    [ 'lint-all', 'build-no-lint' ]
  );

  grunt.registerTask( 'build-no-lint',
    'identical to "build", but does not run "lint-all"',
    [ 'clean', 'set-preload', 'before-requirejs-build', 'requirejs:build', 'after-requirejs-build' ] );

  grunt.registerTask( 'deploy-production',
    'Deploy a simulation. Should be run AFTER grunt build since it uses the shas from dependencies.json in the build directory.\n' +
    'with no options, deploys to phet-dev (since this is still being tested)\n' +
    '--devDeploy : deploys to spot',
    function() {
      deploySimulation( grunt, grunt.option( 'devDeploy' ) );
    }
  );

  grunt.registerTask( 'deploy-dev',
    'Deploy a dev version to spot, or optionally to the server in your preferences file\n' +
    '--buildServer : build the sim with the build server if true\n' +
    '--mkdir : set to true to create the sim dir and .htaccess file before copying the version directory\n' +
    '--test : set to true to disable commit and push, and SCP to a test directory on spot',
    function() {
      if ( grunt.option( 'buildServer' ) ) {
        deploySimulation( grunt, 'simian' );
      }
      else {
        deployDev( grunt, grunt.option( 'mkdir' ), grunt.option( 'test' ) );
      }
    }
  );

  grunt.registerTask( 'lint', 'lint js files that are specific to this repository', [ 'jshint:repoFiles' ] );

  grunt.registerTask( 'lint-all', 'lint all js files that are required to build this repository', [ 'jshint:allFiles' ] );

  grunt.registerTask( 'clean',
    'Erases the build/ directory and all its contents, and recreates the build/ directory',
    function() {
      if ( fs.existsSync( 'build' ) ) {
        grunt.file.delete( 'build' );
      }
      grunt.file.mkdir( 'build' );
    } );

  grunt.registerTask( 'set-preload',
    '(internal use only) Determines the set of files that will be preloaded in the HTML file',
    function() {
      setPreload( grunt, pkg );
    } );

  grunt.registerTask( 'before-requirejs-build',
    '(internal use only) Do things before the requirejs:build task',
    function() {
      assert( pkg.phetLibs, 'phetLibs missing from package.json' );
      assert( pkg.preload, 'preload missing from package.json' );
      beforeRequirejsBuild( grunt, pkg, FALLBACK_LOCAL );
    } );

  grunt.registerTask( 'after-requirejs-build',
    '(internal use only) Do things after the requirejs:build task',
    function() {
      afterRequirejsBuild( grunt, pkg, FALLBACK_LOCAL );
    } );

  //---------------------------------------------------------------------------------------------------------------
  // Utility tasks
  //---------------------------------------------------------------------------------------------------------------

  grunt.registerTask( 'checkout-shas',
    'Check out shas for a project, as specified in dependencies.json',
    function() {
      var buildServer = grunt.option( 'buildServer' ) ? true : false;
      checkoutShas( grunt, pkg.name, false, buildServer );
    } );

  grunt.registerTask( 'checkout-master',
    'Check out master branch for all dependencies, as specified in dependencies.json',
    function() {
      checkoutShas( grunt, pkg.name, true );
    } );

  grunt.registerTask( 'create-sim',
    'Creates a sim based on the simula-rasa template.\n' +
    '--name="string" : the repository name\n' +
    '--author="string" : the author name\n' +
    '--title="string" : (optional) the simulation title\n' +
    '--clean=true : (optional) deletes the repository directory if it exists',
    function() {
      createSim( grunt, grunt.option( 'name' ), grunt.option( 'author' ), grunt.option( 'title' ), grunt.option( 'clean' ) );
    } );

  // See reportMedia.js
  grunt.registerTask( 'report-media', '(project-wide) Report on license.json files throughout all working copies. ' +
                                      'Reports any media (such as images or audio) files that have any of the following problems:\n' +
                                      '(1) missing-license.json (no license.json for the resource)\n' +
                                      '(2) incompatible-license (resource license not approved)\n' +
                                      '(3) not-annotated (entry missing from license.json)\n' +
                                      '(4) missing-file (entry in the license.json but not on the file system)', function() {
    reportMedia( grunt );
  } );

  // see reportThirdParty.js
  grunt.registerTask( 'report-third-party', 'Given a directory of HTML files (required argument), report the ' +
                                            'third party code, images, audio, etc. used in the group ' +
                                            'and commit the changes to sherpa/third-party-licenses.json. ' +
                                            'If the "--copy-from-build" option is given, it will iterate over the active-runnables ' +
                                            'and copy all the files to the report HTML directory (required argument)', function( path ) {
    assert( path, 'The path is a required grunt argument (use taskname:argument)' );
    reportThirdParty( grunt, path );
  } );

  grunt.registerTask( 'published-README',
    'Generates README.md file for a published simulation.',
    function() {
      assert( pkg.phetLibs, 'phetLibs missing from package.json' );
      assert( pkg.simTitleStringKey, 'simTitleStringKey missing from package.json' );
      generateREADME( grunt, pkg.name, pkg.phetLibs, pkg.simTitleStringKey, true /* published */ );
    } );

  grunt.registerTask( 'unpublished-README',
    'Generates README.md file for an unpublished simulation.',
    function() {
      assert( pkg.phetLibs, 'phetLibs missing from package.json' );
      assert( pkg.simTitleStringKey, 'simTitleStringKey missing from package.json' );
      generateREADME( grunt, pkg.name, pkg.phetLibs, pkg.simTitleStringKey, false /* published */ );
    } );

  grunt.registerTask( 'pull-all',
    'Pull all sibling repositories',
    function() {
      pullAll( grunt );
    } );

  grunt.registerTask( 'string-report',
    'After doing a build, reports on which strings are missing for each locale that was built.',
    function() {
      stringReport( grunt, pkg.name, FALLBACK_LOCAL );
    } );

  grunt.registerTask( 'generate-128-thumbnail', 'Generate 128x84 thumbnail', function() {
    generateThumbnails( grunt, pkg.name, 128, 84 );
  } );

  grunt.registerTask( 'generate-600-thumbnail', 'Generate 600x394 thumbnail', function() {
    generateThumbnails( grunt, pkg.name, 600, 394 );
  } );

  grunt.registerTask( 'generate-thumbnails', 'Generate thumbnails', [ 'generate-128-thumbnail', 'generate-600-thumbnail' ] );

  /*
   * Load tasks from grunt plugins that have been installed locally using npm.
   * Put these in package.json and run 'npm install' before running grunt.
   */
  grunt.loadNpmTasks( 'grunt-requirejs' );
  grunt.loadNpmTasks( 'grunt-contrib-jshint' );
};
<commit_msg>Converted build-no-lint to build --lint=false, see #231<commit_after>// Copyright 2002-2015, University of Colorado Boulder

/**
 * Grunt configuration file for PhET projects.
 * Requires a package.json file containing project settings.
 * Tasks share information via global.phet, see individual tasks for details.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 * @author Jon Olson
 * @author Sam Reid
 * @author John Blanco
 */

// built-in node APIs
var assert = require( 'assert' );
var fs = require( 'fs' );

// 3rd-party packages
/* jshint -W079 */
var _ = require( '../../../sherpa/lib/lodash-2.4.1.min' ); // allow _ to be redefined, contrary to jshintOptions.js
/* jshint +W079 */

// PhET custom grunt tasks
var afterRequirejsBuild = require( '../../../chipper/js/grunt/afterRequirejsBuild' );
var beforeRequirejsBuild = require( '../../../chipper/js/grunt/beforeRequirejsBuild' );
var checkoutShas = require( '../../../chipper/js/grunt/checkoutShas' );
var createSim = require( '../../../chipper/js/grunt/createSim' );
var deploySimulation = require( '../../../chipper/js/grunt/deploySimulation' );
var deployDev = require( '../../../chipper/js/grunt/deployDev' );
var generateREADME = require( '../../../chipper/js/grunt/generateREADME' );
var generateThumbnails = require( '../../../chipper/js/grunt/generateThumbnails' );
var pullAll = require( '../../../chipper/js/grunt/pullAll' );
var setPreload = require( '../../../chipper/js/grunt/setPreload' );
var stringReport = require( '../../../chipper/js/grunt/stringReport' );
var reportMedia = require( '../../../chipper/js/grunt/reportMedia' );
var reportThirdParty = require( '../../../chipper/js/grunt/reportThirdParty' );

//TODO look at why this is necessary
/*
 * In Node, global is the global namespace object.
 * Register fs as a global so it can be accessed through the requirejs build system. Text.js plugin
 * may have a superior way to handle this but I (SR) couldn't get it working after a small amount of effort.
 */
global.fs = fs;

module.exports = function( grunt ) {
  'use strict';

  var FALLBACK_LOCAL = 'en';

  // Read package.json, verify that it contains properties required by all PhET repositories
  assert( fs.existsSync( 'package.json' ), 'repository must have a package.json' );
  var pkg = grunt.file.readJSON( 'package.json' );
  assert( pkg.name, 'name missing from package.json' );
  assert( pkg.version, 'version missing from package.json' );
  assert( pkg.license, 'license missing from package.json' );

  // For sims, read common phetLibs from chipper/build.json. We'll do this here since several grunt tasks (including some utilities) need phetLibs.
  if ( pkg.phetLibs ) {
    assert( fs.existsSync( '../chipper/build.json' ), 'missing build.json' );
    var buildInfo = grunt.file.readJSON( '../chipper/build.json' );
    pkg.phetLibs = _.uniq( pkg.phetLibs.concat( buildInfo.common.phetLibs ).sort() );
    grunt.log.debug( 'phetLibs = ' + pkg.phetLibs );
  }

  // TODO: As a temporary means of keeping track of "together" versions, replace "-dev" with "-together" in the version
  // string. This approach has a lot of problems and should be replaced as soon as we work out a more all encompassing
  // way of tracking together-enhanced versions.  See https://github.com/phetsims/special-ops/issues/3 for more info.
  if ( grunt.option( 'together' ) && pkg.version.indexOf( '-dev' ) > -1 ) {
    pkg.version = pkg.version.replace( '-dev', '-together' );
  }

  var globalDefs = {
    // global assertions
    assert: false,
    assertSlow: false,
    // scenery logging
    sceneryLog: false,
    sceneryLayerLog: false,
    sceneryEventLog: false,
    sceneryAccessibilityLog: false,
    phetAllocation: false
  };

  // Enumerate the list of files to be linted for the jshint:allFiles task
  var allFilesToLint = _.map( pkg.phetLibs, function( repo ) {
    return '../' + repo + '/js/**/*.js';
  } );

  // The brand repo has a different structure, so add it explicitly
  allFilesToLint.push( '../brand/*/js/**/*.js' );

  // Don't try to lint the svgPath.js.  It was automatically generated and doesn't match convention
  allFilesToLint.push( '!../kite/js/parser/svgPath.js' );

  // Identify the repo files to lint for the single repo.
  // The brand repo is a special case since it has nested subdirectories instead of a top level js/ directory
  var repoFilesToLint = pkg.name === 'brand' ? [ '*/js/**/*.js' ] :

    // Default case for repo files to lint
    [ 'js/**/*.js' ];
  grunt.initConfig( {
    /*
     * Read in the project settings from the package.json file into the pkg property.
     * This allows us to refer to project settings from within this config file.
     */
    pkg: pkg,

    // configure the RequireJS plugin
    requirejs: {

      // builds the minified script
      build: {
        options: {
          almond: true,
          mainConfigFile: 'js/<%= pkg.name %>-config.js',
          out: 'build/<%= pkg.name %>.min.js',
          name: '<%= pkg.name %>-config',

          // Minification strategy.  Put this to none if you want to debug a non-minified but compiled version
          optimize: 'uglify2',
          wrap: true,
//            generateSourceMaps: true, //#42 commented out this line until source maps are fixed
          preserveLicenseComments: false,
          uglify2: {
            output: {
              inline_script: true // escape </script
            },
            compress: {
              global_defs: globalDefs,
              dead_code: true
            }
          },

          //stub out the plugins so their source code won't be included in the minified file
          stubModules: [ 'string', 'audio', 'image' ]
        }
      }
    },

    // configure the JSHint plugin
    jshint: {

      // source files that are specific to this repository
      repoFiles: repoFilesToLint,

      // All source files for this repository (repository-specific and dependencies).
      // Excludes kite/js/parser/svgPath.js, which is auto-generated.
      allFiles: allFilesToLint,

      // reference external JSHint options in jshintOptions.js
      options: require( './jshintOptions' )
    }
  } );

  //---------------------------------------------------------------------------------------------------------------
  // Primary tasks
  //---------------------------------------------------------------------------------------------------------------

  // Default task ('grunt')

  grunt.registerTask( 'default', 'Builds the English HTML', [ 'build' ] );

  // Add the linting step as an pre-build step.  Can be skipped with --lint=false
  var optionalTasks = [];
  if ( grunt.option( 'lint' ) === false ) {
  }
  else {
    optionalTasks.push( 'lint-all' );
  }

  grunt.registerTask( 'build',
    'Builds the simulation:\n' +
    'with no options, builds HTML for English only\n' +
    '--locales=* : all locales in strings/ directory\n' +
    '--locales=fr : French\n' +
    '--locales=ar,fr,es : Arabic, French and Spanish (comma separated locales)\n' +
    '--localesRepo=$repo : all locales in another repository\'s strings/ directory, ignored if --locales is present\n' +
    '--together : adds additional preload files needed to support together.js',
    optionalTasks.concat( [
      'clean',
      'set-preload',
      'before-requirejs-build',
      'requirejs:build',
      'after-requirejs-build' ] )
  );

  grunt.registerTask( 'deploy-production',
    'Deploy a simulation. Should be run AFTER grunt build since it uses the shas from dependencies.json in the build directory.\n' +
    'with no options, deploys to phet-dev (since this is still being tested)\n' +
    '--devDeploy : deploys to spot',
    function() {
      deploySimulation( grunt, grunt.option( 'devDeploy' ) );
    }
  );

  grunt.registerTask( 'deploy-dev',
    'Deploy a dev version to spot, or optionally to the server in your preferences file\n' +
    '--buildServer : build the sim with the build server if true\n' +
    '--mkdir : set to true to create the sim dir and .htaccess file before copying the version directory\n' +
    '--test : set to true to disable commit and push, and SCP to a test directory on spot',
    function() {
      if ( grunt.option( 'buildServer' ) ) {
        deploySimulation( grunt, 'simian' );
      }
      else {
        deployDev( grunt, grunt.option( 'mkdir' ), grunt.option( 'test' ) );
      }
    }
  );

  grunt.registerTask( 'lint', 'lint js files that are specific to this repository', [ 'jshint:repoFiles' ] );

  grunt.registerTask( 'lint-all', 'lint all js files that are required to build this repository', [ 'jshint:allFiles' ] );

  grunt.registerTask( 'clean',
    'Erases the build/ directory and all its contents, and recreates the build/ directory',
    function() {
      if ( fs.existsSync( 'build' ) ) {
        grunt.file.delete( 'build' );
      }
      grunt.file.mkdir( 'build' );
    } );

  grunt.registerTask( 'set-preload',
    '(internal use only) Determines the set of files that will be preloaded in the HTML file',
    function() {
      setPreload( grunt, pkg );
    } );

  grunt.registerTask( 'before-requirejs-build',
    '(internal use only) Do things before the requirejs:build task',
    function() {
      assert( pkg.phetLibs, 'phetLibs missing from package.json' );
      assert( pkg.preload, 'preload missing from package.json' );
      beforeRequirejsBuild( grunt, pkg, FALLBACK_LOCAL );
    } );

  grunt.registerTask( 'after-requirejs-build',
    '(internal use only) Do things after the requirejs:build task',
    function() {
      afterRequirejsBuild( grunt, pkg, FALLBACK_LOCAL );
    } );

  //---------------------------------------------------------------------------------------------------------------
  // Utility tasks
  //---------------------------------------------------------------------------------------------------------------

  grunt.registerTask( 'checkout-shas',
    'Check out shas for a project, as specified in dependencies.json',
    function() {
      var buildServer = grunt.option( 'buildServer' ) ? true : false;
      checkoutShas( grunt, pkg.name, false, buildServer );
    } );

  grunt.registerTask( 'checkout-master',
    'Check out master branch for all dependencies, as specified in dependencies.json',
    function() {
      checkoutShas( grunt, pkg.name, true );
    } );

  grunt.registerTask( 'create-sim',
    'Creates a sim based on the simula-rasa template.\n' +
    '--name="string" : the repository name\n' +
    '--author="string" : the author name\n' +
    '--title="string" : (optional) the simulation title\n' +
    '--clean=true : (optional) deletes the repository directory if it exists',
    function() {
      createSim( grunt, grunt.option( 'name' ), grunt.option( 'author' ), grunt.option( 'title' ), grunt.option( 'clean' ) );
    } );

  // See reportMedia.js
  grunt.registerTask( 'report-media', '(project-wide) Report on license.json files throughout all working copies. ' +
                                      'Reports any media (such as images or audio) files that have any of the following problems:\n' +
                                      '(1) missing-license.json (no license.json for the resource)\n' +
                                      '(2) incompatible-license (resource license not approved)\n' +
                                      '(3) not-annotated (entry missing from license.json)\n' +
                                      '(4) missing-file (entry in the license.json but not on the file system)', function() {
    reportMedia( grunt );
  } );

  // see reportThirdParty.js
  grunt.registerTask( 'report-third-party', 'Given a directory of HTML files (required argument), report the ' +
                                            'third party code, images, audio, etc. used in the group ' +
                                            'and commit the changes to sherpa/third-party-licenses.json. ' +
                                            'If the "--copy-from-build" option is given, it will iterate over the active-runnables ' +
                                            'and copy all the files to the report HTML directory (required argument)', function( path ) {
    assert( path, 'The path is a required grunt argument (use taskname:argument)' );
    reportThirdParty( grunt, path );
  } );

  grunt.registerTask( 'published-README',
    'Generates README.md file for a published simulation.',
    function() {
      assert( pkg.phetLibs, 'phetLibs missing from package.json' );
      assert( pkg.simTitleStringKey, 'simTitleStringKey missing from package.json' );
      generateREADME( grunt, pkg.name, pkg.phetLibs, pkg.simTitleStringKey, true /* published */ );
    } );

  grunt.registerTask( 'unpublished-README',
    'Generates README.md file for an unpublished simulation.',
    function() {
      assert( pkg.phetLibs, 'phetLibs missing from package.json' );
      assert( pkg.simTitleStringKey, 'simTitleStringKey missing from package.json' );
      generateREADME( grunt, pkg.name, pkg.phetLibs, pkg.simTitleStringKey, false /* published */ );
    } );

  grunt.registerTask( 'pull-all',
    'Pull all sibling repositories',
    function() {
      pullAll( grunt );
    } );

  grunt.registerTask( 'string-report',
    'After doing a build, reports on which strings are missing for each locale that was built.',
    function() {
      stringReport( grunt, pkg.name, FALLBACK_LOCAL );
    } );

  grunt.registerTask( 'generate-128-thumbnail', 'Generate 128x84 thumbnail', function() {
    generateThumbnails( grunt, pkg.name, 128, 84 );
  } );

  grunt.registerTask( 'generate-600-thumbnail', 'Generate 600x394 thumbnail', function() {
    generateThumbnails( grunt, pkg.name, 600, 394 );
  } );

  grunt.registerTask( 'generate-thumbnails', 'Generate thumbnails', [ 'generate-128-thumbnail', 'generate-600-thumbnail' ] );

  /*
   * Load tasks from grunt plugins that have been installed locally using npm.
   * Put these in package.json and run 'npm install' before running grunt.
   */
  grunt.loadNpmTasks( 'grunt-requirejs' );
  grunt.loadNpmTasks( 'grunt-contrib-jshint' );
};
<|endoftext|>


<commit_before>// Copyright 2002-2015, University of Colorado Boulder

/**
 * Grunt configuration file for PhET projects.
 * Tasks share information via global.phet, see individual tasks for details.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 * @author Jon Olson
 * @author Sam Reid
 * @author John Blanco
 */

// use process.env
/* jslint node: true */

// built-in node APIs
var assert = require( 'assert' );
var fs = require( 'fs' );

// 3rd-party packages
/* jshint -W079 */
var _ = require( '../../../sherpa/lib/lodash-2.4.1.min' ); // allow _ to be redefined, contrary to jshintOptions.js
/* jshint +W079 */

// PhET custom grunt tasks
var afterRequirejsBuild = require( '../../../chipper/js/grunt/afterRequirejsBuild' );
var checkoutShas = require( '../../../chipper/js/grunt/checkoutShas' );
var createSim = require( '../../../chipper/js/grunt/createSim' );
var deployProduction = require( '../../../chipper/js/grunt/deployProduction' );
var deployDev = require( '../../../chipper/js/grunt/deployDev' );
var generateREADME = require( '../../../chipper/js/grunt/generateREADME' );
var generateThumbnails = require( '../../../chipper/js/grunt/generateThumbnails' );
var reportMedia = require( '../../../chipper/js/grunt/reportMedia' );
var reportThirdParty = require( '../../../chipper/js/grunt/reportThirdParty' );
var getBuildConfig = require( '../../../chipper/js/grunt/getBuildConfig' );
var getGruntConfig = require( '../../../chipper/js/grunt/getGruntConfig' );

//TODO chipper#300 look at why this is necessary
/*
 * In Node, global is the global namespace object.
 * Register fs as a global so it can be accessed through the requirejs build system. Text.js plugin
 * may have a superior way to handle this but I (SR) couldn't get it working after a small amount of effort.
 */
global.fs = fs;

module.exports = function( grunt ) {
  'use strict';

  //---------------------------------------------------------------------------------------------------------------
  // Configuration

  var buildConfig = getBuildConfig( grunt );

  // Initialize and document all globals
  assert( !global.phet, 'global.phet already exists' );
  global.phet = {

    chipper: {

      // the grunt instance, for situations where we can't pass it as a function argument
      grunt: grunt,

      // polyfill to work around the cache buster arg in the *-config.js file that all sims have.
      getCacheBusterArgs: function() { return ''; },

      // media plugins populate this with license.json entries, see getLicenseEntry.js for format of entries
      licenseEntries: {},

      // use by media plugins, which don't have access to buildConfig
      brand: buildConfig.brand,

      // populated by mipmap.js
      mipmapsToBuild: [],

      // populated by string.js
      strings: {}
    }
  };

  // Initialize grunt
  grunt.initConfig( getGruntConfig( grunt, buildConfig.name, buildConfig.phetLibs ) );

  //---------------------------------------------------------------------------------------------------------------
  // Primary tasks
  //---------------------------------------------------------------------------------------------------------------

  // Default task ('grunt')

  grunt.registerTask( 'default', 'Builds the English HTML', [ 'build' ] );

  // Add the linting step as an pre-build step.  Can be skipped with --lint=false
  var optionalTasks = [];
  if ( grunt.option( 'lint' ) === false ) {
  }
  else {
    optionalTasks.push( 'lint-all' );
  }

  grunt.registerTask( 'build',
    'Builds the simulation:\n' +
    'with no options, builds HTML for English only\n' +
    '--locales=* : all locales in strings/ directory\n' +
    '--locales=fr : French\n' +
    '--locales=ar,fr,es : Arabic, French and Spanish (comma separated locales)\n' +
    '--localesRepo=$repo : all locales in another repository\'s strings/ directory, ignored if --locales is present\n' +
    '--lint=false : skip the lint sub-task',
    optionalTasks.concat( [
      'clean',
      'requirejs:build',
      'after-requirejs-build' ] )
  );

  grunt.registerTask( 'deploy-production',
    'Invoke deployDev and then deploy a simulation to the production server.\n' +
    'Should be run AFTER grunt build since it uses the shas from dependencies.json in the build directory.\n',
    'Deploys to figaro by default, but simian can be used for testing by setting:\n' +
    '"productionServerName": "simian.colorado.edu" and "productionServerURL": "https://phet-dev.colorado.edu" in build-local.json',
    function() {
      // before invoking the build-server, do a dev deploy, including committing and pushing to github
      deployDev( grunt );
      deployProduction( grunt );
    }
  );

  grunt.registerTask( 'deploy-dev',
    'Deploy a dev version to spot, or optionally to the server in your preferences file\n' +
    '--mkdir : set to true to create the sim dir and .htaccess file before copying the version directory\n' +
    '--test : set to true to disable commit and push, and SCP to a test directory on spot',
    function() {
      deployDev( grunt );
    }
  );

  grunt.registerTask( 'lint', 'lint js files that are specific to this repository', [ 'jshint:repoFiles' ] );

  grunt.registerTask( 'lint-all', 'lint all js files that are required to build this repository', [ 'jshint:allFiles' ] );

  grunt.registerTask( 'clean',
    'Erases the build/ directory and all its contents, and recreates the build/ directory',
    function() {
      if ( fs.existsSync( 'build' ) ) {
        grunt.file.delete( 'build' );
      }
      grunt.file.mkdir( 'build' );
    } );

  grunt.registerTask( 'after-requirejs-build',
    '(internal use only) Do things after the requirejs:build task',
    function() {
      afterRequirejsBuild( grunt, buildConfig );
    } );

  //---------------------------------------------------------------------------------------------------------------
  // Utility tasks
  //---------------------------------------------------------------------------------------------------------------

  grunt.registerTask( 'checkout-shas',
    'Check out shas for a project, as specified in dependencies.json',
    function() {
      checkoutShas( grunt, buildConfig.name, false /* toMaster */ );
    } );

  grunt.registerTask( 'checkout-master',
    'Check out master branch for all dependencies, as specified in dependencies.json',
    function() {
      checkoutShas( grunt, buildConfig.name, true /* toMaster */ );
    } );

  grunt.registerTask( 'create-sim',
    'Creates a sim based on the simula-rasa template.\n' +
    '--name="string" : the repository name\n' +
    '--author="string" : the author name\n' +
    '--title="string" : (optional) the simulation title\n' +
    '--clean=true : (optional) deletes the repository directory if it exists',
    function() {
      createSim( grunt );
    } );

  // See reportMedia.js
  grunt.registerTask( 'report-media', '(project-wide) Report on license.json files throughout all working copies. ' +
                                      'Reports any media (such as images or audio) files that have any of the following problems:\n' +
                                      '(1) incompatible-license (resource license not approved)\n' +
                                      '(2) not-annotated (license.json missing or entry missing from license.json)\n' +
                                      '(3) missing-file (entry in the license.json but not on the file system)',
    function() {
      reportMedia( grunt );
    } );

  // see reportThirdParty.js
  grunt.registerTask( 'report-third-party', 'This task is used to create a report of third-party resources (code, ' +
                                            'images, audio, etc) used in a set of PhET simulations by reading the ' +
                                            'license information in built HTML files.\n' +
                                            '--input (required argument) the path to the directory containing HTML ' +
                                            'files which will be reported on.\n' +
                                            '--output (required argument) the path to a file where the report should be ' +
                                            'written. The file is in markdown syntax and the *.md suffix is ' +
                                            'recommended. This will silently overwrite an existing file, if there is ' +
                                            'one.\n' +
                                            '--active-runnables (optional flag, boolean) If this flag is ' +
                                            'supplied, the task iterates over the active-runnables and copies each ' +
                                            'built HTML file into the directory specified with --input before ' +
                                            'running the report. If any HTML files are missing, the report will fail. ' +
                                            'Before using this flag, the developer should run `grunt-all.sh` to make ' +
                                            'sure all of the HTML files are up-to-date. The directory is neither ' +
                                            'automatically created nor automatically cleaned, this is the ' +
                                            'responsibility of the developer. (Note that if you fail to manually ' +
                                            'clean the directory, you may end up with stale HTML files).',
    function() {
      reportThirdParty( grunt );
    } );

  grunt.registerTask( 'published-README',
    'Generates README.md file for a published simulation.',
    function() {
      assert( buildConfig.simTitleStringKey, 'missing buildConfig.simTitleStringKey' );
      generateREADME( grunt, buildConfig.name, buildConfig.phetLibs, buildConfig.simTitleStringKey, true /* published */ );
    } );

  grunt.registerTask( 'unpublished-README',
    'Generates README.md file for an unpublished simulation.',
    function() {
      assert( buildConfig.simTitleStringKey, 'missing buildConfig.simTitleStringKey' );
      generateREADME( grunt, buildConfig.name, buildConfig.phetLibs, buildConfig.simTitleStringKey, false /* published */ );
    } );

  grunt.registerTask( 'generate-thumbnails', 'Generate 128x84 and 600x394 thumbnails to be used on the website.',
    function() {
      var finished = _.after( 2, grunt.task.current.async() );
      generateThumbnails( grunt, buildConfig.name, 128, 84, finished );
      generateThumbnails( grunt, buildConfig.name, 600, 394, finished );
    } );

  /*
   * Load tasks from grunt plugins that have been installed locally using npm.
   * Put these in package.json and run 'npm install' before running grunt.
   */
  grunt.loadNpmTasks( 'grunt-requirejs' );
  grunt.loadNpmTasks( 'grunt-contrib-jshint' );
};
<commit_msg>#285 replace fs.existsSync with grunt.file.exists in Gruntfile.js<commit_after>// Copyright 2002-2015, University of Colorado Boulder

/**
 * Grunt configuration file for PhET projects.
 * Tasks share information via global.phet, see individual tasks for details.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 * @author Jon Olson
 * @author Sam Reid
 * @author John Blanco
 */

// use process.env
/* jslint node: true */

// built-in node APIs
var assert = require( 'assert' );
var fs = require( 'fs' );

// 3rd-party packages
/* jshint -W079 */
var _ = require( '../../../sherpa/lib/lodash-2.4.1.min' ); // allow _ to be redefined, contrary to jshintOptions.js
/* jshint +W079 */

// PhET custom grunt tasks
var afterRequirejsBuild = require( '../../../chipper/js/grunt/afterRequirejsBuild' );
var checkoutShas = require( '../../../chipper/js/grunt/checkoutShas' );
var createSim = require( '../../../chipper/js/grunt/createSim' );
var deployProduction = require( '../../../chipper/js/grunt/deployProduction' );
var deployDev = require( '../../../chipper/js/grunt/deployDev' );
var generateREADME = require( '../../../chipper/js/grunt/generateREADME' );
var generateThumbnails = require( '../../../chipper/js/grunt/generateThumbnails' );
var reportMedia = require( '../../../chipper/js/grunt/reportMedia' );
var reportThirdParty = require( '../../../chipper/js/grunt/reportThirdParty' );
var getBuildConfig = require( '../../../chipper/js/grunt/getBuildConfig' );
var getGruntConfig = require( '../../../chipper/js/grunt/getGruntConfig' );

//TODO chipper#300 look at why this is necessary
/*
 * In Node, global is the global namespace object.
 * Register fs as a global so it can be accessed through the requirejs build system. Text.js plugin
 * may have a superior way to handle this but I (SR) couldn't get it working after a small amount of effort.
 */
global.fs = fs;

module.exports = function( grunt ) {
  'use strict';

  //---------------------------------------------------------------------------------------------------------------
  // Configuration

  var buildConfig = getBuildConfig( grunt );

  // Initialize and document all globals
  assert( !global.phet, 'global.phet already exists' );
  global.phet = {

    chipper: {

      // the grunt instance, for situations where we can't pass it as a function argument
      grunt: grunt,

      // polyfill to work around the cache buster arg in the *-config.js file that all sims have.
      getCacheBusterArgs: function() { return ''; },

      // media plugins populate this with license.json entries, see getLicenseEntry.js for format of entries
      licenseEntries: {},

      // use by media plugins, which don't have access to buildConfig
      brand: buildConfig.brand,

      // populated by mipmap.js
      mipmapsToBuild: [],

      // populated by string.js
      strings: {}
    }
  };

  // Initialize grunt
  grunt.initConfig( getGruntConfig( grunt, buildConfig.name, buildConfig.phetLibs ) );

  //---------------------------------------------------------------------------------------------------------------
  // Primary tasks
  //---------------------------------------------------------------------------------------------------------------

  // Default task ('grunt')

  grunt.registerTask( 'default', 'Builds the English HTML', [ 'build' ] );

  // Add the linting step as an pre-build step.  Can be skipped with --lint=false
  var optionalTasks = [];
  if ( grunt.option( 'lint' ) === false ) {
  }
  else {
    optionalTasks.push( 'lint-all' );
  }

  grunt.registerTask( 'build',
    'Builds the simulation:\n' +
    'with no options, builds HTML for English only\n' +
    '--locales=* : all locales in strings/ directory\n' +
    '--locales=fr : French\n' +
    '--locales=ar,fr,es : Arabic, French and Spanish (comma separated locales)\n' +
    '--localesRepo=$repo : all locales in another repository\'s strings/ directory, ignored if --locales is present\n' +
    '--lint=false : skip the lint sub-task',
    optionalTasks.concat( [
      'clean',
      'requirejs:build',
      'after-requirejs-build' ] )
  );

  grunt.registerTask( 'deploy-production',
    'Invoke deployDev and then deploy a simulation to the production server.\n' +
    'Should be run AFTER grunt build since it uses the shas from dependencies.json in the build directory.\n',
    'Deploys to figaro by default, but simian can be used for testing by setting:\n' +
    '"productionServerName": "simian.colorado.edu" and "productionServerURL": "https://phet-dev.colorado.edu" in build-local.json',
    function() {
      // before invoking the build-server, do a dev deploy, including committing and pushing to github
      deployDev( grunt );
      deployProduction( grunt );
    }
  );

  grunt.registerTask( 'deploy-dev',
    'Deploy a dev version to spot, or optionally to the server in your preferences file\n' +
    '--mkdir : set to true to create the sim dir and .htaccess file before copying the version directory\n' +
    '--test : set to true to disable commit and push, and SCP to a test directory on spot',
    function() {
      deployDev( grunt );
    }
  );

  grunt.registerTask( 'lint', 'lint js files that are specific to this repository', [ 'jshint:repoFiles' ] );

  grunt.registerTask( 'lint-all', 'lint all js files that are required to build this repository', [ 'jshint:allFiles' ] );

  grunt.registerTask( 'clean',
    'Erases the build/ directory and all its contents, and recreates the build/ directory',
    function() {
      if ( grunt.file.exists( 'build' ) ) {
        grunt.file.delete( 'build' );
      }
      grunt.file.mkdir( 'build' );
    } );

  grunt.registerTask( 'after-requirejs-build',
    '(internal use only) Do things after the requirejs:build task',
    function() {
      afterRequirejsBuild( grunt, buildConfig );
    } );

  //---------------------------------------------------------------------------------------------------------------
  // Utility tasks
  //---------------------------------------------------------------------------------------------------------------

  grunt.registerTask( 'checkout-shas',
    'Check out shas for a project, as specified in dependencies.json',
    function() {
      checkoutShas( grunt, buildConfig.name, false /* toMaster */ );
    } );

  grunt.registerTask( 'checkout-master',
    'Check out master branch for all dependencies, as specified in dependencies.json',
    function() {
      checkoutShas( grunt, buildConfig.name, true /* toMaster */ );
    } );

  grunt.registerTask( 'create-sim',
    'Creates a sim based on the simula-rasa template.\n' +
    '--name="string" : the repository name\n' +
    '--author="string" : the author name\n' +
    '--title="string" : (optional) the simulation title\n' +
    '--clean=true : (optional) deletes the repository directory if it exists',
    function() {
      createSim( grunt );
    } );

  // See reportMedia.js
  grunt.registerTask( 'report-media', '(project-wide) Report on license.json files throughout all working copies. ' +
                                      'Reports any media (such as images or audio) files that have any of the following problems:\n' +
                                      '(1) incompatible-license (resource license not approved)\n' +
                                      '(2) not-annotated (license.json missing or entry missing from license.json)\n' +
                                      '(3) missing-file (entry in the license.json but not on the file system)',
    function() {
      reportMedia( grunt );
    } );

  // see reportThirdParty.js
  grunt.registerTask( 'report-third-party', 'This task is used to create a report of third-party resources (code, ' +
                                            'images, audio, etc) used in a set of PhET simulations by reading the ' +
                                            'license information in built HTML files.\n' +
                                            '--input (required argument) the path to the directory containing HTML ' +
                                            'files which will be reported on.\n' +
                                            '--output (required argument) the path to a file where the report should be ' +
                                            'written. The file is in markdown syntax and the *.md suffix is ' +
                                            'recommended. This will silently overwrite an existing file, if there is ' +
                                            'one.\n' +
                                            '--active-runnables (optional flag, boolean) If this flag is ' +
                                            'supplied, the task iterates over the active-runnables and copies each ' +
                                            'built HTML file into the directory specified with --input before ' +
                                            'running the report. If any HTML files are missing, the report will fail. ' +
                                            'Before using this flag, the developer should run `grunt-all.sh` to make ' +
                                            'sure all of the HTML files are up-to-date. The directory is neither ' +
                                            'automatically created nor automatically cleaned, this is the ' +
                                            'responsibility of the developer. (Note that if you fail to manually ' +
                                            'clean the directory, you may end up with stale HTML files).',
    function() {
      reportThirdParty( grunt );
    } );

  grunt.registerTask( 'published-README',
    'Generates README.md file for a published simulation.',
    function() {
      assert( buildConfig.simTitleStringKey, 'missing buildConfig.simTitleStringKey' );
      generateREADME( grunt, buildConfig.name, buildConfig.phetLibs, buildConfig.simTitleStringKey, true /* published */ );
    } );

  grunt.registerTask( 'unpublished-README',
    'Generates README.md file for an unpublished simulation.',
    function() {
      assert( buildConfig.simTitleStringKey, 'missing buildConfig.simTitleStringKey' );
      generateREADME( grunt, buildConfig.name, buildConfig.phetLibs, buildConfig.simTitleStringKey, false /* published */ );
    } );

  grunt.registerTask( 'generate-thumbnails', 'Generate 128x84 and 600x394 thumbnails to be used on the website.',
    function() {
      var finished = _.after( 2, grunt.task.current.async() );
      generateThumbnails( grunt, buildConfig.name, 128, 84, finished );
      generateThumbnails( grunt, buildConfig.name, 600, 394, finished );
    } );

  /*
   * Load tasks from grunt plugins that have been installed locally using npm.
   * Put these in package.json and run 'npm install' before running grunt.
   */
  grunt.loadNpmTasks( 'grunt-requirejs' );
  grunt.loadNpmTasks( 'grunt-contrib-jshint' );
};
<|endoftext|>


<commit_before>'use strict';

class Field extends Model {
    // constructor(data, parent) {
    //     super(data, parent);
    // }

    init() {
    }

    // deinit() {
    //     console.log('Field.deinit:', this.getFullName());
    //     super.deinit();
    // }

    replaceThis(value) {
        return value.replace(/\{([@\w\.]+)\}/g, (text, name) => {
            if (name.indexOf('.') !== -1) {
                let arr = name.split('.');
                if (arr[0] === 'this') {
                    arr[0] = this.getPage().getName();
                }
                if (arr[0] === 'parent' && this.getPage().parentPageName) {
                    arr[0] = this.getPage().parentPageName;
                }
                return '{' + arr.join('.') + '}';
            } else {
                return text;
            }
        });
    }

    fillDefaultValue(row) {
        // console.log('Field.fillDefaultValue', this.getFullName());
        if (this.data.column) {
            const defaultValue = this.replaceThis(this.data.defaultValue);
            const params = {
                ...this.getPage().params,
                ...this.getApp().data.params
            };
            const code = QForms.templateValue(defaultValue, params);
            let value;
            try {
                //console.log('eval: ' + code);
                value = eval(code);
                if (value !== undefined) {
                    row[this.data.column] = value;
                }
            } catch (err) {
                throw new Error(`[${this.getFullName()}] default value error: ${err.toString()}`);
            }
        }
    }

    valueToParams(row) {
        // console.log('Field.valueToParams', this.getFullName());
        if (this.data.column) {
            this.getPage().params[this.getFullName()] = this.getValue(row);
        }
    }

    setValue(row, value) {
        console.log('Field.setValue', this.getFullName(), value);
        if (!this.data.column) throw new Error(`field has no column: ${this.getFullName()}`);
        const valueForDataSource = this.getValueForDataSource(value);
        this.getForm().getDataSource().setValue(row, this.data.column, valueForDataSource);
        this.valueToParams(row);
    }

    getValueForDataSource(value) {
        if (value === undefined || value === null) return value;
        const fieldType = this.getType();
        const valueType = typeof value;
        if (fieldType === 'date') {
            if (!(value instanceof Date)) {
                throw new Error(`${this.getFullName()}: value not instance of Date`);
            }
        } else if (fieldType !== valueType) {
            throw new Error(`${this.getFullName()}: wrong value type for column: ${valueType} instead of ${fieldType}`);
        }
        if (valueType === 'object') {
            if (value instanceof Date) return value.toISOString();
            return JSON.stringify(value);
        }
        return value;
    }

    isChanged(row) {
        // console.log('Field.isChanged', this.getFullName());
        if (!this.data.column) throw new Error(`${this.getFullName()}: field has no column`);
        return this.getDataSource().isRowColumnChanged(row, this.data.column);
    }

    getValueFromDataSource(row) {
        if (!this.hasColumn()) throw new Error(`${this.getFullName()}: no column`);
        return this.getForm().getDataSource().getValue(row, this.data.column);
    }

    hasColumn() {
        return !!this.data.column;
    }

    getValue(row) {
        // console.log('Field.getValue', this.getFullName());
        if (this.data.column) {
            let value = this.getForm().getDataSource().getValue(row, this.data.column);
            if (value === null) return null;
            if (value === undefined) return undefined;
            const fieldType = this.getType();
            if (fieldType === 'date') {
                // if (value === undefined) return null; // workaround for new row
                if (typeof value !== 'string') throw new Error(`${this.getFullName()}: wrong value for date column: ${value}`);
                return new Date(value);
            } else if (fieldType === 'object') {
                return JSON.parse(value);
            }
            return value;
        } else if (this.data.value) {
            return eval(this.data.value);
        } else {
            throw new Error(`no column and no value in field: ${this.getFullName()}`);
        }
    }

    getDataSource() {
        return this.getForm().getDataSource();
    }

    getType() {
        if (this.data.column) {
            return this.getDataSource().getType(this.data.column);
        }
        return null;
    }

    getForm() {
        return this.parent;
    }

    getPage() {
        return this.parent.parent;
    }

    getApp() {
        return this.parent.parent.parent;
    }

    isReadOnly() {
        return this.data.readOnly === 'true';
    }
    isNotNull() {
        return this.data.notNull === 'true';
    }
    isNullable() {
        return this.data.notNull === 'false';
    }
    isVisible() {
        return this.data.isVisible === 'true';
    }
    getWidth() {
        return this.data.width !== '0' ? parseInt(this.data.width) : 100;
    }
}
<commit_msg>get field type from json desc<commit_after>'use strict';

class Field extends Model {
    // constructor(data, parent) {
    //     super(data, parent);
    // }

    init() {
    }

    // deinit() {
    //     console.log('Field.deinit:', this.getFullName());
    //     super.deinit();
    // }

    replaceThis(value) {
        return value.replace(/\{([@\w\.]+)\}/g, (text, name) => {
            if (name.indexOf('.') !== -1) {
                let arr = name.split('.');
                if (arr[0] === 'this') {
                    arr[0] = this.getPage().getName();
                }
                if (arr[0] === 'parent' && this.getPage().parentPageName) {
                    arr[0] = this.getPage().parentPageName;
                }
                return '{' + arr.join('.') + '}';
            } else {
                return text;
            }
        });
    }

    fillDefaultValue(row) {
        // console.log('Field.fillDefaultValue', this.getFullName());
        if (this.data.column) {
            const defaultValue = this.replaceThis(this.data.defaultValue);
            const params = {
                ...this.getPage().params,
                ...this.getApp().data.params
            };
            const code = QForms.templateValue(defaultValue, params);
            let value;
            try {
                //console.log('eval: ' + code);
                value = eval(code);
                if (value !== undefined) {
                    row[this.data.column] = value;
                }
            } catch (err) {
                throw new Error(`[${this.getFullName()}] default value error: ${err.toString()}`);
            }
        }
    }

    valueToParams(row) {
        // console.log('Field.valueToParams', this.getFullName());
        if (this.data.column) {
            this.getPage().params[this.getFullName()] = this.getValue(row);
        }
    }

    setValue(row, value) {
        console.log('Field.setValue', this.getFullName(), value);
        if (!this.data.column) throw new Error(`field has no column: ${this.getFullName()}`);
        const valueForDataSource = this.getValueForDataSource(value);
        this.getForm().getDataSource().setValue(row, this.data.column, valueForDataSource);
        this.valueToParams(row);
    }

    getValueForDataSource(value) {
        if (value === undefined || value === null) return value;
        const fieldType = this.getType();
        const valueType = typeof value;
        if (fieldType === 'date') {
            if (!(value instanceof Date)) {
                throw new Error(`${this.getFullName()}: value not instance of Date`);
            }
        } else if (fieldType !== valueType) {
            throw new Error(`${this.getFullName()}: wrong value type for column: ${valueType} instead of ${fieldType}`);
        }
        if (valueType === 'object') {
            if (value instanceof Date) return value.toISOString();
            return JSON.stringify(value);
        }
        return value;
    }

    isChanged(row) {
        // console.log('Field.isChanged', this.getFullName());
        if (!this.data.column) throw new Error(`${this.getFullName()}: field has no column`);
        return this.getDataSource().isRowColumnChanged(row, this.data.column);
    }

    getValueFromDataSource(row) {
        if (!this.hasColumn()) throw new Error(`${this.getFullName()}: no column`);
        return this.getForm().getDataSource().getValue(row, this.data.column);
    }

    hasColumn() {
        return !!this.data.column;
    }

    getValue(row) {
        // console.log('Field.getValue', this.getFullName());
        if (this.data.column) {
            let value = this.getForm().getDataSource().getValue(row, this.data.column);
            if (value === null) return null;
            if (value === undefined) return undefined;
            const fieldType = this.getType();
            if (fieldType === 'date') {
                // if (value === undefined) return null; // workaround for new row
                if (typeof value !== 'string') throw new Error(`${this.getFullName()}: wrong value for date column: ${value}`);
                return new Date(value);
            } else if (fieldType === 'object') {
                return JSON.parse(value);
            }
            return value;
        } else if (this.data.value) {
            return eval(this.data.value);
        } else {
            throw new Error(`no column and no value in field: ${this.getFullName()}`);
        }
    }

    getDataSource() {
        return this.getForm().getDataSource();
    }

    getType() {
        const dataSource = this.getDataSource();
        if (dataSource.getClassName() === 'SqlDataSource' && this.data.column) {
            return this.getDataSource().getType(this.data.column);
        }
        if (this.data.type) return this.data.type;
        throw new Error(`${this.getFullName()}: field type empty`);
    }

    getForm() {
        return this.parent;
    }

    getPage() {
        return this.parent.parent;
    }

    getApp() {
        return this.parent.parent.parent;
    }

    isReadOnly() {
        return this.data.readOnly === 'true';
    }
    isNotNull() {
        return this.data.notNull === 'true';
    }
    isNullable() {
        return this.data.notNull === 'false';
    }
    isVisible() {
        return this.data.isVisible === 'true';
    }
    getWidth() {
        return this.data.width !== '0' ? parseInt(this.data.width) : 100;
    }
}
<|endoftext|>


<commit_before>var Promise = require('bluebird'),
	mongoose = Promise.promisifyAll(require('mongoose')),
	User = mongoose.model('User');

module.exports = {

	one : One,
	add : Add,
	edit : Edit,
	delete : Delete,
	get : Get,
	bulkAdd : BulkAdd,
	bulkEdit : BulkEdit,
	bulkDelete : BulkDelete

};

function One(request,reply) {

	if(reply.data && reply.data._id) {
		return reply.continue();
	}

	User.findByIdAsyc(request.params.id)
		.then(function(userExist){
			reply.data = userExist;
			reply.continue();
		})
		.catch(function(e) {
			reply.continue(e);
		});
}

function Add(request,reply) {

	var user = new User(request.payload);

	user.saveAsync()
		.then(function() {
			reply.continue();
		})
		.catch(function(e) {
			reply.continue(e);
		});
}

function Edit(request,reply) {
	reply.continue();
}

function Delete(request,reply) {
	reply.continue();
}

function Get(request,reply) {
	console.log(request.headers);

	User.find({})
		.then(function(users) {
			reply.data = users;
			reply.continue();
		})
		.catch(function(e) {
			reply.continue({'msg' : 'Cannot fetch users','error' : e});
		});
}

function BulkAdd(request,reply) {
	reply.continue();
}

function BulkEdit(request,reply) {
	reply.continue();
}

function BulkDelete(request,reply) {
	reply.continue();
}
<commit_msg>Proper indentation for file declaration.<commit_after>var Promise = require('bluebird'),
    mongoose = Promise.promisifyAll(require('mongoose')),
    User = mongoose.model('User');

module.exports = {

	one : One,
	add : Add,
	edit : Edit,
	delete : Delete,
	get : Get,
	bulkAdd : BulkAdd,
	bulkEdit : BulkEdit,
	bulkDelete : BulkDelete

};

function One(request,reply) {

	if(reply.data && reply.data._id) {
		return reply.continue();
	}

	User.findByIdAsyc(request.params.id)
		.then(function(userExist){
			reply.data = userExist;
			reply.continue();
		})
		.catch(function(e) {
			reply.continue(e);
		});
}

function Add(request,reply) {

	var user = new User(request.payload);

	user.saveAsync()
		.then(function() {
			reply.continue();
		})
		.catch(function(e) {
			reply.continue(e);
		});
}

function Edit(request,reply) {
	reply.continue();
}

function Delete(request,reply) {
	reply.continue();
}

function Get(request,reply) {
	console.log(request.headers);

	User.find({})
		.then(function(users) {
			reply.data = users;
			reply.continue();
		})
		.catch(function(e) {
			reply.continue({'msg' : 'Cannot fetch users','error' : e});
		});
}

function BulkAdd(request,reply) {
	reply.continue();
}

function BulkEdit(request,reply) {
	reply.continue();
}

function BulkDelete(request,reply) {
	reply.continue();
}
<|endoftext|>


<commit_before>DV.Schema.events.viewFullscreen = {
  open: function(e){
    e.preventDefault();
    this.application.open('ViewFullscreen');

    return false;
  },

  close: function(e) {
    e.preventDefault();
    this.application.open('ViewFullscreen');

    return false;
  }
};<commit_msg>remove unused ViewFullscreen.js<commit_after><|endoftext|>


<commit_before>angular.module("institution", []).controller("InstitutionController", ["$http", function InstitutionController($http) {
    this.root = "";
    this.userId = "";
    this.pageMode = "view";
    this.details = {
        id: "-1",
        name: "",
        logo: "",
        url: "",
        description: "",
        admins: []
    };
    this.isAdmin = false;
    this.projectList = [];
    this.userList = [];
    this.userListComplete = [];
    this.newUserEmail = "";

    this.getInstitutionDetails = function (institutionId) {
        $http.get(this.root + "/get-institution-details/" + institutionId)
            .then(angular.bind(this, function successCallback(response) {
                this.details = response.data;
                if (this.userId != "") {
                    this.isAdmin = this.details.admins.includes(parseInt(this.userId));
                }
            }), function errorCallback(response) {
                console.log(response);
                alert("Error retrieving the institution details. See console for details.");
            });
    };

    this.getProjectList = function (userId, institutionId) {
        $http.get(this.root + "/get-all-projects?userId=" + userId + "&institutionId=" + institutionId)
            .then(angular.bind(this, function successCallback(response) {
                this.projectList = response.data;
            }), function errorCallback(response) {
                console.log(response);
                alert("Error retrieving the project list. See console for details.");
            });
    };

    this.getUserList = function (institutionId) {
        $http.get(this.root + "/get-all-users?institutionId=" + institutionId)
            .then(angular.bind(this, function successCallback(response) {
                this.userList = response.data;
            }), function errorCallback(response) {
                console.log(response);
                alert("Error retrieving the user list. See console for details.");
            });
    };

    this.getUserListComplete = function () {
        $http.get(this.root + "/get-all-users")
            .then(angular.bind(this, function successCallback(response) {
                this.userListComplete = response.data;
            }), function errorCallback(response) {
                console.log(response);
                alert("Error retrieving the complete user list. See console for details.");
            });
    };

    this.initialize = function (documentRoot, userId) {
        // Make the current documentRoot, userId, and institution id globally available
        this.root = documentRoot;
        this.userId = userId;
        this.details.id = document.getElementById("initial-institution-id").value;

        // If in Create Institution mode, show the institution editing view. Otherwise, load and show the institution details
        if (this.details.id == "0") {
            this.pageMode = "edit";
        } else {
            this.getInstitutionDetails(this.details.id);

            // Load the projectList
            this.getProjectList(this.userId, this.details.id);

            // Load the userList
            this.getUserList(this.details.id);

            // Load the complete userList
            this.getUserListComplete();
        }
    };

    this.updateInstitution = function () {
        var formData = new FormData();
        formData.append("userid", this.userId);
        formData.append("institution-name", this.details.name);
        formData.append("institution-logo", document.getElementById("institution-logo").files[0]);
        formData.append("institution-url", this.details.url);
        formData.append("institution-description", this.details.description);
        $http.post(this.root + "/update-institution/" + this.details.id,
                   formData,
                   {transformRequest: angular.identity,
                    headers: {"Content-Type": undefined}})
            .then(angular.bind(this, function successCallback(response) {
                this.details.id = response.data.id;
                this.isAdmin = true;
                if (response.data.logo != "") {
                    this.details.logo = response.data.logo;
                }
                this.getUserList(this.details.id);
            }), function errorCallback(response) {
                console.log(response);
                alert("Error updating institution details. See console for details.");
            });
    };

    this.togglePageMode = function () {
        if (this.pageMode == "view") {
            this.pageMode = "edit";
        } else {
            this.updateInstitution();
            this.pageMode = "view";
        }
    };

    this.deleteInstitution = function () {
        if (confirm("Do you REALLY want to delete this institution?!")) {
            $http.post(this.root + "/archive-institution/" + this.details.id)
                .then(angular.bind(this, function successCallback(response) {
                    alert("Institution " + this.details.name + " has been deleted.");
                    window.location = this.root + "/home";
                }), function errorCallback(response) {
                    console.log(response);
                    alert("Error deleting institution. See console for details.");
                });
        }
    };

    this.createProject = function () {
        var institutionId = document.getElementById("current-institution-id").value;
        if (institutionId == 0) {
            alert("Please finish creating the institution before adding projects to it.");
        } else if (institutionId == -1) {
            alert("Projects cannot be created without first selecting an institution.");
        } else {
            window.location = this.root + "/project/0?institution=" + institutionId;
        }
    };

    this.updateUserInstitutionRole = function (userId, email, role) {
        $http.post(this.root + "/update-user-institution-role",
                   {userId: userId, institutionId: this.details.id, role: role})
            .then(angular.bind(this, function successCallback(response) {
                alert("User " + email + " has been given role '" + role + "'.");
                if (userId == this.userId && role != "admin") {
                    this.pageMode = "view";
                    this.isAdmin = false;
                }
                this.getUserList(this.details.id);
            }), function errorCallback(response) {
                console.log(response);
                alert("Error updating user institution role. See console for details.");
            });
    };

    this.findUserByEmail = function (userList, email) {
        return userList.find(
            function (user) {
                return user.email == email;
            }
        );
    };

    this.addUser = function () {
        if (this.newUserEmail == "") {
            alert("Please enter an existing user's email address.");
        } else if (this.findUserByEmail(this.userList, this.newUserEmail)) {
            alert(this.newUserEmail + " is already a member of this institution.");
        } else {
            var newUser = this.findUserByEmail(this.userListComplete, this.newUserEmail);
            if (newUser) {
                this.updateUserInstitutionRole(newUser.id, newUser.email, "member");
                this.newUserEmail = "";
            } else {
                alert(this.newUserEmail + " is not an existing user's email address.");
            }
        }
    };

}]);
<commit_msg>Fixing a bug that prevented adding users to newly created institutions.<commit_after>angular.module("institution", []).controller("InstitutionController", ["$http", function InstitutionController($http) {
    this.root = "";
    this.userId = "";
    this.pageMode = "view";
    this.details = {
        id: "-1",
        name: "",
        logo: "",
        url: "",
        description: "",
        admins: []
    };
    this.isAdmin = false;
    this.projectList = [];
    this.userList = [];
    this.userListComplete = [];
    this.newUserEmail = "";

    this.getInstitutionDetails = function (institutionId) {
        $http.get(this.root + "/get-institution-details/" + institutionId)
            .then(angular.bind(this, function successCallback(response) {
                this.details = response.data;
                if (this.userId != "") {
                    this.isAdmin = this.details.admins.includes(parseInt(this.userId));
                }
            }), function errorCallback(response) {
                console.log(response);
                alert("Error retrieving the institution details. See console for details.");
            });
    };

    this.getProjectList = function (userId, institutionId) {
        $http.get(this.root + "/get-all-projects?userId=" + userId + "&institutionId=" + institutionId)
            .then(angular.bind(this, function successCallback(response) {
                this.projectList = response.data;
            }), function errorCallback(response) {
                console.log(response);
                alert("Error retrieving the project list. See console for details.");
            });
    };

    this.getUserList = function (institutionId) {
        $http.get(this.root + "/get-all-users?institutionId=" + institutionId)
            .then(angular.bind(this, function successCallback(response) {
                this.userList = response.data;
            }), function errorCallback(response) {
                console.log(response);
                alert("Error retrieving the user list. See console for details.");
            });
    };

    this.getUserListComplete = function () {
        $http.get(this.root + "/get-all-users")
            .then(angular.bind(this, function successCallback(response) {
                this.userListComplete = response.data;
            }), function errorCallback(response) {
                console.log(response);
                alert("Error retrieving the complete user list. See console for details.");
            });
    };

    this.initialize = function (documentRoot, userId) {
        // Make the current documentRoot, userId, and institution id globally available
        this.root = documentRoot;
        this.userId = userId;
        this.details.id = document.getElementById("initial-institution-id").value;

        // If in Create Institution mode, show the institution editing view. Otherwise, load and show the institution details
        if (this.details.id == "0") {
            this.pageMode = "edit";
        } else {
            this.getInstitutionDetails(this.details.id);

            // Load the projectList
            this.getProjectList(this.userId, this.details.id);

            // Load the userList
            this.getUserList(this.details.id);

            // Load the complete userList
            this.getUserListComplete();
        }
    };

    this.updateInstitution = function () {
        var formData = new FormData();
        formData.append("userid", this.userId);
        formData.append("institution-name", this.details.name);
        formData.append("institution-logo", document.getElementById("institution-logo").files[0]);
        formData.append("institution-url", this.details.url);
        formData.append("institution-description", this.details.description);
        $http.post(this.root + "/update-institution/" + this.details.id,
                   formData,
                   {transformRequest: angular.identity,
                    headers: {"Content-Type": undefined}})
            .then(angular.bind(this, function successCallback(response) {
                this.details.id = response.data.id;
                this.isAdmin = true;
                if (response.data.logo != "") {
                    this.details.logo = response.data.logo;
                }
                this.getUserList(this.details.id);
                this.getUserListComplete();
            }), function errorCallback(response) {
                console.log(response);
                alert("Error updating institution details. See console for details.");
            });
    };

    this.togglePageMode = function () {
        if (this.pageMode == "view") {
            this.pageMode = "edit";
        } else {
            this.updateInstitution();
            this.pageMode = "view";
        }
    };

    this.deleteInstitution = function () {
        if (confirm("Do you REALLY want to delete this institution?!")) {
            $http.post(this.root + "/archive-institution/" + this.details.id)
                .then(angular.bind(this, function successCallback(response) {
                    alert("Institution " + this.details.name + " has been deleted.");
                    window.location = this.root + "/home";
                }), function errorCallback(response) {
                    console.log(response);
                    alert("Error deleting institution. See console for details.");
                });
        }
    };

    this.createProject = function () {
        var institutionId = document.getElementById("current-institution-id").value;
        if (institutionId == 0) {
            alert("Please finish creating the institution before adding projects to it.");
        } else if (institutionId == -1) {
            alert("Projects cannot be created without first selecting an institution.");
        } else {
            window.location = this.root + "/project/0?institution=" + institutionId;
        }
    };

    this.updateUserInstitutionRole = function (userId, email, role) {
        $http.post(this.root + "/update-user-institution-role",
                   {userId: userId, institutionId: this.details.id, role: role})
            .then(angular.bind(this, function successCallback(response) {
                alert("User " + email + " has been given role '" + role + "'.");
                if (userId == this.userId && role != "admin") {
                    this.pageMode = "view";
                    this.isAdmin = false;
                }
                this.getUserList(this.details.id);
            }), function errorCallback(response) {
                console.log(response);
                alert("Error updating user institution role. See console for details.");
            });
    };

    this.findUserByEmail = function (userList, email) {
        return userList.find(
            function (user) {
                return user.email == email;
            }
        );
    };

    this.addUser = function () {
        if (this.newUserEmail == "") {
            alert("Please enter an existing user's email address.");
        } else if (this.findUserByEmail(this.userList, this.newUserEmail)) {
            alert(this.newUserEmail + " is already a member of this institution.");
        } else {
            var newUser = this.findUserByEmail(this.userListComplete, this.newUserEmail);
            if (newUser) {
                this.updateUserInstitutionRole(newUser.id, newUser.email, "member");
                this.newUserEmail = "";
            } else {
                alert(this.newUserEmail + " is not an existing user's email address.");
            }
        }
    };

}]);
<|endoftext|>


<commit_before>lobby.transporter.module.create('module_morph', function(requires) {

requires('lk_ext', 'rows_and_columns');

}, function(thisModule) {

thisModule.addSlots(modules.module_morph, function(add) {

    add.data('_directory', 'transporter');

});

thisModule.addSlots(transporter.module, function(add) {

  add.method('newMorph', function() {
    return new this.Morph(this);
  }, {category: ['user interface']});

  add.method('Morph', function Morph() { Class.initializer.apply(this, arguments); }, {category: ['user interface']});

});

thisModule.addSlots(transporter.module.Morph, function(add) {

  add.data('superclass', RowMorph);

  add.creator('prototype', Object.create(RowMorph.prototype));

  add.data('type', 'transporter.module.Morph');

});

thisModule.addSlots(transporter.module.Morph.prototype, function(add) {

  add.data('constructor', transporter.module.Morph);

  add.method('initialize', function ($super, module) {
    $super();
    this._module = module;

    this.setPadding({top: 2, bottom: 2, left: 4, right: 4, between: 3});
    this.setFill(defaultFillWithColor(Color.red.lighter()));
    this.shape.roundEdgesBy(10);

    this._nameLabel = createLabel(function() { return module.name(); });
    this._fileOutButton = createButton('File out', this.fileOut.bind(this), 2);

    this._changeIndicator = createLabel(function() { return this._module.hasChangedSinceLastFileOut() ? ' has changed ' : ''; }.bind(this));
    this._changeIndicator.setTextColor(Color.green.darker());

    this.setColumns([this._nameLabel, this._changeIndicator, this._fileOutButton, this.createDismissButton()]);

    this.startPeriodicallyUpdating();
  }, {category: ['creating']});

  add.method('inspect', function () { return this._module.name(); }, {category: ['printing']});

  add.method('fileOut', function (evt) {
    MessageNotifierMorph.showIfErrorDuring(function() { this._module.fileOut(); }.bind(this), evt);
    this.refreshContentOfMeAndSubmorphs();
  }, {category: ['commands']});

  add.method('fileOutWithoutAnnotations', function (evt) {
    MessageNotifierMorph.showIfErrorDuring(function() { this._module.fileOutWithoutAnnotations(); }.bind(this), evt);
    this.refreshContentOfMeAndSubmorphs();
  }, {category: ['commands']});

  add.method('forgetIWasChanged', function (evt) {
    this._module.markAsUnchanged();
    this.refreshContentOfMeAndSubmorphs();
  }, {category: ['commands']});

  add.method('getModuleObject', function (evt) {
    evt.hand.world().morphFor(reflect(this._module)).grabMe(evt);
  }, {category: ['commands']});

  add.method('getAllObjects', function (evt) {
    var w = evt.hand.world();
    w.assumePose(w.listPoseOfMorphsFor(this._module.objectsThatMightContainSlotsInMe().map(function(o) { return reflect(o); }), "objects in module " + this._module.name()));
  }, {category: ['commands']});

  add.method('addCommandsTo', function (cmdList) {
    cmdList.addItem({label: 'file out', pluralLabel: 'file out modules', go: this.fileOut.bind(this)});

    cmdList.addItem({label: 'forget I was changed', go: this.forgetIWasChanged.bind(this)});

    cmdList.addLine();

    cmdList.addItem({label: 'get module object', go: this.getModuleObject.bind(this)});

    cmdList.addLine();

    cmdList.addItem({label: 'all objects', go: this.getAllObjects.bind(this)});
  }, {category: ['menu']});

});

thisModule.addSlots(transporter, function(add) {

  add.method('addMenuItemsTo', function(menu, evt) {
    menu.addLine();

    menu.addItem(["save snapshot", function(evt) {
      var snapshotter = new Snapshotter();
      snapshotter.walk(lobby);
      var snapshot = snapshotter.completeSnapshotText();

      var baseDirURL = URL.source.getDirectory().withRelativePath("javascripts/snapshots/");
      var fileName = "snapshot_" + snapshotter._number + ".js";
      var url = baseDirURL.withFilename(fileName);
      var status = new Resource(Record.newPlainInstance({URL: url})).store(snapshot, true).getStatus();
      if (! status.isSuccess()) {
        throw "failed to write " + fileName + ", status is " + status.code();
      }
    }]);

    menu.addItem(["all modules", function(evt) {
      var world = evt.hand.world();
      world.assumePose(world.listPoseOfMorphsFor(Object.newChildOf(enumerator, transporter.module, 'eachModule'), "all modules"));
    }]);

    menu.addItem(["changed modules", function(evt) {
      var world = evt.hand.world();
      world.assumePose(world.listPoseOfMorphsFor(transporter.module.changedOnes(), "all modules"));
    }]);

    // aaa - hack because I haven't managed to get WebDAV working on adamspitz.com yet
    if (! URL.source.hostname.include("adamspitz.com")) {

      menu.addItem(["file in...", function(evt) {
        var world = evt.hand.world();
        var baseDir = new FileDirectory(lobby.transporter.module.urlForCoreModulesDirectory());
        var modulesMenu = new MenuMorph(this.moduleMenuItemsForDir(baseDir, ""), world);
        modulesMenu.openIn(world, evt.point());
      }.bind(this)]);

    }

  }, {category: ['menu']});

  add.method('moduleMenuItemsForDir', function(dir, pathFromModuleSystemRootDir) {
    var menuItems = [];

    var subdirURLs = dir.subdirectories();
    subdirURLs.each(function(subdirURL) {
      var subdir = new FileDirectory(subdirURL);
      var subdirName = subdirURL.filename();
      menuItems.push([subdirName, this.moduleMenuItemsForDir(subdir, pathFromModuleSystemRootDir + "/" + subdirName)]);
    }.bind(this));

    var jsFileNames = dir.filenames().select(function(n) {return n.endsWith(".js");});
    jsFileNames.each(function(n) {
      menuItems.push([n, function(evt) {
        var moduleName = n.substring(0, n.length - 3);
        MessageNotifierMorph.showIfErrorDuring(function() {
          lobby.transporter.module.fileIn(pathFromModuleSystemRootDir, moduleName);
        }, evt);
      }]);
    });

    return menuItems;
  }, {category: ['menu']});

});

});
<commit_msg>Shouldn't leave the "save snapshot" menu item there, since it doesn't actually work yet.<commit_after>lobby.transporter.module.create('module_morph', function(requires) {

requires('lk_ext', 'rows_and_columns');

}, function(thisModule) {

thisModule.addSlots(modules.module_morph, function(add) {

    add.data('_directory', 'transporter');

});

thisModule.addSlots(transporter.module, function(add) {

  add.method('newMorph', function() {
    return new this.Morph(this);
  }, {category: ['user interface']});

  add.method('Morph', function Morph() { Class.initializer.apply(this, arguments); }, {category: ['user interface']});

});

thisModule.addSlots(transporter.module.Morph, function(add) {

  add.data('superclass', RowMorph);

  add.creator('prototype', Object.create(RowMorph.prototype));

  add.data('type', 'transporter.module.Morph');

});

thisModule.addSlots(transporter.module.Morph.prototype, function(add) {

  add.data('constructor', transporter.module.Morph);

  add.method('initialize', function ($super, module) {
    $super();
    this._module = module;

    this.setPadding({top: 2, bottom: 2, left: 4, right: 4, between: 3});
    this.setFill(defaultFillWithColor(Color.red.lighter()));
    this.shape.roundEdgesBy(10);

    this._nameLabel = createLabel(function() { return module.name(); });
    this._fileOutButton = createButton('File out', this.fileOut.bind(this), 2);

    this._changeIndicator = createLabel(function() { return this._module.hasChangedSinceLastFileOut() ? ' has changed ' : ''; }.bind(this));
    this._changeIndicator.setTextColor(Color.green.darker());

    this.setColumns([this._nameLabel, this._changeIndicator, this._fileOutButton, this.createDismissButton()]);

    this.startPeriodicallyUpdating();
  }, {category: ['creating']});

  add.method('inspect', function () { return this._module.name(); }, {category: ['printing']});

  add.method('fileOut', function (evt) {
    MessageNotifierMorph.showIfErrorDuring(function() { this._module.fileOut(); }.bind(this), evt);
    this.refreshContentOfMeAndSubmorphs();
  }, {category: ['commands']});

  add.method('fileOutWithoutAnnotations', function (evt) {
    MessageNotifierMorph.showIfErrorDuring(function() { this._module.fileOutWithoutAnnotations(); }.bind(this), evt);
    this.refreshContentOfMeAndSubmorphs();
  }, {category: ['commands']});

  add.method('forgetIWasChanged', function (evt) {
    this._module.markAsUnchanged();
    this.refreshContentOfMeAndSubmorphs();
  }, {category: ['commands']});

  add.method('getModuleObject', function (evt) {
    evt.hand.world().morphFor(reflect(this._module)).grabMe(evt);
  }, {category: ['commands']});

  add.method('getAllObjects', function (evt) {
    var w = evt.hand.world();
    w.assumePose(w.listPoseOfMorphsFor(this._module.objectsThatMightContainSlotsInMe().map(function(o) { return reflect(o); }), "objects in module " + this._module.name()));
  }, {category: ['commands']});

  add.method('addCommandsTo', function (cmdList) {
    cmdList.addItem({label: 'file out', pluralLabel: 'file out modules', go: this.fileOut.bind(this)});

    cmdList.addItem({label: 'forget I was changed', go: this.forgetIWasChanged.bind(this)});

    cmdList.addLine();

    cmdList.addItem({label: 'get module object', go: this.getModuleObject.bind(this)});

    cmdList.addLine();

    cmdList.addItem({label: 'all objects', go: this.getAllObjects.bind(this)});
  }, {category: ['menu']});

});

thisModule.addSlots(transporter, function(add) {

  add.method('addMenuItemsTo', function(menu, evt) {
    menu.addLine();

    var snapshottingWorks = false; // aaa - turn this on once it works.
    if (snapshottingWorks) {
    menu.addItem(["save snapshot", function(evt) {
      var snapshotter = new Snapshotter();
      snapshotter.walk(lobby);
      var snapshot = snapshotter.completeSnapshotText();

      var baseDirURL = URL.source.getDirectory().withRelativePath("javascripts/snapshots/");
      var fileName = "snapshot_" + snapshotter._number + ".js";
      var url = baseDirURL.withFilename(fileName);
      var status = new Resource(Record.newPlainInstance({URL: url})).store(snapshot, true).getStatus();
      if (! status.isSuccess()) {
        throw "failed to write " + fileName + ", status is " + status.code();
      }
    }]);
    }

    menu.addItem(["all modules", function(evt) {
      var world = evt.hand.world();
      world.assumePose(world.listPoseOfMorphsFor(Object.newChildOf(enumerator, transporter.module, 'eachModule'), "all modules"));
    }]);

    menu.addItem(["changed modules", function(evt) {
      var world = evt.hand.world();
      world.assumePose(world.listPoseOfMorphsFor(transporter.module.changedOnes(), "all modules"));
    }]);

    // aaa - hack because I haven't managed to get WebDAV working on adamspitz.com yet
    if (! URL.source.hostname.include("adamspitz.com")) {

      menu.addItem(["file in...", function(evt) {
        var world = evt.hand.world();
        var baseDir = new FileDirectory(lobby.transporter.module.urlForCoreModulesDirectory());
        var modulesMenu = new MenuMorph(this.moduleMenuItemsForDir(baseDir, ""), world);
        modulesMenu.openIn(world, evt.point());
      }.bind(this)]);

    }

  }, {category: ['menu']});

  add.method('moduleMenuItemsForDir', function(dir, pathFromModuleSystemRootDir) {
    var menuItems = [];

    var subdirURLs = dir.subdirectories();
    subdirURLs.each(function(subdirURL) {
      var subdir = new FileDirectory(subdirURL);
      var subdirName = subdirURL.filename();
      menuItems.push([subdirName, this.moduleMenuItemsForDir(subdir, pathFromModuleSystemRootDir + "/" + subdirName)]);
    }.bind(this));

    var jsFileNames = dir.filenames().select(function(n) {return n.endsWith(".js");});
    jsFileNames.each(function(n) {
      menuItems.push([n, function(evt) {
        var moduleName = n.substring(0, n.length - 3);
        MessageNotifierMorph.showIfErrorDuring(function() {
          lobby.transporter.module.fileIn(pathFromModuleSystemRootDir, moduleName);
        }, evt);
      }]);
    });

    return menuItems;
  }, {category: ['menu']});

});

});
<|endoftext|>


<commit_before>module.exports = function (options) {
  var separator = options && options.separator ? options.separator : ','
    , escapeNewlines = options && options.escapeNewlines === true
    , regexp = new RegExp('[' + separator + '\r\n"]')
    , escape = function (cell) {

        if (typeof(cell) === 'string') {
          if (escapeNewlines) {
            cell = cell.replace(/\n/g, '\\n')
          }

          cell = regexp.test(cell) ? '"' + cell.replace(/"/g, '""') + '"' : cell
        }

        return cell
      }

  return function (array) {
    return array.map(escape).join(separator)
  }
}<commit_msg>work in IE8<commit_after>var map = function (input, fn) {
      var result = Array(input.length)

      for(var i = 0; i < input.length; ++i) {
        result[i] = fn(input[i])
      }

      return result
    }

module.exports = function (options) {
  var separator = options && options.separator ? options.separator : ','
    , escapeNewlines = options && options.escapeNewlines === true
    , regexp = new RegExp('[' + separator + '\r\n"]')
    , escape = function (cell) {

        if (typeof(cell) === 'string') {
          if (escapeNewlines) {
            cell = cell.replace(/\n/g, '\\n')
          }

          cell = regexp.test(cell) ? '"' + cell.replace(/"/g, '""') + '"' : cell
        }

        return cell
      }

  return function (array) {
    return map(array, escape).join(separator)
  }
}<|endoftext|>