<commit_before>'use:strict';
var _ = require('underscore')
  , d3 = require('d3')
  , base = require('d3by5-base-chart')
  , barOptions = require('./barOptions')
  , labelOptions = require('./labelOptions')
  , barPositions = require('./barPositions')
  , barDimensions = require('./barDimensions')
;

module.exports = BarGraph;

/**
 * The entrypoint
 * @return {[type]} [description]
 */
function BarGraph () {

  var chart = {
    // enumish
    ANCHOR_LEFT: 'left',
    ANCHOR_RIGHT: 'right',
    ANCHOR_BOTTOM: 'bottom',
    ANCHOR_TOP: 'top',

    BARLAYOUT_STACKED: 'stacked',
    BARLAYOUT_GROUPED: 'grouped',

    LABEL_INSIDE: 'inside',
    LABEL_OUTSIDE: 'outside',
    LABEL_FIT: 'fit',         // TODO: Not in use ??!?
    LABEL_NONE: 'none',

    options: {
        padding: 2,
        anchor: 'right',
        chartClass: 'chart-bar',
        labelPosition: 'none',
        labelAlign: 'left',
        labelColor: '#000',
        valuesPosition: 'none',
        valuesAlign: 'left',
        valuesColor: '#000',
        idPrefix: 'bar-',
        barLayout: 'grouped'
    },

    init: function (selection) {

      if (arguments.length) {
        this.selection = selection;
        this.draw();
      }
      return this;
    },

    draw: function () {
      var that = this
        , positions = {}
        , dimensions = {}
      ;
      // force a value for the dataType if there is a multi dimensional dataset
      this.options.barLayout = (that.options.dataType === that.DATATYPE_MULTIDIMENSIONAL) ? this.options.barLayout || this.BARLAYOUT_GROUPED : null;

      this.maxValue = this.getMaxValue();
      this.breadthScale = this.getBreadthScale();
      this.lengthScale = this.getLengthScale();

      //
      // Create a grouped breadth scale and update the dataset
      //
      if (that.options.dataType === that.DATATYPE_MULTIDIMENSIONAL) {
        this.groupedBreadthScale = this.getGroupedBreadthScale();

        if (that.options.barLayout === this.BARLAYOUT_STACKED) {
          var lpos;
          _.each(that.options.data, function (data) {
            lpos = 0;
            data.values = _.map(data.values, function (d) {
                            lpos +=  d.values;
                            d.lpos = lpos;
                            return d;
                          });
          });
        }
      }

      positions.x = this.getBarXPos();
      positions.y = this.getBarYPos();
      dimensions.width = this.getBarWidth();
      dimensions.height = this.getBarHeight();

      this.selection.each(function() {

        var dom = d3.select(this);

        // remove old
        if (that.svg) {
          that.svg.remove();
        }

        //
        // The main svg element
        //
        that.svg = dom.append('svg')
            .attr('class', 'chart barchart')
            .attr('height', that.options.height)
            .attr('width', that.options.width);

        // The actual bars
        var bars = that.svg.selectAll('rect.chart__bar')
             .data(that.options.data)
            .enter()
            .append('g')
            .attr('transform', function (d) {
              var _x = that.isVertical() ? that.breadthScale(d.label) : that.options.margin[that.options.anchor]
                , _y = that.isVertical() ? that.options.margin.top : that.breadthScale(d.label)
              ;
              return 'translate(' + _x + ',' + _y +')';
            });

        //
        // Supply additional data if multi dimensional
        //
        if (that.options.dataType === that.DATATYPE_MULTIDIMENSIONAL) {
          bars.selectAll("rect")
            .data(function(d) {
              return d.values;
            })
          .enter().append("rect")
            .attr(dimensions)
            .attr(positions)
            .style("fill", function(d) {
              return d.color;
            });

        }
        //
        // Single dimension just sets the barse
        //
        else {
          bars.append("rect")
              .attr(dimensions)
              .attr(positions)
              .style("fill", function(d) {
                return d.color;
              });
        }

        // Draw labels if required
        if (that.options.labelPosition !== that.LABEL_NONE) {
          that.drawLabels();
        }
      });

      this.onBarLayoutChange = function () {
        this.draw();
      };

      // sets a method to allow redrawing
      this.onAnchorChange = function () {
        this.draw();
      };
    },

    /**
     * Sets the layout of the chart
     * @param  {String} value - the type of layout to use allowed values = BARLAYOUT_GROUPED | BARLAYOUT_STACKED
     * @return {Mixed}        - the value or chart
     */
    barLayout: function (value) {
      if (!arguments.length) return this.options.barLayout;

      value = String(value).toLowerCase();
      // wrong value supplied
      if (value !== this.BARLAYOUT_GROUPED &&
          value !== this.BARLAYOUT_STACKED) {
        console.error(value, 'is invalid. Only ', this.BARLAYOUT_STACKED, ' OR ', this.BARLAYOUT_GROUPED, ' allowed');
        return this;
      }
      this.options.barLayout = value;
      // redraw if the graph has been loaded
      if (typeof this.onBarLayoutChange === 'function') {
        this.onBarLayoutChange();
      }

      return this;
    },

    /**
     * Sets the direction of the graph
     * @param  {String} value - the direction used, allowed values = ANCHOR_LEFT | ANCHOR_RIGHT | ANCHOR_BOTTOM | ANCHOR_TOP
     * @return {Mixed}        - the value or chart
     */
    anchor: function (value) {
      if (!arguments.length) return this.options.anchor;

      value = String(value).toLowerCase();
      // wrong value supplied
      if (value !== this.ANCHOR_LEFT &&
          value !== this.ANCHOR_RIGHT &&
          value !== this.ANCHOR_TOP &&
          value !== this.ANCHOR_BOTTOM) {
        console.error(value, 'is invalid. Only ', this.ANCHOR_LEFT, ', ', this.ANCHOR_RIGHT, ', ', this.ANCHOR_TOP, ' or ', this.ANCHOR_BOTTOM, 'allowed');
        return this;
      }
      this.options.anchor = value;

      // redraw if the graph has been loaded
      if (typeof this.onAnchorChange === 'function') {
        this.onAnchorChange();
      }

      return this;
    },

    /**
     * Turns the labels on and off by fixin them
     * @param  {String} value - the position of labels or null (this.LABEL_INSIDE| this.LABEL_OUTSIDE | this.LABEL_FIT | this.LABEL_NONE)
     *                          labels will be positioned separately on the
     * @return {Mixed}        - the value or chart
     */
    label: function (value) {
      if (!arguments.length) return this.options.labelPosition;

      value = String(value).toLowerCase();
      // wrong value supplied
      if (value !== this.LABEL_INSIDE &&
          value !== this.LABEL_OUTSIDE &&
          value !== this.LABEL_FIT  &&
          value !== this.LABEL_NONE) {
        console.error(value, 'is invalid. Only ', this.LABEL_INSIDE, ', ', this.LABEL_OUTSIDE, ', ', this.LABEL_FIT, ' or ', this.LABEL_NONE, 'allowed');
      }
      this.options.labelPosition = value;

      // redraw if the graph has been loaded
      if (typeof this.onLabelChange === 'function') {
        this.onLabelChange();
      }
      return this;
    }
  };

  chart = _.extend(chart, barOptions);
  chart = _.extend(chart, barPositions);
  chart = _.extend(chart, barDimensions);
  chart = _.extend(chart, labelOptions);
// keep the options clean
  chart.options = _.extend(chart.options, base.options);
  base = _.omit(base, 'options');
  chart = _.extend(chart, base);
  return (chart.init());

}<commit_msg>Renamed .label to .labelPosition<commit_after>'use:strict';
var _ = require('underscore')
  , d3 = require('d3')
  , base = require('d3by5-base-chart')
  , barOptions = require('./barOptions')
  , labelOptions = require('./labelOptions')
  , barPositions = require('./barPositions')
  , barDimensions = require('./barDimensions')
;

module.exports = BarGraph;

/**
 * The entrypoint
 * @return {[type]} [description]
 */
function BarGraph () {

  var chart = {
    // enumish
    ANCHOR_LEFT: 'left',
    ANCHOR_RIGHT: 'right',
    ANCHOR_BOTTOM: 'bottom',
    ANCHOR_TOP: 'top',

    BARLAYOUT_STACKED: 'stacked',
    BARLAYOUT_GROUPED: 'grouped',

    LABEL_INSIDE: 'inside',
    LABEL_OUTSIDE: 'outside',
    LABEL_FIT: 'fit',         // TODO: Not in use ??!?
    LABEL_NONE: 'none',

    options: {
        padding: 2,
        anchor: 'right',
        chartClass: 'chart-bar',
        labelPosition: 'none',
        labelAlign: 'left',
        labelColor: '#000',
        valuesPosition: 'none',
        valuesAlign: 'left',
        valuesColor: '#000',
        idPrefix: 'bar-',
        barLayout: 'grouped'
    },

    init: function (selection) {

      if (arguments.length) {
        this.selection = selection;
        this.draw();
      }
      return this;
    },

    draw: function () {
      var that = this
        , positions = {}
        , dimensions = {}
      ;
      // force a value for the dataType if there is a multi dimensional dataset
      this.options.barLayout = (that.options.dataType === that.DATATYPE_MULTIDIMENSIONAL) ? this.options.barLayout || this.BARLAYOUT_GROUPED : null;

      this.maxValue = this.getMaxValue();
      this.breadthScale = this.getBreadthScale();
      this.lengthScale = this.getLengthScale();

      //
      // Create a grouped breadth scale and update the dataset
      //
      if (that.options.dataType === that.DATATYPE_MULTIDIMENSIONAL) {
        this.groupedBreadthScale = this.getGroupedBreadthScale();

        if (that.options.barLayout === this.BARLAYOUT_STACKED) {
          var lpos;
          _.each(that.options.data, function (data) {
            lpos = 0;
            data.values = _.map(data.values, function (d) {
                            lpos +=  d.values;
                            d.lpos = lpos;
                            return d;
                          });
          });
        }
      }

      positions.x = this.getBarXPos();
      positions.y = this.getBarYPos();
      dimensions.width = this.getBarWidth();
      dimensions.height = this.getBarHeight();

      this.selection.each(function() {

        var dom = d3.select(this);

        // remove old
        if (that.svg) {
          that.svg.remove();
        }

        //
        // The main svg element
        //
        that.svg = dom.append('svg')
            .attr('class', 'chart barchart')
            .attr('height', that.options.height)
            .attr('width', that.options.width);

        // The actual bars
        var bars = that.svg.selectAll('rect.chart__bar')
             .data(that.options.data)
            .enter()
            .append('g')
            .attr('transform', function (d) {
              var _x = that.isVertical() ? that.breadthScale(d.label) : that.options.margin[that.options.anchor]
                , _y = that.isVertical() ? that.options.margin.top : that.breadthScale(d.label)
              ;
              return 'translate(' + _x + ',' + _y +')';
            });

        //
        // Supply additional data if multi dimensional
        //
        if (that.options.dataType === that.DATATYPE_MULTIDIMENSIONAL) {
          bars.selectAll("rect")
            .data(function(d) {
              return d.values;
            })
          .enter().append("rect")
            .attr(dimensions)
            .attr(positions)
            .style("fill", function(d) {
              return d.color;
            });

        }
        //
        // Single dimension just sets the barse
        //
        else {
          bars.append("rect")
              .attr(dimensions)
              .attr(positions)
              .style("fill", function(d) {
                return d.color;
              });
        }

        // Draw labels if required
        if (that.options.labelPosition !== that.LABEL_NONE) {
          that.drawLabels();
        }
      });

      this.onBarLayoutChange = function () {
        this.draw();
      };

      // sets a method to allow redrawing
      this.onAnchorChange = function () {
        this.draw();
      };
    },

    /**
     * Sets the layout of the chart
     * @param  {String} value - the type of layout to use allowed values = BARLAYOUT_GROUPED | BARLAYOUT_STACKED
     * @return {Mixed}        - the value or chart
     */
    barLayout: function (value) {
      if (!arguments.length) return this.options.barLayout;

      value = String(value).toLowerCase();
      // wrong value supplied
      if (value !== this.BARLAYOUT_GROUPED &&
          value !== this.BARLAYOUT_STACKED) {
        console.error(value, 'is invalid. Only ', this.BARLAYOUT_STACKED, ' OR ', this.BARLAYOUT_GROUPED, ' allowed');
        return this;
      }
      this.options.barLayout = value;
      // redraw if the graph has been loaded
      if (typeof this.onBarLayoutChange === 'function') {
        this.onBarLayoutChange();
      }

      return this;
    },

    /**
     * Sets the direction of the graph
     * @param  {String} value - the direction used, allowed values = ANCHOR_LEFT | ANCHOR_RIGHT | ANCHOR_BOTTOM | ANCHOR_TOP
     * @return {Mixed}        - the value or chart
     */
    anchor: function (value) {
      if (!arguments.length) return this.options.anchor;

      value = String(value).toLowerCase();
      // wrong value supplied
      if (value !== this.ANCHOR_LEFT &&
          value !== this.ANCHOR_RIGHT &&
          value !== this.ANCHOR_TOP &&
          value !== this.ANCHOR_BOTTOM) {
        console.error(value, 'is invalid. Only ', this.ANCHOR_LEFT, ', ', this.ANCHOR_RIGHT, ', ', this.ANCHOR_TOP, ' or ', this.ANCHOR_BOTTOM, 'allowed');
        return this;
      }
      this.options.anchor = value;

      // redraw if the graph has been loaded
      if (typeof this.onAnchorChange === 'function') {
        this.onAnchorChange();
      }

      return this;
    },

    /**
     * Turns the labels on and off by fixin them
     * @param  {String} value - the position of labels or null (this.LABEL_INSIDE| this.LABEL_OUTSIDE | this.LABEL_FIT | this.LABEL_NONE)
     *                          labels will be positioned separately on the
     * @return {Mixed}        - the value or chart
     */
    labelPosition: function (value) {
      if (!arguments.length) return this.options.labelPosition;

      value = String(value).toLowerCase();
      // wrong value supplied
      if (value !== this.LABEL_INSIDE &&
          value !== this.LABEL_OUTSIDE &&
          value !== this.LABEL_FIT  &&
          value !== this.LABEL_NONE) {
        console.error(value, 'is invalid. Only ', this.LABEL_INSIDE, ', ', this.LABEL_OUTSIDE, ', ', this.LABEL_FIT, ' or ', this.LABEL_NONE, 'allowed');
      }
      this.options.labelPosition = value;

      // redraw if the graph has been loaded
      if (typeof this.onLabelChange === 'function') {
        this.onLabelChange();
      }
      return this;
    }
  };

  chart = _.extend(chart, barOptions);
  chart = _.extend(chart, barPositions);
  chart = _.extend(chart, barDimensions);
  chart = _.extend(chart, labelOptions);
// keep the options clean
  chart.options = _.extend(chart.options, base.options);
  base = _.omit(base, 'options');
  chart = _.extend(chart, base);
  return (chart.init());

}<|endoftext|>


<commit_before>/* global firebase */
import 'babel-polyfill';

import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';

import { Router, Route, useRouterHistory } from 'react-router';
import { createHashHistory } from 'history';
import { syncHistoryWithStore } from 'react-router-redux';

import reducer from './reducers/';
import { startSession, changeAuthoring } from './actions';

import AuthoringUpload from './containers/authoring-upload';
import Navigation from "./containers/navigation";
import ChallengeContainerSelector from "./containers/challenge-container-selector";

import loggerMiddleware from './middleware/gv-log';
import itsMiddleware, { initializeITSSocket } from './middleware/its-log';
import routerMiddleware from './middleware/router-history';
import stateSaveMiddleware, {authoringVersionNumber} from './middleware/state-save';
import soundsMiddleware from 'redux-sounds';
import thunk from 'redux-thunk';

import GeneticsUtils from './utilities/genetics-utils';
import urlParams from './utilities/url-params';
import uuid from 'uuid';
import { initFirebase, userAuth } from "./utilities/firebase-auth";

// trivial check for Windows as part of user agent string
if (navigator.userAgent.indexOf('Windows') >= 0)
  document.body.className += ' os-windows';

let store, history;

initFirebase.then(function (auth) {
  postAuthInitialization(auth);
}, function(err){
  console.log(err);
  postAuthInitialization(userAuth());
});

const postAuthInitialization = function (auth) {
  store = configureStore();
  const guideServer = "wss://guide.intellimedia.ncsu.edu:/guide-protocol";
  const socketPath = "/v3/socket.io";

  initializeITSSocket(guideServer, socketPath, store);
  // generate pseudo-random sessionID
  const sessionID = uuid.v4();
  loggingMetadata.userName = auth.domain_uid;
  loggingMetadata.classInfo = auth.class_info_url;
  loggingMetadata.studentId = auth.domain_uid;
  loggingMetadata.externalId = auth.externalId;
  loggingMetadata.returnUrl = auth.returnUrl;
  // start the session before syncing history, which triggers navigation
  store.dispatch(startSession(sessionID));
  history = syncHistoryWithStore(hashHistory, store);

  loadAuthoring();
};

function convertAuthoring(authoring) {
  return GeneticsUtils.convertDashAllelesObjectToABAlleles(authoring,
                          ["alleles", "baseDrake","initialDrakeCombos", "targetDrakeCombos"]);
}

// TODO: session ID and application name could be passed in via a container
// use placeholder ID for duration of session and hard-coded name for now.
let loggingMetadata = {
  applicationName: "GeniStarDev"
};

const hashHistory = useRouterHistory(createHashHistory)({ queryKey: false });

const soundsData = {
  hatchDrake: "resources/audio/BabyDragon.mp3",
  receiveCoin: "resources/audio/coin.mp3"
};
// Pre-load middleware with our sounds.
const loadedSoundsMiddleware = soundsMiddleware(soundsData);

const createStoreWithMiddleware =
  applyMiddleware(
    thunk,
    loggerMiddleware(loggingMetadata),
    itsMiddleware(loggingMetadata),
    routerMiddleware(hashHistory),
    stateSaveMiddleware(),
    loadedSoundsMiddleware
  )(createStore);

export default function configureStore(initialState) {
  return createStoreWithMiddleware(reducer, initialState, window.devToolsExtension && window.devToolsExtension());
}

const isAuthorUploadRequested = (urlParams.author === "upload");
let isAuthorUploadEnabled = isAuthorUploadRequested;  // e.g. check PRODUCTION flag

function loadAuthoring() {
  const localAuthoring = urlParams.localAuthoring || false;
  if (localAuthoring) {
    const url = `resources/authoring/${localAuthoring}.json`;
    fetch(url)
      .then(response => response.json())
      .then(handleAuthoringLoad, () => alert(`Cannot load ${url}`));
  } else {
    const db = firebase.database(),
          ref = db.ref(authoringVersionNumber + "/authoring");

    ref.once("value", function(authoringData) {
      handleAuthoringLoad(authoringData.val());
    });
  }
}

function handleCompleteUpload(authoring) {
  store.dispatch(changeAuthoring(convertAuthoring(authoring)));
  isAuthorUploadEnabled = false;
  renderApp();
}

function handleAuthoringLoad(authoring) {
  let convertedAuthoring = convertAuthoring(authoring);
  window.GV2Authoring = convertedAuthoring;
  store.dispatch(changeAuthoring(convertedAuthoring));
  renderApp();
}

function renderApp() {
  const content = isAuthorUploadEnabled
                    ? <AuthoringUpload isEnabled={isAuthorUploadEnabled}
                                        onCompleteUpload={handleCompleteUpload} />
                    :

                        <Router history={history}>
                          <Route path="/navigation" component={Navigation} />
                          <Route path="/(:level/:mission/:challenge)" component={ChallengeContainerSelector} />
                          <Route path="/(:challengeId)" component={ChallengeContainerSelector} />
                        </Router>

;
  render(
    <Provider store={store}>
      {content}
    </Provider>
  , document.getElementById("gv"));
}<commit_msg>Use staging ITS path if we are on staging deployment<commit_after>/* global firebase */
import 'babel-polyfill';

import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';

import { Router, Route, useRouterHistory } from 'react-router';
import { createHashHistory } from 'history';
import { syncHistoryWithStore } from 'react-router-redux';

import reducer from './reducers/';
import { startSession, changeAuthoring } from './actions';

import AuthoringUpload from './containers/authoring-upload';
import Navigation from "./containers/navigation";
import ChallengeContainerSelector from "./containers/challenge-container-selector";

import loggerMiddleware from './middleware/gv-log';
import itsMiddleware, { initializeITSSocket } from './middleware/its-log';
import routerMiddleware from './middleware/router-history';
import stateSaveMiddleware, {authoringVersionNumber} from './middleware/state-save';
import soundsMiddleware from 'redux-sounds';
import thunk from 'redux-thunk';

import GeneticsUtils from './utilities/genetics-utils';
import urlParams from './utilities/url-params';
import uuid from 'uuid';
import { initFirebase, userAuth } from "./utilities/firebase-auth";

// trivial check for Windows as part of user agent string
if (navigator.userAgent.indexOf('Windows') >= 0)
  document.body.className += ' os-windows';

let store, history;

initFirebase.then(function (auth) {
  postAuthInitialization(auth);
}, function(err){
  console.log(err);
  postAuthInitialization(userAuth());
});

const ITSServers = {
  production: {
    url: "wss://guide.intellimedia.ncsu.edu:/guide-protocol",
    path: "/v3/socket.io"
  },
  staging: {
    url: "wss://imediadev.csc.ncsu.edu:/guide-protocol",
    path: "/guide/v3/socket.io"
  }
};

const postAuthInitialization = function (auth) {
  store = configureStore();
  const instance = window.location.href.indexOf('/branch/staging') < 0 ? "production" : "staging";
  const ITSServer = ITSServers[instance];

  initializeITSSocket(ITSServer.url, ITSServer.path, store);

  // generate pseudo-random sessionID
  const sessionID = uuid.v4();
  loggingMetadata.userName = auth.domain_uid;
  loggingMetadata.classInfo = auth.class_info_url;
  loggingMetadata.studentId = auth.domain_uid;
  loggingMetadata.externalId = auth.externalId;
  loggingMetadata.returnUrl = auth.returnUrl;
  // start the session before syncing history, which triggers navigation
  store.dispatch(startSession(sessionID));
  history = syncHistoryWithStore(hashHistory, store);

  loadAuthoring();
};

function convertAuthoring(authoring) {
  return GeneticsUtils.convertDashAllelesObjectToABAlleles(authoring,
                          ["alleles", "baseDrake","initialDrakeCombos", "targetDrakeCombos"]);
}

// TODO: session ID and application name could be passed in via a container
// use placeholder ID for duration of session and hard-coded name for now.
let loggingMetadata = {
  applicationName: "GeniStarDev"
};

const hashHistory = useRouterHistory(createHashHistory)({ queryKey: false });

const soundsData = {
  hatchDrake: "resources/audio/BabyDragon.mp3",
  receiveCoin: "resources/audio/coin.mp3"
};
// Pre-load middleware with our sounds.
const loadedSoundsMiddleware = soundsMiddleware(soundsData);

const createStoreWithMiddleware =
  applyMiddleware(
    thunk,
    loggerMiddleware(loggingMetadata),
    itsMiddleware(loggingMetadata),
    routerMiddleware(hashHistory),
    stateSaveMiddleware(),
    loadedSoundsMiddleware
  )(createStore);

export default function configureStore(initialState) {
  return createStoreWithMiddleware(reducer, initialState, window.devToolsExtension && window.devToolsExtension());
}

const isAuthorUploadRequested = (urlParams.author === "upload");
let isAuthorUploadEnabled = isAuthorUploadRequested;  // e.g. check PRODUCTION flag

function loadAuthoring() {
  const localAuthoring = urlParams.localAuthoring || false;
  if (localAuthoring) {
    const url = `resources/authoring/${localAuthoring}.json`;
    fetch(url)
      .then(response => response.json())
      .then(handleAuthoringLoad, () => alert(`Cannot load ${url}`));
  } else {
    const db = firebase.database(),
          ref = db.ref(authoringVersionNumber + "/authoring");

    ref.once("value", function(authoringData) {
      handleAuthoringLoad(authoringData.val());
    });
  }
}

function handleCompleteUpload(authoring) {
  store.dispatch(changeAuthoring(convertAuthoring(authoring)));
  isAuthorUploadEnabled = false;
  renderApp();
}

function handleAuthoringLoad(authoring) {
  let convertedAuthoring = convertAuthoring(authoring);
  window.GV2Authoring = convertedAuthoring;
  store.dispatch(changeAuthoring(convertedAuthoring));
  renderApp();
}

function renderApp() {
  const content = isAuthorUploadEnabled
                    ? <AuthoringUpload isEnabled={isAuthorUploadEnabled}
                                        onCompleteUpload={handleCompleteUpload} />
                    :

                        <Router history={history}>
                          <Route path="/navigation" component={Navigation} />
                          <Route path="/(:level/:mission/:challenge)" component={ChallengeContainerSelector} />
                          <Route path="/(:challengeId)" component={ChallengeContainerSelector} />
                        </Router>

;
  render(
    <Provider store={store}>
      {content}
    </Provider>
  , document.getElementById("gv"));
}<|endoftext|>


<commit_before>'use strict';

function resizeIframe(iframe) {
  iframe.style.height = iframe.contentWindow.document.body.scrollHeight + 'px';
}

(function() {

  document.getElementById('navToggle').addEventListener('click', function() {
    var book = document.querySelector('.book');
    book.classList.toggle('with-nav');
    localStorage.setItem('withNav', Number(book.classList.contains('with-nav')));
  });

  // This is a hacky way to highlight the current active tab. This should
  // probably be done in the template generation, but this is way easier.
  var pathname = window.location.pathname;
  var file = pathname.substr(pathname.lastIndexOf('/') + 1);
  var navItem = document.querySelector('.nav-top .item a[href="' + file + '"]');
  if (navItem) {
    navItem.classList.add('active');
  }

  var mainNavItem = document.querySelector('nav a[href^="' + (file.split('_')[0] || 'index.html') + '"]');
  if (mainNavItem) {
    mainNavItem.classList.add('active');
  }

})();

document.addEventListener('DOMContentLoaded', function() {
  document.querySelector('.book').classList.toggle(
    'with-nav',
    Number(localStorage.getItem('withNav'))
  );
});

// Marking as loaded triggers tabzilla fade-in
window.addEventListener('load', function() {
  document.body.classList.add('loaded');
});

// Demos that dynamically make the page grow require extra effort in sizing the iframe
// These demos should trigger a custom event on the parent:
// if (window.parent !== window) { window.parent.document.body.dispatchEvent(new CustomEvent('iframeresize')); }
(function(iframe)  {
  var callback = function () {
    resizeIframe(iframe);
  };

  if(iframe) {
    document.body.addEventListener('iframeresize', callback);
    iframe.addEventListener('load', callback);
  }
})(document.querySelector('iframe'));

// Launch demos in new window when image is clicked
(function(launchIcon) {
  if(launchIcon) {
      launchIcon.addEventListener('click', function(e) {
        e.preventDefault();
        window.open(launchIcon.getAttribute('data-href'));
      });
  }
})(document.querySelector('.demo-launch'));
<commit_msg>reverse philosophy as per @darkwing's suggestion<commit_after>'use strict';

function resizeIframe(iframe) {
  iframe.style.height = iframe.contentWindow.document.body.scrollHeight + 'px';
}

(function() {

  document.getElementById('navToggle').addEventListener('click', function() {
    var book = document.querySelector('.book');
    localStorage.setItem('hideNav', 1-Number(book.classList.toggle('with-nav')));
  });

  // This is a hacky way to highlight the current active tab. This should
  // probably be done in the template generation, but this is way easier.
  var pathname = window.location.pathname;
  var file = pathname.substr(pathname.lastIndexOf('/') + 1);
  var navItem = document.querySelector('.nav-top .item a[href="' + file + '"]');
  if (navItem) {
    navItem.classList.add('active');
  }

  var mainNavItem = document.querySelector('nav a[href^="' + (file.split('_')[0] || 'index.html') + '"]');
  if (mainNavItem) {
    mainNavItem.classList.add('active');
  }

})();

document.addEventListener('DOMContentLoaded', function() {
  document.querySelector('.book').classList.toggle(
    'with-nav',
    1-Number(localStorage.getItem('hideNav'))
  );
});

// Marking as loaded triggers tabzilla fade-in
window.addEventListener('load', function() {
  document.body.classList.add('loaded');
});

// Demos that dynamically make the page grow require extra effort in sizing the iframe
// These demos should trigger a custom event on the parent:
// if (window.parent !== window) { window.parent.document.body.dispatchEvent(new CustomEvent('iframeresize')); }
(function(iframe)  {
  var callback = function () {
    resizeIframe(iframe);
  };

  if(iframe) {
    document.body.addEventListener('iframeresize', callback);
    iframe.addEventListener('load', callback);
  }
})(document.querySelector('iframe'));

// Launch demos in new window when image is clicked
(function(launchIcon) {
  if(launchIcon) {
      launchIcon.addEventListener('click', function(e) {
        e.preventDefault();
        window.open(launchIcon.getAttribute('data-href'));
      });
  }
})(document.querySelector('.demo-launch'));
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2022 Google LLC
 *
 * Use of this source code is governed by a BSD-style
 * license that can be found in the LICENSE file or at
 * https://developers.google.com/open-source/licenses/bsd
 */
import {Xen} from './Xen/xen-async.js';
import {DragDrop} from './drag-drop.js';
import {IconsCss} from './Material/material-icon-font/icons.css.js';
const {assign} = Object;

export class ContainerLayout extends DragDrop {
  static get observedAttributes() {
    return [
      'selected',
      'rects',
      // The color of the boxer.
      'color'
    ];
  }
  _didMount() {
    this.boxer = this._dom.$('[boxer]');
    document.addEventListener('keydown', event => this.onKeydown(event));
  }
  update() {
    this.updateGeometry();
  }
  onKeydown(event) {
    if (!this.hasActiveInput()) {
      // Delete the selected node or edge when pressing the "backspace" or "delete" key .
      if (event.key === 'Backspace' || event.key === 'Delete') {
        this.deleteAction(this.target);
      }
    }
  }
  hasActiveInput() {
    const active = this.getActiveElement(document);
    return active?.contentEditable
      || ['INPUT', 'SELECT', 'TEXTAREA'].includes(active?.tagName)
      ;
  }
  getActiveElement({activeElement}) {
    return activeElement?.shadowRoot ? this.getActiveElement(activeElement.shadowRoot) : activeElement;
  }
  render({color}) {
    const styleOverrides = `
      [edge] {
        border-color: ${color}
      }
      [corner] {
        border-color: ${color}
      }
    `;
    return {
      styleOverrides
    };
  }
  getTargetKey(target) {
    return target?.getAttribute('particle');
  }
  deleteAction(target) {
    if (target) {
      this.key = this.getTargetKey(target);
      this.fire('delete');
    }
  }
  onSlotChange() {
    this.updateGeometry();
  }
  updateGeometry() {
    this.updateSelectionAndPositions(this.selected, this.rects);
  }
  updateSelectionAndPositions(selected, rects) {
    rects?.forEach(({id, position}) => this.position(id, position));
    this.select(null);
    selected?.forEach(id => {
      if (this.getChildById(id)) {
        this.select(id);
      }
    });
  }
  position(id, position) {
    if (position == null) {
      // Set initial position to (16, 16);
      const target = this.getChildById(id);
      if (target) {
        if (!this.getRect(target)) {
          const rect = {l: 16, t: 16, w: 240, h: 180};
          this.setBoxStyle(target, rect);
          // this.firePosition(target);
        }
      }
    } else {
      const child = this.getChildById(id);
      if (child && position) {
        this.setBoxStyle(child, position);
      }
    }
  }
  select(id) {
    this.target = this.getChildById(id);
    // TODO(mariakleiner): it is possible that the `target` hasn't rendered yet and will remain unselected.
    this.rect = this.target && this.getRect(this.target);
    this.restyleSelection();
    this.updateOrders(this.target);
  }
  firePosition(target) {
    this.key = this.getTargetKey(target);
    this.value = null;
    if (target) {
      const {l, t, w, h} = this.getRect(target);
      this.value = {l, t, w, h};
    }
    this.fire('position');
  }
  updateOrders(target) {
    const particleDivs = this.querySelectorAll('[particle]');
    particleDivs.forEach(div => {
      if (!this.rects?.find(rect => rect.id === div.id)) {
        div.style.zIndex = 98;
      } else {
        div.style.zIndex = (div === target ? 100 : 99);
      }
    });
  }
  // deselect when clicking empty backgroud
  onContainerDown(e) {
    this.select(null);
    this.firePosition(null);
  }
  //
  // implement drag-drop handlers
  doDown(e) {
    e.stopPropagation();
    // dom target
    const attrs = e.target.attributes;
    const edges = ['top', 'right', 'bottom', 'left'];
    const from = edges.map(e => attrs[e]?.name).join(':');
    if (from === ':::') {
      // component target
      this.dragKind = 'move';
      this.target = this.getEventTarget(e);
    } else {
      // resize target
      this.dragKind = 'resize';
      this.dragFrom = from;
    }
    this.rect = this.target && this.getRect(this.target);
    this.restyleSelection();
    this.updateOrders(this.target);
    // This is to select the node right away when pointer is down.
    this.firePosition(this.target);
    // TODO(sjmiles): hack to allow dragging only from title bar
    if (from === ':::') {
      const t0 = e.composedPath?.()?.[0];
      //console.log(t0 && t0.attributes.title);
      if (t0 && !t0.attributes.title) {
        return false;
      }
    }
}
  doMove(dx, dy) {
    // grid-snap
    const snap = rect => DragDrop.snap(rect, 16);
    // perform drag operation
    const dragRect = this.doDrag(snap(this.rect), dx, dy, this.dragKind, this.dragFrom);
    // install output rectangle
    this.setBoxStyle(this.target, snap(dragRect));
    requestAnimationFrame(() => this.setBoxStyle(this.boxer, this.getRect(this.target)));
  }
  doDrag({l, t, w, h}, dx, dy, dragKind, dragFrom) {
    if (dragKind === 'move') {
      return {l: l+dx, t: t+dy, w, h};
    } else if (dragKind === 'resize') {
      if (dragFrom.includes('top')) {
        t = t+dy;
        h = h-dy;
      }
      if (dragFrom.includes('bottom')) {
        h = h+dy;
      }
      if (dragFrom.includes('left')) {
        l = l+dx;
        w = w-dx;
      }
      if (dragFrom.includes('right')) {
        w = w+dx;
      }
    }
    return {l, t, w, h};
  }
  doUp() {
    if (this.target) {
      this.firePosition(this.target);
    }
  }
  //
  sanitizeId(id) {
    return id?.replace(/[)(:]/g, '_');
  }
  getChildById(id) {
    return this.querySelector(`#${this.sanitizeId(id)}`);
  }
  getEventTarget(e) {
    const p = e.composedPath();
    const i = p.indexOf(e.currentTarget);
    return p[i-1];
  }
  getRect(elt) {
    const local = elt.getBoundingClientRect();
    // it's possible elt has lost it's parent (temporarily)
    const parentRect = elt.offsetParent?.getBoundingClientRect() ?? {x: 0, y: 0};
    const scrollTop = elt.offsetParent?.scrollTop || 0;
    const scrollLeft = elt.offsetParent?.scrollLeft || 0;
    return {
      l: local.x - parentRect.x + scrollLeft,
      t: local.y - parentRect.y + scrollTop,
      w: local.width, h: local.height
    };
  }
  setBoxStyle(elt, {l, t, w, h}) {
    [l, t, w, h] = [l, t, w, h].map(Math.round);
    assign(elt.style, {
      transform: `translate(${l}px, ${t}px)`,
      width: `${!(w>0) ? 0 : w}px`,
      height: `${!(h>0) ? 0 : h}px`
    });
  }
  restyleSelection() {
    this.boxer.hidden = !this.target;
    if (this.target) {
      this.setBoxStyle(this.boxer, this.getRect(this.target));
    }
  }
  get template() {
    return Xen.Template.html`

  <slot on-pointerdown="onDown" on-pointerup="onUp" on-slotchange="onSlotChange"></slot>

`;
  }
}

customElements.define('container-layout', ContainerLayout);
<commit_msg>Minor tweaks to container layout (#144)<commit_after>/**
 * @license
 * Copyright 2022 Google LLC
 *
 * Use of this source code is governed by a BSD-style
 * license that can be found in the LICENSE file or at
 * https://developers.google.com/open-source/licenses/bsd
 */
import {Xen} from './Xen/xen-async.js';
import {DragDrop} from './drag-drop.js';
import {IconsCss} from './Material/material-icon-font/icons.css.js';
const {assign} = Object;

export class ContainerLayout extends DragDrop {
  static get observedAttributes() {
    return [
      'selected',
      'rects',
      // The color of the boxer.
      'color'
    ];
  }
  _didMount() {
    this.boxer = this._dom.$('[boxer]');
    document.addEventListener('keydown', event => this.onKeydown(event));
  }
  update(inputs, state) {
    // De-dedup inputs to get rid of UI flashes (caused by rects being rendered
    // in old positions right after being moved to a new position).
    const inputsJson = JSON.stringify(inputs);
    if (state.inputsJson !== inputsJson) {
      state.inputsJson = inputsJson;
      this.updateGeometry();
    }
  }
  onKeydown(event) {
    if (!this.hasActiveInput()) {
      // Delete the selected node or edge when pressing the "backspace" or "delete" key .
      if (event.key === 'Backspace' || event.key === 'Delete') {
        this.deleteAction(this.target);
      }
    }
  }
  hasActiveInput() {
    const active = this.getActiveElement(document);
    return active?.contentEditable
      || ['INPUT', 'SELECT', 'TEXTAREA'].includes(active?.tagName)
      ;
  }
  getActiveElement({activeElement}) {
    return activeElement?.shadowRoot ? this.getActiveElement(activeElement.shadowRoot) : activeElement;
  }
  render({color}) {
    const styleOverrides = `
      [edge] {
        border-color: ${color}
      }
      [corner] {
        border-color: ${color}
      }
    `;
    return {
      styleOverrides
    };
  }
  getTargetKey(target) {
    return target?.getAttribute('particle');
  }
  deleteAction(target) {
    if (target) {
      this.key = this.getTargetKey(target);
      this.fire('delete');
    }
  }
  onSlotChange() {
    this.updateGeometry();
  }
  updateGeometry() {
    this.updateSelectionAndPositions(this.selected, this.rects);
  }
  updateSelectionAndPositions(selected, rects) {
    rects?.forEach(({id, position}) => this.position(id, position));
    this.select(null);
    selected?.forEach(id => {
      if (this.getChildById(id)) {
        this.select(id);
      }
    });
  }
  position(id, position) {
    if (position == null) {
      // Set initial position to (16, 16);
      const target = this.getChildById(id);
      if (target) {
        const rect = this.getRect(target);
        if (rect.l === 0 && rect.t === 0) {
          rect.l = 16;
          rect.t = 16;
        }
        this.setBoxStyle(target, rect);
        this.firePosition(target);
      }
    } else {
      const child = this.getChildById(id);
      if (child && position) {
        this.setBoxStyle(child, position);
      }
    }
  }
  select(id) {
    this.target = this.getChildById(id);
    // TODO(mariakleiner): it is possible that the `target` hasn't rendered yet and will remain unselected.
    this.rect = this.target && this.getRect(this.target);
    this.restyleSelection();
    this.updateOrders(this.target);
  }
  firePosition(target) {
    this.key = this.getTargetKey(target);
    this.value = null;
    if (target) {
      const {l, t, w, h} = this.getRect(target);
      this.value = {l, t, w, h};
    }
    this.fire('position');
  }
  updateOrders(target) {
    const particleDivs = this.querySelectorAll('[particle]');
    particleDivs.forEach(div => {
      if (!this.rects?.find(rect => rect.id === div.id)) {
        div.style.zIndex = 98;
      } else {
        div.style.zIndex = (div === target ? 100 : 99);
      }
    });
  }
  // deselect when clicking empty backgroud
  onContainerDown(e) {
    this.select(null);
    this.firePosition(null);
  }
  //
  // implement drag-drop handlers
  doDown(e) {
    e.stopPropagation();
    // dom target
    const attrs = e.target.attributes;
    const edges = ['top', 'right', 'bottom', 'left'];
    const from = edges.map(e => attrs[e]?.name).join(':');
    if (from === ':::') {
      // component target
      this.dragKind = 'move';
      this.target = this.getEventTarget(e);
    } else {
      // resize target
      this.dragKind = 'resize';
      this.dragFrom = from;
    }
    this.rect = this.target && this.getRect(this.target);
    this.restyleSelection();
    this.updateOrders(this.target);
    // This is to select the node right away when pointer is down.
    this.firePosition(this.target);
    // TODO(sjmiles): hack to allow dragging only from title bar
    if (from === ':::') {
      const t0 = e.composedPath?.()?.[0];
      //console.log(t0 && t0.attributes.title);
      if (t0 && !t0.attributes.title) {
        return false;
      }
    }
}
  doMove(dx, dy) {
    // grid-snap
    const snap = rect => DragDrop.snap(rect, 16);
    // perform drag operation
    const dragRect = this.doDrag(snap(this.rect), dx, dy, this.dragKind, this.dragFrom);
    // install output rectangle
    this.setBoxStyle(this.target, snap(dragRect));
    requestAnimationFrame(() => this.setBoxStyle(this.boxer, this.getRect(this.target)));
  }
  doDrag({l, t, w, h}, dx, dy, dragKind, dragFrom) {
    if (dragKind === 'move') {
      return {l: l+dx, t: t+dy, w, h};
    } else if (dragKind === 'resize') {
      if (dragFrom.includes('top')) {
        t = t+dy;
        h = h-dy;
      }
      if (dragFrom.includes('bottom')) {
        h = h+dy;
      }
      if (dragFrom.includes('left')) {
        l = l+dx;
        w = w-dx;
      }
      if (dragFrom.includes('right')) {
        w = w+dx;
      }
    }
    return {l, t, w, h};
  }
  doUp() {
    if (this.target) {
      this.firePosition(this.target);
    }
  }
  //
  sanitizeId(id) {
    return id?.replace(/[)(:]/g, '_');
  }
  getChildById(id) {
    return this.querySelector(`#${this.sanitizeId(id)}`);
  }
  getEventTarget(e) {
    const p = e.composedPath();
    const i = p.indexOf(e.currentTarget);
    return p[i-1];
  }
  getRect(elt) {
    const local = elt.getBoundingClientRect();
    // it's possible elt has lost it's parent (temporarily)
    const parentRect = elt.offsetParent?.getBoundingClientRect() ?? {x: 0, y: 0};
    const scrollTop = elt.offsetParent?.scrollTop || 0;
    const scrollLeft = elt.offsetParent?.scrollLeft || 0;
    return {
      l: local.x - parentRect.x + scrollLeft,
      t: local.y - parentRect.y + scrollTop,
      w: local.width, h: local.height
    };
  }
  setBoxStyle(elt, {l, t, w, h}) {
    [l, t, w, h] = [l, t, w, h].map(Math.round);
    assign(elt.style, {
      transform: `translate(${l}px, ${t}px)`,
      width: `${!(w>0) ? 0 : w}px`,
      height: `${!(h>0) ? 0 : h}px`
    });
  }
  restyleSelection() {
    this.boxer.hidden = !this.target;
    if (this.target) {
      this.setBoxStyle(this.boxer, this.getRect(this.target));
    }
  }
  get template() {
    return Xen.Template.html`

  <slot on-pointerdown="onDown" on-pointerup="onUp" on-slotchange="onSlotChange"></slot>

`;
  }
}

customElements.define('container-layout', ContainerLayout);
<|endoftext|>


<commit_before>/**
 * P2P-Bomberman game lounge class.
 * Handles pre-game management.
 *
 * Author: Markus Konrad <post@mkonrad.net>
 */

var postGameStartCallback = null;

function LoungeClass(mode) {
	this._gameMode 		= mode;
	this._p2pComm 		= null;
	this._playerManager = null;
	this._ownPlayer		= null;
}

/**
 * Set up the game lounge.
 * If in MP mode, this function requires a <joinId> to join to. If
 * <joinId> is 0, we will create a new MP game.
 */
LoungeClass.prototype.setup = function(joinId) {
	console.log('Setting up lounge in mode ' + this._gameMode + ' and join id ' + joinId);

	postGameStartCallback = {obj: this, fn: this._startGame};

	// show necessary elements
	if (this._gameMode === GameModeSinglePlayer) {
		this._setupSP();
	} else {
		this._setupMP(joinId);
	}
}

/**
 * Make special setup for singleplayer mode
 */
LoungeClass.prototype._setupSP = function() {
	// show the necessary divs
	$('#singleplayer_conf').show();

	// bind handlers
	$('#singleplayer_start_btn').click(function() {
		$('#lounge').hide();
		init('game');
	}.bind(this));
}

/**
 * Make special setup for multiplayer mode
 */
LoungeClass.prototype._setupMP = function(joinId) {
	// show the necessary divs
	$('#multiplayer_conf').show();
	$('#playerlist').show();
	$('#name').attr('disabled');

	// bind handlers
	$('#name').change(function() {
		this._nameChanged($('#name').val());
	}.bind(this));
	$('#ready').change(function() {
		this._statusChanged($('#ready:checked').val());
	}.bind(this));

	// set up player manager
	this._playerManager = new PlayerManagerClass();

	// set up P2P comm.
	this._p2pComm = new P2PCommClass();
	this._p2pComm.setup();

	$('#player_conn_status').text('receiving peer id...');
	this._p2pComm.createPeer(function(id){
		this.player_id = id;
		$('#player_id').text(this.player_id);	// set the player id when we got it from the server.
		$('#player_conn_status').text('awaiting connections');
		$('#player_conn_status').removeClass('status_unknown').addClass('ok');

		this._postConnectionSetup();

		if (joinId !== 0) {	// join a player
			this._p2pComm.joinPeer(joinId);
		}
	}.bind(this), function(err) {
		$('#player_conn_status').text('oops!');
		$('#player_conn_status').removeClass('status_unknown').addClass('not_ok');
	}.bind(this));
}

/**
 * Start the game (depending on game mode).
 */
LoungeClass.prototype._startGame = function() {
	// create the game object
    game = new GameClass(this._gameMode);

    // set up the game depending on game mode
	if (this._gameMode === GameModeSinglePlayer) {
		game.setup(null, null);
	} else {
		game.setup(this._playerManager, this._p2pComm);
	}

	// show/hide elements and start the game
	$('#main > h1').hide();
    game.startGame();
    $('#game').show();
}

LoungeClass.prototype._joiningPeer = function(peerId) {
	$('#player_conn_status').text('joining ' + peerId + '...');
}

LoungeClass.prototype._joinedPeer = function(peerId) {
	$('#player_conn_status').text('awaiting connections');
	this._sendOwnStatus(peerId);
}

LoungeClass.prototype._errorJoiningPeer = function(err) {
	$('#player_conn_status').text('oops! error joining!');
	$('#player_conn_status').removeClass('status_unknown').addClass('not_ok');
}

LoungeClass.prototype._postConnectionSetup = function() {
	// create our local player object
	this._ownPlayer = new PlayerClass(PlayerTypeLocalKeyboardArrows);
	var playerId = this._p2pComm.getPeerId();
	var playerName = 'player_' + playerId;
	this._ownPlayer.setId(playerId).setName(playerName);	// set its properties

	// add our player to the player manager
	this._playerManager.addPlayer(this._ownPlayer);

	// set the form values
	$('#name').val(playerName);
	$('#name').removeAttr('disabled');

	// set the p2p event handlers
	this._p2pComm.setConnEstablishingHandler(this, this._joiningPeer, this._joinedPeer, this._errorJoiningPeer);
	this._p2pComm.setConnOpenedHandler(this, this._playerConnected);
	this._p2pComm.setConnClosedHandler(this, this._playerDisconnected);
	this._p2pComm.setMsgHandler(MsgTypePlayerMetaData, this, this._receivedPlayerMetaData);

	// add our player to the list
	this._addPlayerToList(playerId, playerName, this._ownPlayer.getColor());
}

LoungeClass.prototype._addPlayerToList = function(id, playerName, playerColor) {
	var playerNameField = $('#playerlist_id_' + id);

	// create a new remote player object
	if (playerColor === null) {	// it must be a remote player
		if (this._playerManager.playerExists(id)) {
			this._playerManager.removePlayer(id);
			if (playerNameField) playerNameField.detach();
		}

		// create a new remote player and set its properties
		var remotePlayer = new PlayerClass(PlayerTypeRemote);
		remotePlayer.setId(id).setName(playerName);
		playerColor = PlayerColors[this._playerManager.getPlayers().length];	// set a color depending on the index
		remotePlayer.setColor(playerColor);
		this._p2pComm.setMsgHandler(MsgTypePlayerPos, this._ownPlayer, this._ownPlayer.receivePos);	// set a handler p2p event "player position"
		this._playerManager.addPlayer(remotePlayer);	// add it to the player manager
	}

	// create the new element
	var list = $('#playerlist > ul');
	var htmlId = 'playerlist_id_' + id;
	var elem = '
    ' + playerName + '
';
	list.append(elem);
}

LoungeClass.prototype._updatePlayerList = function(id, playerName, status) {
	// update player data in player manager
	this._playerManager.getPlayer(id).setName(playerName).setStatus(status);

	// check everybody's status
	var players = this._playerManager.getPlayers();
	var numReady = 0;
	var numPlayers = players.length;
	for (var i = 0; i < numPlayers; i++) {
		if (players[i].getStatus() === PlayerStatusReady) {
			numReady++;
		}
	}

	if (numReady === numPlayers) {	// everybody's ready!!
		$('#lounge').hide();
		init('game');				// so let's start the game
	}

	// update html
	var elem = $('#playerlist_id_' + id);
	var children = elem.children();
	elem.html(' ' + playerName).prepend(children);

	if (status === PlayerStatusNotReady) {
		elem.removeClass('ok').addClass('not_ok');
	} else if (status === PlayerStatusReady) {
		elem.removeClass('not_ok').addClass('ok');
	}
}

LoungeClass.prototype._nameChanged = function(v) {
	this._ownPlayer.setName(v);
	this._sendOwnStatus(0);	// to all
	this._updatePlayerList(this._ownPlayer.getId(), this._ownPlayer.getName(), this._ownPlayer.getStatus());
}

LoungeClass.prototype._statusChanged = function(v) {
	var status = PlayerStatusNotReady;

	if (v === 'ready') {
		status = PlayerStatusReady;
	}
	this._ownPlayer.setStatus(status);
	this._sendOwnStatus(0);	// to all
	this._updatePlayerList(this._ownPlayer.getId(), this._ownPlayer.getName(), this._ownPlayer.getStatus());
}

LoungeClass.prototype._receivedPlayerMetaData = function(conn, msg) {
	if (this._playerManager.playerExists(msg.id)) {	// we already know this player
		this._updatePlayerList(msg.id, msg.name, msg.status);
	} else {	// a new player connected!
		if (this._playerManager.getPlayers().length >= Conf.maxNumPlayers) {	// we already have enough players
			console.log('too many players - closing connection!');
			this._p2pComm.disconnectFromPeer(msg.id);
		} else {	// add this player to the list and send hin some information
			this._p2pComm.sendKnownPeers(msg.id);
			this._sendOwnStatus(msg.id);
			this._addPlayerToList(msg.id, msg.name, null);
		}
	}
}

LoungeClass.prototype._sendOwnStatus = function(receiverId) {
	// console.log('Sending own status: ' + this._ownPlayer + ' to peer ' + receiverId);
	this._p2pComm.sendPlayerMetaData(receiverId, this._ownPlayer.getId(), this._ownPlayer.getName(), this._ownPlayer.getStatus());
}

LoungeClass.prototype._playerConnected = function(peerId) {
	console.log('player connected: ' + peerId);

	this._sendOwnStatus(peerId);
}

LoungeClass.prototype._playerDisconnected = function(peerId) {
	console.log('player disconnected: ' + peerId);

	// remove the player from the list
	var playerNameField = $('#playerlist_id_' + peerId);
	if (this._playerManager.playerExists(peerId)) {
		this._playerManager.removePlayer(peerId);
		if (playerNameField) playerNameField.detach();
	}
}
<commit_msg>also removing player from view now (fixes issue #3)<commit_after>/**
 * P2P-Bomberman game lounge class.
 * Handles pre-game management.
 *
 * Author: Markus Konrad <post@mkonrad.net>
 */

var postGameStartCallback = null;

function LoungeClass(mode) {
	this._gameMode 		= mode;
	this._p2pComm 		= null;
	this._playerManager = null;
	this._ownPlayer		= null;
}

/**
 * Set up the game lounge.
 * If in MP mode, this function requires a <joinId> to join to. If
 * <joinId> is 0, we will create a new MP game.
 */
LoungeClass.prototype.setup = function(joinId) {
	console.log('Setting up lounge in mode ' + this._gameMode + ' and join id ' + joinId);

	postGameStartCallback = {obj: this, fn: this._startGame};

	// show necessary elements
	if (this._gameMode === GameModeSinglePlayer) {
		this._setupSP();
	} else {
		this._setupMP(joinId);
	}
}

/**
 * Make special setup for singleplayer mode
 */
LoungeClass.prototype._setupSP = function() {
	// show the necessary divs
	$('#singleplayer_conf').show();

	// bind handlers
	$('#singleplayer_start_btn').click(function() {
		$('#lounge').hide();
		init('game');
	}.bind(this));
}

/**
 * Make special setup for multiplayer mode
 */
LoungeClass.prototype._setupMP = function(joinId) {
	// show the necessary divs
	$('#multiplayer_conf').show();
	$('#playerlist').show();
	$('#name').attr('disabled');

	// bind handlers
	$('#name').change(function() {
		this._nameChanged($('#name').val());
	}.bind(this));
	$('#ready').change(function() {
		this._statusChanged($('#ready:checked').val());
	}.bind(this));

	// set up player manager
	this._playerManager = new PlayerManagerClass();

	// set up P2P comm.
	this._p2pComm = new P2PCommClass();
	this._p2pComm.setup();

	$('#player_conn_status').text('receiving peer id...');
	this._p2pComm.createPeer(function(id){
		this.player_id = id;
		$('#player_id').text(this.player_id);	// set the player id when we got it from the server.
		$('#player_conn_status').text('awaiting connections');
		$('#player_conn_status').removeClass('status_unknown').addClass('ok');

		this._postConnectionSetup();

		if (joinId !== 0) {	// join a player
			this._p2pComm.joinPeer(joinId);
		}
	}.bind(this), function(err) {
		$('#player_conn_status').text('oops!');
		$('#player_conn_status').removeClass('status_unknown').addClass('not_ok');
	}.bind(this));
}

/**
 * Start the game (depending on game mode).
 */
LoungeClass.prototype._startGame = function() {
	// create the game object
    game = new GameClass(this._gameMode);

    // set up the game depending on game mode
	if (this._gameMode === GameModeSinglePlayer) {
		game.setup(null, null);
	} else {
		game.setup(this._playerManager, this._p2pComm);
	}

	// show/hide elements and start the game
	$('#main > h1').hide();
    game.startGame();
    $('#game').show();
}

LoungeClass.prototype._joiningPeer = function(peerId) {
	$('#player_conn_status').text('joining ' + peerId + '...');
}

LoungeClass.prototype._joinedPeer = function(peerId) {
	$('#player_conn_status').text('awaiting connections');
	this._sendOwnStatus(peerId);
}

LoungeClass.prototype._errorJoiningPeer = function(err) {
	$('#player_conn_status').text('oops! error joining!');
	$('#player_conn_status').removeClass('status_unknown').addClass('not_ok');
}

LoungeClass.prototype._postConnectionSetup = function() {
	// create our local player object
	this._ownPlayer = new PlayerClass(PlayerTypeLocalKeyboardArrows);
	var playerId = this._p2pComm.getPeerId();
	var playerName = 'player_' + playerId;
	this._ownPlayer.setId(playerId).setName(playerName);	// set its properties

	// add our player to the player manager
	this._playerManager.addPlayer(this._ownPlayer);

	// set the form values
	$('#name').val(playerName);
	$('#name').removeAttr('disabled');

	// set the p2p event handlers
	this._p2pComm.setConnEstablishingHandler(this, this._joiningPeer, this._joinedPeer, this._errorJoiningPeer);
	this._p2pComm.setConnOpenedHandler(this, this._playerConnected);
	this._p2pComm.setConnClosedHandler(this, this._playerDisconnected);
	this._p2pComm.setMsgHandler(MsgTypePlayerMetaData, this, this._receivedPlayerMetaData);

	// add our player to the list
	this._addPlayerToList(playerId, playerName, this._ownPlayer.getColor());
}

LoungeClass.prototype._addPlayerToList = function(id, playerName, playerColor) {
	var playerNameField = $('#playerlist_id_' + id);

	// create a new remote player object
	if (playerColor === null) {	// it must be a remote player
		if (this._playerManager.playerExists(id)) {
			this._playerManager.removePlayer(id);
			if (playerNameField) playerNameField.detach();
		}

		// create a new remote player and set its properties
		var remotePlayer = new PlayerClass(PlayerTypeRemote);
		remotePlayer.setId(id).setName(playerName);
		playerColor = PlayerColors[this._playerManager.getPlayers().length];	// set a color depending on the index
		remotePlayer.setColor(playerColor);
		this._p2pComm.setMsgHandler(MsgTypePlayerPos, this._ownPlayer, this._ownPlayer.receivePos);	// set a handler p2p event "player position"
		this._playerManager.addPlayer(remotePlayer);	// add it to the player manager
	}

	// create the new element
	var list = $('#playerlist > ul');
	var htmlId = 'playerlist_id_' + id;
	var elem = '
    ' + playerName + '
';
	list.append(elem);
}

LoungeClass.prototype._updatePlayerList = function(id, playerName, status) {
	// update player data in player manager
	this._playerManager.getPlayer(id).setName(playerName).setStatus(status);

	// check everybody's status
	var players = this._playerManager.getPlayers();
	var numReady = 0;
	var numPlayers = players.length;
	for (var i = 0; i < numPlayers; i++) {
		if (players[i].getStatus() === PlayerStatusReady) {
			numReady++;
		}
	}

	if (numReady === numPlayers) {	// everybody's ready!!
		$('#lounge').hide();
		init('game');				// so let's start the game
	}

	// update html
	var elem = $('#playerlist_id_' + id);
	var children = elem.children();
	elem.html(' ' + playerName).prepend(children);

	if (status === PlayerStatusNotReady) {
		elem.removeClass('ok').addClass('not_ok');
	} else if (status === PlayerStatusReady) {
		elem.removeClass('not_ok').addClass('ok');
	}
}

LoungeClass.prototype._nameChanged = function(v) {
	this._ownPlayer.setName(v);
	this._sendOwnStatus(0);	// to all
	this._updatePlayerList(this._ownPlayer.getId(), this._ownPlayer.getName(), this._ownPlayer.getStatus());
}

LoungeClass.prototype._statusChanged = function(v) {
	var status = PlayerStatusNotReady;

	if (v === 'ready') {
		status = PlayerStatusReady;
	}
	this._ownPlayer.setStatus(status);
	this._sendOwnStatus(0);	// to all
	this._updatePlayerList(this._ownPlayer.getId(), this._ownPlayer.getName(), this._ownPlayer.getStatus());
}

LoungeClass.prototype._receivedPlayerMetaData = function(conn, msg) {
	if (this._playerManager.playerExists(msg.id)) {	// we already know this player
		this._updatePlayerList(msg.id, msg.name, msg.status);
	} else {	// a new player connected!
		if (this._playerManager.getPlayers().length >= Conf.maxNumPlayers) {	// we already have enough players
			console.log('too many players - closing connection!');
			this._p2pComm.disconnectFromPeer(msg.id);
		} else {	// add this player to the list and send hin some information
			this._p2pComm.sendKnownPeers(msg.id);
			this._sendOwnStatus(msg.id);
			this._addPlayerToList(msg.id, msg.name, null);
		}
	}
}

LoungeClass.prototype._sendOwnStatus = function(receiverId) {
	// console.log('Sending own status: ' + this._ownPlayer + ' to peer ' + receiverId);
	this._p2pComm.sendPlayerMetaData(receiverId, this._ownPlayer.getId(), this._ownPlayer.getName(), this._ownPlayer.getStatus());
}

LoungeClass.prototype._playerConnected = function(peerId) {
	console.log('player connected: ' + peerId);

	this._sendOwnStatus(peerId);
}

LoungeClass.prototype._playerDisconnected = function(peerId) {
	console.log('player disconnected: ' + peerId);

	// remove the player from the game
	if (this._playerManager.playerExists(peerId)) {
		if (this._playerManager._map && this._playerManager._map._view) {	// also remove the player from the view!
			this._playerManager._map._view.removeEntity(this._playerManager.getPlayer(peerId));
		}

		// remove the player from the player manager
		this._playerManager.removePlayer(peerId);

		// remove the player from the player list
		var playerNameField = $('#playerlist_id_' + peerId);
		if (playerNameField) playerNameField.detach();
	}
}
<|endoftext|>


<commit_before>$(function() {
    //Create a canvas
    var canvas = new fabric.Canvas('c', {
        isDrawingMode: false
    });

    //Drag&drop area settings
    $('[name="image"]').ezdz({
        text: '
Drop or select a picture
',
        validators: {
            maxSize: 4000000
        },
        reject: function(file, errors) {
            if (errors.mimeType) {
                alert(file.name + ' must be jpg or png.');
            }
            if (errors.maxSize) {
                alert(file.name + ' must be size:4mb max.');
            }
        }
    });

    //Canvas background color
    $('.canvas-background-color').minicolors({
        defaultValue: '#fff',
    });
    var canvasbcolor = $(".canvas-background-color").val();
    canvas.backgroundColor = canvasbcolor;
    canvas.renderAll();

    $(".canvas-background-color").on("change", function() {
        var canvasbcolor = $(".canvas-background-color").val();
        canvas.backgroundColor = canvasbcolor;
        canvas.renderAll();
    });

    //Make an image canvas background
    $("#image-background").click(function() {
        var x = $('.ezdz-dropzone img').attr('src');
        canvas.setBackgroundImage(x,
            canvas.renderAll.bind(canvas), {
                width: 500,
                height: 400,
                backgroundImageStretch: false
            });

        $("#c").css("border", "none");
        return false;
    });

    //Add an image to canvas
    $("#image-on").click(function() {
        var x2 = $('.ezdz-dropzone img').attr('src');

        fabric.Image.fromURL(x2, function(oImg) {
            canvas.add(oImg);

        }, {
            "scaleX": 0.40,
            "scaleY": 0.40
        });

        $("#c").css("border", "none");
        return false;
    });

    //Default text color
    $('.text-color').minicolors({
        defaultValue: '#333',
    });

    //Hit enter and add text
    $('#text').bind('change keyup input', function(e) {
        var key = e.which;
        if (key == 13) {

            var myText = $("#text").val();
            $("#text").val('');

            var mycolor = $(".text-color").val();
            var myfont = $("#text-font option:selected").val();

            var text = new fabric.Text(myText, {
                fontFamily: myfont,
                fontSize: 40,
                fill: mycolor,
                left: 40,
                top: 50
            });
            text.hasRotatingPoint = true;
            canvas.add(text);

            $("#selection").trigger("click");
        }
        return false;
    });

    //Defaut draw color
    $('.draw-color').minicolors({
        defaultValue: '#333',
    });

    //Click button and start to draw
    $("#draw").click(function() {
        canvas.isDrawingMode = true;
        $(".draw-color").on("change", function() {
            var mycolor = $(".draw-color").val();
            canvas.freeDrawingBrush.color = mycolor;
            return false;
        });

        canvas.renderAll();
        $(this).addClass('active');
        $("#selection").removeClass('active');
        return false;
    });

    //Click button to activate selection mode
    $("#selection").addClass('active');
    $("#selection").click(function() {
        canvas.isDrawingMode = false;
        $(this).addClass('active');
        $("#draw").removeClass('active');
        return false;
    });

    //Update brush width
    $("#range").on("change", function() {
        var rangeVal = $(this).val();
        $("#value").val(rangeVal);
        canvas.freeDrawingBrush.width = rangeVal;
        return false;
    });

    $("#value").on("keyup", function() {
        var rangeShownVal = $(this).val();
        if (rangeShownVal < 51) {
            $("#range").val(rangeShownVal);
            canvas.freeDrawingBrush.width = rangeShownVal;
        } else {
            alert("Max is 50");
            var rangeVal2 = $("#range").val();
            $("#value").val(rangeVal2);
        }
        return false;
    });

    //Delete selected object
    function deleteObjects() {
        var activeObject = canvas.getActiveObject(),
            activeGroup = canvas.getActiveGroup();
        if (activeObject) {
            if (confirm('Are you sure?')) {
                canvas.remove(activeObject);
            }
        }
    };
    $("#delete").click(function() {
        deleteObjects();
        return false;
    });

    //Clear canvas
    $("#delete-all").click(function() {
        if (confirm('Are you sure?')) {
            canvas.backgroundImage = false;
            canvas.clear();
        }
        return false;
    });

    //Save canvas as image
    $("#save").click(function() {
        window.open(canvas.toDataURL('png'));
        return false;
    });
});

//About modal
var modal = $('.modal');
var overlay = $(".overlay");
overlay.hide();
$('.open-modal').click(function() {
    modal.show();
    overlay.show();
});

$('.close-modal').click(function() {
    modal.hide();
    overlay.hide();
});

$(".overlay").click(function() {
    modal.hide();
    overlay.hide();
});

//Confirmation before closing the tab
window.onbeforeunload = function(e) {
    e = e || window.event;
    if (e) {
        e.returnValue = 'Sure?';
    }
    return 'Sure?';
};
<commit_msg>Update script.js<commit_after>$(".loader").show();
$(".page_wrapper").hide();

$(function() {
    $(".loader").fadeOut(500, function() {
        $(".page_wrapper").show();
    });

    //Create a canvas
    var canvas = new fabric.Canvas('c', {
        isDrawingMode: false
    });

    //Drag&drop area settings
    $('[name="image"]').ezdz({
        text: '
Drop or select a picture
',
        validators: {
            maxSize: 4000000
        },
        reject: function(file, errors) {
            if (errors.mimeType) {
                alert(file.name + ' must be jpg or png.');
            }
            if (errors.maxSize) {
                alert(file.name + ' must be size:4mb max.');
            }
        }
    });

    //Canvas background color
    $('.canvas-background-color').minicolors({
        defaultValue: '#fff',
    });
    var canvasbcolor = $(".canvas-background-color").val();
    canvas.backgroundColor = canvasbcolor;
    canvas.renderAll();

    $(".canvas-background-color").on("change", function() {
        var canvasbcolor = $(".canvas-background-color").val();
        canvas.backgroundColor = canvasbcolor;
        canvas.renderAll();
    });

    //Make an image canvas background
    $("#image-background").click(function() {
        var x = $('.ezdz-dropzone img').attr('src');
        canvas.setBackgroundImage(x,
            canvas.renderAll.bind(canvas), {
                width: 500,
                height: 400,
                backgroundImageStretch: false
            });

        $("#c").css("border", "none");
        return false;
    });

    //Add an image to canvas
    $("#image-on").click(function() {
        var x2 = $('.ezdz-dropzone img').attr('src');

        fabric.Image.fromURL(x2, function(oImg) {
            canvas.add(oImg);

        }, {
            "scaleX": 0.40,
            "scaleY": 0.40
        });

        $("#c").css("border", "none");
        return false;
    });

    //Default text color
    $('.text-color').minicolors({
        defaultValue: '#333',
    });

    //Hit enter and add text
    $('#text').bind('change keyup input', function(e) {
        var key = e.which;
        if (key == 13) {

            var myText = $("#text").val();
            $("#text").val('');

            var mycolor = $(".text-color").val();
            var myfont = $("#text-font option:selected").val();

            var text = new fabric.Text(myText, {
                fontFamily: myfont,
                fontSize: 40,
                fill: mycolor,
                left: 40,
                top: 50
            });
            text.hasRotatingPoint = true;
            canvas.add(text);

            $("#selection").trigger("click");
        }
        return false;
    });

    //Defaut draw color
    $('.draw-color').minicolors({
        defaultValue: '#333',
    });

    //Click button and start to draw
    $("#draw").click(function() {
        canvas.isDrawingMode = true;
        $(".draw-color").on("change", function() {
            var mycolor = $(".draw-color").val();
            canvas.freeDrawingBrush.color = mycolor;
            return false;
        });

        canvas.renderAll();
        $(this).addClass('active');
        $("#selection").removeClass('active');
        return false;
    });

    //Click button to activate selection mode
    $("#selection").addClass('active');
    $("#selection").click(function() {
        canvas.isDrawingMode = false;
        $(this).addClass('active');
        $("#draw").removeClass('active');
        return false;
    });

    //Update brush width
    $("#range").on("change", function() {
        var rangeVal = $(this).val();
        $("#value").val(rangeVal);
        canvas.freeDrawingBrush.width = rangeVal;
        return false;
    });

    $("#value").on("keyup", function() {
        var rangeShownVal = $(this).val();
        if (rangeShownVal < 51) {
            $("#range").val(rangeShownVal);
            canvas.freeDrawingBrush.width = rangeShownVal;
        } else {
            alert("Max is 50");
            var rangeVal2 = $("#range").val();
            $("#value").val(rangeVal2);
        }
        return false;
    });

    //Delete selected object
    function deleteObjects() {
        var activeObject = canvas.getActiveObject(),
            activeGroup = canvas.getActiveGroup();
        if (activeObject) {
            if (confirm('Are you sure?')) {
                canvas.remove(activeObject);
            }
        }
    };
    $("#delete").click(function() {
        deleteObjects();
        return false;
    });

    //Clear canvas
    $("#delete-all").click(function() {
        if (confirm('Are you sure?')) {
            canvas.backgroundImage = false;
            canvas.clear();
        }
        return false;
    });

    //Save canvas as image
    $("#save").click(function() {
        window.open(canvas.toDataURL('png'));
        return false;
    });
});

//About modal
var modal = $('.modal');
var overlay = $(".overlay");
overlay.hide();
$('.open-modal').click(function() {
    modal.show();
    overlay.show();
});

$('.close-modal').click(function() {
    modal.hide();
    overlay.hide();
});

$(".overlay").click(function() {
    modal.hide();
    overlay.hide();
});

//Confirmation before closing the tab
window.onbeforeunload = function(e) {
    e = e || window.event;
    if (e) {
        e.returnValue = 'Sure?';
    }
    return 'Sure?';
};
<|endoftext|>


<commit_before>function Shape(id) {
    this.id = id;
    this.paper_shape = undefined;

    this.fill_color = undefined;
    this.stroke_color = undefined;
    this.stroke_width = undefined;

    this._configJSON = function() {
        var fill_color_json = ColorsAdapter.paperColorToHex(this.fill_color);
        var stroke_color_json = ColorsAdapter.paperColorToHex(this.stroke_color);
        return {
            'shape_id': this.id,
            'fill_color': fill_color_json.hex_color,
            'fill_alpha': fill_color_json.alpha,
            'stroke_color': stroke_color_json.hex_color,
            'stroke_alpha': stroke_color_json.alpha,
            'stroke_width': this.stroke_width
        }
    };

    this._bindWrapper = function() {
        if (typeof this.paper_shape !== 'undefined') {
            this.paper_shape.shape_wrapper = this;
        }
    };

    this.updateShapeDetails = function() {
        console.log('Using default updateShapeDetails method');
    };

    this.getCenter = function() {
        if (typeof this.paper_shape !== 'undefined') {
            var bbox = this.paper_shape.bounds;
            return {
                'x': bbox.center.x,
                'y': bbox.center.y
            }
        } else {
            console.info('Shape not initialized');
        }
    };

    this.getBoundingBoxDimensions = function() {
        if (typeof this.paper_shape !== 'undefined') {
            var bbox = this.paper_shape.bounds;
            return {
                'width': bbox.width,
                'height': bbox.height
            };
        } else {
            console.info('Shape not initialized');
        }
    };

    this.contains = function(point_x, point_y) {
        var point_obj = new paper.Point(point_x, point_y);
        if (typeof this.paper_shape !== 'undefined')
            return this.paper_shape.contains(point_obj);
        else
            console.warn('Paper.js shape not initialized');
    };

    this.configure = function(shape_config) {
        if (typeof this.paper_shape !== 'undefined') {
            this.fill_color = ColorsAdapter.hexToPaperColor(shape_config.fill_color, shape_config.fill_alpha);
            this.paper_shape.setFillColor(this.fill_color);
            this.stroke_color = ColorsAdapter.hexToPaperColor(shape_config.stroke_color, shape_config.stroke_alpha);
            this.paper_shape.setStrokeColor(this.stroke_color);
            this.stroke_width = shape_config.stroke_width;
            this.paper_shape.setStrokeWidth(this.stroke_width);
        }
    };

    this.enableEvents = function(events_list) {
        if (typeof events_list === 'undefined') {
            for (var ev in this.default_events) {
                this.paper_shape[this.default_events[ev]] = true;
            }
        } else {
            for (var ev in events_list) {
                if (this.default_events.indexOf(events_list[ev]) !== -1) {
                    this.paper_shape[events_list[ev]] = true;
                } else {
                    console.warn('Unknown event ' + events_list[ev]);
                }
            }
        }
    };

    this.disableEvents = function(events_list) {
        if (typeof events_list === 'undefined') {
            for (var ev in this.default_events) {
                this.paper_shape[this.default_events[ev]] = false;
            }
        } else {
            for (var ev in events_list) {
                if (this.default_events.indexOf(events_list[ev]) !== -1) {
                    this.paper_shape[events_list[ev]] = false;
                } else {
                    console.warn('Unknown event ' + events_list[ev]);
                }
            }
        }
    };

    this._buildEvents = function() {
        this.paper_shape.on({
            mousedrag: function(event) {
                if (this[Shape.MOUSE_DRAG_EVENT] === true) {
                    document.body.style.cursor = 'move';
                    this.position = new paper.Point(
                        this.position.x + event.delta.x,
                        this.position.y + event.delta.y
                    );
                }
            },
            mouseup:  function(event) {
                this.shape_wrapper.updateShapeDetails();
                document.body.style.cursor = 'auto';
            }
        });
    };

    this.initializeEvents = function(activate_events) {
        var activate = (typeof activate_events === 'undefined') ? false : activate_events;
        this._buildEvents();
        if (activate) {
            this.enableEvents();
        } else {
            this.disableEvents();
        }
    };

    this.select = function() {
        if (typeof this.paper_shape !== 'undefined') {
            this.paper_shape.setSelected(true);
        }
    };

    this.deselect = function() {
        if (typeof this.paper_shape !== 'undefined') {
            this.paper_shape.setSelected(false);
        }
    };

    this.isSelected = function() {
        if (typeof this.paper_shape !== 'undefined') {
            return this.paper_shape.selected;
        } else {
            return undefined;
        }
    };

    this.show = function() {
        if (typeof this.paper_shape !== 'undefined') {
            this.paper_shape.setVisible(true);
        }
    };

    this.hide = function() {
        if (typeof this.paper_shape !== 'undefined') {
            this.paper_shape.setVisible(false);
        }
    };

    this.delete = function() {
        if (typeof this.paper_shape !== 'undefined') {
            this.paper_shape.remove();
        }
    };
}

Shape.MOUSE_DRAG_EVENT = 'mouse_drag_event';
Shape.prototype.default_events = [
    Shape.MOUSE_DRAG_EVENT
];

function Rectangle(id, origin_x, origin_y, width, height) {
    Shape.call(this, id);

    this.origin_x = origin_x;
    this.origin_y = origin_y;
    this.width = width;
    this.height = height;

    this.toPaperShape = function(activate_events) {
        var rect = new paper.Shape.Rectangle({
            point: [this.origin_x, this.origin_y],
            size: [this.width, this.height]
        });
        this.paper_shape = rect;
        this._bindWrapper();
        this.initializeEvents(activate_events);
    };

    this.updateShapeDetails = function() {
        this.origin_x = this.paper_shape.point.x;
        this.origin_y = this.paper_shape.point.y;
        this.width = this.paper_shape.size.width;
        this.height = this.paper_shape.size.height;
    };

    this.toJSON = function(x_offset, y_offset) {
        var shape_json = this._configJSON();
        $.extend(
            shape_json,
            {
                'origin_x': this.origin_x + x_offset,
                'origin_y': this.origin_y + y_offset,
                'width': this.width,
                'height': this.height,
                'type': 'rectangle'
            });
        return shape_json;
    };
}

Rectangle.prototype = new Shape();

function Ellipse(id, center_x, center_y, radius_x, radius_y) {
    Shape.call(this, id);

    this.center_x = center_x;
    this.center_y = center_y;
    this.radius_x = radius_x;
    this.radius_y = radius_y;

    this.toPaperShape = function(activate_events) {
        var ellipse = new paper.Shape.Ellipse({
            center: [this.center_x, this.center_y],
            radius: [this.radius_x, this.radius_y]
        });
        this.paper_shape = ellipse;
        this._bindWrapper();
        this.initializeEvents(activate_events);
    };

    this.updateShapeDetails = function() {
        this.center_x = this.paper_shape.position.x;
        this.center_y = this.paper_shape.position.y;
        this.radius_x = this.paper_shape.radius.width;
        this.radius_y = this.paper_shape.radius.height;
    };

    this.toJSON = function(x_offset, y_offset) {
        var shape_json = this._configJSON();
        $.extend(
            shape_json,
            {
                'center_x': this.center_x + x_offset,
                'center_y': this.center_y + y_offset,
                'radius_x': this.radius_x,
                'radius_y': this.radius_y,
                'type': 'ellipse'
            });
        return shape_json;
    };
}

Ellipse.prototype = new Shape();

function Circle(id, center_x, center_y, radius) {
    Shape.call(this, id);

    this.center_x = center_x;
    this.center_y = center_y;
    this.radius = radius;

    this.toPaperShape = function(activate_events) {
        var circle = new paper.Shape.Circle({
            center: [this.center_x, this.center_y],
            radius: this.radius
        });
        this.paper_shape = circle;
        this._bindWrapper();
        this.initializeEvents(activate_events);
    };

    this.updateShapeDetails = function() {
        this.center_x = this.paper_shape.position.x;
        this.center_y = this.paper_shape.position.y;
        this.radius = this.paper_shape.radius;
    };

    this.toJSON = function(x_offset, y_offset) {
        var shape_json = this._configJSON();
        $.extend(
            shape_json,
            {
                'center_x': this.center_x + x_offset,
                'center_y': this.center_y + y_offset,
                'radius': this.radius,
                'type': 'circle'
            });
        return shape_json;
    };
}

Circle.prototype = new Shape();

function Line(id, from_x, from_y, to_x, to_y) {
    Shape.call(this, id);

    this.from_x = from_x;
    this.from_y = from_y;
    this.to_x = to_x;
    this.to_y = to_y;

    this.toPaperShape = function(activate_events) {
        var line = new paper.Path.Line({
            from: [this.from_x, this.from_y],
            to: [this.to_x, this.to_y]
        });
        this.paper_shape = line;
        this._bindWrapper();
        this.initializeEvents(activate_events);
    };

    this.updateShapeDetails = function() {
        this.from_x = this.paper_shape.segments[0].point.x;
        this.from_y = this.paper_shape.segments[0].point.y;
        this.to_x = this.paper_shape.segments[1].point.x;
        this.to_y = this.paper_shape.segments[1].point.y;
    };

    this.toJSON = function(x_offset, y_offset) {
        var shape_json = this._configJSON();
        $.extend(
            shape_json,
            {
                'from_x': this.from_x + x_offset,
                'from_y': this.from_y + y_offset,
                'to_x': this.to_x + x_offset,
                'to_y': this.to_y + y_offset,
                'type': 'line'
            });
        return shape_json;
    };
}

Line.prototype = new Shape();<commit_msg>shape objects can be created with a 2D transformation that will be applied when the shape is drawn on the canvas<commit_after>function Shape(id, transform_matrix) {
    this.id = id;
    this.original_transform_matrix = transform_matrix;
    this.paper_shape = undefined;

    this.fill_color = undefined;
    this.stroke_color = undefined;
    this.stroke_width = undefined;

    this._configJSON = function() {
        var fill_color_json = ColorsAdapter.paperColorToHex(this.fill_color);
        var stroke_color_json = ColorsAdapter.paperColorToHex(this.stroke_color);
        return {
            'shape_id': this.id,
            'transform': (typeof this.original_transform_matrix !== 'undefined') ?
                this.original_transform_matrix.toJSON() : undefined,
            'fill_color': fill_color_json.hex_color,
            'fill_alpha': fill_color_json.alpha,
            'stroke_color': stroke_color_json.hex_color,
            'stroke_alpha': stroke_color_json.alpha,
            'stroke_width': this.stroke_width
        }
    };

    this._bindWrapper = function() {
        if (typeof this.paper_shape !== 'undefined') {
            this.paper_shape.shape_wrapper = this;
        }
    };

    this.updateShapeDetails = function() {
        console.log('Using default updateShapeDetails method');
    };

    this.transformShape = function(transform_matrix) {
        if (typeof this.paper_shape !== 'undefined') {
            this.paper_shape.transform(transform_matrix);
        } else {
            console.info('Shape not initialized');
        }
    };

    this.getCenter = function() {
        if (typeof this.paper_shape !== 'undefined') {
            var bbox = this.paper_shape.bounds;
            return {
                'x': bbox.center.x,
                'y': bbox.center.y
            }
        } else {
            console.info('Shape not initialized');
        }
    };

    this.getBoundingBoxDimensions = function() {
        if (typeof this.paper_shape !== 'undefined') {
            var bbox = this.paper_shape.bounds;
            return {
                'width': bbox.width,
                'height': bbox.height
            };
        } else {
            console.info('Shape not initialized');
        }
    };

    this.contains = function(point_x, point_y) {
        var point_obj = new paper.Point(point_x, point_y);
        if (typeof this.paper_shape !== 'undefined')
            return this.paper_shape.contains(point_obj);
        else
            console.info('Shape not initialized');
    };

    this.configure = function(shape_config) {
        if (typeof this.paper_shape !== 'undefined') {
            this.fill_color = ColorsAdapter.hexToPaperColor(shape_config.fill_color, shape_config.fill_alpha);
            this.paper_shape.setFillColor(this.fill_color);
            this.stroke_color = ColorsAdapter.hexToPaperColor(shape_config.stroke_color, shape_config.stroke_alpha);
            this.paper_shape.setStrokeColor(this.stroke_color);
            this.stroke_width = shape_config.stroke_width;
            this.paper_shape.setStrokeWidth(this.stroke_width);
        }
    };

    this.enableEvents = function(events_list) {
        if (typeof events_list === 'undefined') {
            for (var ev in this.default_events) {
                this.paper_shape[this.default_events[ev]] = true;
            }
        } else {
            for (var ev in events_list) {
                if (this.default_events.indexOf(events_list[ev]) !== -1) {
                    this.paper_shape[events_list[ev]] = true;
                } else {
                    console.warn('Unknown event ' + events_list[ev]);
                }
            }
        }
    };

    this.disableEvents = function(events_list) {
        if (typeof events_list === 'undefined') {
            for (var ev in this.default_events) {
                this.paper_shape[this.default_events[ev]] = false;
            }
        } else {
            for (var ev in events_list) {
                if (this.default_events.indexOf(events_list[ev]) !== -1) {
                    this.paper_shape[events_list[ev]] = false;
                } else {
                    console.warn('Unknown event ' + events_list[ev]);
                }
            }
        }
    };

    this._buildEvents = function() {
        this.paper_shape.on({
            mousedrag: function(event) {
                if (this[Shape.MOUSE_DRAG_EVENT] === true) {
                    document.body.style.cursor = 'move';
                    this.position = new paper.Point(
                        this.position.x + event.delta.x,
                        this.position.y + event.delta.y
                    );
                }
            },
            mouseup:  function(event) {
                this.shape_wrapper.updateShapeDetails();
                document.body.style.cursor = 'auto';
            }
        });
    };

    this.initializeEvents = function(activate_events) {
        var activate = (typeof activate_events === 'undefined') ? false : activate_events;
        this._buildEvents();
        if (activate) {
            this.enableEvents();
        } else {
            this.disableEvents();
        }
    };

    this.select = function() {
        if (typeof this.paper_shape !== 'undefined') {
            this.paper_shape.setSelected(true);
        }
    };

    this.deselect = function() {
        if (typeof this.paper_shape !== 'undefined') {
            this.paper_shape.setSelected(false);
        }
    };

    this.isSelected = function() {
        if (typeof this.paper_shape !== 'undefined') {
            return this.paper_shape.selected;
        } else {
            return undefined;
        }
    };

    this.show = function() {
        if (typeof this.paper_shape !== 'undefined') {
            this.paper_shape.setVisible(true);
        }
    };

    this.hide = function() {
        if (typeof this.paper_shape !== 'undefined') {
            this.paper_shape.setVisible(false);
        }
    };

    this.delete = function() {
        if (typeof this.paper_shape !== 'undefined') {
            this.paper_shape.remove();
        }
    };
}

Shape.MOUSE_DRAG_EVENT = 'mouse_drag_event';
Shape.prototype.default_events = [
    Shape.MOUSE_DRAG_EVENT
];

function Rectangle(id, origin_x, origin_y, width, height, transform_matrix) {
    Shape.call(this, id, transform_matrix);

    this.origin_x = origin_x;
    this.origin_y = origin_y;
    this.width = width;
    this.height = height;

    this.toPaperShape = function(activate_events) {
        var rect = new paper.Shape.Rectangle({
            point: [this.origin_x, this.origin_y],
            size: [this.width, this.height]
        });
        this.paper_shape = rect;
        if (typeof this.original_transform_matrix !== 'undefined')
            this.transformShape(this.original_transform_matrix);
        this._bindWrapper();
        this.initializeEvents(activate_events);
    };

    this.updateShapeDetails = function() {
        this.origin_x = this.paper_shape.point.x;
        this.origin_y = this.paper_shape.point.y;
        this.width = this.paper_shape.size.width;
        this.height = this.paper_shape.size.height;
    };

    this.toJSON = function(x_offset, y_offset) {
        var shape_json = this._configJSON();
        $.extend(
            shape_json,
            {
                'origin_x': this.origin_x + x_offset,
                'origin_y': this.origin_y + y_offset,
                'width': this.width,
                'height': this.height,
                'type': 'rectangle'
            });
        return shape_json;
    };
}

Rectangle.prototype = new Shape();

function Ellipse(id, center_x, center_y, radius_x, radius_y, transform_matrix) {
    Shape.call(this, id, transform_matrix);

    this.center_x = center_x;
    this.center_y = center_y;
    this.radius_x = radius_x;
    this.radius_y = radius_y;

    this.toPaperShape = function(activate_events) {
        var ellipse = new paper.Shape.Ellipse({
            center: [this.center_x, this.center_y],
            radius: [this.radius_x, this.radius_y]
        });
        this.paper_shape = ellipse;
        if (typeof this.original_transform_matrix !== 'undefined')
            this.transformShape(this.original_transform_matrix);
        this._bindWrapper();
        this.initializeEvents(activate_events);
    };

    this.updateShapeDetails = function() {
        this.center_x = this.paper_shape.position.x;
        this.center_y = this.paper_shape.position.y;
        this.radius_x = this.paper_shape.radius.width;
        this.radius_y = this.paper_shape.radius.height;
    };

    this.toJSON = function(x_offset, y_offset) {
        var shape_json = this._configJSON();
        $.extend(
            shape_json,
            {
                'center_x': this.center_x + x_offset,
                'center_y': this.center_y + y_offset,
                'radius_x': this.radius_x,
                'radius_y': this.radius_y,
                'type': 'ellipse'
            });
        return shape_json;
    };
}

Ellipse.prototype = new Shape();

function Circle(id, center_x, center_y, radius, transform_matrix) {
    Shape.call(this, id, transform_matrix);

    this.center_x = center_x;
    this.center_y = center_y;
    this.radius = radius;

    this.toPaperShape = function(activate_events) {
        var circle = new paper.Shape.Circle({
            center: [this.center_x, this.center_y],
            radius: this.radius
        });
        this.paper_shape = circle;
        if (typeof this.original_transform_matrix !== 'undefined')
            this.transformShape(this.original_transform_matrix);
        this._bindWrapper();
        this.initializeEvents(activate_events);
    };

    this.updateShapeDetails = function() {
        this.center_x = this.paper_shape.position.x;
        this.center_y = this.paper_shape.position.y;
        this.radius = this.paper_shape.radius;
    };

    this.toJSON = function(x_offset, y_offset) {
        var shape_json = this._configJSON();
        $.extend(
            shape_json,
            {
                'center_x': this.center_x + x_offset,
                'center_y': this.center_y + y_offset,
                'radius': this.radius,
                'type': 'circle'
            });
        return shape_json;
    };
}

Circle.prototype = new Shape();

function Line(id, from_x, from_y, to_x, to_y, transform_matrix) {
    Shape.call(this, id, transform_matrix);

    this.from_x = from_x;
    this.from_y = from_y;
    this.to_x = to_x;
    this.to_y = to_y;

    this.toPaperShape = function(activate_events) {
        var line = new paper.Path.Line({
            from: [this.from_x, this.from_y],
            to: [this.to_x, this.to_y]
        });
        this.paper_shape = line;
        if (typeof this.original_transform_matrix !== 'undefined')
            this.transformShape(this.original_transform_matrix);
        this._bindWrapper();
        this.initializeEvents(activate_events);
    };

    this.updateShapeDetails = function() {
        this.from_x = this.paper_shape.segments[0].point.x;
        this.from_y = this.paper_shape.segments[0].point.y;
        this.to_x = this.paper_shape.segments[1].point.x;
        this.to_y = this.paper_shape.segments[1].point.y;
    };

    this.toJSON = function(x_offset, y_offset) {
        var shape_json = this._configJSON();
        $.extend(
            shape_json,
            {
                'from_x': this.from_x + x_offset,
                'from_y': this.from_y + y_offset,
                'to_x': this.to_x + x_offset,
                'to_y': this.to_y + y_offset,
                'type': 'line'
            });
        return shape_json;
    };
}

Line.prototype = new Shape();<|endoftext|>