<commit_before>// Strider Simple Worker
// Niall O'Higgins 2012
//
// A simple, in-process worker implementation for Strider.
//

var exec = require('child_process').exec
var gitane = require('gitane')
var gumshoe = require('gumshoe')
var path = require('path')
var spawn = require('child_process').spawn
var Step = require('step')

// Built-in rules for project-type detection
var DEFAULT_PROJECT_TYPE_RULES = [

  // Node
  {filename:"package.json", grep:/express/i, language:"node.js", framework:"express", prepare:"npm install", test:"npm test"},
  {filename:"package.json", grep:/connect/i, language:"node.js", framework:"connect", prepare:"npm install", test:"npm test"},
  {filename:"package.json", exists:true, language:"node.js", framework:null, prepare:"npm install", test:"npm test"},
  // Python
  {filename:"setup.py", grep:/pyramid/i, language:"python", framework:"pyramid"},
  {filename:"manage.py", grep:/django/i, language:"python", framework:"django"},
  {filename:"setup.py", exists:true, language:"python", framework:null},

]

// detection rules which may be added by worker plugins
// rules must contain a property *test* which can either be a string or a function taking a callback.
// if a string, this is a shell command to be executed to run project tests (e.g. "npm install")
// if a function, this accepts a callback argument of signature function(err) which must be called.
//
// rules may contain a property *prepare* which can be a string or a function like *test*. this is for pre-test
// preparations (e.g. "npm install")
//
var detectionRules = []
// pre-start commands which may be added by worker plugins
// E.g. to start mongodb or postgresql
// pre-start commands may be either a string or a function
// XXX: foreground vs daemonprocesses
// if a string, this is a shell command to be executed to run project tests (e.g. "mongod")
// if a function, this accepts a callback argument of signature function(err) which must be called.
var setupActions = []
// teardown commands which may be added by worker plugins
// E.g. to stop mongodb or postgresql
// teardown commands may be either a string or a function
// XXX: foreground vs daemonprocesses
// if a string, this is a shell command to be executed to run project tests (e.g. "kill $PID")
// if a function, this accepts a callback argument of signature function(err) which must be called.
var teardownActions = []

function registerEvents(emitter) {

  // the queue.new_task event is primary way jobs are submitted
  //

  emitter.on('queue.new_job', function(data) {

    function updateStatus(evType, timeElapsed, opts) {
      var msg = {
        userId:data.user_id,
        jobId:data.job_id,
        timeElapsed:timeElapsed,
        repoUrl:data.repo_config.url,
        stdout: opts.stdout || "",
        stderr: opts.stderr || "",
        stdmerged: opts.stdmerged || "",
        autodetectResult:opts.autodetectResult || null,
        testExitCode: null,
        deployExitCode: null,
      }
      if (opts.testExitCode !== undefined) {
        msg.testExitCode = opts.testExitCode
      }
      if (opts.deployExitCode !== undefined) {
        msg.deployExitCode = opts.deployExitCode
      }

      emitter.emit(evType, msg)
    }

    // cross-process (per-job) output buffers
    var stderrBuffer = ""
    var stdoutBuffer = ""
    var stdmergedBuffer = ""

    function forkProc(t1, cwd, shell) {
      var split = shell.split(/\s+/)
      var cmd = split[0]
      var args = split.slice(1)
      var env = process.env
      env.PAAS_NAME = 'strider'
      var proc = spawn(cmd, args, {cwd: cwd, env: env})

      // per-process output buffers
      proc.stderrBuffer = ""
      proc.stdoutBuffer = ""
      proc.stdmergedBuffer = ""

      proc.stdout.setEncoding('utf8')
      proc.stderr.setEncoding('utf8')

      proc.stdout.on('data', function(buf) {
        proc.stdoutBuffer += buf
        proc.stdmergedBuffer += buf
        stdoutBuffer += buf
        stdmergedBuffer += buf
        var t2 = new Date()
        var elapsed = (t2.getTime() - t1.getTime()) / 1000
        updateStatus("queue.task_update", elapsed, {stdout:buf})
      })

      proc.stderr.on('data', function(buf) {
        proc.stderrBuffer += buf
        proc.stdmergedBuffer += buf
        stderrBuffer += buf
        stdmergedBuffer += buf
        var t2 = new Date()
        var elapsed = (t2.getTime() - t1.getTime()) / 1000
        updateStatus("queue.task_update", elapsed, {stderr:buf})
      })

      return proc
    }

    function doTestRun(cwd, prepareCmd, testCmd) {
      var preProc = forkProc(t1, cwd, prepareCmd)
      preProc.on('exit', function(exitCode) {
        console.log("process exited with code: %d", exitCode)
        if (exitCode === 0) {
          // Preparatory phase completed OK - continue
          var testProc = forkProc(t1, cwd, testCmd)

          testProc.on('exit', function(exitCode) {
            var t2 = new Date()
            var elapsed = (t2.getTime() - t1.getTime()) / 1000
            updateStatus("queue.task_complete", elapsed, {
              stderr:stderrBuffer,
              stdout:stdoutBuffer,
              stdmerged:stdmergedBuffer,
              testExitCode:exitCode,
              deployExitCode:null
            })
          })
        } else {
          var t2 = new Date()
          var elapsed = (t2.getTime() - t1.getTime()) / 1000
          updateStatus("queue.task_complete", elapsed, {
            stderr:stderrBuffer,
            stdout:stdoutBuffer,
            stdmerged:stdmergedBuffer,
            testExitCode:exitCode,
            deployExitCode:null
          })

        }
      })
    }

    var dir = path.join(__dirname, '_work')
    console.log('new job')
    var t1 = new Date()
    Step(
      function() {
        // XXX: Support incremental builds at some point
        exec('rm -rf ' + dir + ' ; mkdir -p ' + dir, this)
      },
      function(err) {
        if (err) throw err
        console.log("cloning %s", data.repo_ssh_url)
        var t2 = new Date()
        var elapsed = (t2.getTime() - t1.getTime()) / 1000
        var msg = "Starting git clone of repo at " + data.repo_ssh_url + "\n"
        updateStatus("queue.task_update", elapsed, {stdout:msg, stdmerged:msg})
        gitane.run(dir, data.repo_config.privkey, 'git clone ' + data.repo_ssh_url, this)
      },
      function(err, stderr, stdout) {
        if (err) throw err
        this.workingDir = path.join(dir, path.basename(data.repo_ssh_url.replace('.git', '')))
        var t2 = new Date()
        var elapsed = (t2.getTime() - t1.getTime()) / 1000
        var msg = "Git clone complete\n"
        updateStatus("queue.task_update", elapsed, {stdout:stdout, stderr:stderr, stdmerged:stdout+stderr})
        updateStatus("queue.task_update", elapsed, {stdout:msg, stdmerged:msg})
        gumshoe.run(this.workingDir, detectionRules, this)
      },
      function(err, result) {
        if (err) throw err
        // XXX: Setup
        doTestRun(this.workingDir, result.prepare, result.test)
        // XXX: Teardown

      }

    )
  })
}

// Add an array of detection rules
function addDetectionRules(r) {
  detectionRules = detectionRules.concat(r)
}

// Add a single detection rule
function addDetectionRule(r) {
  detectionRules.push(r)

}

module.exports = function(context, cb) {
  // XXX test purposes
  detectionRules = DEFAULT_PROJECT_TYPE_RULES
  // Build a worker context, which is a stripped-down version of the webapp context
  var workerContext = {
    addDetectionRule:addDetectionRule,
    addDetectionRules:addDetectionRules,
    config: context.config,
    emitter: context.emitter,
    extdir: context.extdir,
  }

  Step(
    function() {
      context.loader.initExtensions(context.extdir, "worker", workerContext, null, this)
    },
    function(err, initialized) {
      registerEvents(context.emitter)
      console.log("Strider Simple Worker ready")
      cb(null, null)
    }
  )

}
<commit_msg>fix comment.<commit_after>// Strider Simple Worker
// Niall O'Higgins 2012
//
// A simple, in-process worker implementation for Strider.
//

var exec = require('child_process').exec
var gitane = require('gitane')
var gumshoe = require('gumshoe')
var path = require('path')
var spawn = require('child_process').spawn
var Step = require('step')

// Built-in rules for project-type detection
var DEFAULT_PROJECT_TYPE_RULES = [

  // Node
  {filename:"package.json", grep:/express/i, language:"node.js", framework:"express", prepare:"npm install", test:"npm test"},
  {filename:"package.json", grep:/connect/i, language:"node.js", framework:"connect", prepare:"npm install", test:"npm test"},
  {filename:"package.json", exists:true, language:"node.js", framework:null, prepare:"npm install", test:"npm test"},
  // Python
  {filename:"setup.py", grep:/pyramid/i, language:"python", framework:"pyramid"},
  {filename:"manage.py", grep:/django/i, language:"python", framework:"django"},
  {filename:"setup.py", exists:true, language:"python", framework:null},

]

// detection rules which may be added by worker plugins
// rules must contain a property *test* which can either be a string or a function taking a callback.
// if a string, this is a shell command to be executed to run project tests (e.g. "npm install")
// if a function, this accepts a callback argument of signature function(err) which must be called.
//
// rules may contain a property *prepare* which can be a string or a function like *test*. this is for pre-test
// preparations (e.g. "npm install")
//
var detectionRules = []
// pre-start commands which may be added by worker plugins
// E.g. to start mongodb or postgresql
// pre-start commands may be either a string or a function
// XXX: foreground vs daemonprocesses
// if a string, this is a shell command to be executed to run project tests (e.g. "mongod")
// if a function, this accepts a callback argument of signature function(err) which must be called.
var setupActions = []
// teardown commands which may be added by worker plugins
// E.g. to stop mongodb or postgresql
// teardown commands may be either a string or a function
// XXX: foreground vs daemonprocesses
// if a string, this is a shell command to be executed to run project tests (e.g. "kill $PID")
// if a function, this accepts a callback argument of signature function(err) which must be called.
var teardownActions = []

function registerEvents(emitter) {

  // the queue.new_job event is primary way jobs are submitted
  //

  emitter.on('queue.new_job', function(data) {

    function updateStatus(evType, timeElapsed, opts) {
      var msg = {
        userId:data.user_id,
        jobId:data.job_id,
        timeElapsed:timeElapsed,
        repoUrl:data.repo_config.url,
        stdout: opts.stdout || "",
        stderr: opts.stderr || "",
        stdmerged: opts.stdmerged || "",
        autodetectResult:opts.autodetectResult || null,
        testExitCode: null,
        deployExitCode: null,
      }
      if (opts.testExitCode !== undefined) {
        msg.testExitCode = opts.testExitCode
      }
      if (opts.deployExitCode !== undefined) {
        msg.deployExitCode = opts.deployExitCode
      }

      emitter.emit(evType, msg)
    }

    // cross-process (per-job) output buffers
    var stderrBuffer = ""
    var stdoutBuffer = ""
    var stdmergedBuffer = ""

    function forkProc(t1, cwd, shell) {
      var split = shell.split(/\s+/)
      var cmd = split[0]
      var args = split.slice(1)
      var env = process.env
      env.PAAS_NAME = 'strider'
      var proc = spawn(cmd, args, {cwd: cwd, env: env})

      // per-process output buffers
      proc.stderrBuffer = ""
      proc.stdoutBuffer = ""
      proc.stdmergedBuffer = ""

      proc.stdout.setEncoding('utf8')
      proc.stderr.setEncoding('utf8')

      proc.stdout.on('data', function(buf) {
        proc.stdoutBuffer += buf
        proc.stdmergedBuffer += buf
        stdoutBuffer += buf
        stdmergedBuffer += buf
        var t2 = new Date()
        var elapsed = (t2.getTime() - t1.getTime()) / 1000
        updateStatus("queue.task_update", elapsed, {stdout:buf})
      })

      proc.stderr.on('data', function(buf) {
        proc.stderrBuffer += buf
        proc.stdmergedBuffer += buf
        stderrBuffer += buf
        stdmergedBuffer += buf
        var t2 = new Date()
        var elapsed = (t2.getTime() - t1.getTime()) / 1000
        updateStatus("queue.task_update", elapsed, {stderr:buf})
      })

      return proc
    }

    function doTestRun(cwd, prepareCmd, testCmd) {
      var preProc = forkProc(t1, cwd, prepareCmd)
      preProc.on('exit', function(exitCode) {
        console.log("process exited with code: %d", exitCode)
        if (exitCode === 0) {
          // Preparatory phase completed OK - continue
          var testProc = forkProc(t1, cwd, testCmd)

          testProc.on('exit', function(exitCode) {
            var t2 = new Date()
            var elapsed = (t2.getTime() - t1.getTime()) / 1000
            updateStatus("queue.task_complete", elapsed, {
              stderr:stderrBuffer,
              stdout:stdoutBuffer,
              stdmerged:stdmergedBuffer,
              testExitCode:exitCode,
              deployExitCode:null
            })
          })
        } else {
          var t2 = new Date()
          var elapsed = (t2.getTime() - t1.getTime()) / 1000
          updateStatus("queue.task_complete", elapsed, {
            stderr:stderrBuffer,
            stdout:stdoutBuffer,
            stdmerged:stdmergedBuffer,
            testExitCode:exitCode,
            deployExitCode:null
          })

        }
      })
    }

    var dir = path.join(__dirname, '_work')
    console.log('new job')
    var t1 = new Date()
    Step(
      function() {
        // XXX: Support incremental builds at some point
        exec('rm -rf ' + dir + ' ; mkdir -p ' + dir, this)
      },
      function(err) {
        if (err) throw err
        console.log("cloning %s", data.repo_ssh_url)
        var t2 = new Date()
        var elapsed = (t2.getTime() - t1.getTime()) / 1000
        var msg = "Starting git clone of repo at " + data.repo_ssh_url + "\n"
        updateStatus("queue.task_update", elapsed, {stdout:msg, stdmerged:msg})
        gitane.run(dir, data.repo_config.privkey, 'git clone ' + data.repo_ssh_url, this)
      },
      function(err, stderr, stdout) {
        if (err) throw err
        this.workingDir = path.join(dir, path.basename(data.repo_ssh_url.replace('.git', '')))
        var t2 = new Date()
        var elapsed = (t2.getTime() - t1.getTime()) / 1000
        var msg = "Git clone complete\n"
        updateStatus("queue.task_update", elapsed, {stdout:stdout, stderr:stderr, stdmerged:stdout+stderr})
        updateStatus("queue.task_update", elapsed, {stdout:msg, stdmerged:msg})
        gumshoe.run(this.workingDir, detectionRules, this)
      },
      function(err, result) {
        if (err) throw err
        // XXX: Setup
        doTestRun(this.workingDir, result.prepare, result.test)
        // XXX: Teardown

      }

    )
  })
}

// Add an array of detection rules
function addDetectionRules(r) {
  detectionRules = detectionRules.concat(r)
}

// Add a single detection rule
function addDetectionRule(r) {
  detectionRules.push(r)

}

module.exports = function(context, cb) {
  // XXX test purposes
  detectionRules = DEFAULT_PROJECT_TYPE_RULES
  // Build a worker context, which is a stripped-down version of the webapp context
  var workerContext = {
    addDetectionRule:addDetectionRule,
    addDetectionRules:addDetectionRules,
    config: context.config,
    emitter: context.emitter,
    extdir: context.extdir,
  }

  Step(
    function() {
      context.loader.initExtensions(context.extdir, "worker", workerContext, null, this)
    },
    function(err, initialized) {
      registerEvents(context.emitter)
      console.log("Strider Simple Worker ready")
      cb(null, null)
    }
  )

}
<|endoftext|>


<commit_before>(function(){
	'use strict';

	angular
		.module('PLMApp')
		.controller('Exercise', Exercise);

	Exercise.$inject = [
		'$window', '$http', '$scope', '$sce', '$stateParams',
		'connection', 'listenersHandler', 'langs', 'exercisesList',
		'canvas', 'drawWithDOM',
		'$timeout', '$interval',
		'locker',
		'BuggleWorld', 'BuggleWorldView',
		'BatWorld', 'BatWorldView'
	];

	function Exercise($window, $http, $scope, $sce, $stateParams,
		connection, listenersHandler, langs, exercisesList,
		canvas, drawWithDOM,
		$timeout, $interval,
		locker,
		BuggleWorld, BuggleWorldView,
		BatWorld, BatWorldView) {

		var exercise = this;

		var panelID = 'panel';
		var canvasID = 'canvas';

		exercise.lessonID = $stateParams.lessonID;
		exercise.id = $stateParams.exerciseID;

		exercise.displayInstructions = 'instructions';
		exercise.displayResults = 'result';

		exercise.isRunning = false;
		exercise.isPlaying = false;
		exercise.isChangingProgLang = false;
		exercise.playedDemo = false;

		exercise.instructions = null;
		exercise.api = null;
		exercise.resultType = null;
		exercise.result = '';
		exercise.logs = '';

		exercise.nonImplementedWorldException = false;

		exercise.initialWorlds = {};
		exercise.answerWorlds = {};
		exercise.currentWorlds = {};
		exercise.currentWorld = null;
		exercise.currentWorldID = null;
		exercise.worldKind = 'current';
		exercise.worldIDs = []; // Mandatory to generate dynamically the select
		exercise.updateModelLoop = null;
		exercise.updateViewLoop = null;

		locker.bind($scope, 'timer', 1000);
		exercise.timer = locker.get('timer');

		exercise.currentState = -1;
		exercise.lastStateDrawn = -1;
		exercise.preventLoop = false;

		exercise.currentProgrammingLanguage = null;
		exercise.programmingLanguages = [];

		exercise.editor = null;
        exercise.ide = "codemirror";

		exercise.exercisesAsList = null;
		exercise.exercisesAsTree = null;
		exercise.defaultNextExercise = null;
		exercise.selectedRootLecture = null;
		exercise.selectedNextExercise = null;

		exercise.drawServiceType = '';
		exercise.drawService = null;
		exercise.drawingArea = 'drawingArea';

		exercise.demoNeeded = false;
		exercise.objectiveViewNeeded = false;
		exercise.animationPlayerNeeded = false;

		exercise.instructionsIsFullScreen = false;
		exercise.instructionsClass='';
		exercise.worldsViewClass='';

		exercise.runDemo = runDemo;
		exercise.runCode = runCode;
		exercise.reset = reset;
		exercise.replay = replay;
		exercise.stopExecution = stopExecution;
		exercise.setWorldState = setWorldState;
		exercise.setCurrentWorld = setCurrentWorld;
		exercise.switchToObjective = switchToObjective;
		exercise.setProgrammingLanguage = setProgrammingLanguage;
		exercise.setSelectedRootLecture = setSelectedRootLecture;
		exercise.setSelectedNextExercise = setSelectedNextExercise;
		exercise.updateSpeed = updateSpeed;
		exercise.resetExercise = resetExercise;
		exercise.resizeInstructions = resizeInstructions;

		$scope.codemirrorLoaded = function(_editor){
			exercise.editor = _editor;
			resizeCodeMirror();
		};

		function getExercise() {
			var args = {
					lessonID: exercise.lessonID,
			};
			if(exercise.id !== '')
			{
				args.exerciseID = exercise.id;
			}
			connection.sendMessage('getExercise', args);
		}

		$scope.$on('exercisesListReady', initExerciseSelector);

		var offDisplayMessage = listenersHandler.register('onmessage', handleMessage);
		getExercise();

		function handleMessage(data) {
			console.log('message received: ', data);
			var cmd = data.cmd;
			var args = data.args;
			switch(cmd) {
				case 'exercise':
					setExercise(args.exercise);
					break;
				case 'executionResult':
					displayResult(args.msgType, args.msg);
					break;
				case 'demoEnded':
					console.log('The demo ended!');
					exercise.isRunning = false;
					exercise.playedDemo = true;
					break;
				case 'operations':
					handleOperations(args.worldID, 'current', args.operations);
					break;
				case 'demoOperations':
					handleOperations(args.worldID, 'answer', args.operations);
					break;
				case 'log':
					exercise.logs += args.msg;
					break;
				case 'newProgLang':
					updateUI(args.newProgLang, args.instructions, null, args.code);
					exercise.isChangingProgLang = false;
					break;
				case 'newHumanLang':
					updateUI(exercise.currentProgrammingLanguage, args.instructions, args.api, null);
					break;
			}
		}

		function setExercise(data) {
			exercise.id = data.id;
			exercise.instructions = $sce.trustAsHtml(data.instructions);
			exercise.api = $sce.trustAsHtml(data.api);
			exercise.code = data.code.trim();
			exercise.currentWorldID = data.selectedWorldID;

			if(data.exception === 'nonImplementedWorldException') {
				exercise.nonImplementedWorldException = true;
			}

			if(!exercise.nonImplementedWorldException) {
				for(var worldID in data.initialWorlds) {
					if(data.initialWorlds.hasOwnProperty(worldID)) {
						exercise.initialWorlds[worldID] = {};
						var initialWorld = data.initialWorlds[worldID];
						var world;
						switch(initialWorld.type) {
							case 'BuggleWorld':
								exercise.demoNeeded = true;
								exercise.objectiveViewNeeded = true;
								exercise.animationPlayerNeeded = true;
								world = new BuggleWorld(initialWorld);
								initCanvas(BuggleWorldView.draw);
								break;
							case 'BatWorld':
								world = new BatWorld(initialWorld);
								BatWorldView.setScope($scope);
								initDrawWithDOM(BatWorldView.draw);
								break;
						}
						exercise.initialWorlds[worldID] = world;
						exercise.answerWorlds[worldID] = world.clone();
						exercise.currentWorlds[worldID] = world.clone();
					}
				}

				exercise.worldIDs = Object.keys(exercise.currentWorlds);

				setCurrentWorld('current');

				window.addEventListener('resize', resizeCodeMirror, false);
			}

			exercise.programmingLanguages = data.programmingLanguages;
			for(var i=0; i<exercise.programmingLanguages.length; i++) {
				var pl = exercise.programmingLanguages[i];
				if(pl.lang === data.currentProgrammingLanguage) {
					exercise.currentProgrammingLanguage = pl;
					setIDEMode(pl);
				}
			}

			$(document).foundation('dropdown', 'reflow');
			$(document).foundation('equalizer', 'reflow');

			exercise.resultType = null;
			exercise.result = '';
			exercise.logs = '';

			exercisesList.setCurrentLessonID(exercise.lessonID);
		}

		function updateInstructions(instructions, api) {
			exercise.instructions = $sce.trustAsHtml(instructions);
			exercise.api = $sce.trustAsHtml(api);
		}

		function setCurrentWorld(worldKind) {
			$timeout.cancel(exercise.updateModelLoop);
			$interval.cancel(exercise.updateViewLoop);
			exercise.worldKind = worldKind;
			exercise.currentWorld = exercise[exercise.worldKind+'Worlds'][exercise.currentWorldID];
			exercise.currentState = exercise.currentWorld.currentState;
			exercise.drawService.setWorld(exercise.currentWorld);
		}

		function runDemo() {
			exercise.updateViewLoop = null;
			exercise.isPlaying = true;
			setCurrentWorld('answer');
			if(!exercise.playedDemo) {
				var args = {
						lessonID: exercise.lessonID,
						exerciseID: exercise.id,
				};
				connection.sendMessage('runDemo', args);
				exercise.isRunning = true;
			}
			else {
				// We don't need to query the server again
				// Just to replay the animation
				replay();
			}
		}

		function switchToObjective() {
			if(!exercise.playedDemo) {
				runDemo();
			}
			else {
				setCurrentWorld('answer');
			}
		}

		function runCode() {
			var args;

			exercise.updateViewLoop = null;
			exercise.isPlaying = true;
			exercise.worldIDs.map(function(key) {
				reset(key, 'current', false);
			});
			setCurrentWorld('current');
			args = {
					lessonID: exercise.lessonID,
					exerciseID: exercise.id,
					code: exercise.code
			};
			connection.sendMessage('runExercise', args);
			exercise.isRunning = true;
		}

		function stopExecution() {
			connection.sendMessage('stopExecution', null);
		}

		function displayResult(msgType, msg) {
			console.log(msgType, ' - ', msg);
			exercise.result = msg;
			if(msgType === 1) {
				$('#successModal').foundation('reveal', 'open');
			}
			exercise.resultType = msgType;
			exercise.display = 'result';
			exercise.isRunning = false;
		}

		function reset(worldID, worldKind, keepOperations) {
			// We may want to keep the operations in order to replay the execution
			var operations = [];
			var steps = [];
			if(keepOperations === true) {
				operations = exercise[worldKind+'Worlds'][worldID].operations;
				steps = exercise[worldKind+'Worlds'][worldID].steps;
			}

			var initialWorld = exercise.initialWorlds[worldID];
			exercise[worldKind+'Worlds'][worldID] = initialWorld.clone();
			exercise[worldKind+'Worlds'][worldID].operations = operations;
			exercise[worldKind+'Worlds'][worldID].steps = steps;

			if(worldID === exercise.currentWorldID) {
				exercise.currentState = -1;
				exercise.currentWorld = exercise[worldKind+'Worlds'][worldID];
				exercise.drawService.setWorld(exercise.currentWorld);
			}

			exercise.lastStateDrawn = -1;

			$timeout.cancel(exercise.updateViewLoop);
			exercise.isPlaying = false;
		}

		function replay() {
			reset(exercise.currentWorldID, exercise.worldKind, true);
			exercise.isPlaying = true;
			startUpdateModelLoop();
			startUpdateViewLoop();
		}

		function handleOperations(worldID, worldKind, operations) {
			var world = exercise[worldKind+'Worlds'][worldID];
			world.addOperations(operations);
			if(exercise.updateViewLoop === null) {
				exercise.isPlaying = true;
				startUpdateModelLoop();
				startUpdateViewLoop();
			}
		}

		function startUpdateModelLoop() {
			exercise.updateModelLoop = $timeout(updateModel, exercise.timer);
		}

		function updateModel() {
			var currentState = exercise.currentWorld.currentState;
			var nbStates = exercise.currentWorld.operations.length-1;
			if(currentState !== nbStates) {
				exercise.currentWorld.setState(++currentState);
				exercise.currentState = currentState;
			}

			if(!exercise.isRunning && currentState === nbStates){
				exercise.updateModelLoop = null;
				exercise.isPlaying = false;
			}
			else {
				exercise.updateModelLoop = $timeout(updateModel, exercise.timer);
			}
		}

		function startUpdateViewLoop() {
			exercise.updateViewLoop = $interval(updateView, 1/10);
		}

		function updateView() {
			if(exercise.lastStateDrawn !== exercise.currentWorld.currentState) {
				exercise.drawService.update();
				exercise.lastStateDrawn	= exercise.currentWorld.currentState;
			}

			if(!exercise.isPlaying){
				$interval.cancel(exercise.updateViewLoop);
			}
		}

		function setWorldState(state) {
			$timeout.cancel(exercise.updateModelLoop);
			$interval.cancel(exercise.updateViewLoop);
			exercise.isPlaying = false;
			state = parseInt(state);
			exercise.currentWorld.setState(state);
			exercise.currentState = state;
			exercise.drawService.update();
		}

		function initExerciseSelector() {
			exercisesList.setCurrentExerciseID(exercise.id);
			exercise.exercisesAsList = exercisesList.getExercisesList();
			exercise.exercisesAsTree = exercisesList.getExercisesTree();
			exercise.defaultNextExercise = exercisesList.getNextExerciseID();
			exercise.selectedRootLecture = null;
			exercise.selectedNextExercise = null;

			// Update modal
			$(document).foundation('reveal', 'reflow');
		}

		function setSelectedRootLecture(rootLecture) {
			exercise.selectedRootLecture = rootLecture;
			setSelectedNextExercise(rootLecture);
		}

		function setSelectedNextExercise(exo) {
			exercise.selectedNextExercise = exo;
		}

		function setIDEMode(pl) {
			switch(pl.lang.toLowerCase()) {
				case 'java':
					exercise.editor.setOption('mode', 'text/x-java');
					break;
				case 'scala':
					exercise.editor.setOption('mode', 'text/x-scala');
					break;
				case 'c':
					exercise.editor.setOption('mode', 'text/x-csrc');
					break;
				case 'python':
					exercise.editor.setOption('mode', 'text/x-python');
					break;
			}
		}

        function setProgrammingLanguage(pl) {
            if (pl.lang == 'Blockly'){
                exercise.ide = 'blockly';
                Blockly.fireUiEvent(window, 'resize');
            }else
                exercise.ide = 'codemirror'
            exercise.isChangingProgLang = true;
            connection.sendMessage('setProgrammingLanguage', {
                programmingLanguage: pl.lang
            });
        }

		function updateUI(pl, instructions, api, code) {
			exercise.currentProgrammingLanguage = pl;
			setIDEMode(pl);
			exercise.instructions = $sce.trustAsHtml(instructions);
			if(api !== null)
				exercise.api = $sce.trustAsHtml(api);
			if(code !== null)
			exercise.code = code;
		}

		function resetExercise() {
			$('#resetExerciseModal').foundation('reveal', 'close');
			connection.sendMessage('revertExercise', {});
		}

		function updateSpeed () {
			$scope.timer = $('#executionSpeed').val();
		}

		$scope.$on('$destroy',function() {
			offDisplayMessage();
			$timeout.cancel(exercise.updateModelLoop);
			$interval.cancel(exercise.updateViewLoop);
			exercise.initialWorlds = {};
			exercise.answerWorlds = {};
			exercise.currentWorlds = {};
			exercise.currentWorld = null;
			exercise.drawService.setWorld(null);
			exercise.instructions = null;
			exercise.api = null;
			exercise.resultType = null;
			exercise.result = null;
			exercise.logs = null;
		});

		function initCanvas(draw) {
			var canvasElt;
			var canvasWidth;
			var canvasHeight;

			exercise.drawServiceType = 'canvas';
			exercise.drawService = canvas;

			canvasElt = document.getElementById(canvasID);
			canvasWidth = $('#'+exercise.drawingArea).parent().width();
			canvasHeight = canvasWidth;

			canvas.init(canvasElt, canvasWidth, canvasHeight, draw);

			window.addEventListener('resize', resizeCanvas, false);
		}

		function initDrawWithDOM(draw) {
			var domElt;
			var panelWidth;

			exercise.drawServiceType = 'drawWithDOM';
			exercise.drawService = drawWithDOM;

			domElt = $('#'+panelID);
			panelWidth = $('#'+exercise.drawingArea).parent().width();
			domElt.css('height', panelWidth);
			domElt.css('overflow-y', 'auto');

			drawWithDOM.init(domElt, draw);
		}

		function resizeCanvas() {
			var canvasWidth = $('#'+exercise.drawingArea).parent().width();
			var canvasHeight = canvasWidth;
			exercise.drawService.resize(canvasWidth, canvasHeight);
			$(document).foundation('equalizer', 'reflow');
		}

		function resizeCodeMirror() {
			// Want to keep the IDE's height equals to the draw surface's one
			var drawingAreaHeight = $('#'+exercise.drawingArea).parent().width();
			exercise.editor.setSize(null, drawingAreaHeight);
			exercise.editor.refresh();
			$(document).foundation('equalizer', 'reflow');
		}

		function resizeInstructions() {
			if(!exercise.instructionsIsFullScreen) {
				exercise.instructionsIsFullScreen = true;
				exercise.instructionsClass='instructions-fullscreen';
				exercise.worldsViewClass='worlds-view-reduce';
			}
			else {
				exercise.instructionsIsFullScreen = false;
				exercise.instructionsClass='';
				exercise.worldsViewClass='';
			}
		}
	}
})();<commit_msg>Update exercise controller<commit_after>(function () {
    'use strict';

    angular
        .module('PLMApp')
        .controller('Exercise', Exercise);

    Exercise.$inject = [
  '$window', '$http', '$scope', '$sce', '$stateParams',
  'connection', 'listenersHandler', 'langs', 'exercisesList',
  'canvas', 'drawWithDOM',
  '$timeout', '$interval',
  'locker',
  'BuggleWorld', 'BuggleWorldView',
  'BatWorld', 'BatWorldView'
 ];

    function Exercise($window, $http, $scope, $sce, $stateParams,
        connection, listenersHandler, langs, exercisesList,
        canvas, drawWithDOM,
        $timeout, $interval,
        locker,
        BuggleWorld, BuggleWorldView,
        BatWorld, BatWorldView) {

        var exercise = this;

        var panelID = 'panel';
        var canvasID = 'canvas';

        exercise.lessonID = $stateParams.lessonID;
        exercise.id = $stateParams.exerciseID;

        exercise.displayInstructions = 'instructions';
        exercise.displayResults = 'result';

        exercise.isRunning = false;
        exercise.isPlaying = false;
        exercise.isChangingProgLang = false;
        exercise.playedDemo = false;

        exercise.instructions = null;
        exercise.api = null;
        exercise.resultType = null;
        exercise.result = '';
        exercise.logs = '';

        exercise.nonImplementedWorldException = false;

        exercise.initialWorlds = {};
        exercise.answerWorlds = {};
        exercise.currentWorlds = {};
        exercise.currentWorld = null;
        exercise.currentWorldID = null;
        exercise.worldKind = 'current';
        exercise.worldIDs = []; // Mandatory to generate dynamically the select
        exercise.updateModelLoop = null;
        exercise.updateViewLoop = null;

        locker.bind($scope, 'timer', 1000);
        exercise.timer = locker.get('timer');

        exercise.currentState = -1;
        exercise.lastStateDrawn = -1;
        exercise.preventLoop = false;

        exercise.currentProgrammingLanguage = null;
        exercise.programmingLanguages = [];

        exercise.editor = null;
        exercise.ide = "codemirror";

        exercise.exercisesAsList = null;
        exercise.exercisesAsTree = null;
        exercise.defaultNextExercise = null;
        exercise.selectedRootLecture = null;
        exercise.selectedNextExercise = null;

        exercise.drawServiceType = '';
        exercise.drawService = null;
        exercise.drawingArea = 'drawingArea';

        exercise.demoNeeded = false;
        exercise.objectiveViewNeeded = false;
        exercise.animationPlayerNeeded = false;

        exercise.instructionsIsFullScreen = false;
        exercise.instructionsClass = '';
        exercise.worldsViewClass = '';

        exercise.runDemo = runDemo;
        exercise.runCode = runCode;
        exercise.reset = reset;
        exercise.replay = replay;
        exercise.stopExecution = stopExecution;
        exercise.setWorldState = setWorldState;
        exercise.setCurrentWorld = setCurrentWorld;
        exercise.switchToObjective = switchToObjective;
        exercise.setProgrammingLanguage = setProgrammingLanguage;
        exercise.setSelectedRootLecture = setSelectedRootLecture;
        exercise.setSelectedNextExercise = setSelectedNextExercise;
        exercise.updateSpeed = updateSpeed;
        exercise.resetExercise = resetExercise;
        exercise.resizeInstructions = resizeInstructions;

        $scope.codemirrorLoaded = function (_editor) {
            exercise.editor = _editor;
            resizeCodeMirror();
        };

        function getExercise() {
            var args = {
                lessonID: exercise.lessonID,
            };
            if (exercise.id !== '') {
                args.exerciseID = exercise.id;
            }
            connection.sendMessage('getExercise', args);
        }

        $scope.$on('exercisesListReady', initExerciseSelector);

        var offDisplayMessage = listenersHandler.register('onmessage', handleMessage);
        getExercise();

        function handleMessage(data) {
            console.log('message received: ', data);
            var cmd = data.cmd;
            var args = data.args;
            switch (cmd) {
            case 'exercise':
                setExercise(args.exercise);
                break;
            case 'executionResult':
                displayResult(args.msgType, args.msg);
                break;
            case 'demoEnded':
                console.log('The demo ended!');
                exercise.isRunning = false;
                exercise.playedDemo = true;
                break;
            case 'operations':
                handleOperations(args.worldID, 'current', args.operations);
                break;
            case 'demoOperations':
                handleOperations(args.worldID, 'answer', args.operations);
                break;
            case 'log':
                exercise.logs += args.msg;
                break;
            case 'newProgLang':
                updateUI(args.newProgLang, args.instructions, null, args.code);
                exercise.isChangingProgLang = false;
                break;
            case 'newHumanLang':
                updateUI(exercise.currentProgrammingLanguage, args.instructions, args.api, null);
                break;
            }
        }

        function setExercise(data) {
            exercise.id = data.id;
            exercise.instructions = $sce.trustAsHtml(data.instructions);
            exercise.api = $sce.trustAsHtml(data.api);
            exercise.code = data.code.trim();
            exercise.currentWorldID = data.selectedWorldID;

            if (data.exception === 'nonImplementedWorldException') {
                exercise.nonImplementedWorldException = true;
            }

            if (!exercise.nonImplementedWorldException) {
                for (var worldID in data.initialWorlds) {
                    if (data.initialWorlds.hasOwnProperty(worldID)) {
                        exercise.initialWorlds[worldID] = {};
                        var initialWorld = data.initialWorlds[worldID];
                        var world;
                        switch (initialWorld.type) {
                        case 'BuggleWorld':
                            exercise.demoNeeded = true;
                            exercise.objectiveViewNeeded = true;
                            exercise.animationPlayerNeeded = true;
                            world = new BuggleWorld(initialWorld);
                            initCanvas(BuggleWorldView.draw);
                            break;
                        case 'BatWorld':
                            world = new BatWorld(initialWorld);
                            BatWorldView.setScope($scope);
                            initDrawWithDOM(BatWorldView.draw);
                            break;
                        }
                        exercise.initialWorlds[worldID] = world;
                        exercise.answerWorlds[worldID] = world.clone();
                        exercise.currentWorlds[worldID] = world.clone();
                    }
                }

                exercise.worldIDs = Object.keys(exercise.currentWorlds);

                setCurrentWorld('current');

                window.addEventListener('resize', resizeCodeMirror, false);
            }

            exercise.programmingLanguages = data.programmingLanguages;
            for (var i = 0; i < exercise.programmingLanguages.length; i++) {
                var pl = exercise.programmingLanguages[i];
                if (pl.lang === data.currentProgrammingLanguage) {
                    exercise.currentProgrammingLanguage = pl;
                    setIDEMode(pl);
                }
            }

            $(document).foundation('dropdown', 'reflow');
            $(document).foundation('equalizer', 'reflow');

            exercise.resultType = null;
            exercise.result = '';
            exercise.logs = '';

            exercisesList.setCurrentLessonID(exercise.lessonID);
        }

        function updateInstructions(instructions, api) {
            exercise.instructions = $sce.trustAsHtml(instructions);
            exercise.api = $sce.trustAsHtml(api);
        }

        function setCurrentWorld(worldKind) {
            $timeout.cancel(exercise.updateModelLoop);
            $interval.cancel(exercise.updateViewLoop);
            exercise.worldKind = worldKind;
            exercise.currentWorld = exercise[exercise.worldKind + 'Worlds'][exercise.currentWorldID];
            exercise.currentState = exercise.currentWorld.currentState;
            exercise.drawService.setWorld(exercise.currentWorld);
        }

        function runDemo() {
            exercise.updateViewLoop = null;
            exercise.isPlaying = true;
            setCurrentWorld('answer');
            if (!exercise.playedDemo) {
                var args = {
                    lessonID: exercise.lessonID,
                    exerciseID: exercise.id,
                };
                connection.sendMessage('runDemo', args);
                exercise.isRunning = true;
            } else {
                // We don't need to query the server again
                // Just to replay the animation
                replay();
            }
        }

        function switchToObjective() {
            if (!exercise.playedDemo) {
                runDemo();
            } else {
                setCurrentWorld('answer');
            }
        }

        function runCode() {
            var args;

            exercise.updateViewLoop = null;
            exercise.isPlaying = true;
            exercise.worldIDs.map(function (key) {
                reset(key, 'current', false);
            });
            setCurrentWorld('current');

            // Zone test pour blockly
            Blockly.JavaScript.INFINITE_LOOP_TRAP = null;
            exercise.code = Blockly.Python.workspaceToCode();
            alert(exercise.code);
            // Zone test pour blockly

            args = {
                lessonID: exercise.lessonID,
                exerciseID: exercise.id,
                code: exercise.code
            };
            connection.sendMessage('runExercise', args);
            exercise.isRunning = true;

        }

        function stopExecution() {
            connection.sendMessage('stopExecution', null);
        }

        function displayResult(msgType, msg) {
            console.log(msgType, ' - ', msg);
            exercise.result = msg;
            if (msgType === 1) {
                $('#successModal').foundation('reveal', 'open');
            }
            exercise.resultType = msgType;
            exercise.display = 'result';
            exercise.isRunning = false;
        }

        function reset(worldID, worldKind, keepOperations) {
            // We may want to keep the operations in order to replay the execution
            var operations = [];
            var steps = [];
            if (keepOperations === true) {
                operations = exercise[worldKind + 'Worlds'][worldID].operations;
                steps = exercise[worldKind + 'Worlds'][worldID].steps;
            }

            var initialWorld = exercise.initialWorlds[worldID];
            exercise[worldKind + 'Worlds'][worldID] = initialWorld.clone();
            exercise[worldKind + 'Worlds'][worldID].operations = operations;
            exercise[worldKind + 'Worlds'][worldID].steps = steps;

            if (worldID === exercise.currentWorldID) {
                exercise.currentState = -1;
                exercise.currentWorld = exercise[worldKind + 'Worlds'][worldID];
                exercise.drawService.setWorld(exercise.currentWorld);
            }

            exercise.lastStateDrawn = -1;

            $timeout.cancel(exercise.updateViewLoop);
            exercise.isPlaying = false;
        }

        function replay() {
            reset(exercise.currentWorldID, exercise.worldKind, true);
            exercise.isPlaying = true;
            startUpdateModelLoop();
            startUpdateViewLoop();
        }

        function handleOperations(worldID, worldKind, operations) {
            var world = exercise[worldKind + 'Worlds'][worldID];
            world.addOperations(operations);
            if (exercise.updateViewLoop === null) {
                exercise.isPlaying = true;
                startUpdateModelLoop();
                startUpdateViewLoop();
            }
        }

        function startUpdateModelLoop() {
            exercise.updateModelLoop = $timeout(updateModel, exercise.timer);
        }

        function updateModel() {
            var currentState = exercise.currentWorld.currentState;
            var nbStates = exercise.currentWorld.operations.length - 1;
            if (currentState !== nbStates) {
                exercise.currentWorld.setState(++currentState);
                exercise.currentState = currentState;
            }

            if (!exercise.isRunning && currentState === nbStates) {
                exercise.updateModelLoop = null;
                exercise.isPlaying = false;
            } else {
                exercise.updateModelLoop = $timeout(updateModel, exercise.timer);
            }
        }

        function startUpdateViewLoop() {
            exercise.updateViewLoop = $interval(updateView, 1 / 10);
        }

        function updateView() {
            if (exercise.lastStateDrawn !== exercise.currentWorld.currentState) {
                exercise.drawService.update();
                exercise.lastStateDrawn = exercise.currentWorld.currentState;
            }

            if (!exercise.isPlaying) {
                $interval.cancel(exercise.updateViewLoop);
            }
        }

        function setWorldState(state) {
            $timeout.cancel(exercise.updateModelLoop);
            $interval.cancel(exercise.updateViewLoop);
            exercise.isPlaying = false;
            state = parseInt(state);
            exercise.currentWorld.setState(state);
            exercise.currentState = state;
            exercise.drawService.update();
        }

        function initExerciseSelector() {
            exercisesList.setCurrentExerciseID(exercise.id);
            exercise.exercisesAsList = exercisesList.getExercisesList();
            exercise.exercisesAsTree = exercisesList.getExercisesTree();
            exercise.defaultNextExercise = exercisesList.getNextExerciseID();
            exercise.selectedRootLecture = null;
            exercise.selectedNextExercise = null;

            // Update modal
            $(document).foundation('reveal', 'reflow');
        }

        function setSelectedRootLecture(rootLecture) {
            exercise.selectedRootLecture = rootLecture;
            setSelectedNextExercise(rootLecture);
        }

        function setSelectedNextExercise(exo) {
            exercise.selectedNextExercise = exo;
        }

        function setIDEMode(pl) {
            switch (pl.lang.toLowerCase()) {
            case 'java':
                exercise.editor.setOption('mode', 'text/x-java');
                break;
            case 'scala':
                exercise.editor.setOption('mode', 'text/x-scala');
                break;
            case 'c':
                exercise.editor.setOption('mode', 'text/x-csrc');
                break;
            case 'python':
                exercise.editor.setOption('mode', 'text/x-python');
                break;
            }
        }

        function setProgrammingLanguage(pl) {
            if (pl.lang == 'Blockly') {
                exercise.ide = 'blockly';
                Blockly.fireUiEvent(window, 'resize');
            } else
                exercise.ide = 'codemirror'
            exercise.isChangingProgLang = true;
            connection.sendMessage('setProgrammingLanguage', {
                programmingLanguage: pl.lang
            });
        }

        function updateUI(pl, instructions, api, code) {
            exercise.currentProgrammingLanguage = pl;
            setIDEMode(pl);
            exercise.instructions = $sce.trustAsHtml(instructions);
            if (api !== null)
                exercise.api = $sce.trustAsHtml(api);
            if (code !== null)
                exercise.code = code;
        }

        function resetExercise() {
            $('#resetExerciseModal').foundation('reveal', 'close');
            connection.sendMessage('revertExercise', {});
        }

        function updateSpeed() {
            $scope.timer = $('#executionSpeed').val();
        }

        $scope.$on('$destroy', function () {
            offDisplayMessage();
            $timeout.cancel(exercise.updateModelLoop);
            $interval.cancel(exercise.updateViewLoop);
            exercise.initialWorlds = {};
            exercise.answerWorlds = {};
            exercise.currentWorlds = {};
            exercise.currentWorld = null;
            exercise.drawService.setWorld(null);
            exercise.instructions = null;
            exercise.api = null;
            exercise.resultType = null;
            exercise.result = null;
            exercise.logs = null;
        });

        function initCanvas(draw) {
            var canvasElt;
            var canvasWidth;
            var canvasHeight;

            exercise.drawServiceType = 'canvas';
            exercise.drawService = canvas;

            canvasElt = document.getElementById(canvasID);
            canvasWidth = $('#' + exercise.drawingArea).parent().width();
            canvasHeight = canvasWidth;

            canvas.init(canvasElt, canvasWidth, canvasHeight, draw);

            window.addEventListener('resize', resizeCanvas, false);
        }

        function initDrawWithDOM(draw) {
            var domElt;
            var panelWidth;

            exercise.drawServiceType = 'drawWithDOM';
            exercise.drawService = drawWithDOM;

            domElt = $('#' + panelID);
            panelWidth = $('#' + exercise.drawingArea).parent().width();
            domElt.css('height', panelWidth);
            domElt.css('overflow-y', 'auto');

            drawWithDOM.init(domElt, draw);
        }

        function resizeCanvas() {
            var canvasWidth = $('#' + exercise.drawingArea).parent().width();
            var canvasHeight = canvasWidth;
            exercise.drawService.resize(canvasWidth, canvasHeight);
            $(document).foundation('equalizer', 'reflow');
        }

        function resizeCodeMirror() {
            // Want to keep the IDE's height equals to the draw surface's one
            var drawingAreaHeight = $('#' + exercise.drawingArea).parent().width();
            exercise.editor.setSize(null, drawingAreaHeight);
            exercise.editor.refresh();
            $(document).foundation('equalizer', 'reflow');
        }

        function resizeInstructions() {
            if (!exercise.instructionsIsFullScreen) {
                exercise.instructionsIsFullScreen = true;
                exercise.instructionsClass = 'instructions-fullscreen';
                exercise.worldsViewClass = 'worlds-view-reduce';
            } else {
                exercise.instructionsIsFullScreen = false;
                exercise.instructionsClass = '';
                exercise.worldsViewClass = '';
            }
        }
    }
})();<|endoftext|>


<commit_before>/**
 * @module 101/equals
 */

/**
 * Functional version of ===
 * @function module:101/equals
 * @param {*} a - any value
 * @param {*} b - any value
 * @return {boolean} a === b
 */
module.exports = function (a, b) {
  if (arguments.length === 1) {
    return equals.bind(null, a);
  }
  else {
    return equals(a, b);
  }
};

function equals (a, b) {
  return a === b;
}<commit_msg>ES6 Object.is w/ MDN defined polyfill alternative<commit_after>/**
 * @module 101/equals
 */

/**
 * Functional version of ===
 * @function module:101/equals
 * @param {*} a - any value
 * @param {*} b - any value
 * @return {boolean} a === b
 */
module.exports = function (a, b) {
  if (arguments.length === 1) {
    return equals.bind(null, a);
  }
  else {
    return equals(a, b);
  }
};

function equals (v1, v2) {
  if (Object.is) {
    return Object.is(v1, v2);
  }
  else {
    // ES6 Object.is polyfill
    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
    if (v1 === 0 && v2 === 0) {
      return 1 / v1 === 1 / v2;
    }
    if (v1 !== v1) {
      return v2 !== v2;
    }
    return v1 === v2;
  }
}
<|endoftext|>


<commit_before>(function () {
    'use strict';

    /*jslint
        browser: true
    */
    /*global
        L, reqwest
    */

    var map,
        tileURL = 'https://maps.bustimes.org/styles/klokantech-basic/{z}/{x}/{y}' + (L.Browser.retina ? '@2x' : '') + '.png';

    function getMarker(latLng, direction) {
        direction = (direction || 0) - 135;
        return L.marker(latLng, {
            icon: L.divIcon({
                iconSize: [16, 16],
                html: '

',
                className: 'just-arrow'
            })
        }).on('mouseover', function () {
            this.openPopup();
        });
    }

    var timeMarkerOptions = {
        radius: 3,
        fillColor: '#000',
        fillOpacity: 1,
        weight: 0,
    };

    function closeMap() {
        window.location.hash = '';
        return false;
    }

    window.onkeydown = function(event) {
        if (event.keyCode === 27) { // esc
            closeMap();
        }
    };

    window.onhashchange = function(event) {
        if (map) {
            map.remove();
            map = null;
        }
        maybeOpenMap();
        event.preventDefault();
        return false;
    };

    function maybeOpenMap() {
        var journey = window.location.hash.slice(1),
            element = document.getElementById(journey);

        if (!element) {
            return;
        }

        reqwest('/' + journey + '.json', function(locations) {

            var mapContainer = element.getElementsByClassName('map')[0],
                layerGroup = L.layerGroup();

            map =  L.map(mapContainer);

            layerGroup.addTo(map);

            map.attributionControl.setPrefix('');

            L.tileLayer(tileURL, {
                attribution: '© MapTiler © OpenStreetMap contributors'
            }).addTo(map);

            var j,
                dateTime,
                popup,
                delta,
                coordinates,
                line = [],
                bounds,
                latDistance,
                lngDistance,
                timeDistance,
                previousCoordinates,
                minutes,
                previousTimestamp,
                latSpeed,
                lngSpeed;

            for (var i = locations.length - 1; i >= 0; i -= 1) {
                dateTime = new Date(locations[i].datetime);
                popup = dateTime.toTimeString().slice(0, 8);
                delta = locations[i].delta;
                if (delta) {
                    popup += '
About ';
                    if (delta > 0) {
                        popup += delta;
                    } else {
                        popup += delta * -1;
                    }
                    popup += ' minute';
                    if (delta !== 1 && delta !== -1) {
                        popup += 's';
                    }
                    if (delta > 0) {
                        popup += ' early';
                    } else {
                        popup += ' late';
                    }
                }
                coordinates = L.latLng(locations[i].coordinates[1], locations[i].coordinates[0]);
                getMarker(coordinates, locations[i].direction).bindPopup(popup).addTo(layerGroup);

                if (previousCoordinates) {
                    latDistance = previousCoordinates.lat - coordinates.lat;
                    lngDistance = previousCoordinates.lng - coordinates.lng;
                    minutes = Math.floor(previousTimestamp / 60000) * 60000;
                    timeDistance = previousTimestamp - dateTime.getTime();
                    latSpeed = latDistance / timeDistance;
                    lngSpeed = lngDistance / timeDistance;

                    for (j = previousTimestamp - minutes; j <= timeDistance; j += 60000) {
                        L.circleMarker(L.latLng(
                            previousCoordinates.lat - latSpeed * j,
                            previousCoordinates.lng - lngSpeed * j
                        ), timeMarkerOptions).addTo(layerGroup);
                    }
                }
                line.push(coordinates);
                previousTimestamp = dateTime.getTime();
                previousCoordinates = coordinates;
            }
            line = L.polyline(line, {
                style: {
                    weight: 3,
                    color: '#87f'
                }
            });
            line.addTo(layerGroup);
            bounds = line.getBounds();
            map.fitBounds(bounds);
            map.setMaxBounds(bounds.pad(.5));
            line.bringToBack();
        });
    }

    maybeOpenMap();
})();
<commit_msg>journey history map: use tooltip instead of popup<commit_after>(function () {
    'use strict';

    /*jslint
        browser: true
    */
    /*global
        L, reqwest
    */

    var map,
        tileURL = 'https://maps.bustimes.org/styles/klokantech-basic/{z}/{x}/{y}' + (L.Browser.retina ? '@2x' : '') + '.png';

    function getMarker(latLng, direction) {
        direction = (direction || 0) - 135;
        return L.marker(latLng, {
            icon: L.divIcon({
                iconSize: [16, 16],
                html: '

',
                className: 'just-arrow'
            })
        });
    }

    var timeMarkerOptions = {
        radius: 3,
        fillColor: '#000',
        fillOpacity: 1,
        weight: 0,
    };

    function closeMap() {
        window.location.hash = '';
        return false;
    }

    window.onkeydown = function(event) {
        if (event.keyCode === 27) { // esc
            closeMap();
        }
    };

    window.onhashchange = function(event) {
        if (map) {
            map.remove();
            map = null;
        }
        maybeOpenMap();
        event.preventDefault();
        return false;
    };

    function maybeOpenMap() {
        var journey = window.location.hash.slice(1),
            element = document.getElementById(journey);

        if (!element) {
            return;
        }

        reqwest('/' + journey + '.json', function(locations) {

            var mapContainer = element.getElementsByClassName('map')[0],
                layerGroup = L.layerGroup();

            map =  L.map(mapContainer);

            layerGroup.addTo(map);

            map.attributionControl.setPrefix('');

            L.tileLayer(tileURL, {
                attribution: '© MapTiler © OpenStreetMap contributors'
            }).addTo(map);

            var j,
                dateTime,
                popup,
                delta,
                coordinates,
                line = [],
                bounds,
                latDistance,
                lngDistance,
                timeDistance,
                previousCoordinates,
                minutes,
                previousTimestamp,
                latSpeed,
                lngSpeed;

            for (var i = locations.length - 1; i >= 0; i -= 1) {
                dateTime = new Date(locations[i].datetime);
                popup = dateTime.toTimeString().slice(0, 8);
                delta = locations[i].delta;
                if (delta) {
                    popup += '
About ';
                    if (delta > 0) {
                        popup += delta;
                    } else {
                        popup += delta * -1;
                    }
                    popup += ' minute';
                    if (delta !== 1 && delta !== -1) {
                        popup += 's';
                    }
                    if (delta > 0) {
                        popup += ' early';
                    } else {
                        popup += ' late';
                    }
                }
                coordinates = L.latLng(locations[i].coordinates[1], locations[i].coordinates[0]);
                getMarker(coordinates, locations[i].direction).bindTooltip(popup).addTo(layerGroup);

                if (previousCoordinates) {
                    latDistance = previousCoordinates.lat - coordinates.lat;
                    lngDistance = previousCoordinates.lng - coordinates.lng;
                    minutes = Math.floor(previousTimestamp / 60000) * 60000;
                    timeDistance = previousTimestamp - dateTime.getTime();
                    latSpeed = latDistance / timeDistance;
                    lngSpeed = lngDistance / timeDistance;

                    for (j = previousTimestamp - minutes; j <= timeDistance; j += 60000) {
                        L.circleMarker(L.latLng(
                            previousCoordinates.lat - latSpeed * j,
                            previousCoordinates.lng - lngSpeed * j
                        ), timeMarkerOptions).addTo(layerGroup);
                    }
                }
                line.push(coordinates);
                previousTimestamp = dateTime.getTime();
                previousCoordinates = coordinates;
            }
            line = L.polyline(line, {
                style: {
                    weight: 3,
                    color: '#87f'
                }
            });
            line.addTo(layerGroup);
            bounds = line.getBounds();
            map.fitBounds(bounds);
            map.setMaxBounds(bounds.pad(.5));
            line.bringToBack();
        });
    }

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


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

'use strict'

// BUILD.JS: This file is responsible for building static HTML pages

const fs = require('fs')
const path = require('path')
const Metalsmith = require('metalsmith')
const collections = require('metalsmith-collections')
const feed = require('metalsmith-feed')
const discoverHelpers = require('metalsmith-discover-helpers')
const discoverPartials = require('metalsmith-discover-partials')
const layouts = require('metalsmith-layouts')
const markdown = require('metalsmith-markdown')
const prism = require('metalsmith-prism')
const permalinks = require('metalsmith-permalinks')
const pagination = require('metalsmith-yearly-pagination')
const defaultsDeep = require('lodash.defaultsdeep')
const autoprefixer = require('autoprefixer')
const marked = require('marked')
const postcss = require('postcss')
const fibers = require('fibers')
const sass = require('sass')
const ncp = require('ncp')
const junk = require('junk')
const semver = require('semver')

const githubLinks = require('./scripts/plugins/githubLinks')
const navigation = require('./scripts/plugins/navigation')
const anchorMarkdownHeadings = require('./scripts/plugins/anchor-markdown-headings')
const loadVersions = require('./scripts/load-versions')
const latestVersion = require('./scripts/helpers/latestversion')

// Set the default language, also functions as a fallback for properties which
// are not defined in the given language.
const DEFAULT_LANG = 'en'

// Set up the Markdown renderer that we'll use for our Metalsmith build process,
// with the necessary adjustments that we need to make in order to have Prism
// work.
const renderer = new marked.Renderer()
renderer.heading = anchorMarkdownHeadings
const markedOptions = {
  langPrefix: 'language-',
  renderer
}

// This function imports a given language file and uses the default language set
// in DEFAULT_LANG as a fallback to prevent any strings that aren't filled out
// from appearing as blank.
function i18nJSON (lang) {
  const defaultJSON = require(`./locale/${DEFAULT_LANG}/site.json`)
  const templateJSON = require(`./locale/${lang}/site.json`)

  return defaultsDeep({}, templateJSON, defaultJSON)
}

// This function imports language file for each given locale in array 'localesList'
// and based on it generating locales data, which includes full language name, english language name, locale and link
function generateLocalesData (localesList) {
  return localesList.map(localeEl => {
    const { language, languageEnglishVersion, locale, url } = require(`./locale/${localeEl}/site.json`)
    return { language, locale, url, languageEnglishVersion }
  })
}

// This is the function where the actual magic happens. This contains the main
// Metalsmith build cycle used for building a locale subsite, such as the
// english one.
function buildLocale (source, locale, opts) {
  console.log(`[metalsmith] build/${locale} started`)
  const labelForBuild = `[metalsmith] build/${locale} finished`
  console.time(labelForBuild)
  const metalsmith = Metalsmith(__dirname)

  metalsmith
  // Sets global metadata imported from the locale's respective site.json.
    .metadata({
      site: i18nJSON(locale),
      project: source.project,
      locales: opts.localesData
    })
  // Sets the build source as the locale folder.
    .source(path.join(__dirname, 'locale', locale))
    // site.json files aren't needed in the output dir
    .ignore('site.json')
    .use(withPreserveLocale(opts && opts.preserveLocale))
    // Extracts the main menu and sub-menu links form locale's site.json and
    // adds them to the metadata. This data is used in the navigation template
    .use(navigation(source.project.latestVersions))
    // Defines the blog post/guide collections used to internally group them for
    // easier future handling and feed generation.
    .use(collections({
      blog: {
        pattern: 'blog/**/*.md',
        sortBy: 'date',
        reverse: true,
        refer: false
      },
      blogReleases: {
        pattern: 'blog/release/*.md',
        sortBy: 'date',
        reverse: true,
        refer: false
      },
      blogVulnerability: {
        pattern: 'blog/vulnerability/*.md',
        sortBy: 'date',
        reverse: true,
        refer: false
      },
      knowledgeBase: {
        pattern: 'knowledge/**/*.md',
        refer: false
      },
      guides: {
        pattern: 'docs/guides/!(index).md'
      }
    }))
    .use(pagination({
      path: 'blog/year',
      iteratee: (post, idx) => ({
        post,
        displaySummary: idx < 10
      })
    }))
    .use(markdown(markedOptions))
    .use(githubLinks({ locale, site: i18nJSON(locale) }))
    .use(prism())
    // Set pretty permalinks, we don't want .html suffixes everywhere.
    .use(permalinks({
      relative: false
    }))
    // Generates the feed XML files from their respective collections which were
    // defined earlier on.
    .use(feed({
      collection: 'blog',
      destination: 'feed/blog.xml',
      title: 'Node.js Blog'
    }))
    .use(feed({
      collection: 'blogReleases',
      destination: 'feed/releases.xml',
      title: 'Node.js Blog: Releases'
    }))
    .use(feed({
      collection: 'blogVulnerability',
      destination: 'feed/vulnerability.xml',
      title: 'Node.js Blog: Vulnerability Reports'
    }))
    // Finally, this compiles the rest of the layouts present in ./layouts.
    // They're language-agnostic, but have to be regenerated for every locale
    // anyways.
    .use(discoverPartials({
      directory: 'layouts/partials',
      pattern: /\.hbs$/
    }))
    .use(discoverHelpers({
      directory: 'scripts/helpers',
      pattern: /\.js$/
    }))
    .use(layouts())
    // Pipes the generated files into their respective subdirectory in the build
    // directory.
    .destination(path.join(__dirname, 'build', locale))

  // This actually executes the build and stops the internal timer after
  // completion.
  metalsmith.build((err) => {
    if (err) { throw err }
    console.timeEnd(labelForBuild)
  })
}

// This plugin reads the files present in the english locale that are missing
// in the current locale being built (requires preserveLocale flag)
function withPreserveLocale (preserveLocale) {
  return (files, m, next) => {
    if (preserveLocale) {
      const path = m.path('locale/en')
      m.read(path, (err, newfiles) => {
        if (err) {
          console.error(err)
          return next(err)
        }

        Object.keys(newfiles).forEach((key) => {
          if (!files[key]) {
            files[key] = newfiles[key]
          }
        })
        next()
      })
    } else {
      next()
    }
  }
}

// This function builds the static/css folder for all the Sass files.
function buildCSS () {
  console.log('[sass] static/css started')
  const labelForBuild = '[sass] static/css finished'
  console.time(labelForBuild)

  const src = path.join(__dirname, 'layouts/css/styles.scss')
  const dest = path.join(__dirname, 'build/static/css/styles.css')

  const sassOpts = {
    file: src,
    fiber: fibers,
    outFile: dest,
    outputStyle: process.env.NODE_ENV !== 'development' ? 'compressed' : 'expanded'
  }

  fs.mkdir(path.join(__dirname, 'build/static/css'), { recursive: true }, (err) => {
    if (err) {
      throw err
    }

    sass.render(sassOpts, (error, result) => {
      if (error) {
        throw error
      }

      postcss([autoprefixer]).process(result.css, { from: src }).then(res => {
        res.warnings().forEach(warn => {
          console.warn(warn.toString())
        })

        fs.writeFile(dest, res.css, (err) => {
          if (err) {
            throw err
          }

          console.timeEnd(labelForBuild)
        })
      })
    })
  })
}

// This function copies the rest of the static assets to their subfolder in the
// build directory.
function copyStatic () {
  console.log('[ncp] build/static started')
  const labelForBuild = '[ncp] build/static finished'
  console.time(labelForBuild)
  fs.mkdir(path.join(__dirname, 'build/static'), { recursive: true }, (err) => {
    if (err) {
      throw err
    }

    ncp(path.join(__dirname, 'static'), path.join(__dirname, 'build/static'), (error) => {
      if (error) {
        return console.error(error)
      }
      console.timeEnd(labelForBuild)
    })
  })
}

function getSource (callback) {
  // Loads all node/io.js versions.
  loadVersions((err, versions) => {
    const source = {
      project: {
        versions,
        latestVersions: {
          current: latestVersion.current(versions),
          lts: latestVersion.lts(versions)
        },
        blacklivesmatter: {
          visible: true,
          text: '#BlackLivesMatter',
          link: '/en/black-lives-matter/'
        },
        banner: {
          visible: true,
          text: 'The OpenJS World CFP is open until Feb 15 - submit your talk ideas!',
          link: 'https://openjsworld.com/'
        }
      }
    }
    if (semver.gt(source.project.latestVersions.lts.node, source.project.latestVersions.current.node)) {
      // If LTS is higher than Current hide it from the main page
      source.project.latestVersions.hideCurrent = true
    }

    callback(err, source)
  })
}

// This is where the build is orchestrated from, as indicated by the function
// name. It brings together all build steps and dependencies and executes them.
function fullBuild (opts) {
  const { selectedLocales, preserveLocale } = opts
  getSource((err, source) => {
    if (err) { throw err }

    // Executes the build cycle for every locale.
    fs.readdir(path.join(__dirname, 'locale'), (e, locales) => {
      if (e) {
        throw e
      }
      const filteredLocales = locales.filter(file => junk.not(file) && (selectedLocales ? selectedLocales.includes(file) : true))
      const localesData = generateLocalesData(filteredLocales)
      filteredLocales.forEach((locale) => {
        buildLocale(source, locale, { preserveLocale, localesData })
      })
    })
  })
}

// Starts the build if the file was executed from the command line
if (require.main === module) {
  const preserveLocale = process.argv.includes('--preserveLocale')
  const selectedLocales = process.env.DEFAULT_LOCALE ? process.env.DEFAULT_LOCALE.toLowerCase().split(',') : process.env.DEFAULT_LOCALE
  // Copy static files
  copyStatic()
  // Build CSS
  buildCSS()
  fullBuild({ selectedLocales, preserveLocale })
}

exports.getSource = getSource
exports.fullBuild = fullBuild
exports.buildCSS = buildCSS
exports.buildLocale = buildLocale
exports.copyStatic = copyStatic
exports.generateLocalesData = generateLocalesData
<commit_msg>docs: Update OpenJs World CFP deadline to date listed on the OpenJS website (#3711)<commit_after>#! /usr/bin/env node

'use strict'

// BUILD.JS: This file is responsible for building static HTML pages

const fs = require('fs')
const path = require('path')
const Metalsmith = require('metalsmith')
const collections = require('metalsmith-collections')
const feed = require('metalsmith-feed')
const discoverHelpers = require('metalsmith-discover-helpers')
const discoverPartials = require('metalsmith-discover-partials')
const layouts = require('metalsmith-layouts')
const markdown = require('metalsmith-markdown')
const prism = require('metalsmith-prism')
const permalinks = require('metalsmith-permalinks')
const pagination = require('metalsmith-yearly-pagination')
const defaultsDeep = require('lodash.defaultsdeep')
const autoprefixer = require('autoprefixer')
const marked = require('marked')
const postcss = require('postcss')
const fibers = require('fibers')
const sass = require('sass')
const ncp = require('ncp')
const junk = require('junk')
const semver = require('semver')

const githubLinks = require('./scripts/plugins/githubLinks')
const navigation = require('./scripts/plugins/navigation')
const anchorMarkdownHeadings = require('./scripts/plugins/anchor-markdown-headings')
const loadVersions = require('./scripts/load-versions')
const latestVersion = require('./scripts/helpers/latestversion')

// Set the default language, also functions as a fallback for properties which
// are not defined in the given language.
const DEFAULT_LANG = 'en'

// Set up the Markdown renderer that we'll use for our Metalsmith build process,
// with the necessary adjustments that we need to make in order to have Prism
// work.
const renderer = new marked.Renderer()
renderer.heading = anchorMarkdownHeadings
const markedOptions = {
  langPrefix: 'language-',
  renderer
}

// This function imports a given language file and uses the default language set
// in DEFAULT_LANG as a fallback to prevent any strings that aren't filled out
// from appearing as blank.
function i18nJSON (lang) {
  const defaultJSON = require(`./locale/${DEFAULT_LANG}/site.json`)
  const templateJSON = require(`./locale/${lang}/site.json`)

  return defaultsDeep({}, templateJSON, defaultJSON)
}

// This function imports language file for each given locale in array 'localesList'
// and based on it generating locales data, which includes full language name, english language name, locale and link
function generateLocalesData (localesList) {
  return localesList.map(localeEl => {
    const { language, languageEnglishVersion, locale, url } = require(`./locale/${localeEl}/site.json`)
    return { language, locale, url, languageEnglishVersion }
  })
}

// This is the function where the actual magic happens. This contains the main
// Metalsmith build cycle used for building a locale subsite, such as the
// english one.
function buildLocale (source, locale, opts) {
  console.log(`[metalsmith] build/${locale} started`)
  const labelForBuild = `[metalsmith] build/${locale} finished`
  console.time(labelForBuild)
  const metalsmith = Metalsmith(__dirname)

  metalsmith
  // Sets global metadata imported from the locale's respective site.json.
    .metadata({
      site: i18nJSON(locale),
      project: source.project,
      locales: opts.localesData
    })
  // Sets the build source as the locale folder.
    .source(path.join(__dirname, 'locale', locale))
    // site.json files aren't needed in the output dir
    .ignore('site.json')
    .use(withPreserveLocale(opts && opts.preserveLocale))
    // Extracts the main menu and sub-menu links form locale's site.json and
    // adds them to the metadata. This data is used in the navigation template
    .use(navigation(source.project.latestVersions))
    // Defines the blog post/guide collections used to internally group them for
    // easier future handling and feed generation.
    .use(collections({
      blog: {
        pattern: 'blog/**/*.md',
        sortBy: 'date',
        reverse: true,
        refer: false
      },
      blogReleases: {
        pattern: 'blog/release/*.md',
        sortBy: 'date',
        reverse: true,
        refer: false
      },
      blogVulnerability: {
        pattern: 'blog/vulnerability/*.md',
        sortBy: 'date',
        reverse: true,
        refer: false
      },
      knowledgeBase: {
        pattern: 'knowledge/**/*.md',
        refer: false
      },
      guides: {
        pattern: 'docs/guides/!(index).md'
      }
    }))
    .use(pagination({
      path: 'blog/year',
      iteratee: (post, idx) => ({
        post,
        displaySummary: idx < 10
      })
    }))
    .use(markdown(markedOptions))
    .use(githubLinks({ locale, site: i18nJSON(locale) }))
    .use(prism())
    // Set pretty permalinks, we don't want .html suffixes everywhere.
    .use(permalinks({
      relative: false
    }))
    // Generates the feed XML files from their respective collections which were
    // defined earlier on.
    .use(feed({
      collection: 'blog',
      destination: 'feed/blog.xml',
      title: 'Node.js Blog'
    }))
    .use(feed({
      collection: 'blogReleases',
      destination: 'feed/releases.xml',
      title: 'Node.js Blog: Releases'
    }))
    .use(feed({
      collection: 'blogVulnerability',
      destination: 'feed/vulnerability.xml',
      title: 'Node.js Blog: Vulnerability Reports'
    }))
    // Finally, this compiles the rest of the layouts present in ./layouts.
    // They're language-agnostic, but have to be regenerated for every locale
    // anyways.
    .use(discoverPartials({
      directory: 'layouts/partials',
      pattern: /\.hbs$/
    }))
    .use(discoverHelpers({
      directory: 'scripts/helpers',
      pattern: /\.js$/
    }))
    .use(layouts())
    // Pipes the generated files into their respective subdirectory in the build
    // directory.
    .destination(path.join(__dirname, 'build', locale))

  // This actually executes the build and stops the internal timer after
  // completion.
  metalsmith.build((err) => {
    if (err) { throw err }
    console.timeEnd(labelForBuild)
  })
}

// This plugin reads the files present in the english locale that are missing
// in the current locale being built (requires preserveLocale flag)
function withPreserveLocale (preserveLocale) {
  return (files, m, next) => {
    if (preserveLocale) {
      const path = m.path('locale/en')
      m.read(path, (err, newfiles) => {
        if (err) {
          console.error(err)
          return next(err)
        }

        Object.keys(newfiles).forEach((key) => {
          if (!files[key]) {
            files[key] = newfiles[key]
          }
        })
        next()
      })
    } else {
      next()
    }
  }
}

// This function builds the static/css folder for all the Sass files.
function buildCSS () {
  console.log('[sass] static/css started')
  const labelForBuild = '[sass] static/css finished'
  console.time(labelForBuild)

  const src = path.join(__dirname, 'layouts/css/styles.scss')
  const dest = path.join(__dirname, 'build/static/css/styles.css')

  const sassOpts = {
    file: src,
    fiber: fibers,
    outFile: dest,
    outputStyle: process.env.NODE_ENV !== 'development' ? 'compressed' : 'expanded'
  }

  fs.mkdir(path.join(__dirname, 'build/static/css'), { recursive: true }, (err) => {
    if (err) {
      throw err
    }

    sass.render(sassOpts, (error, result) => {
      if (error) {
        throw error
      }

      postcss([autoprefixer]).process(result.css, { from: src }).then(res => {
        res.warnings().forEach(warn => {
          console.warn(warn.toString())
        })

        fs.writeFile(dest, res.css, (err) => {
          if (err) {
            throw err
          }

          console.timeEnd(labelForBuild)
        })
      })
    })
  })
}

// This function copies the rest of the static assets to their subfolder in the
// build directory.
function copyStatic () {
  console.log('[ncp] build/static started')
  const labelForBuild = '[ncp] build/static finished'
  console.time(labelForBuild)
  fs.mkdir(path.join(__dirname, 'build/static'), { recursive: true }, (err) => {
    if (err) {
      throw err
    }

    ncp(path.join(__dirname, 'static'), path.join(__dirname, 'build/static'), (error) => {
      if (error) {
        return console.error(error)
      }
      console.timeEnd(labelForBuild)
    })
  })
}

function getSource (callback) {
  // Loads all node/io.js versions.
  loadVersions((err, versions) => {
    const source = {
      project: {
        versions,
        latestVersions: {
          current: latestVersion.current(versions),
          lts: latestVersion.lts(versions)
        },
        blacklivesmatter: {
          visible: true,
          text: '#BlackLivesMatter',
          link: '/en/black-lives-matter/'
        },
        banner: {
          visible: true,
          text: 'The OpenJS World CFP is open until Feb 22 - submit your talk ideas!',
          link: 'https://openjsworld.com/'
        }
      }
    }
    if (semver.gt(source.project.latestVersions.lts.node, source.project.latestVersions.current.node)) {
      // If LTS is higher than Current hide it from the main page
      source.project.latestVersions.hideCurrent = true
    }

    callback(err, source)
  })
}

// This is where the build is orchestrated from, as indicated by the function
// name. It brings together all build steps and dependencies and executes them.
function fullBuild (opts) {
  const { selectedLocales, preserveLocale } = opts
  getSource((err, source) => {
    if (err) { throw err }

    // Executes the build cycle for every locale.
    fs.readdir(path.join(__dirname, 'locale'), (e, locales) => {
      if (e) {
        throw e
      }
      const filteredLocales = locales.filter(file => junk.not(file) && (selectedLocales ? selectedLocales.includes(file) : true))
      const localesData = generateLocalesData(filteredLocales)
      filteredLocales.forEach((locale) => {
        buildLocale(source, locale, { preserveLocale, localesData })
      })
    })
  })
}

// Starts the build if the file was executed from the command line
if (require.main === module) {
  const preserveLocale = process.argv.includes('--preserveLocale')
  const selectedLocales = process.env.DEFAULT_LOCALE ? process.env.DEFAULT_LOCALE.toLowerCase().split(',') : process.env.DEFAULT_LOCALE
  // Copy static files
  copyStatic()
  // Build CSS
  buildCSS()
  fullBuild({ selectedLocales, preserveLocale })
}

exports.getSource = getSource
exports.fullBuild = fullBuild
exports.buildCSS = buildCSS
exports.buildLocale = buildLocale
exports.copyStatic = copyStatic
exports.generateLocalesData = generateLocalesData
<|endoftext|>


<commit_before>Taskar.Sections.NewForm = function(sectionsBar){
  sectionsBar.on('click',     '.add_section',       show);
  sectionsBar.on('click',     'input[type=button]', hide);
  sectionsBar.on('key:esc',   'form',               hide);
  sectionsBar.on('submit',    'form',               Taskar.Sections.validateForm);

  var form   = $('new_section');
      appear = new S2.FX.Style(form, {
        before: function(e){ form.setStyle({ width: '0px', opacity: 0.0 }); },
        after:  function(e){ form.down('input[type=text]').focus(); }
      });

  function show(e, element){
    e.stop();

    form.down('form').reset();
    form.down('input[name*=insert_before]').setValue(element.getAttribute('data-before'));

    if (element.previous() == form && form.visible()){
      return;
    }

    form.hide();

    element.hide()
    element.show.bind(element).defer();
    element.insert({before: form});

    form.show();

    appear.play(null, {style: 'opacity:1; width:' + form.getWidth() + 'px'})
  }

  function hide(){
    form.hide();
  }
};<commit_msg>if in project there isn't any section, open new section form and focus on the field<commit_after>Taskar.Sections.NewForm = function(sectionsBar){
  sectionsBar.on('click',     '.add_section',       show);
  sectionsBar.on('click',     'input[type=button]', hide);
  sectionsBar.on('key:esc',   'form',               hide);
  sectionsBar.on('submit',    'form',               Taskar.Sections.validateForm);

  var form   = $('new_section');
      appear = new S2.FX.Style(form, {
        before: function(e){ form.setStyle({ width: '0px', opacity: 0.0 }); },
        after:  function(e){ form.down('input[type=text]').focus(); }
      });

  function show(e, element){
    e.stop();

    form.down('form').reset();
    form.down('input[name*=insert_before]').setValue(element.getAttribute('data-before'));

    if (element.previous() == form && form.visible()){
      return;
    }

    form.hide();

    element.hide()
    element.show.bind(element).defer();
    element.insert({before: form});

    form.show();

    appear.play(null, {style: 'opacity:1; width:' + form.getWidth() + 'px'})
  }

  function hide(){
    form.hide();
  }

  if (sectionsBar.select('.section').length == 0){
    form.show().down('input[type=text]').highlight().focus();
  }
};<|endoftext|>