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

'use strict';

// Include Gulp & Tools We'll Use
var gulp = require('gulp');
var $ = require('gulp-load-plugins')();
var del = require('del');
var runSequence = require('run-sequence');
var browserSync = require('browser-sync');
var pagespeed = require('psi');
var reload = browserSync.reload;
var exec = require('child_process').exec;

var AUTOPREFIXER_BROWSERS = [
  'ie >= 10',
  'ie_mob >= 10',
  'ff >= 30',
  'chrome >= 34',
  'safari >= 7',
  'opera >= 23',
  'ios >= 7',
  'android >= 4.4',
  'bb >= 10'
];

// Lint JavaScript
gulp.task('jshint', function () {
  return gulp.src('app/scripts/**/*.js')
    .pipe(reload({stream: true, once: true}))
    .pipe($.jshint())
    .pipe($.jshint.reporter('jshint-stylish'))
    .pipe($.if(!browserSync.active, $.jshint.reporter('fail')));
});

// Optimize Images
gulp.task('images', function () {
  return gulp.src('app/images/**/*')
    .pipe($.cache($.imagemin({
      progressive: true,
      interlaced: true
    })))
    .pipe(gulp.dest('dist/images'))
    .pipe($.size({title: 'images'}));
});

// Copy all files (not dirs) at the root level (app)
gulp.task('copy', function () {
  return gulp.src([
    'app/*',
    '!app/_includes/*'
  ], {
    dot: true
  }).pipe(gulp.dest('dist'))
    .pipe($.size({title: 'copy'}));
});

// Copy "fonts" folder
gulp.task('fonts', function () {
  return gulp.src(['app/fonts/**'])
    .pipe(gulp.dest('dist/fonts'))
    .pipe($.size({title: 'fonts'}));
});

// Copy "_layouts" folder
gulp.task('layouts', function () {
  return gulp.src(['app/_layouts/*'])
    .pipe(gulp.dest('dist/_layouts'))
    .pipe($.size({title: 'layouts'}));
});

// Copy "about" folder
gulp.task('about', function () {
  return gulp.src(['app/about/*'])
    .pipe(gulp.dest('dist/about'))
    .pipe($.size({title: 'about'}));
});

// Copy "blog" folder
gulp.task('blog', function () {
  return gulp.src(['app/blog/*'])
    .pipe(gulp.dest('dist/blog'))
    .pipe($.size({title: 'blog'}));
});

// Compile and Automatically Prefix Stylesheets
gulp.task('styles', function () {
  // For best performance, don't add Sass partials to `gulp.src`
  return gulp.src([
    'app/styles/*.scss',
    'app/styles/**/*.css',
    'app/styles/components/components.scss'
  ])
    .pipe($.sourcemaps.init())
    .pipe($.changed('styles', {extension: '.scss'}))
    .pipe($.sass({
      precision: 10,
      onError: console.error.bind(console, 'Sass error:')
    }))
    .pipe($.autoprefixer({browsers: AUTOPREFIXER_BROWSERS}))
    .pipe($.sourcemaps.write())
    .pipe(gulp.dest('.tmp/styles'))
    // Concatenate And Minify Styles
    .pipe($.if('*.css', $.csso()))
    .pipe(gulp.dest('dist/styles'))
    .pipe($.size({title: 'styles'}));
});

// Scan Your HTML For Assets & Optimize Them
gulp.task('html', function () {
  var assets = $.useref.assets({searchPath: '{.tmp,app}'});

  return gulp.src('app/_includes/*.html')
    .pipe(assets)
    // Concatenate And Minify JavaScript
    .pipe($.if('*.js', $.uglify({preserveComments: 'some'})))
    // Remove Any Unused CSS
    // Note: If not using the Style Guide, you can delete it from
    // the next line to only include styles your project uses.
    // .pipe($.if('*.css', $.uncss({
    //   html: [
    //     'app/index.html'
    //     // 'app/styleguide.html'
    //   ],
    //   // CSS Selectors for UnCSS to ignore
    //   // ignore: [
    //   //   /.side-nav.open/,
    //   //   /.header-bar.open/
    //   // ]
    // })))
    // Concatenate And Minify Styles
    // In case you are still using useref build blocks
    .pipe($.if('*.css', $.csso()))
    .pipe(assets.restore())
    .pipe($.useref())
    // Update Production Style Guide Paths
    .pipe($.replace('components/components.css', 'components/main.min.css'))
    // Minify Any HTML
    // .pipe($.if('*.html', $.minifyHtml()))
    // Output Files
    .pipe($.if('*.html', gulp.dest('dist/_includes'), gulp.dest('dist')))
    .pipe($.size({title: 'html'}));
});

// Static assets revisioning: unicorn.css → unicorn-098f6bcd.css
gulp.task('rev', function () {
  return gulp.src(['dist/styles/*.css', 'dist/scripts/*.js'], {base: 'dist'})
    .pipe(gulp.dest('dist'))
    .pipe($.rev())
    .pipe(gulp.dest('dist'))
    .pipe($.rev.manifest('dist/rev-manifest.json', {base: 'dist'}))
    .pipe(gulp.dest('dist'));
});

// Rewrite occurences of filenames which have been renamed by 'rev' task
gulp.task('rev-replace', function () {
  var manifest = gulp.src('dist/rev-manifest.json');
  return gulp.src('dist/_includes/*.html')
    .pipe($.revReplace({manifest: manifest}))
    .pipe(gulp.dest('dist/_includes'));
});

// Clean Output Directory
gulp.task('clean', del.bind(null, ['.tmp', 'dist/*', '!dist/.git']));

// Build Jekyll site (development)
gulp.task('jekyll', function (cb) {
  exec('jekyll build --source app --destination dist/_site', function (err) {
    if (err) cb(err);
    cb();
  });
});

// Build Jekyll site (assets compressed)
gulp.task('jekyll:dist', function (cb) {
  exec('jekyll build --source dist --destination dist/_site', function (err) {
    if (err) cb(err);
    cb();
  });
});

// Serve the Jekyll site and watch files for changes & reload
gulp.task('serve', ['styles', 'jekyll'], function () {
  browserSync({
    notify: false,
    // Run as an https by uncommenting 'https: true'
    // Note: this uses an unsigned certificate which on first access
    //       will present a certificate warning in the browser.
    // https: true,
    server: ['.tmp', 'dist/_site']
  });

  gulp.watch(['app/*.{yml,xml}'], ['jekyll', reload]);
  gulp.watch(['app/**/*.{html,md}'], ['jekyll', reload]);
  gulp.watch(['app/styles/**/*.{scss,css}'], ['styles', 'jekyll', reload]);
  gulp.watch(['app/scripts/**/*.js'], ['jshint', 'jekyll', reload]);
  gulp.watch(['app/images/**/*'], ['jekyll', reload]);
});

// Serve the Jekyll site from the dist build
gulp.task('serve:dist', function () {
  runSequence('default', 'jekyll:dist', function () {
    browserSync({
      notify: false,
      // Run as an https by uncommenting 'https: true'
      // Note: this uses an unsigned certificate which on first access
      //       will present a certificate warning in the browser.
      // https: true,
      server: 'dist/_site'
    });
  });
});

// Build site ready to be pushed to GitHub (assets compressed)
gulp.task('default', ['clean'], function (cb) {
  runSequence('styles',
              [ 'jshint',
                'html',
                'images',
                'fonts',
                'layouts',
                'about',
                'blog',
                'copy'
              ],
              'rev',
              'rev-replace',
              cb);
});

// Run PageSpeed Insights
// Update `url` below to the public URL for your site
gulp.task('pagespeed', pagespeed.bind(null, {
  // By default, we use the PageSpeed Insights
  // free (no API key) tier. You can use a Google
  // Developer API key if you have one. See
  // http://goo.gl/RkN0vE for info key: 'YOUR_API_KEY'
  url: 'https://www.sergiorus.com',
  strategy: 'mobile'
}));

// Load custom tasks from the `tasks` directory
try { require('require-dir')('tasks'); } catch (err) {}
<commit_msg>Update 'clean' task<commit_after>/**
 *
 *  Web Starter Kit
 *  Copyright 2014 Google Inc. All rights reserved.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License
 *
 */

'use strict';

// Include Gulp & Tools We'll Use
var gulp = require('gulp');
var $ = require('gulp-load-plugins')();
var del = require('del');
var runSequence = require('run-sequence');
var browserSync = require('browser-sync');
var pagespeed = require('psi');
var reload = browserSync.reload;
var exec = require('child_process').exec;

var AUTOPREFIXER_BROWSERS = [
  'ie >= 10',
  'ie_mob >= 10',
  'ff >= 30',
  'chrome >= 34',
  'safari >= 7',
  'opera >= 23',
  'ios >= 7',
  'android >= 4.4',
  'bb >= 10'
];

// Lint JavaScript
gulp.task('jshint', function () {
  return gulp.src('app/scripts/**/*.js')
    .pipe(reload({stream: true, once: true}))
    .pipe($.jshint())
    .pipe($.jshint.reporter('jshint-stylish'))
    .pipe($.if(!browserSync.active, $.jshint.reporter('fail')));
});

// Optimize Images
gulp.task('images', function () {
  return gulp.src('app/images/**/*')
    .pipe($.cache($.imagemin({
      progressive: true,
      interlaced: true
    })))
    .pipe(gulp.dest('dist/images'))
    .pipe($.size({title: 'images'}));
});

// Copy all files (not dirs) at the root level (app)
gulp.task('copy', function () {
  return gulp.src([
    'app/*',
    '!app/_includes/*'
  ], {
    dot: true
  }).pipe(gulp.dest('dist'))
    .pipe($.size({title: 'copy'}));
});

// Copy "fonts" folder
gulp.task('fonts', function () {
  return gulp.src(['app/fonts/**'])
    .pipe(gulp.dest('dist/fonts'))
    .pipe($.size({title: 'fonts'}));
});

// Copy "_layouts" folder
gulp.task('layouts', function () {
  return gulp.src(['app/_layouts/*'])
    .pipe(gulp.dest('dist/_layouts'))
    .pipe($.size({title: 'layouts'}));
});

// Copy "about" folder
gulp.task('about', function () {
  return gulp.src(['app/about/*'])
    .pipe(gulp.dest('dist/about'))
    .pipe($.size({title: 'about'}));
});

// Copy "blog" folder
gulp.task('blog', function () {
  return gulp.src(['app/blog/*'])
    .pipe(gulp.dest('dist/blog'))
    .pipe($.size({title: 'blog'}));
});

// Compile and Automatically Prefix Stylesheets
gulp.task('styles', function () {
  // For best performance, don't add Sass partials to `gulp.src`
  return gulp.src([
    'app/styles/*.scss',
    'app/styles/**/*.css',
    'app/styles/components/components.scss'
  ])
    .pipe($.sourcemaps.init())
    .pipe($.changed('styles', {extension: '.scss'}))
    .pipe($.sass({
      precision: 10,
      onError: console.error.bind(console, 'Sass error:')
    }))
    .pipe($.autoprefixer({browsers: AUTOPREFIXER_BROWSERS}))
    .pipe($.sourcemaps.write())
    .pipe(gulp.dest('.tmp/styles'))
    // Concatenate And Minify Styles
    .pipe($.if('*.css', $.csso()))
    .pipe(gulp.dest('dist/styles'))
    .pipe($.size({title: 'styles'}));
});

// Scan Your HTML For Assets & Optimize Them
gulp.task('html', function () {
  var assets = $.useref.assets({searchPath: '{.tmp,app}'});

  return gulp.src('app/_includes/*.html')
    .pipe(assets)
    // Concatenate And Minify JavaScript
    .pipe($.if('*.js', $.uglify({preserveComments: 'some'})))
    // Remove Any Unused CSS
    // Note: If not using the Style Guide, you can delete it from
    // the next line to only include styles your project uses.
    // .pipe($.if('*.css', $.uncss({
    //   html: [
    //     'app/index.html'
    //     // 'app/styleguide.html'
    //   ],
    //   // CSS Selectors for UnCSS to ignore
    //   // ignore: [
    //   //   /.side-nav.open/,
    //   //   /.header-bar.open/
    //   // ]
    // })))
    // Concatenate And Minify Styles
    // In case you are still using useref build blocks
    .pipe($.if('*.css', $.csso()))
    .pipe(assets.restore())
    .pipe($.useref())
    // Update Production Style Guide Paths
    .pipe($.replace('components/components.css', 'components/main.min.css'))
    // Minify Any HTML
    // .pipe($.if('*.html', $.minifyHtml()))
    // Output Files
    .pipe($.if('*.html', gulp.dest('dist/_includes'), gulp.dest('dist')))
    .pipe($.size({title: 'html'}));
});

// Static assets revisioning: unicorn.css → unicorn-098f6bcd.css
gulp.task('rev', function () {
  return gulp.src(['dist/styles/*.css', 'dist/scripts/*.js'], {base: 'dist'})
    .pipe(gulp.dest('dist'))
    .pipe($.rev())
    .pipe(gulp.dest('dist'))
    .pipe($.rev.manifest('dist/rev-manifest.json', {base: 'dist'}))
    .pipe(gulp.dest('dist'));
});

// Rewrite occurences of filenames which have been renamed by 'rev' task
gulp.task('rev-replace', function () {
  var manifest = gulp.src('dist/rev-manifest.json');
  return gulp.src('dist/_includes/*.html')
    .pipe($.revReplace({manifest: manifest}))
    .pipe(gulp.dest('dist/_includes'));
});

// Clean Output Directory
gulp.task('clean', del.bind(null, ['.tmp', 'dist/*', '!dist/.git', '!dist/_posts', '!dist/.gitignore']));

// Build Jekyll site (development)
gulp.task('jekyll', function (cb) {
  exec('jekyll build --source app --destination dist/_site', function (err) {
    if (err) cb(err);
    cb();
  });
});

// Build Jekyll site (assets compressed)
gulp.task('jekyll:dist', function (cb) {
  exec('jekyll build --source dist --destination dist/_site', function (err) {
    if (err) cb(err);
    cb();
  });
});

// Serve the Jekyll site and watch files for changes & reload
gulp.task('serve', ['styles', 'jekyll'], function () {
  browserSync({
    notify: false,
    // Run as an https by uncommenting 'https: true'
    // Note: this uses an unsigned certificate which on first access
    //       will present a certificate warning in the browser.
    // https: true,
    server: ['.tmp', 'dist/_site']
  });

  gulp.watch(['app/*.{yml,xml}'], ['jekyll', reload]);
  gulp.watch(['app/**/*.{html,md}'], ['jekyll', reload]);
  gulp.watch(['app/styles/**/*.{scss,css}'], ['styles', 'jekyll', reload]);
  gulp.watch(['app/scripts/**/*.js'], ['jshint', 'jekyll', reload]);
  gulp.watch(['app/images/**/*'], ['jekyll', reload]);
});

// Serve the Jekyll site from the dist build
gulp.task('serve:dist', function () {
  runSequence('default', 'jekyll:dist', function () {
    browserSync({
      notify: false,
      // Run as an https by uncommenting 'https: true'
      // Note: this uses an unsigned certificate which on first access
      //       will present a certificate warning in the browser.
      // https: true,
      server: 'dist/_site'
    });
  });
});

// Build site ready to be pushed to GitHub (assets compressed)
gulp.task('default', ['clean'], function (cb) {
  runSequence('styles',
              [ 'jshint',
                'html',
                'images',
                'fonts',
                'layouts',
                'about',
                'blog',
                'copy'
              ],
              'rev',
              'rev-replace',
              cb);
});

// Run PageSpeed Insights
// Update `url` below to the public URL for your site
gulp.task('pagespeed', pagespeed.bind(null, {
  // By default, we use the PageSpeed Insights
  // free (no API key) tier. You can use a Google
  // Developer API key if you have one. See
  // http://goo.gl/RkN0vE for info key: 'YOUR_API_KEY'
  url: 'https://www.sergiorus.com',
  strategy: 'mobile'
}));

// Load custom tasks from the `tasks` directory
try { require('require-dir')('tasks'); } catch (err) {}
<|endoftext|>


<commit_before>$(document).ready(function(){

  var quote;
  var author;
// Define getQuote function - data included is based on forismatic instructions
  function getQuote() {
    $.ajax({
      url: "http://api.forismatic.com/api/1.0/",
      jsonp: "jsonp",
      dataType: "jsonp",
      data: {
        method: "getQuote",
        lang: "en",
        format: "jsonp"
      },
      success: function (response) {
        quote = response.quoteText;
        author = response.quoteAuthor;

        //Will print quote - refers to quote id in html
        $("#quote").text(quote);

        // If an author is associated to quote this will print author's name, if not (else) this will print text - unknown
        if (author){
          $("#author").text("- " + author);
        }
        else {
          $("#author").text("- unknown.")
        }
      }

    })
  }
//Calls function, but will just refresh page
getQuote();

//Calls function to perform when button is clicked, rather than just refreshing the page
$(".newquote").on("click", function(evt){
  evt.preventDefault(); // preventDefault stops page from jumping back to the top, each time button is clicked
  getQuote();
  });

//Opens new twitter window  when tweet button is clicked to share quote
//  $(".tweet").on("click", function(evt){
//    evt.preventDefault(); // preventDefault stops page from jumping back to the top, each time button is clicked
//    window.open("https://twitter.com/share");
//  })

//Opens new twitter window when tweet button is clicked and pre-populates quote text!!!!
$(".tweet").on("click", function(evt){
  evt.preventDefault(); // preventDefault stops page from jumping back to the top, each time button is clicked
  window.open("https://twitter.com/intent/tweet?text=" + encodeURIComponent(quote + " - " + author));
})

});
<commit_msg>Delete script.js<commit_after><|endoftext|>


<commit_before>$(function(){
  var savedData = localStorage.getItem("fusen");
  var fusenList = JSON.parse(savedData);

  if(fusenList && (fusenList.constructor != 'Array')){
    jQuery.each(fusenList,function(){
      addFusen(this);
    });
  }
  $('#add').on('click',function(){
    addFusen();
  });

  $( "#workspace" ).sortable({
    revert: true
  });
});

function addFusen(data){
  data = data || '';
  $('#workspace').append(
    $('<li>')
      .attr('contenteditable','true')
      .text(data)
      .on('click',function(){
        $(this).focus();
      })
      .on('blur',function(){
        var fusenList = [];
        $('#workspace li').each(function(){
          fusenList.push($(this).text());
        });
        localStorage.setItem('fusen',JSON.stringify(fusenList));
        console.log('saved!');
      })
  );
}
  <commit_msg>ignore empty fusen for save.<commit_after>$(function(){
  var savedData = localStorage.getItem("fusen");
  var fusenList = JSON.parse(savedData);

  if(fusenList && (fusenList.constructor != 'Array')){
    jQuery.each(fusenList,function(){
      addFusen(this);
    });
  }
  $('#add').on('click',function(){
    addFusen();
  });

  $( "#workspace" ).sortable({
    revert: true
  });
});

function addFusen(data){
  data = data || '';
  $('#workspace').append(
    $('<li>')
      .attr('contenteditable','true')
      .text(data)
      .on('click',function(){
        $(this).focus();
      })
      .on('blur',function(){
        var fusenList = [];
        $('#workspace li').each(function(){
          if($(this).text().trim() == '') return;
          fusenList.push($(this).text());
        });
        localStorage.setItem('fusen',JSON.stringify(fusenList));
        console.log('saved!');
      })
  );
}
  <|endoftext|>


<commit_before>function SharikiGameType(players, config) {
    /**
     * Whether there is an active (selected) shell.
     * @type {boolean}
     */
    this._isActive = false;
    /**
     * The row of the active shell.
     * @type {number}
     */
    this._activeRow = null;
    /**
     * The column of the active shell.
     * @type {number}
     */
    this._activeCol = null;

    /**
     * Whether there is a valid swap at this moment in time.
     * @type {boolean}
     */
    this._validSwap = false;

    /**
     * Constructs a Board object using the constraints imposed by Shariki
     * and the configuration, and stores it in this._board.
     */
    this._makeBoard = function() {
        this._board = new Board(this._config.width, this._config.height);
        this._fillBoard(this._board);
    }

    /**
     * Executes a single turn of the game. While waiting for the user to make
     * a move, a background task is run to check if the board is in the
     * game-over state (see this._checkBoard()). Returns whether or not the
     * game is in fact over at the beginning of the turn.
     *
     * @param {Player} currentPlayer The current player (unused)
     * @param {number} turnCount The current turn number (unused)
     */
    this._turn = function(currentPlayer, turnCount) {
        var gameOver = this._checkBoard();
        if(gameOver)
            return true;
        while(!this._validSwap) {
            // wait T milliseconds
        }
        this._validSwap = false;
        return false;
    }

    /**
     * This method handles the functionality of selecting a single shell.

     * @param {number} row The row of the selected shell
     * @param {number} col The column of the selected shell
     */
    this.selectShell = function(row, col) {
        // no shell is active, make selected shell active
        if(!this._isActive) {
            this._isActive = true;
            this._activeRow = row;
            this._activeCol = col;
            // notify renderer here
        }
        // selected shell is adjacent to active shell, try to swap
        else if(Board.isAdjacent(this._activeRow, this._activeCol, row, col)) {
            this._trySwap(this._activeRow, this._activeCol, row, col);
        }
        // unselect shell
        else {
            this._isActive = false;
            this._activeRow = null;
            this._activeCol = null;
            // notify renderer here
        }
    }

    /**
     * Attempts to make a swap.
     */
    this._trySwap = function(activeRow, activeCol, selectedRow, selectedCol) {
        this._swap(activeRow, activeCol, selectedRow, selectedCol);
        // notify renderer (in parallel)

        var validSwap = this._makeConnection(activeRow,   activeCol,
                                             selectedRow, selectedCol);

        if(!validSwap) {
            this._swap(activeRow, activeCol, selectedRow, selectedCol);
            // notify renderer
        }
    }

    /**
     * Checks if the 2 swapped shells make a connection
     * If they do, clear the connected shells, give points, and make new
     * shells fall in.
     *
     * @param  {number} activeRow Row of active shell.
     * @param  {number} activeCol Column of active shell.
     * @param  {number} selectedRow Row of selected shell.
     * @param  {number} selectedCol Column of selected shell.
     * @return {boolean} Whether or not the swap was valid.
     */
    this._makeConnection = function(activeRow, activeCol,
                                    selectedRow, selectedCol) {
        var activeShell = this._board.get(activeRow, activeCol);
        var selectedShell = this._board.get(selectedRow, selectedCol);

        // ...
    }

    /**
     * Swaps two shells.
     *
     * @param  {number} activeRow Row of active shell.
     * @param  {number} activeCol Column of active shell.
     * @param  {number} selectedRow Row of selected shell.
     * @param  {number} selectedCol Column of selected shell.
     */
    this._swap = function(activeRow, activeCol, selectedRow, selectedCol) {
        var activeShell = this._board.get(activeRow, activeCol);
        var selectedShell = this._board.get(selectedRow, selectedCol);

        this._board.set(selectedRow, selectedCol, activeShell);
        this._board.set(activeRow, activeCol, selectedShell);
    }

    /**
     * Checks to see if the board is in the game-over position. This means that
     * there is no possible way to connect 3 or more with a single swap. If at
     * any point during execution, the user makes a swap, obviously it was
     * possible, and false is returned.
     *
     * @return {boolean} Whether or not the board is in game-over position.
     */
    this._checkBoard = function() {
        /* ... */
    }
}
// make this extend GameType
<commit_msg>Updated GameType<commit_after>function SharikiGameType(players, config) {
    /**
     * Whether there is an active (selected) shell.
     * @type {boolean}
     */
    this._isActive = false;
    /**
     * The row of the active shell.
     * @type {number}
     */
    this._activeRow = null;
    /**
     * The column of the active shell.
     * @type {number}
     */
    this._activeCol = null;

    /**
     * Whether there is a valid swap at this moment in time.
     * @type {boolean}
     */
    this._validSwap = false;

    /**
     * Constructs a Board object using the constraints imposed by Shariki
     * and the configuration, and stores it in this._board.
     */
    this._makeBoard = function() {
        this._board = new Board(this._config.width, this._config.height);
        this._fillBoard(this._board);
    }

    /**
     * Executes a single turn of the game. While waiting for the user to make
     * a move, a background task is run to check if the board is in the
     * game-over state (see this._checkBoard()). Returns whether or not the
     * game is in fact over at the beginning of the turn.
     *
     * @param {Player} currentPlayer The current player (unused)
     * @param {number} turnCount The current turn number (unused)
     */
    this._turn = function(currentPlayer, turnCount) {
        var gameOver = this._checkBoard();
        if(gameOver)
            return true;
        while(!this._validSwap) {
            // wait T milliseconds
        }
        this._validSwap = false;
        return false;
    }

    /**
     * This method handles the functionality of selecting a single shell.

     * @param {number} row The row of the selected shell
     * @param {number} col The column of the selected shell
     */
    this.selectShell = function(player,row, col) {
        // no shell is active, make selected shell active
        if(!this._isActive) {
            this._isActive = true;
            this._activeRow = row;
            this._activeCol = col;
            // notify renderer here
        }
        // selected shell is adjacent to active shell, try to swap
        else if(Util.isAdjacent(this._activeRow, this._activeCol, row, col)) {
            this._trySwap(this._activeRow, this._activeCol, row, col);
        }
        // unselect shell
        else {
            this._isActive = false;
            this._activeRow = null;
            this._activeCol = null;
            // notify renderer here
        }
    }

    /**
     * Attempts to make a swap.
     */
    this._trySwap = function(player,activeRow, activeCol, selectedRow, selectedCol) {
        this._swap(player.board, activeRow, activeCol, selectedRow, selectedCol);
        // notify renderer (in parallel)

        var validSwap = this._makeConnection(player, activeRow,   activeCol,
                                             selectedRow, selectedCol);

        if(!validSwap) {
            this._swap(player.board, activeRow, activeCol, selectedRow, selectedCol);
            // notify renderer
        }
    }

    /**
     * Checks if the 2 swapped shells make a connection
     * If they do, clear the connected shells, give points, and make new
     * shells fall in.
     *
     * @param  {number} activeRow Row of active shell.
     * @param  {number} activeCol Column of active shell.
     * @param  {number} selectedRow Row of selected shell.
     * @param  {number} selectedCol Column of selected shell.
     * @return {boolean} Whether or not the swap was valid.
     */
    this._makeConnection = function(player,activeRow, activeCol,
                                    selectedRow, selectedCol) {
        var activeShell = player.board.get(activeRow, activeCol);
        var selectedShell = player.board.get(selectedRow, selectedCol);

        // ...
    }

    /**
     * Swaps two shells.
     *
     * @param  {number} activeRow Row of active shell.
     * @param  {number} activeCol Column of active shell.
     * @param  {number} selectedRow Row of selected shell.
     * @param  {number} selectedCol Column of selected shell.
     */
    this._swap = function(board,activeRow, activeCol, selectedRow, selectedCol) {
        var activeShell = board.get(activeRow, activeCol);
        var selectedShell = board.get(selectedRow, selectedCol);

        board.set(selectedRow, selectedCol, activeShell);
        board.set(activeRow, activeCol, selectedShell);
    }

    /**
     * Checks to see if the board is in the game-over position. This means that
     * there is no possible way to connect 3 or more with a single swap. If at
     * any point during execution, the user makes a swap, obviously it was
     * possible, and false is returned.
     *
     * @return {boolean} Whether or not the board is in game-over position.
     */
    this._checkBoard = function() {
        /* ... */
    }
}
// make this extend GameType
<|endoftext|>


<commit_before>let canvas = document.getElementsByTagName('canvas')[0],
    ctx = canvas.getContext('2d');

let dataPoints = [],
    centroids = [],
    dataPointsAssignedCentroids = {}; // { dataPointIndex: centroidIndex }

let colors = [
    '#ED0A3F',
    '#0095B7',
    '#33CC99',
    '#00468C',
    '#0066FF',
    '#EE34D2',
    '#C88A65',
    '#A50B5E',
    '#733380',
    '#87421F'
]

let drawDataPoint = function([x, y], index) {
    ctx.save();
        ctx.fillStyle = colors[dataPointsAssignedCentroids[index]];
        ctx.beginPath();
        ctx.arc(x, y, 5, 0, 2 * Math.PI);
        ctx.fill();
    ctx.restore();
}

let drawCentroid = function([x, y], index) {
    ctx.save()
        ctx.strokeStyle = ctx.fillStyle = colors[index];
        ctx.beginPath();
        ctx.save()
            ctx.lineWidth = 2;
            ctx.arc(x, y, 8, 0, 2 * Math.PI);
            ctx.stroke();
        ctx.restore();
        ctx.beginPath();
        ctx.arc(x, y, 4, 0, 2 * Math.PI);
        ctx.fill();
    ctx.restore();
}

let redrawAll = function() {
    canvas.width = canvas.width;
    dataPoints.map(drawDataPoint);
    centroids.map(drawCentroid);
}

let addingDataPointsManually = false,
    addingCentroidsManually = false;

let buttonAddDataPointsManually = document.getElementById('add-data-points-manually'),
    buttonAddCentroidsManually = document.getElementById('add-centroids-manually');

let toggleButtonText = function(button) {
    let currentText = button.innerHTML;
    button.innerHTML = button.getAttribute('data-toggle');
    button.setAttribute('data-toggle', currentText);
}

let updateCanvasStyles = function() {
    if (addingDataPointsManually || addingCentroidsManually) {
        canvas.classList.add('canvas-picking-active');
    } else {
        canvas.classList.remove('canvas-picking-active');
    }
}

let toggleAddingDataPointsManually = function() {
    if (addingCentroidsManually) {
        toggleAddingCentroidsManually();
    }
    addingDataPointsManually = !addingDataPointsManually;
    toggleButtonText(buttonAddDataPointsManually);
    updateCanvasStyles();
}

let toggleAddingCentroidsManually = function() {
    if (!addingCentroidsManually && isCentroidLimitReached()) {
        showCentroidLimitReachedMessage();
        return;
    }
    if (addingDataPointsManually) {
        toggleAddingDataPointsManually();
    }
    addingCentroidsManually = !addingCentroidsManually;
    toggleButtonText(buttonAddCentroidsManually);
    updateCanvasStyles();
}

buttonAddDataPointsManually.addEventListener('click', toggleAddingDataPointsManually, false);
buttonAddCentroidsManually.addEventListener('click', toggleAddingCentroidsManually, false);

let pointClickedOnCanvas = function(e) {
    let canvasRect = canvas.getBoundingClientRect();
    return [
        e.clientX - canvasRect.left - 1,
        e.clientY - canvasRect.top - 1
    ];
};

let isCentroidLimitReached = () => centroids.length >= colors.length;

let tryAddNewCentroid = function(point) {
    if (isCentroidLimitReached()) {
        return false;
    }
    centroids.push(point);
    return true;
}

let showCentroidLimitReachedMessage = function() {
    // using timeout to show alert after canvas is refreshed
    setTimeout(() => alert(`Sorry, reached limit of ${colors.length} colors.`), 10);
}

canvas.addEventListener('click', function(e) {
    let newPoint = pointClickedOnCanvas(e);
    if (addingDataPointsManually) {
        dataPoints.push(newPoint);
    } else if (addingCentroidsManually) {
        if (!tryAddNewCentroid(newPoint)) {
            showCentroidLimitReachedMessage();
            toggleAddingCentroidsManually();
        }
    }
    redrawAll();
}, false);

let randInt = function(min, max) {
    if (arguments.length == 1) {
        max = arguments[0];
        min = 0;
    }
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

document.getElementById('add-data-points-randomly').addEventListener('click', function() {
    let count = +document.getElementById('add-data-points-randomly-count').value;
    for (let i = 0; i < count; ++i) {
        let newPoint;
        do {
            newPoint = [
                randInt(0, canvas.width - 1),
                randInt(0, canvas.height - 1)
            ];
        } while (newPoint in centroids);
        dataPoints.push(newPoint);
    }
    redrawAll();
}, false);

document.getElementById('add-centroids-randomly').addEventListener('click', function() {
    let count = +document.getElementById('add-centroids-randomly-count').value,
        limitReached = false;
    for (let i = 0; i < count; ++i) {
        let newPoint;
        do {
            newPoint = [
                randInt(0, canvas.width - 1),
                randInt(0, canvas.height - 1)
            ];
        } while (newPoint in centroids);
        if (!tryAddNewCentroid(newPoint)) {
            limitReached = true;
            break;
        }
    }
    redrawAll();
    if (limitReached) {
        showCentroidLimitReachedMessage();
    }
}, false);

document.getElementById('remove-all-data-points').addEventListener('click', function() {
    dataPoints = [];
    redrawAll();
}, false);

document.getElementById('remove-all-centroids').addEventListener('click', function() {
    centroids = [];
    redrawAll();
}, false);

let distance = (point1, point2) => {
    return Math.sqrt(Math.pow(point1[0] - point2[0], 2) + Math.pow(point1[1] - point2[1], 2));
};

document.getElementById('reassign-data-points').addEventListener('click', function() {
    dataPoints.map((point, pointIndex) => {
        let smallestDistance = Number.MAX_SAFE_INTEGER,
            closestCentroidIndex = undefined;
        centroids.map((centroid, centroidIndex) => {
            let dist = distance(point, centroid);
            if (dist < smallestDistance) {
                smallestDistance = dist;
                closestCentroidIndex = centroidIndex;
            }
        });
        dataPointsAssignedCentroids[pointIndex] = closestCentroidIndex;
    });
    console.log(dataPointsAssignedCentroids);
    redrawAll();
}, false);
<commit_msg>Add centroids' positions update<commit_after>let canvas = document.getElementsByTagName('canvas')[0],
    ctx = canvas.getContext('2d');

let dataPoints = [],
    centroids = [],
    dataPointsAssignedCentroids = {}; // { dataPointIndex: centroidIndex }

let colors = [
    '#ED0A3F',
    '#0095B7',
    '#33CC99',
    '#00468C',
    '#0066FF',
    '#EE34D2',
    '#C88A65',
    '#A50B5E',
    '#733380',
    '#87421F'
]

let drawDataPoint = function([x, y], index) {
    ctx.save();
        ctx.fillStyle = colors[dataPointsAssignedCentroids[index]];
        ctx.beginPath();
        ctx.arc(x, y, 5, 0, 2 * Math.PI);
        ctx.fill();
    ctx.restore();
}

let drawCentroid = function([x, y], index) {
    ctx.save()
        ctx.strokeStyle = ctx.fillStyle = colors[index];
        ctx.beginPath();
        ctx.save()
            ctx.lineWidth = 2;
            ctx.arc(x, y, 8, 0, 2 * Math.PI);
            ctx.stroke();
        ctx.restore();
        ctx.beginPath();
        ctx.arc(x, y, 4, 0, 2 * Math.PI);
        ctx.fill();
    ctx.restore();
}

let redrawAll = function() {
    canvas.width = canvas.width;
    dataPoints.map(drawDataPoint);
    centroids.map(drawCentroid);
}

let addingDataPointsManually = false,
    addingCentroidsManually = false;

let buttonAddDataPointsManually = document.getElementById('add-data-points-manually'),
    buttonAddCentroidsManually = document.getElementById('add-centroids-manually');

let toggleButtonText = function(button) {
    let currentText = button.innerHTML;
    button.innerHTML = button.getAttribute('data-toggle');
    button.setAttribute('data-toggle', currentText);
}

let updateCanvasStyles = function() {
    if (addingDataPointsManually || addingCentroidsManually) {
        canvas.classList.add('canvas-picking-active');
    } else {
        canvas.classList.remove('canvas-picking-active');
    }
}

let toggleAddingDataPointsManually = function() {
    if (addingCentroidsManually) {
        toggleAddingCentroidsManually();
    }
    addingDataPointsManually = !addingDataPointsManually;
    toggleButtonText(buttonAddDataPointsManually);
    updateCanvasStyles();
}

let toggleAddingCentroidsManually = function() {
    if (!addingCentroidsManually && isCentroidLimitReached()) {
        showCentroidLimitReachedMessage();
        return;
    }
    if (addingDataPointsManually) {
        toggleAddingDataPointsManually();
    }
    addingCentroidsManually = !addingCentroidsManually;
    toggleButtonText(buttonAddCentroidsManually);
    updateCanvasStyles();
}

buttonAddDataPointsManually.addEventListener('click', toggleAddingDataPointsManually, false);
buttonAddCentroidsManually.addEventListener('click', toggleAddingCentroidsManually, false);

let pointClickedOnCanvas = function(e) {
    let canvasRect = canvas.getBoundingClientRect();
    return [
        e.clientX - canvasRect.left - 1,
        e.clientY - canvasRect.top - 1
    ];
};

let isCentroidLimitReached = () => centroids.length >= colors.length;

let tryAddNewCentroid = function(point) {
    if (isCentroidLimitReached()) {
        return false;
    }
    centroids.push(point);
    return true;
}

let showCentroidLimitReachedMessage = function() {
    // using timeout to show alert after canvas is refreshed
    setTimeout(() => alert(`Sorry, reached limit of ${colors.length} colors.`), 10);
}

canvas.addEventListener('click', function(e) {
    let newPoint = pointClickedOnCanvas(e);
    if (addingDataPointsManually) {
        dataPoints.push(newPoint);
    } else if (addingCentroidsManually) {
        if (!tryAddNewCentroid(newPoint)) {
            showCentroidLimitReachedMessage();
            toggleAddingCentroidsManually();
        }
    }
    redrawAll();
}, false);

let randInt = function(min, max) {
    if (arguments.length == 1) {
        max = arguments[0];
        min = 0;
    }
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

document.getElementById('add-data-points-randomly').addEventListener('click', function() {
    let count = +document.getElementById('add-data-points-randomly-count').value;
    for (let i = 0; i < count; ++i) {
        let newPoint;
        do {
            newPoint = [
                randInt(0, canvas.width - 1),
                randInt(0, canvas.height - 1)
            ];
        } while (newPoint in centroids);
        dataPoints.push(newPoint);
    }
    redrawAll();
}, false);

document.getElementById('add-centroids-randomly').addEventListener('click', function() {
    let count = +document.getElementById('add-centroids-randomly-count').value,
        limitReached = false;
    for (let i = 0; i < count; ++i) {
        let newPoint;
        do {
            newPoint = [
                randInt(0, canvas.width - 1),
                randInt(0, canvas.height - 1)
            ];
        } while (newPoint in centroids);
        if (!tryAddNewCentroid(newPoint)) {
            limitReached = true;
            break;
        }
    }
    redrawAll();
    if (limitReached) {
        showCentroidLimitReachedMessage();
    }
}, false);

document.getElementById('remove-all-data-points').addEventListener('click', function() {
    dataPoints = [];
    redrawAll();
}, false);

document.getElementById('remove-all-centroids').addEventListener('click', function() {
    centroids = [];
    redrawAll();
}, false);

let distance = (point1, point2) => {
    return Math.sqrt(Math.pow(point1[0] - point2[0], 2) + Math.pow(point1[1] - point2[1], 2));
};

document.getElementById('reassign-data-points').addEventListener('click', function() {
    dataPoints.map((point, pointIndex) => {
        let smallestDistance = Number.MAX_SAFE_INTEGER,
            closestCentroidIndex = undefined;
        centroids.map((centroid, centroidIndex) => {
            let dist = distance(point, centroid);
            if (dist < smallestDistance) {
                smallestDistance = dist;
                closestCentroidIndex = centroidIndex;
            }
        });
        dataPointsAssignedCentroids[pointIndex] = closestCentroidIndex;
    });
    redrawAll();
}, false);

document.getElementById('update-centroids-positions').addEventListener('click', function() {
    centroids.map((centroid, centroidIndex) => {
        let assignedPoints = dataPoints.filter((_, pointIndex) => dataPointsAssignedCentroids[pointIndex] == centroidIndex),
            sumX = 0,
            sumY = 0;
        if (assignedPoints.length == 0)
            return;
        assignedPoints.map(([x, y]) => {
            sumX += x;
            sumY += y;
        });
        centroid[0] = sumX / assignedPoints.length;
        centroid[1] = sumY / assignedPoints.length;
    });
    redrawAll();
}, false);
<|endoftext|>


<commit_before>"use strict";

var CityTour = CityTour || {};

CityTour.RiverGenerator = (function() {
  var generateRiverCurves = function(middleRow, columnsToGenerate) {
    var SUB_DIVISIONS = 1;
    var MIN_RIVER_BENDS = 3;
    var MAX_RIVER_BENDS = 8;
    var MAX_BEND_AMOUNT = 20 * SUB_DIVISIONS;
    var TOP_BANK_OFFSET = 4 * SUB_DIVISIONS;
    var BOTTOM_BANK_OFFSET = 12 * SUB_DIVISIONS;
    var TOP_BANK_MAX_JITTER = 6 * SUB_DIVISIONS;
    var HALF_TOP_BANK_MAX_JITTER = TOP_BANK_MAX_JITTER / 2;
    var BOTTOM_BANK_MAX_JITTER = 6 * SUB_DIVISIONS;
    var HALF_BOTTOM_BANK_MAX_JITTER = BOTTOM_BANK_MAX_JITTER / 2;

    var i;
    var baseCurvePoints, topCurvePoints, bottomCurvePoints;
    var randomJitter;
    var baseCurve, topCurve, bottomCurve;

    var riverSubDivisions = Math.round((Math.random() * (MAX_RIVER_BENDS - MIN_RIVER_BENDS))) + MIN_RIVER_BENDS;
    baseCurvePoints = [new THREE.Vector2(0, middleRow)];
    for (i = 1; i <= riverSubDivisions; i++) {
      var column = columnsToGenerate * i * (1 / riverSubDivisions);
      baseCurvePoints.push(new THREE.Vector2(column, middleRow + ((Math.random() * MAX_BEND_AMOUNT) - (MAX_BEND_AMOUNT / 2))));
    }
    baseCurve = new THREE.SplineCurve(baseCurvePoints);

    topCurvePoints = [];
    bottomCurvePoints = [];
    for (i = 0; i < baseCurvePoints.length; i++) {
      randomJitter = Math.round(((Math.random() * TOP_BANK_MAX_JITTER) - HALF_TOP_BANK_MAX_JITTER));
      topCurvePoints.push(new THREE.Vector2(baseCurvePoints[i].x, baseCurvePoints[i].y + TOP_BANK_OFFSET + randomJitter));

      randomJitter = Math.round(((Math.random() * BOTTOM_BANK_MAX_JITTER) - HALF_BOTTOM_BANK_MAX_JITTER));
      bottomCurvePoints.push(new THREE.Vector2(baseCurvePoints[i].x, baseCurvePoints[i].y + BOTTOM_BANK_OFFSET + randomJitter));
    }

    topCurve = new THREE.SplineCurve(topCurvePoints);
    bottomCurve = new THREE.SplineCurve(bottomCurvePoints);

    return { topCurve: topCurve, bottomCurve: bottomCurve, };
  };

  var addRiver = function(terrainCoordinates, middleRow, columnsToGenerate) {
    var x, z, xStep;
    var xCoordinate, zCoordinate;
    var minimumRiverBankHeight;
    var vector, topVector, bottomVector;

    var riverCurves = generateRiverCurves(middleRow, columnsToGenerate);
    var topCurve = riverCurves.topCurve;
    var bottomCurve = riverCurves.bottomCurve;

    xStep = 1 / columnsToGenerate;

    // Find the lowest point on the north river bank
    minimumRiverBankHeight = Number.POSITIVE_INFINITY;
    for (x = 0.0; x <= 1.0; x += xStep) {
      vector = topCurve.getPointAt(x);
      xCoordinate = Math.round(vector.x);
      zCoordinate = Math.round(vector.y);

      if (terrainCoordinates[xCoordinate][zCoordinate].height < minimumRiverBankHeight) {
        minimumRiverBankHeight = terrainCoordinates[xCoordinate][zCoordinate].height;
      }
    }
    // Find the lowest point on the south river bank
    for (x = 0.0; x <= 1.0; x += xStep) {
      vector = bottomCurve.getPointAt(x);
      xCoordinate = Math.round(vector.x);
      zCoordinate = Math.round(vector.y);

      if (terrainCoordinates[xCoordinate][zCoordinate].height < minimumRiverBankHeight) {
        minimumRiverBankHeight = terrainCoordinates[xCoordinate][zCoordinate].height;
      }
    }
    // Set every terrain point between the two river banks to same height as the lowest point
    for (x = 0.0; x <= 1.0; x += xStep / 2) {
      topVector = topCurve.getPointAt(x);
      bottomVector = bottomCurve.getPointAt(x);
      xCoordinate = Math.round(topVector.x);

      for (z = Math.ceil(topVector.y); z < bottomVector.y; z++) {
        terrainCoordinates[xCoordinate][z].height = minimumRiverBankHeight;
      }
    }
    // Fill the river with water
    floodFill(terrainCoordinates, 0, topCurve.getPointAt(0.0).y, minimumRiverBankHeight, CityTour.Terrain.WATER);

    var i;
    var baseHeight;
    var newHeight;
    var depth;
    var heightIncreaseAmount;
    var previousX = -1;

    // "Erode" the north river bank
    baseHeight = minimumRiverBankHeight;
    depth = Math.round(CityTour.Math.lerp(16, 20, Math.random()));
    for (i = 1; i < depth; i++) {
      heightIncreaseAmount = CityTour.Math.lerp(0.0, 2.0, Math.random());
      baseHeight += heightIncreaseAmount;
      previousX = -1;

      for (x = 0.0; x <= 1.0; x += xStep / 2) {
        vector = topCurve.getPointAt(x);
        xCoordinate = Math.round(vector.x);

        if (xCoordinate > previousX) {
          zCoordinate = Math.ceil(vector.y) - i;

          newHeight = baseHeight + heightIncreaseAmount + CityTour.Math.lerp(-2.0, 2.0, Math.random());

          if (newHeight < terrainCoordinates[xCoordinate][zCoordinate].height) {
            terrainCoordinates[xCoordinate][zCoordinate].height = newHeight;
          }
        }
      }

      previousX = xCoordinate;
    }
  };

  var floodFill = function(terrainCoordinates, x, z, height, material) {
    terrainCoordinates[x][z].height = height;
    terrainCoordinates[x][z].material = material;

    if (terrainCoordinates[x - 1] &&
        terrainCoordinates[x - 1][z] &&
        terrainCoordinates[x - 1][z].height <= height &&
        terrainCoordinates[x - 1][z].material != material) {
      floodFill(terrainCoordinates, x - 1, z, height, material);
    }
    if (terrainCoordinates[x + 1] &&
        terrainCoordinates[x + 1][z] &&
        terrainCoordinates[x + 1][z].height <= height &&
        terrainCoordinates[x + 1][z].material != material) {
      floodFill(terrainCoordinates, x + 1, z, height, material);
    }
    if (terrainCoordinates[x] &&
        terrainCoordinates[x][z - 1] &&
        terrainCoordinates[x][z - 1].height <= height &&
        terrainCoordinates[x][z - 1].material != material) {
      floodFill(terrainCoordinates, x, z - 1, height, material);
    }
    if (terrainCoordinates[x] &&
        terrainCoordinates[x][z + 1] &&
        terrainCoordinates[x][z + 1].height <= height &&
        terrainCoordinates[x][z + 1].material != material) {
      floodFill(terrainCoordinates, x, z + 1, height, material);
    }
  };

  return {
    addRiver: addRiver,
  };
})();
<commit_msg>Extracting function<commit_after>"use strict";

var CityTour = CityTour || {};

CityTour.RiverGenerator = (function() {
  var generateRiverCurves = function(middleRow, columnsToGenerate) {
    var SUB_DIVISIONS = 1;
    var MIN_RIVER_BENDS = 3;
    var MAX_RIVER_BENDS = 8;
    var MAX_BEND_AMOUNT = 20 * SUB_DIVISIONS;
    var TOP_BANK_OFFSET = 4 * SUB_DIVISIONS;
    var BOTTOM_BANK_OFFSET = 12 * SUB_DIVISIONS;
    var TOP_BANK_MAX_JITTER = 6 * SUB_DIVISIONS;
    var HALF_TOP_BANK_MAX_JITTER = TOP_BANK_MAX_JITTER / 2;
    var BOTTOM_BANK_MAX_JITTER = 6 * SUB_DIVISIONS;
    var HALF_BOTTOM_BANK_MAX_JITTER = BOTTOM_BANK_MAX_JITTER / 2;

    var i;
    var baseCurvePoints, topCurvePoints, bottomCurvePoints;
    var randomJitter;
    var baseCurve, topCurve, bottomCurve;

    var riverSubDivisions = Math.round((Math.random() * (MAX_RIVER_BENDS - MIN_RIVER_BENDS))) + MIN_RIVER_BENDS;
    baseCurvePoints = [new THREE.Vector2(0, middleRow)];
    for (i = 1; i <= riverSubDivisions; i++) {
      var column = columnsToGenerate * i * (1 / riverSubDivisions);
      baseCurvePoints.push(new THREE.Vector2(column, middleRow + ((Math.random() * MAX_BEND_AMOUNT) - (MAX_BEND_AMOUNT / 2))));
    }
    baseCurve = new THREE.SplineCurve(baseCurvePoints);

    topCurvePoints = [];
    bottomCurvePoints = [];
    for (i = 0; i < baseCurvePoints.length; i++) {
      randomJitter = Math.round(((Math.random() * TOP_BANK_MAX_JITTER) - HALF_TOP_BANK_MAX_JITTER));
      topCurvePoints.push(new THREE.Vector2(baseCurvePoints[i].x, baseCurvePoints[i].y + TOP_BANK_OFFSET + randomJitter));

      randomJitter = Math.round(((Math.random() * BOTTOM_BANK_MAX_JITTER) - HALF_BOTTOM_BANK_MAX_JITTER));
      bottomCurvePoints.push(new THREE.Vector2(baseCurvePoints[i].x, baseCurvePoints[i].y + BOTTOM_BANK_OFFSET + randomJitter));
    }

    topCurve = new THREE.SplineCurve(topCurvePoints);
    bottomCurve = new THREE.SplineCurve(bottomCurvePoints);

    return { topCurve: topCurve, bottomCurve: bottomCurve, };
  };

  var addRiver = function(terrainCoordinates, middleRow, columnsToGenerate) {
    var x, z, xStep;
    var xCoordinate, zCoordinate;
    var minimumRiverBankHeight;
    var vector, topVector, bottomVector;

    var riverCurves = generateRiverCurves(middleRow, columnsToGenerate);
    var topCurve = riverCurves.topCurve;
    var bottomCurve = riverCurves.bottomCurve;

    xStep = 1 / columnsToGenerate;

    // Find the lowest point on the north river bank
    minimumRiverBankHeight = Number.POSITIVE_INFINITY;
    for (x = 0.0; x <= 1.0; x += xStep) {
      vector = topCurve.getPointAt(x);
      xCoordinate = Math.round(vector.x);
      zCoordinate = Math.round(vector.y);

      if (terrainCoordinates[xCoordinate][zCoordinate].height < minimumRiverBankHeight) {
        minimumRiverBankHeight = terrainCoordinates[xCoordinate][zCoordinate].height;
      }
    }
    // Find the lowest point on the south river bank
    for (x = 0.0; x <= 1.0; x += xStep) {
      vector = bottomCurve.getPointAt(x);
      xCoordinate = Math.round(vector.x);
      zCoordinate = Math.round(vector.y);

      if (terrainCoordinates[xCoordinate][zCoordinate].height < minimumRiverBankHeight) {
        minimumRiverBankHeight = terrainCoordinates[xCoordinate][zCoordinate].height;
      }
    }
    // Set every terrain point between the two river banks to same height as the lowest point
    for (x = 0.0; x <= 1.0; x += xStep / 2) {
      topVector = topCurve.getPointAt(x);
      bottomVector = bottomCurve.getPointAt(x);
      xCoordinate = Math.round(topVector.x);

      for (z = Math.ceil(topVector.y); z < bottomVector.y; z++) {
        terrainCoordinates[xCoordinate][z].height = minimumRiverBankHeight;
      }
    }
    // Fill the river with water
    floodFill(terrainCoordinates, 0, topCurve.getPointAt(0.0).y, minimumRiverBankHeight, CityTour.Terrain.WATER);

    erodeNorthRiverBank(terrainCoordinates, topCurve, minimumRiverBankHeight, xStep);
  };

  var erodeNorthRiverBank = function(terrainCoordinates, riverBankCurve, riverBankHeight, xStep) {
    var i, x;
    var xCoordinate, zCoordinate;
    var vector;
    var baseHeight;
    var newHeight;
    var depth;
    var heightIncreaseAmount;
    var previousX = -1;

    baseHeight = riverBankHeight;
    depth = Math.round(CityTour.Math.lerp(16, 20, Math.random()));
    for (i = 1; i < depth; i++) {
      heightIncreaseAmount = CityTour.Math.lerp(0.0, 2.0, Math.random());
      baseHeight += heightIncreaseAmount;
      previousX = -1;

      for (x = 0.0; x <= 1.0; x += xStep / 2) {
        vector = riverBankCurve.getPointAt(x);
        xCoordinate = Math.round(vector.x);

        if (xCoordinate > previousX) {
          zCoordinate = Math.ceil(vector.y) - i;

          newHeight = baseHeight + heightIncreaseAmount + CityTour.Math.lerp(-2.0, 2.0, Math.random());

          if (newHeight < terrainCoordinates[xCoordinate][zCoordinate].height) {
            terrainCoordinates[xCoordinate][zCoordinate].height = newHeight;
          }
        }
      }

      previousX = xCoordinate;
    }
  };

  var floodFill = function(terrainCoordinates, x, z, height, material) {
    terrainCoordinates[x][z].height = height;
    terrainCoordinates[x][z].material = material;

    if (terrainCoordinates[x - 1] &&
        terrainCoordinates[x - 1][z] &&
        terrainCoordinates[x - 1][z].height <= height &&
        terrainCoordinates[x - 1][z].material != material) {
      floodFill(terrainCoordinates, x - 1, z, height, material);
    }
    if (terrainCoordinates[x + 1] &&
        terrainCoordinates[x + 1][z] &&
        terrainCoordinates[x + 1][z].height <= height &&
        terrainCoordinates[x + 1][z].material != material) {
      floodFill(terrainCoordinates, x + 1, z, height, material);
    }
    if (terrainCoordinates[x] &&
        terrainCoordinates[x][z - 1] &&
        terrainCoordinates[x][z - 1].height <= height &&
        terrainCoordinates[x][z - 1].material != material) {
      floodFill(terrainCoordinates, x, z - 1, height, material);
    }
    if (terrainCoordinates[x] &&
        terrainCoordinates[x][z + 1] &&
        terrainCoordinates[x][z + 1].height <= height &&
        terrainCoordinates[x][z + 1].material != material) {
      floodFill(terrainCoordinates, x, z + 1, height, material);
    }
  };

  return {
    addRiver: addRiver,
  };
})();
<|endoftext|>


<commit_before>/**
 Copyright 2014 Google Inc. All rights reserved.

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

var truncate = function (str, width) {
    if (str && str.length > width) {
        return str.slice(0, width) + "...";
    }
    return str;
};

var pods = [];
var services = [];
var controllers = [];
var uses = {};

var groups = {};

var insertByName = function(index, value) {
  if (!value || !value.metadata.labels || !value.metadata.name) {
    return;
  }
  // console.log("type = " + value.type + " labels = " + value.metadata.name);
  //	var list = groups[value.metadata.name];
  var key = value.metadata.labels.name;
	var list = groups[key];
	if (!list) {
		list = [];
		groups[key] = list;
	}
	list.push(value);
};

var groupByName = function () {
    $.each(pods.items, insertByName);
    $.each(controllers.items, insertByName);
    $.each(services.items, insertByName);
};

var matchesLabelQuery = function (labels, selector) {
    var match = true;

var connectControllers = function() {
    connectUses();
	for (var i = 0; i < controllers.items.length; i++) {
		var controller = controllers.items[i];
    console.log("controller: " + controller.metadata.name)
		for (var j = 0; j < pods.items.length; j++) {
			var pod = pods.items[j];
			if (pod.metadata.labels.name == controller.metadata.labels.name) {
        if (controller.metadata.labels.version && pod.metadata.labels.version && (controller.metadata.labels.version != pod.metadata.labels.version)) {
          continue;
        }
        console.log('connect controller: ' + 'controller-' + controller.metadata.name + ' to pod-' + pod.metadata.name);
				jsPlumb.connect({
					source: 'controller-' + controller.metadata.name,
					target: 'pod-' + pod.metadata.name,
					anchors:["Bottom", "Bottom"],
					paintStyle:{lineWidth:5,strokeStyle:'rgb(51,105,232)'},
					joinStyle:"round",
					endpointStyle:{ fillStyle: 'rgb(51,105,232)', radius: 7 },
					connector: ["Flowchart", { cornerRadius:5 }]});
			}
		}
	}
	for (var i = 0; i < services.items.length; i++) {
		var service = services.items[i];
    //            if (service.metadata.name == 'kubernetes' || service.metadata.name == 'skydns' || service.metadata.name == 'kubernetes-ro') { continue; }
		for (var j = 0; j < pods.items.length; j++) {
			var pod = pods.items[j];
      console.log('connect service: ' + 'service-' + service.metadata.name + ' to pod-' + pod.metadata.name);
			if (matchesLabelQuery(pod.metadata.labels, service.spec.selector)) {
				jsPlumb.connect(
					{
						source: 'service-' + service.metadata.name,
						target: 'pod-' + pod.metadata.name,
						anchors:["Bottom", "Top"],
						paintStyle:{lineWidth:5,strokeStyle:'rgb(0,153,57)'},
						endpointStyle:{ fillStyle: 'rgb(0,153,57)', radius: 7 },
						connector:["Flowchart", { cornerRadius:5 }]});
			}
		}
	}
};

var connectControllers = function () {
    //connectUses();
    for (var i = 0; i < controllers.items.length; i++) {
        var controller = controllers.items[i];
        for (var j = 0; j < pods.items.length; j++) {
            var pod = pods.items[j];
            if (pod.metadata.labels && controller.metadata && controller.metadata.labels && controller.metadata.labels.name &&
                pod.metadata.labels['name'] == controller.metadata.labels.name) {

                jsPlumb.connect({
                    source: controller.metadata.uid,
                    target: pod.metadata.uid,
                    anchors: ["Bottom", "Bottom"],
                    paintStyle: {lineWidth: 5, strokeStyle: 'rgb(51,105,232)'},
                    joinStyle: "round",
                    endpointStyle: {fillStyle: 'rgb(51,105,232)', radius: 7},
                    connector: ["Flowchart", {cornerRadius: 5}]
                });
            }
        }
    }

    for (var i = 0; i < services.items.length; i++) {
        var service = services.items[i];
        if (service.metadata.name == 'kubernetes') {
            continue;
        }

        for (var j = 0; j < pods.items.length; j++) {

            var pod = pods.items[j];
            if (matchesLabelQuery(pod.metadata.labels, service.metadata.name)) {

                jsPlumb.connect(
                    {
                        source: service.metadata.name,
                        target: pod.metadata.uid,
                        anchors: ["Bottom", "Top"],
                        paintStyle: {lineWidth: 5, strokeStyle: 'rgb(0,153,57)'},
                        endpointStyle: {fillStyle: 'rgb(0,153,57)', radius: 7},
                        joinStyle: "round",
                        connector: ["Flowchart", {cornerRadius: 5}]
                    });
            }
        }
    }
};

var colors = [
	'rgb(213,15,37)',
	'rgb(238,178,17)',
	'rgb(17,178,238)'
]

var connectUses = function() {
	var colorIx = 0;
	var keys = [];
	$.each(uses, function(key) {
		keys.push(key);
	});
	keys.sort(function(a, b) { return a > b; });
	$.each(keys, function(idx) {
		var key = keys[idx];
		var list = uses[key];
		var color = colors[colorIx];
		colorIx++;
		if (colorIx >= colors.length) { colorIx = 0;};
		$.each(pods.items, function(i, pod) {
        var podKey = pod.metadata.labels.name;
         console.log('connect uses key: ' +key + ', ' + podKey);
			if (podKey == key) {
				$.each(list, function(j, serviceId) {
          console.log('connect: ' + 'pod-' + pod.metadata.name + ' to service-' + serviceId);
					jsPlumb.connect(
					{
						source: 'pod-' + pod.metadata.name,
						target: 'service-' + serviceId,
						endpoint: "Blank",
						anchors:["Bottom", "Right"],
						connector: "Straight",
						paintStyle:{lineWidth:2,strokeStyle:color},
						overlays:[
    						[ "Arrow", { width:15, length:30, location: 0.3}],
    						[ "Arrow", { width:15, length:30, location: 0.6}],
    						[ "Arrow", { width:15, length:30, location: 1}],
    					],
					});
				});
			}
		});
	});
};

var makeGroupOrder = function() {
  var groupScores = {};
  $.each(groups, function(key, val) {
    console.log("group key: " + key);
		if (!groupScores[key]) {
		  groupScores[key] = 0;
		}
		if (uses[key]) {
			value = uses[key];
		  $.each(value, function(ix, uses_label) {
				if (!groupScores[uses_label]) {
				    groupScores[uses_label] = 1;
				} else {
				    groupScores[uses_label]++;
				}
			});
		} else {
			if (!groupScores["no-service"]) {
				groupScores["no-service"] = 1;
			} else {
				groupScores["no-service"]++;
			}
		}
	});
  var groupOrder = [];
  $.each(groupScores, function(key, value) {
    groupOrder.push(key);
	});
  groupOrder.sort(function(a, b) { return groupScores[a] - groupScores[b]; });

	console.log(groupOrder);
  return groupOrder;
};

var renderGroups = function() {
	var elt = $('#sheet');
	var y = 10;
	var serviceLeft = 0;
	var groupOrder = makeGroupOrder();
  var counts = {}
	$.each(groupOrder, function(ix, key) {
		list = groups[key];
		// list = value;
    if (!list) {
    	return;
    }
		var div = $('
');
		var x = 100;
		$.each(list, function(index, value) {
      console.log("render groups: " + value.type + ", " + value.metadata.name + ", " + index)
			var eltDiv = null;
			if (value.type == "pod") {
				eltDiv = $('
');
				eltDiv.html('' +
          truncate(value.metadata.name, 8) +
          (value.metadata.labels.version ? "

" + value.metadata.labels.version : "") +
          '');
			} else if (value.type == "service") {
				eltDiv = $('
');
				eltDiv.html('' +
          value.metadata.name +
          (value.metadata.labels.version ? "

" + value.metadata.labels.version : "") +
          (value.spec.clusterIP ? "

" + value.spec.clusterIP : "") +
          (value.status.loadBalancer && value.status.loadBalancer.ingress ? "
" + value.status.loadBalancer.ingress[0].ip + "" : "") +
          '');
			} else {
        var key = 'controller-' + value.metadata.labels.name;
        counts[key] = key in counts ? counts[key] + 1 : 0;
        console.log("COUNTS? " + key + ", " + counts[key]);
				eltDiv = $('
');
				eltDiv.html('' +
          value.metadata.name +
          (value.metadata.labels.version ? "

" + value.metadata.labels.version : "") +
          '');
			}
			div.append(eltDiv);
			x += 130;
		});
		y += 400;
		serviceLeft += 200;
		elt.append(div);
	});
};

var insertUse = function(name, use) {
  for (var i = 0; i < uses[name].length; i++) {
    if (uses[name][i] == use) {
      return;
    }
  }
  uses[name].push(use);
};

var loadData = function() {
	var deferred = new $.Deferred();
	var req1 = $.getJSON("/api/v1/pods?labelSelector=visualize%3Dtrue", function( data ) {
		pods = data;
		$.each(data.items, function(key, val) {
    	val.type = 'pod';
      if (val.metadata.labels && val.metadata.labels.uses) {
      	var key = val.metadata.labels.name;
		    if (!uses[key]) {
			  	uses[key] = val.metadata.labels.uses.split("_");
		    } else {
			  	$.each(val.metadata.labels.uses.split("_"), function(ix, use) { insertUse(key, use); });
		    }
		  }
    });
	});

	var req2 = $.getJSON("/api/v1/replicationcontrollers?labelSelector=visualize%3Dtrue", function( data ) {
		controllers = data;
		$.each(data.items, function(key, val) { val.type = 'replicationController'; console.log("Controller ID = " + val.metadata.name) });
	});

    var req2 = $.getJSON("/api/v1/replicationcontrollers", function (data) {
        controllers = data;
        $.each(data.items, function (key, val) {
            val.type = 'replicationController';
        });
    });

	var req3 = $.getJSON("/api/v1/services?labelSelector=visualize%3Dtrue", function( data ) {
		services = data;
		console.log("loadData(): Services");
		console.log(services);
		$.each(data.items, function(key, val) { val.type = 'service'; console.log("service ID = " + val.metadata.name)});
	});
	$.when(req1, req2, req3).then(function() {
		deferred.resolve();
	});
	return deferred;
}

function refresh(instance) {

	console.log("Refresh");
	pods = [];
	services = [];
	controllers = [];
	uses = {};
	groups = {};

	$.when(loadData()).then(function() {
		groupByName();
		$('#sheet').empty();
		renderGroups();
		connectControllers();

		setTimeout(function() {
			refresh(instance);
		}, 1000);
  });
}

jsPlumb.bind("ready", function() {
	var instance = jsPlumb.getInstance({
		// default drag options
		DragOptions : { cursor: 'pointer', zIndex:2000 },
		// the overlays to decorate each connection with.  note that the label overlay uses a function to generate the label text; in this
		// case it returns the 'labelText' member that we set on each connection in the 'init' method below.
		ConnectionOverlays : [
			[ "Arrow", { location:1 } ],
			//[ "Label", {
			//	location:0.1,
			//	id:"label",
			//	cssClass:"aLabel"
			//}]
		],
		Container:"flowchart-demo"
	});

	refresh(instance);
	jsPlumb.fire("jsPlumbDemoLoaded", instance);
  });
<commit_msg>Updated<commit_after>/**
Copyright 2014 Google Inc. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

var truncate = function(str, width) {
	if (str.length > width) {
		return str.slice(0, width) + "...";
	}
	return str;
}

var pods = [];
var services = [];
var controllers = [];
var uses = {};

var groups = {};

var insertByName = function(index, value) {
  if (!value || !value.metadata.labels || !value.metadata.name) {
    return;
  }
  // console.log("type = " + value.type + " labels = " + value.metadata.name);
  //	var list = groups[value.metadata.name];
  var key = value.metadata.labels.name;
	var list = groups[key];
	if (!list) {
		list = [];
		groups[key] = list;
	}
	list.push(value);
};

var groupByName = function() {
	$.each(pods.items, insertByName);
	$.each(controllers.items, insertByName);
	$.each(services.items, insertByName);
};

var matchesLabelQuery = function(labels, selector) {
	var match = true;
	$.each(selector, function(key, value) {
		if (labels[key] != value) {
			match = false;
		}
	});
	return match;
}

var connectControllers = function() {
    connectUses();
	for (var i = 0; i < controllers.items.length; i++) {
		var controller = controllers.items[i];
    console.log("controller: " + controller.metadata.name)
		for (var j = 0; j < pods.items.length; j++) {
			var pod = pods.items[j];
			if (pod.metadata.labels.name == controller.metadata.labels.name) {
        if (controller.metadata.labels.version && pod.metadata.labels.version && (controller.metadata.labels.version != pod.metadata.labels.version)) {
          continue;
        }
        console.log('connect controller: ' + 'controller-' + controller.metadata.name + ' to pod-' + pod.metadata.name);
				jsPlumb.connect({
					source: 'controller-' + controller.metadata.name,
					target: 'pod-' + pod.metadata.name,
					anchors:["Bottom", "Bottom"],
					paintStyle:{lineWidth:5,strokeStyle:'rgb(51,105,232)'},
					joinStyle:"round",
					endpointStyle:{ fillStyle: 'rgb(51,105,232)', radius: 7 },
					connector: ["Flowchart", { cornerRadius:5 }]});
			}
		}
	}
	for (var i = 0; i < services.items.length; i++) {
		var service = services.items[i];
    //            if (service.metadata.name == 'kubernetes' || service.metadata.name == 'skydns' || service.metadata.name == 'kubernetes-ro') { continue; }
		for (var j = 0; j < pods.items.length; j++) {
			var pod = pods.items[j];
      console.log('connect service: ' + 'service-' + service.metadata.name + ' to pod-' + pod.metadata.name);
			if (matchesLabelQuery(pod.metadata.labels, service.spec.selector)) {
				jsPlumb.connect(
					{
						source: 'service-' + service.metadata.name,
						target: 'pod-' + pod.metadata.name,
						anchors:["Bottom", "Top"],
						paintStyle:{lineWidth:5,strokeStyle:'rgb(0,153,57)'},
						endpointStyle:{ fillStyle: 'rgb(0,153,57)', radius: 7 },
						connector:["Flowchart", { cornerRadius:5 }]});
			}
		}
	}
};

var colors = [
	'rgb(213,15,37)',
	'rgb(238,178,17)',
	'rgb(17,178,238)'
]

var connectUses = function() {
	var colorIx = 0;
	var keys = [];
	$.each(uses, function(key) {
		keys.push(key);
	});
	keys.sort(function(a, b) { return a > b; });
	$.each(keys, function(idx) {
		var key = keys[idx];
		var list = uses[key];
		var color = colors[colorIx];
		colorIx++;
		if (colorIx >= colors.length) { colorIx = 0;};
		$.each(pods.items, function(i, pod) {
        var podKey = pod.metadata.labels.name;
         console.log('connect uses key: ' +key + ', ' + podKey);
			if (podKey == key) {
				$.each(list, function(j, serviceId) {
          console.log('connect: ' + 'pod-' + pod.metadata.name + ' to service-' + serviceId);
					jsPlumb.connect(
					{
						source: 'pod-' + pod.metadata.name,
						target: 'service-' + serviceId,
						endpoint: "Blank",
						anchors:["Bottom", "Right"],
						connector: "Straight",
						paintStyle:{lineWidth:2,strokeStyle:color},
						overlays:[
    						[ "Arrow", { width:15, length:30, location: 0.3}],
    						[ "Arrow", { width:15, length:30, location: 0.6}],
    						[ "Arrow", { width:15, length:30, location: 1}],
    					],
					});
				});
			}
		});
	});
};

var makeGroupOrder = function() {
  var groupScores = {};
  $.each(groups, function(key, val) {
    console.log("group key: " + key);
		if (!groupScores[key]) {
		  groupScores[key] = 0;
		}
		if (uses[key]) {
			value = uses[key];
		  $.each(value, function(ix, uses_label) {
				if (!groupScores[uses_label]) {
				    groupScores[uses_label] = 1;
				} else {
				    groupScores[uses_label]++;
				}
			});
		} else {
			if (!groupScores["no-service"]) {
				groupScores["no-service"] = 1;
			} else {
				groupScores["no-service"]++;
			}
		}
	});
  var groupOrder = [];
  $.each(groupScores, function(key, value) {
    groupOrder.push(key);
	});
  groupOrder.sort(function(a, b) { return groupScores[a] - groupScores[b]; });

	console.log(groupOrder);
  return groupOrder;
};

var renderGroups = function() {
	var elt = $('#sheet');
	var y = 10;
	var serviceLeft = 0;
	var groupOrder = makeGroupOrder();
  var counts = {}
	$.each(groupOrder, function(ix, key) {
		list = groups[key];
		// list = value;
    if (!list) {
    	return;
    }
		var div = $('
');
		var x = 100;
		$.each(list, function(index, value) {
      console.log("render groups: " + value.type + ", " + value.metadata.name + ", " + index)
			var eltDiv = null;
			if (value.type == "pod") {
				eltDiv = $('
');
				eltDiv.html('' +
          truncate(value.metadata.name, 8) +
          (value.metadata.labels.version ? "

" + value.metadata.labels.version : "") +
          '');
			} else if (value.type == "service") {
				eltDiv = $('
');
				eltDiv.html('' +
          value.metadata.name +
          (value.metadata.labels.version ? "

" + value.metadata.labels.version : "") +
          (value.spec.clusterIP ? "

" + value.spec.clusterIP : "") +
          (value.status.loadBalancer && value.status.loadBalancer.ingress ? "
" + value.status.loadBalancer.ingress[0].ip + "" : "") +
          '');
			} else {
        var key = 'controller-' + value.metadata.labels.name;
        counts[key] = key in counts ? counts[key] + 1 : 0;
        console.log("COUNTS? " + key + ", " + counts[key]);
				eltDiv = $('
');
				eltDiv.html('' +
          value.metadata.name +
          (value.metadata.labels.version ? "

" + value.metadata.labels.version : "") +
          '');
			}
			div.append(eltDiv);
			x += 130;
		});
		y += 400;
		serviceLeft += 200;
		elt.append(div);
	});
};

var insertUse = function(name, use) {
  for (var i = 0; i < uses[name].length; i++) {
    if (uses[name][i] == use) {
      return;
    }
  }
  uses[name].push(use);
};

var loadData = function() {
	var deferred = new $.Deferred();
	var req1 = $.getJSON("/api/v1/pods?labelSelector=visualize%3Dtrue", function( data ) {
		pods = data;
		$.each(data.items, function(key, val) {
    	val.type = 'pod';
      if (val.metadata.labels && val.metadata.labels.uses) {
      	var key = val.metadata.labels.name;
		    if (!uses[key]) {
			  	uses[key] = val.metadata.labels.uses.split("_");
		    } else {
			  	$.each(val.metadata.labels.uses.split("_"), function(ix, use) { insertUse(key, use); });
		    }
		  }
    });
	});

	var req2 = $.getJSON("/api/v1/replicationcontrollers?labelSelector=visualize%3Dtrue", function( data ) {
		controllers = data;
		$.each(data.items, function(key, val) { val.type = 'replicationController'; console.log("Controller ID = " + val.metadata.name) });
	});

	var req3 = $.getJSON("/api/v1/services?labelSelector=visualize%3Dtrue", function( data ) {
		services = data;
		console.log("loadData(): Services");
		console.log(services);
		$.each(data.items, function(key, val) { val.type = 'service'; console.log("service ID = " + val.metadata.name)});
	});
	$.when(req1, req2, req3).then(function() {
		deferred.resolve();
	});
	return deferred;
}

function refresh(instance) {

	console.log("Refresh");
	pods = [];
	services = [];
	controllers = [];
	uses = {};
	groups = {};

	$.when(loadData()).then(function() {
		groupByName();
		$('#sheet').empty();
		renderGroups();
		connectControllers();

		setTimeout(function() {
			refresh(instance);
		}, 1000);
  });
}

jsPlumb.bind("ready", function() {
	var instance = jsPlumb.getInstance({
		// default drag options
		DragOptions : { cursor: 'pointer', zIndex:2000 },
		// the overlays to decorate each connection with.  note that the label overlay uses a function to generate the label text; in this
		// case it returns the 'labelText' member that we set on each connection in the 'init' method below.
		ConnectionOverlays : [
			[ "Arrow", { location:1 } ],
			//[ "Label", {
			//	location:0.1,
			//	id:"label",
			//	cssClass:"aLabel"
			//}]
		],
		Container:"flowchart-demo"
	});

	refresh(instance);
	jsPlumb.fire("jsPlumbDemoLoaded", instance);
  });
<|endoftext|>


<commit_before>(function() {
  /** Returns outer with slice(start, end) replaced by inner. */
  function splice(outer, inner, start, end) {
    return outer.slice(0, start)
      + inner
      + outer.slice(typeof end === 'undefined' ? start : end);
  }

  /** Returns true if before with one insertion at index is equal to after. */
  function singleAddition(before, after, index) {
    return before.length + 1 === after.length
      && before.slice(0, index) === after.slice(0, index)
      && before.slice(index) === after.slice(index + 1);
  }

  /** Returns true if before with one deletion at index is equal to after. */
  function singleDeletion(before, after, index) {
    return singleAddition(after, before, index);
  }

  /** Returns true if the parentheses in string are balanced. */
  function balanced(string) {
    var count = 0;
    for (var i = 0; i < string.length; ++i) {
      if (string[i] === '(') {
        ++count;
      } else if (string[i] === ')') {
        --count;
        if (count < 0) {
          return false;
        }
      }
    }
    return count === 0;
  }

  /**
   * Returns the index of the closing parenthesis in string to match an opening
   * parenthesis at start, or -1 if no matching parenthesis is found.
   */
  function match(string, start) {
    var count = 0;
    for (var i = start + 1; i < string.length; ++i) {
      if (string[i] === '(') {
        ++count;
      } else if (string[i] === ')') {
        if (count > 0) {
          --count;
        } else {
          return i;
        }
      }
    }
    return -1;
  }

  /**
   * Returns a version of after with backslashes replaced by lambdas and
   * parentheses balanced, given the index of the cursor and the previous,
   * balanced, state.
   */
  function correct(before, after, index) {
    if (singleAddition(before, after, index - 1)) {
      switch (after[index - 1]) {
      case '\\':
        return {text: splice(after, 'λ', index - 1, index), index: index};
      case '(':
        return {text: splice(after, ')', index), index: index};
      case ')':
        if (after[index] === ')') {
          return {text: before, index: index};
        } else {
          return {text: before, index: index - 1};
        }
      default:
        return {text: after, index: index};
      }
    } else if (singleDeletion(before, after, index)) {
      switch (before[index]) {
      case '(':
        var end = match(before, index);
        return {
          text: before.slice(0, index)
            + before.slice(index + 1, end)
            + before.slice(end + 1),
          index: index
        };
      case ')':
        return {text: before, index: index};
      default:
        return {text: after, index: index};
      }
    } else if (balanced(after)) {
      return {text: after.replace(/\\/g, 'λ'), index: index};
    } else {
      return {text: before, index: before.length - (after.length - index)};
    }
  }

  /**
   * Returns the value, selectionStart, and selectionEnd of textArea as an
   * object with text, start, and end keys.
   */
  function poll(textArea) {
    return {
      text: textArea.value,
      start: textArea.selectionStart,
      end: textArea.selectionEnd
    };
  }

  /**
   * Sets the value, selectionStart, and selectionEnd of textArea using the
   * text, start, and end properties of state.
   */
  function update(textArea, state) {
    textArea.value = state.text;
    textArea.selectionStart = state.start;
    textArea.selectionEnd = state.end;
  }

  /**
   * Configures textArea to replace backslashes with lambdas and keep
   * parentheses balanced.
   */
  function smart(textArea) {
    var previous = poll(textArea);
    textArea.addEventListener('input', function() {
      var current = poll(textArea);
      var corrected = correct(previous.text, current.text, current.start);
      var next = {
        text: corrected.text,
        start: corrected.index,
        end: corrected.index
      };
      previous = next;
      update(textArea, next);
    });
  }

  var input = document.getElementById('input');
  var output = document.getElementById('output');
  smart(input);
  input.addEventListener('input', function() {
    output.value = input.value;
  });
  input.readOnly = false;
})();
<commit_msg>Move string change predicates further down<commit_after>(function() {
  /** Returns outer with slice(start, end) replaced by inner. */
  function splice(outer, inner, start, end) {
    return outer.slice(0, start)
      + inner
      + outer.slice(typeof end === 'undefined' ? start : end);
  }

  /** Returns true if the parentheses in string are balanced. */
  function balanced(string) {
    var count = 0;
    for (var i = 0; i < string.length; ++i) {
      if (string[i] === '(') {
        ++count;
      } else if (string[i] === ')') {
        --count;
        if (count < 0) {
          return false;
        }
      }
    }
    return count === 0;
  }

  /**
   * Returns the index of the closing parenthesis in string to match an opening
   * parenthesis at start, or -1 if no matching parenthesis is found.
   */
  function match(string, start) {
    var count = 0;
    for (var i = start + 1; i < string.length; ++i) {
      if (string[i] === '(') {
        ++count;
      } else if (string[i] === ')') {
        if (count > 0) {
          --count;
        } else {
          return i;
        }
      }
    }
    return -1;
  }

  /** Returns true if before with one insertion at index is equal to after. */
  function singleAddition(before, after, index) {
    return before.length + 1 === after.length
      && before.slice(0, index) === after.slice(0, index)
      && before.slice(index) === after.slice(index + 1);
  }

  /** Returns true if before with one deletion at index is equal to after. */
  function singleDeletion(before, after, index) {
    return singleAddition(after, before, index);
  }

  /**
   * Returns a version of after with backslashes replaced by lambdas and
   * parentheses balanced, given the index of the cursor and the previous,
   * balanced, state.
   */
  function correct(before, after, index) {
    if (singleAddition(before, after, index - 1)) {
      switch (after[index - 1]) {
      case '\\':
        return {text: splice(after, 'λ', index - 1, index), index: index};
      case '(':
        return {text: splice(after, ')', index), index: index};
      case ')':
        if (after[index] === ')') {
          return {text: before, index: index};
        } else {
          return {text: before, index: index - 1};
        }
      default:
        return {text: after, index: index};
      }
    } else if (singleDeletion(before, after, index)) {
      switch (before[index]) {
      case '(':
        var end = match(before, index);
        return {
          text: before.slice(0, index)
            + before.slice(index + 1, end)
            + before.slice(end + 1),
          index: index
        };
      case ')':
        return {text: before, index: index};
      default:
        return {text: after, index: index};
      }
    } else if (balanced(after)) {
      return {text: after.replace(/\\/g, 'λ'), index: index};
    } else {
      return {text: before, index: before.length - (after.length - index)};
    }
  }

  /**
   * Returns the value, selectionStart, and selectionEnd of textArea as an
   * object with text, start, and end keys.
   */
  function poll(textArea) {
    return {
      text: textArea.value,
      start: textArea.selectionStart,
      end: textArea.selectionEnd
    };
  }

  /**
   * Sets the value, selectionStart, and selectionEnd of textArea using the
   * text, start, and end properties of state.
   */
  function update(textArea, state) {
    textArea.value = state.text;
    textArea.selectionStart = state.start;
    textArea.selectionEnd = state.end;
  }

  /**
   * Configures textArea to replace backslashes with lambdas and keep
   * parentheses balanced.
   */
  function smart(textArea) {
    var previous = poll(textArea);
    textArea.addEventListener('input', function() {
      var current = poll(textArea);
      var corrected = correct(previous.text, current.text, current.start);
      var next = {
        text: corrected.text,
        start: corrected.index,
        end: corrected.index
      };
      previous = next;
      update(textArea, next);
    });
  }

  var input = document.getElementById('input');
  var output = document.getElementById('output');
  smart(input);
  input.addEventListener('input', function() {
    output.value = input.value;
  });
  input.readOnly = false;
})();
<|endoftext|>


<commit_before>var bird = {
    v: 0,
    y: 0,
};

function init() {

    // Make the canvas fill the screen
    var canvas = document.getElementById('mainCanvas');
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;

    // Create the scene
    var stage  = new createjs.Stage('mainCanvas');

    // Create the bird
    bird = new createjs.Shape();
    bird.graphics.beginFill('Crimson').drawCircle(0, 0, 50);
    bird.y = window.innerHeight / 2;
    bird.x = window.innerWidth / 2;
    stage.addChild(bird);

    // createjs.Tween.get(circle).to({});
    // createjs.Tween.get(circle, {loop: true})
        // .to({x: 400}, 1000, createjs.Ease.getPowInOut(4))
        // .to({alpha: 0, y: 75}, 500, createjs.Ease.getPowInOut(2))
        // .to({alpha: 0, y: 125}, 100)
        // .to({alpha: 1, y: 100}, 500, createjs.Ease.getPowInOut(2))
        // .to({x: 100}, 800, createjs.Ease.getPowInOut(2));

    // Handle events
    stage.addEventListener("stagemousedown", handleClick);
    stage.addEventListener('tick', handleTick);

    // Enable touch gestures
    createjs.Touch.enable(stage, true);

    // Start animation
    createjs.Ticker.setFPS(60);
    createjs.Ticker.addEventListener('tick', stage);
}

function handleClick() {
    console.log("you clicked");
}

function handleTick() {
    // createjs.Tween.
    // console.log('hey');
}
<commit_msg>Cleaner data<commit_after>var game = {
    fps: 60,
    gravity: 9.8,
    stage: null,
    bird: {
        y: 0.5,
        velocity: 0,
        radius: 35,
        color: 'Crimson',
        shape: null,
    }
};

function init() {

    // Make the canvas fill the screen
    var canvas = document.getElementById('mainCanvas');
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;

    // Create the scene
    game.stage = new createjs.Stage('mainCanvas');

    // Create the bird
    var bird = game.bird;
    bird.shape = new createjs.Shape();
    bird.shape.graphics.beginFill(bird.color).drawCircle(0, 0, bird.radius);
    bird.shape.y = window.innerHeight * bird.y;
    bird.shape.x = window.innerWidth / 2;
    game.stage.addChild(bird.shape);

    // createjs.Tween.get(circle).to({});
    // createjs.Tween.get(circle, {loop: true})
        // .to({x: 400}, 1000, createjs.Ease.getPowInOut(4))
        // .to({alpha: 0, y: 75}, 500, createjs.Ease.getPowInOut(2))
        // .to({alpha: 0, y: 125}, 100)
        // .to({alpha: 1, y: 100}, 500, createjs.Ease.getPowInOut(2))
        // .to({x: 100}, 800, createjs.Ease.getPowInOut(2));

    // Handle events
    game.stage.addEventListener("stagemousedown", handleClick);
    game.stage.addEventListener('tick', handleTick);

    // Enable touch gestures
    createjs.Touch.enable(game.stage, true);

    // Start animation
    createjs.Ticker.setFPS(game.fps);
    createjs.Ticker.addEventListener('tick', game.stage);
}

function handleClick() {
    console.log("you clicked");
}

function handleTick() {
    // createjs.Tween.
    // console.log('hey');
}
<|endoftext|>


<commit_before>function isLocalStorageAvailable() {
    try {
        return 'localStorage' in window && window['localStorage'] !== null;
    } catch (e) {
        return false;
    }
}
function wipeLocalStorage() {
    if (isLocalStorageAvailable()) localStorage.clear();
}

function visualizePlayingState(state) {
    if (! state) {
        $('#play').html('Pause');
        isWorking = false;
    }
    else {
        $('#play').html('Play');
        isWorking = true;
    }
}<commit_msg>minor bugfix with button states<commit_after>function isLocalStorageAvailable() {
    try {
        return 'localStorage' in window && window['localStorage'] !== null;
    } catch (e) {
        return false;
    }
}
function wipeLocalStorage() {
    if (isLocalStorageAvailable()) localStorage.clear();
}

function visualizePlayingState(state) {
    if (! state) {
        $('#play').html('Pause');
        isWorking = true;
    }
    else {
        $('#play').html('Play');
        isWorking = false;
    }
}<|endoftext|>


<commit_before>// Housekeeping
var vinci = {}

// Get input
vinci.input = function () {
  return document.querySelector("textarea").value
}

document.querySelector("textarea").addEventListener("drop", function (event) {
  event.preventDefault()

  var file = event.dataTransfer.files[0]
    , reader = new FileReader()

  reader.addEventListener("load", function (evt) {
    document.querySelector("textarea").value = evt.target.result
  })

  reader.readAsText(file)
}, false)

// Process data
vinci.output = function (data) {
  try {
    data = JSON.parse(data)
  } catch (e) {
    data = false
  }
  return data
}

// Give output
document.querySelector("button").addEventListener("click", function () {
  var data = vinci.output(vinci.input())

  if (data) {

    var payload = []

    for (var i in data) {
      var tmp = {
        name: i
      }
      delete data[i]
      payload.push(tmp)
    }

    data.name = "Viz"
    data.children = payload

    document.querySelector("pre").innerHTML = JSON.stringify(data, null, "\t")

    var tree = d3.layout.tree().size([300,150])
      , svg = d3.select("#viz").append("svg")
                .attr("width", 640)
                .attr("height", 640)
                .append("g")
                .attr("transform", "translate(40, 0)")

      , diagonal = d3.svg.diagonal()
                    .projection(function(d) { return [d.y, d.x] })

      , nodes = tree.nodes(data)

      , links = tree.links(nodes)

      , link = svg.selectAll("pathlink")
                    .data(links).enter().append("path")
                    .attr("class", "link").attr("d", diagonal)

      , node = svg.selectAll("g.node")
                    .data(nodes).enter().append("g")
                    .attr("transform", function(d) {
                      return "translate(" + d.y + "," + d.x + ")"
                    })

      node.append("circle").attr("r", 3.5)
      node.append("text")
          .attr("dx", function(d) { return d.children ? -8 : 8 })
          .attr("dy", 3)
          .attr("text-anchor", function(d) { return d.children ? "end" : "start" })
          .text(function(d) { return d.name })
  } else {
    document.querySelector("pre").innerHTML = "Input could not be parsed as JSON"
  }
}, false)
<commit_msg>Second level recursion of the Tree layout - Needed change in svg form-factor - Now shows the second level elements, post-processed - JSON stringifier argument altered for better legibility<commit_after>// Housekeeping
var vinci = {}

// Get input
vinci.input = function () {
  return document.querySelector("textarea").value
}

document.querySelector("textarea").addEventListener("drop", function (event) {
  event.preventDefault()

  var file = event.dataTransfer.files[0]
    , reader = new FileReader()

  reader.addEventListener("load", function (evt) {
    document.querySelector("textarea").value = evt.target.result
  })

  reader.readAsText(file)
}, false)

// Process data
vinci.output = function (data) {
  try {
    data = JSON.parse(data)
  } catch (e) {
    data = false
  }
  return data
}

// Give output
document.querySelector("button").addEventListener("click", function () {
  var data = vinci.output(vinci.input())

  if (data) {

    var payload = []

    for (var i in data) {
      var tmp = {
        name: i,
        children: (function () {
          var arr = []
          for (var j in data[i]) {
            arr.push({
              "name": (function () {
                var tmp = data[i][j].split("-")
                tmp.pop()
                return tmp.join("-")
              })()
            })
          }
          return arr
        })()
      }
      delete data[i]
      payload.push(tmp)
    }

    data.name = "Viz"
    data.children = payload

    document.querySelector("pre").innerHTML = JSON.stringify(data, null, "  ")

    var tree = d3.layout.tree().size([2000,250])
      , svg = d3.select("#viz").append("svg")
                .attr("width", 640)
                .attr("height", 2048)
                .append("g")
                .attr("transform", "translate(40, 0)")

      , diagonal = d3.svg.diagonal()
                    .projection(function(d) { return [d.y, d.x] })

      , nodes = tree.nodes(data)

      , links = tree.links(nodes)

      , link = svg.selectAll("pathlink")
                    .data(links).enter().append("path")
                    .attr("class", "link").attr("d", diagonal)

      , node = svg.selectAll("g.node")
                    .data(nodes).enter().append("g")
                    .attr("transform", function(d) {
                      return "translate(" + d.y + "," + d.x + ")"
                    })

      node.append("circle").attr("r", 3.5)
      node.append("text")
          .attr("dx", function(d) { return d.children ? -8 : 8 })
          .attr("dy", 3)
          .attr("text-anchor", function(d) { return d.children ? "end" : "start" })
          .text(function(d) { return d.name })
  } else {
    document.querySelector("pre").innerHTML = "Input could not be parsed as JSON"
  }
}, false)
<|endoftext|>


<commit_before>// ==UserScript==
// @name       Harvest Timesheets UI Improvements
// @namespace  adamalton.co.uk
// @version    0.1
// @description  Make the Harvest timesheet interface less painful
// @match      https://*.harvestapp.com/time/week*
// @copyright  2014+, Adam Alton
// ==/UserScript==

;(function(){

    //***************************************************************************************************************
    //************************************************ COLOURS ******************************************************
    //***************************************************************************************************************

    var colours = ['FFFFAA', 'FFAAFF', 'AAFFFF', 'FFAAAA', 'AAFFAA', 'AAAAFF'];
    var $rows = $("tr.week-view-entry");
    var prefixes = [];
    for(var i=0; i<$rows.length; i++){
        var $row = $rows.eq(i);
        var project = $row.find(".project").text();
        var bracketed_name = project.match(/\[.+\]/);
        var prefix = bracketed_name || project;
        prefix = prefix.length ? prefix[0] : null;
        if(prefix){
            var position = prefixes.indexOf(prefix);
            if(position == -1){
                position = prefixes.length; //the 1-indexing of length gives us the position that it will be in
                prefixes.push(prefix);
            }
            var colour = colours[position];
            while(colour === undefined){
                //if we got to the end of our list of colours then loop around
                position -= colours.length;
                colour = colours[position];
            }
            $row.css('background-color', '#' + colour);
        }
    }
})();

(function(){
    //***************************************************************************************************************
    //****************************************** KEYBOARD NAVIGATION ************************************************
    //***************************************************************************************************************
	var UP = 38, DOWN = 40, RIGHT = 39, LEFT = 37;
    var keypress = function(e){
        if([UP, DOWN].indexOf(e.keyCode) != -1){
            var $this = $(this);
            var col_num = $this.closest("td").index(); //Note this includes the column of project names
            var $row = $this.closest("tr");
            var $new_row = e.keyCode == UP ? $row.prev() : $row.next();
            $new_row.find("td").eq(col_num).find("input.js-compound-entry").focus();
            return false;
        }else if(e.keyCode == RIGHT){
            $(this).closest("td").next().find("input.js-compound-entry").focus();
            return false;
        }else if (e.keyCode == LEFT){
            $(this).closest("td").prev().find("input.js-compound-entry").focus();
            return false;
        }
    };
    $(document).on("keydown", "input.js-compound-entry", keypress);

})();
<commit_msg>Making the colours less bright.<commit_after>// ==UserScript==
// @name       Harvest Timesheets UI Improvements
// @namespace  adamalton.co.uk
// @version    0.1
// @description  Make the Harvest timesheet interface less painful
// @match      https://*.harvestapp.com/time/week*
// @copyright  2014+, Adam Alton
// ==/UserScript==

;(function(){

    //***************************************************************************************************************
    //************************************************ COLOURS ******************************************************
    //***************************************************************************************************************

    var colours = ['FFFFDD', 'FFDDFF', 'DDFFFF', 'FFDDDD', 'DDFFDD', 'DDDDFF'];
    var $rows = $("tr.wDDk-view-entry");
    var prefixes = [];
    for(var i=0; i<$rows.length; i++){
        var $row = $rows.eq(i);
        var project = $row.find(".project").text();
        var bracketed_name = project.match(/\[.+\]/);
        var prefix = bracketed_name || project;
        prefix = prefix.length ? prefix[0] : null;
        if(prefix){
            var position = prefixes.indexOf(prefix);
            if(position == -1){
                position = prefixes.length; //the 1-indexing of length gives us the position that it will be in
                prefixes.push(prefix);
            }
            var colour = colours[position];
            while(colour === undefined){
                //if we got to the end of our list of colours then loop around
                position -= colours.length;
                colour = colours[position];
            }
            $row.css('background-color', '#' + colour);
        }
    }
})();

(function(){
    //***************************************************************************************************************
    //****************************************** KEYBOARD NAVIGATION ************************************************
    //***************************************************************************************************************
	var UP = 38, DOWN = 40, RIGHT = 39, LEFT = 37;
    var keypress = function(e){
        if([UP, DOWN].indexOf(e.keyCode) != -1){
            var $this = $(this);
            var col_num = $this.closest("td").index(); //Note this includes the column of project names
            var $row = $this.closest("tr");
            var $new_row = e.keyCode == UP ? $row.prev() : $row.next();
            $new_row.find("td").eq(col_num).find("input.js-compound-entry").focus();
            return false;
        }else if(e.keyCode == RIGHT){
            $(this).closest("td").next().find("input.js-compound-entry").focus();
            return false;
        }else if (e.keyCode == LEFT){
            $(this).closest("td").prev().find("input.js-compound-entry").focus();
            return false;
        }
    };
    $(document).on("keydown", "input.js-compound-entry", keypress);

})();
<|endoftext|>