<commit_before>import macro from 'vtk.js/Sources/macros';
import vtkAbstractWidgetFactory from 'vtk.js/Sources/Widgets/Core/AbstractWidgetFactory';
import vtkCircleContextRepresentation from 'vtk.js/Sources/Widgets/Representations/CircleContextRepresentation';
import vtkPlaneManipulator from 'vtk.js/Sources/Widgets/Manipulators/PlaneManipulator';
import vtkSphereHandleRepresentation from 'vtk.js/Sources/Widgets/Representations/SphereHandleRepresentation';
import vtkStateBuilder from 'vtk.js/Sources/Widgets/Core/StateBuilder';

import { ViewTypes } from 'vtk.js/Sources/Widgets/Core/WidgetManager/Constants';

import { vec3 } from 'gl-matrix';

// ----------------------------------------------------------------------------
// Widget linked to a view
// ----------------------------------------------------------------------------

function widgetBehavior(publicAPI, model) {
  publicAPI.handleLeftButtonPress = (callData) => {
    if (!model.activeState || !model.activeState.getActive()) {
      return macro.VOID;
    }

    model.painting = true;
    const trailCircle = model.widgetState.addTrail();
    trailCircle.set(
      model.activeState.get('origin', 'up', 'right', 'direction', 'scale1')
    );
    publicAPI.invokeStartInteractionEvent();
    return macro.EVENT_ABORT;
  };

  publicAPI.handleMouseMove = (callData) => publicAPI.handleEvent(callData);

  publicAPI.handleLeftButtonRelease = () => {
    if (model.painting) {
      publicAPI.invokeEndInteractionEvent();
      model.widgetState.clearTrailList();
    }
    model.painting = false;
  };

  publicAPI.handleEvent = (callData) => {
    if (
      model.manipulator &&
      model.activeState &&
      model.activeState.getActive()
    ) {
      const normal = model.camera.getDirectionOfProjection();
      const up = model.camera.getViewUp();
      const right = [];
      vec3.cross(right, up, normal);
      model.activeState.setUp(...up);
      model.activeState.setRight(...right);
      model.activeState.setDirection(...normal);
      model.manipulator.setNormal(normal);

      const worldCoords = model.manipulator.handleEvent(
        callData,
        model.apiSpecificRenderWindow
      );

      if (worldCoords.length) {
        model.widgetState.setTrueOrigin(...worldCoords);
        model.activeState.setOrigin(...worldCoords);

        if (model.painting) {
          const trailCircle = model.widgetState.addTrail();
          trailCircle.set(
            model.activeState.get(
              'origin',
              'up',
              'right',
              'direction',
              'scale1'
            )
          );
        }
      }

      publicAPI.invokeInteractionEvent();
      return macro.EVENT_ABORT;
    }
    return macro.VOID;
  };

  publicAPI.grabFocus = () => {
    if (!model.hasFocus) {
      model.activeState = model.widgetState.getHandle();
      model.activeState.activate();
      model.interactor.requestAnimation(publicAPI);

      const canvas = model.apiSpecificRenderWindow.getCanvas();
      canvas.onmouseenter = () => {
        if (
          model.hasFocus &&
          model.activeState === model.widgetState.getHandle()
        ) {
          model.activeState.setVisible(true);
        }
      };
      canvas.onmouseleave = () => {
        if (
          model.hasFocus &&
          model.activeState === model.widgetState.getHandle()
        ) {
          model.activeState.setVisible(false);
        }
      };
    }
    model.hasFocus = true;
  };

  publicAPI.loseFocus = () => {
    if (model.hasFocus) {
      model.interactor.cancelAnimation(publicAPI);
    }
    model.widgetState.deactivate();
    model.widgetState.getHandle().deactivate();
    model.activeState = null;
    model.hasFocus = false;
  };

  macro.get(publicAPI, model, ['painting']);
}

// ----------------------------------------------------------------------------
// Factory
// ----------------------------------------------------------------------------

function vtkPaintWidget(publicAPI, model) {
  model.classHierarchy.push('vtkPaintWidget');

  // --- Widget Requirement ---------------------------------------------------
  model.behavior = widgetBehavior;

  publicAPI.getRepresentationsForViewType = (viewType) => {
    switch (viewType) {
      case ViewTypes.DEFAULT:
      case ViewTypes.GEOMETRY:
      case ViewTypes.SLICE:
        return [
          {
            builder: vtkCircleContextRepresentation,
            labels: ['handle', 'trail'],
          },
        ];
      case ViewTypes.VOLUME:
      default:
        return [{ builder: vtkSphereHandleRepresentation, labels: ['handle'] }];
    }
  };
  // --- Widget Requirement ---------------------------------------------------

  // Default state
  model.widgetState = vtkStateBuilder
    .createBuilder()
    .addField({
      name: 'trueOrigin',
      initialValue: [0, 0, 0],
    })
    .addStateFromMixin({
      labels: ['handle'],
      mixins: [
        'origin',
        'color',
        'scale1',
        'orientation',
        'manipulator',
        'visible',
      ],
      name: 'handle',
      initialValues: {
        scale1: model.radius * 2,
        origin: [0, 0, 0],
        orientation: [1, 0, 0, 0, 1, 0, 0, 0, 1],
        visible: true,
      },
    })
    .addDynamicMixinState({
      labels: ['trail'],
      mixins: ['origin', 'color', 'scale1', 'orientation'],
      name: 'trail',
      initialValues: {
        scale1: model.radius * 2,
        origin: [0, 0, 0],
        orientation: [1, 0, 0, 0, 1, 0, 0, 0, 1],
      },
    })
    .build();

  const handle = model.widgetState.getHandle();

  // Default manipulator
  model.manipulator = vtkPlaneManipulator.newInstance();
  handle.setManipulator(model.manipulator);

  // override
  const superSetRadius = publicAPI.setRadius;
  publicAPI.setRadius = (r) => {
    if (superSetRadius(r)) {
      handle.setScale1(r);
    }
  };
}

// ----------------------------------------------------------------------------

const DEFAULT_VALUES = {
  manipulator: null,
  radius: 1,
  painting: false,
  color: [1],
};

// ----------------------------------------------------------------------------

export function extend(publicAPI, model, initialValues = {}) {
  Object.assign(model, DEFAULT_VALUES, initialValues);

  vtkAbstractWidgetFactory.extend(publicAPI, model, initialValues);

  macro.get(publicAPI, model, ['painting']);
  macro.setGet(publicAPI, model, ['manipulator', 'radius', 'color']);

  vtkPaintWidget(publicAPI, model);
}

// ----------------------------------------------------------------------------

export const newInstance = macro.newInstance(extend, 'vtkPaintWidget');

// ----------------------------------------------------------------------------

export default { newInstance, extend };
<commit_msg>fix(paintwidget): fix paintwidget 'handleLeftButtonRelease'<commit_after>import macro from 'vtk.js/Sources/macros';
import vtkAbstractWidgetFactory from 'vtk.js/Sources/Widgets/Core/AbstractWidgetFactory';
import vtkCircleContextRepresentation from 'vtk.js/Sources/Widgets/Representations/CircleContextRepresentation';
import vtkPlaneManipulator from 'vtk.js/Sources/Widgets/Manipulators/PlaneManipulator';
import vtkSphereHandleRepresentation from 'vtk.js/Sources/Widgets/Representations/SphereHandleRepresentation';
import vtkStateBuilder from 'vtk.js/Sources/Widgets/Core/StateBuilder';

import { ViewTypes } from 'vtk.js/Sources/Widgets/Core/WidgetManager/Constants';

import { vec3 } from 'gl-matrix';

// ----------------------------------------------------------------------------
// Widget linked to a view
// ----------------------------------------------------------------------------

function widgetBehavior(publicAPI, model) {
  publicAPI.handleLeftButtonPress = (callData) => {
    if (!model.activeState || !model.activeState.getActive()) {
      return macro.VOID;
    }

    model.painting = true;
    const trailCircle = model.widgetState.addTrail();
    trailCircle.set(
      model.activeState.get('origin', 'up', 'right', 'direction', 'scale1')
    );
    publicAPI.invokeStartInteractionEvent();
    return macro.EVENT_ABORT;
  };

  publicAPI.handleMouseMove = (callData) => publicAPI.handleEvent(callData);

  publicAPI.handleLeftButtonRelease = () => {
    if (model.painting) {
      publicAPI.invokeEndInteractionEvent();
      model.widgetState.clearTrailList();
    }
    model.painting = false;
    return model.hasFocus ? macro.EVENT_ABORT : macro.VOID;
  };

  publicAPI.handleEvent = (callData) => {
    if (
      model.manipulator &&
      model.activeState &&
      model.activeState.getActive()
    ) {
      const normal = model.camera.getDirectionOfProjection();
      const up = model.camera.getViewUp();
      const right = [];
      vec3.cross(right, up, normal);
      model.activeState.setUp(...up);
      model.activeState.setRight(...right);
      model.activeState.setDirection(...normal);
      model.manipulator.setNormal(normal);

      const worldCoords = model.manipulator.handleEvent(
        callData,
        model.apiSpecificRenderWindow
      );

      if (worldCoords.length) {
        model.widgetState.setTrueOrigin(...worldCoords);
        model.activeState.setOrigin(...worldCoords);

        if (model.painting) {
          const trailCircle = model.widgetState.addTrail();
          trailCircle.set(
            model.activeState.get(
              'origin',
              'up',
              'right',
              'direction',
              'scale1'
            )
          );
        }
      }

      publicAPI.invokeInteractionEvent();
      return macro.EVENT_ABORT;
    }
    return macro.VOID;
  };

  publicAPI.grabFocus = () => {
    if (!model.hasFocus) {
      model.activeState = model.widgetState.getHandle();
      model.activeState.activate();
      model.interactor.requestAnimation(publicAPI);

      const canvas = model.apiSpecificRenderWindow.getCanvas();
      canvas.onmouseenter = () => {
        if (
          model.hasFocus &&
          model.activeState === model.widgetState.getHandle()
        ) {
          model.activeState.setVisible(true);
        }
      };
      canvas.onmouseleave = () => {
        if (
          model.hasFocus &&
          model.activeState === model.widgetState.getHandle()
        ) {
          model.activeState.setVisible(false);
        }
      };
    }
    model.hasFocus = true;
  };

  publicAPI.loseFocus = () => {
    if (model.hasFocus) {
      model.interactor.cancelAnimation(publicAPI);
    }
    model.widgetState.deactivate();
    model.widgetState.getHandle().deactivate();
    model.activeState = null;
    model.hasFocus = false;
  };

  macro.get(publicAPI, model, ['painting']);
}

// ----------------------------------------------------------------------------
// Factory
// ----------------------------------------------------------------------------

function vtkPaintWidget(publicAPI, model) {
  model.classHierarchy.push('vtkPaintWidget');

  // --- Widget Requirement ---------------------------------------------------
  model.behavior = widgetBehavior;

  publicAPI.getRepresentationsForViewType = (viewType) => {
    switch (viewType) {
      case ViewTypes.DEFAULT:
      case ViewTypes.GEOMETRY:
      case ViewTypes.SLICE:
        return [
          {
            builder: vtkCircleContextRepresentation,
            labels: ['handle', 'trail'],
          },
        ];
      case ViewTypes.VOLUME:
      default:
        return [{ builder: vtkSphereHandleRepresentation, labels: ['handle'] }];
    }
  };
  // --- Widget Requirement ---------------------------------------------------

  // Default state
  model.widgetState = vtkStateBuilder
    .createBuilder()
    .addField({
      name: 'trueOrigin',
      initialValue: [0, 0, 0],
    })
    .addStateFromMixin({
      labels: ['handle'],
      mixins: [
        'origin',
        'color',
        'scale1',
        'orientation',
        'manipulator',
        'visible',
      ],
      name: 'handle',
      initialValues: {
        scale1: model.radius * 2,
        origin: [0, 0, 0],
        orientation: [1, 0, 0, 0, 1, 0, 0, 0, 1],
        visible: true,
      },
    })
    .addDynamicMixinState({
      labels: ['trail'],
      mixins: ['origin', 'color', 'scale1', 'orientation'],
      name: 'trail',
      initialValues: {
        scale1: model.radius * 2,
        origin: [0, 0, 0],
        orientation: [1, 0, 0, 0, 1, 0, 0, 0, 1],
      },
    })
    .build();

  const handle = model.widgetState.getHandle();

  // Default manipulator
  model.manipulator = vtkPlaneManipulator.newInstance();
  handle.setManipulator(model.manipulator);

  // override
  const superSetRadius = publicAPI.setRadius;
  publicAPI.setRadius = (r) => {
    if (superSetRadius(r)) {
      handle.setScale1(r);
    }
  };
}

// ----------------------------------------------------------------------------

const DEFAULT_VALUES = {
  manipulator: null,
  radius: 1,
  painting: false,
  color: [1],
};

// ----------------------------------------------------------------------------

export function extend(publicAPI, model, initialValues = {}) {
  Object.assign(model, DEFAULT_VALUES, initialValues);

  vtkAbstractWidgetFactory.extend(publicAPI, model, initialValues);

  macro.get(publicAPI, model, ['painting']);
  macro.setGet(publicAPI, model, ['manipulator', 'radius', 'color']);

  vtkPaintWidget(publicAPI, model);
}

// ----------------------------------------------------------------------------

export const newInstance = macro.newInstance(extend, 'vtkPaintWidget');

// ----------------------------------------------------------------------------

export default { newInstance, extend };
<|endoftext|>


<commit_before>var width = 800,
    height = 800,
    radius = Math.min(width, height)*.4,
    color = d3.scale.category20c();

var partition = d3.layout.partition()
    .sort(null)
    .size([2 * Math.PI, radius * radius])
    .value(function(d) { return d.size; });

var arc = d3.svg.arc()
    .startAngle(function(d) { return d.x; })
    .endAngle(function(d) { return d.x + d.dx; })
    .innerRadius(function(d) { return Math.sqrt(d.y); })
    .outerRadius(function(d) { return Math.sqrt(d.y + d.dy); });

function draw_chart(order) {
  var svg = d3.select("#chart").append("svg")
    .attr("width", width)
    .attr("height", height)
  .append("g")
    .attr("transform", "translate(" + width / 2 + "," + height * .5 + ")");

  d3.select(self.frameElement).style("height", height + "px");

  var file = "demo_emissions.json";
  if (order != "country") {
    file = "demo_emissions2.json";
    arc
      .innerRadius(function(d) { return Math.sqrt(100000 - d.y); })
      .outerRadius(function(d) { return Math.sqrt(100000 - d.y + d.dy); });
  }
  else {
    arc
      .innerRadius(function(d) { return Math.sqrt(d.y); })
      .outerRadius(function(d) { return Math.sqrt(d.y + d.dy); });
  }
  d3.json(file, function(error, root) {
    var tooltip = d3.select("body")
      .append("div")
      .style("position", "absolute")
      .style("z-index", "10")
      .style("visibility", "hidden");

    var path = svg.datum(root).selectAll("path")
      .data(partition.nodes)
      .enter();

    path.append("path")
      .attr("display", function(d) { return d.depth ? null : "none"; }) // hide inner ring
      .attr("d", arc)
      .style("stroke", "#fff")
      .style("fill", function(d) { return color((d.children ? d : d.parent).name); })
      .style("fill-rule", "evenodd")
      .each(stash);

    path.append("text")
      .attr("transform", function(d) { return "translate(" + arc.centroid(d)  + ")" + "rotate(" + rotateText(d) + ")" ; })
      .attr("text-anchor", "middle")
      .style("font-size", function(d) { return (12/d.depth+6)+"px"; })
      .style("fill", "#444")
      .text(function(d) { return d.size>300 ? d.name: ""; });

    d3.selectAll("input").on("change", function change() {
      var value = function(d) { return d.size; };

      path.data(partition.value(value).nodes)
	.transition()
        .duration(1500)
        .attrTween("d", arcTween);
    });
  });
}

// how to rotate text
function rotateText(d) {
  // Offset the angle by 90 deg since the '0' degree axis for arc is Y axis, while
  // for text it is the X axis.
  var thetaDeg = (180 / Math.PI * (arc.startAngle()(d) + arc.endAngle()(d)) / 2 - 90);
  // If we are rotating the text by more than 90 deg, then "flip" it.
  // This is why "text-anchor", "middle" is important, otherwise, this "flip" would
  // a little harder.
  return (thetaDeg > 90) ? thetaDeg - 180 : thetaDeg;
}

// Stash the old values for transition.
function stash(d) {
  d.x0 = d.x;
  d.dx0 = d.dx;
}

// Interpolate the arcs in data space.
function arcTween(a) {
  var i = d3.interpolate({x: a.x0, dx: a.dx0}, a);
  return function(t) {
    var b = i(t);
    a.x0 = b.x;
    a.dx0 = b.dx;
    return arc(b);
  };
}
<commit_msg>fix colors<commit_after>var width = 800,
    height = 800,
    radius = Math.min(width, height)*.4,
    color = d3.scale.category20c();

var partition = d3.layout.partition()
    .sort(null)
    .size([2 * Math.PI, radius * radius])
    .value(function(d) { return d.size; });

var arc = d3.svg.arc()
    .startAngle(function(d) { return d.x; })
    .endAngle(function(d) { return d.x + d.dx; })
    .innerRadius(function(d) { return Math.sqrt(d.y); })
    .outerRadius(function(d) { return Math.sqrt(d.y + d.dy); });

function draw_chart(order) {
  var svg = d3.select("#chart").append("svg")
    .attr("width", width)
    .attr("height", height)
  .append("g")
    .attr("transform", "translate(" + width / 2 + "," + height * .5 + ")");

  d3.select(self.frameElement).style("height", height + "px");

  var file = "demo_emissions.json";
  if (order != "country") {
    file = "demo_emissions2.json";
    arc
      .innerRadius(function(d) { return Math.sqrt(100000 - d.y); })
      .outerRadius(function(d) { return Math.sqrt(100000 - d.y + d.dy); });
  }
  else {
    arc
      .innerRadius(function(d) { return Math.sqrt(d.y); })
      .outerRadius(function(d) { return Math.sqrt(d.y + d.dy); });
  }
  d3.json(file, function(error, root) {
    var tooltip = d3.select("body")
      .append("div")
      .style("position", "absolute")
      .style("z-index", "10")
      .style("visibility", "hidden");

    var path = svg.datum(root).selectAll("path")
      .data(partition.nodes)
      .enter();

    path.append("path")
      .attr("display", function(d) { return d.depth ? null : "none"; }) // hide inner ring
      .attr("d", arc)
      .style("stroke", "#fff")
      .style("fill", function(d) {
	var name = d.parent ? d.parent.name : "";
	if (name != ""){
	  name = d.parent.parent ? "" : d.name;
	  if (name == "") {
	    name = d.parent.parent.parent ? d.parent.parent.name : d.parent.name;
	  }
	}
	return color(name); })
      .style("fill-rule", "evenodd")
      .each(stash);

    path.append("text")
      .attr("transform", function(d) { return "translate(" + arc.centroid(d)  + ")" + "rotate(" + rotateText(d) + ")" ; })
      .attr("text-anchor", "middle")
      .style("font-size", function(d) { return (12/d.depth+6)+"px"; })
      .style("fill", "#444")
      .text(function(d) { return d.size>300 ? d.name : ""; });

    d3.selectAll("input").on("change", function change() {
      var value = function(d) { return d.size; };

      path.data(partition.value(value).nodes)
	.transition()
        .duration(1500)
        .attrTween("d", arcTween);
    });
  });
}

// how to rotate text
function rotateText(d) {
  // Offset the angle by 90 deg since the '0' degree axis for arc is Y axis, while
  // for text it is the X axis.
  var thetaDeg = (180 / Math.PI * (arc.startAngle()(d) + arc.endAngle()(d)) / 2 - 90);
  // If we are rotating the text by more than 90 deg, then "flip" it.
  // This is why "text-anchor", "middle" is important, otherwise, this "flip" would
  // a little harder.
  return (thetaDeg > 90) ? thetaDeg - 180 : thetaDeg;
}

// Stash the old values for transition.
function stash(d) {
  d.x0 = d.x;
  d.dx0 = d.dx;
}

// Interpolate the arcs in data space.
function arcTween(a) {
  var i = d3.interpolate({x: a.x0, dx: a.dx0}, a);
  return function(t) {
    var b = i(t);
    a.x0 = b.x;
    a.dx0 = b.dx;
    return arc(b);
  };
}
<|endoftext|>


<commit_before>var controllerurl = location.pathname.replace(/edit$/, '').replace(/edit\/$/, '');

(function ($) {
	$().ready(function () {
		// find the save button and add in its accesskey... really need to
		// get around to patching this in core!
		$('#Form_StatusForm_action_startediting').attr('accesskey', 's');

		// add the lock update ping
		var timeout = $('#Form_EditForm_LockLength').val() ? $('#Form_EditForm_LockLength').val() * 1000 : 300000;

		if (timeout) {
			setInterval(function () {
				var url = $('#Form_EditForm_LockUpdate').val();
				$.post(url);
			}, timeout);
		}

		$('#Form_EditForm_LockUpdate')

		// toggle the new page form
		var createPageForm = $('#Form_CreatePageForm').hide();
		$('#Form_EditForm_action_addpage_t').click(function () {
			createPageForm.toggle();
		    return false;
		});

		// deleting an existing page
		$('#Form_EditForm_action_delete').click(function () {
			if (!confirm("Are you sure you want to delete this page?")) {
				return false;
			}
			return true;
		});

		// manage the creation of a new page
		createPageForm.submit(function () {
			if (!$(this).find('[@name="NewPageName"]').val()) {
				$(this).find('[@name="NewPageName"]').css('border', '1px solid red');
				return false;
			}
		});

		// preview stuff

		var previewdiv = $('#editorPreview');

		//initial preview load
		var updateFlag = true;
		updatePreview();
		updateFlag = false;

		var t = setInterval(updatePreview,5000);

		$('#Form_EditForm_Content').keyup(function(){
			updateFlag = true;
		});

		if($('.markitup').length > 0){
			previewdiv.hide();
			previewdiv.after('
show / hide preview
');
			updatePreview();

			$('#showPreview a').click(function(){
				previewdiv.toggle();
				$('#Form_EditForm_Content').focus();
				return false;
			});
		}

		function updatePreview(){
			if(updateFlag){
				$.post(
					previewdiv.attr('data-url'),
					{ content: $('#Form_EditForm_Content').val() },
					function(data){
						if(data){
							if(previewdiv.html() != data){
								previewdiv.html(data);
							}
						}else{
							previewdiv.html('error: no data');
						}

					}
				);
				updateFlag = false;
			}
		}

		// image dialog window

		window.simpleWikiImageDialog = function(editorType){
			$("#dialogContent").load(controllerurl + '/imagepicker').dialog({
		        title: "Insert an image",
				modal: true,
				autoOpen: true,
				height: 500,
				width: 500,
				open: function(event, ui){
					$('#ExistingImage').livequery(function(){
					 	$(this).hide()
					 });
				},
				buttons: {
					"Insert / upload image": function() {
						$this = $(this);
						type = $('#Type input:radio:checked').val();
						title = $('#Form_ImagePickerForm_Title').val();
						if(type == 'new'){
							if($('#Form_ImagePickerForm_NewImage').val().length > 0){
								$('#Form_ImagePickerForm').ajaxSubmit({
						        	dataType:  'json',
						        	url: controllerurl + '/imageupload',
						        	beforeSubmit: function(){
						        		$('#uploadingIcon').show();
						        	},
						        	success: function(data){
						        		// validate
										if(data.error){
						        			$('#uploadingIcon').hide();
						        			$('#Form_ImagePickerForm_NewImage').val('');
						        			alert(data.text);
						        			return false;
										}else{
											insertImageIntoEditor(data.link, title, editorType);
											$this.dialog( "close" );
											$('#Form_EditForm_Content').focus();
										}
						        	}

						   		});
							}else{
								alert("Please select an image to upload");
								$( this ).dialog( "close" );
								$('#Form_EditForm_Content').focus();
								return;
							}

    					}else{
    						insertImageIntoEditor($("#Form_ImagePickerForm_ExistingImage").attr('data-link'), title, editorType);
    						$( this ).dialog( "close" );
							$('#Form_EditForm_Content').focus();
    					}
					},
					"Cancel": function() {
						$( this ).dialog( "close" );
						$('#Form_EditForm_Content').focus();
					}
				}
			});

		}

		function insertImageIntoEditor(link, title, editorType){
			if(editorType == 'markdown'){
				title = title ? '"' + title + '"' : '';
				$.markItUp({replaceWith:'![' + title + '](' + link + ' ' + title + ')'});
			}else if(editorType == 'wiki'){
				$.markItUp({replaceWith:'[[Image:' + link + '|' + title + ']]'});
			}
		}

		// autocomplete functionality for image field

		$( "#Form_ImagePickerForm_ExistingImage" ).livequery(function(){
			$(this).autocomplete({
				source: function( request, response ) {
					$.get(controllerurl + '/linklist', {term : request.term, type : 'image'}, function(data){
						if(data && data.length){
							var items = [];
							for (var id = 0; id < data.length; id++){
								items.push({
									label : data[id].Label,
									value : data[id].Title,
									id : data[id].ID,
									link : data[id].Link
								});
							}
							response(items);
						}
					});
				},
				minLength: 2,
				select: function( event, ui ) {
					$(this).val(ui.item.label);
					$(this).attr('data-id', ui.item.id);
					$(this).attr('data-link', ui.item.link);
				}
			});

		});

		// update dialog image input field on image type radio button change

		$('#Form_ImagePickerForm #Type input:radio').live('change', function(){
			type = $(this).val();
			if(type == 'new'){
				$('#NewImage').show().next().hide();
			}else if(type == 'existing'){
				$('#NewImage').hide().next().show();
			}
		});

		$('#NewImage').live('change', function(){

		});

		// link dialog window

		window.simpleWikiLinkDialog = function(editorType){

			$("#dialogContent").load(controllerurl + '/linkpicker').dialog({
		        title: "Insert a link",
				modal: true,
				autoOpen: true,
				height: 500,
				width: 500,
				open: function(event, ui){
					// hide title field for wiki editor as it is not supported
					if(editorType == 'wiki'){
						$('#dialogContent #Title').livequery(function(){
							$(this).hide();
						});
					}
				},
				buttons: {
					"Insert Link": function() {
						link ='';
						if($('#Type input:radio:checked').val() == 'external'){
							link = $('#Form_LinkPickerForm_Link').val();
						}else{
							link = $('#Form_LinkPickerForm_Link').attr('data-link');
						}

						// validate link selection
						if(!link){
							alert('Please select or insert a link');
							$('#Form_LinkPickerForm_Link').focus();
							return false;
						}

						if(editorType == 'markdown'){
							if($('#Form_LinkPickerForm_Title').val()){
								title = '"' + $('#Form_LinkPickerForm_Title').val() + '"';
							}else{
								title = '';
							}

							$.markItUp({
								openWith:'[',
								closeWith:'](' + link + ' ' + title + ')',
								placeHolder:'Your text to link here...'
							});
						}else if(editorType == 'wiki'){
							$.markItUp({
								openWith:'[' + link + ' ',
								closeWith:']',
								placeHolder:'Your text to link here...'
							});
						}

						$( this ).dialog( "close" );
						$('#Form_EditForm_Content').focus();
					},
					"Cancel": function() {
						$( this ).dialog( "close" );
						$('#Form_EditForm_Content').focus();
					}
				}
			});
		}

		// autocomplete functionality for link field

		$( "#Form_LinkPickerForm_Link" ).livequery(function(){
			if($('#Type input:radio:checked').val() != 'external'){
				$(this).autocomplete({
					source: function( request, response ) {
						$.get(controllerurl + '/linklist', {term : request.term, type : $('#Type input:radio:checked').val()}, function(data){
							if(data && data.length){
								console.log(data);
								var items = [];
								for (var id = 0; id < data.length; id++){
									items.push({
										label : data[id].Label,
										value : data[id].Title,
										id : data[id].ID,
										link : data[id].Link
									});
								}
								response(items);
							}
						});
					},
					minLength: 2,
					select: function( event, ui ) {
						$(this).val(ui.item.label);
						$(this).attr('data-id', ui.item.id);
						$(this).attr('data-link', ui.item.link);
					}
				});
			}
		});

		// update dialog link input field on link type radio button change

		$('#Form_LinkPickerForm #Type input:radio').live('change', function(){
			type = $(this).val();
			label = $('#Link label');
			input = $('#Link input');
			if(type == 'file'){
				label.text('Search by file name');
				input.val('');
			}else if(type == 'external'){
				label.text('Enter external link URL');
				input.val('http://');
			}else if(type == 'page'){
				label.text('Search by page title');
				input.val('');
			}
			input.focus();
		});

		// warn about formatting issues before changing editor type

		origin = $('#Form_EditForm_EditorType option:selected').val();
		$('#Form_EditForm_EditorType').change(function(){
			if($('#Form_EditForm_Content').val().length >10){
				choice = confirm('Changing the editor type with existing content can upset formatting. Change anyway?');
				if(choice == true){
					$('#Form_EditForm').submit();
				}else{
					$(this).val(origin);
				}
			}else{
				$('#Form_EditForm').submit();
			}
		});

	});
})(jQuery);

		<commit_msg>updateflag set to false on post callback, preview updated on image/link insert<commit_after>var controllerurl = location.pathname.replace(/edit$/, '').replace(/edit\/$/, '');

(function ($) {
	$().ready(function () {
		// find the save button and add in its accesskey... really need to
		// get around to patching this in core!
		$('#Form_StatusForm_action_startediting').attr('accesskey', 's');

		// add the lock update ping
		var timeout = $('#Form_EditForm_LockLength').val() ? $('#Form_EditForm_LockLength').val() * 1000 : 300000;

		if (timeout) {
			setInterval(function () {
				var url = $('#Form_EditForm_LockUpdate').val();
				$.post(url);
			}, timeout);
		}

		$('#Form_EditForm_LockUpdate')

		// toggle the new page form
		var createPageForm = $('#Form_CreatePageForm').hide();
		$('#Form_EditForm_action_addpage_t').click(function () {
			createPageForm.toggle();
		    return false;
		});

		// deleting an existing page
		$('#Form_EditForm_action_delete').click(function () {
			if (!confirm("Are you sure you want to delete this page?")) {
				return false;
			}
			return true;
		});

		// manage the creation of a new page
		createPageForm.submit(function () {
			if (!$(this).find('[@name="NewPageName"]').val()) {
				$(this).find('[@name="NewPageName"]').css('border', '1px solid red');
				return false;
			}
		});

		// preview stuff

		var previewdiv = $('#editorPreview');

		//initial preview load
		var updateFlag = true;
		updatePreview();
		updateFlag = false;

		var t = setInterval(updatePreview,5000);

		$('#Form_EditForm_Content').keyup(function(){
			updateFlag = true;
		});

		if($('.markitup').length > 0){
			previewdiv.hide();
			previewdiv.after('
show / hide preview
');
			updatePreview();

			$('#showPreview a').click(function(){
				previewdiv.toggle();
				$('#Form_EditForm_Content').focus();
				return false;
			});
		}

		function updatePreview(){
			if(updateFlag){
				$.post(
					previewdiv.attr('data-url'),
					{ content: $('#Form_EditForm_Content').val() },
					function(data){
						if(data){
							if(previewdiv.html() != data){
								previewdiv.html(data);
								updateFlag = false;
							}
						}
					}
				);
			}
		}

		// image dialog window

		window.simpleWikiImageDialog = function(editorType){
			$("#dialogContent").load(controllerurl + '/imagepicker').dialog({
		        title: "Insert an image",
				modal: true,
				autoOpen: true,
				height: 500,
				width: 500,
				open: function(event, ui){
					$('#ExistingImage').livequery(function(){
					 	$(this).hide()
					 });
				},
				buttons: {
					"Insert / upload image": function() {
						$this = $(this);
						type = $('#Type input:radio:checked').val();
						title = $('#Form_ImagePickerForm_Title').val();
						if(type == 'new'){
							if($('#Form_ImagePickerForm_NewImage').val().length > 0){
								$('#Form_ImagePickerForm').ajaxSubmit({
						        	dataType:  'json',
						        	url: controllerurl + '/imageupload',
						        	beforeSubmit: function(){
						        		$('#uploadingIcon').show();
						        	},
						        	success: function(data){
						        		// validate
										if(data.error){
						        			$('#uploadingIcon').hide();
						        			$('#Form_ImagePickerForm_NewImage').val('');
						        			alert(data.text);
						        			return false;
										}else{
											insertImageIntoEditor(data.link, title, editorType);
											$this.dialog( "close" );
											$('#Form_EditForm_Content').focus();
											updateFlag = true;
											updatePreview();

										}
						        	}

						   		});
							}else{
								alert("Please select an image to upload");
								return;
							}

    					}else{
    						insertImageIntoEditor($("#Form_ImagePickerForm_ExistingImage").attr('data-link'), title, editorType);
    						$( this ).dialog( "close" );
							$('#Form_EditForm_Content').focus();
							updateFlag = true;
							updatePreview();
    					}
					},
					"Cancel": function() {
						$( this ).dialog( "close" );
						$('#Form_EditForm_Content').focus();
					}
				}
			});

		}

		function insertImageIntoEditor(link, title, editorType){
			if(editorType == 'markdown'){
				title = title ? '"' + title + '"' : '';
				$.markItUp({replaceWith:'![' + title + '](' + link + ' ' + title + ')'});
			}else if(editorType == 'wiki'){
				$.markItUp({replaceWith:'[[Image:' + link + '|' + title + ']]'});
			}
		}

		// autocomplete functionality for image field

		$( "#Form_ImagePickerForm_ExistingImage" ).livequery(function(){
			$(this).autocomplete({
				source: function( request, response ) {
					$.get(controllerurl + '/linklist', {term : request.term, type : 'image'}, function(data){
						if(data && data.length){
							var items = [];
							for (var id = 0; id < data.length; id++){
								items.push({
									label : data[id].Label,
									value : data[id].Title,
									id : data[id].ID,
									link : data[id].Link
								});
							}
							response(items);
						}
					});
				},
				minLength: 2,
				select: function( event, ui ) {
					$(this).val(ui.item.label);
					$(this).attr('data-id', ui.item.id);
					$(this).attr('data-link', ui.item.link);
				}
			});

		});

		// update dialog image input field on image type radio button change

		$('#Form_ImagePickerForm #Type input:radio').live('change', function(){
			type = $(this).val();
			if(type == 'new'){
				$('#NewImage').show().next().hide();
			}else if(type == 'existing'){
				$('#NewImage').hide().next().show();
			}
		});

		$('#NewImage').live('change', function(){

		});

		// link dialog window

		window.simpleWikiLinkDialog = function(editorType){

			$("#dialogContent").load(controllerurl + '/linkpicker').dialog({
		        title: "Insert a link",
				modal: true,
				autoOpen: true,
				height: 500,
				width: 500,
				open: function(event, ui){
					// hide title field for wiki editor as it is not supported
					if(editorType == 'wiki'){
						$('#dialogContent #Title').livequery(function(){
							$(this).hide();
						});
					}
				},
				buttons: {
					"Insert Link": function() {
						link ='';
						if($('#Type input:radio:checked').val() == 'external'){
							link = $('#Form_LinkPickerForm_Link').val();
						}else{
							link = $('#Form_LinkPickerForm_Link').attr('data-link');
						}

						// validate link selection
						if(!link){
							alert('Please select or insert a link');
							$('#Form_LinkPickerForm_Link').focus();
							return false;
						}

						if(editorType == 'markdown'){
							if($('#Form_LinkPickerForm_Title').val()){
								title = '"' + $('#Form_LinkPickerForm_Title').val() + '"';
							}else{
								title = '';
							}

							$.markItUp({
								openWith:'[',
								closeWith:'](' + link + ' ' + title + ')',
								placeHolder:'Your text to link here...'
							});
						}else if(editorType == 'wiki'){
							$.markItUp({
								openWith:'[' + link + ' ',
								closeWith:']',
								placeHolder:'Your text to link here...'
							});
						}

						updateFlag = true;
						updatePreview();

						$( this ).dialog( "close" );
						$('#Form_EditForm_Content').focus();
					},
					"Cancel": function() {
						$( this ).dialog( "close" );
						$('#Form_EditForm_Content').focus();
					}
				}
			});
		}

		// autocomplete functionality for link field

		$( "#Form_LinkPickerForm_Link" ).livequery(function(){
			if($('#Type input:radio:checked').val() != 'external'){
				$(this).autocomplete({
					source: function( request, response ) {
						$.get(controllerurl + '/linklist', {term : request.term, type : $('#Type input:radio:checked').val()}, function(data){
							if(data && data.length){
								var items = [];
								for (var id = 0; id < data.length; id++){
									items.push({
										label : data[id].Label,
										value : data[id].Title,
										id : data[id].ID,
										link : data[id].Link
									});
								}
								response(items);
							}
						});
					},
					minLength: 2,
					select: function( event, ui ) {
						$(this).val(ui.item.label);
						$(this).attr('data-id', ui.item.id);
						$(this).attr('data-link', ui.item.link);
					}
				});
			}
		});

		// update dialog link input field on link type radio button change

		$('#Form_LinkPickerForm #Type input:radio').live('change', function(){
			type = $(this).val();
			label = $('#Link label');
			input = $('#Link input');
			if(type == 'file'){
				label.text('Search by file name');
				input.val('');
			}else if(type == 'external'){
				label.text('Enter external link URL');
				input.val('http://');
			}else if(type == 'page'){
				label.text('Search by page title');
				input.val('');
			}
			input.focus();
		});

		// warn about formatting issues before changing editor type

		origin = $('#Form_EditForm_EditorType option:selected').val();
		$('#Form_EditForm_EditorType').change(function(){
			if($('#Form_EditForm_Content').val().length >10){
				choice = confirm('Changing the editor type with existing content can upset formatting. Change anyway?');
				if(choice == true){
					$('#Form_EditForm').submit();
				}else{
					$(this).val(origin);
				}
			}else{
				$('#Form_EditForm').submit();
			}
		});

	});
})(jQuery);

		<|endoftext|>


<commit_before>function dbConnect() {
  var url      = require('url');
  var redisUrl = url.parse(process.env.REDIS_URL);
  var redis    = require('redis'),
      client   = redis.createClient(redisUrl.port, redisUrl.hostname);
  var key      = process.env.NB_REGION;
  return {
    client: client,
    key: key
  };
}

function readDb(callback) {
  var db = dbConnect();

  var value = JSON.stringify({'limit': '', 'sightings': ''});
  db.client.setnx(db.key, value);
  db.client.get(db.key, function(err, reply) {
    callback(reply);
    db.client.quit();
  });
}

function writeDb(value) {
  var db = dbConnect();

  db.client.set(db.key, value);
  db.client.quit();
}

function getSightings(prev, callback) {
  var http     = require('http');
  var endpoint = '/ws1.1/data/notable/region/recent?';
  var rgnType  = 'rtype=subnational1';
  var rgn      = '&r=' + process.env.NB_REGION;
  var fmt      = '&fmt=json';
  var back     = '&back=' + process.env.NB_BACK;
  var query    = rgnType + rgn + fmt + back;
  var path     = endpoint + query;
  var options = {
    host: 'ebird.org',
    port: 80,
    path: path,
    method: 'GET'
  };
  var req = http.request(options, function(res) {
    res.setEncoding('utf-8');
    var str = '';
    res.on('data', function (chunk) {
      str += chunk;
    });
    res.on('end', function() {
      var data = {};
      data.prev = prev;
      data.sightings = JSON.parse(str);
      callback(data);
    });
  });
  req.on('error', function(e) {
    logger('problem with request: ' + e.message);
  });
  req.end();
}

function dataHash(data) {
  var crypto = require('crypto');
  return crypto.createHash('md5').update(JSON.stringify(data)).digest('hex');
}

function valid(sightings) {
  var validNew = [];
  if (typeof sightings != 'undefined') {
    sightings.forEach(function(item, index) {
      if (item.obsValid === true) {
        validNew.push(item);
      }
    });
  }
  return validNew;
}

function unique(data) {
  var uniqSightings = [];
  var hashes        = [];
  if (typeof data != 'undefined') {
    var index;
    for (index = 0; index < data.length; ++index) {
      var hashId = dataHash(data[index]);
      if (hashes.indexOf(hashId) == -1) {
        hashes.push(hashId);
        uniqSightings.push(data[index]);
      }
    }
  }
  return uniqSightings;
}

function newBirds(data, prev) {
  if (typeof prev == 'undefined') {
    return data;
  } else {
    var novel = [];
    if (typeof data != 'undefined') {
      var index;
      for (index = 0; index < data.length; ++index) {
        var hashId = dataHash(data[index]);
        if (JSON.stringify(prev).indexOf(hashId) == -1) {
          novel.push(data[index]);
        }
      }
    }
    return novel;
  }
}

function tweet(sightings) {
  var twitter = require('twit');
  var T = new twitter({
      consumer_key:         process.env.NB_T_CONSUMER_KEY
    , consumer_secret:      process.env.NB_T_CONSUMER_SECRET
    , access_token:         process.env.NB_T_ACCESS_TOKEN
    , access_token_secret:  process.env.NB_T_ACCESS_TOKEN_SECRET
  });

  sightings.forEach(function(item, index) {
    var update = '';
    if (typeof item.howMany != 'undefined') {
      update = item.howMany + ' ';
    }
    update = update + item.comName;
    update = update + ' (' + item.sciName + ')';
    update = update + ' - ' + item.locName;
    update = update + ' - ' + item.obsDt;
    if (update.length + 23 <= 140) {
      var birdName = item.comName.replace(/ /g,"_").replace(/'/g,'');
      var birdLink = 'http://www.allaboutbirds.org/guide/' + birdName + '/id';
      update = update + ' ' + birdLink;
    }

    T.post('statuses/update', {
        status: update
      , lat: item.lat
      , long: item.lng
      , place_id: item.locName
    }, function(err, data, response) {
      if(err) {
        logger(err);
      }
    });
  });
}

function logger(msg) {
  var winston = require('winston');
  winston.add(winston.transports.File, {
    filename: '/var/log/nbirds/error.log'
  });
  winston.remove(winston.transports.Console);
  winston.error('Fail:' + msg);
}

function limitResetAll() {
  var limit = {};
  limit = limitResetFifteen(limit);
  limit = limitResetDaily(limit);
  return limit;
}

function limitResetFifteen(limit) {
  limit.fifteenMin = {};
  limit.fifteenMin.remain = 180;
  limit.fifteenMin.reset  = Math.round(Date.now() / 1000 + 15 * 60);
  return limit;
}

function limitResetDaily(limit) {
  limit.daily = {};
  limit.daily.remain = 2400;
  limit.daily.reset  = Math.round(Date.now() / 1000 + 24 * 60 * 60);
  return limit;
}

function limitReset(interval, limit) {
  if (interval == 'all') {
    limit = limitResetAll();
  } else if (interval == 'fifteenMin') {
    limit = limitResetFifteen(limit);
  } else if (interval == 'daily') {
    limit = limitResetDaily(limit);
  }
  return limit;
}

function rateLimit(limit) {
  if (limit === '') {
    limit = limitReset('all', limit);
  } else if (Math.round(Date.now() / 1000) >= limit.daily.reset) {
    limit = limitReset('daily', limit);
  } else if (Math.round(Date.now() / 1000) >= limit.fifteenMin.reset) {
    limit = limitReset('fifteenMin', limit);
  }
  return limit;
}

function truncateSightings(sightings, rl) {
  var daily = rl.daily.remain;
  var fifteenMin = rl.fifteenMin.remain;
  var limit = daily <= fifteenMin ? daily : fifteenMin;
  sightings.length = limit < sightings.length ? limit : sightings.length;
  return sightings;
}

function dbStore(prev, sightings, limit) {
  limit.fifteenMin.remain = limit.fifteenMin.remain - sightings.length;
  limit.daily.remain = limit.daily.remain - sightings.length;
  var timeStamp = Math.round(Date.now() / 1000);
  var stash = dbPrune(prev, timeStamp);
  var value = {};
  var index;
  for (index = 0; index < sightings.length; ++index) {
    var hashId = dataHash(sightings[index]);
    stash[hashId] = timeStamp;
  }
  value.sightings = stash;
  value.limit = limit;
  writeDb(JSON.stringify(value));
}

function dbPrune(prev, timeStamp) {
  if (typeof prev != 'undefined') {
    var stash = {};
    for (var index in prev) {
      if (prev[index] > timeStamp - (60 * 60 * 24 * (process.env.NB_BACK + 3))) {
        stash[index] = prev[index];
      }
    }
    return stash;
  }
}

function nbController() {
  readDb(function(prev) {
    getSightings(prev, function(data) {
      var uniq = unique(valid(data.sightings));
      var prev = JSON.parse(data.prev);
      var novel = newBirds(uniq, prev.sightings);
      var rl = rateLimit(prev.limit);
      var sightings = truncateSightings(novel, rl);
      tweet(sightings);
      dbStore(prev.sightings, sightings, rl);
    });
  });
}

setInterval(nbController, 3 * 60 * 1000);
<commit_msg>Improved logging implementation<commit_after>var winston = require('winston');
var logger = new (winston.Logger)({
  transports: [
    new (winston.transports.File)({ filename: '/var/log/nbirds/error.log' })
  ],
  exitOnError: false
});

function dbConnect() {
  var url      = require('url');
  var redisUrl = url.parse(process.env.REDIS_URL);
  var redis    = require('redis'),
      client   = redis.createClient(redisUrl.port, redisUrl.hostname);
  var key      = process.env.NB_REGION;
  return {
    client: client,
    key: key
  };
}

function readDb(callback) {
  var db = dbConnect();

  var value = JSON.stringify({'limit': '', 'sightings': ''});
  db.client.setnx(db.key, value);
  db.client.get(db.key, function(err, reply) {
    callback(reply);
    db.client.quit();
  });
}

function writeDb(value) {
  var db = dbConnect();

  db.client.set(db.key, value);
  db.client.quit();
}

function getSightings(prev, callback) {
  var http     = require('http');
  var endpoint = '/ws1.1/data/notable/region/recent?';
  var rgnType  = 'rtype=subnational1';
  var rgn      = '&r=' + process.env.NB_REGION;
  var fmt      = '&fmt=json';
  var back     = '&back=' + process.env.NB_BACK;
  var query    = rgnType + rgn + fmt + back;
  var path     = endpoint + query;
  var options = {
    host: 'ebird.org',
    port: 80,
    path: path,
    method: 'GET'
  };
  var req = http.request(options, function(res) {
    res.setEncoding('utf-8');
    var str = '';
    res.on('data', function (chunk) {
      str += chunk;
    });
    res.on('end', function() {
      var data = {};
      data.prev = prev;
      data.sightings = JSON.parse(str);
      callback(data);
    });
  });
  req.on('error', function(e) {
    logger.warn('problem with request: ' + e.message);
  });
  req.end();
}

function dataHash(data) {
  var crypto = require('crypto');
  return crypto.createHash('md5').update(JSON.stringify(data)).digest('hex');
}

function valid(sightings) {
  var validNew = [];
  if (typeof sightings != 'undefined') {
    sightings.forEach(function(item, index) {
      if (item.obsValid === true) {
        validNew.push(item);
      }
    });
  }
  return validNew;
}

function unique(data) {
  var uniqSightings = [];
  var hashes        = [];
  if (typeof data != 'undefined') {
    var index;
    for (index = 0; index < data.length; ++index) {
      var hashId = dataHash(data[index]);
      if (hashes.indexOf(hashId) == -1) {
        hashes.push(hashId);
        uniqSightings.push(data[index]);
      }
    }
  }
  return uniqSightings;
}

function newBirds(data, prev) {
  if (typeof prev == 'undefined') {
    return data;
  } else {
    var novel = [];
    if (typeof data != 'undefined') {
      var index;
      for (index = 0; index < data.length; ++index) {
        var hashId = dataHash(data[index]);
        if (JSON.stringify(prev).indexOf(hashId) == -1) {
          novel.push(data[index]);
        }
      }
    }
    return novel;
  }
}

function tweet(sightings) {
  var twitter = require('twit');
  var T = new twitter({
      consumer_key:         process.env.NB_T_CONSUMER_KEY
    , consumer_secret:      process.env.NB_T_CONSUMER_SECRET
    , access_token:         process.env.NB_T_ACCESS_TOKEN
    , access_token_secret:  process.env.NB_T_ACCESS_TOKEN_SECRET
  });

  sightings.forEach(function(item, index) {
    var update = '';
    if (typeof item.howMany != 'undefined') {
      update = item.howMany + ' ';
    }
    update = update + item.comName;
    update = update + ' (' + item.sciName + ')';
    update = update + ' - ' + item.locName;
    update = update + ' - ' + item.obsDt;
    if (update.length + 23 <= 140) {
      var birdName = item.comName.replace(/ /g,"_").replace(/'/g,'');
      var birdLink = 'http://www.allaboutbirds.org/guide/' + birdName + '/id';
      update = update + ' ' + birdLink;
    }

    T.post('statuses/update', {
        status: update
      , lat: item.lat
      , long: item.lng
      , place_id: item.locName
    }, function(err, data, response) {
      if(err) {
        logger.warn(err);
      }
    });
  });
}

function limitResetAll() {
  var limit = {};
  limit = limitResetFifteen(limit);
  limit = limitResetDaily(limit);
  return limit;
}

function limitResetFifteen(limit) {
  limit.fifteenMin = {};
  limit.fifteenMin.remain = 180;
  limit.fifteenMin.reset  = Math.round(Date.now() / 1000 + 15 * 60);
  return limit;
}

function limitResetDaily(limit) {
  limit.daily = {};
  limit.daily.remain = 2400;
  limit.daily.reset  = Math.round(Date.now() / 1000 + 24 * 60 * 60);
  return limit;
}

function limitReset(interval, limit) {
  if (interval == 'all') {
    limit = limitResetAll();
  } else if (interval == 'fifteenMin') {
    limit = limitResetFifteen(limit);
  } else if (interval == 'daily') {
    limit = limitResetDaily(limit);
  }
  return limit;
}

function rateLimit(limit) {
  if (limit === '') {
    limit = limitReset('all', limit);
  } else if (Math.round(Date.now() / 1000) >= limit.daily.reset) {
    limit = limitReset('daily', limit);
  } else if (Math.round(Date.now() / 1000) >= limit.fifteenMin.reset) {
    limit = limitReset('fifteenMin', limit);
  }
  return limit;
}

function truncateSightings(sightings, rl) {
  var daily = rl.daily.remain;
  var fifteenMin = rl.fifteenMin.remain;
  var limit = daily <= fifteenMin ? daily : fifteenMin;
  sightings.length = limit < sightings.length ? limit : sightings.length;
  return sightings;
}

function dbStore(prev, sightings, limit) {
  limit.fifteenMin.remain = limit.fifteenMin.remain - sightings.length;
  limit.daily.remain = limit.daily.remain - sightings.length;
  var timeStamp = Math.round(Date.now() / 1000);
  var stash = dbPrune(prev, timeStamp);
  var value = {};
  var index;
  for (index = 0; index < sightings.length; ++index) {
    var hashId = dataHash(sightings[index]);
    stash[hashId] = timeStamp;
  }
  value.sightings = stash;
  value.limit = limit;
  writeDb(JSON.stringify(value));
}

function dbPrune(prev, timeStamp) {
  if (typeof prev != 'undefined') {
    var stash = {};
    for (var index in prev) {
      if (prev[index] > timeStamp - (60 * 60 * 24 * (process.env.NB_BACK + 3))) {
        stash[index] = prev[index];
      }
    }
    return stash;
  }
}

function nbController() {
  readDb(function(prev) {
    getSightings(prev, function(data) {
      var uniq = unique(valid(data.sightings));
      var prev = JSON.parse(data.prev);
      var novel = newBirds(uniq, prev.sightings);
      var rl = rateLimit(prev.limit);
      var sightings = truncateSightings(novel, rl);
      tweet(sightings);
      dbStore(prev.sightings, sightings, rl);
    });
  });
}

setInterval(nbController, 3 * 60 * 1000);
<|endoftext|>


<commit_before>// Copyright 2017-2022, University of Colorado Boulder

/**
 * General help information for how to navigation a simulation with a keyboard. In general, this file supports a fair
 * number of options, like displaying group content, or shortcuts for checkbox interaction. The algorithm this type
 * implements set all the optional potential rows as null, and then fills them in if the options is provided. Then at the
 * end anything that is null is filtered out.
 *
 * @author Jesse Greenberg (PhET Interactive Simulations)
 * @author Michael Kauzmann (PhET Interactive Simulations)
 */

import merge from '../../../../phet-core/js/merge.js';
import sceneryPhet from '../../sceneryPhet.js';
import sceneryPhetStrings from '../../sceneryPhetStrings.js';
import TextKeyNode from '../TextKeyNode.js';
import KeyboardHelpIconFactory from './KeyboardHelpIconFactory.js';
import KeyboardHelpSection from './KeyboardHelpSection.js';
import KeyboardHelpSectionRow from './KeyboardHelpSectionRow.js';

class BasicActionsKeyboardHelpSection extends KeyboardHelpSection {

  /**
   * @param {Object} [options]
   */
  constructor( options ) {

    options = merge( {
      withCheckboxContent: false // if true, the help content will include information about how to interact with checkboxes
    }, options );

    // 'press buttons' content
    const spaceIcon = TextKeyNode.space();
    const pressButtonsItemRow = KeyboardHelpSectionRow.labelWithIcon(
      sceneryPhetStrings.keyboardHelpDialog.pressButtonsStringProperty, spaceIcon, {
        labelInnerContent: sceneryPhetStrings.a11y.keyboardHelpDialog.general.pressButtonsDescriptionStringProperty
      } );

    // 'exit a dialog' content
    const exitADialogIcon = TextKeyNode.esc();
    const exitADialogRow = KeyboardHelpSectionRow.labelWithIcon(
      sceneryPhetStrings.keyboardHelpDialog.exitADialogStringProperty, exitADialogIcon, {
        labelInnerContent: sceneryPhetStrings.a11y.keyboardHelpDialog.general.exitDialogDescriptionStringProperty
      } );

    // 'toggle checkboxes' content
    let toggleCheckboxes = null;
    if ( options.withCheckboxContent ) {
      toggleCheckboxes = KeyboardHelpSectionRow.labelWithIcon(
        sceneryPhetStrings.keyboardHelpDialog.toggleCheckboxesStringProperty, TextKeyNode.space(), {
          labelInnerContent: sceneryPhetStrings.a11y.keyboardHelpDialog.general.toggleCheckboxesDescriptionStringProperty
        } );
    }

    const leftRightArrowsIcon = KeyboardHelpIconFactory.leftRightArrowKeysRowIcon();
    const upDownArrowsIcon = KeyboardHelpIconFactory.upDownArrowKeysRowIcon();
    const moveBetweenItemsInAGroupRow = KeyboardHelpSectionRow.labelWithIcon(
      sceneryPhetStrings.keyboardHelpDialog.moveBetweenItemsInAGroupStringProperty,
      KeyboardHelpIconFactory.iconOrIcon( leftRightArrowsIcon, upDownArrowsIcon ), {
        labelInnerContent: sceneryPhetStrings.a11y.keyboardHelpDialog.general.groupNavigationDescriptionStringProperty
      } );

    const moveToNextItemRow = KeyboardHelpSectionRow.labelWithIcon(
      sceneryPhetStrings.keyboardHelpDialog.moveToNextItemOrGroupStringProperty,
      TextKeyNode.tab(), {
        labelInnerContent: sceneryPhetStrings.a11y.keyboardHelpDialog.general.tabGroupDescriptionStringProperty
      } );

    const moveToPreviousItemRow = KeyboardHelpSectionRow.labelWithIcon(
      sceneryPhetStrings.keyboardHelpDialog.moveToPreviousItemOrGroupStringProperty,
      KeyboardHelpIconFactory.shiftPlusIcon( TextKeyNode.tab() ), {
        labelInnerContent: sceneryPhetStrings.a11y.keyboardHelpDialog.general.shiftTabGroupDescriptionStringProperty
      } );

    const content = [
      moveToNextItemRow,
      moveToPreviousItemRow,
      moveBetweenItemsInAGroupRow,
      pressButtonsItemRow,
      toggleCheckboxes,
      exitADialogRow
    ].filter( row => row !== null ); // If any optional rows are null, omit them.

    // order the rows of content
    super( sceneryPhetStrings.keyboardHelpDialog.basicActionsStringProperty, content, options );
  }
}

sceneryPhet.register( 'BasicActionsKeyboardHelpSection', BasicActionsKeyboardHelpSection );
export default BasicActionsKeyboardHelpSection;<commit_msg>delete unnecessary local vars for icons<commit_after>// Copyright 2017-2022, University of Colorado Boulder

/**
 * General help information for how to navigation a simulation with a keyboard. In general, this file supports a fair
 * number of options, like displaying group content, or shortcuts for checkbox interaction. The algorithm this type
 * implements set all the optional potential rows as null, and then fills them in if the options is provided. Then at the
 * end anything that is null is filtered out.
 *
 * @author Jesse Greenberg (PhET Interactive Simulations)
 * @author Michael Kauzmann (PhET Interactive Simulations)
 */

import merge from '../../../../phet-core/js/merge.js';
import sceneryPhet from '../../sceneryPhet.js';
import sceneryPhetStrings from '../../sceneryPhetStrings.js';
import TextKeyNode from '../TextKeyNode.js';
import KeyboardHelpIconFactory from './KeyboardHelpIconFactory.js';
import KeyboardHelpSection from './KeyboardHelpSection.js';
import KeyboardHelpSectionRow from './KeyboardHelpSectionRow.js';

class BasicActionsKeyboardHelpSection extends KeyboardHelpSection {

  /**
   * @param {Object} [options]
   */
  constructor( options ) {

    options = merge( {
      withCheckboxContent: false // if true, the help content will include information about how to interact with checkboxes
    }, options );

    // 'press buttons' content
    const pressButtonsItemRow = KeyboardHelpSectionRow.labelWithIcon(
      sceneryPhetStrings.keyboardHelpDialog.pressButtonsStringProperty, TextKeyNode.space(), {
        labelInnerContent: sceneryPhetStrings.a11y.keyboardHelpDialog.general.pressButtonsDescriptionStringProperty
      } );

    // 'exit a dialog' content
    const exitADialogRow = KeyboardHelpSectionRow.labelWithIcon(
      sceneryPhetStrings.keyboardHelpDialog.exitADialogStringProperty, TextKeyNode.esc(), {
        labelInnerContent: sceneryPhetStrings.a11y.keyboardHelpDialog.general.exitDialogDescriptionStringProperty
      } );

    // 'toggle checkboxes' content
    let toggleCheckboxes = null;
    if ( options.withCheckboxContent ) {
      toggleCheckboxes = KeyboardHelpSectionRow.labelWithIcon(
        sceneryPhetStrings.keyboardHelpDialog.toggleCheckboxesStringProperty, TextKeyNode.space(), {
          labelInnerContent: sceneryPhetStrings.a11y.keyboardHelpDialog.general.toggleCheckboxesDescriptionStringProperty
        } );
    }

    const leftRightArrowsIcon = KeyboardHelpIconFactory.leftRightArrowKeysRowIcon();
    const upDownArrowsIcon = KeyboardHelpIconFactory.upDownArrowKeysRowIcon();
    const moveBetweenItemsInAGroupRow = KeyboardHelpSectionRow.labelWithIcon(
      sceneryPhetStrings.keyboardHelpDialog.moveBetweenItemsInAGroupStringProperty,
      KeyboardHelpIconFactory.iconOrIcon( leftRightArrowsIcon, upDownArrowsIcon ), {
        labelInnerContent: sceneryPhetStrings.a11y.keyboardHelpDialog.general.groupNavigationDescriptionStringProperty
      } );

    const moveToNextItemRow = KeyboardHelpSectionRow.labelWithIcon(
      sceneryPhetStrings.keyboardHelpDialog.moveToNextItemOrGroupStringProperty,
      TextKeyNode.tab(), {
        labelInnerContent: sceneryPhetStrings.a11y.keyboardHelpDialog.general.tabGroupDescriptionStringProperty
      } );

    const moveToPreviousItemRow = KeyboardHelpSectionRow.labelWithIcon(
      sceneryPhetStrings.keyboardHelpDialog.moveToPreviousItemOrGroupStringProperty,
      KeyboardHelpIconFactory.shiftPlusIcon( TextKeyNode.tab() ), {
        labelInnerContent: sceneryPhetStrings.a11y.keyboardHelpDialog.general.shiftTabGroupDescriptionStringProperty
      } );

    const content = [
      moveToNextItemRow,
      moveToPreviousItemRow,
      moveBetweenItemsInAGroupRow,
      pressButtonsItemRow,
      toggleCheckboxes,
      exitADialogRow
    ].filter( row => row !== null ); // If any optional rows are null, omit them.

    // order the rows of content
    super( sceneryPhetStrings.keyboardHelpDialog.basicActionsStringProperty, content, options );
  }
}

sceneryPhet.register( 'BasicActionsKeyboardHelpSection', BasicActionsKeyboardHelpSection );
export default BasicActionsKeyboardHelpSection;<|endoftext|>


<commit_before>
import TestUtils from 'react-addons-test-utils';
import React from 'react';
import ReactDOM from 'react-dom';

import Select from '../';

const { renderIntoDocument, Simulate } = TestUtils;
import identity from 'lodash/utility/identity';
import fixture from './fixture';
//onChangeSpy = jest.fn();

describe('The select ', () => {
    describe('when called with no props', () => {
        let component;
        beforeEach(
            () => {
                const shallowRenderer = TestUtils.createRenderer();
                shallowRenderer.render(<Select />);
                component = shallowRenderer.getRenderOutput();
            }
        );
        it('should render an empty select', () => {

        });
    });
    describe('when called with minimal props', () => {
        let component, domNode;
        const mockValues = fixture.VALUES;
        beforeEach(
            () => {
                component = renderIntoDocument(<Select name='selectName' values={mockValues} onChange={identity} />);
                domNode = ReactDOM.findDOMNode(component);
            }
        );
        it('should render an empty select', () => {
            // i18n.init({
            //     dev: {
            //         translation: {
            //             select:{
            //                 unSelected: '-',
            //                 noLabel: 'No label'
            //             }
            //         }
            //     }
            // }, () => {
            expect(domNode.tagName).toBe('DIV');
            expect(domNode.getAttribute('data-focus')).toBe('select');
            const domSelect = ReactDOM.findDOMNode(component.refs.htmlSelect);
            expect(domSelect.tagName).toBe('SELECT');
            expect(domSelect.options.length).toBe(mockValues.length + 1);
            expect(domSelect.options[0].value).toBe('UNSELECTED_KEY');
            //expect(domSelect.options[0].innerHTML).toBe('select.unSelected');
            expect(+domSelect.options[1].value).toBe(mockValues[0].code);
            expect(domSelect.options[1].innerHTML).toBe(mockValues[0].label);
            // });
        });
        it('should not have a value', () => {
            expect(component.getValue()).toBe(null);
        });
    });
    describe('when a value is provided', () => {
        const { VALUE, VALUES } = fixture;
        let component, domNode;
        beforeEach(() => {
            component = renderIntoDocument(<Select name='selectName' onChange={identity} value={VALUE} values={VALUES} />);
        });
        it('should return the value when provided as a props', () => {
            expect(component.getValue()).toBe(VALUE);
        });
        it('should render the value in the DOM', () => {
            expect(ReactDOM.findDOMNode(component.refs.htmlSelect).value).toBe(`${VALUE}`);
        });
    });
    describe('when there is a isActive in the select list', () => {
        const { VALUES, VALUE } = fixture;
        let component;
        beforeEach(() => {
            component = renderIntoDocument(<Select isRequired hasUndefined={false} name='selectName' onChange={identity} value={VALUE} values={VALUES} />);
        });
        describe('when there is list without isActive present', () => {
            it('shoud not filter the select list', () => {
                expect(ReactDOM.findDOMNode(component.refs.htmlSelect).options.length).toBe(VALUES.length);
            });
        });
        describe('when there is list with isActive present', () => {
            const { VALUES_ACTIVE, VALUE } = fixture;
            let component;
            beforeEach(() => {
                component = renderIntoDocument(<Select isRequired hasUndefined={false} name='selectName' onChange={identity} value={VALUE} values={VALUES_ACTIVE} />);
            });
            it('shoud filter the select list', () => {
                expect(ReactDOM.findDOMNode(component.refs.htmlSelect).options.length).toBe(2);
            });
        });
        describe('when there is list with a custom isActive present', () => {
            const { VALUES_CUSTOM_ACTIVE, VALUE } = fixture;
            let component;
            beforeEach(() => {
                component = renderIntoDocument(<Select isRequired hasUndefined={false} isActiveProperty='isActiveCustom' name='selectName' onChange={identity} value={VALUE} values={VALUES_CUSTOM_ACTIVE} />);
            });
            it('shoud filter the select list according to the custom value', () => {
                expect(ReactDOM.findDOMNode(component.refs.htmlSelect).options.length).toBe(2);
            });
        });
        describe('when there is list with a wrong custom isActive present', () => {
            const { VALUES_CUSTOM_ACTIVE, VALUE } = fixture;
            let component;
            beforeEach(() => {
                component = renderIntoDocument(<Select isActiveProperty='isActiveCustomPAPA' isRequired hasUndefined={false} name='selectName' onChange={identity} value={VALUE} values={VALUES_CUSTOM_ACTIVE} />);
            });
            it('shoud not filter the select list', () => {
                expect(ReactDOM.findDOMNode(component.refs.htmlSelect).options.length).toBe(VALUES_CUSTOM_ACTIVE.length);
            });
        });
    });
    describe('when the user select a new value', () => {
        const { VALUE, VALUES } = fixture;
        let onChangeSpy, component;
        beforeEach(
            () => {
                onChangeSpy = jest.fn();
                component = renderIntoDocument(<Select name='selectName' onChange={onChangeSpy} value={VALUE} values={VALUES} />);
            }
        );
        it('should call onChange with the new value', () => {
            Simulate.change(ReactDOM.findDOMNode(component.refs.htmlSelect), { target: { value: VALUE } });
            expect(onChangeSpy).toHaveBeenCalledTimes(1);
            expect(onChangeSpy).toHaveBeenCalledWith(VALUE);
        });
    });
    describe('when there is an error', () => {
        const { VALUE, VALUES } = fixture;
        const error = 'MY ERROR';
        let component;
        beforeEach(
            () => {
                component = renderIntoDocument(<Select error={error} name='selectName' onChange={identity} value={VALUE} values={VALUES} />);
            }
        );
        it('it should be displayed', () => {
            const errorDOMNode = ReactDOM.findDOMNode(component.refs.error);
            expect(errorDOMNode).toBeDefined();
            expect(errorDOMNode.innerHTML).toBe(error);
        });
    })
});
<commit_msg>Select input: error translation<commit_after>
import TestUtils from 'react-addons-test-utils';
import React from 'react';
import ReactDOM from 'react-dom';

import { init } from 'focus-core/translation';

import Select from '../';

const { renderIntoDocument, Simulate } = TestUtils;
import identity from 'lodash/utility/identity';
import fixture from './fixture';
//onChangeSpy = jest.fn();

const i18nConfig = {
    resources: {},
    lng: 'fr-FR'///langOpts.i18nCulture
};

describe('The select ', () => {
    beforeEach(() => {
        init(i18nConfig);
    });
    describe('when called with no props', () => {
        let component;
        beforeEach(
            () => {
                const shallowRenderer = TestUtils.createRenderer();
                shallowRenderer.render(<Select />);
                component = shallowRenderer.getRenderOutput();
            }
        );
        it('should render an empty select', () => {

        });
    });
    describe('when called with minimal props', () => {
        let component, domNode;
        const mockValues = fixture.VALUES;
        beforeEach(
            () => {
                component = renderIntoDocument(<Select name='selectName' values={mockValues} onChange={identity} />);
                domNode = ReactDOM.findDOMNode(component);
            }
        );
        it('should render an empty select', () => {
            // i18n.init({
            //     dev: {
            //         translation: {
            //             select:{
            //                 unSelected: '-',
            //                 noLabel: 'No label'
            //             }
            //         }
            //     }
            // }, () => {
            expect(domNode.tagName).toBe('DIV');
            expect(domNode.getAttribute('data-focus')).toBe('select');
            const domSelect = ReactDOM.findDOMNode(component.refs.htmlSelect);
            expect(domSelect.tagName).toBe('SELECT');
            expect(domSelect.options.length).toBe(mockValues.length + 1);
            expect(domSelect.options[0].value).toBe('UNSELECTED_KEY');
            //expect(domSelect.options[0].innerHTML).toBe('select.unSelected');
            expect(+domSelect.options[1].value).toBe(mockValues[0].code);
            expect(domSelect.options[1].innerHTML).toBe(mockValues[0].label);
            // });
        });
        it('should not have a value', () => {
            expect(component.getValue()).toBe(null);
        });
    });
    describe('when a value is provided', () => {
        const { VALUE, VALUES } = fixture;
        let component, domNode;
        beforeEach(() => {
            component = renderIntoDocument(<Select name='selectName' onChange={identity} value={VALUE} values={VALUES} />);
        });
        it('should return the value when provided as a props', () => {
            expect(component.getValue()).toBe(VALUE);
        });
        it('should render the value in the DOM', () => {
            expect(ReactDOM.findDOMNode(component.refs.htmlSelect).value).toBe(`${VALUE}`);
        });
    });
    describe('when there is a isActive in the select list', () => {
        const { VALUES, VALUE } = fixture;
        let component;
        beforeEach(() => {
            component = renderIntoDocument(<Select isRequired hasUndefined={false} name='selectName' onChange={identity} value={VALUE} values={VALUES} />);
        });
        describe('when there is list without isActive present', () => {
            it('shoud not filter the select list', () => {
                expect(ReactDOM.findDOMNode(component.refs.htmlSelect).options.length).toBe(VALUES.length);
            });
        });
        describe('when there is list with isActive present', () => {
            const { VALUES_ACTIVE, VALUE } = fixture;
            let component;
            beforeEach(() => {
                component = renderIntoDocument(<Select isRequired hasUndefined={false} name='selectName' onChange={identity} value={VALUE} values={VALUES_ACTIVE} />);
            });
            it('shoud filter the select list', () => {
                expect(ReactDOM.findDOMNode(component.refs.htmlSelect).options.length).toBe(2);
            });
        });
        describe('when there is list with a custom isActive present', () => {
            const { VALUES_CUSTOM_ACTIVE, VALUE } = fixture;
            let component;
            beforeEach(() => {
                component = renderIntoDocument(<Select isRequired hasUndefined={false} isActiveProperty='isActiveCustom' name='selectName' onChange={identity} value={VALUE} values={VALUES_CUSTOM_ACTIVE} />);
            });
            it('shoud filter the select list according to the custom value', () => {
                expect(ReactDOM.findDOMNode(component.refs.htmlSelect).options.length).toBe(2);
            });
        });
        describe('when there is list with a wrong custom isActive present', () => {
            const { VALUES_CUSTOM_ACTIVE, VALUE } = fixture;
            let component;
            beforeEach(() => {
                component = renderIntoDocument(<Select isActiveProperty='isActiveCustomPAPA' isRequired hasUndefined={false} name='selectName' onChange={identity} value={VALUE} values={VALUES_CUSTOM_ACTIVE} />);
            });
            it('shoud not filter the select list', () => {
                expect(ReactDOM.findDOMNode(component.refs.htmlSelect).options.length).toBe(VALUES_CUSTOM_ACTIVE.length);
            });
        });
    });
    describe('when the user select a new value', () => {
        const { VALUE, VALUES } = fixture;
        let onChangeSpy, component;
        beforeEach(
            () => {
                onChangeSpy = jest.fn();
                component = renderIntoDocument(<Select name='selectName' onChange={onChangeSpy} value={VALUE} values={VALUES} />);
            }
        );
        it('should call onChange with the new value', () => {
            Simulate.change(ReactDOM.findDOMNode(component.refs.htmlSelect), { target: { value: VALUE } });
            expect(onChangeSpy).toHaveBeenCalledTimes(1);
            expect(onChangeSpy).toHaveBeenCalledWith(VALUE);
        });
    });
    describe('when there is an error', () => {
        const { VALUE, VALUES } = fixture;
        const error = 'MY ERROR';
        let component;
        beforeEach(
            () => {
                component = renderIntoDocument(<Select error={error} name='selectName' onChange={identity} value={VALUE} values={VALUES} />);
            }
        );
        it('it should be displayed', () => {
            const errorDOMNode = ReactDOM.findDOMNode(component.refs.error);
            expect(errorDOMNode).toBeDefined();
            expect(errorDOMNode.innerHTML).toBe(error);
        });
    })
});
<|endoftext|>


<commit_before>define(['Backbone', 'socket'], function(Backbone, io){
  var SocketController = Backbone.View.extend({

    el: '#main-content',

    socketEvents: {
      'update estimations' : 'updateEstimations',
      'update players'     : 'updatePlayers',
      'closing'            : 'closeGame'
    },

    __initialize: function(){
      window.socket = io.connect();
      var view      = this;

      _.each(this.socketEvents, function(method, key){
        if(typeof view[method] === 'function'){
          socket.on(key, _.bind(view[method], view));
        }
      });

    },

    initialize: function(){
      this.message = this.options.router.playerMessage;

      this.message.on('change', this.showMessage, this);
      this.__initialize();
    },

    showMessage: function(){
      var message = this.message.get('message');
      this.$('.js-message').text(message);
    },

    updatePlayers: function(message, list){
      //Populate message
      this.message.set({ message: message });

      //Update player list
      var pls = _.map(list, function(v, k){return v;});
      this.options.router.players.update(pls);
    },

    updateEstimations: function(message, task, estimations){
      var router = this.options.router;
      var tasks  = router.project.taskList.collection;

      //Populate message
      this.message.set({ message: message });

      var taskModel = tasks.get(task);

      //Update task if selected
      if(taskModel){
        taskModel.set({estimated: estimations});
      }

    },

    closeGame: function(message, task, time){
      var router = this.options.router;
      var tasks  = router.project.taskList.collection;

      var taskModel = tasks.get(task);

      if(taskModel){
        var closed = taskModel.get('estimated') ? true : false;
        taskModel.set({closed: closed, time: time}).trigger('timer');
      }

    }

  });

  return SocketController;
});
<commit_msg>we need to save the estimations<commit_after>define(['Backbone', 'socket'], function(Backbone, io){
  var SocketController = Backbone.View.extend({

    el: '#main-content',

    socketEvents: {
      'update estimations' : 'updateEstimations',
      'update players'     : 'updatePlayers',
      'closing'            : 'closeGame'
    },

    __initialize: function(){
      window.socket = io.connect();
      var view      = this;

      _.each(this.socketEvents, function(method, key){
        if(typeof view[method] === 'function'){
          socket.on(key, _.bind(view[method], view));
        }
      });

    },

    initialize: function(){
      this.message = this.options.router.playerMessage;

      this.message.on('change', this.showMessage, this);
      this.__initialize();
    },

    showMessage: function(){
      var message = this.message.get('message');
      this.$('.js-message').text(message);
    },

    updatePlayers: function(message, list){
      //Populate message
      this.message.set({ message: message });

      //Update player list
      var pls = _.map(list, function(v, k){return v;});
      this.options.router.players.update(pls);
    },

    updateEstimations: function(message, task, estimations){
      var router = this.options.router;
      var tasks  = router.project.taskList.collection;

      //Populate message
      this.message.set({ message: message });

      var taskModel = tasks.get(task);

      //Update task if selected
      if(taskModel){
        taskModel.save({estimated: estimations});
      }

    },

    closeGame: function(message, task, time){
      var router = this.options.router;
      var tasks  = router.project.taskList.collection;

      var taskModel = tasks.get(task);

      if(taskModel){
        var closed = taskModel.get('estimated') ? true : false;
        taskModel.set({closed: closed, time: time}).trigger('timer');
      }

    }

  });

  return SocketController;
});
<|endoftext|>


<commit_before>const path = require('path');
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const port = require('../config/base.config').website_info.port;

function copyJs(fname) {
  return {
    entry: [
      path.resolve(__dirname, '../src/js/', fname)
    ],
    output: {
      path: path.resolve(__dirname, '../public/js'),
      publicPath: '/js',
      filename: fname
    },
    module: {
      rules: [{
        test: /\.js$/,
        use: ExtractTextPlugin.extract({
          use: [{
            loader: 'raw-loader'
          }]
        })
      }]
    },
    plugins: [
      new ExtractTextPlugin({
        filename: fname
      })
    ]
  }
}

[{
  entry: {
    client_struct:
    [
      'react-hot-loader/patch',
      'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=2000&reload=true',
      path.resolve(__dirname, '../src/js/client_struct.js')
    ],
    my_struct: [
      'react-hot-loader/patch',
      'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=2000&reload=true',
      path.resolve(__dirname, '../src/js/my_struct.js')
    ]
  },
  output: {
    library: 'MyStructs',
    path: path.resolve(__dirname, '../public/js'),
    publicPath: '/js',
    filename: '[name].min.js'
  },
  devtool: 'inline-source-map',
  devServer: {
    contentBase: path.resolve(__dirname, '../public'),
    hot: true,
    port: port,
    publicPath: '/',
    historyApiFallback: true
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: [{
          loader: 'babel-loader',
          options: {
            presets: ['es2015', 'react']
          }
        }]
      }
    ]
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NamedModulesPlugin(),
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify(process.env.NODE_ENV)
      }
    })
  ],
  resolve: {
    extensions: ['.js']
  }
},
copyJs('admin.js'),
copyJs('article_edit.js'),
copyJs('base.js'),
{
  entry: {
    admin: [
      'react-hot-loader/patch',
      //'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=2000&reload=true',
      path.resolve(__dirname, '../src/css/admin.scss')
    ],
    article_edit: [
      'react-hot-loader/patch',
      //'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=2000&reload=true',
      path.resolve(__dirname, '../src/css/article_edit.scss')
    ],
    base: path.resolve(__dirname, '../src/css/base.scss'),
    md: path.resolve(__dirname, '../src/css/md.scss'),
  },
  output: {
    path: path.resolve(__dirname, '../public/css'),
    publicPath: '/css',
    filename: '[name].css'
  },
  devtool: 'inline-source-map',
  devServer: {
    contentBase: path.resolve(__dirname, '../public'),
    publicPath: '/',
    hot: true,
    port: port,
    historyApiFallback: true
  },
  module: {
    rules: [{
      test: /\.scss$/,
      use: ExtractTextPlugin.extract({
        use: [{
          loader: 'css-loader',
          options: {
          }
        }, {
          loader: 'sass-loader'
        }]
      })
    }]
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NamedModulesPlugin(),
    new ExtractTextPlugin({
      filename: '[name].css'
    })
  ]
}]

/**
 * 单个 compiler 可以热重载，多个会无限重载，待修复
 * 还要把 style 的打包整合起来
 */
module.exports = [{
  name: "react-component-struct",
  entry: {
    client_struct:
    [
      'react-hot-loader/patch',
      'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=2000&reload=true',
      path.resolve(__dirname, '../src/js/client_struct.js')
    ],
    my_struct: [
      'react-hot-loader/patch',
      'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=2000&reload=true',
      path.resolve(__dirname, '../src/js/my_struct.js')
    ]
  },
  output: {
    library: 'MyStructs',
    path: path.resolve(__dirname, '../public/js'),
    publicPath: '/js',
    filename: '[name].min.js'
  },
  devtool: 'inline-source-map',
  devServer: {
    contentBase: path.resolve(__dirname, '../public'),
    hot: true,
    port: port,
    publicPath: '/',
    historyApiFallback: true
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: [{
          loader: 'babel-loader',
          options: {
            presets: ['es2015', 'react']
          }
        }]
      }
    ]
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NamedModulesPlugin(),
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify(process.env.NODE_ENV)
      }
    })
  ],
  resolve: {
    extensions: ['.js']
  }
},/*
{
  name: "raw-jsfile",
  entry: {
    article_edit: path.resolve(__dirname, '../src/js/article_edit.js'),
    admin: path.resolve(__dirname, '../src/js/admin.js'),
    base: path.resolve(__dirname, '../src/js/base.js')
  },
  output: {
    path: path.resolve(__dirname, '../public/js'),
    publicPath: '/js',
    filename: '[name].js'
  },
  devtool: 'inline-source-map',
  devServer: {
    contentBase: path.resolve(__dirname, '../public'),
    hot: true,
    port: port,
    publicPath: '/',
    historyApiFallback: true
  },
  module: {
    rules: [{
      test: /\.js$/,
      use: ExtractTextPlugin.extract({
        use: [{
          loader: 'raw-loader'
        }]
      })
    }]
  },
  plugins: [
    new ExtractTextPlugin({
      filename: '[name].js'
    })
  ]
}*/
]<commit_msg>problem to be fixed<commit_after>const path = require('path');
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const port = require('../config/base.config').website_info.port;

/*
function copyJs(fname) {
  return {
    entry: [
      path.resolve(__dirname, '../src/js/', fname)
    ],
    output: {
      path: path.resolve(__dirname, '../public/js'),
      publicPath: '/js',
      filename: fname
    },
    module: {
      rules: [{
        test: /\.js$/,
        use: ExtractTextPlugin.extract({
          use: [{
            loader: 'raw-loader'
          }]
        })
      }]
    },
    plugins: [
      new ExtractTextPlugin({
        filename: fname
      })
    ]
  }
}

[{
  entry: {
    client_struct:
    [
      'react-hot-loader/patch',
      'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=2000&reload=true',
      path.resolve(__dirname, '../src/js/client_struct.js')
    ],
    my_struct: [
      'react-hot-loader/patch',
      'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=2000&reload=true',
      path.resolve(__dirname, '../src/js/my_struct.js')
    ]
  },
  output: {
    library: 'MyStructs',
    path: path.resolve(__dirname, '../public/js'),
    publicPath: '/js',
    filename: '[name].min.js'
  },
  devtool: 'inline-source-map',
  devServer: {
    contentBase: path.resolve(__dirname, '../public'),
    hot: true,
    port: port,
    publicPath: '/',
    historyApiFallback: true
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: [{
          loader: 'babel-loader',
          options: {
            presets: ['es2015', 'react']
          }
        }]
      }
    ]
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NamedModulesPlugin(),
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify(process.env.NODE_ENV)
      }
    })
  ],
  resolve: {
    extensions: ['.js']
  }
},
copyJs('admin.js'),
copyJs('article_edit.js'),
copyJs('base.js'),
{
  entry: {
    admin: [
      'react-hot-loader/patch',
      //'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=2000&reload=true',
      path.resolve(__dirname, '../src/css/admin.scss')
    ],
    article_edit: [
      'react-hot-loader/patch',
      //'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=2000&reload=true',
      path.resolve(__dirname, '../src/css/article_edit.scss')
    ],
    base: path.resolve(__dirname, '../src/css/base.scss'),
    md: path.resolve(__dirname, '../src/css/md.scss'),
  },
  output: {
    path: path.resolve(__dirname, '../public/css'),
    publicPath: '/css',
    filename: '[name].css'
  },
  devtool: 'inline-source-map',
  devServer: {
    contentBase: path.resolve(__dirname, '../public'),
    publicPath: '/',
    hot: true,
    port: port,
    historyApiFallback: true
  },
  module: {
    rules: [{
      test: /\.scss$/,
      use: ExtractTextPlugin.extract({
        use: [{
          loader: 'css-loader',
          options: {
          }
        }, {
          loader: 'sass-loader'
        }]
      })
    }]
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NamedModulesPlugin(),
    new ExtractTextPlugin({
      filename: '[name].css'
    })
  ]
}]*/

/**
 * 单个 compiler 可以热重载，多个会无限重载，待修复
 * 还要把 style 的打包整合起来
 */
module.exports = [{
  name: "react-component-struct",
  entry: {
    client_struct:
    [
      'react-hot-loader/patch',
      'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=2000&reload=true',
      path.resolve(__dirname, '../src/js/client_struct.js')
    ],
    my_struct: [
      'react-hot-loader/patch',
      'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=2000&reload=true',
      path.resolve(__dirname, '../src/js/my_struct.js')
    ]
  },
  output: {
    library: 'MyStructs',
    path: path.resolve(__dirname, '../public/js'),
    publicPath: '/js',
    filename: '[name].min.js'
  },
  devtool: 'inline-source-map',
  devServer: {
    contentBase: path.resolve(__dirname, '../public'),
    hot: true,
    port: port,
    publicPath: '/',
    historyApiFallback: true
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: [{
          loader: 'babel-loader',
          options: {
            presets: ['es2015', 'react']
          }
        }]
      }
    ]
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NamedModulesPlugin(),
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify(process.env.NODE_ENV)
      }
    })
  ],
  resolve: {
    extensions: ['.js']
  }
},/*
{
  name: "raw-jsfile",
  entry: {
    article_edit: path.resolve(__dirname, '../src/js/article_edit.js'),
    admin: path.resolve(__dirname, '../src/js/admin.js'),
    base: path.resolve(__dirname, '../src/js/base.js')
  },
  output: {
    path: path.resolve(__dirname, '../public/js'),
    publicPath: '/js',
    filename: '[name].js'
  },
  devtool: 'inline-source-map',
  devServer: {
    contentBase: path.resolve(__dirname, '../public'),
    hot: true,
    port: port,
    publicPath: '/',
    historyApiFallback: true
  },
  module: {
    rules: [{
      test: /\.js$/,
      use: ExtractTextPlugin.extract({
        use: [{
          loader: 'raw-loader'
        }]
      })
    }]
  },
  plugins: [
    new ExtractTextPlugin({
      filename: '[name].js'
    })
  ]
}*/
]<|endoftext|>


<commit_before>'use strict';

function EntityWall(world, entity) {
    this.__init__(world, entity);
}

EntityWall.prototype = {

    __init__: function(world, entity) {
        this.all_entities = [];

        var was_loaded = false;

        if (!is_defined(entity)) {
            this.create_new(world);
        } else {
            was_loaded = true;
            this.load_from_entity(world, entity);
        }
        this.base_wall_init_start();

        if (was_loaded) {
            this.create_new_finalize();
        } else {
            this.load_from_entity_finalize();
        }
        this.base_wall_init_end();
    },
    get_all_entities: function() {
        return this.all_entities;
    },

    _edit_entity: function(entity, entity_button) {
        this.wall_entity_editor.edit_entity(entity, entity_button);
    },

    add_entity: function (entity) {
        // TODO : This needs to dynamically update.
        entity.set_property(ENTITY_PROPERTY_GROUP_NAME, this.entity_wall_title.get_text());

        var entity_name = entity.get_value(ENTITY_PROPERTY_NAME);
        var entity_relative_id = entity.get_relative_id();
        var entity_button = this.entity_wall.add_row(null, entity_relative_id).add_2D_button([0, 1], entity_name, COLOR_YELLOW, null);
        entity_button.set_engage_function(this._edit_entity.bind(this, entity, entity_button));

        this.all_entities.push(entity);

        this.base_wall.refresh_position_and_look_at();
    },

    delete_entity: function(entity) {
        var entity_reference_to_remove = -1;
        for (var e = 0; e < this.all_entities.length; e++) {
            if (this.all_entities[e] === entity) {
                entity_reference_to_remove = e;
                break;
            }
        }
        if (entity_reference_to_remove !== NOT_FOUND) {
            this.all_entities.splice(entity_reference_to_remove, 1);
        }

        var entity_name = entity.get_value(ENTITY_PROPERTY_NAME);
        var entity_relative_id = entity.get_relative_id();
        this.entity_wall.delete_row_by_name(entity_relative_id);
        MANAGER_ENTITY.delete_entity_by_id(entity_relative_id);

        this.base_wall.refresh_position_and_look_at();
    },

    /*        ___                 __   __   ___      ___         __                __           __        __          __
     | |\ | |  |  |  /\  |       /  ` |__) |__   /\   |  | |\ | / _`     /\  |\ | |  \    |    /  \  /\  |  \ | |\ | / _`
     | | \| |  |  | /~~\ |___    \__, |  \ |___ /~~\  |  | | \| \__>    /~~\ | \| |__/    |___ \__/ /~~\ |__/ | | \| \__> */
    base_wall_init_start: function() {
        // Create the standard functionality of the entity wall.
        this.create_new_entity_button = this.base_wall.add_row(0).add_2D_button([0, 1], 'create new entity', COLOR_GREEN, null);
        // TODO : Create a button for deleting the entity wall!!

        this.base_wall.world.root_attachables.push(this.base_wall);

        this.entity_wall = this.base_wall.add_floating_wall_attachment(this.base_wall.width * .8, this.base_wall.height * .8, 0, 0, 5, false);
        this.entity_wall.set_background_color(COLOR_BLACK, true);
    },

    base_wall_init_end: function() {
        // Wall used for creating new entities and editing existing entities.
        this.wall_entity_editor = new EntityEditor(this);
        this.wall_entity_editor.set_create_entity_display_button(this.create_new_entity_button);

        this.base_wall.refresh_position_and_look_at();
    },

    create_new: function(world) {
        var data = get_player_blink_spot(200);

        this.base_wall = new FloatingWall(400, 600, data[0], data[1], world, true);
        this.entity_wall_title = this.base_wall.add_row(-1).add_3D_element('Entity Wall', TYPE_INPUT);

        this.base_wall.set_to_saveable(world.entity);
    },

    create_new_finalize: function() {
        this.entity_wall.set_to_saveable();
        this.entity_wall_entity = this.entity_wall.get_self_entity();
        this.entity_wall._entity.set_property(ENTITY_DEFAULT_PROPERTY_TYPE, ENTITY_TYPE_ENTITY_WALL);
        this.entity_wall._entity.add_parent(this.base_wall.get_self_entity());
    },

    load_from_entity: function(world, entity) {
        this.entity_wall_entity = entity;

        // Load the base wall.
        this.base_wall = new FloatingWall(400, 600, null, null, world, true);
        this.base_wall.load_from_entity_data(this.entity_wall_entity.get_parent());
        this.base_wall.refresh_position_and_look_at();

        this.entity_wall_title = this.base_wall.get_row_with_index(-1).elements[0];
    },

    load_from_entity_finalize: function() {
        var number_of_children = this.entity_wall_entity.number_of_children();
        for (var e = 0; e < number_of_children; e++) {
            this.add_entity(this.entity_wall_entity.children[e]);
        }
    }

};
<commit_msg>general updating 60<commit_after>'use strict';

function EntityWall(world, entity) {
    this.__init__(world, entity);
}

EntityWall.prototype = {

    __init__: function(world, entity) {
        this.all_entities = [];

        var was_loaded = false;

        if (!is_defined(entity)) {
            this.create_new(world);
        } else {
            was_loaded = true;
            this.load_from_entity(world, entity);
        }
        this.base_wall_init_start();

        if (was_loaded) {
            this.create_new_finalize();
        } else {
            this.load_from_entity_finalize();
        }
        this.base_wall_init_end();
    },
    get_all_entities: function() {
        return this.all_entities;
    },

    _edit_entity: function(entity, entity_button) {
        this.wall_entity_editor.edit_entity(entity, entity_button);
    },

    add_entity: function (entity) {
        // TODO : This needs to dynamically update.
        entity.set_property(ENTITY_PROPERTY_GROUP_NAME, this.entity_wall_title.get_text());

        var entity_name = entity.get_value(ENTITY_PROPERTY_NAME);
        var entity_relative_id = entity.get_relative_id();
        var entity_button = this.entity_wall.add_row(null, entity_relative_id).add_2D_button([0, 1], entity_name, COLOR_YELLOW, null);
        entity_button.set_engage_function(this._edit_entity.bind(this, entity, entity_button));

        this.all_entities.push(entity);

        this.base_wall.refresh_position_and_look_at();
    },

    delete_entity: function(entity) {
        var entity_reference_to_remove = -1;
        for (var e = 0; e < this.all_entities.length; e++) {
            if (this.all_entities[e] === entity) {
                entity_reference_to_remove = e;
                break;
            }
        }
        if (entity_reference_to_remove !== NOT_FOUND) {
            this.all_entities.splice(entity_reference_to_remove, 1);
        }

        var entity_name = entity.get_value(ENTITY_PROPERTY_NAME);
        var entity_relative_id = entity.get_relative_id();
        this.entity_wall.delete_row_by_name(entity_relative_id);
        MANAGER_ENTITY.delete_entity_by_id(entity_relative_id);

        this.base_wall.refresh_position_and_look_at();
    },

    /*        ___                 __   __   ___      ___         __                __           __        __          __
     | |\ | |  |  |  /\  |       /  ` |__) |__   /\   |  | |\ | / _`     /\  |\ | |  \    |    /  \  /\  |  \ | |\ | / _`
     | | \| |  |  | /~~\ |___    \__, |  \ |___ /~~\  |  | | \| \__>    /~~\ | \| |__/    |___ \__/ /~~\ |__/ | | \| \__> */
    base_wall_init_start: function() {
        // Create the standard functionality of the entity wall.
        this.create_new_entity_button = this.base_wall.add_row(0).add_2D_button([0, 1], 'create new entity', COLOR_GREEN, null);
        // TODO : Create a button for deleting the entity wall!!

        this.base_wall.world.root_attachables.push(this.base_wall);

        this.entity_wall = this.base_wall.add_floating_wall_attachment(this.base_wall.width * .8, this.base_wall.height * .8, 0, 0, 5, false);
        this.entity_wall.set_background_color(COLOR_BLACK, true);
    },

    base_wall_init_end: function() {
        // Wall used for creating new entities and editing existing entities.
        this.wall_entity_editor = new EntityEditor(this);
        this.wall_entity_editor.set_create_entity_display_button(this.create_new_entity_button);

        this.base_wall.refresh_position_and_look_at();
    },

    create_new: function(world) {
        var data = get_player_blink_spot(200);

        this.base_wall = new FloatingWall(400, 600, data[0], data[1], world, true);
        this.entity_wall_title = this.base_wall.add_row(-1).add_3D_element('Entity Wall', TYPE_INPUT);

        this.base_wall.set_to_saveable(world.entity);
    },

    create_new_finalize: function() {
        this.entity_wall.set_to_saveable();
        this.entity_wall_entity = this.entity_wall.get_self_entity();
        this.entity_wall._entity.set_property(ENTITY_DEFAULT_PROPERTY_TYPE, ENTITY_TYPE_ENTITY_WALL);
        this.entity_wall._entity.add_parent(this.base_wall.get_self_entity());
    },

    load_from_entity: function(world, entity) {
        this.entity_wall_entity = entity;

        // Load the base wall.
        this.base_wall = new FloatingWall(400, 600, null, null, world, true);
        this.base_wall.load_from_entity_data(this.entity_wall_entity.get_parent());
        this.base_wall.refresh_position_and_look_at();

        this.entity_wall_title = this.base_wall.get_row_with_index(-1).elements[0];
    },

    load_from_entity_finalize: function() {
        l(this.entity_wall_entity);
        var number_of_children = this.entity_wall_entity.number_of_children();
        for (var e = 0; e < number_of_children; e++) {
            this.add_entity(this.entity_wall_entity.children[e]);
        }
    }

};
<|endoftext|>