<commit_before>tool.minDistance = 10;
tool.maxDistance = 45;

// Initialise Socket.io
var socket = io.connect('/');

// Random User ID
// Used when sending data
var uid = (function () {
  var S4 = function () {
    return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
  };
  return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
}());

function getParameterByName(name)
{
  name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
  var regexS = "[\\?&]" + name + "=([^&#]*)";
  var regex = new RegExp(regexS);
  var results = regex.exec(window.location.search);
  if(results == null) {
    return "";
  }
  else {
    return decodeURIComponent(results[1].replace(/\+/g, " "));
  }
}

// Join the room
var room = window.location.pathname.split("/")[2];
socket.emit('subscribe', { room: room });

// JSON data ofthe users current drawing
// Is sent to the user
var path_to_send = {};

// Calculates colors
var active_color_rgb;
var active_color_json = {};
var $opacity = $('#opacity');
var update_active_color = function (r, g, b) {
  console.log(r + ", " + g + ", " + b );
  var rgb_array = $('.active').attr('data-color').split(',');
  var red = rgb_array[0] / 255;
  if (r) {
    red = r / 255;
  }
  var green = rgb_array[1] / 255;
  if (g) {
    green = g / 255;
  }
  var blue = rgb_array[2] / 255;
  if (b) {
    blue = b / 255;
  }
  var opacity = $opacity.val() / 255;

  active_color_rgb = new RgbColor(red, green, blue, opacity);
  active_color_rgb._alpha = opacity;

  active_color_json = {
    "red": red,
    "green": green,
    "blue": blue,
    "opacity": opacity
  };

};

// Get the active color from the UI eleements
var authorColor = getParameterByName('authorColor');
var authorColors = {};
if (authorColor != "" && authorColor.substr(0,4) == "rgb(") {
  authorColor = authorColor.substr(4, authorColor.indexOf(")")-4);
  authorColors = authorColor.split(",");
  if (authorColors.length == 3) {
    update_active_color(authorColors[0], authorColors[1], authorColors[2]);
  } else {
    update_active_color();
  }
}

// ---------------------------------
// DRAWING EVENTS

var send_paths_timer;
var timer_is_active = false;

function onMouseDown(event) {

  var point = event.point;

  path = new Path();
  path.fillColor = active_color_rgb;
  path.add(event.point);
  view.draw();

  // The data we will send every 100ms on mouse drag
  path_to_send = {
    rgba: active_color_json,
    start: event.point,
    path: []
  };

}

function onMouseDrag(event) {

  var step = event.delta / 2;
  step.angle += 90;

  var top = event.middlePoint + step;
  var bottom = event.middlePoint - step;

  path.add(top);
  path.insert(0, bottom);
  path.smooth();
  view.draw();

  // Add data to path
  path_to_send.path.push({
    top: top,
    bottom: bottom
  });

  // Send paths every 100ms
  if (!timer_is_active) {

    send_paths_timer = setInterval(function () {

      socket.emit('draw:progress', room, uid, JSON.stringify(path_to_send));
      path_to_send.path = new Array();

    }, 100);

  }

  timer_is_active = true;

}

function onMouseUp(event) {

  // Close the users path
  path.add(event.point);
  path.closed = true;
  path.smooth();
  view.draw();

  // Send the path to other users
  path_to_send.end = event.point;
  socket.emit('draw:end', room, uid, JSON.stringify(path_to_send));

  // Stop new path data being added & sent
  clearInterval(send_paths_timer);
  path_to_send.path = new Array();
  timer_is_active = false;

}

// ---------------------------------
// CONTROLS EVENTS

var $color = $('.color');
$color.on('click', function () {

  $color.removeClass('active');
  $(this).addClass('active');

  update_active_color();

});

$opacity.on('change', function () {

  update_active_color();

});

$('#clearCanvas').on('click', function() {
  clearCanvas();
  socket.emit('canvas:clear', room);
});

$('#exportSVG').on('click', function() {
  exportSVG();
});

$('#exportPNG').on('click', function() {
  exportPNG();
});

function clearCanvas() {
  // Remove all but the active layer
  if (project.layers.length > 1) {
    var activeLayerID = project.activeLayer._id;
    for (var i=0; i<project.layers.length; i++) {
      if (project.layers[i]._id != activeLayerID) {
        project.layers[i].remove();
        i--;
      }
    }
  }

  // Remove all of the children from the active layer
  if (paper.project.activeLayer && paper.project.activeLayer.hasChildren()) {
    paper.project.activeLayer.removeChildren();
  }
  view.draw();
}

function exportSVG() {
  //console.log(paper.project.exportSVG());
  var svg = paper.project.exportSVG();
  encodeAsImgAndLink(svg);
}

// Encodes svg as a base64 text and opens a new browser window
// to the svg image that can be saved as a .svg on the users
// local filesystem. This skips making a round trip to the server
// for a POST.
function encodeAsImgAndLink(svg){
  // Add some critical information
  svg.setAttribute('version', '1.1');
  svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');

  var dummy = document.createElement('div');
  dummy.appendChild(svg);

  var b64 = Base64.encode(dummy.innerHTML);

  window.open("data:image/svg+xml;base64,\n"+b64);
}

function exportPNG() {
  var canvas = document.getElementById('myCanvas');
  window.open(canvas.toDataURL('image/png'));
}

// ---------------------------------
// SOCKET.IO EVENTS

socket.on('draw:progress', function (artist, data) {

  // It wasnt this user who created the event
  if (artist !== uid && data) {
    progress_external_path(JSON.parse(data), artist);
  }

});

socket.on('draw:end', function (artist, data) {

  // It wasnt this user who created the event
  if (artist !== uid && data) {
    end_external_path(JSON.parse(data), artist);
  }

});

socket.on('user:connect', function (user_count) {
  update_user_count(user_count);
});

socket.on('user:disconnect', function (user_count) {
  update_user_count(user_count);
});

socket.on('project:load', function (json) {
  paper.project.importJSON(json.project);
  view.draw();
});

socket.on('project:load:error', function() {
  $('#lostConnection').show();
});

socket.on('canvas:clear', function() {
  clearCanvas();
});

// ---------------------------------
// SOCKET.IO EVENT FUNCTIONS

// Updates the active connections
var $user_count = $('#userCount');
var $user_count_wrapper = $('#userCountWrapper');

function update_user_count(count) {

  $user_count_wrapper.css('opacity', 1);
  $user_count.text((count === 1) ? " just you, why not invite some friends?" : " " + count);

}

var external_paths = {};

// Ends a path
var end_external_path = function (points, artist) {

  var path = external_paths[artist];

  if (path) {

    // Close the path
    path.add(new Point(points.end[1], points.end[2]));
    path.closed = true;
    path.smooth();
    view.draw();

    // Remove the old data
    external_paths[artist] = false;

  }

};

// Continues to draw a path in real time
progress_external_path = function (points, artist) {

  var path = external_paths[artist];

  // The path hasnt already been started
  // So start it
  if (!path) {

    // Creates the path in an easy to access way
    external_paths[artist] = new Path();
    path = external_paths[artist];

    // Starts the path
    var start_point = new Point(points.start[1], points.start[2]);
    var color = new RgbColor(points.rgba.red, points.rgba.green, points.rgba.blue, points.rgba.opacity);
    path.fillColor = color;
    path.add(start_point);

  }

  // Draw all the points along the length of the path
  var paths = points.path;
  var length = paths.length;
  for (var i = 0; i < length; i++) {

    path.add(new Point(paths[i].top[1], paths[i].top[2]));
    path.insert(0, new Point(paths[i].bottom[1], paths[i].bottom[2]));

  }

  path.smooth();
  view.draw();

};<commit_msg>Removed log statement<commit_after>tool.minDistance = 10;
tool.maxDistance = 45;

// Initialise Socket.io
var socket = io.connect('/');

// Random User ID
// Used when sending data
var uid = (function () {
  var S4 = function () {
    return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
  };
  return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
}());

function getParameterByName(name)
{
  name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
  var regexS = "[\\?&]" + name + "=([^&#]*)";
  var regex = new RegExp(regexS);
  var results = regex.exec(window.location.search);
  if(results == null) {
    return "";
  }
  else {
    return decodeURIComponent(results[1].replace(/\+/g, " "));
  }
}

// Join the room
var room = window.location.pathname.split("/")[2];
socket.emit('subscribe', { room: room });

// JSON data ofthe users current drawing
// Is sent to the user
var path_to_send = {};

// Calculates colors
var active_color_rgb;
var active_color_json = {};
var $opacity = $('#opacity');
var update_active_color = function (r, g, b) {
  var rgb_array = $('.active').attr('data-color').split(',');
  var red = rgb_array[0] / 255;
  if (r) {
    red = r / 255;
  }
  var green = rgb_array[1] / 255;
  if (g) {
    green = g / 255;
  }
  var blue = rgb_array[2] / 255;
  if (b) {
    blue = b / 255;
  }
  var opacity = $opacity.val() / 255;

  active_color_rgb = new RgbColor(red, green, blue, opacity);
  active_color_rgb._alpha = opacity;

  active_color_json = {
    "red": red,
    "green": green,
    "blue": blue,
    "opacity": opacity
  };

};

// Get the active color from the UI eleements
var authorColor = getParameterByName('authorColor');
var authorColors = {};
if (authorColor != "" && authorColor.substr(0,4) == "rgb(") {
  authorColor = authorColor.substr(4, authorColor.indexOf(")")-4);
  authorColors = authorColor.split(",");
  if (authorColors.length == 3) {
    update_active_color(authorColors[0], authorColors[1], authorColors[2]);
  } else {
    update_active_color();
  }
}

// ---------------------------------
// DRAWING EVENTS

var send_paths_timer;
var timer_is_active = false;

function onMouseDown(event) {

  var point = event.point;

  path = new Path();
  path.fillColor = active_color_rgb;
  path.add(event.point);
  view.draw();

  // The data we will send every 100ms on mouse drag
  path_to_send = {
    rgba: active_color_json,
    start: event.point,
    path: []
  };

}

function onMouseDrag(event) {

  var step = event.delta / 2;
  step.angle += 90;

  var top = event.middlePoint + step;
  var bottom = event.middlePoint - step;

  path.add(top);
  path.insert(0, bottom);
  path.smooth();
  view.draw();

  // Add data to path
  path_to_send.path.push({
    top: top,
    bottom: bottom
  });

  // Send paths every 100ms
  if (!timer_is_active) {

    send_paths_timer = setInterval(function () {

      socket.emit('draw:progress', room, uid, JSON.stringify(path_to_send));
      path_to_send.path = new Array();

    }, 100);

  }

  timer_is_active = true;

}

function onMouseUp(event) {

  // Close the users path
  path.add(event.point);
  path.closed = true;
  path.smooth();
  view.draw();

  // Send the path to other users
  path_to_send.end = event.point;
  socket.emit('draw:end', room, uid, JSON.stringify(path_to_send));

  // Stop new path data being added & sent
  clearInterval(send_paths_timer);
  path_to_send.path = new Array();
  timer_is_active = false;

}

// ---------------------------------
// CONTROLS EVENTS

var $color = $('.color');
$color.on('click', function () {

  $color.removeClass('active');
  $(this).addClass('active');

  update_active_color();

});

$opacity.on('change', function () {

  update_active_color();

});

$('#clearCanvas').on('click', function() {
  clearCanvas();
  socket.emit('canvas:clear', room);
});

$('#exportSVG').on('click', function() {
  exportSVG();
});

$('#exportPNG').on('click', function() {
  exportPNG();
});

function clearCanvas() {
  // Remove all but the active layer
  if (project.layers.length > 1) {
    var activeLayerID = project.activeLayer._id;
    for (var i=0; i<project.layers.length; i++) {
      if (project.layers[i]._id != activeLayerID) {
        project.layers[i].remove();
        i--;
      }
    }
  }

  // Remove all of the children from the active layer
  if (paper.project.activeLayer && paper.project.activeLayer.hasChildren()) {
    paper.project.activeLayer.removeChildren();
  }
  view.draw();
}

function exportSVG() {
  //console.log(paper.project.exportSVG());
  var svg = paper.project.exportSVG();
  encodeAsImgAndLink(svg);
}

// Encodes svg as a base64 text and opens a new browser window
// to the svg image that can be saved as a .svg on the users
// local filesystem. This skips making a round trip to the server
// for a POST.
function encodeAsImgAndLink(svg){
  // Add some critical information
  svg.setAttribute('version', '1.1');
  svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');

  var dummy = document.createElement('div');
  dummy.appendChild(svg);

  var b64 = Base64.encode(dummy.innerHTML);

  window.open("data:image/svg+xml;base64,\n"+b64);
}

function exportPNG() {
  var canvas = document.getElementById('myCanvas');
  window.open(canvas.toDataURL('image/png'));
}

// ---------------------------------
// SOCKET.IO EVENTS

socket.on('draw:progress', function (artist, data) {

  // It wasnt this user who created the event
  if (artist !== uid && data) {
    progress_external_path(JSON.parse(data), artist);
  }

});

socket.on('draw:end', function (artist, data) {

  // It wasnt this user who created the event
  if (artist !== uid && data) {
    end_external_path(JSON.parse(data), artist);
  }

});

socket.on('user:connect', function (user_count) {
  update_user_count(user_count);
});

socket.on('user:disconnect', function (user_count) {
  update_user_count(user_count);
});

socket.on('project:load', function (json) {
  paper.project.importJSON(json.project);
  view.draw();
});

socket.on('project:load:error', function() {
  $('#lostConnection').show();
});

socket.on('canvas:clear', function() {
  clearCanvas();
});

// ---------------------------------
// SOCKET.IO EVENT FUNCTIONS

// Updates the active connections
var $user_count = $('#userCount');
var $user_count_wrapper = $('#userCountWrapper');

function update_user_count(count) {

  $user_count_wrapper.css('opacity', 1);
  $user_count.text((count === 1) ? " just you, why not invite some friends?" : " " + count);

}

var external_paths = {};

// Ends a path
var end_external_path = function (points, artist) {

  var path = external_paths[artist];

  if (path) {

    // Close the path
    path.add(new Point(points.end[1], points.end[2]));
    path.closed = true;
    path.smooth();
    view.draw();

    // Remove the old data
    external_paths[artist] = false;

  }

};

// Continues to draw a path in real time
progress_external_path = function (points, artist) {

  var path = external_paths[artist];

  // The path hasnt already been started
  // So start it
  if (!path) {

    // Creates the path in an easy to access way
    external_paths[artist] = new Path();
    path = external_paths[artist];

    // Starts the path
    var start_point = new Point(points.start[1], points.start[2]);
    var color = new RgbColor(points.rgba.red, points.rgba.green, points.rgba.blue, points.rgba.opacity);
    path.fillColor = color;
    path.add(start_point);

  }

  // Draw all the points along the length of the path
  var paths = points.path;
  var length = paths.length;
  for (var i = 0; i < length; i++) {

    path.add(new Point(paths[i].top[1], paths[i].top[2]));
    path.insert(0, new Point(paths[i].bottom[1], paths[i].bottom[2]));

  }

  path.smooth();
  view.draw();

};<|endoftext|>


<commit_before>var inflect = require('i')()
  , RSVP = require('rsvp')
  , _ = require('lodash');

/**
 * Setup routes for a resource, given a name and model.
 *
 * @param {String} name
 * @param {Object} model
 */
function route(name, model) {
  var _this = this
    , router = this.router
    , adapter = this.adapter

    // options
    , baseUrl = this.options.baseUrl
    , namespace = this.options.namespace
    , production = this.options.production

    // routes
    , collection = inflect.pluralize(name)
    , collectionRoute = namespace + '/' + collection
    , individualRoute = namespace + '/' + collection + '/:id';

  // constants
  var MIME = {
    standard: ['application/vnd.api+json', 'application/json'],
    patch: ['application/json-patch+json']
  },
  errorMessages = {
    400: 'Request was malformed.',
    403: 'Access forbidden.',
    404: 'Resource not found.',
    405: 'Method not permitted.',
    412: 'Request header "Content-Type" must be one of: ' + MIME.standard.join(', '),
    500: 'Oops, something went wrong.',
    501: 'Feature not implemented.'
  };

  // response emitters
  var sendError = function(req, res, status, error) {
    if(!!error) console.log(error);
    var object = {
      error: errorMessages[status],
      detail: error
    }, str = production ?
      JSON.stringify(object, null, null) :
      JSON.stringify(object, null, 2) + '\n';

    res.set('Content-Type', MIME.standard[1]);
    res.send(status, str);
  };
  var sendResponse = function(req, res, status, object) {
    if(!!baseUrl) object = appendLinks.call(_this, object);

    var str = production ?
      JSON.stringify(object, null, null) :
      JSON.stringify(object, null, 2) + '\n';

    res.set('Content-Type', !req.get('User-Agent') ? MIME.standard[0] : MIME.standard[1]);
    res.send(status, str);
  };
  var methodNotAllowed = function(req, res) {
    sendError(req, res, 405);
  };

  /*!
   * Do before transformation
   *
   * @param {Object} [model]
   * @param {Object} resource
   * @param {Object} request
   */
  var beforeTransform = function(model, resource, request) {
    if(arguments.length < 3) {
      request = resource;
      resource = model;
      model = name;
    }
    return new RSVP.Promise(function(resolve) {
      if(!_this._before.hasOwnProperty(model)) return resolve(resource);
      resolve(_this._before[model].call(resource, request));
    });
  };

  /*!
   * Do after transformation
   *
   * @param {Object} [model]
   * @param {Object} resource
   * @param {Object} request
   */
  var afterTransform = function(model, resource, request) {
    if(arguments.length < 3) {
      request = resource;
      resource = model;
      model = name;
    }
    return new RSVP.Promise(function(resolve) {
      if(!_this._after.hasOwnProperty(model)) return resolve(resource);
      resolve(_this._after[model].call(resource, request));
    });
  };

  var mimeCheck = function(contentType) {
    return MIME.standard.indexOf(contentType.split(';').shift().toLowerCase()) >= 0;
  };

  /*!
   * Handle creating a resource.
   */
  router.post(collectionRoute, function(req, res) {

    // header error handling
    if(!mimeCheck(req.get('content-type'))) {
      return sendError(req, res, 412);
    }

    var before = [];
    try {
      req.body[collection].forEach(function(resource) {
        before.push(beforeTransform(resource, req));
      });
    } catch(error) {
      return sendError(req, res, 400, error);
    }

    // do before transforms
    RSVP.all(before)

    // create the resources
    .then(function(resources) {
      return RSVP.all(resources.map(function(resource) {
        return adapter.create(model, resource);
      }));
    }, function(error) {
      sendError(req, res, 403, error);
    })

    // do after transforms
    .then(function(resources) {
      return RSVP.all(resources.map(function(resource) {
        return afterTransform(resource, req);
      }));
    }, function(error) {
      sendError(req, res, 500, error);
    })

    // send the response
    .then(function(resources) {
      var body = {};
      body[collection] = resources;
      sendResponse(req, res, 201, body);
    }, function(error) {
      sendError(req, res, 403, error);
    });
  });

  /*
   * Get a list of resources.
   */
  router.get(collectionRoute, function(req, res) {
    var ids = [];
    if(typeof req.query.ids == 'string') ids = req.query.ids.split(',');
    if(typeof req.query.ids == 'array') ids = req.query.ids;

    // get resources by IDs
    adapter.findMany(model, ids)

    // do after transforms
    .then(function(resources) {
      return RSVP.all(resources.map(function(resource) {
        return afterTransform(resource, req);
      }));
    }, function(error) {
      sendError(req, res, 500, error);
    })

    // send the response
    .then(function(resources) {
      var body = {};
      body[collection] = resources;
      sendResponse(req, res, 200, body);
    }, function(error) {
      sendError(req, res, 403, error);
    });

  });

  /*
   * Handle unsupported methods on a collection of resources.
   */
  router.put(collectionRoute, methodNotAllowed);
  router.patch(collectionRoute, methodNotAllowed);
  router.delete(collectionRoute, methodNotAllowed);

  /*
   * Get an individual resource, or many.
   */
  router.get(individualRoute, function(req, res) {
    var ids = req.params.id.split(',');

    // get resources by IDs
    adapter.findMany(model, ids)

    // do after transforms
    .then(function(resources) {
      if(resources.length) {
        return RSVP.all(resources.map(function(resource) {
          return afterTransform(resource, req);
        }));
      } else {
        sendError(req, res, 404);
      }
    }, function(error) {
      sendError(req, res, 500, error);
    })

    // send the response
    .then(function(resources) {
      var body = {};
      body[collection] = resources;
      sendResponse(req, res, 200, body);
    }, function(error) {
      sendError(req, res, 500, error);
    });
  });

  /*
   * Get the related resources of an individual resource.
   */
  router.get(individualRoute + '/:key', function(req, res) {
    var id = req.params.id
      , key = req.params.key;

    // get a resource by ID
    adapter.find(model, id)

    // do after transform
    .then(function(resource) {
      return afterTransform(resource, req);
    }, function(error) {
      sendError(req, res, 404, error);
    })

    // change context to resource
    .then(function(resource) {
      var ids, relatedModel;
      try {
        ids = resource.links[key];
        ids = _.isArray(ids) ? ids : [ids];
        relatedModel = _this._schema[name][key];
        relatedModel = _.isArray(relatedModel) ? relatedModel[0] : relatedModel;
        relatedModel = _.isPlainObject(relatedModel) ? relatedModel.ref : relatedModel;
      } catch(error) {
        return sendError(req, res, 404, error);
      }

      // find related resources
      adapter.findMany(relatedModel, ids)

      // do after transforms
      .then(function(resources) {
        return RSVP.all(resources.map(function(resource) {
          return afterTransform(relatedModel, resource, req);
        }));
      }, function(error) {
        sendError(req, res, 500, error);
      })

      // send the response
      .then(function(resources) {
        var body = {};
        body[inflect.pluralize(relatedModel)] = resources;
        sendResponse(req, res, 200, body);
      }, function(error) {
        sendError(req, res, 403, error);
      });

    }, function(error) {
      sendError(req, res, 403, error);
    });
  });

  /*
   * Put a resource.
   */
  router.put(individualRoute, function(req, res) {

    // header error handling
    if(!mimeCheck(req.get('content-type'))) {
      return sendError(req, res, 412);
    }

    var id = req.params.id
      , update;

    try {
      update = req.body[collection][0];
      if(!update) return sendError(req, res, 400);
    } catch(error) {
      return sendError(req, res, 400, error);
    }

    // try to find the resource by ID
    adapter.find(model, id)

    // resource found, let's update it
    .then(function() {

      // do before transform
      beforeTransform(update, req)

      // update the resource
      .then(function(update) {
        return adapter.update(model, id, update);
      }, function(error) {
        sendError(req, res, 403, error);
      })

      // do after transform
      .then(function(update) {
        return afterTransform(update, req);
      }, function(error) {
        sendError(req, res, 500, error);
      })

      // send the response
      .then(function(update) {
        var body = {};
        body[collection] = [update];
        sendResponse(req, res, 200, body);
      }, function(error) {
        sendError(req, res, 403, error);
      });

    },

    // resource not found, try to create it
    function() {

      // do before transform
      beforeTransform(update, req)

      // create the resource
      .then(function(resource) {
        return adapter.create(model, id, resource);
      }, function(error) {
        sendError(req, res, 403, error);
      })

      // do after transform
      .then(function(resource) {
        return afterTransform(resource, req);
      }, function(error) {
        sendError(req, res, 500, error);
      })

      // send the response
      .then(function(resource) {
        var body = {};
        body[collection] = [resource];
        sendResponse(req, res, 201, body);
      }, function(error) {
        sendError(req, res, 500, error);
      });

    });
  });

  /*
   * Delete a resource.
   */
  router.delete(individualRoute, function(req, res) {
    var id = req.params.id;

    // find the resource by ID
    adapter.find(model, id)

    // do before transform
    .then(function(resource) {
      return beforeTransform(resource);
    }, function(error) {
      sendError(req, res, 404, error);
    })

    // let's delete it
    .then(function() {
      adapter.delete(model, id).then(function() {
        sendResponse(req, res, 204);
      }, function(error) {
        sendError(req, res, 500, error);
      });
    },

    // resource not found
    function(error) {
      sendError(req, res, 404, error);
    });
  });

  /*
   * Patch a resource.
   */
  router.patch(individualRoute, function(req, res) {
    var id = req.params.id;

    // header error handling
    if(!mimeCheck(req.get('content-type'))) {
      return sendError(req, res, 412);
    }

    var update = {};
    try {
      // parse patch request
      req.body.forEach(function(operation) {
        var field = operation.path.split('/').pop();
        var value = operation.value;
        if(operation.op == 'replace') {
          update[field] = value;
        }
      });
    } catch(error) {
      return sendError(req, res, 400, error);
    }

    // do before transform
    beforeTransform(update, req)

    // update the resource
    .then(function(update) {
      return adapter.update(model, id, update);
    }, function(error) {
      sendError(req, res, 403, error);
    })

    // do after transform
    .then(function(resource) {
      return afterTransform(resource, req);
    }, function(error) {
      sendError(req, res, 500, error);
    })

    // send the response
    .then(function(resource) {
      var body = {};
      body[collection] = [resource];
      sendResponse(req, res, 200, body);
    }, function(error) {
      sendError(req, res, 500, error);
    });
  });

  /*
   * POSTing a resource to a predetermined ID is not allowed,
   * since that is what PUT is for.
   */
  router.post(individualRoute, methodNotAllowed);

}

/*
 * Append a top level "links" object for URL-style JSON API.
 *
 * @api private
 * @param {Object} body deserialized response body
 * @return {Object}
 */
function appendLinks(body) {
  var schemas = this._schema
    , baseUrl = this.options.baseUrl
    , namespace = this.options.namespace;

  _.each(body, function(value, key) {
    if(key == 'meta') return;
    var modelName = inflect.singularize(key)
      , schema = schemas[modelName]
      , associations = [];

    _.each(schema, function(value, key) {
      var type = _.isArray(value) ? value[0] : value;
      type = _.isPlainObject(type) ? type.ref : type;
      if(typeof type == 'string') {
        type = inflect.pluralize(type);
        associations.push({key: key, type: type});
      }
    });

    if(!associations.length) return;
    body.links = body.links || {};
    associations.forEach(function(association) {
      var name = [key, association.key].join('.');
      body.links[name] = {
        href: baseUrl + '/' + (!!namespace ? namespace + '/' : '') +
          key + '/{' + name + '}',
        type: association.type
      };
    });
  });
  return body;
}

/*
 * Expose the route method.
 */
module.exports = route;
<commit_msg>add location header to URL-style post response<commit_after>var inflect = require('i')()
  , RSVP = require('rsvp')
  , _ = require('lodash');

// constants
var MIME = {
  standard: ['application/vnd.api+json', 'application/json'],
  patch: ['application/json-patch+json']
},
errorMessages = {
  400: 'Request was malformed.',
  403: 'Access forbidden.',
  404: 'Resource not found.',
  405: 'Method not permitted.',
  412: 'Request header "Content-Type" must be one of: ' + MIME.standard.join(', '),
  500: 'Oops, something went wrong.',
  501: 'Feature not implemented.'
};

/**
 * Setup routes for a resource, given a name and model.
 *
 * @param {String} name
 * @param {Object} model
 */
function route(name, model) {
  var _this = this
    , router = this.router
    , adapter = this.adapter

    // options
    , baseUrl = this.options.baseUrl
    , namespace = this.options.namespace
    , production = this.options.production

    // routes
    , collection = inflect.pluralize(name)
    , collectionRoute = namespace + '/' + collection
    , individualRoute = namespace + '/' + collection + '/:id';

  // response emitters
  var sendError = function(req, res, status, error) {
    if(!!error) console.log(error);
    var object = {
      error: errorMessages[status],
      detail: error
    }, str = production ?
      JSON.stringify(object, null, null) :
      JSON.stringify(object, null, 2) + '\n';

    res.set('Content-Type', MIME.standard[1]);
    res.send(status, str);
  };
  var sendResponse = function(req, res, status, object) {
    if(!!baseUrl) object = appendLinks.call(_this, object);

    var str = production ?
      JSON.stringify(object, null, null) :
      JSON.stringify(object, null, 2) + '\n';

    res.set('Content-Type', !req.get('User-Agent') ? MIME.standard[0] : MIME.standard[1]);
    res.send(status, str);
  };
  var methodNotAllowed = function(req, res) {
    sendError(req, res, 405);
  };

  /*!
   * Do before transformation
   *
   * @param {Object} [model]
   * @param {Object} resource
   * @param {Object} request
   */
  var beforeTransform = function(model, resource, request) {
    if(arguments.length < 3) {
      request = resource;
      resource = model;
      model = name;
    }
    return new RSVP.Promise(function(resolve) {
      if(!_this._before.hasOwnProperty(model)) return resolve(resource);
      resolve(_this._before[model].call(resource, request));
    });
  };

  /*!
   * Do after transformation
   *
   * @param {Object} [model]
   * @param {Object} resource
   * @param {Object} request
   */
  var afterTransform = function(model, resource, request) {
    if(arguments.length < 3) {
      request = resource;
      resource = model;
      model = name;
    }
    return new RSVP.Promise(function(resolve) {
      if(!_this._after.hasOwnProperty(model)) return resolve(resource);
      resolve(_this._after[model].call(resource, request));
    });
  };

  var mimeCheck = function(contentType) {
    return MIME.standard.indexOf(contentType.split(';').shift().toLowerCase()) >= 0;
  };

  /*!
   * Handle creating a resource.
   */
  router.post(collectionRoute, function(req, res) {

    // header error handling
    if(!mimeCheck(req.get('content-type'))) {
      return sendError(req, res, 412);
    }

    var before = [];
    try {
      req.body[collection].forEach(function(resource) {
        before.push(beforeTransform(resource, req));
      });
    } catch(error) {
      return sendError(req, res, 400, error);
    }

    // do before transforms
    RSVP.all(before)

    // create the resources
    .then(function(resources) {
      return RSVP.all(resources.map(function(resource) {
        return adapter.create(model, resource);
      }));
    }, function(error) {
      sendError(req, res, 403, error);
    })

    // do after transforms
    .then(function(resources) {
      return RSVP.all(resources.map(function(resource) {
        return afterTransform(resource, req);
      }));
    }, function(error) {
      sendError(req, res, 500, error);
    })

    // send the response
    .then(function(resources) {
      var body = {};
      body[collection] = resources;
      if(options.baseUrl) {
        var location = baseUrl + '/';
        location +=  !!namespace ? namespace + '/' : '';
        location += collection + '/' + resources[0].id;
        res.set('Location', location);
      }
      sendResponse(req, res, 201, body);
    }, function(error) {
      sendError(req, res, 403, error);
    });
  });

  /*
   * Get a list of resources.
   */
  router.get(collectionRoute, function(req, res) {
    var ids = [];
    if(typeof req.query.ids == 'string') ids = req.query.ids.split(',');
    if(typeof req.query.ids == 'array') ids = req.query.ids;

    // get resources by IDs
    adapter.findMany(model, ids)

    // do after transforms
    .then(function(resources) {
      return RSVP.all(resources.map(function(resource) {
        return afterTransform(resource, req);
      }));
    }, function(error) {
      sendError(req, res, 500, error);
    })

    // send the response
    .then(function(resources) {
      var body = {};
      body[collection] = resources;
      sendResponse(req, res, 200, body);
    }, function(error) {
      sendError(req, res, 403, error);
    });

  });

  /*
   * Handle unsupported methods on a collection of resources.
   */
  router.put(collectionRoute, methodNotAllowed);
  router.patch(collectionRoute, methodNotAllowed);
  router.delete(collectionRoute, methodNotAllowed);

  /*
   * Get an individual resource, or many.
   */
  router.get(individualRoute, function(req, res) {
    var ids = req.params.id.split(',');

    // get resources by IDs
    adapter.findMany(model, ids)

    // do after transforms
    .then(function(resources) {
      if(resources.length) {
        return RSVP.all(resources.map(function(resource) {
          return afterTransform(resource, req);
        }));
      } else {
        sendError(req, res, 404);
      }
    }, function(error) {
      sendError(req, res, 500, error);
    })

    // send the response
    .then(function(resources) {
      var body = {};
      body[collection] = resources;
      sendResponse(req, res, 200, body);
    }, function(error) {
      sendError(req, res, 500, error);
    });
  });

  /*
   * Get the related resources of an individual resource.
   */
  router.get(individualRoute + '/:key', function(req, res) {
    var id = req.params.id
      , key = req.params.key;

    // get a resource by ID
    adapter.find(model, id)

    // do after transform
    .then(function(resource) {
      return afterTransform(resource, req);
    }, function(error) {
      sendError(req, res, 404, error);
    })

    // change context to resource
    .then(function(resource) {
      var ids, relatedModel;
      try {
        ids = resource.links[key];
        ids = _.isArray(ids) ? ids : [ids];
        relatedModel = _this._schema[name][key];
        relatedModel = _.isArray(relatedModel) ? relatedModel[0] : relatedModel;
        relatedModel = _.isPlainObject(relatedModel) ? relatedModel.ref : relatedModel;
      } catch(error) {
        return sendError(req, res, 404, error);
      }

      // find related resources
      adapter.findMany(relatedModel, ids)

      // do after transforms
      .then(function(resources) {
        return RSVP.all(resources.map(function(resource) {
          return afterTransform(relatedModel, resource, req);
        }));
      }, function(error) {
        sendError(req, res, 500, error);
      })

      // send the response
      .then(function(resources) {
        var body = {};
        body[inflect.pluralize(relatedModel)] = resources;
        sendResponse(req, res, 200, body);
      }, function(error) {
        sendError(req, res, 403, error);
      });

    }, function(error) {
      sendError(req, res, 403, error);
    });
  });

  /*
   * Put a resource.
   */
  router.put(individualRoute, function(req, res) {

    // header error handling
    if(!mimeCheck(req.get('content-type'))) {
      return sendError(req, res, 412);
    }

    var id = req.params.id
      , update;

    try {
      update = req.body[collection][0];
      if(!update) return sendError(req, res, 400);
    } catch(error) {
      return sendError(req, res, 400, error);
    }

    // try to find the resource by ID
    adapter.find(model, id)

    // resource found, let's update it
    .then(function() {

      // do before transform
      beforeTransform(update, req)

      // update the resource
      .then(function(update) {
        return adapter.update(model, id, update);
      }, function(error) {
        sendError(req, res, 403, error);
      })

      // do after transform
      .then(function(update) {
        return afterTransform(update, req);
      }, function(error) {
        sendError(req, res, 500, error);
      })

      // send the response
      .then(function(update) {
        var body = {};
        body[collection] = [update];
        sendResponse(req, res, 200, body);
      }, function(error) {
        sendError(req, res, 403, error);
      });

    },

    // resource not found, try to create it
    function() {

      // do before transform
      beforeTransform(update, req)

      // create the resource
      .then(function(resource) {
        return adapter.create(model, id, resource);
      }, function(error) {
        sendError(req, res, 403, error);
      })

      // do after transform
      .then(function(resource) {
        return afterTransform(resource, req);
      }, function(error) {
        sendError(req, res, 500, error);
      })

      // send the response
      .then(function(resource) {
        var body = {};
        body[collection] = [resource];
        sendResponse(req, res, 201, body);
      }, function(error) {
        sendError(req, res, 500, error);
      });

    });
  });

  /*
   * Delete a resource.
   */
  router.delete(individualRoute, function(req, res) {
    var id = req.params.id;

    // find the resource by ID
    adapter.find(model, id)

    // do before transform
    .then(function(resource) {
      return beforeTransform(resource);
    }, function(error) {
      sendError(req, res, 404, error);
    })

    // let's delete it
    .then(function() {
      adapter.delete(model, id).then(function() {
        sendResponse(req, res, 204);
      }, function(error) {
        sendError(req, res, 500, error);
      });
    },

    // resource not found
    function(error) {
      sendError(req, res, 404, error);
    });
  });

  /*
   * Patch a resource.
   */
  router.patch(individualRoute, function(req, res) {
    var id = req.params.id;

    // header error handling
    if(!mimeCheck(req.get('content-type'))) {
      return sendError(req, res, 412);
    }

    var update = {};
    try {
      // parse patch request
      req.body.forEach(function(operation) {
        var field = operation.path.split('/').pop();
        var value = operation.value;
        if(operation.op == 'replace') {
          update[field] = value;
        }
      });
    } catch(error) {
      return sendError(req, res, 400, error);
    }

    // do before transform
    beforeTransform(update, req)

    // update the resource
    .then(function(update) {
      return adapter.update(model, id, update);
    }, function(error) {
      sendError(req, res, 403, error);
    })

    // do after transform
    .then(function(resource) {
      return afterTransform(resource, req);
    }, function(error) {
      sendError(req, res, 500, error);
    })

    // send the response
    .then(function(resource) {
      var body = {};
      body[collection] = [resource];
      sendResponse(req, res, 200, body);
    }, function(error) {
      sendError(req, res, 500, error);
    });
  });

  /*
   * POSTing a resource to a predetermined ID is not allowed,
   * since that is what PUT is for.
   */
  router.post(individualRoute, methodNotAllowed);

}

/*
 * Append a top level "links" object for URL-style JSON API.
 *
 * @api private
 * @param {Object} body deserialized response body
 * @return {Object}
 */
function appendLinks(body) {
  var schemas = this._schema
    , baseUrl = this.options.baseUrl
    , namespace = this.options.namespace;

  _.each(body, function(value, key) {
    if(key == 'meta') return;
    var modelName = inflect.singularize(key)
      , schema = schemas[modelName]
      , associations = [];

    _.each(schema, function(value, key) {
      var type = _.isArray(value) ? value[0] : value;
      type = _.isPlainObject(type) ? type.ref : type;
      if(typeof type == 'string') {
        type = inflect.pluralize(type);
        associations.push({key: key, type: type});
      }
    });

    if(!associations.length) return;
    body.links = body.links || {};
    associations.forEach(function(association) {
      var name = [key, association.key].join('.');
      body.links[name] = {
        href: baseUrl + '/' + (!!namespace ? namespace + '/' : '') +
          key + '/{' + name + '}',
        type: association.type
      };
    });
  });
  return body;
}

/*
 * Expose the route method.
 */
module.exports = route;
<|endoftext|>


<commit_before>'use strict';

var Ruler = function (rules, options) {
  this.rules = rules;
  this.options = options || {};
};

Ruler.prototype = {
  setup: function (validator) {
    this.validator = validator;
  },

  getProperty: function ($input) {
    if (!$input) {
      return null;
    }
    return $input.attr(this.options.property || 'name');
  },

  // ## _checkRules
  // this is the method which really apply the validation
  _checkRules: function (rule, value) {
    if (!this.validator) {
      throw new Error('validator is not defined.');
    }
    return this.validator[rule.rule].apply(null, rule.options ? [value, rule.options] : [value]);
  },

  _validateField: function (name, value) {
    var _rules = this.rules[name];

    // return early if no rule found
    if (!_rules) {
      if (this.options.ignoreMissing) {
        return true;
      }
      else {
        throw new Error('Rule not found for attribute #' + name);
      }
    }

    if (!(Array.isArray ? Array.isArray(_rules) : _rules instanceof Array)) {
      console.error('rule should be defined as an array.');
    }

    for (var i = 0, len = _rules.length; i < len; i += 1) {
      if (!this._checkRules(_rules[i], value)) {
        this._addError(name, _rules[i]);
        break; // break on first error
      }
    }

    return this.errors;
  },

  // ## _addError
  // internal method to add rule error to the error structure
  // can be adapted to your own use case or to add logic.
  // received the invalid `rule`, meaning that just throw an error
  _addError: function (prop, rule) {
    this.errors[prop] = rule.message;
  },

  // ## foo
  // this method need to find its name.
  foo: function (attrs, options) {
    this.errors = {}; // reset errors field
    var prop;

    // initialize errors with empty values
    // useful to track field validation that will be run, an empty value
    // meaning no error, thus a valid entry.
    if (options && options.empty) {
      for (prop in attrs) {
        this._addError(prop, '');
      }
    }

    // parse each attributes to validate against given rules
    for (prop in attrs) {
      this._validateField(prop, attrs[prop]);
    }

    // return errors if any added or null
    return Object.keys(this.errors).length ? this.errors : null;
  },

  // ## $validate
  // validate a Jquery field input.
  $validate: function ($input, options) {
    var attrs = {};
    attrs[this.getProperty($input)] = $input.val();

    return this.foo(attrs, options);
  },

  // ## validate
  // validate an object against given rule
  validate: function (attrs, options) {
    return this.foo(attrs, options);
  }
};
<commit_msg>fix(ruler): check rule under exported rules property<commit_after>'use strict';

var Ruler = function (rules, options) {
  this.rules = rules;
  this.options = options || {};
};

Ruler.prototype = {
  setup: function (validator) {
    this.validator = validator;
  },

  getProperty: function ($input) {
    if (!$input) {
      return null;
    }
    return $input.attr(this.options.property || 'name');
  },

  // ## _checkRules
  // this is the method which really apply the validation
  _checkRules: function (rule, value) {
    if (!this.validator) {
      throw new Error('validator is not defined.');
    }
    return this.validator.rules[rule.rule].apply(null, rule.options ? [value, rule.options] : [value]);
  },

  _validateField: function (name, value) {
    var _rules = this.rules[name];

    // return early if no rule found
    if (!_rules) {
      if (this.options.ignoreMissing) {
        return true;
      }
      else {
        throw new Error('Rule not found for attribute #' + name);
      }
    }

    if (!(Array.isArray ? Array.isArray(_rules) : _rules instanceof Array)) {
      console.error('rule should be defined as an array.');
    }

    for (var i = 0, len = _rules.length; i < len; i += 1) {
      if (!this._checkRules(_rules[i], value)) {
        this._addError(name, _rules[i]);
        break; // break on first error
      }
    }

    return this.errors;
  },

  // ## _addError
  // internal method to add rule error to the error structure
  // can be adapted to your own use case or to add logic.
  // received the invalid `rule`, meaning that just throw an error
  _addError: function (prop, rule) {
    this.errors[prop] = rule.message;
  },

  // ## foo
  // this method need to find its name.
  foo: function (attrs, options) {
    this.errors = {}; // reset errors field
    var prop;

    // initialize errors with empty values
    // useful to track field validation that will be run, an empty value
    // meaning no error, thus a valid entry.
    if (options && options.empty) {
      for (prop in attrs) {
        this._addError(prop, '');
      }
    }

    // parse each attributes to validate against given rules
    for (prop in attrs) {
      this._validateField(prop, attrs[prop]);
    }

    // return errors if any added or null
    return Object.keys(this.errors).length ? this.errors : null;
  },

  // ## $validate
  // validate a Jquery field input.
  $validate: function ($input, options) {
    var attrs = {};
    attrs[this.getProperty($input)] = $input.val();

    return this.foo(attrs, options);
  },

  // ## validate
  // validate an object against given rule
  validate: function (attrs, options) {
    return this.foo(attrs, options);
  }
};
<|endoftext|>


<commit_before>/**
 * A standalone rules array to hold the basic spof rules
 * @property rules
 * @type Object[]
 */
var rules = [];

/**
 * A global function to check if a given URL belongs to the a third party domain.
 * A block pattern is maintained which lists all the possible third party scripts
 * that can be included in a page
 * @param {String} url The url to be verified
 * @return {Boolean} The flag indicating if the given url belongs to a third party domain
 * @method is3P
 */
function is3P(url) {
	// Known 3rd-party assets
	// Ref: https://github.com/pmeenan/spof-o-matic/blob/master/src/background.js
	var BLOCK_PATTERN = [
         'platform\.twitter\.com',
         'connect\.facebook\.net',
         'platform\.linkedin\.com',
         'assets\.pinterest\.com',
         'widgets\.digg\.com',
         '.*\.addthis\.com',
         'ajax\.googleapis\.com',
         'code\.jquery\.com',
         'cdn\.jquerytools\.org',
         'apis\.google\.com',
         '.*\.google-analytics\.com',
         '.*\.chartbeat\.com',
         'static\.chartbeat\.com',
         '.*\.2o7\.net',
         '.*\.revsci\.net',
         '.*\.omtrdc\.net',
         'b\.scorecardresearch\.com',
         'cdn\.sailthru\.com',
         '.*browserid\.org',
         'ad\.doubleclick\.net',
         'js\.adsonar\.com',
         'ycharts\.com',
         '.*\.googlecode\.com',
         '.*\.gstatic\.com',
         '.*\.quantserve\.com',
         '.*\.brightcove\.com',
         '.*\.disqus\.com',
         '.*\.lognornal\.com'
    ],
	i,
	l,
	regEx;

	for(i = 0, l = BLOCK_PATTERN.length; i < l; i++) {
		regEx = new RegExp(BLOCK_PATTERN[i], 'im');
		if(regEx.test(url)) {
			return true;
		}
	}
	return false;
}

/**
 * Rule: Load 3rd party JS Asynchronously
 */
rules.push({
	// Rule metadata
	id: "3rdparty-scripts",
	name: "Load 3rd Party JS Asynchronously",
	desc: "Always load 3rd party external scripts asyncronously to prevent frontend spof",

	// The check function to verify the rule
	check: function($, cssContent, reporter) {
		var rule = this,
			html = $('html').html(),
			pageLength = html.length,
			scripts = $('script'),
			/**
			 * Determines the SPOF score for the script url in the page
			 * @param {String} src The script source for which the score should be determined
			 * @return {int} The score
			 * @method getScore
			 */
			getScore = function(src) {
				var value = 100 - (((html.indexOf(src) + src.length) / pageLength ) * 100);
				return Math.round(value);
			},
			i,
			l,
			script,
			src;

		for(i = 0, l = scripts.length; i < l; i++) {
			script = scripts[i];
			src = script.src;
			if(src && is3P(src)) {
				if(!script.defer || !script.async) {
					reporter.error("ERROR: Possible SPOF attack due to 3rd party script - " + src, src, getScore(src), rule);
				}
			}
		}
	}
});

/**
 * Rule: Load Application JS Non-blocking
 */
rules.push({
	// Rule metadata
	id: "application-js",
	name: "Load Application JS Non-blocking",
	desc: "Load application JS in a non-blocking fashion or towards the end of page",

	// The check function to verify the rule
	check: function($, cssContent, reporter) {
		var rule = this,
			html = $('html').html(),
			pageLength = html.length,
			scripts = $('script'),
			/**
			 * Determines the SPOF score for the script url in the page
			 * @param {String} src The script source for which the score should be determined
			 * @return {int} The score
			 * @method getScore
			 */
			getScore = function(src) {
				var value = 100 - (((html.indexOf(src) + src.length) / pageLength ) * 100);
				return Math.round(value);
			},
			i,
			l,
			script,
			src,
			score;

		for(i = 0, l = scripts.length; i < l; i++) {
			script = scripts[i];
			src = script.src;
			if(src && !is3P(src)) {
				score = getScore(src);
				if(score > 50 && (!script.defer || !script.async)) {
					reporter.warn("WARNING: Possible SPOF attack due to script " + src, src, score, rule);
				}
			}
		}
	}
});

/**
 * Rule: Stylesheet With @font-face
 */
rules.push({
	// Rule metadata
	id: "fontface-stylesheet",
	name: "Stylesheet With @font-face",
	desc: "Try to inline font files. Also make them compressed and cacheable",

	// The check function to verify the rule
	check: function($, cssContent, reporter) {
		var rule = this,
			pattern = /@font-face[\s\n]*{([^{}]*)}/gim, // RegEx pattern for retrieving all the font-face styles
			urlPattern = /url[\s]*\([\s]*['"]?([^'"]*)['"]?[\s]*\)/gim, // RegEx pattern for retrieving the urls  font-face styles
			fontFaceMatches;

		if(!cssContent || cssContent === "") {
			// Return immediately if CSS content is empty
			return;
		}

		fontFaceMatches = cssContent.match(pattern);
		if(!fontFaceMatches) {
			// Return if no font-face style
			return;
		}

		fontFaceMatches.forEach(function(match) {
			// replace local entries
			match = match.replace(/local\([^\)]*\)[\s]*[,]?/gim, '');
			var fontUrl;
			while(fontUrl = urlPattern.exec(match)) {
				fontUrl[1] && reporter.warn("WARNING: Possible SPOF attack due to @font-face style in external stylesheet", fontUrl[1], "NA", rule);
			}
		});
	}
});

/**
 * Rule: Inline @font-face
 */
rules.push({
	// Rule metadata
	id: "fontface-inline",
	name: "Inline @font-face",
	desc: "Make sure the external fonts are compressed, cached and small in size",

	// The check function to verify the rule
	check: function($, cssContent, reporter) {
		var rule = this,
			html = $('html').html(),
			pattern = /@font-face[\s\n]*{([^{}]*)}/gim, // RegEx pattern for retrieving all the font-face styles
			urlPattern = /url[\s]*\([\s]*['"]?([^'"]*)['"]?[\s]*\)/gim, // RegEx pattern for retrieving the urls  font-face styles
			fontFaceMatches;

		fontFaceMatches = html.match(pattern);
		if(!fontFaceMatches) {
			// Return if no font-face style
			return;
		}

		fontFaceMatches.forEach(function(match) {
			// replace local entries
			match = match.replace(/local\([^\)]*\)[\s]*[,]?/gim, '');
			var fontUrl;
			while(fontUrl = urlPattern.exec(match)) {
				fontUrl[1] && reporter.warn("WARNING: Possible SPOF attack due to inline @font-face style", fontUrl[1], "NA", rule);
			}
		});
	}
});

/**
 * Rule: Inline @font-face precede Script tag IE issue
 */
rules.push({
	// Rule metadata
	id: "fontface-inline-precede-script-IE",
	name: "Inline @font-face precede Script tag IE issue",
	desc: "Make sure inlined @font-face is not preceded by a SCRIPT tag, causes SPOF in IE",

	// The check function to verify the rule
	check: function($, cssContent, reporter) {
		var rule = this,
			html = $('html').html(),
			scriptStylePattern=/<script[^>]*>[\s\S]*?<\/script>\s*<style[^>]*>[\s\S]*?<\/style>/gim,
			scriptStyleMatches,
			multipleMsg;

		scriptStyleMatches = html.match(scriptStylePattern);
		if(!scriptStyleMatches) {
			// Return if no font-face style
			return;
		}

		multipleMsg = scriptStyleMatches.length > 1? 'multiple occurances of ': '';
		reporter.warn("WARNING: Possible SPOF attack in IE due to " + multipleMsg + "inline @font-face preceded by a SCRIPT tag", "NA", "NA", rule);
	}
});

// Exporting the rules for node environment
if(typeof exports != 'undefined') {
	exports.rules = rules;
}
<commit_msg>Correcting rule description<commit_after>/**
 * A standalone rules array to hold the basic spof rules
 * @property rules
 * @type Object[]
 */
var rules = [];

/**
 * A global function to check if a given URL belongs to the a third party domain.
 * A block pattern is maintained which lists all the possible third party scripts
 * that can be included in a page
 * @param {String} url The url to be verified
 * @return {Boolean} The flag indicating if the given url belongs to a third party domain
 * @method is3P
 */
function is3P(url) {
	// Known 3rd-party assets
	// Ref: https://github.com/pmeenan/spof-o-matic/blob/master/src/background.js
	var BLOCK_PATTERN = [
         'platform\.twitter\.com',
         'connect\.facebook\.net',
         'platform\.linkedin\.com',
         'assets\.pinterest\.com',
         'widgets\.digg\.com',
         '.*\.addthis\.com',
         'ajax\.googleapis\.com',
         'code\.jquery\.com',
         'cdn\.jquerytools\.org',
         'apis\.google\.com',
         '.*\.google-analytics\.com',
         '.*\.chartbeat\.com',
         'static\.chartbeat\.com',
         '.*\.2o7\.net',
         '.*\.revsci\.net',
         '.*\.omtrdc\.net',
         'b\.scorecardresearch\.com',
         'cdn\.sailthru\.com',
         '.*browserid\.org',
         'ad\.doubleclick\.net',
         'js\.adsonar\.com',
         'ycharts\.com',
         '.*\.googlecode\.com',
         '.*\.gstatic\.com',
         '.*\.quantserve\.com',
         '.*\.brightcove\.com',
         '.*\.disqus\.com',
         '.*\.lognornal\.com'
    ],
	i,
	l,
	regEx;

	for(i = 0, l = BLOCK_PATTERN.length; i < l; i++) {
		regEx = new RegExp(BLOCK_PATTERN[i], 'im');
		if(regEx.test(url)) {
			return true;
		}
	}
	return false;
}

/**
 * Rule: Load 3rd party JS Asynchronously
 */
rules.push({
	// Rule metadata
	id: "3rdparty-scripts",
	name: "Load 3rd Party JS Asynchronously",
	desc: "Always load 3rd party external scripts asyncronously to prevent frontend spof",

	// The check function to verify the rule
	check: function($, cssContent, reporter) {
		var rule = this,
			html = $('html').html(),
			pageLength = html.length,
			scripts = $('script'),
			/**
			 * Determines the SPOF score for the script url in the page
			 * @param {String} src The script source for which the score should be determined
			 * @return {int} The score
			 * @method getScore
			 */
			getScore = function(src) {
				var value = 100 - (((html.indexOf(src) + src.length) / pageLength ) * 100);
				return Math.round(value);
			},
			i,
			l,
			script,
			src;

		for(i = 0, l = scripts.length; i < l; i++) {
			script = scripts[i];
			src = script.src;
			if(src && is3P(src)) {
				if(!script.defer || !script.async) {
					reporter.error("ERROR: Possible SPOF attack due to 3rd party script - " + src, src, getScore(src), rule);
				}
			}
		}
	}
});

/**
 * Rule: Load Application JS Non-blocking
 */
rules.push({
	// Rule metadata
	id: "application-js",
	name: "Load Application JS Non-blocking",
	desc: "Load application JS in a non-blocking fashion or towards the end of page",

	// The check function to verify the rule
	check: function($, cssContent, reporter) {
		var rule = this,
			html = $('html').html(),
			pageLength = html.length,
			scripts = $('script'),
			/**
			 * Determines the SPOF score for the script url in the page
			 * @param {String} src The script source for which the score should be determined
			 * @return {int} The score
			 * @method getScore
			 */
			getScore = function(src) {
				var value = 100 - (((html.indexOf(src) + src.length) / pageLength ) * 100);
				return Math.round(value);
			},
			i,
			l,
			script,
			src,
			score;

		for(i = 0, l = scripts.length; i < l; i++) {
			script = scripts[i];
			src = script.src;
			if(src && !is3P(src)) {
				score = getScore(src);
				if(score > 50 && (!script.defer || !script.async)) {
					reporter.warn("WARNING: Possible SPOF attack due to script " + src, src, score, rule);
				}
			}
		}
	}
});

/**
 * Rule: Stylesheet With @font-face
 */
rules.push({
	// Rule metadata
	id: "fontface-stylesheet",
	name: "Stylesheet With @font-face",
	desc: "Try to inline @font-face style. Also make the font files compressed and cacheable",

	// The check function to verify the rule
	check: function($, cssContent, reporter) {
		var rule = this,
			pattern = /@font-face[\s\n]*{([^{}]*)}/gim, // RegEx pattern for retrieving all the font-face styles
			urlPattern = /url[\s]*\([\s]*['"]?([^'"]*)['"]?[\s]*\)/gim, // RegEx pattern for retrieving the urls  font-face styles
			fontFaceMatches;

		if(!cssContent || cssContent === "") {
			// Return immediately if CSS content is empty
			return;
		}

		fontFaceMatches = cssContent.match(pattern);
		if(!fontFaceMatches) {
			// Return if no font-face style
			return;
		}

		fontFaceMatches.forEach(function(match) {
			// replace local entries
			match = match.replace(/local\([^\)]*\)[\s]*[,]?/gim, '');
			var fontUrl;
			while(fontUrl = urlPattern.exec(match)) {
				fontUrl[1] && reporter.warn("WARNING: Possible SPOF attack due to @font-face style in external stylesheet", fontUrl[1], "NA", rule);
			}
		});
	}
});

/**
 * Rule: Inline @font-face
 */
rules.push({
	// Rule metadata
	id: "fontface-inline",
	name: "Inline @font-face",
	desc: "Make sure the fonts files are compressed, cached and small in size",

	// The check function to verify the rule
	check: function($, cssContent, reporter) {
		var rule = this,
			html = $('html').html(),
			pattern = /@font-face[\s\n]*{([^{}]*)}/gim, // RegEx pattern for retrieving all the font-face styles
			urlPattern = /url[\s]*\([\s]*['"]?([^'"]*)['"]?[\s]*\)/gim, // RegEx pattern for retrieving the urls  font-face styles
			fontFaceMatches;

		fontFaceMatches = html.match(pattern);
		if(!fontFaceMatches) {
			// Return if no font-face style
			return;
		}

		fontFaceMatches.forEach(function(match) {
			// replace local entries
			match = match.replace(/local\([^\)]*\)[\s]*[,]?/gim, '');
			var fontUrl;
			while(fontUrl = urlPattern.exec(match)) {
				fontUrl[1] && reporter.warn("WARNING: Possible SPOF attack due to inline @font-face style", fontUrl[1], "NA", rule);
			}
		});
	}
});

/**
 * Rule: Inline @font-face precede Script tag IE issue
 */
rules.push({
	// Rule metadata
	id: "fontface-inline-precede-script-IE",
	name: "Inline @font-face precede Script tag IE issue",
	desc: "Make sure inlined @font-face is not preceded by a SCRIPT tag, causes SPOF in IE",

	// The check function to verify the rule
	check: function($, cssContent, reporter) {
		var rule = this,
			html = $('html').html(),
			scriptStylePattern=/<script[^>]*>[\s\S]*?<\/script>\s*<style[^>]*>[\s\S]*?<\/style>/gim,
			scriptStyleMatches,
			multipleMsg;

		scriptStyleMatches = html.match(scriptStylePattern);
		if(!scriptStyleMatches) {
			// Return if no font-face style
			return;
		}

		multipleMsg = scriptStyleMatches.length > 1? 'multiple occurances of ': '';
		reporter.warn("WARNING: Possible SPOF attack in IE due to " + multipleMsg + "inline @font-face preceded by a SCRIPT tag", "NA", "NA", rule);
	}
});

// Exporting the rules for node environment
if(typeof exports != 'undefined') {
	exports.rules = rules;
}
<|endoftext|>


<commit_before>'use strict';

var winSpawn = require('win-spawn');

module.exports = function (options) {

  var args = [];

  if(options.port) {
    args.push('--port=' + options.port);
  }

  if(options.node && options.grunt) {
    args.unshift(options.grunt);
  }

  runInDir(options.node || 'grunt', '.', args, function() {
  });
};

function runInDir(command, cwd, args, callback) {
  var command = winSpawn(command, args, {
    stdio: 'inherit',
    cwd: cwd
  });

  command.on('error', function() {
    console.log('There was an error trying to run this command'.red);
    console.log('Please make sure you have grunt installed'.red);
  });

  command.on('close', function() {
    callback();
  });
};
<commit_msg>add args to wh<commit_after>'use strict';

var winSpawn = require('win-spawn');

module.exports = function (options) {

  var args = [];

  if(options.port) {
    args.push('--port=' + options.port);
  }

  if(options.node && options.grunt) {
    args.unshift(options.grunt);
  }

  if(options.node) {
    args.push('--nodebin=' + options.node);
  }

  if(options.grunt) {
    args.push('--gruntbin=' + options.grunt);
  }

  if(options.npm) {
    args.push('--npmbin=' + options.npm);
  }

  if(options.token) {
    args.push('--token=' + options.token);
  }

  if(options.email) {
    args.push('--email=' + options.email);
  }

  runInDir(options.node || 'grunt', '.', args, function() {
  });
};

function runInDir(command, cwd, args, callback) {
  var command = winSpawn(command, args, {
    stdio: 'inherit',
    cwd: cwd
  });

  command.on('error', function() {
    console.log('There was an error trying to run this command'.red);
    console.log('Please make sure you have grunt installed'.red);
  });

  command.on('close', function() {
    callback();
  });
};
<|endoftext|>


<commit_before>'use strict'

const Boom = require('boom')
const Hapi = require('hapi')
const pify = require('pify')

const apis = require('./apis.js')
const handlers = require('./handlers.js')
const values = require('./values.js')
const wrapper = require('./wrapper.js')

// return only the pertinent data from a Hapi Request
function normaliseHapiRequest (request, params) {
  const urlInfo = Object.assign({}, request.url, {
    host: request.info.host,
    hostname: request.info.hostname,
    params,
    protocol: wrapper.protocolFromHeaders(request.headers)
  })
  delete urlInfo.auth
  delete urlInfo.hash
  delete urlInfo.href
  delete urlInfo.path
  delete urlInfo.port
  delete urlInfo.search
  delete urlInfo.slashes
  return {
    body: request.payload,
    headers: request.headers,
    method: request.method,
    url: urlInfo
  }
}

function startServer (options) {
  options = options || {}
  const server = new Hapi.Server()
  server.connection({ port: options.port })

  let cors = true // development only, not recommended in production
  if (options.cors && options.cors.origins) {
    cors = {
      origin: options.cors.origins,
      headers: options.cors.headers || values.DEFAULT_HEADERS
    }
  }
  server.route({
    config: {
      cors
    },
    handler (request, reply) {
      // TODO: consider sanitising this input
      const cwd = options.cwd
      const route = request.params.route
      if (!route) {
        reply(Boom.methodNotAllowed())
        return
      }

      apis.wipeRouteFromRequireCache(cwd, route)
        .then(() => apis.getHandlerConfig(cwd, route, request.method))
        .then((handlerConfig) => {
          if (!handlerConfig || !handlerConfig.handler) {
            reply(Boom.notFound())
            return
          }
          if (typeof handlerConfig.handler !== 'function') {
            reply(Boom.notImplemented())
            return
          }

          return handlers.executeHandler(handlerConfig.handler, normaliseHapiRequest(request, handlerConfig.params))
            .then((result) => {
              if (result) {
                const jsonResult = JSON.stringify(result, null, 2)
                const response = reply(null, jsonResult)
                response.type('application/json')
              } else {
                reply(null, 200)
              }
            })
        })
        .catch((err) => reply(err))
    },
    // HTTP HEAD is automatically provided based on GET
    method: values.METHODS,
    path: '/{route*}' // catch-all
  })

  return server.register({
    register: require('good'),
    options: {
      ops: false,
      reporters: {
        console: [{
          module: 'good-console'
        }, 'stdout']
      }
    }
  })
    .then(() => pify(server.start.bind(server))())
    .then(() => server)
}

module.exports = {
  normaliseHapiRequest,
  startServer
}
<commit_msg>SC-18 # Updated server cmd with new CORS options<commit_after>'use strict'

const Boom = require('boom')
const Hapi = require('hapi')
const pify = require('pify')

const apis = require('./apis.js')
const handlers = require('./handlers.js')
const values = require('./values.js')
const wrapper = require('./wrapper.js')

function normaliseHapiCors (cors) {
  return {
    credentials: cors.credentials,
    exposedHeaders: cors.exposedHeaders,
    headers: cors.headers,
    maxAge: cors.maxAge,
    origin: cors.origins
  }
}

// return only the pertinent data from a Hapi Request
function normaliseHapiRequest (request, params) {
  const urlInfo = Object.assign({}, request.url, {
    host: request.info.host,
    hostname: request.info.hostname,
    params,
    protocol: wrapper.protocolFromHeaders(request.headers)
  })
  delete urlInfo.auth
  delete urlInfo.hash
  delete urlInfo.href
  delete urlInfo.path
  delete urlInfo.port
  delete urlInfo.search
  delete urlInfo.slashes
  return {
    body: request.payload,
    headers: request.headers,
    method: request.method,
    url: urlInfo
  }
}

function startServer (options) {
  options = options || {}
  const server = new Hapi.Server()
  server.connection({ port: options.port })

  server.route({
    config: {
      cors: options.cors ? normaliseHapiCors(options.cors) : false
    },
    handler (request, reply) {
      // TODO: consider sanitising this input
      const cwd = options.cwd
      const route = request.params.route
      if (!route) {
        reply(Boom.methodNotAllowed())
        return
      }

      apis.wipeRouteFromRequireCache(cwd, route)
        .then(() => apis.getHandlerConfig(cwd, route, request.method))
        .then((handlerConfig) => {
          if (!handlerConfig || !handlerConfig.handler) {
            reply(Boom.notFound())
            return
          }
          if (typeof handlerConfig.handler !== 'function') {
            reply(Boom.notImplemented())
            return
          }

          return handlers.executeHandler(handlerConfig.handler, normaliseHapiRequest(request, handlerConfig.params))
            .then((result) => {
              if (result) {
                const jsonResult = JSON.stringify(result, null, 2)
                const response = reply(null, jsonResult)
                response.type('application/json')
              } else {
                reply(null, 200)
              }
            })
        })
        .catch((err) => reply(err))
    },
    // HTTP HEAD is automatically provided based on GET
    method: values.METHODS.filter((method) => method !== 'OPTIONS'),
    path: '/{route*}' // catch-all
  })

  return server.register({
    register: require('good'),
    options: {
      ops: false,
      reporters: {
        console: [{
          module: 'good-console'
        }, 'stdout']
      }
    }
  })
    .then(() => pify(server.start.bind(server))())
    .then(() => server)
}

module.exports = {
  normaliseHapiCors,
  normaliseHapiRequest,
  startServer
}
<|endoftext|>


<commit_before>/**
 * Класс вычислений оставшегося времени для лотов
 * Время синхронезируется с сервером
 * @constructor
 */
function LotTimer () {

    this._serverTime;

}

LotTimer.prototype = {

    /**
     * Серверное время
     * @type {timestamp}
     */
    _serverTime: null,

    /**
     * Остаток времени
     * Обновляеться с сервера переодически - корректируеться
     * А так же обновляется при выислении по таймеру
     * @type {timestamp}
     */
     _remainderTime: null,

    /**
     * Синхронезауия времени с сервером
     */
    syncServerTime: function () {},

    /**
     * Получение данных лотов
     */
    getLotsData: function () {},

    /**
     * Получение оставшегося времени
     * @param timeFrom {timestamp} - от какого времени вести отчет
     * @return {timestamp} - время сколько осталось
     */
    getRemainderTime: function (timeFrom) {
        var start, diffTime, diffSec;

        // время последнего запроса лотов
        start = timeFrom;

        // сколько прошло времени с последнего запроса лотов
        diffTime = (this.getPresentTime() - start);

        // количество секунд с прошедших за это время
        diffSec = Math.floor(diffTime / 1000);

        // расчет прошедших секунд со времени старта
        // вычитаем из времени старта, для получения времени оставшегося
        return this.addSecondsTo(timeFrom, 0 - (diffSec + 1));
    },

    /**
     * Получить текущее время
     */
    getPresentTime: function () {
        return new Date().getTime();
    },

    /**
     * Добавление секунд к указанному времени
     * @param timeSpan {timestamp} указанное время
     * @param s {Number} сколько секунд надо добавить
     * @returns {timestamp}
     */
    addSecondsTo: function (timeSpan, s) {

        var ts = timeSpan.Hours * 3600;
        ts += timeSpan.Minutes * 60;
        ts += timeSpan.Seconds;

        ts += s;

        var h = ~~(ts / 3600);
        ts = ts % 3600;
        var m = ~~(ts / 60);
        ts = ts % 60;

        timeSpan.Hours = h;
        timeSpan.Minutes = m;
        timeSpan.Seconds = ts;
        return timeSpan;
    }
};<commit_msg>added the function for Sync Server time<commit_after>/**
 * Класс вычислений оставшегося времени для лотов
 * Время синхронезируется с сервером
 * @constructor
 * @dependencies {jQuery}
 * @param options {Object} - настройки
 */
function LotTimer (options) {

    this._syncUrl = options.syncUrl;
    this._durationMode = options._durationMode;

}

LotTimer.prototype = {

    /**
     * URL для синхронизации серверного времени
     */
    _syncUrl: '',

    /**
     * Серверное время
     * @type {timestamp}
     */
    _serverTime: null,

    /**
     * Остаток времени
     * Обновляеться с сервера переодически - корректируеться
     * А так же обновляется при выислении по таймеру
     * @type {timestamp}
     */
     _remainderTime: null,

    /**
     * Если параметр выставлен в "ProlongationByLots" - проиходит обновление лотов
     */
    _durationMode: null,

    /**
     * Возаращает URL со случайным параметром, чтобы не кешировать
     * ответ от сервера, даже если он не поддерживает ETag
     * @param url {String} - URL запроса
     * @private
     */
    _getURLWithCMD: function (url) {
        return url + '?' + Date.now();
    },

    /**
     * Синхронезация времени с сервером
     */
    syncServerTime: function () {
        var startSyncTime = this.getPresentTime();

        $.get(this._getURLWithCMD(this._syncUrl)
            .done(this._parseResponseServerTime.bind(this, startSyncTime)))
            .fail(this.signalServerResponseFail.bind(this));
    },

    _parseResponseServerTime: function (response, startSyncTime) {
        var timeSpentOnRequest;

        if ($.type(response) !== 'object' || !response.endTimeSpan) {
            this.signalServerResponseFail();
            return;
        }

        // вычисляеться время потраченное на отправление и полуение запроса от сервера
        // делиться на два - это интересно почему, видимо из практики вычитано
        timeSpentOnRequest = Math.floor((this.getPresentTime() - startSyncTime) / 2);

        // Вычисляеться еоличество прошедщих секунд по вычелсенному времени потраченному на запрос
        // и добавляеться к серверному времени
        this._remainderTime = addSeconds(response.endTimeSpan, Math.round(timeSpentOnRequest / 1000));

        if (this._isTimeOver()) {
            this.signalTimeIsOver();
            return;
        }

        this.setServerTime(response.dateTime);

    },

    /**
     * Сохранить серверное время
     * @param dataTime {Date}
     */
    setServerTime: function (dataTime) {

        if (dataTime) {
            this._serverTime = new Date(
                dataTime.Year,
                dataTime.Month - 1,
                dataTime.Day,
                dataTime.Hour,
                dataTime.Minute,
                dataTime.Second,
                dataTime.Millisecond + diffGetDate
            );
        }

        this.signalServerTimeUpdated();

        if (this._durationMode === 'ProlongationByLots') {
            this.getLotsServerTime();
        }
    },

    /**
     * Проверка завершены ли торги или нет?
     * @param remainingTime {timestamp}
     * @private
     * @return {Boolean}
     */
    _isTimeOver: function (remainingTime) {
        var _remainingTime = remainingTime || this._remainderTime;

        return _remainingTime.Hours == 0
            && _remainingTime.Minutes == 0
            && _remainingTime.Seconds == 0;
    },

    /**
     * Сигнал о завершении торгов
     * @override
     */
    signalTimeIsOver: function () {},

    /**
     * Запрос к серверу для получения серверного времени обвалился или пришел неверный ответ
     * @override
     */
    signalServerResponseFail: function () {},

    /**
     * Сигнал о завершении оновления серверного времени
     */
    signalServerTimeUpdated: function () {
        //this.showServerTime();
        //this.showRemainingTime();
    },

    /**
     * Получение данных лотов
     */
    getLotsData: function () {},

    /**
     * Получение оставшегося времени
     * @param timeFrom {timestamp} - от какого времени вести отчет
     * @return {timestamp} - время сколько осталось
     */
    getRemainderTime: function (timeFrom) {
        var start, diffTime, diffSec;

        // время последнего запроса лотов
        start = timeFrom;

        // сколько прошло времени с последнего запроса лотов
        diffTime = (this.getPresentTime() - start);

        // количество секунд с прошедших за это время
        diffSec = Math.floor(diffTime / 1000);

        // расчет прошедших секунд со времени старта
        // вычитаем из времени старта, для получения времени оставшегося
        return this.addSecondsTo(timeFrom, 0 - (diffSec + 1));
    },

    /**
     * Получить текущее время
     */
    getPresentTime: function () {
        return new Date().getTime();
    },

    /**
     * Добавление секунд к указанному времени
     * @param timeSpan {timestamp} указанное время
     * @param s {Number} сколько секунд надо добавить
     * @returns {timestamp}
     */
    addSecondsTo: function (timeSpan, s) {

        var ts = timeSpan.Hours * 3600;
        ts += timeSpan.Minutes * 60;
        ts += timeSpan.Seconds;

        ts += s;

        var h = ~~(ts / 3600);
        ts = ts % 3600;
        var m = ~~(ts / 60);
        ts = ts % 60;

        timeSpan.Hours = h;
        timeSpan.Minutes = m;
        timeSpan.Seconds = ts;
        return timeSpan;
    }
};<|endoftext|>


<commit_before>import React from 'react';
// Needed for React Developer Tools
window.React = React;

import Router, {Route, DefaultRoute, NotFoundRoute} from 'react-router';
import RouterContainer from './services/RouterContainer';

import AuthService from './services/AuthService';
import Master from './master';
import Login from './pages/Login';
import Home from './pages/Home';
import ErrorPage from './pages/ErrorPage';

var routes = (
  <Route handler={Master}>
    <DefaultRoute name="home" handler={Home}/>
    <Route name="login" handler={Login}/>
    <NotFoundRoute handler={ErrorPage}/>
  </Route>
);

var router = Router.create({routes});
RouterContainer.set(router);

let jwt = localStorage.getItem('jwt');
if (jwt && jwt !== undefined && jwt !== null && jwt !== 'undefined') {
  AuthService.verify(jwt);
}

router.run(function (Handler) {
  React.render(<Handler />, document.body);
});
<commit_msg>Update<commit_after>import React from 'react';
import ReactDOM from 'react-dom';

import API from './api';

import {routes} from './routes';

// needed for React Developer Tools
window.React = React;

const checkToken = () => {
  let jwt = localStorage.getItem('jwt');
  if (
    jwt
    && jwt !== undefined
    && jwt !== null
    && jwt !== 'undefined'
  ) {
    API.auth.verify(jwt);
  }
  // check token every 30 minutes
  setTimeout(checkToken, 1000 * 60 * 30);
};

checkToken();

const app = document.getElementById('app');

try {
  ReactDOM.render(routes, app);
} catch (e) {
  console.error(e);
}
<|endoftext|>


<commit_before>{
        'type': 'game', // game or console
        'name': 'Computer Space',
        'authors': ['Nolan Bushnell', 'Ted Dabney'],
        'company': ['Nutting Associates'],
        'year': '1971',
        'date': '20-06-1951', // Day, Month, Year
        'headline': 'First Game about sport',
        'resume': [
            'Lorem ipsum dolor sit amet, ut viverra vestibulum odio, ',
            'aliquam in massa, velit rutrum pede ipsum mauris duis, ',
            'congue massa sem elit, integer risus. Faucibus in lacus, ',
            'ligula ornare nibh, elit suspendisse tortor sed volutpat, ',
            'etiam tempus wisi semper montes odio, lorem vitae vel eu '
        ].join(''),
        'full_text': [
            'Lorem ipsum dolor sit amet, ut viverra vestibulum odio, ',
            'aliquam in massa, velit rutrum pede ipsum mauris duis, ',
            'congue massa sem elit, integer risus. Faucibus in lacus, ',
            'ligula ornare nibh, elit suspendisse tortor sed volutpat, ',
            'etiam tempus wisi semper montes odio, lorem vitae vel eu ',
            'convallis. Volutpat blandit egestas et libero eu per, venenatis ',
            'aenean, proin quis aliquam viverra potenti vestibulum auctor, ',
            'non amet tellus sodales. In ullamco tellus risus nisl morbi amet. ',
            'Vel quisque nunc in placerat. Vitae id urna, nulla suspendisse, ',
            'molestie sodales ac nisl nulla, tempus occaecati. Sodales lectus, ',
            'mauris dis lectus amet, nascetur ipsum, hac suspendisse blandit eros.'
        ].join(''),
        'thumbnail': 'thumbnail_example.png',
        'main_img': {'name': 'main_picture.png', 'caption': "description"},
        'images': [
            {'name': 'picture1.png', 'caption': "description1"},
            {'name': 'picture2.png', 'caption': "description2"},
            {'name': 'picture3.png', 'caption': "description3"}
        ],
        'video_gameplay': [
            {'url': 'video_url', 'caption': "description", 'type': "youtube"} // type: youtube or vimeo
        ],
        'demos': [
            {'url': 'demonstration_url', 'caption': "description"}
        ],
        'references': [
            {'url': 'http://www.technologizer.com/2011/12/11/computer-space-and-the-dawn-of-the-arcade-video-game/', 'caption': "Computer Space and the Dawn of the Arcade Video Game"},
            {'url': 'reference_url2', 'caption': "description"},
            {'url': 'reference_url3', 'caption': "description"}
        ]
}
<commit_msg>Update computer_space_temp.js<commit_after>{
        'type': 'console', // game or console
        'name': 'Computer Space',
        'authors': ['Nolan K. Bushnell', 'Ted Dabney'],
        'company': ['Nutting Associates'],
        'year': '1971',
        'date': '15-10-1971', // Day, Month, Year
        'headline': 'First Game about sport',
        'resume': [
            'Lorem ipsum dolor sit amet, ut viverra vestibulum odio, ',
            'aliquam in massa, velit rutrum pede ipsum mauris duis, ',
            'congue massa sem elit, integer risus. Faucibus in lacus, ',
            'ligula ornare nibh, elit suspendisse tortor sed volutpat, ',
            'etiam tempus wisi semper montes odio, lorem vitae vel eu '
        ].join(''),
        'full_text': [
            'Em 1971  os Estados Unidos da América começavam a retirar suas tropas do Vietnã e a geração paz e amor aos poucos dava espaço para o pessoal da discoteca.',
            'Em meio a este cenário de transição do protesto para a era da tecnologia e eletrônica, Nolan K. Bushnell, o então engenheiro-chefe da  Nutting Associates em parceiria com Ted Dabney , lança o Arcade Computer Space.',
            'Computer Space tem clara inspiração no game SpaceWar e foi o primeiro arcade produzido em grande escala.',
            'O game consiste em eliminar adversários que se assemelham a um disco voador; para isso você comanda uma espaçonave parecida com um ônibus espacial e que poderia ser tribulado pelos Thunderbirds.',
            'Bushnell transformou o quarto da sua filha em uma oficina para poder criar uma máquina que devido ao espaço que os computadores da época ocupavam, serviria apenas para jogar o SpaceWar!, e essa máquina foi chamada de Computer Space. O Computer Space foi o primeiro arcade do mundo e também o primeiro jogo eletrônico a ser comercializado no mundo. Como a experiência deu certo, a Nutting Associates contrata Bushnell temporariamente para montar os arcades Computer Space. Mais tarde Bushnell sai da empresa e funda a Atari.'
        ].join(''),
        'thumbnail': 'thumbnail_example.png',
        'main_img': {'name': 'main_picture.png', 'caption': "description"},
        'images': [
            {'name': 'picture1.png', 'caption': "description1"},
            {'name': 'picture2.png', 'caption': "description2"},
            {'name': 'picture3.png', 'caption': "description3"}
        ],
        'video_gameplay': [
            {'url': 'https://www.youtube.com/watch?feature=player_embedded&v=Nhdu2Jh9cuc', 'caption': "Computer Space gameplay", 'type': "youtube"} // type: youtube or vimeo
        ],
        'demos': [
            {'url': 'demonstration_url', 'caption': "description"}
        ],
        'references': [
            {'url': 'http://www.technologizer.com/2011/12/11/computer-space-and-the-dawn-of-the-arcade-video-game/', 'caption': "Computer Space and the Dawn of the Arcade Video Game"},
            {'url': 'http://blablagames.net/?p=910', 'caption': "BLA…Bla…Games"},
            {'url': 'https://gamesample.wordpress.com/tag/computer-space/', 'caption': "A historia dos jogos eletrônicos parte 2 – O nascimento dos Arcades e consoles"}
        ]
}
<|endoftext|>