<commit_before>define(['srv/core/Handler', 'srv/core/AuthenticationService', 'srv/core/HttpError', 'srv/error/MethodNotAllowedError', 'srv/core/AuthenticationRequest', 'flow', 'JSON'],
    function (Handler, AuthenticationService, HttpError, MethodNotAllowedError, AuthenticationRequest, flow, JSON) {

    var ERROR = {
        AUTHENTICATION_EXPIRED: "Authentication expired",
        AUTHENTICATION_FAILED: "Authentication failed",
        NO_IDENTITY_FOUND: "No identity found"
    };

    return Handler.inherit('srv.handler.SessionHandler', {

        defaults: {
            path: "/api/authentications"
        },

        inject: {
            authenticationService: AuthenticationService
        },

        isResponsibleForRequest: function (context) {
            var ret = this.callBase(),
                pathName = context.request.urlInfo.pathname;

            return ret && pathName.indexOf(this.$.path) === 0;
        },

        _createAuthenticationRequest: function(context){
            var post = JSON.parse(context.request.body);
            return new AuthenticationRequest(post);
        },

        _handleMissingIdentity: function(authentication, cb){
            cb(ERROR.NO_IDENTITY_FOUND);
        },

        _handleAuthenticationSuccess: function(authentication, cb){
            this.$.authenticationService.saveAuthentication(authentication, cb);
        },

        handleRequest: function (context, callback) {

            var pathName = context.request.urlInfo.pathname,
                method = this._getRequestMethod(context);

            var regex = new RegExp(this.$.path.replace(/\//g,"\\/") + "\/" + "([a-fA-F0-9-]+)"),
                self = this,
                authService = this.$.authenticationService;

            if (pathName === this.$.path) {
                // /api/authentication request

                if (method === "POST") {

                    flow()
                        .seq("authentication", function (cb) {
                            var authRequest = self._createAuthenticationRequest(context);
                            authService.authenticateByRequest(authRequest, cb);
                        })
                        .seq("newAuthentication", function (cb) {
                            if (!this.vars.authentication.has('identity')) {
                                self._handleMissingIdentity(this.vars.authentication, cb);
                            } else {
                                self._handleAuthenticationSuccess(this.vars.authentication, cb);
                            }
                        })
                        .exec(function (err, results) {
                            if (!err) {
                                var authentication = results.newAuthentication;

                                context.user.addAuthentication(authentication);

                                var response = context.response,
                                    uri = context.request.urlInfo.uri;

                                response.writeHead(201, {
                                    Location: uri + "/" + authentication.identifier()
                                });

                                var res = {};

                                res[authentication.idField] = authentication.identifier();
                                res.data = authentication.get("providerUserData");

                                response.write(JSON.stringify(res, 2), 'utf8');

                                response.end();
                            }
                            callback(err);
                        });
                } else {
                    throw new MethodNotAllowedError("Method not supported", ["POST"]);
                }
            } else if (regex.test(pathName)) {
                // current authentication

                if (method === "GET") {
                    var match = pathName.match(regex),
                        token = match ? match.pop() : null;

                    flow()
                        .seq("authentication",function(cb){
                            authService.authenticateByToken(token, cb);
                        })
                        .exec(function(err, results){
                            if(!err){
                                var res = {},
                                    response = context.response;

                                res[results.authentication.idField] = results.authentication.identifier();
                                res.data = results.authentication.get("providerUserData");

                                response.writeHead(200);

                                response.write(JSON.stringify(res, 2), 'utf8');

                                response.end();
                            } else {

                            }

                            callback(err);
                        });

//                    var authenticationFilters = this.$.authenticationService.authenticateByToken(token, function(err, authentication){
//                        if(!err){
//
//                        }
//                    });
//                    for (var i = 0; i < authenticationFilters.length; i++) {
//                        var filter = authenticationFilters[i];
//                        var authenticationData = context.session.getItem(filter.$.sessionKey);
//                        if (authenticationData) {
//                            var response = context.response;
//
//                            response.writeHead(200, "", {
//                                'Content-Type': 'application/json; charset=utf-8'
//                            });
//
//                            response.write(JSON.stringify(authenticationData), 'utf8');
//                            response.end();
//                            return;
//                        }
//                    }
//                    throw new HttpError("Resource not found.", 404);
                    // TODO: retrieve authentications
                } else if (method === "DELETE") {
                    // TODO: logout
                } else {
                    throw new MethodNotAllowedError("Method not supported", ["GET", "POST"]);
                }

            } else {
                throw new HttpError("Resource not found.", 404);
            }

        },

        /***
         * determinate the request method from the request
         *
         * @param {srv.core.Context} context
         * @return {String} method
         * @private
         */
        _getRequestMethod: function (context) {

            var parameter = context.request.urlInfo.parameter;
            if (parameter.method) {
                return parameter.method.toUpperCase();
            }

            return context.request.method;
        }

    });
});<commit_msg>fixed check for Identity in AuthenticationHandler<commit_after>define(['srv/core/Handler', 'srv/core/AuthenticationService', 'srv/core/HttpError', 'srv/error/MethodNotAllowedError', 'srv/core/AuthenticationRequest', 'flow', 'JSON'],
    function (Handler, AuthenticationService, HttpError, MethodNotAllowedError, AuthenticationRequest, flow, JSON) {

    var ERROR = {
        AUTHENTICATION_EXPIRED: "Authentication expired",
        AUTHENTICATION_FAILED: "Authentication failed",
        NO_IDENTITY_FOUND: "No identity found"
    };

    return Handler.inherit('srv.handler.SessionHandler', {

        defaults: {
            path: "/api/authentications"
        },

        inject: {
            authenticationService: AuthenticationService
        },

        isResponsibleForRequest: function (context) {
            var ret = this.callBase(),
                pathName = context.request.urlInfo.pathname;

            return ret && pathName.indexOf(this.$.path) === 0;
        },

        _createAuthenticationRequest: function(context){
            var post = JSON.parse(context.request.body);
            return new AuthenticationRequest(post);
        },

        _handleMissingIdentity: function(authentication, cb){
            cb(ERROR.NO_IDENTITY_FOUND);
        },

        _handleAuthenticationSuccess: function(authentication, cb){
            this.$.authenticationService.saveAuthentication(authentication, cb);
        },

        handleRequest: function (context, callback) {

            var pathName = context.request.urlInfo.pathname,
                method = this._getRequestMethod(context);

            var regex = new RegExp(this.$.path.replace(/\//g,"\\/") + "\/" + "([a-fA-F0-9-]+)"),
                self = this,
                authService = this.$.authenticationService;

            if (pathName === this.$.path) {
                // /api/authentication request

                if (method === "POST") {

                    flow()
                        .seq("authentication", function (cb) {
                            var authRequest = self._createAuthenticationRequest(context);
                            authService.authenticateByRequest(authRequest, cb);
                        })
                        .seq("newAuthentication", function (cb) {
                            if (!this.vars.authentication.get('identity')) {
                                self._handleMissingIdentity(this.vars.authentication, cb);
                            } else {
                                self._handleAuthenticationSuccess(this.vars.authentication, cb);
                            }
                        })
                        .exec(function (err, results) {
                            if (!err) {
                                var authentication = results.newAuthentication;

                                context.user.addAuthentication(authentication);

                                var response = context.response,
                                    uri = context.request.urlInfo.uri;

                                response.writeHead(201, {
                                    Location: uri + "/" + authentication.identifier()
                                });

                                var res = {};

                                res[authentication.idField] = authentication.identifier();
                                res.data = authentication.get("providerUserData");

                                response.write(JSON.stringify(res, 2), 'utf8');

                                response.end();
                            }
                            callback(err);
                        });
                } else {
                    throw new MethodNotAllowedError("Method not supported", ["POST"]);
                }
            } else if (regex.test(pathName)) {
                // current authentication

                if (method === "GET") {
                    var match = pathName.match(regex),
                        token = match ? match.pop() : null;

                    flow()
                        .seq("authentication",function(cb){
                            authService.authenticateByToken(token, cb);
                        })
                        .exec(function(err, results){
                            if(!err){
                                var res = {},
                                    response = context.response;

                                res[results.authentication.idField] = results.authentication.identifier();
                                res.data = results.authentication.get("providerUserData");

                                response.writeHead(200);

                                response.write(JSON.stringify(res, 2), 'utf8');

                                response.end();
                            } else {

                            }

                            callback(err);
                        });

//                    var authenticationFilters = this.$.authenticationService.authenticateByToken(token, function(err, authentication){
//                        if(!err){
//
//                        }
//                    });
//                    for (var i = 0; i < authenticationFilters.length; i++) {
//                        var filter = authenticationFilters[i];
//                        var authenticationData = context.session.getItem(filter.$.sessionKey);
//                        if (authenticationData) {
//                            var response = context.response;
//
//                            response.writeHead(200, "", {
//                                'Content-Type': 'application/json; charset=utf-8'
//                            });
//
//                            response.write(JSON.stringify(authenticationData), 'utf8');
//                            response.end();
//                            return;
//                        }
//                    }
//                    throw new HttpError("Resource not found.", 404);
                    // TODO: retrieve authentications
                } else if (method === "DELETE") {
                    // TODO: logout
                } else {
                    throw new MethodNotAllowedError("Method not supported", ["GET", "POST"]);
                }

            } else {
                throw new HttpError("Resource not found.", 404);
            }

        },

        /***
         * determinate the request method from the request
         *
         * @param {srv.core.Context} context
         * @return {String} method
         * @private
         */
        _getRequestMethod: function (context) {

            var parameter = context.request.urlInfo.parameter;
            if (parameter.method) {
                return parameter.method.toUpperCase();
            }

            return context.request.method;
        }

    });
});<|endoftext|>


<commit_before>import _ from 'underscore';
import Backbone from 'backbone';
import VirtualizedListView from 'backbone-virtualized-listview';

import ListView from './list-view.js';
import ColumnGroup from './column-group.js';
import rowTemplate from './row.jade';
import tableTemplate from './table.jade';

const listTemplate = ({ headRows, footRows, columnGroup }) => {
  return tableTemplate({
    header: {
      rows: _.reduce(headRows, (memo, row) => {
        if (row === 'column-header-rows') {
          return memo.concat(columnGroup.headerRows);
        } else if (_.has(row, 'html')) {
          memo.push({
            classes: row.classes,
            cells: [{
              rowspan: 1,
              colspan: columnGroup.width,
              html: row.html,
            }],
          });
          return memo;
        } else if (_.has(row, 'item')) {
          const columns = columnGroup.leafColumns;
          memo.push({
            classes: row.classes,
            cells: _.map(columns, col => row.item[col.name] || {}),
          });
          return memo;
        }
      }, []),
    },
    footer: {
      rows: footRows,
    },
  });
};

const itemTemplate = rowTemplate;

function getItems({ columnGroup, bodyRows }) {
  return {
    length: bodyRows.length,
    slice(start, stop) {
      return _.map(bodyRows.slice(start, stop), row => ({
        row: {
          classes: row.classes,
          cells: _.map(columnGroup.leafColumns, col => row.item[col.name]),
        },
      }));
    },
  };
}

const MODEL_OPTIONS = ['columnGroup', 'headRows', 'footRows'];
const ITEMS_OPTIONS = ['columnGroup', 'bodyRows'];

class TableView extends Backbone.View {
  initialize({
    virtualize = false,
    fixedHeader = false,
    columns = [],
    headRows = [],
    bodyRows = [],
    footRows = [],
    events = {},
  }) {
    this.fixedHeader = fixedHeader;
    this.options = {
      columnGroup: new ColumnGroup(columns),
      headRows,
      bodyRows,
      footRows,
      events,
    };

    const ListViewType = virtualize ? VirtualizedListView : ListView;

    this.listView = new ListViewType({
      el: this.$el,
      listTemplate,
      itemTemplate,
      items: getItems(_.pick(this.options, ITEMS_OPTIONS)),
      model: _.pick(this.options, MODEL_OPTIONS),
      events,
    });
  }

  reset(options = {}, callback) {
    const isSet = key => _.has(options, key);
    const changed = {};

    if (isSet('columns')) {
      options.columnGroup = new ColumnGroup(options.columns);
      delete options.columns;
    }

    _.extend(this.options, options);

    if (_.some(MODEL_OPTIONS, isSet)) {
      changed.model = _.pick(this.options, MODEL_OPTIONS);
    }
    if (_.some(ITEMS_OPTIONS, isSet)) {
      changed.items = getItems(_.pick(this.options, ITEMS_OPTIONS));
    }
    if (_.has(options, 'events')) {
      changed.events = options.events;
    }
    this.listView.reset(changed, callback);
  }

  render(callback) {
    this.listView.render(callback);
    return this;
  }

  scrollToItem(...args) {
    this.listView.scrollToItem(...args);
  }
}

export default TableView;

<commit_msg>translate the rows for tfoot<commit_after>import _ from 'underscore';
import Backbone from 'backbone';
import VirtualizedListView from 'backbone-virtualized-listview';

import ListView from './list-view.js';
import ColumnGroup from './column-group.js';
import rowTemplate from './row.jade';
import tableTemplate from './table.jade';

function translateRow(columnGroup, row) {
  if (_.has(row, 'html')) {
    return {
      classes: row.classes,
      cells: [{
        rowspan: 1,
        colspan: columnGroup.width,
        html: row.html,
      }],
    };
  }
  if (_.has(row, 'item')) {
    return {
      classes: row.classes,
      cells: _.map(columnGroup.leafColumns, col => row.item[col.name] || {}),
    };
  }
  return row;
}

const listTemplate = ({ headRows, footRows, columnGroup }) => {
  return tableTemplate({
    header: {
      rows: _.reduce(headRows, (memo, row) => {
        if (row === 'column-header-rows') {
          return memo.concat(columnGroup.headerRows);
        }
        memo.push(translateRow(columnGroup, row));
        return memo;
      }, []),
    },

    footer: {
      rows: _.map(footRows, translateRow),
    },
  });
};

const itemTemplate = rowTemplate;

function getItems({ columnGroup, bodyRows }) {
  return {
    length: bodyRows.length,
    slice(start, stop) {
      return _.map(bodyRows.slice(start, stop), row => ({
        row: {
          classes: row.classes,
          cells: _.map(columnGroup.leafColumns, col => row.item[col.name]),
        },
      }));
    },
  };
}

const MODEL_OPTIONS = ['columnGroup', 'headRows', 'footRows'];
const ITEMS_OPTIONS = ['columnGroup', 'bodyRows'];

class TableView extends Backbone.View {
  initialize({
    virtualize = false,
    fixedHeader = false,
    columns = [],
    headRows = [],
    bodyRows = [],
    footRows = [],
    events = {},
  }) {
    this.fixedHeader = fixedHeader;
    this.options = {
      columnGroup: new ColumnGroup(columns),
      headRows,
      bodyRows,
      footRows,
      events,
    };

    const ListViewType = virtualize ? VirtualizedListView : ListView;

    this.listView = new ListViewType({
      el: this.$el,
      listTemplate,
      itemTemplate,
      items: getItems(_.pick(this.options, ITEMS_OPTIONS)),
      model: _.pick(this.options, MODEL_OPTIONS),
      events,
    });
  }

  reset(options = {}, callback) {
    const isSet = key => _.has(options, key);
    const changed = {};

    if (isSet('columns')) {
      options.columnGroup = new ColumnGroup(options.columns);
      delete options.columns;
    }

    _.extend(this.options, options);

    if (_.some(MODEL_OPTIONS, isSet)) {
      changed.model = _.pick(this.options, MODEL_OPTIONS);
    }
    if (_.some(ITEMS_OPTIONS, isSet)) {
      changed.items = getItems(_.pick(this.options, ITEMS_OPTIONS));
    }
    if (_.has(options, 'events')) {
      changed.events = options.events;
    }
    this.listView.reset(changed, callback);
  }

  render(callback) {
    this.listView.render(callback);
    return this;
  }

  scrollToItem(...args) {
    this.listView.scrollToItem(...args);
  }
}

export default TableView;

<|endoftext|>


<commit_before>//Function loops through a table and returns JSON —— There should be/is a way to do this with just D3. :/
function createJSON(tableName){
  var myRows = [];
  var headersText = [];

  var tableHeaders=tableName+" th"
    , rowNames = tableName+" tbody tr"
	var $headers = $(tableHeaders);

	// Loop through grabbing everything
	var $rows = $(rowNames).each(function(index) {
	  $cells = $(this).find("td");
	  myRows[index] = {};

	  $cells.each(function(cellIndex) {
	    // Set the header text
	    if(headersText[cellIndex] === undefined) {
	      headersText[cellIndex] = $($headers[cellIndex]).text();
	    }
	    // Update the row object with the header/cell combo
	    myRows[index][headersText[cellIndex]] = $(this).text();
	  });
	});
  return myRows;
}

/*Creating a D3 SVG chart*/
function createChart(sourceTable, targetChart, graphingVariable, label, options){//graphing Variable='miles run' and labels are things like "Soccer, baseball etc."
  //create the JSON from the table
  var tableJSON= createJSON(sourceTable);
  console.log(tableJSON);

  //Check and set the options
  var defaultOptions={"type":"column", "graph":"Miles", "height":200, "showTable":true};
  if (options){
    //Check individual options
    if (!options.type){options.type=defaultOptions.type};
    if (!options.graph){options.graph=defaultOptions.graph};
    if (!options.height){options.height=defaultOptions.height; console.log("You can set the height of the chart by adding -- 'height': #### -- to the options parameter")};
    if (typeof options.showTable === 'undefined'){options.showTable = true;console.log("You can hide the original table by adding a value of -- 'showTable': false -- to the options parameter");};
  }else{
    options = typeof options !== 'undefined' ? options : defaultOptions;
    console.log("No option parameters set, so we'll just use the default settings :)")
  }

  //set the variables
  var margin = {top: 20, left: 10, bottom: 10, right: 10}
    , width = parseInt(d3.select(targetChart).style('width'))
    , width = width - margin.left - margin.right
    , height = options.height
    , barPadding = 20
    , padding = 20
    , setResize = false;

  //Creat the yScale
  var yScale=d3.scale.linear()
  	.domain([0,d3.max(tableJSON, function(d){return d[graphingVariable]})])
    .range([height,0]);

  var yAxis=d3.svg.axis()
  	.scale(yScale)
  	.orient('left')
  	.ticks(5);

  //create the SVG
  var newSVG = d3.select(targetChart).append('svg')
    .attr("id", label)
    .style('width', (width + margin.left + margin.right) + 'px')
    .append('g')
    .attr('transform', 'translate(' + [margin.left, margin.top] + ')');

  var g = newSVG.selectAll('g')
        .data(tableJSON)
        .enter()
        .append('g')

  function verticalBars(){
    g.append("rect")
      .attr('width',width / tableJSON.length - barPadding)/*Makes the bar widths scale based on the number of bars */
      .attr('height',function(d){return height-yScale(d[graphingVariable]);})/*height scaled based on yScale */
      .attr('x',function(d,i){return i*(width/tableJSON.length)+padding; /*Number of bars spaced evenly across the width of the SVG */})
      .attr('y',function(d){return yScale(d[graphingVariable]);/*y position scaled based on the new yScale (solves for SVG positioning)*/})
      .classed('bar',true)
      .on("mouseover", function(){d3.select(this).style("fill", "#C00");})
      .on("mouseout", function(){d3.select(this).style("fill", "#900");})
      .on("mousedown", animate)
      .each(function(d) {
          if(!setResize){
            d3.select(window).on('resize', resize);
            setResize=true;//This is a hacked solution to waiting until the chart has been created before calling the resize function. Might be better on the newSVG secgtion.
          }
        });

    g.append('text')
      .text(function(d){return d[graphingVariable] + ' '/*+graphingVariable*/})
      .attr('x',function (d, i){return i * (width/tableJSON.length)+(width/tableJSON.length-barPadding)/2+padding;})//Center the labels
      .attr('y',function (d){return yScale(d[graphingVariable])-5})
      .attr('text-anchor','middle')
      .classed('barvalues', true);

    g.append('text')
        .text(function(d){return d[label] + ''})
        .attr('x',function (d, i){return i * (width/tableJSON.length)+(width/tableJSON.length-barPadding)/2+padding;})//Center the labels
        .attr('y',height+20)
        .attr('text-anchor','middle')
        .classed('barlabels', true)

    function animate(){
      var oldHeight= d3.select(this).attr('height');
      console.log('There is no point to this animation. just reminding myself that i might be able to use animation');
      d3.select(this).transition()
              .duration(1000)
              .attr("height", 10)
              .attr('y',function(d){return height-10;})
            .transition()
            .delay(2000)
            .attr("height", oldHeight)
            .attr('y',function(d){return yScale(d[graphingVariable]);})
            .each("end", function(d){console.log("done. you could execute a second animation that runs on completion here.")});
    }

    if(!options.showTable){
      $(sourceTable).css({'position':'absolute','left':'-9999px'}); //preferable to 'display':'none' because it's still readable by screen readers.
    }
  }

  //Create the graph depending on what type of chart is set in the options
  if (options.type=="column"){verticalBars()} else{console.log("Sorry, we don't currently support "+options.type+" charts.")};

  //Adding the y-axis
  newSVG.append('g')
  	.attr('class', 'axis')
  	.attr('transform','translate('+padding+',0)')
  	.call(yAxis);
  //============

  function resize() {
    console.log('resizing');
    width = parseInt(d3.select(targetChart).style('width'), 10);
    width = width - margin.left - margin.right;

    d3.select(newSVG.node().parentNode)
      .style('width', (width + margin.left + margin.right) + 'px');

    newSVG.selectAll('rect')
      .attr('width', width / tableJSON.length - barPadding)
      .attr('x',function (d,i){return i*(width/tableJSON.length)+padding; })

    newSVG.selectAll('.barlabels')
      .attr('x',function (d, i){return i * (width/tableJSON.length)+(width/tableJSON.length-barPadding)/2+padding;})//Center the labels
    newSVG.selectAll('.barvalues')
      .attr('x',function (d, i){return i * (width/tableJSON.length)+(width/tableJSON.length-barPadding)/2+padding;})//Center the labels
  }
}
//http://stackoverflow.com/questions/7169370/d3-js-and-document-onready
<commit_msg>Added support for horizontal bar charts that resize<commit_after>//Function loops through a table and returns JSON —— There's probably a way to do this with D3. :/
function createJSON(tableName){
  var myRows = [];
  var headersText = [];

  var tableHeaders=tableName+" th"
    , rowNames = tableName+" tbody tr"
	var $headers = $(tableHeaders);

	// Loop through grabbing everything
	var $rows = $(rowNames).each(function(index) {
	  $cells = $(this).find("td");
	  myRows[index] = {};

	  $cells.each(function(cellIndex) {
	    // Set the header text
	    if(headersText[cellIndex] === undefined) {
	      headersText[cellIndex] = $($headers[cellIndex]).text();
	    }
	    // Update the row object with the header/cell combo
	    myRows[index][headersText[cellIndex]] = $(this).text();
	  });
	});
  return myRows;
}

/*Creating a D3 SVG chart*/
function createChart(sourceTable, targetChart, graphingVariable, label, options){//graphing Variable='miles run' and labels are things like "Soccer, baseball etc."

  //CREATE THE JSON FROM THE TABLE --------------------------------------------------------
  var tableJSON= createJSON(sourceTable);
  console.log(tableJSON);

  //DEFINE THE VARIABLES --------------------------------------------------------

  //Check and set the options
  var defaultOptions={"type":"column", "graph":"Miles", "height":200, "showTable":true};
  if (options){
    if (!options.type){options.type=defaultOptions.type};
    if (!options.graph){options.graph=defaultOptions.graph};
    if (!options.height){options.height=defaultOptions.height; console.log("You can set the height of the chart by adding -- 'height': #### -- to the options parameter")};
    if (typeof options.showTable === 'undefined'){options.showTable = true;console.log("You can hide the original table by adding a value of -- 'showTable': false -- to the options parameter");};
  }else{
    options = typeof options !== 'undefined' ? options : defaultOptions;
    console.log("No option parameters set, so we'll just use the default settings :)")
  }
  if(!options.showTable){
    $(sourceTable).css({'position':'absolute','left':'-9999px'}); //preferable to 'display':'none' because it's still readable by screen readers.
  }

  //set the variables — some of these seem redundant and arbitrary
  var margin = {top: 20, left: 10, bottom: 10, right: 10}
    , width = parseInt(d3.select(targetChart).style('width'))
    , width = width - margin.left - margin.right
    , height = options.height
    , barPadding = 20
    , padding = 20
    , paddingLeftLabels = 80 //arbitrary
    , setResize = false;

  //CREATE THE DIFFERENT X- and Y-SCALES --------------------------------------------------------

  //Create the yScale
  var yScale=d3.scale.linear()
    .domain([0,d3.max(tableJSON, function(d){return d[graphingVariable]})])
    .range([height,0]);

  var yAxis=d3.svg.axis()
  	.scale(yScale)
  	.orient('left')
  	.ticks(5);

  //Create the xScale
  var xScale=d3.scale.linear()
    .domain([0,d3.max(tableJSON, function(d){return d[graphingVariable]})])
    .range([0,width]);

  var xAxis=d3.svg.axis()
    .scale(xScale)
    .orient('top')
    .ticks(5);

  //Adding the y-axis
  function addYaxis(){newSVG.append('g')
    .attr('class', 'axis')
    .attr('transform','translate('+padding+',0)')
    .call(yAxis);
  }
  function addXaxis(){newSVG.append('g')
    .attr('class', 'axis')
    .call(xAxis);
    //.attr('y', 'height')
  }

  //CREATE THE SVG-------------------------------------------------------
  var newSVG = d3.select(targetChart).append('svg')
    .attr("id", label)
    .style('width', (width + margin.left + margin.right) + 'px')
    .style('height',height+margin.top+margin.bottom+padding)
    .append('g')
    .attr('transform', 'translate(' + [margin.left, margin.top] + ')');

  //Add the <g>groups for the bars
  var g = newSVG.selectAll('g')
    .data(tableJSON)
    .enter()
    .append('g')

  var hoverColor=function(){d3.select(this).style("fill", "#C00");}
  var offColor=function(){d3.select(this).style("fill", "#900");}

  //DEFINE THE DIFFERENT TYPES OF CHARTS--------------------------------------------------------

  //Horizontal bar chart--------------------------------------------------------
  function horizontalBars(){
    xScale.range([paddingLeftLabels,width-padding]);

    g.append("rect")
      .attr('width',function(d){return xScale(d[graphingVariable]) - paddingLeftLabels;})
      .attr('height',height / tableJSON.length - barPadding)
      .attr('x', paddingLeftLabels)
      .attr('y',function(d,i){return i*(height/tableJSON.length)+padding})
      .classed('bar',true)
      .on("mouseover", hoverColor)
      .on("mouseout", offColor)
      .each(function(d) {
          if(!setResize){
            d3.select(window).on('resize', resize);
            setResize=true;//This is a hacked solution to waiting until the chart has been created before calling the resize function. Might be better on the newSVG secgtion.
          }
        });

    //Add the labels
    g.append('text')
      .text(function(d){return d[label]})
      .attr('x',0)
      .attr('y',function(d,i){return i*(height/tableJSON.length)+.5*(height / tableJSON.length)+20})
      .classed('barlabels', true);

    //Add the values
    g.append('text')
      .text(function(d){return d[graphingVariable]})
      .attr('x',function(d){return xScale(d[graphingVariable])+5;})
      .attr('y',function(d,i){return i*(height/tableJSON.length)+.5*(height / tableJSON.length)+20})
      .classed('barvalues', true);

    //Add the xAxis
    addXaxis();
  }

  //Vertical column bar chart--------------------------------------------------------
  function verticalBars(){
    g.append("rect")
      .attr('width',width / tableJSON.length - barPadding)/*Makes the bar widths scale based on the number of bars */
      .attr('height',function(d){return height-yScale(d[graphingVariable]);})/*height scaled based on yScale */
      .attr('x',function(d,i){return i*(width/tableJSON.length)+padding; /*Number of bars spaced evenly across the width of the SVG */})
      .attr('y',function(d){return yScale(d[graphingVariable]);/*y position scaled based on the new yScale (solves for SVG positioning)*/})
      .classed('bar',true)
      .on("mouseover", hoverColor)
      .on("mouseout", offColor)
      .on("mousedown", animate)
      .each(function(d) {
          if(!setResize){
            d3.select(window).on('resize', resize);
            setResize=true;//This is a hacked solution to waiting until the chart has been created before calling the resize function. Might be better on the newSVG secgtion.
            //http://stackoverflow.com/questions/7169370/d3-js-and-document-onready
          }
        });

    //Add the values
    g.append('text')
      .text(function(d){return d[graphingVariable] + ' '/*+graphingVariable*/})
      .attr('x',function (d, i){return i * (width/tableJSON.length)+(width/tableJSON.length-barPadding)/2+padding;})//Center the labels
      .attr('y',function (d){return yScale(d[graphingVariable])-5})
      .attr('text-anchor','middle')
      .classed('barvalues', true);

    //add the labels
    g.append('text')
        .text(function(d){return d[label]})
        .attr('x',function (d, i){return i * (width/tableJSON.length)+(width/tableJSON.length-barPadding)/2+padding;})//Center the labels
        .attr('y',height+20)
        .attr('text-anchor','middle')
        .classed('barlabels', true)

    //Superfluous animation
    function animate(){
      var oldHeight= d3.select(this).attr('height');
      console.log('There is no point to this animation. just reminding myself that i might be able to use animation');
      d3.select(this).transition()
        .duration(1000)
        .attr("height", 10)
        .attr('y',function(d){return height-10;})
        .transition()
        .delay(2000)
        .attr("height", oldHeight)
        .attr('y',function(d){return yScale(d[graphingVariable]);})
        .each("end", function(d){console.log("done. you could execute a second animation that runs on completion here.")});
    }

    //Add the yAxis at the end
    addYaxis();
  }
  //Set the resizing options based on the type of chart-------------------------------
  var rectXpos, rectW, barLabelsX, barValuesX;
  if(options.type=='column'){
    rectXpos=function (d,i){return i*(width/tableJSON.length)+padding};
    rectW=function (d){return width / tableJSON.length - barPadding};
    barLabelsX=function (d, i){return i * (width/tableJSON.length)+(width/tableJSON.length-barPadding)/2+padding;};
    barValuesX=function (d, i){return i * (width/tableJSON.length)+(width/tableJSON.length-barPadding)/2+padding;};
  }
  if(options.type=='bar'){
    rectXpos=paddingLeftLabels;
    rectW=function(d){xScale.range([paddingLeftLabels,width-padding]);return xScale(d[graphingVariable]) - paddingLeftLabels;};//Make this a function to calculate the width
    barLabelsX=0;
    barValuesX=function(d){return xScale(d[graphingVariable])+5;};//Make this a function to calculate the width
  }

  //DEFINE THE RESIZE FUNCTION--------------------------------------------------------
  function resize() {
    console.log('resizing');
    width = parseInt(d3.select(targetChart).style('width'), 10);
    width = width - margin.left - margin.right;

    d3.select(newSVG.node().parentNode)
      .style('width', (width + margin.left + margin.right) + 'px');

    newSVG.selectAll('rect')
      .attr('width',rectW)
      .attr('x',rectXpos)

    newSVG.selectAll('.barlabels')
      .attr('x',barLabelsX)//Center the labels
    newSVG.selectAll('.barvalues')
      .attr('x',barValuesX)//Center the labels

    //I'll need to adjust the xAxis for horizontal charts
    //xAxis.scale(xScale)
  }

  //Create the graph depending on what type of chart is set in the options--------------------------------------------------------
  if (options.type=="column"){
    verticalBars()
  } else if (options.type=="bar"){horizontalBars()
  } else {
    console.log("Sorry, we don't currently support "+options.type+" charts.")
  };
}

/*
Known issues:
resize the horizontal axis on resize
limit label widths (or hide labels) below a certain point
adjust the height of the graphic based on the amoung of variables being graphed
Adjust the distance between bars based on the height to data ratio
*/

<|endoftext|>


<commit_before>// Copyright (c) 2016,17 Walter Bender
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the The GNU Affero General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// You should have received a copy of the GNU Affero General Public
// License along with this library; if not, write to the Free Software
// Foundation, 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA
//

const NUMBERBLOCKDEFAULT = 4;

const DEFAULTPALETTE = 'rhythm';

const TITLESTRING = _('Music Blocks is a collection of tools for exploring musical concepts.');

// We don't include 'extras' since we want to be able to delete
// plugins from the extras palette.
BUILTINPALETTES = ['rhythm', 'pitch', 'meter', 'tone', 'intervals', 'volume', 'drum', 'flow', 'action', 'boxes', 'widgets', 'mouse', 'pen', 'number', 'boolean', 'media', 'sensors', 'heap', 'mice', 'extras'];

const BUILTINPALETTESFORL23N = [_('rhythm'), _('pitch'), _('meter'), _('tone'), _('intervals'), _('volume'), _('drum'), _('flow'), _('action'), _('boxes'), _('widgets'), _('mouse'), _('pen'), _('number'), _('boolean'), _('media'), _('sensors'), _('heap'), _('mice'), _('extras')];

function getMainToolbarButtonNames(name) {
    return (['popdown-palette', 'run', 'step', 'step-music', 'stop-turtle', 'hard-stop-turtle', 'clear', 'palette', 'hide-blocks', 'collapse-blocks', 'go-home', 'help', 'sugarizer-stop'].indexOf(name) > -1);
};

function getAuxToolbarButtonNames(name) {
    return (['planet', 'open', 'save', 'paste-disabled', 'Cartesian', 'compile', 'utility', 'empty-trash', 'restore-trash'].indexOf(name) > -1);
}

function createDefaultStack() {
    DATAOBJS =
        [[0, 'start', screen.width / 3, 75, [null, 1, null]],

         [1, 'newnote', 0, 0, [0, 2, 5, 9]],
         [2, 'divide', 0, 0, [1, 3, 4]],
         [3, ['number', {'value': 1}], 0, 0, [2]],
         [4, ['number', {'value': 4}], 0, 0, [2]],
         [5, 'vspace', 0, 0, [1, 6]],
         [6, 'pitch', 0, 0, [5, 7, 8, null]],
         [7, ['solfege', {'value': 'sol'}], 0, 0, [6]],
         [8, ['number', {'value': 4}], 0, 0, [6]],
         [9, 'hidden', 0, 0, [1, 10]],

         [10, 'newnote', 0, 0, [9, 11, 14, 18]],
         [11, 'divide', 0, 0, [10, 12, 13]],
         [12, ['number', {'value': 1}], 0, 0, [11]],
         [13, ['number', {'value': 4}], 0, 0, [11]],
         [14, 'vspace', 0, 0, [10, 15]],
         [15, 'pitch', 0, 0, [14, 16, 17, null]],
         [16, ['solfege', {'value': 'mi'}], 0, 0, [15]],
         [17, ['number', {'value': 4}], 0, 0, [15]],
         [18, 'hidden', 0, 0, [10, 19]],

         [19, 'newnote', 0, 0, [18, 20, 23, 27]],
         [20, 'divide', 0, 0, [19, 21, 22]],
         [21, ['number', {'value': 1}], 0, 0, [20]],
         [22, ['number', {'value': 2}], 0, 0, [20]],
         [23, 'vspace', 0, 0, [19, 24]],
         [24, 'pitch', 0, 0, [23, 25, 26, null]],
         [25, ['solfege', {'value': 'sol'}], 0, 0, [24]],
         [26, ['number', {'value': 4}], 0, 0, [24]],
         [27, 'hidden', 0, 0, [19, null]],
        ];
};

function createHelpContent() {
    HELPCONTENT = [
        [_('Welcome to Music Blocks'), _('Music Blocks is a collection of manipulative tools for exploring fundamental musical concepts in an integrative and fun way.'), 'activity/activity-icon-mouse-color.svg'],
        [_('Meet Mr. Mouse!'), _('Mr Mouse is our Music Blocks conductor.') + ' ' + _('Mr Mouse encourages you to explore Music Blocks.') + ' ' + _('Let us start our tour!'), 'activity/activity-icon-mouse-color.svg'],
	//.TRANS: the buttons used to open various palettes of blocks
        [_('Palette buttons'),
        //.TRANS: Please add commas to list: Rhythm, Pitch, Tone, Action, and more.
	 _('This toolbar contains the palette buttons, including Rhythm Pitch Tone Action and more.') + ' ' + _('Click to show the palettes of blocks and drag blocks from the palettes onto the canvas to use them.'), 'images/icons.svg'],
        [_('Play music'), _('Click to run the music note by note. Alternatively, you can hit the ENTER or RETURN key.'), 'header-icons/play-button.svg'],
        [_('Run fast'), _('Click the run button to run the project in fast mode.'), 'header-icons/run-button.svg'],
        [_('Run slow'), _('Long press the run button to run the project in slow mode.'), 'header-icons/slow-button.svg'],
        [_('Run music slow'), _('Extra-long press the run button to run the music in slow mode.'), 'header-icons/slow-music-button.svg'],
        [_('Run step by step'), _('Click to run the project step by step.'), 'header-icons/step-button.svg'],
        [_('Run note by note'), _('Click to run the music note by note.'), 'header-icons/step-music-button.svg'],
        [_('Stop'), _('Stop the music (and the turtles).'), 'header-icons/stop-turtle-button.svg'],
        [_('Clean'), _('Clear the screen and return the turtles to their initial positions.'), 'header-icons/clear-button.svg'],
        [_('Show/hide blocks'), _('Hide or show the blocks and the palettes.'), 'header-icons/hide-blocks-button.svg'],
        [_('Expand/collapse collapsable blocks'), _('Expand or collapse start and action stacks.'), 'header-icons/collapse-blocks-button.svg'],
        [_('Home'), _('Return all blocks to the center of the screen.'), 'header-icons/go-home-button.svg'],
        [_('Help'), _('Show these messages.'), 'header-icons/help-button.svg'],
        [_('Expand/collapse option toolbar'), _('Click this button to expand or collapse the auxillary toolbar.'), 'header-icons/menu-button.svg'],
        [_('Load samples from server'), _('This button opens a viewer for loading example projects.'), 'header-icons/planet-button.svg'],
        [_('Load project from files'), _('You can also load projects from the file system.'), 'header-icons/open-button.svg'],
        [_('Save project'), _('Save your project to a file.'), 'header-icons/save-button.svg'],
        [_('Save sheet music'), _('Save your project to as a Lilypond file.'), 'header-icons/lilypond-button.svg'],
        [_('Copy'), _('To copy a stack to the clipboard, do a long press on the stack. You can also use Alt+C to copy a stack of blocks. ') + ' ' + _('The Paste Button will highlight.'), 'header-icons/paste-button.svg'],
        [_('Paste'), _('The paste button is enabled when there are blocks copied onto the clipboard. You can also use Alt+P to paste a stack of blocks. '), 'header-icons/paste-disabled-button.svg'],
        [_('Cartesian') + '/' + _('Polar'), _('Show or hide a coordinate grid.'), 'header-icons/Cartesian-polar-button.svg'],
        // [_('Polar'), _('Show or hide a polar-coordinate grid.'), 'header-icons/polar-button.svg'],
        [_('Settings'), _('Open a panel for configuring Music Blocks.'), 'header-icons/utility-button.svg'],
        [_('Decrease block size'), _('Decrease the size of the blocks.'), 'header-icons/smaller-button.svg'],
        [_('Increase block size'), _('Increase the size of the blocks.'), 'header-icons/bigger-button.svg'],
        [_('Display statistics'), _('Display statistics about your Music project.'), 'header-icons/stats-button.svg'],
        [_('Load plugin from file'), _('You can load new blocks from the file system.'), 'header-icons/plugins-button.svg'],
        [_('Enable scrolling'), _('You can scroll the blocks on the canvas.'), 'header-icons/scroll-unlock-button.svg'],
        [_('Delete all'), _('Remove all content on the canvas, including the blocks.'), 'header-icons/empty-trash-button.svg'],
        [_('Undo'), _('Restore blocks from the trash.'), 'header-icons/restore-trash-button.svg'],
        [_('Congratulations.'), _('You have finished the tour. Please enjoy Music Blocks!'), 'activity/activity-icon-mouse-color.svg']
    ];
};
<commit_msg>Add note shortcuts to tutorial<commit_after>// Copyright (c) 2016,17 Walter Bender
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the The GNU Affero General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// You should have received a copy of the GNU Affero General Public
// License along with this library; if not, write to the Free Software
// Foundation, 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA
//

const NUMBERBLOCKDEFAULT = 4;

const DEFAULTPALETTE = 'rhythm';

const TITLESTRING = _('Music Blocks is a collection of tools for exploring musical concepts.');

// We don't include 'extras' since we want to be able to delete
// plugins from the extras palette.
BUILTINPALETTES = ['rhythm', 'pitch', 'meter', 'tone', 'intervals', 'volume', 'drum', 'flow', 'action', 'boxes', 'widgets', 'mouse', 'pen', 'number', 'boolean', 'media', 'sensors', 'heap', 'mice', 'extras'];

const BUILTINPALETTESFORL23N = [_('rhythm'), _('pitch'), _('meter'), _('tone'), _('intervals'), _('volume'), _('drum'), _('flow'), _('action'), _('boxes'), _('widgets'), _('mouse'), _('pen'), _('number'), _('boolean'), _('media'), _('sensors'), _('heap'), _('mice'), _('extras')];

function getMainToolbarButtonNames(name) {
    return (['popdown-palette', 'run', 'step', 'step-music', 'stop-turtle', 'hard-stop-turtle', 'clear', 'palette', 'hide-blocks', 'collapse-blocks', 'go-home', 'help', 'sugarizer-stop'].indexOf(name) > -1);
};

function getAuxToolbarButtonNames(name) {
    return (['planet', 'open', 'save', 'paste-disabled', 'Cartesian', 'compile', 'utility', 'empty-trash', 'restore-trash'].indexOf(name) > -1);
}

function createDefaultStack() {
    DATAOBJS =
        [[0, 'start', screen.width / 3, 75, [null, 1, null]],

         [1, 'newnote', 0, 0, [0, 2, 5, 9]],
         [2, 'divide', 0, 0, [1, 3, 4]],
         [3, ['number', {'value': 1}], 0, 0, [2]],
         [4, ['number', {'value': 4}], 0, 0, [2]],
         [5, 'vspace', 0, 0, [1, 6]],
         [6, 'pitch', 0, 0, [5, 7, 8, null]],
         [7, ['solfege', {'value': 'sol'}], 0, 0, [6]],
         [8, ['number', {'value': 4}], 0, 0, [6]],
         [9, 'hidden', 0, 0, [1, 10]],

         [10, 'newnote', 0, 0, [9, 11, 14, 18]],
         [11, 'divide', 0, 0, [10, 12, 13]],
         [12, ['number', {'value': 1}], 0, 0, [11]],
         [13, ['number', {'value': 4}], 0, 0, [11]],
         [14, 'vspace', 0, 0, [10, 15]],
         [15, 'pitch', 0, 0, [14, 16, 17, null]],
         [16, ['solfege', {'value': 'mi'}], 0, 0, [15]],
         [17, ['number', {'value': 4}], 0, 0, [15]],
         [18, 'hidden', 0, 0, [10, 19]],

         [19, 'newnote', 0, 0, [18, 20, 23, 27]],
         [20, 'divide', 0, 0, [19, 21, 22]],
         [21, ['number', {'value': 1}], 0, 0, [20]],
         [22, ['number', {'value': 2}], 0, 0, [20]],
         [23, 'vspace', 0, 0, [19, 24]],
         [24, 'pitch', 0, 0, [23, 25, 26, null]],
         [25, ['solfege', {'value': 'sol'}], 0, 0, [24]],
         [26, ['number', {'value': 4}], 0, 0, [24]],
         [27, 'hidden', 0, 0, [19, null]],
        ];
};

function createHelpContent() {
    HELPCONTENT = [
        [_('Welcome to Music Blocks'), _('Music Blocks is a collection of manipulative tools for exploring fundamental musical concepts in an integrative and fun way.'), 'activity/activity-icon-mouse-color.svg'],
        [_('Meet Mr. Mouse!'), _('Mr Mouse is our Music Blocks conductor.') + ' ' + _('Mr Mouse encourages you to explore Music Blocks.') + ' ' + _('Let us start our tour!'), 'activity/activity-icon-mouse-color.svg'],
	//.TRANS: the buttons used to open various palettes of blocks
        [_('Palette buttons'),
        //.TRANS: Please add commas to list: Rhythm, Pitch, Tone, Action, and more.
	 _('This toolbar contains the palette buttons, including Rhythm Pitch Tone Action and more.') + ' ' + _('Click to show the palettes of blocks and drag blocks from the palettes onto the canvas to use them.'), 'images/icons.svg'],
        [_('Play music'), _('Click to run the music note by note. Alternatively, you can hit the ENTER or RETURN key.'), 'header-icons/play-button.svg'],
        [_('Run fast'), _('Click the run button to run the project in fast mode.'), 'header-icons/run-button.svg'],
        [_('Run slow'), _('Long press the run button to run the project in slow mode.'), 'header-icons/slow-button.svg'],
        [_('Run music slow'), _('Extra-long press the run button to run the music in slow mode.'), 'header-icons/slow-music-button.svg'],
        [_('Run step by step'), _('Click to run the project step by step.'), 'header-icons/step-button.svg'],
        [_('Run note by note'), _('Click to run the music note by note.'), 'header-icons/step-music-button.svg'],
        [_('Stop'), _('Stop the music (and the turtles).'), 'header-icons/stop-turtle-button.svg'],
        [_('Clean'), _('Clear the screen and return the turtles to their initial positions.'), 'header-icons/clear-button.svg'],
        [_('Show/hide blocks'), _('Hide or show the blocks and the palettes.'), 'header-icons/hide-blocks-button.svg'],
        [_('Expand/collapse collapsable blocks'), _('Expand or collapse start and action stacks.'), 'header-icons/collapse-blocks-button.svg'],
        [_('Home'), _('Return all blocks to the center of the screen.'), 'header-icons/go-home-button.svg'],
        [_('Help'), _('Show these messages.'), 'header-icons/help-button.svg'],
        [_('Expand/collapse option toolbar'), _('Click this button to expand or collapse the auxillary toolbar.'), 'header-icons/menu-button.svg'],
        [_('Load samples from server'), _('This button opens a viewer for loading example projects.'), 'header-icons/planet-button.svg'],
        [_('Load project from files'), _('You can also load projects from the file system.'), 'header-icons/open-button.svg'],
        [_('Save project'), _('Save your project to a file.'), 'header-icons/save-button.svg'],
        [_('Save sheet music'), _('Save your project to as a Lilypond file.'), 'header-icons/lilypond-button.svg'],
        [_('Copy'), _('To copy a stack to the clipboard, do a long press on the stack. You can also use Alt+C to copy a stack of blocks. ') + ' ' + _('The Paste Button will highlight.'), 'header-icons/paste-button.svg'],
        [_('Paste'), _('The paste button is enabled when there are blocks copied onto the clipboard. You can also use Alt+P to paste a stack of blocks. '), 'header-icons/paste-disabled-button.svg'],
        [_('Cartesian') + '/' + _('Polar'), _('Show or hide a coordinate grid.'), 'header-icons/Cartesian-polar-button.svg'],
        // [_('Polar'), _('Show or hide a polar-coordinate grid.'), 'header-icons/polar-button.svg'],
        [_('Settings'), _('Open a panel for configuring Music Blocks.'), 'header-icons/utility-button.svg'],
        [_('Decrease block size'), _('Decrease the size of the blocks.'), 'header-icons/smaller-button.svg'],
        [_('Increase block size'), _('Increase the size of the blocks.'), 'header-icons/bigger-button.svg'],
        [_('Display statistics'), _('Display statistics about your Music project.'), 'header-icons/stats-button.svg'],
        [_('Load plugin from file'), _('You can load new blocks from the file system.'), 'header-icons/plugins-button.svg'],
        [_('Enable scrolling'), _('You can scroll the blocks on the canvas.'), 'header-icons/scroll-unlock-button.svg'],
        [_('Delete all'), _('Remove all content on the canvas, including the blocks.'), 'header-icons/empty-trash-button.svg'],
        [_('Undo'), _('Restore blocks from the trash.'), 'header-icons/restore-trash-button.svg'],
        [_('Shortcuts'), _('Dragging note blocks can be difficult! Try using the "d" key to make do, "r" to make re and so on.'), ''],
        [_('Congratulations.'), _('You have finished the tour. Please enjoy Music Blocks!'), 'activity/activity-icon-mouse-color.svg']
    ];
};
<|endoftext|>


<commit_before>/*
The MIT License (MIT)

Copyright (c) 2014 gskinner.com, inc.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
 */
var Docs = {};

Docs.NONPRINTING_CHARS = {
	"0" : "NULL",
	"1" : "SOH",
	"2" : "STX",
	"3" : "ETX",
	"4" : "EOT",
	"5" : "ENQ",
	"6" : "ACK",
	"7" : "BELL",
	"8" : "BS",
	"9" : "TAB", //
	"10" : "LINE FEED", //
	"11" : "VERTICAL TAB",
	"12" : "FORM FEED",
	"13" : "CARRIAGE RETURN", //
	"14" : "SO",
	"15" : "SI",
	"16" : "DLE",
	"17" : "DC1",
	"18" : "DC2",
	"19" : "DC3",
	"20" : "DC4",
	"21" : "NAK",
	"22" : "SYN",
	"23" : "ETB",
	"24" : "CAN",
	"25" : "EM",
	"26" : "SUB",
	"27" : "ESC",
	"28" : "FS",
	"29" : "GS",
	"30" : "RS",
	"31" : "US",
	"32" : "SPACE", //
	"127" : "DEL"
};

Docs.content = null;
Docs.ids = null;

Docs.setContent = function(content) {
	Docs.content = content;
	var ids = {};
	var parseContent = function(content, o) {
		var kids = content.kids;
		if (content.id) {
			ids[content.id] = content;
			if (o) { o[content.id] = content; }
		}
		if (kids) {
			o = content.ids = {};
			for (var i= 0, l=kids.length; i<l; i++) {
				parseContent(kids[i], o);
				kids[i].parent = content;
			}
		}
	};
	parseContent(content.library);
	parseContent(content.misc);
	Docs.ids = ids;
};

Docs.getItem = function(id) {
	return Docs.ids[id];
};

Docs.forMatch = function(match) {
	if (!match) { return null; }
	var str =	"match: "+TextUtils.htmlSafe(TextUtils.shorten(match[0],50))+
		"
range: "+match.index+"-"+match.end;
	var l = match.length;
	if (l > 1) { str += "
"; }
	for (var i=1; i<l; i++) {
		if (i > 1) { str += "
"; }
		str += "group #"+i+": "+TextUtils.htmlSafe(TextUtils.shorten(match[i],20));
	}
	return str;
};

Docs.forToken = function(token) {
	var pre="", post="", label="", docs = Docs.content;
	if (!token) { return null; }
	if (token.open) { token = token.open; }
	if (token.err) {
		return "ERROR: "+docs.errors[token.err] || "["+token.err+"]";
	}

	var type=token.type, clss=token.clss, ids=Docs.ids, id=type, tip;

	var node = ids[id];
	if (node) {
		label = token.label || node.label || node.id;
		if (id == "group") { label += " #"+token.num; }
		label = ""+label[0].toUpperCase()+label.substr(1)+". ";
	}

	// Special cases:
	if (clss == "quant") { node = ids[clss]; }
	if (type == "char" || clss == "esc") {
		if (clss == "esc") { pre = ((ids[type]&&ids[type].desc)||"Escaped character.")+" "; }
		node = ids["char"];
	}

	tip = node ? (node.tip||node.desc) : "no docs for type='"+type+"'";
	return label+pre+$.fillTags(tip, token, Docs)+post;
};

Docs.forErrorResult = function(type, errors) {
	var node = Docs.ids[type];
	return "ERROR: " + (node.tip || node.desc);
};

Docs.getDesc = function(id) {
	var node = Docs.ids[id];
	return node&&node.desc||("Content not found:"+id);
};

Docs.getQuant = function(token) {
	var min = token.min, max = token.max;
	return min==max ? min : max == -1 ? min+" or more" : "between "+min+" and "+max;
};

Docs.getChar = function(token) {
	var chr = Docs.NONPRINTING_CHARS[token.code];
	return chr ? chr : "\""+String.fromCharCode(token.code)+"\"";
};

Docs.getEscCharDocs = function(c, t, template) {
	var code = c.charCodeAt(0);
	var chr = Docs.NONPRINTING_CHARS[code] || c;
	return {
		token: "\\"+(t||c),
		label: chr.toLowerCase(),
		desc: $.fillTags(template, {code:code}, Docs)
	};
};

Docs.getCtrlKey = function() {
	return Utils.getCtrlKey();
};
<commit_msg>Proper title bolding for simple escaped chars tooltip.<commit_after>/*
The MIT License (MIT)

Copyright (c) 2014 gskinner.com, inc.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
 */
var Docs = {};

Docs.NONPRINTING_CHARS = {
	"0" : "NULL",
	"1" : "SOH",
	"2" : "STX",
	"3" : "ETX",
	"4" : "EOT",
	"5" : "ENQ",
	"6" : "ACK",
	"7" : "BELL",
	"8" : "BS",
	"9" : "TAB", //
	"10" : "LINE FEED", //
	"11" : "VERTICAL TAB",
	"12" : "FORM FEED",
	"13" : "CARRIAGE RETURN", //
	"14" : "SO",
	"15" : "SI",
	"16" : "DLE",
	"17" : "DC1",
	"18" : "DC2",
	"19" : "DC3",
	"20" : "DC4",
	"21" : "NAK",
	"22" : "SYN",
	"23" : "ETB",
	"24" : "CAN",
	"25" : "EM",
	"26" : "SUB",
	"27" : "ESC",
	"28" : "FS",
	"29" : "GS",
	"30" : "RS",
	"31" : "US",
	"32" : "SPACE", //
	"127" : "DEL"
};

Docs.content = null;
Docs.ids = null;

Docs.setContent = function(content) {
	Docs.content = content;
	var ids = {};
	var parseContent = function(content, o) {
		var kids = content.kids;
		if (content.id) {
			ids[content.id] = content;
			if (o) { o[content.id] = content; }
		}
		if (kids) {
			o = content.ids = {};
			for (var i= 0, l=kids.length; i<l; i++) {
				parseContent(kids[i], o);
				kids[i].parent = content;
			}
		}
	};
	parseContent(content.library);
	parseContent(content.misc);
	Docs.ids = ids;
};

Docs.getItem = function(id) {
	return Docs.ids[id];
};

Docs.forMatch = function(match) {
	if (!match) { return null; }
	var str =	"match: "+TextUtils.htmlSafe(TextUtils.shorten(match[0],50))+
		"
range: "+match.index+"-"+match.end;
	var l = match.length;
	if (l > 1) { str += "
"; }
	for (var i=1; i<l; i++) {
		if (i > 1) { str += "
"; }
		str += "group #"+i+": "+TextUtils.htmlSafe(TextUtils.shorten(match[i],20));
	}
	return str;
};

Docs.forToken = function(token) {
	var pre="", post="", label="", docs = Docs.content;
	if (!token) { return null; }
	if (token.open) { token = token.open; }
	if (token.err) {
		return "ERROR: "+docs.errors[token.err] || "["+token.err+"]";
	}

	var type=token.type, clss=token.clss, ids=Docs.ids, id=type, tip;

	var node = ids[id];
	if (node) {
		label = token.label || node.label || node.id;
		if (id == "group") { label += " #"+token.num; }
		label = ""+label[0].toUpperCase()+label.substr(1)+". ";
	}

	// Special cases:
	if (clss == "quant") { node = ids[clss]; }
	if (type == "char" || clss == "esc") {
		if (clss == "esc") { pre = ((ids[type]&&ids[type].desc)||"Escaped character.")+" "; }
		node = ids["char"];
	}

	tip = node ? (node.tip||node.desc) : "no docs for type='"+type+"'";
	return label+pre+$.fillTags(tip, token, Docs)+post;
};

Docs.forErrorResult = function(type, errors) {
	var node = Docs.ids[type];
	return "ERROR: " + (node.tip || node.desc);
};

Docs.getDesc = function(id) {
	var node = Docs.ids[id];
	return node&&node.desc||("Content not found:"+id);
};

Docs.getQuant = function(token) {
	var min = token.min, max = token.max;
	return min==max ? min : max == -1 ? min+" or more" : "between "+min+" and "+max;
};

Docs.getChar = function(token) {
	var chr = Docs.NONPRINTING_CHARS[token.code];
	return chr ? chr : "\""+String.fromCharCode(token.code)+"\"";
};

Docs.getEscCharDocs = function(c, t, template) {
	var code = c.charCodeAt(0);
	var chr = Docs.NONPRINTING_CHARS[code] || c;
	return {
		token: "\\"+(t||c),
		label: chr.toLowerCase(),
		desc: $.fillTags(template, {code:code}, Docs)
	};
};

Docs.getCtrlKey = function() {
	return Utils.getCtrlKey();
};
<|endoftext|>


<commit_before>"use strict";
/**
 * Panel 0.1.3.1
 *
 * By Kevin Chang
 * + touchstart now calls mouseDown and click. Dangerous?
 * + runOnTopComponent: functional
 * + Added addEventListener calls in ViewPanel.
 *
 * TODO:
 * + Dispatch touch events
 * + Focus to know where to send key events
 */

/**
 * Basic GUI structure. Can contain other Panels.
 *
 * Configure its draw() and clickHandler()!
 */
function Panel(x, y, width, height) {
  this.type = "PANEL";
  this.x = x;
  this.y = y;
  this.width = width;
  this.height = height;
  this.color = "#000000"
  this.parentComponent = null;
  this.components = {};
  this.z_index = 0; // Inspired by CSS. Higher number means it will be up front.
}

/**
 * The main drawing function. Contains space for pre- and post-processing.
 */
Panel.prototype.draw = function(ctx, windowX, windowY) {

  this.preprocess(ctx, windowX, windowY);
  this.drawPanel(ctx, windowX, windowY);
  this.postprocess(ctx, windowX, windowY);

  Object.keys(this.components)
    .map(function(key) { return this.components[key] }, this)
    .sort(function(obj1, obj2) { return obj1.z_index - obj2.z_index}) // sorts-by-ascending
    .forEach(function(obj) { obj.draw(ctx, windowX + obj.x, windowY + obj.y) });

}

/**
 * Draws itself onto the given context.
 */
Panel.prototype.drawPanel = function(ctx, windowX, windowY) {
  ctx.fillStyle = this.color;
  ctx.fillRect(windowX, windowY, this.width, this.height);
}

/**
 * Useful for resizing. Defaults to no-op.
 */
Panel.prototype.preprocess = function(ctx, windowX, windowY) {};

/**
 * Useful for drawing non-panel objects. Defaults to no-op.
 */
Panel.prototype.postprocess = function(ctx, windowX, windowY) {};

/**
 * Given a point, returns whether point is on this panel.
 *
 * Example: Panel(0, 0, 2, 2):
 *  (1, 1) -> true
 *  (3, 3) -> false
 */
Panel.containsPoint = function(panel, point) {
  return ((panel.x <= point.clientX && point.clientX <= panel.x + panel.width)
      && (panel.y <= point.clientY && point.clientY <= panel.y + panel.height));
}

Panel.prototype.runOnTopComponent = function(mouseEvent, eventHandler) {

  return Object.keys(this.components) // Gets keys
    .map(function(key) { return this.components[key] }, this) // Gets panel references
    .filter(function(panel) { return Panel.containsPoint(panel, mouseEvent)}) // Removes unclicked ones
    .sort(function(panel1, panel2) { return -1 * (panel1.z_index - panel2.z_index) }) // Sorts descending
    .slice(0, 1)
    .forEach(eventHandler);
}

/**
 * Given a click, delegates to its top component.
 */
Panel.prototype.clickHandler = function(event) {
  this.runOnTopComponent(event, function(component) {
    component.clickHandler({clientX : event.clientX - component.x, clientY : event.clientY - component.y});
  });
}

Panel.prototype.mousedownHandler = function(event) {
  this.runOnTopComponent(event, function(component) {
    component.mousedownHandler({clientX : event.clientX - component.x, clientY : event.clientY - component.y});
  });
}

Panel.prototype.mouseupHandler = function(event) {
  this.runOnTopComponent(event, function(component) {
    component.mouseupHandler({clientX : event.clientX - component.x, clientY : event.clientY - component.y});
  });
}

Panel.prototype.mousemoveHandler = function(event) {
  this.runOnTopComponent(event, function(component) {
    component.mousemoveHandler({clientX : event.clientX - component.x, clientY : event.clientY - component.y});
  });
}

Panel.prototype.mousewheelHandler = function(event) {
  this.runOnTopComponent(event, function(component) {
    component.mousewheelHandler({clientX : event.clientX - component.x, clientY : event.clientY - component.y, wheelDelta : event.wheelDelta });
  });
}

// TODO: Add focus
Panel.prototype.keydownHandler = function(key) {
  Object.keys(this.components) // Gets keys
    .map(function(key) { return this.components[key] }, this) // Gets panel references
    .forEach(function(panel) { panel.keydownHandler(key)} ) // Calls all the panel handlers
}

/**
 * Add components
 */
Panel.prototype.addComponent = function(name, component) {
  this.components[name] = component;
  this.components[name].parent = this;
}

/**
 * Setter function for the window size and position.
 */
Panel.prototype.setWindow = function(x, y, width, height) {
  this.x = x;
  this.y = y;
  this.width = width;
  this.height = height;
}

/**
 * The MainPanel is a special Panel that has access to the canvas.
 *
 * GUI hierarchies should start with a MainPanel.
 */
function ViewPanel(canvas) {
  this.canvas = canvas;
  Panel.call(this, 0, 0, this.canvas.width, this.canvas.height);

  this.canvas = canvas;
  this.canvas.width = window.innerWidth;
  this.canvas.height = window.innerHeight;
  this.canvas.onmousedown = function(){ return false; };
  this.ctx = this.canvas.getContext("2d");

  var instance = this;

  this.canvas.onmousedown = function(){ return false; };

  this.canvas.addEventListener('click', function(event) {
  	instance.clickHandler(event);
  }, false);

  this.canvas.addEventListener('mousedown', function(event) {
  	instance.mousedownHandler(event);
  }, false);

  this.canvas.addEventListener('mouseup', function(event) {
  	instance.mouseupHandler(event);
  }, false);

  this.canvas.addEventListener('mousemove', function(event) {
  	instance.mousemoveHandler(event);
  }, false);

  this.canvas.addEventListener('mousewheel', function(event) {
  	instance.mousewheelHandler(event);
  }, false);

  this.canvas.addEventListener("DOMMouseScroll", function(event) {
  	instance.mousewheelHandler({clientX : event.clientX, clientY : event.clientY, wheelDelta : event.detail * -1});
  }, false);

  window.addEventListener('keydown', function(key) {
  	instance.keydownHandler(key);
  }, false);

  this.canvas.addEventListener('touchstart', function(event) {
  	event.preventDefault(); // not a click!
  	var touches = event.changedTouches;

  	if (touches.length > 0)	{
      instance.clickHandler({clientX: touches[0].clientX, clientY: touches[0].clientY});
  		instance.mousedownHandler({clientX: touches[0].clientX, clientY: touches[0].clientY});
  	}
  });

  this.canvas.addEventListener('touchmove', function(event) {
  	event.preventDefault(); // not a click!
  	var touches = event.changedTouches;

  	if (touches.length > 0)	{
  		instance.mousemoveHandler({clientX: touches[0].clientX, clientY: touches[0].clientY});
  	}
  });

  this.canvas.addEventListener('touchend', function(event) {
  	event.preventDefault(); // not a click!
  	var touches = event.changedTouches;

  	if (touches.length > 0)	{
  		instance.mouseupHandler({clientX: touches[0].clientX, clientY: touches[0].clientY});
  	}
  });
}

ViewPanel.prototype = Object.create(Panel.prototype);

/**
 * Specialized draw used to start GUI hierarcies.
 */
ViewPanel.prototype.draw = function() {
  this.setWindow(0, 0, window.innerWidth, window.innerHeight);
  this.canvas.width = window.innerWidth;
  this.canvas.height = window.innerHeight;
  Panel.prototype.draw.call(this, this.ctx, 0, 0);
}
<commit_msg>Upgrade to Panel 1.3.2<commit_after>"use strict";
/**
 * Panel 0.1.3.2
 *
 * By Kevin Chang
 * + touchevents have own delegators
 *
 * TODO:
 * + Focus to know where to send key events
 * + Consider consolidating mouse and touch into pointer events
 */

/**
 * Basic GUI structure. Can contain other Panels.
 *
 * Configure its draw() and clickHandler()!
 */
function Panel(x, y, width, height) {
  this.type = "PANEL";
  this.x = x;
  this.y = y;
  this.width = width;
  this.height = height;
  this.color = "#000000"
  this.parentComponent = null;
  this.components = {};
  this.z_index = 0; // Inspired by CSS. Higher number means it will be up front.
}

/**
 * The main drawing function. Contains space for pre- and post-processing.
 */
Panel.prototype.draw = function(ctx, windowX, windowY) {

  this.preprocess(ctx, windowX, windowY);
  this.drawPanel(ctx, windowX, windowY);
  this.postprocess(ctx, windowX, windowY);

  Object.keys(this.components)
    .map(function(key) { return this.components[key] }, this)
    .sort(function(obj1, obj2) { return obj1.z_index - obj2.z_index}) // sorts-by-ascending
    .forEach(function(obj) { obj.draw(ctx, windowX + obj.x, windowY + obj.y) });

}

/**
 * Draws itself onto the given context.
 */
Panel.prototype.drawPanel = function(ctx, windowX, windowY) {
  ctx.fillStyle = this.color;
  ctx.fillRect(windowX, windowY, this.width, this.height);
}

/**
 * Useful for resizing. Defaults to no-op.
 */
Panel.prototype.preprocess = function(ctx, windowX, windowY) {};

/**
 * Useful for drawing non-panel objects. Defaults to no-op.
 */
Panel.prototype.postprocess = function(ctx, windowX, windowY) {};

/**
 * Given a point, returns whether point is on this panel.
 *
 * Example: Panel(0, 0, 2, 2):
 *  (1, 1) -> true
 *  (3, 3) -> false
 */
Panel.containsPoint = function(panel, point) {
  return ((panel.x <= point.clientX && point.clientX <= panel.x + panel.width)
      && (panel.y <= point.clientY && point.clientY <= panel.y + panel.height));
}

Panel.prototype.runOnTopComponent = function(mouseEvent, eventHandler) {

  return Object.keys(this.components) // Gets keys
    .map(function(key) { return this.components[key] }, this) // Gets panel references
    .filter(function(panel) { return Panel.containsPoint(panel, mouseEvent)}) // Removes unclicked ones
    .sort(function(panel1, panel2) { return -1 * (panel1.z_index - panel2.z_index) }) // Sorts descending
    .slice(0, 1)
    .forEach(eventHandler);
}

/**
 * Given a click, delegates to its top component.
 */
Panel.prototype.clickHandler = function(event) {
  this.runOnTopComponent(event, function(component) {
    component.clickHandler({clientX : event.clientX - component.x, clientY : event.clientY - component.y});
  });
}

Panel.prototype.mousedownHandler = function(event) {
  this.runOnTopComponent(event, function(component) {
    component.mousedownHandler({clientX : event.clientX - component.x, clientY : event.clientY - component.y});
  });
}

Panel.prototype.mouseupHandler = function(event) {
  this.runOnTopComponent(event, function(component) {
    component.mouseupHandler({clientX : event.clientX - component.x, clientY : event.clientY - component.y});
  });
}

Panel.prototype.mousemoveHandler = function(event) {
  this.runOnTopComponent(event, function(component) {
    component.mousemoveHandler({clientX : event.clientX - component.x, clientY : event.clientY - component.y});
  });
}

Panel.prototype.mousewheelHandler = function(event) {
  this.runOnTopComponent(event, function(component) {
    component.mousewheelHandler({clientX : event.clientX - component.x, clientY : event.clientY - component.y, wheelDelta : event.wheelDelta });
  });
}

// TODO: Add focus
Panel.prototype.keydownHandler = function(key) {
  Object.keys(this.components) // Gets keys
    .map(function(key) { return this.components[key] }, this) // Gets panel references
    .forEach(function(panel) { panel.keydownHandler(key)} ) // Calls all the panel handlers
}

Panel.prototype.touchstartHandler = function(event) {
  this.runOnTopComponent(event, function(component) {
    component.touchstartHandler({clientX : event.clientX - component.x, clientY : event.clientY - component.y, identifier : event.identifier});
  });
}

Panel.prototype.touchmoveHandler = function(event) {
  console.log(event);
  this.runOnTopComponent(event, function(component) {
    component.touchmoveHandler({clientX : event.clientX - component.x, clientY : event.clientY - component.y, identifier : event.identifier});
  });
}

Panel.prototype.touchendHandler = function(event) {
  this.runOnTopComponent(event, function(component) {
    component.touchendHandler({clientX : event.clientX - component.x, clientY : event.clientY - component.y, identifier : event.identifier});
  });
}

/**
 * Add components
 */
Panel.prototype.addComponent = function(name, component) {
  this.components[name] = component;
  this.components[name].parent = this;
}

/**
 * Setter function for the window size and position.
 */
Panel.prototype.setWindow = function(x, y, width, height) {
  this.x = x;
  this.y = y;
  this.width = width;
  this.height = height;
}

/**
 * The MainPanel is a special Panel that has access to the canvas.
 *
 * GUI hierarchies should start with a MainPanel.
 */
function ViewPanel(canvas) {
  this.canvas = canvas;
  Panel.call(this, 0, 0, this.canvas.width, this.canvas.height);

  this.canvas = canvas;
  this.canvas.width = window.innerWidth;
  this.canvas.height = window.innerHeight;
  this.canvas.onmousedown = function(){ return false; };
  this.ctx = this.canvas.getContext("2d");

  var instance = this;

  this.canvas.onmousedown = function(){ return false; };

  this.canvas.addEventListener('click', function(event) {
  	instance.clickHandler(event);
  }, false);

  this.canvas.addEventListener('mousedown', function(event) {
  	instance.mousedownHandler(event);
  }, false);

  this.canvas.addEventListener('mouseup', function(event) {
  	instance.mouseupHandler(event);
  }, false);

  this.canvas.addEventListener('mousemove', function(event) {
  	instance.mousemoveHandler(event);
  }, false);

  this.canvas.addEventListener('mousewheel', function(event) {
  	instance.mousewheelHandler(event);
  }, false);

  this.canvas.addEventListener("DOMMouseScroll", function(event) {
  	instance.mousewheelHandler({clientX : event.clientX, clientY : event.clientY, wheelDelta : event.detail * -1});
  }, false);

  window.addEventListener('keydown', function(key) {
  	instance.keydownHandler(key);
  }, false);

  this.canvas.addEventListener('touchstart', function(event) {
  	event.preventDefault(); // not a click!
    for (var touchID in event.changedTouches) {
      instance.touchstartHandler(event.changedTouches[touchID]);
    }
  });

  this.canvas.addEventListener('touchmove', function(event) {
  	event.preventDefault(); // not a click!
    for (var touchID in event.changedTouches) {
      instance.touchmoveHandler(event.changedTouches[touchID]);
    }
  });

  this.canvas.addEventListener('touchend', function(event) {
  	event.preventDefault(); // not a click!
  	var touches = event.changedTouches;
    for (var touchID in event.changedTouches) {
      instance.touchendHandler(event.changedTouches[touchID]);
    }
  });
}

ViewPanel.prototype = Object.create(Panel.prototype);

/**
 * Specialized draw used to start GUI hierarcies.
 */
ViewPanel.prototype.draw = function() {
  this.setWindow(0, 0, window.innerWidth, window.innerHeight);
  this.canvas.width = window.innerWidth;
  this.canvas.height = window.innerHeight;
  Panel.prototype.draw.call(this, this.ctx, 0, 0);
}
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react'
import shallowCompare from 'react-addons-shallow-compare'
import { connect } from 'react-redux'
import { setNavbarState, setViewportSizeAttributes } from '../actions/gui'
import { addScrollObject, removeScrollObject } from '../components/interface/ScrollComponent'
import { addResizeObject, removeResizeObject } from '../components/interface/ResizeComponent'
import { Viewport } from '../components/viewport/Viewport'
import { scrollToTop } from '../components/interface/Viewport'

class ViewportContainer extends Component {
  static propTypes = {
    dispatch: PropTypes.func.isRequired,
    isNavbarFixed: PropTypes.bool.isRequired,
    isNavbarHidden: PropTypes.bool.isRequired,
    isNavbarSkippingTransition: PropTypes.bool.isRequired,
    isNotificationsActive: PropTypes.bool.isRequired,
    isOffsetLayout: PropTypes.bool.isRequired,
    isProfileMenuActive: PropTypes.bool.isRequired,
    offset: PropTypes.number.isRequired,
    pathname: PropTypes.string.isRequired,
  }

  componentWillMount() {
    this.scrollYAtDirectionChange = null
  }

  componentDidMount() {
    const { isOffsetLayout } = this.props
    addResizeObject(this)
    addScrollObject(this)
    scrollToTop({ isOffsetLayout })
  }

  shouldComponentUpdate(nextProps, nextState) {
    return shallowCompare(this, nextProps, nextState)
  }

  componentWillUnmount() {
    removeResizeObject(this)
    removeScrollObject(this)
  }

  onResize(resizeAttributes) {
    this.props.dispatch(setViewportSizeAttributes(resizeAttributes))
  }

  onScrollTop() {
    const { dispatch, isNavbarFixed } = this.props
    if (isNavbarFixed) {
      dispatch(setNavbarState({
        isFixed: false,
        isHidden: false,
        isSkippingTransition: false,
      }))
    }
  }

  onScrollDirectionChange(scrollProperties) {
    const { scrollY } = scrollProperties
    if (scrollY >= this.props.offset) {
      this.scrollYAtDirectionChange = scrollY
    }
  }

  onScroll(scrollProperties) {
    const { scrollY, scrollDirection } = scrollProperties
    const { dispatch, isNavbarFixed, isNavbarHidden, isNavbarSkippingTransition } = this.props
    let nextIsFixed = isNavbarFixed
    let nextIsHidden = isNavbarHidden
    let nextIsSkippingTransition = isNavbarSkippingTransition

    // Going from absolute to fixed positioning
    if (scrollY >= this.props.offset && !isNavbarFixed) {
      nextIsFixed = true
      nextIsHidden = true
      nextIsSkippingTransition = true
    }

    // Scroll just changed directions so it's about to either be shown or hidden
    if (scrollY >= this.props.offset && this.scrollYAtDirectionChange) {
      const distance = Math.abs(scrollY - this.scrollYAtDirectionChange)
      const delay = scrollDirection === 'down' ? 20 : 80
      const isScrollingDown = scrollDirection === 'down'

      if (distance >= delay) {
        nextIsHidden = isScrollingDown
        nextIsSkippingTransition = false
        this.scrollYAtDirectionChange = null
      }
    }
    if (isNavbarFixed !== nextIsFixed || isNavbarHidden !== nextIsHidden ||
        isNavbarSkippingTransition !== nextIsSkippingTransition) {
      dispatch(setNavbarState({
        isFixed: nextIsFixed,
        isHidden: nextIsHidden,
        isSkippingTransition: nextIsSkippingTransition,
      }))
    }
  }

  // Add the viewport component then things like scrolling,
  // resizing, etc.
  render() {
    return <Viewport { ...this.props } />
  }

}

const mapStateToProps = (state) => {
  const { gui, modal, routing } = state
  return {
    offset: gui.coverOffset ? gui.coverOffset - 80 : 160,
    isNavbarFixed: gui.isNavbarFixed,
    isNavbarHidden: gui.isNavbarHidden,
    isNavbarSkippingTransition: gui.isNavbarSkippingTransition,
    isNotificationsActive: modal.isNotificationsActive,
    isOffsetLayout: gui.isOffsetLayout,
    isProfileMenuActive: gui.isProfileMenuActive,
    pathname: routing.location.pathname,
  }
}

export default connect(mapStateToProps)(ViewportContainer)

<commit_msg>Only update gui size attributes when they change<commit_after>import React, { Component, PropTypes } from 'react'
import shallowCompare from 'react-addons-shallow-compare'
import { connect } from 'react-redux'
import { setNavbarState, setViewportSizeAttributes } from '../actions/gui'
import { addScrollObject, removeScrollObject } from '../components/interface/ScrollComponent'
import { addResizeObject, removeResizeObject } from '../components/interface/ResizeComponent'
import { Viewport } from '../components/viewport/Viewport'
import { scrollToTop } from '../components/interface/Viewport'

class ViewportContainer extends Component {
  static propTypes = {
    dispatch: PropTypes.func.isRequired,
    innerHeight: PropTypes.number.isRequired,
    innerWidth: PropTypes.number.isRequired,
    isNavbarFixed: PropTypes.bool.isRequired,
    isNavbarHidden: PropTypes.bool.isRequired,
    isNavbarSkippingTransition: PropTypes.bool.isRequired,
    isNotificationsActive: PropTypes.bool.isRequired,
    isOffsetLayout: PropTypes.bool.isRequired,
    isProfileMenuActive: PropTypes.bool.isRequired,
    offset: PropTypes.number.isRequired,
    pathname: PropTypes.string.isRequired,
  }

  componentWillMount() {
    this.scrollYAtDirectionChange = null
  }

  componentDidMount() {
    const { isOffsetLayout } = this.props
    addResizeObject(this)
    addScrollObject(this)
    scrollToTop({ isOffsetLayout })
  }

  shouldComponentUpdate(nextProps, nextState) {
    return shallowCompare(this, nextProps, nextState)
  }

  componentWillUnmount() {
    removeResizeObject(this)
    removeScrollObject(this)
  }

  onResize(resizeAttributes) {
    const { innerHeight: prevHeight, innerWidth: prevWidth } = this.props
    const { innerHeight: nextHeight, innerWidth: nextWidth } = resizeAttributes
    if (prevHeight !== nextHeight || prevWidth !== nextWidth) {
      this.props.dispatch(setViewportSizeAttributes(resizeAttributes))
    }
  }

  onScrollTop() {
    const { dispatch, isNavbarFixed } = this.props
    if (isNavbarFixed) {
      dispatch(setNavbarState({
        isFixed: false,
        isHidden: false,
        isSkippingTransition: false,
      }))
    }
  }

  onScrollDirectionChange(scrollProperties) {
    const { scrollY } = scrollProperties
    if (scrollY >= this.props.offset) {
      this.scrollYAtDirectionChange = scrollY
    }
  }

  onScroll(scrollProperties) {
    const { scrollY, scrollDirection } = scrollProperties
    const { dispatch, isNavbarFixed, isNavbarHidden, isNavbarSkippingTransition } = this.props
    let nextIsFixed = isNavbarFixed
    let nextIsHidden = isNavbarHidden
    let nextIsSkippingTransition = isNavbarSkippingTransition

    // Going from absolute to fixed positioning
    if (scrollY >= this.props.offset && !isNavbarFixed) {
      nextIsFixed = true
      nextIsHidden = true
      nextIsSkippingTransition = true
    }

    // Scroll just changed directions so it's about to either be shown or hidden
    if (scrollY >= this.props.offset && this.scrollYAtDirectionChange) {
      const distance = Math.abs(scrollY - this.scrollYAtDirectionChange)
      const delay = scrollDirection === 'down' ? 20 : 80
      const isScrollingDown = scrollDirection === 'down'

      if (distance >= delay) {
        nextIsHidden = isScrollingDown
        nextIsSkippingTransition = false
        this.scrollYAtDirectionChange = null
      }
    }
    if (isNavbarFixed !== nextIsFixed || isNavbarHidden !== nextIsHidden ||
        isNavbarSkippingTransition !== nextIsSkippingTransition) {
      dispatch(setNavbarState({
        isFixed: nextIsFixed,
        isHidden: nextIsHidden,
        isSkippingTransition: nextIsSkippingTransition,
      }))
    }
  }

  // Add the viewport component then things like scrolling,
  // resizing, etc.
  render() {
    return <Viewport { ...this.props } />
  }

}

const mapStateToProps = (state) => {
  const { gui, modal, routing } = state
  return {
    innerHeight: gui.innerHeight,
    innerWidth: gui.innerWidth,
    isNavbarFixed: gui.isNavbarFixed,
    isNavbarHidden: gui.isNavbarHidden,
    isNavbarSkippingTransition: gui.isNavbarSkippingTransition,
    isNotificationsActive: modal.isNotificationsActive,
    isOffsetLayout: gui.isOffsetLayout,
    isProfileMenuActive: gui.isProfileMenuActive,
    offset: gui.coverOffset ? gui.coverOffset - 80 : 160,
    pathname: routing.location.pathname,
  }
}

export default connect(mapStateToProps)(ViewportContainer)

<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import { BEACONS } from '../../constants/action_types'
import { LOGGED_IN_PROMOTIONS } from '../../constants/promotions/logged_in'
import { LOGGED_OUT_PROMOTIONS } from '../../constants/promotions/logged_out'
import { loadCommunities, loadDiscoverUsers, loadFeaturedUsers } from '../../actions/discover'
import { trackEvent } from '../../actions/tracking'
import Banderole from '../../components/assets/Banderole'
import Beacon from '../../components/beacons/Beacon'
import StreamComponent from '../../components/streams/StreamComponent'
import { TabListLinks } from '../../components/tabs/TabList'

const BEACON_VERSION = '1'

export class Discover extends Component {

  static propTypes = {
    dispatch: PropTypes.func.isRequired,
    isLoggedIn: PropTypes.bool.isRequired,
    lastDiscoverBeaconVersion: PropTypes.string,
    params: PropTypes.shape({
      type: PropTypes.string,
    }).isRequired,
    pathname: PropTypes.string.isRequired,
  };

  componentWillMount() {
    const { lastDiscoverBeaconVersion, isLoggedIn } = this.props
    this.state = {
      isBeaconActive: isLoggedIn && lastDiscoverBeaconVersion !== BEACON_VERSION,
    }
  }

  onDismissBeacon = () => {
    const { dispatch } = this.props
    this.setState({ isBeaconActive: false })
    dispatch({ type: BEACONS.LAST_DISCOVER_VERSION, payload: { version: BEACON_VERSION } })
  };

  static preRender = (store, routerState) =>
    store.dispatch(loadDiscoverUsers(routerState.params.type || 'recommended'));

  creditsTrackingEvent = () => {
    const { dispatch } = this.props
    dispatch(trackEvent(`banderole-credits-clicked`))
  };

  renderBeacon() {
    return (
      <Beacon emoji="crystal_ball" onDismiss={ this.onDismissBeacon }>
        Discover inspiring people and beautiful things you won’t find anywhere else.
      </Beacon>
    )
  }

  render() {
    const { isLoggedIn, params, pathname } = this.props
    const { isBeaconActive } = this.state
    const type = params.type || (isLoggedIn ? 'recommended' : 'trending')
    let action = loadDiscoverUsers(type)
    if (type === 'communities') {
      action = loadCommunities()
    } else if (type === 'featured-users') {
      action = loadFeaturedUsers()
    }
    const tabs = isLoggedIn ?
      [
        { to: '/discover', children: 'Recommended' },
        { to: '/discover/trending', children: 'Trending' },
        { to: '/discover/recent', children: 'Recent' },
        // { to: '/discover/communities', children: 'Communities' },
        // { to: '/discover/featured-users', children: 'Featured Users' },
      ] :
      [
        { to: '/explore', children: 'Trending' },
        { to: '/explore/recommended', children: 'Recommended' },
        { to: '/explore/recent', children: 'Recent' },
      ]
    return (
      <section className="Discover Panel" key={`discover_${type}`}>
        { isBeaconActive ? this.renderBeacon() : null }
        <Banderole
          creditsClickAction={ this.creditsTrackingEvent }
          isLoggedIn={ isLoggedIn }
          userlist={ isLoggedIn ? LOGGED_IN_PROMOTIONS : LOGGED_OUT_PROMOTIONS }
        />
        <TabListLinks
          activePath={ pathname }
          className="LabelTabList"
          tabClasses="LabelTab"
          tabs={ tabs }
        />
        <StreamComponent action={action} ref="streamComponent" />

    )
  }
}

function mapStateToProps(state) {
  return {
    isLoggedIn: state.authentication.isLoggedIn,
    lastDiscoverBeaconVersion: state.gui.lastDiscoverBeaconVersion,
    pathname: state.routing.location.pathname,
  }
}

export default connect(mapStateToProps)(Discover)

<commit_msg>Teach how to route logged in users<commit_after>import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import { routeActions } from 'react-router-redux'
import { BEACONS } from '../../constants/action_types'
import { LOGGED_IN_PROMOTIONS } from '../../constants/promotions/logged_in'
import { LOGGED_OUT_PROMOTIONS } from '../../constants/promotions/logged_out'
import { loadCommunities, loadDiscoverUsers, loadFeaturedUsers } from '../../actions/discover'
import { trackEvent } from '../../actions/tracking'
import Banderole from '../../components/assets/Banderole'
import Beacon from '../../components/beacons/Beacon'
import StreamComponent from '../../components/streams/StreamComponent'
import { TabListLinks } from '../../components/tabs/TabList'

const BEACON_VERSION = '1'

export class Discover extends Component {

  static propTypes = {
    currentStream: PropTypes.string,
    dispatch: PropTypes.func.isRequired,
    isLoggedIn: PropTypes.bool.isRequired,
    lastDiscoverBeaconVersion: PropTypes.string,
    params: PropTypes.shape({
      type: PropTypes.string,
    }).isRequired,
    pathname: PropTypes.string.isRequired,
  };

  componentWillMount() {
    const { currentStream, dispatch, lastDiscoverBeaconVersion,
            isLoggedIn, pathname } = this.props

    if (pathname === '/' && isLoggedIn) {
      const replaceTarget = currentStream || '/following'
      dispatch(routeActions.replace(replaceTarget))
    }

    this.state = {
      isBeaconActive: isLoggedIn && lastDiscoverBeaconVersion !== BEACON_VERSION,
    }
  }

  onDismissBeacon = () => {
    const { dispatch } = this.props
    this.setState({ isBeaconActive: false })
    dispatch({ type: BEACONS.LAST_DISCOVER_VERSION, payload: { version: BEACON_VERSION } })
  };

  static preRender = (store, routerState) =>
    store.dispatch(loadDiscoverUsers(routerState.params.type || 'recommended'));

  creditsTrackingEvent = () => {
    const { dispatch } = this.props
    dispatch(trackEvent(`banderole-credits-clicked`))
  };

  renderBeacon() {
    return (
      <Beacon emoji="crystal_ball" onDismiss={ this.onDismissBeacon }>
        Discover inspiring people and beautiful things you won’t find anywhere else.
      </Beacon>
    )
  }

  render() {
    const { isLoggedIn, params, pathname } = this.props
    const { isBeaconActive } = this.state
    const type = params.type || (isLoggedIn ? 'recommended' : 'trending')
    let action = loadDiscoverUsers(type)
    if (type === 'communities') {
      action = loadCommunities()
    } else if (type === 'featured-users') {
      action = loadFeaturedUsers()
    }
    const tabs = isLoggedIn ?
      [
        { to: '/discover', children: 'Recommended' },
        { to: '/discover/trending', children: 'Trending' },
        { to: '/discover/recent', children: 'Recent' },
        // { to: '/discover/communities', children: 'Communities' },
        // { to: '/discover/featured-users', children: 'Featured Users' },
      ] :
      [
        { to: '/explore', children: 'Trending' },
        { to: '/explore/recommended', children: 'Recommended' },
        { to: '/explore/recent', children: 'Recent' },
      ]
    return (
      <section className="Discover Panel" key={`discover_${type}`}>
        { isBeaconActive ? this.renderBeacon() : null }
        <Banderole
          creditsClickAction={ this.creditsTrackingEvent }
          isLoggedIn={ isLoggedIn }
          userlist={ isLoggedIn ? LOGGED_IN_PROMOTIONS : LOGGED_OUT_PROMOTIONS }
        />
        <TabListLinks
          activePath={ pathname }
          className="LabelTabList"
          tabClasses="LabelTab"
          tabs={ tabs }
        />
        <StreamComponent action={action} ref="streamComponent" />

    )
  }
}

function mapStateToProps(state) {
  return {
    currentStream: state.gui.currentStream,
    isLoggedIn: state.authentication.isLoggedIn,
    lastDiscoverBeaconVersion: state.gui.lastDiscoverBeaconVersion,
    pathname: state.routing.location.pathname,
  }
}

export default connect(mapStateToProps)(Discover)
<|endoftext|>