<commit_before>/*global Tile */
import Geo from './geo';
import {StyleParser} from './styles/style_parser';
import {StyleManager} from './styles/style_manager';
import WorkerBroker from './utils/worker_broker';
import Texture from './gl/texture';

import log from 'loglevel';

export default class Tile {

    /**
        Tile
        @constructor
        Required properties:
        coords: object with {x, y, z} properties identifying tile coordinate location
        worker: web worker to handle tile construction
    */
    constructor({ coords, worker, max_zoom, style_zoom }) {
        Object.assign(this, {
            coords: {
                x: null,
                y: null,
                z: null
            },
            debug: {},
            loading: false,
            loaded: false,
            error: null,
            worker: null,
            generation: null,
            visible: false,
            center_dist: 0
        });

        this.worker = worker;
        this.max_zoom = max_zoom;

        this.coords = coords;
        this.coords = Tile.calculateOverZoom(this.coords, this.max_zoom);
        this.key = Tile.key(this.coords);
        this.min = Geo.metersForTile(this.coords);
        this.max = Geo.metersForTile({x: this.coords.x + 1, y: this.coords.y + 1, z: this.coords.z }),
        this.span = { x: (this.max.x - this.min.x), y: (this.max.y - this.min.y) };
        this.bounds = { sw: { x: this.min.x, y: this.max.y }, ne: { x: this.max.x, y: this.min.y } };
        this.style_zoom = style_zoom || this.coords.z; // zoom level to be used for styling

        this.meshes = {}; // renderable VBO meshes keyed by style
        this.textures = []; // textures that the tile owns (labels, etc.)
    }

    static create(spec) {
        return new Tile(spec);
    }

    static key({x, y, z}) {
        return [x, y, z].join('/');
    }

    static calculateOverZoom({x, y, z}, max_zoom) {
        max_zoom = max_zoom || z;

        if (z > max_zoom) {
            let zdiff = z - max_zoom;

            x = Math.floor(x >> zdiff);
            y = Math.floor(y >> zdiff);
            z -= zdiff;
        }

        return {x, y, z};
    }

    // Sort a set of tile instances (which already have a distance from center tile computed)
    static sort(tiles) {
        return tiles.sort((a, b) => {
            let ad = a.center_dist;
            let bd = b.center_dist;
            return (bd > ad ? -1 : (bd === ad ? 0 : 1));
        });
    }

    freeResources() {
        if (this.meshes) {
            for (let m in this.meshes) {
                this.meshes[m].destroy();
            }
        }

        if (this.textures) {
            for (let t of this.textures) {
                let texture = Texture.textures[t];
                if (texture) {
                    texture.destroy();
                }
            }
        }

        this.meshes = {};
        this.textures = [];
    }

    destroy() {
        this.workerMessage('removeTile', this.key);
        this.freeResources();
        this.worker = null;
    }

    buildAsMessage() {
        return {
            key: this.key,
            coords: this.coords,
            min: this.min,
            max: this.max,
            style_zoom: this.style_zoom,
            generation: this.generation,
            debug: this.debug
        };
    }

    workerMessage (...message) {
        return WorkerBroker.postMessage(this.worker, ...message);
    }

    build(generation) {
        this.generation = generation;
        if (!this.loaded) {
            this.loading = true;
        }
        return this.workerMessage('buildTile', { tile: this.buildAsMessage() }).catch(e => { throw e; });
    }

    // Process geometry for tile - called by web worker
    // Returns a set of tile keys that should be sent to the main thread (so that we can minimize data exchange between worker and main thread)
    static buildGeometry (tile, layers, rules, styles) {
        tile.debug.rendering = +new Date();

        for (let source_name in tile.sources) {
            let source = tile.sources[source_name];
            source.debug.rendering = +new Date();
            source.debug.features = 0;

            // Treat top-level style rules as 'layers'
            for (let layer_name in layers) {
                let layer = layers[layer_name];
                // Skip layers with no data source defined
                if (!layer.data) {
                    log.warn(`Layer ${layer} was defined without a geometry data source and will not be rendered.`);
                    continue;
                }

                // Source names don't match
                if (layer.data.source !== source_name) {
                    continue;
                }

                let geom = Tile.getDataForSource(source, layer.data, layer_name);
                if (!geom) {
                    continue;
                }

                // Render features in layer
                let num_features = geom.features.length;
                for (let f = num_features-1; f >= 0; f--) {
                    let feature = geom.features[f];
                    let context = StyleParser.getFeatureParseContext(feature, tile);

                    // Get draw groups for this feature
                    let layer_rules = rules[layer_name];
                    let draw_groups = layer_rules.buildDrawGroups(context, true);
                    if (!draw_groups) {
                        continue;
                    }

                    // Render draw groups
                    for (let group_name in draw_groups) {
                        let group = draw_groups[group_name];
                        if (!group.visible) {
                            continue;
                        }

                        // Add to style
                        let style_name = group.style || group_name;
                        let style = styles[style_name];

                        if (!style) {
                            log.warn(`Style '${style_name}' not found for rule in layer '${layer_name}':`, group, feature);
                            continue;
                        }

                        context.properties = group.properties; // add rule-specific properties to context

                        style.addFeature(feature, group, context);

                        context.properties = null; // clear group-specific properties
                    }

                    source.debug.features++;
                }

            }

            source.debug.rendering = +new Date() - source.debug.rendering;
        }

        // Finalize array buffer for each render style
        let tile_styles = StyleManager.stylesForTile(tile.key);
        tile.mesh_data = {};
        let queue = [];
        for (let style_name of tile_styles) {
            let style = styles[style_name];
            queue.push(style.endData(tile.key).then((style_data) => {
                if (style_data) {
                    tile.mesh_data[style_name] = {
                        vertex_data: style_data.vertex_data,
                        uniforms: style_data.uniforms,
                        textures: style_data.textures
                    };
                }
            }));
        }

        return Promise.all(queue).then(() => {
            // Aggregate debug info
            tile.debug.rendering = +new Date() - tile.debug.rendering;
            tile.debug.projection = 0;
            tile.debug.features = 0;
            tile.debug.network = 0;
            tile.debug.parsing = 0;

            for (let i in tile.sources) {
                tile.debug.features  += tile.sources[i].debug.features;
                tile.debug.projection += tile.sources[i].debug.projection;
                tile.debug.network += tile.sources[i].debug.network;
                tile.debug.parsing += tile.sources[i].debug.parsing;
            }

            // Return keys to be transfered to main thread
            return ['mesh_data'];
        });
    }

    /**
        Retrieves geometry from a tile according to a data source definition
    */
    static getDataForSource (source_data, source_config, default_layer = null) {
        var geom;

        if (source_config != null) {
            // If no layer specified, and a default source layer exists
            if (!source_config.layer && source_data.layers._default) {
                geom = source_data.layers._default;
            }
            // If no layer specified, and a default requested layer exists
            else if (!source_config.layer && default_layer) {
                geom = source_data.layers[default_layer];
            }
            // If a layer is specified by name, use it
            else if (typeof source_config.layer === 'string') {
                geom = source_data.layers[source_config.layer];
            }
            // Assemble a custom layer via a function, which is called with all source layers
            else if (typeof source_config.layer === 'function') {
                geom = source_config.layer(source_data.layers);
            }
        }

        return geom;
    }

    /**
       Called on main thread when a web worker completes processing
       for a single tile.
    */
    buildMeshes(styles) {
        if (this.error) {
            log.error(`main thread tile load error for ${this.key}: ${this.error}`);
            return;
        }

        // Cleanup existing VBOs
        this.freeResources();

        // Debug
        this.debug.geometries = 0;
        this.debug.buffer_size = 0;

        // Create VBOs
        let mesh_data = this.mesh_data;
        if (mesh_data) {
            for (var s in mesh_data) {
                if (mesh_data[s].vertex_data) {
                    this.debug.buffer_size += mesh_data[s].vertex_data.byteLength;
                    if (!styles[s]) {
                        log.warn(`Could not create mesh because style '${s}' not found, for tile ${this.key}, aborting tile`);
                        this.meshes = {};
                        break;
                    }
                    this.meshes[s] = styles[s].makeMesh(mesh_data[s].vertex_data, mesh_data[s]);
                    this.debug.geometries += this.meshes[s].geometry_count;
                }

                // Assign ownership to textures if needed
                if (mesh_data[s].textures) {
                    this.textures.push(...mesh_data[s].textures);
                }
            }
        }

        this.debug.geom_ratio = (this.debug.geometries / this.debug.features).toFixed(1);
        this.mesh_data = null; // TODO: might want to preserve this for rebuilding geometries when styles/etc. change?
        this.printDebug();
    }

    /**
        Called on main thread when web worker completes processing, but tile has since been discarded
        Frees resources that would have been transferred to the tile object.
        Static method because the tile object no longer exists (the tile data returned by the worker is passed instead).
    */
    static abortBuild (tile) {
        if (tile.mesh_data) {
            for (let s in tile.mesh_data) {
                let textures = tile.mesh_data[s].textures;
                if (textures) {
                    for (let t of textures) {
                        let texture = Texture.textures[t];
                        if (texture) {
                            log.trace(`destroying texture ${t} for tile ${tile.key}`);
                            texture.destroy();
                        }
                    }
                }
            }
        }
    }

    printDebug () {
        log.debug(`Tile: debug for ${this.key}: [  ${JSON.stringify(this.debug)} ]`);
    }

    update(scene) {
        if (this.coords.z === scene.center_tile.z && scene.visible_tiles[this.key]) {
            this.visible = true;
        }
        else {
            this.visible = false;
        }

        // TODO: handle tiles of mismatching zoom levels
        if (this.coords.z === scene.center_tile.z) {
            this.center_dist = Math.abs(scene.center_tile.x - this.coords.x) + Math.abs(scene.center_tile.y - this.coords.y);
        }
        else {
            this.center_dist = Infinity;
        }
    }

    // Slice a subset of keys out of a tile
    // Includes a minimum set of pre-defined keys for load state, debug. etc.
    // We use this to send a subset of the tile back to the main thread, to minimize unnecessary data transfer
    // (e.g. very large items like feature geometry are not needed on the main thread)
    static slice (tile, keys) {
        let keep = [
            'key',
            'loading',
            'loaded',
            'generation',
            'error',
            'debug'
        ];
        keep.push(...keys);

        // Build the tile subset
        var tile_subset = {};
        for (let key of keep) {
            tile_subset[key] = tile[key];
        }

        return tile_subset;
    }

    /**
        Called on worker to cancel loading
        Static method because the worker only has object representations of tile data, there is no
        tile instance created yet.
    */
    static cancel(tile) {
        if (tile && tile.sources) {
            Object.keys(tile.sources).
                map(s => tile.sources[s].request).
                filter(s => s).
                forEach(s => s.abort());
        }
    }

    merge(other) {
        for (var key in other) {
            if (key !== 'key') {
                this[key] = other[key];
            }
        }
        return this;
    }

}
<commit_msg>fix tile build on source error<commit_after>/*global Tile */
import Geo from './geo';
import {StyleParser} from './styles/style_parser';
import {StyleManager} from './styles/style_manager';
import WorkerBroker from './utils/worker_broker';
import Texture from './gl/texture';

import log from 'loglevel';

export default class Tile {

    /**
        Tile
        @constructor
        Required properties:
        coords: object with {x, y, z} properties identifying tile coordinate location
        worker: web worker to handle tile construction
    */
    constructor({ coords, worker, max_zoom, style_zoom }) {
        Object.assign(this, {
            coords: {
                x: null,
                y: null,
                z: null
            },
            debug: {},
            loading: false,
            loaded: false,
            error: null,
            worker: null,
            generation: null,
            visible: false,
            center_dist: 0
        });

        this.worker = worker;
        this.max_zoom = max_zoom;

        this.coords = coords;
        this.coords = Tile.calculateOverZoom(this.coords, this.max_zoom);
        this.key = Tile.key(this.coords);
        this.min = Geo.metersForTile(this.coords);
        this.max = Geo.metersForTile({x: this.coords.x + 1, y: this.coords.y + 1, z: this.coords.z }),
        this.span = { x: (this.max.x - this.min.x), y: (this.max.y - this.min.y) };
        this.bounds = { sw: { x: this.min.x, y: this.max.y }, ne: { x: this.max.x, y: this.min.y } };
        this.style_zoom = style_zoom || this.coords.z; // zoom level to be used for styling

        this.meshes = {}; // renderable VBO meshes keyed by style
        this.textures = []; // textures that the tile owns (labels, etc.)
    }

    static create(spec) {
        return new Tile(spec);
    }

    static key({x, y, z}) {
        return [x, y, z].join('/');
    }

    static calculateOverZoom({x, y, z}, max_zoom) {
        max_zoom = max_zoom || z;

        if (z > max_zoom) {
            let zdiff = z - max_zoom;

            x = Math.floor(x >> zdiff);
            y = Math.floor(y >> zdiff);
            z -= zdiff;
        }

        return {x, y, z};
    }

    // Sort a set of tile instances (which already have a distance from center tile computed)
    static sort(tiles) {
        return tiles.sort((a, b) => {
            let ad = a.center_dist;
            let bd = b.center_dist;
            return (bd > ad ? -1 : (bd === ad ? 0 : 1));
        });
    }

    freeResources() {
        if (this.meshes) {
            for (let m in this.meshes) {
                this.meshes[m].destroy();
            }
        }

        if (this.textures) {
            for (let t of this.textures) {
                let texture = Texture.textures[t];
                if (texture) {
                    texture.destroy();
                }
            }
        }

        this.meshes = {};
        this.textures = [];
    }

    destroy() {
        this.workerMessage('removeTile', this.key);
        this.freeResources();
        this.worker = null;
    }

    buildAsMessage() {
        return {
            key: this.key,
            coords: this.coords,
            min: this.min,
            max: this.max,
            style_zoom: this.style_zoom,
            generation: this.generation,
            debug: this.debug
        };
    }

    workerMessage (...message) {
        return WorkerBroker.postMessage(this.worker, ...message);
    }

    build(generation) {
        this.generation = generation;
        if (!this.loaded) {
            this.loading = true;
        }
        return this.workerMessage('buildTile', { tile: this.buildAsMessage() }).catch(e => { throw e; });
    }

    // Process geometry for tile - called by web worker
    // Returns a set of tile keys that should be sent to the main thread (so that we can minimize data exchange between worker and main thread)
    static buildGeometry (tile, layers, rules, styles) {
        tile.debug.rendering = +new Date();

        for (let source_name in tile.sources) {
            let source = tile.sources[source_name];
            source.debug.rendering = +new Date();
            source.debug.features = 0;

            // Treat top-level style rules as 'layers'
            for (let layer_name in layers) {
                let layer = layers[layer_name];
                // Skip layers with no data source defined
                if (!layer.data) {
                    log.warn(`Layer ${layer} was defined without a geometry data source and will not be rendered.`);
                    continue;
                }

                // Source names don't match
                if (layer.data.source !== source_name) {
                    continue;
                }

                let geom = Tile.getDataForSource(source, layer.data, layer_name);
                if (!geom) {
                    continue;
                }

                // Render features in layer
                let num_features = geom.features.length;
                for (let f = num_features-1; f >= 0; f--) {
                    let feature = geom.features[f];
                    let context = StyleParser.getFeatureParseContext(feature, tile);

                    // Get draw groups for this feature
                    let layer_rules = rules[layer_name];
                    let draw_groups = layer_rules.buildDrawGroups(context, true);
                    if (!draw_groups) {
                        continue;
                    }

                    // Render draw groups
                    for (let group_name in draw_groups) {
                        let group = draw_groups[group_name];
                        if (!group.visible) {
                            continue;
                        }

                        // Add to style
                        let style_name = group.style || group_name;
                        let style = styles[style_name];

                        if (!style) {
                            log.warn(`Style '${style_name}' not found for rule in layer '${layer_name}':`, group, feature);
                            continue;
                        }

                        context.properties = group.properties; // add rule-specific properties to context

                        style.addFeature(feature, group, context);

                        context.properties = null; // clear group-specific properties
                    }

                    source.debug.features++;
                }

            }

            source.debug.rendering = +new Date() - source.debug.rendering;
        }

        // Finalize array buffer for each render style
        let tile_styles = StyleManager.stylesForTile(tile.key);
        tile.mesh_data = {};
        let queue = [];
        for (let style_name of tile_styles) {
            let style = styles[style_name];
            queue.push(style.endData(tile.key).then((style_data) => {
                if (style_data) {
                    tile.mesh_data[style_name] = {
                        vertex_data: style_data.vertex_data,
                        uniforms: style_data.uniforms,
                        textures: style_data.textures
                    };
                }
            }));
        }

        return Promise.all(queue).then(() => {
            // Aggregate debug info
            tile.debug.rendering = +new Date() - tile.debug.rendering;
            tile.debug.projection = 0;
            tile.debug.features = 0;
            tile.debug.network = 0;
            tile.debug.parsing = 0;

            for (let i in tile.sources) {
                tile.debug.features  += tile.sources[i].debug.features;
                tile.debug.projection += tile.sources[i].debug.projection;
                tile.debug.network += tile.sources[i].debug.network;
                tile.debug.parsing += tile.sources[i].debug.parsing;
            }

            // Return keys to be transfered to main thread
            return ['mesh_data'];
        });
    }

    /**
        Retrieves geometry from a tile according to a data source definition
    */
    static getDataForSource (source_data, source_config, default_layer = null) {
        var geom;

        if (source_config != null) {
            // If no layer specified, and a default source layer exists
            if (!source_config.layer && source_data.layers._default) {
                geom = source_data.layers._default;
            }
            // If no layer specified, and a default requested layer exists
            else if (!source_config.layer && default_layer) {
                geom = source_data.layers[default_layer];
            }
            // If a layer is specified by name, use it
            else if (typeof source_config.layer === 'string') {
                geom = source_data.layers[source_config.layer];
            }
            // Assemble a custom layer via a function, which is called with all source layers
            else if (typeof source_config.layer === 'function') {
                geom = source_config.layer(source_data.layers);
            }
        }

        return geom;
    }

    /**
       Called on main thread when a web worker completes processing
       for a single tile.
    */
    buildMeshes(styles) {
        if (this.error) {
            log.error(`main thread tile load error for ${this.key}: ${this.error}`);
            return;
        }

        // Cleanup existing VBOs
        this.freeResources();

        // Debug
        this.debug.geometries = 0;
        this.debug.buffer_size = 0;

        // Create VBOs
        let mesh_data = this.mesh_data;
        if (mesh_data) {
            for (var s in mesh_data) {
                if (mesh_data[s].vertex_data) {
                    this.debug.buffer_size += mesh_data[s].vertex_data.byteLength;
                    if (!styles[s]) {
                        log.warn(`Could not create mesh because style '${s}' not found, for tile ${this.key}, aborting tile`);
                        this.meshes = {};
                        break;
                    }
                    this.meshes[s] = styles[s].makeMesh(mesh_data[s].vertex_data, mesh_data[s]);
                    this.debug.geometries += this.meshes[s].geometry_count;
                }

                // Assign ownership to textures if needed
                if (mesh_data[s].textures) {
                    this.textures.push(...mesh_data[s].textures);
                }
            }
        }

        this.debug.geom_ratio = (this.debug.geometries / this.debug.features).toFixed(1);
        this.mesh_data = null; // TODO: might want to preserve this for rebuilding geometries when styles/etc. change?
        this.printDebug();
    }

    /**
        Called on main thread when web worker completes processing, but tile has since been discarded
        Frees resources that would have been transferred to the tile object.
        Static method because the tile object no longer exists (the tile data returned by the worker is passed instead).
    */
    static abortBuild (tile) {
        if (tile.mesh_data) {
            for (let s in tile.mesh_data) {
                let textures = tile.mesh_data[s].textures;
                if (textures) {
                    for (let t of textures) {
                        let texture = Texture.textures[t];
                        if (texture) {
                            log.trace(`destroying texture ${t} for tile ${tile.key}`);
                            texture.destroy();
                        }
                    }
                }
            }
        }
    }

    printDebug () {
        log.debug(`Tile: debug for ${this.key}: [  ${JSON.stringify(this.debug)} ]`);
    }

    update(scene) {
        if (this.coords.z === scene.center_tile.z && scene.visible_tiles[this.key]) {
            this.visible = true;
        }
        else {
            this.visible = false;
        }

        // TODO: handle tiles of mismatching zoom levels
        if (this.coords.z === scene.center_tile.z) {
            this.center_dist = Math.abs(scene.center_tile.x - this.coords.x) + Math.abs(scene.center_tile.y - this.coords.y);
        }
        else {
            this.center_dist = Infinity;
        }
    }

    // Slice a subset of keys out of a tile
    // Includes a minimum set of pre-defined keys for load state, debug. etc.
    // We use this to send a subset of the tile back to the main thread, to minimize unnecessary data transfer
    // (e.g. very large items like feature geometry are not needed on the main thread)
    static slice (tile, keys) {
        let keep = [
            'key',
            'loading',
            'loaded',
            'generation',
            'error',
            'debug'
        ];
        if (Array.isArray(keys)) {
            keep.push(...keys);
        }

        // Build the tile subset
        var tile_subset = {};
        for (let key of keep) {
            tile_subset[key] = tile[key];
        }

        return tile_subset;
    }

    /**
        Called on worker to cancel loading
        Static method because the worker only has object representations of tile data, there is no
        tile instance created yet.
    */
    static cancel(tile) {
        if (tile && tile.sources) {
            Object.keys(tile.sources).
                map(s => tile.sources[s].request).
                filter(s => s).
                forEach(s => s.abort());
        }
    }

    merge(other) {
        for (var key in other) {
            if (key !== 'key') {
                this[key] = other[key];
            }
        }
        return this;
    }

}
<|endoftext|>


<commit_before>var nodemailer = require('nodemailer'),
    secrets       = require('../../config/secrets'),
    slug          = require('slug'),
    Tutorial      = require('../models/tutorial.server.model');

module.exports = {

  create: function(req, res, next){
    var tuts            = new Tutorial();
    tuts.title          = req.body.title;
    tuts.slug           = slug(req.body.title.toLowerCase());
    tuts.content        = req.body.content;
    tuts.postedBy       = req.body.postedBy;
    tuts.save( function(err, tuts){
      if(err) {
        if(err.name == 'MongoError' && err.message.indexOf('$title_1') > 0 || err.message.indexOf('$slug_1') > 0) {
          return res.json({ success: false, message: "Tutorial is already registered. Please write another" });
        }else{
          return res.json(err);
        }
      } else {
        return res.status(200).json({ success: true, message: "Tutorial submitted successfully.Review will take place now" });
      }
    });
    next();
  },

  getAllTutorials: function( req, res){
    Tutorial.find({ approval_status: true }, function(err, tutorials) {
      res.status(200).json(tutorials);
    });
  },

  getEachTutorialDetails: function(req, res, next){
    var tutorialSlug = req.params.slug;

    Tutorial.find({ slug: tutorialSlug }, function (err, tutorial) {
      if(err) {
        return res.status(404).json({ err: err });
      }

      if(tutorial.length === 0){
        return res.json({ success: false, message: 'Tutorial not found.' });
      }
      else if(tutorial.length == 1) {
        var tutorialDetails = {};
        var tuts = tutorial[0];
        tutorialDetails.id             = tuts._id;
        tutorialDetails.title          = tuts.title;
        tutorialDetails.content        = tuts.content;
        tutorialDetails.postedBy       = tuts.postedBy;
        tutorialDetails.postedOn       = tuts.created_on;

        return res.json({success: true, tutorial: tutorialDetails});
      }
      next();
    });
  },
};<commit_msg>Add Self-Explanatory Comments<commit_after>var nodemailer = require('nodemailer'),
    secrets       = require('../../config/secrets'),
    slug          = require('slug'),
    Tutorial      = require('../models/tutorial.server.model');

module.exports = {

  /**
   * Saves A New Tutorial Posted By User
   * @param  {void}   req
   * @param  {void}   res
   * @param  {Function} next
   * @return {object}
   */
  create: function(req, res, next){
    var tuts            = new Tutorial();
    tuts.title          = req.body.title;
    tuts.slug           = slug(req.body.title.toLowerCase());
    tuts.content        = req.body.content;
    tuts.postedBy       = req.body.postedBy;
    tuts.save( function(err, tuts){
      if(err) {
        if(err.name == 'MongoError' && err.message.indexOf('$title_1') > 0 || err.message.indexOf('$slug_1') > 0) {
          return res.json({ success: false, message: "Tutorial is already registered. Please write another" });
        }else{
          return res.json(err);
        }
      } else {
        return res.status(200).json({ success: true, message: "Tutorial submitted successfully.Review will take place now" });
      }
    });
    next();
  },

  /**
   * Fetch All Tutorials that have been approved
   * @param  {void} req
   * @param  {void} res
   * @return {object}
   */
  getAllTutorials: function( req, res){
    Tutorial.find({ approval_status: true }, function(err, tutorials) {
      res.status(200).json(tutorials);
    });
  },

  /**
   * Fetch the Details of Each Tutorial
   * @param  {void}   req
   * @param  {void}   res
   * @param  {Function} next
   * @return {object}
   */
  getEachTutorialDetails: function(req, res, next){
    var tutorialSlug = req.params.slug;

    Tutorial.find({ slug: tutorialSlug }, function (err, tutorial) {
      if(err) {
        return res.status(404).json({ err: err });
      }

      if(tutorial.length === 0){
        return res.json({ success: false, message: 'Tutorial not found.' });
      }
      else if(tutorial.length == 1) {
        var tutorialDetails = {};
        var tuts = tutorial[0];
        tutorialDetails.id             = tuts._id;
        tutorialDetails.title          = tuts.title;
        tutorialDetails.content        = tuts.content;
        tutorialDetails.postedBy       = tuts.postedBy;
        tutorialDetails.postedOn       = tuts.created_on;

        return res.json({success: true, tutorial: tutorialDetails});
      }
      next();
    });
  },
};<|endoftext|>


<commit_before>define(
	[ "knockout", "jquery", "jquery-ui"],
	function(ko, $) {
		self.activeTab = ko.observable(topicexplorerModel.view.activeTab).publishOn("TabView.activeTab");
		self.activeModule = ko.observable(topicexplorerModel.view.tab[self.activeTab()].module);
//		self.activeTab.subscribe(function(newValue) {
//			console.log(topicexplorerModel.view.tab[newValue].module);
//			self.activeModule(topicexplorerModel.view.tab[newValue].module);
//			ko.postbox.publish("TabView.activeTab", newValue);
//		});

		self.tabs = ko.observableArray(topicexplorerModel.view.tabs);
		self.invisibleTabs = ko.observableArray(topicexplorerModel.view.invisbleTabs);

		self.windowWidth = ko.observable(topicexplorerModel.view.windowWidth).subscribeTo("windowWidth");

		self.windowWidth.subscribe(function(newValue) {
			topicexplorerModel.view.windowWidth = newValue;
			var allTabs = topicexplorerModel.view.invisibleTabs.concat(topicexplorerModel.view.tabs);
			if($("#desktop").width() / allTabs.length >= 200) {
				topicexplorerModel.view.tabs = allTabs;
				topicexplorerModel.view.invisibleTabs = new Array();
			} else {
				var numVisibleTabs = Math.floor(($("#desktop").width() - 76) / 200);
				topicexplorerModel.view.invisibleTabs = allTabs.slice(0, allTabs.length - numVisibleTabs);
				topicexplorerModel.view.tabs = allTabs.slice(allTabs.length - numVisibleTabs);
			}
			self.invisibleTabs(topicexplorerModel.view.invisibleTabs);
			self.tabs(topicexplorerModel.view.tabs);
		});

		self.setActive = function(allowLoading) {
//
			if(!allowLoading) topicexplorerModel.data.documentsLoading(true);
			$("#desktop").scrollTop(0);
			$(".tab").removeClass('active');
			$("#tab" + topicexplorerModel.view.activeTab).addClass('active');

			self.activeTab(topicexplorerModel.view.activeTab);

			if(self.activeModule() != topicexplorerModel.view.tab[topicexplorerModel.view.activeTab].module) {
				self.activeModule(topicexplorerModel.view.tab[topicexplorerModel.view.activeTab].module);
			}

			ko.postbox.publish("TabView.activeTab", topicexplorerModel.view.activeTab);

//			ko.postbox.publish("TabView.activeTab", topicexplorerModel.view.activeTab);
//			console.log("newTab");

			$("#desktop").scrollTop(topicexplorerModel.view.tab[topicexplorerModel.view.activeTab].scrollPosition);
			if(!allowLoading) topicexplorerModel.data.documentsLoading(false);
		};
		self.toggleActive = function(active) {
			$('#tabMenu').hide();
			topicexplorerModel.view.tab[topicexplorerModel.view.activeTab].scrollPosition = $("#desktop").scrollTop();
			topicexplorerModel.view.activeTab = active;
			var index = $.inArray(active, self.invisibleTabs());
			if(index > -1) {
				topicexplorerModel.view.invisibleTabs.splice(index, 1);
				var newInvisibleTab = topicexplorerModel.view.tabs.shift();
				topicexplorerModel.view.invisibleTabs.push(newInvisibleTab);
				topicexplorerModel.view.tabs.unshift(active);
				self.invisibleTabs(topicexplorerModel.view.invisibleTabs);
				self.tabs(topicexplorerModel.view.tabs);
			}
			self.setActive(false);
		};
		self.deleteTab = function(tab) {
			if(topicexplorerModel.view.tabs.length < 2) {
				alert("Ein Tab muss bleiben!");
				return;
			}

			var tabIndex = $.inArray(tab, topicexplorerModel.view.tabs);

			topicexplorerModel.view.tabs.splice(tabIndex, 1);

			if(self.invisibleTabs().length > 0) {
				var newTabIndex = topicexplorerModel.view.invisibleTabs.shift();
				topicexplorerModel.view.tabs.unshift(newTabIndex);
				self.invisibleTabs(topicexplorerModel.view.invisibleTabs);
			}

			self.tabs(topicexplorerModel.view.tabs);

			if(topicexplorerModel.view.activeTab == tab) {
				if(tabIndex > topicexplorerModel.view.tabs.length - 1) {
					tabIndex = topicexplorerModel.view.tabs.length - 1;
				}
				topicexplorerModel.view.activeTab = topicexplorerModel.view.tabs[tabIndex];
				self.setActive(false);
			}

			delete topicexplorerModel.view.tab[tab];
		};

		self.deleteAllHidden = function() {
			if(confirm("Alle " + self.invisibleTabs().length + " unsichtbaren Tabs loeschen?")) {
				for(key in topicexplorerModel.view.invisibleTabs) {
					delete(topicexplorerModel.view.tab[topicexplorerModel.view.invisibleTabs[key]]);
				}

				topicexplorerModel.view.invisibleTabs = new Array();
				self.invisibleTabs(topicexplorerModel.view.invisibleTabs);
			}
		};
		topicexplorerModel.checkTabExists = function(getParameter) {
			topicexplorerModel.view.tab[topicexplorerModel.view.activeTab].scrollPosition = $("#desktop").scrollTop();

			for(key in topicexplorerModel.view.tab) {
				if(topicexplorerModel.view.tab[key].getParameter == getParameter) {
				var index = $.inArray(key, topicexplorerModel.view.invisibleTabs);
				if(index > -1) {
					topicexplorerModel.view.invisibleTabs.splice(index, 1);
					var newInvisibleTab = topicexplorerModel.view.tabs.shift();
					topicexplorerModel.view.invisibleTabs.push(newInvisibleTab);
					topicexplorerModel.view.tabs.unshift(key);
					self.tabs(topicexplorerModel.view.tabs);
					self.invisibleTabs(topicexplorerModel.view.invisbleTabs);

				}
				topicexplorerModel.view.activeTab = key;

				self.setActive(false);
				return true;
				}
			}
			return false;
		};

		topicexplorerModel.newTab = function(param, headLine, module, focus) {
			if(topicexplorerModel.checkTabExists(param)) {
				return;
			}

			var index = ++topicexplorerModel.view.tabsLastIndex;
			topicexplorerModel.view.activeTab = "t" + index;

			topicexplorerModel.view.tabs.push("t" + index);

			topicexplorerModel.view.tab["t" + index] = new Array();
			topicexplorerModel.view.tab["t" + index].scrollPosition = 0;
			topicexplorerModel.view.tab["t" + index].tabTitle = headLine;
			topicexplorerModel.view.tab["t" + index].module = module;
			topicexplorerModel.view.tab["t" + index].getParameter = param;
			topicexplorerModel.view.tab["t" + index].focus = focus;
			topicexplorerModel.view.tab["t" + index].documentsFull = ko.observable(true);

			if($("#desktop").width() < topicexplorerModel.view.tabs.length * 200 || topicexplorerModel.view.invisibleTabs.length) {
				var allTabs = topicexplorerModel.view.invisibleTabs.concat(topicexplorerModel.view.tabs);
				var numVisibleTabs = Math.floor(($("#desktop").width() - 76) / 200);
				topicexplorerModel.view.invisibleTabs = allTabs.slice(0, allTabs.length - numVisibleTabs);
				topicexplorerModel.view.tabs = allTabs.slice(allTabs.length - numVisibleTabs);
				self.invisibleTabs(topicexplorerModel.view.invisibleTabs);
			}
			self.tabs(topicexplorerModel.view.tabs);
			self.setActive(true);

		};

		return self;
});<commit_msg>#58 wait 10 ms until actualize scrollPosition<commit_after>define(
	[ "knockout", "jquery", "jquery-ui"],
	function(ko, $) {
		self.activeTab = ko.observable(topicexplorerModel.view.activeTab).publishOn("TabView.activeTab");
		self.activeModule = ko.observable(topicexplorerModel.view.tab[self.activeTab()].module);
//		self.activeTab.subscribe(function(newValue) {
//			console.log(topicexplorerModel.view.tab[newValue].module);
//			self.activeModule(topicexplorerModel.view.tab[newValue].module);
//			ko.postbox.publish("TabView.activeTab", newValue);
//		});

		self.tabs = ko.observableArray(topicexplorerModel.view.tabs);
		self.invisibleTabs = ko.observableArray(topicexplorerModel.view.invisbleTabs);

		self.windowWidth = ko.observable(topicexplorerModel.view.windowWidth).subscribeTo("windowWidth");

		self.windowWidth.subscribe(function(newValue) {
			topicexplorerModel.view.windowWidth = newValue;
			var allTabs = topicexplorerModel.view.invisibleTabs.concat(topicexplorerModel.view.tabs);
			if($("#desktop").width() / allTabs.length >= 200) {
				topicexplorerModel.view.tabs = allTabs;
				topicexplorerModel.view.invisibleTabs = new Array();
			} else {
				var numVisibleTabs = Math.floor(($("#desktop").width() - 76) / 200);
				topicexplorerModel.view.invisibleTabs = allTabs.slice(0, allTabs.length - numVisibleTabs);
				topicexplorerModel.view.tabs = allTabs.slice(allTabs.length - numVisibleTabs);
			}
			self.invisibleTabs(topicexplorerModel.view.invisibleTabs);
			self.tabs(topicexplorerModel.view.tabs);
		});

		self.setActive = function(allowLoading) {
//
			if(!allowLoading) topicexplorerModel.data.documentsLoading(true);
			$("#desktop").scrollTop(0);
			$(".tab").removeClass('active');
			$("#tab" + topicexplorerModel.view.activeTab).addClass('active');

			self.activeTab(topicexplorerModel.view.activeTab);

			if(self.activeModule() != topicexplorerModel.view.tab[topicexplorerModel.view.activeTab].module) {
				self.activeModule(topicexplorerModel.view.tab[topicexplorerModel.view.activeTab].module);
			}

			ko.postbox.publish("TabView.activeTab", topicexplorerModel.view.activeTab);

			setTimeout(function() {
				$("#desktop").scrollTop(topicexplorerModel.view.tab[topicexplorerModel.view.activeTab].scrollPosition);
			}, 10);

			if(!allowLoading) topicexplorerModel.data.documentsLoading(false);
		};
		self.toggleActive = function(active) {
			$('#tabMenu').hide();
			topicexplorerModel.view.tab[topicexplorerModel.view.activeTab].scrollPosition = $("#desktop").scrollTop();
			topicexplorerModel.view.activeTab = active;
			var index = $.inArray(active, self.invisibleTabs());
			if(index > -1) {
				topicexplorerModel.view.invisibleTabs.splice(index, 1);
				var newInvisibleTab = topicexplorerModel.view.tabs.shift();
				topicexplorerModel.view.invisibleTabs.push(newInvisibleTab);
				topicexplorerModel.view.tabs.unshift(active);
				self.invisibleTabs(topicexplorerModel.view.invisibleTabs);
				self.tabs(topicexplorerModel.view.tabs);
			}
			self.setActive(false);
		};
		self.deleteTab = function(tab) {
			if(topicexplorerModel.view.tabs.length < 2) {
				alert("Ein Tab muss bleiben!");
				return;
			}

			var tabIndex = $.inArray(tab, topicexplorerModel.view.tabs);

			topicexplorerModel.view.tabs.splice(tabIndex, 1);

			if(self.invisibleTabs().length > 0) {
				var newTabIndex = topicexplorerModel.view.invisibleTabs.shift();
				topicexplorerModel.view.tabs.unshift(newTabIndex);
				self.invisibleTabs(topicexplorerModel.view.invisibleTabs);
			}

			self.tabs(topicexplorerModel.view.tabs);

			if(topicexplorerModel.view.activeTab == tab) {
				if(tabIndex > topicexplorerModel.view.tabs.length - 1) {
					tabIndex = topicexplorerModel.view.tabs.length - 1;
				}
				topicexplorerModel.view.activeTab = topicexplorerModel.view.tabs[tabIndex];
				self.setActive(false);
			}

			delete topicexplorerModel.view.tab[tab];
		};

		self.deleteAllHidden = function() {
			if(confirm("Alle " + self.invisibleTabs().length + " unsichtbaren Tabs loeschen?")) {
				for(key in topicexplorerModel.view.invisibleTabs) {
					delete(topicexplorerModel.view.tab[topicexplorerModel.view.invisibleTabs[key]]);
				}

				topicexplorerModel.view.invisibleTabs = new Array();
				self.invisibleTabs(topicexplorerModel.view.invisibleTabs);
			}
		};
		topicexplorerModel.checkTabExists = function(getParameter) {
			topicexplorerModel.view.tab[topicexplorerModel.view.activeTab].scrollPosition = $("#desktop").scrollTop();

			for(key in topicexplorerModel.view.tab) {
				if(topicexplorerModel.view.tab[key].getParameter == getParameter) {
				var index = $.inArray(key, topicexplorerModel.view.invisibleTabs);
				if(index > -1) {
					topicexplorerModel.view.invisibleTabs.splice(index, 1);
					var newInvisibleTab = topicexplorerModel.view.tabs.shift();
					topicexplorerModel.view.invisibleTabs.push(newInvisibleTab);
					topicexplorerModel.view.tabs.unshift(key);
					self.tabs(topicexplorerModel.view.tabs);
					self.invisibleTabs(topicexplorerModel.view.invisbleTabs);

				}
				topicexplorerModel.view.activeTab = key;

				self.setActive(false);
				return true;
				}
			}
			return false;
		};

		topicexplorerModel.newTab = function(param, headLine, module, focus) {
			if(topicexplorerModel.checkTabExists(param)) {
				return;
			}

			var index = ++topicexplorerModel.view.tabsLastIndex;
			topicexplorerModel.view.activeTab = "t" + index;

			topicexplorerModel.view.tabs.push("t" + index);

			topicexplorerModel.view.tab["t" + index] = new Array();
			topicexplorerModel.view.tab["t" + index].scrollPosition = 0;
			topicexplorerModel.view.tab["t" + index].tabTitle = headLine;
			topicexplorerModel.view.tab["t" + index].module = module;
			topicexplorerModel.view.tab["t" + index].getParameter = param;
			topicexplorerModel.view.tab["t" + index].focus = focus;
			topicexplorerModel.view.tab["t" + index].documentsFull = ko.observable(true);

			if($("#desktop").width() < topicexplorerModel.view.tabs.length * 200 || topicexplorerModel.view.invisibleTabs.length) {
				var allTabs = topicexplorerModel.view.invisibleTabs.concat(topicexplorerModel.view.tabs);
				var numVisibleTabs = Math.floor(($("#desktop").width() - 76) / 200);
				topicexplorerModel.view.invisibleTabs = allTabs.slice(0, allTabs.length - numVisibleTabs);
				topicexplorerModel.view.tabs = allTabs.slice(allTabs.length - numVisibleTabs);
				self.invisibleTabs(topicexplorerModel.view.invisibleTabs);
			}
			self.tabs(topicexplorerModel.view.tabs);
			self.setActive(true);

		};

		return self;
});<|endoftext|>


<commit_before>$(function() {
    var profileCard = {
        $profileImage: $("#profileImage"),
        $name: $("#name"),
        $primaryInfo: $("#primaryInfo"), $secondaryInfo: $("#secondaryInfo"), init: function(user) {
            this.user = user;

            this.render();
        },
        render: function() {
            var mu = Math.round(this.user.mu*100)/100;
            var sigma = Math.round(this.user.sigma*100)/100;
            var score = Math.round((this.user.mu-(3*this.user.sigma))*100)/100;

            var vr = "|";
            this.$profileImage.attr("src", "https://avatars.githubusercontent.com/u/"+this.user["oauthID"]);
            this.$name.html("" + this.user['username'] + "");

            this.$primaryInfo.append("Rank " + this.user['rank']+"");
            this.$primaryInfo.append("
");
            this.$primaryInfo.append("" + this.user['tier'] + " Tier");
            this.$primaryInfo.append("
");
            this.$primaryInfo.append(""+ score +" points");

            this.$secondaryInfo.append($("Made in "+this.user['language']+ ""));
            this.$secondaryInfo.append($("
"));
            if(this.user['level'] != 'Other') {
                this.$secondaryInfo.append($("At "+this.user['level']+ " level"));
                this.$secondaryInfo.append($("
"));
            }
            if(this.user['organization'] != 'Other') {
                this.$secondaryInfo.append($("Member of "+this.user['organization']+ ""));
                this.$secondaryInfo.append($("
"));
            }
            this.$secondaryInfo.append($(""+this.user['numSubmissions']+" "+(parseInt(this.user['numSubmissions']) == 1 ? "bot" : "bots")+" submitted"));
            this.$secondaryInfo.append($("
"));
            this.$secondaryInfo.append($(""+this.user['numGames']+" games played"));
        }
    };

    var historyTable = {
        $panel: $("#historyPanel"),
        $tableBody: $("#historyTableBody"),
        init: function(name, histories) {
            this.name = name;
            this.histories = histories;
            this.render();
        },
        render: function() {
            if(this.histories.length == 0) {
                this.$panel.css("display", "none");
            } else {
                this.$panel.css("display", "block");
                this.$tableBody.empty();
                for(var a = 0; a < Math.min(5, this.histories.length); a++) {
                    this.$tableBody.append(this.getTableRow(this.histories[a]));
                }
            }
        },
        getTableRow: function(history) {
            return "
"+this.name+" v"+history.versionNumber+"	"+history.lastRank+" of "+history.lastNumPlayers+"	"+(history.lastNumGames != null ? history.lastNumGames : "Not Recorded")+"
";
        }
    }

    var notifsTable = {
        $panel: $("#notifsPanel"),
        $tableBody: $("#notifsTableBody"),
        modal: {
            $modal: $("#notifModal"),
            $title: $("#notifModalHeader"),
            $body: $("#notifModalBody"),
            init: function(title, body) {
                this.title = title;
                this.body = body;

                this.render();
            },
            render: function() {
                this.$title.html("
"+this.title+"
");
                this.$body.html(this.body);
            },
            show: function() {
                this.$modal.modal("show");
            }
        },
        show: function() {
            this.$panel.attr("display", "block");
        },
        init: function(notifs) {
            this.name = name;
            this.notifs = notifs;
            this.render();
        },
        render: function() {
            if(this.notifs.length == 0) {
                this.$panel.css("display", "none");
            } else {
                this.$panel.css("display", "block");
                this.$tableBody.empty();
                for(var a = 0; a < Math.min(10, this.notifs.length); a++) {
                    this.$tableBody.append(this.getTableRow(this.notifs[a]));
                }
            }
        },
        getTableRow: function(notif) {
            var titleColor = null;
            if(parseInt(notif.mood) == -1) titleColor = "#d9534f";
            if(parseInt(notif.mood) == 0) titleColor = "#5bc0de";
            if(parseInt(notif.mood) == 1) titleColor = "#5cb85c";

            $title = $(""+notif.title+"	");
            $title.css("color", titleColor);

            var dateComponents = notif.creationTime.split(/[- :]/);
            var gameDate = new Date(Date.UTC(dateComponents[0], dateComponents[1]-1, dateComponents[2], dateComponents[3], dateComponents[4], dateComponents[5]));
            var dateString = gameDate.toLocaleString();

            $row = $("

");
            $row.append($title);
            $row.append(""+dateString+"	");
            $row.css("cursor", "pointer");
            $row.click(this, this.notifClicked.bind(this, notif));

            return $row;
        },
        notifClicked: function(notif) {
            console.log(this);
            this.modal.init(notif.title, notif.body);
            this.modal.show();
        }
    }

    var statTable = {
        $tableBody: $("#statTableBody"),
        stats: [],
        init: function(stats) {
            this.stats = stats;
            this.render();
        },
        render: function() {
            this.$tableBody.empty();
            for(var a = 0; a < this.stats.length; a++) {
                this.$tableBody.append(this.getTableRow(this.stats[a]));
            }
        },
        getTableRow: function(stat) {
            if(stat.mouseOverText != undefined && stat.mouseOverText != null) {
                return "
"+stat.name+"	"+stat.value+"
";
            } else {
                return "
"+stat.name+"	"+stat.value+"
";
            }
        }
    }

    var gameTable = {
        $alternateMessage: $("#noGameMessage"),
        $panel: $("#gamePanel"),
        $table: $("#gameTable"),
        $tableHeader: $("#gameTableHeader"),
        $tableBody: $("#gameTableBody"),
        $loadButton: $("#loadButton"),
        games: [],
        init: function(userID, isMe, getNextGames) {
            this.userID = userID;
            this.getNextGames = getNextGames;
            this.isMe = isMe;

            this.games = getNextGames(this.userID);
            for(var a = 0; a < this.games.length; a++) this.games[a].users.sort(function(p1, p2) { return parseInt(p1.rank) - parseInt(p2.rank); });

            this.$loadButton.click(this, this.loadMore.bind(this));

            this.render();
        },
        render: function() {
            if(this.games.length == 0) {
                this.$alternateMessage.css("display", "block");
                this.$panel.css("display", "none");
            } else {
                this.$tableHeader.html("Time	Opponents	Result	Dimensions	View	");
                if(this.isMe) this.$tableHeader.append("Error Log	");
                this.$tableBody.empty();
                for(var a = 0; a < this.games.length; a++) {
                    this.$tableBody.append(this.getTableRow(this.games[a]));
                }
            }
        },
        getTableRow: function(game) {
            var userID = this.userID;

            playersList = game.users.filter(function(player) {
                return player.userID != userID;
            }).map(function(player) {
                return "";
            }).join(" ");

            var thisUser = game.users.find(function(p){return parseInt(p.userID)==userID;});
            var result = thisUser.rank + " of " + game.users.length;

            var dateComponents = game.timestamp.split(/[- :]/);
            var gameDate = new Date(Date.UTC(dateComponents[0], dateComponents[1]-1, dateComponents[2], dateComponents[3], dateComponents[4], dateComponents[5]));

            var $row = $("
"+gameDate.toLocaleTimeString()+"	"+playersList+"	"+result+"	"+game.mapWidth+"x"+game.mapHeight+"
");
            if(this.isMe) {
                var me = null;
                for(var a = 0; a < game.users.length; a++) {
                    if(game.users[a].userID == this.userID) {
                        me = game.users[a];
                        break;
                    }
                }
                if(me.errorLogName != undefined && me.errorLogName != null) $row.append("	");
                else $row.append("NA	");
            }
            return $row;
        },
        loadMore: function() {
            this.games = this.games.concat(this.getNextGames(this.userID, this.games[this.games.length-1].gameID));
            this.render();
        }
    }

    var userIDGET = getGET("userID");
    if(userIDGET != null || (session = getSession())) {
        var isMe = (userIDGET == null || userIDGET == undefined);
        var user = isMe ? getUser(session['userID']) : getUser(userIDGET);
        user["username"] = escapeHtml(filterXSS(user["username"]));
        user["language"] = escapeHtml(filterXSS(user["language"]));

        if(user['isRunning'] == 0) {
            $("#normalBody").css("display", "none");
            $("#noBotMessage").css("display", "block");
        } else {
            $(document).prop('title', user["username"]);

            profileCard.init(user);
            gameTable.init(parseInt(user["userID"]), isMe, function(userID, startingID) {
                var rawGames = getLatestGamesForUser(userID, 5, startingID);
                var games = [];
                for(var a = 0; a < rawGames.length; a++) {
                    games.push(rawGames[a]);
                }
                return games;
            });
            historyTable.init(user.username, getHistories(user["userID"]));

            if(isMe) {
                notifsTable.init(getNotifications(user["userID"]));
                notifsTable.show();
            }
        }
    } else {
        $("#normalBody").css("display", "none");
        $("#loginMessage").css("display", "block");
    }
})
<commit_msg>Remove unnecessary level logic<commit_after>$(function() {
    var profileCard = {
        $profileImage: $("#profileImage"),
        $name: $("#name"),
        $primaryInfo: $("#primaryInfo"), $secondaryInfo: $("#secondaryInfo"), init: function(user) {
            this.user = user;

            this.render();
        },
        render: function() {
            var mu = Math.round(this.user.mu*100)/100;
            var sigma = Math.round(this.user.sigma*100)/100;
            var score = Math.round((this.user.mu-(3*this.user.sigma))*100)/100;

            var vr = "|";
            this.$profileImage.attr("src", "https://avatars.githubusercontent.com/u/"+this.user["oauthID"]);
            this.$name.html("" + this.user['username'] + "");

            this.$primaryInfo.append("Rank " + this.user['rank']+"");
            this.$primaryInfo.append("
");
            this.$primaryInfo.append("" + this.user['tier'] + " Tier");
            this.$primaryInfo.append("
");
            this.$primaryInfo.append(""+ score +" points");

            this.$secondaryInfo.append($("Made in "+this.user['language']+ ""));
            this.$secondaryInfo.append($("
"));
            this.$secondaryInfo.append($("At "+this.user['level']+ " level"));
            this.$secondaryInfo.append($("
"));
            if(this.user['organization'] != 'Other') {
                this.$secondaryInfo.append($("Member of "+this.user['organization']+ ""));
                this.$secondaryInfo.append($("
"));
            }
            this.$secondaryInfo.append($(""+this.user['numSubmissions']+" "+(parseInt(this.user['numSubmissions']) == 1 ? "bot" : "bots")+" submitted"));
            this.$secondaryInfo.append($("
"));
            this.$secondaryInfo.append($(""+this.user['numGames']+" games played"));
        }
    };

    var historyTable = {
        $panel: $("#historyPanel"),
        $tableBody: $("#historyTableBody"),
        init: function(name, histories) {
            this.name = name;
            this.histories = histories;
            this.render();
        },
        render: function() {
            if(this.histories.length == 0) {
                this.$panel.css("display", "none");
            } else {
                this.$panel.css("display", "block");
                this.$tableBody.empty();
                for(var a = 0; a < Math.min(5, this.histories.length); a++) {
                    this.$tableBody.append(this.getTableRow(this.histories[a]));
                }
            }
        },
        getTableRow: function(history) {
            return "
"+this.name+" v"+history.versionNumber+"	"+history.lastRank+" of "+history.lastNumPlayers+"	"+(history.lastNumGames != null ? history.lastNumGames : "Not Recorded")+"
";
        }
    }

    var notifsTable = {
        $panel: $("#notifsPanel"),
        $tableBody: $("#notifsTableBody"),
        modal: {
            $modal: $("#notifModal"),
            $title: $("#notifModalHeader"),
            $body: $("#notifModalBody"),
            init: function(title, body) {
                this.title = title;
                this.body = body;

                this.render();
            },
            render: function() {
                this.$title.html("
"+this.title+"
");
                this.$body.html(this.body);
            },
            show: function() {
                this.$modal.modal("show");
            }
        },
        show: function() {
            this.$panel.attr("display", "block");
        },
        init: function(notifs) {
            this.name = name;
            this.notifs = notifs;
            this.render();
        },
        render: function() {
            if(this.notifs.length == 0) {
                this.$panel.css("display", "none");
            } else {
                this.$panel.css("display", "block");
                this.$tableBody.empty();
                for(var a = 0; a < Math.min(10, this.notifs.length); a++) {
                    this.$tableBody.append(this.getTableRow(this.notifs[a]));
                }
            }
        },
        getTableRow: function(notif) {
            var titleColor = null;
            if(parseInt(notif.mood) == -1) titleColor = "#d9534f";
            if(parseInt(notif.mood) == 0) titleColor = "#5bc0de";
            if(parseInt(notif.mood) == 1) titleColor = "#5cb85c";

            $title = $(""+notif.title+"	");
            $title.css("color", titleColor);

            var dateComponents = notif.creationTime.split(/[- :]/);
            var gameDate = new Date(Date.UTC(dateComponents[0], dateComponents[1]-1, dateComponents[2], dateComponents[3], dateComponents[4], dateComponents[5]));
            var dateString = gameDate.toLocaleString();

            $row = $("

");
            $row.append($title);
            $row.append(""+dateString+"	");
            $row.css("cursor", "pointer");
            $row.click(this, this.notifClicked.bind(this, notif));

            return $row;
        },
        notifClicked: function(notif) {
            console.log(this);
            this.modal.init(notif.title, notif.body);
            this.modal.show();
        }
    }

    var statTable = {
        $tableBody: $("#statTableBody"),
        stats: [],
        init: function(stats) {
            this.stats = stats;
            this.render();
        },
        render: function() {
            this.$tableBody.empty();
            for(var a = 0; a < this.stats.length; a++) {
                this.$tableBody.append(this.getTableRow(this.stats[a]));
            }
        },
        getTableRow: function(stat) {
            if(stat.mouseOverText != undefined && stat.mouseOverText != null) {
                return "
"+stat.name+"	"+stat.value+"
";
            } else {
                return "
"+stat.name+"	"+stat.value+"
";
            }
        }
    }

    var gameTable = {
        $alternateMessage: $("#noGameMessage"),
        $panel: $("#gamePanel"),
        $table: $("#gameTable"),
        $tableHeader: $("#gameTableHeader"),
        $tableBody: $("#gameTableBody"),
        $loadButton: $("#loadButton"),
        games: [],
        init: function(userID, isMe, getNextGames) {
            this.userID = userID;
            this.getNextGames = getNextGames;
            this.isMe = isMe;

            this.games = getNextGames(this.userID);
            for(var a = 0; a < this.games.length; a++) this.games[a].users.sort(function(p1, p2) { return parseInt(p1.rank) - parseInt(p2.rank); });

            this.$loadButton.click(this, this.loadMore.bind(this));

            this.render();
        },
        render: function() {
            if(this.games.length == 0) {
                this.$alternateMessage.css("display", "block");
                this.$panel.css("display", "none");
            } else {
                this.$tableHeader.html("Time	Opponents	Result	Dimensions	View	");
                if(this.isMe) this.$tableHeader.append("Error Log	");
                this.$tableBody.empty();
                for(var a = 0; a < this.games.length; a++) {
                    this.$tableBody.append(this.getTableRow(this.games[a]));
                }
            }
        },
        getTableRow: function(game) {
            var userID = this.userID;

            playersList = game.users.filter(function(player) {
                return player.userID != userID;
            }).map(function(player) {
                return "";
            }).join(" ");

            var thisUser = game.users.find(function(p){return parseInt(p.userID)==userID;});
            var result = thisUser.rank + " of " + game.users.length;

            var dateComponents = game.timestamp.split(/[- :]/);
            var gameDate = new Date(Date.UTC(dateComponents[0], dateComponents[1]-1, dateComponents[2], dateComponents[3], dateComponents[4], dateComponents[5]));

            var $row = $("
"+gameDate.toLocaleTimeString()+"	"+playersList+"	"+result+"	"+game.mapWidth+"x"+game.mapHeight+"
");
            if(this.isMe) {
                var me = null;
                for(var a = 0; a < game.users.length; a++) {
                    if(game.users[a].userID == this.userID) {
                        me = game.users[a];
                        break;
                    }
                }
                if(me.errorLogName != undefined && me.errorLogName != null) $row.append("	");
                else $row.append("NA	");
            }
            return $row;
        },
        loadMore: function() {
            this.games = this.games.concat(this.getNextGames(this.userID, this.games[this.games.length-1].gameID));
            this.render();
        }
    }

    var userIDGET = getGET("userID");
    if(userIDGET != null || (session = getSession())) {
        var isMe = (userIDGET == null || userIDGET == undefined);
        var user = isMe ? getUser(session['userID']) : getUser(userIDGET);
        user["username"] = escapeHtml(filterXSS(user["username"]));
        user["language"] = escapeHtml(filterXSS(user["language"]));

        if(user['isRunning'] == 0) {
            $("#normalBody").css("display", "none");
            $("#noBotMessage").css("display", "block");
        } else {
            $(document).prop('title', user["username"]);

            profileCard.init(user);
            gameTable.init(parseInt(user["userID"]), isMe, function(userID, startingID) {
                var rawGames = getLatestGamesForUser(userID, 5, startingID);
                var games = [];
                for(var a = 0; a < rawGames.length; a++) {
                    games.push(rawGames[a]);
                }
                return games;
            });
            historyTable.init(user.username, getHistories(user["userID"]));

            if(isMe) {
                notifsTable.init(getNotifications(user["userID"]));
                notifsTable.show();
            }
        }
    } else {
        $("#normalBody").css("display", "none");
        $("#loginMessage").css("display", "block");
    }
})
<|endoftext|>


<commit_before>/* global describe, it, before, beforeEach */
const {expect, spy} = require('chai')
  .use(require('chai-dom'))
  .use(require('chai-style'))
  .use(require('chai-spies'))

let sidenav
let component

describe('mn-sidenav (webcomponent)', () => {
  before(loadComponent)
  beforeEach(cleanView)
  beforeEach(createComponent)
  beforeEach(setPageObject)

  describe('instance', () => {
    it('should work with a constructor', () => {
      const MnSidenav = window.customElements.get('mn-sidenav')
      component = new MnSidenav()
      expect(component).to.be.instanceof(MnSidenav)
    })

    it('should work with document.createElement()', () => {
      const MnSidenav = window.customElements.get('mn-sidenav')
      component = document.createElement('mn-sidenav')
      expect(component).to.be.instanceof(MnSidenav)
    })
  })

  describe('component', () => {
    it('should have the .mn-sidenav class', () => {
      expect(component).to.have.class('mn-sidenav')
    })

    it('should have the .mn-card class', () => {
      expect(component).to.have.class('mn-card')
    })

    it('should have a backdrop into body', () => {
      expect(document.body).to.have.class('mn-backdrop')
    })
  })

  describe('css style', () => {
    it('should have a width', () => {
      expect(component).to.have.style('width', '320px')
    })

    it('should have a height', () => {
      expect(component).to.have.style('height', '100vh')
    })

    it('should have a position', () => {
      expect(component).to.have.style('position', 'fixed')
    })

    it('should have a top', () => {
      expect(component).to.have.style('top', '0')
    })

    it.skip('should have a z-index', () => {
      expect(component).to.have.style('z-index', '20')
    })

    it.skip('should have a transform', () => {
      expect(component).to.have.style('transform', 'translate3d(-100%, 0, 0)')
    })

    it('should have a will-change', () => {
      expect(component).to.have.style('will-change', 'transform')
    })

    it('should have a transition', () => {
      expect(component).to.have.style('transition', 'transform .32s ease')
    })

    it('should have a border-radius', () => {
      expect(component).to.have.style('border-radius', '0')
    })

    it('should have a overflow', () => {
      expect(component).to.have.style('overflow', 'auto')
    })
  })

  describe('method open', () => {
    it('should display sidenav', () => {
      component.open()
      expect(component).to.have.class('visible')
      expect(document.body).to.have.class('mn-sidenav-visible')
    })

    it('should display backdrop', () => {
      component.open()
      expect(document.body).to.have.class('mn-backdrop-visible')
    })

    it('should be called on click in button with open-sidenav attribute', () => {
      const button = document.createElement('button')
      button.setAttribute('open-sidenav', component.id)
      document.body.appendChild(button)

      const open = spy.on(component, 'open')
      button.click()

      expect(open).to.have.been.called()
    })

    it('should scroll to top', () => {
      component.scroll(300)
      console.log()
      component.open()
      expect(component.scrollTop).to.equal(0)
    })

    it('should scroll to element with class .active', () => {
      const active = component.querySelector('div:nth-child(3)')
      active.classList.add('active')
      component.scrollTop = 300
      component.open()

      expect(component.scrollTop).to.equal(12)
    })
  })

  describe('method close', () => {
    it('should hide sidenav', () => {
      component.open()
      component.close()
      expect(component).to.not.have.class('visible')
      expect(document.body).to.not.have.class('mn-sidenav-visible')
    })

    it('should hide backdrop', () => {
      component.open()
      component.close()
      expect(document.body).to.not.have.class('mn-backdrop-visible')
    })

    it('should be called on click in button with close-sidenav attribute', () => {
      const button = document.createElement('button')
      button.setAttribute('close-sidenav', '')
      document.body.appendChild(button)

      const close = spy.on(component, 'close')
      component.open()
      button.click()

      expect(close).to.have.been.called()
    })

    it('should be called when press ESC', () => {
      const close = spy.on(component, 'close')
      component.open()
      const esc = new Event('keyup')
      esc.key = 'Escape'
      document.dispatchEvent(esc)

      expect(close).to.have.been.called()
    })
  })

  describe('method toggle', () => {
    it('should display sidenav if not visible', () => {
      const open = spy.on(component, 'open')
      component.toggle()
      expect(open).to.have.been.called()
    })

    it('should hide sidenav if visible', () => {
      const close = spy.on(component, 'close')
      component.open()
      component.toggle()
      expect(close).to.have.been.called()
    })

    it('should be called on click in button with toggle-sidenav attribute', () => {
      const button = document.createElement('button')
      button.setAttribute('toggle-sidenav', component.id)
      document.body.appendChild(button)

      const toggle = spy.on(component, 'toggle')
      button.click()
      button.click()

      expect(toggle).to.have.been.called.twice
    })
  })
})

function loadComponent() {
  // require('minimalist').sidenav
}

function cleanView() {
  const sidenav = document.querySelector('sidenav')

  if (sidenav) {
    sidenav.parentNode.removeChild(sidenav)
  }
}

function createComponent() {
  component = document.createElement('mn-sidenav')
  component.id = 'menu'
  document.body.appendChild(component)

  Array(100)
    .fill(document.createElement('div'))
    .forEach(div => {
      div.textContent = 'teste'
      component.appendChild(div)
  })
}

function setPageObject() {
  const SidenavPageObject = require('./sidenav.po.js')
  sidenav = new SidenavPageObject(component)
}
<commit_msg>fix loop to create divs inside sidenav to test, and remove cleanView from sidenav.spec<commit_after>/* global describe, it, before, beforeEach */
const {expect, spy} = require('chai')
  .use(require('chai-dom'))
  .use(require('chai-style'))
  .use(require('chai-spies'))

let sidenav
let component

describe('mn-sidenav (webcomponent)', () => {
  before(loadComponent)
  beforeEach(createComponent)
  beforeEach(setPageObject)

  describe('instance', () => {
    it('should work with a constructor', () => {
      const MnSidenav = window.customElements.get('mn-sidenav')
      component = new MnSidenav()
      expect(component).to.be.instanceof(MnSidenav)
    })

    it('should work with document.createElement()', () => {
      const MnSidenav = window.customElements.get('mn-sidenav')
      component = document.createElement('mn-sidenav')
      expect(component).to.be.instanceof(MnSidenav)
    })
  })

  describe('component', () => {
    it('should have the .mn-sidenav class', () => {
      expect(component).to.have.class('mn-sidenav')
    })

    it('should have the .mn-card class', () => {
      expect(component).to.have.class('mn-card')
    })

    it('should have a backdrop into body', () => {
      expect(document.body).to.have.class('mn-backdrop')
    })
  })

  describe('css style', () => {
    it('should have a width', () => {
      expect(component).to.have.style('width', '320px')
    })

    it('should have a height', () => {
      expect(component).to.have.style('height', '100vh')
    })

    it('should have a position', () => {
      expect(component).to.have.style('position', 'fixed')
    })

    it('should have a top', () => {
      expect(component).to.have.style('top', '0')
    })

    it.skip('should have a z-index', () => {
      expect(component).to.have.style('z-index', '20')
    })

    it.skip('should have a transform', () => {
      expect(component).to.have.style('transform', 'translate3d(-100%, 0, 0)')
    })

    it('should have a will-change', () => {
      expect(component).to.have.style('will-change', 'transform')
    })

    it('should have a transition', () => {
      expect(component).to.have.style('transition', 'transform .32s ease')
    })

    it('should have a border-radius', () => {
      expect(component).to.have.style('border-radius', '0')
    })

    it('should have a overflow', () => {
      expect(component).to.have.style('overflow', 'auto')
    })
  })

  describe('method open', () => {
    it('should display sidenav', () => {
      component.open()
      expect(component).to.have.class('visible')
      expect(document.body).to.have.class('mn-sidenav-visible')
    })

    it('should display backdrop', () => {
      component.open()
      expect(document.body).to.have.class('mn-backdrop-visible')
    })

    it('should be called on click in button with open-sidenav attribute', () => {
      const button = document.createElement('button')
      button.setAttribute('open-sidenav', component.id)
      document.body.appendChild(button)

      const open = spy.on(component, 'open')
      button.click()

      expect(open).to.have.been.called()
    })

    it('should scroll to top', () => {
      sidenav.scroll(300)
      component.open()
      expect(component.scrollTop).to.equal(0)
    })

    it('should scroll to element with class .active', () => {
      const active = component.querySelector('div:nth-child(3)')
      active.classList.add('active')
      component.scrollTop = 300
      component.open()

      expect(component.scrollTop).to.equal(12)
    })
  })

  describe('method close', () => {
    it('should hide sidenav', () => {
      component.open()
      component.close()
      expect(component).to.not.have.class('visible')
      expect(document.body).to.not.have.class('mn-sidenav-visible')
    })

    it('should hide backdrop', () => {
      component.open()
      component.close()
      expect(document.body).to.not.have.class('mn-backdrop-visible')
    })

    it('should be called on click in button with close-sidenav attribute', () => {
      const button = document.createElement('button')
      button.setAttribute('close-sidenav', '')
      document.body.appendChild(button)

      const close = spy.on(component, 'close')
      component.open()
      button.click()

      expect(close).to.have.been.called()
    })

    it('should be called when press ESC', () => {
      const close = spy.on(component, 'close')
      component.open()
      const esc = new Event('keyup')
      esc.key = 'Escape'
      document.dispatchEvent(esc)

      expect(close).to.have.been.called()
    })
  })

  describe('method toggle', () => {
    it('should display sidenav if not visible', () => {
      const open = spy.on(component, 'open')
      component.toggle()
      expect(open).to.have.been.called()
    })

    it('should hide sidenav if visible', () => {
      const close = spy.on(component, 'close')
      component.open()
      component.toggle()
      expect(close).to.have.been.called()
    })

    it('should be called on click in button with toggle-sidenav attribute', () => {
      const button = document.createElement('button')
      button.setAttribute('toggle-sidenav', component.id)
      document.body.appendChild(button)

      const toggle = spy.on(component, 'toggle')
      button.click()
      button.click()

      expect(toggle).to.have.been.called.twice
    })
  })
})

function loadComponent() {
  // require('minimalist').sidenav
}

function createComponent() {
  component = document.createElement('mn-sidenav')
  component.id = 'menu'

  Array(100)
    .fill(null)
    .forEach(() => {
      const div = document.createElement('div')
      div.textContent = 'test'
      component.appendChild(div)
    })

  document.body.appendChild(component)
}

function setPageObject() {
  const SidenavPageObject = require('./sidenav.po.js')
  sidenav = new SidenavPageObject(component)
}
<|endoftext|>


<commit_before>var dataType = require('../sensor_config');
require('../server_functions');
var startSequence = [255, 123, 10];		//Signature of the start of a can frame?

/* Checks three databytes, and returns true if they contain
 * a start sequence.
 * Input: An array containing three bytes
 * OutPut: True if the three bytes matches the start sequence, False if not.
 */
function isStartSequence(data) {

	for(var i = 0; i < data.length; i++) {
		if (data[i] !== startSequence[i]) {
			return false;
		}
	}

	return true;
	//Legacy code, kept tempt uncommented for now. Should be removed when new code has been proven
	/*if((package_start_counter === 0) && (currByte === startSequence[0]))
		package_start_counter = 1;
	else if((package_start_counter === 1) && (currByte === startSequence[1]))
		package_start_counter = 2;
	else if((package_start_counter === 2) && (currByte === startSequence[2]))
	{
		package_start_counter = 0;
		package_start = true;
		continue;
	}*/
}

module.exports = function(data) {
	// Data from serialport
	var datain = data;
	//Output
	var valOut = 0;
	//Size of remaining package bytes
	var bytesToRead = -1;

	//Processes each data byte
	while (datain.length !== 0) {
		currByte = datain[0];

		//Looks for a package  start sequence
		if (isStartSequence(datain.slice(0, startSequence.length))) {
			//removes the start sequence bytes
			datain = datain.slice(startSequence.length);
			currByte = datain[0];
			//Gets the packagetype
			var packageTypeKey = getDataType(dataType, currByte);

		}
		else {
			datain.Shift();
		}
	}

	//Loops through each byte in data stream
	for (var i=0; i < datain.length; i++) {
		var currByte = datain[i]; // the current byte in the stream

		/*Looks for start sequence*/
		if (isStartSequence(datain.slice(i, i + startSequence.length))) {
			package_start = true;
			//Start sequence found, move the index past the start sequence bytes
			i += startSequence.length;
			currByte = datain[i];

		}

		//Begin Legacy code /*********************************************************

		////console.log("("+currByte+")");

		// Search data pack. start sequence, if found then next byte is a type
		/*if((package_start_counter === 0) && (currByte === startSequence[0]))
			package_start_counter = 1;
		else if((package_start_counter === 1) && (currByte === startSequence[1]))
			package_start_counter = 2;
		else if((package_start_counter === 2) && (currByte === startSequence[2])){
			package_start_counter = 0;
			package_start = true;
			continue;
		}*/

		//End legacy code*************************************************************

		// Packet start found, get packet ID and size
		if (package_start) {

			// Reset
			package_start = false;
			valOut = 0;
			//Get data type key
			dataTypeKey = getDataType(dataType,currByte);

			// Valid data type found
			if(dataTypeKey !== -1){
				bytesToRead = (dataType[dataTypeKey].datalength / 8); //Bytes to read
			}
			else
				console.error("Invalid data (ID: "+currByte+")");
			continue;
		}

		// Read Data bytes
		if (bytesToRead > 0) {
			valOut = valOut + (currByte << (8*(bytesToRead-1)));	//Shift bytes
			bytesToRead -= 1; // Databyte counter
			continue;
		}

		// No more data bytes,
		if (bytesToRead === 0) {

			var name = dataType[dataTypeKey].name;
			var value = dataType[dataTypeKey].conv(valOut);

			//Format value to range
			value = Math.min(value, dataType[dataTypeKey].max);
			value = Math.max(value, dataType[dataTypeKey].min);

			var sensor = {
				name: name,
				val: value,
				timestamp: new Date().getTime()
			};
			sensors[numSensors++] = sensor;

			// Store the bytes
			if (dataType[dataTypeKey].active === 1) {
				// Add to data pack
				dataTx[dataCounter++] = sensor;

			}

			// Reset
			bytesToRead = -1;
			valOut = 0;

			// Next data byte ?
			dataTypeKey = getDataType(dataType,currByte);
			// Valid ?
			if (dataTypeKey !== -1) {
				bytesToRead = (dataType[dataTypeKey].datalength/8);
			}
			// No more data, transmit fetched data to client
			// Store data here and call dataTx
			else {
				// Tx data to all clients
				//console.log("Tx data -------------------------------------------------");
				txData(dataTx);
				dataCounter = 0;
				dataTx = [];

				// flush all collected data to db
				db.sensor.insert(sensors);
				sensors = [];
				numSensors = 0;
			}
		}
	}

};
<commit_msg>Changes to data parser<commit_after>var dataType = require('../sensor_config');
require('../server_functions');
var startSequence = [255, 123, 10];		//Signature of the start of a can frame?

/* Checks three databytes, and returns true if they contain
 * a start sequence.
 * Input: An array containing three bytes
 * OutPut: True if the three bytes matches the start sequence, False if not.
 */
function isStartSequence(data) {

	for(var i = 0; i < data.length; i++) {
		if (data[i] !== startSequence[i]) {
			return false;
		}
	}

	return true;
	//Legacy code, kept tempt uncommented for now. Should be removed when new code has been proven
	/*if((package_start_counter === 0) && (currByte === startSequence[0]))
		package_start_counter = 1;
	else if((package_start_counter === 1) && (currByte === startSequence[1]))
		package_start_counter = 2;
	else if((package_start_counter === 2) && (currByte === startSequence[2]))
	{
		package_start_counter = 0;
		package_start = true;
		continue;
	}*/
}
//Reads the value in a package of multiple bytes
function getValOut(data) {
	var value = 0;
	for (var i = 0; i < data.length; i++) {
		value = value + (data[i] << (8*(k-1)))	//Shift bytes
	};
	return value;
}

module.exports = function(data) {
	// Data from serialport
	var datain = data;
	//Output
	var valOut = 0;
	//Size of remaining package bytes
	var bytesToRead = -1;

	//Processes each data byte
	while (datain.length !== 0) {
		currByte = datain[0];

		//Looks for a package  start sequence
		if (isStartSequence(datain.slice(0, startSequence.length))) {
			//removes the start sequence bytes
			datain = datain.slice(startSequence.length);
			currByte = datain[0];
			//Gets the packagetype
			var packageTypeKey = getDataType(dataType, currByte);

		}
		else {
			datain.Shift();
		}
	}

	//Loops through each byte in data stream
	for (var i=0; i < datain.length; i++) {
		var currByte = datain[i]; // the current byte in the stream

		/*Looks for start sequence*/
		if (isStartSequence(datain.slice(i, i + startSequence.length))) {
			package_start = true;
			//Start sequence found, move the index past the start sequence bytes
			i += startSequence.length;
			currByte = datain[i];

		}

		//Begin Legacy code /*********************************************************

		////console.log("("+currByte+")");

		// Search data pack. start sequence, if found then next byte is a type
		/*if((package_start_counter === 0) && (currByte === startSequence[0]))
			package_start_counter = 1;
		else if((package_start_counter === 1) && (currByte === startSequence[1]))
			package_start_counter = 2;
		else if((package_start_counter === 2) && (currByte === startSequence[2])){
			package_start_counter = 0;
			package_start = true;
			continue;
		}*/

		//End legacy code*************************************************************

		// Packet start found, get packet ID and size
		if (package_start) {

			// Reset
			package_start = false;
			valOut = 0;
			//Get data type key
			dataTypeKey = getDataType(dataType,currByte);

			// Valid data type found
			if(dataTypeKey !== -1){
				bytesToRead = (dataType[dataTypeKey].datalength / 8); //Bytes to read
			}
			else {
				console.error("Invalid data (ID: "+currByte+")");
			}

			//Finds the value of the package
			valOut = getValOut(datain.slice(i, i + bytesToRead);

			//No more data in the current package
			var name = dataType[dataTypeKey].name;
			var value = dataType[dataTypeKey].conv(valOut);

			//Format value to range
			value = Math.min(value, dataType[dataTypeKey].max);
			value = Math.max(value, dataType[dataTypeKey].min);

			var sensor = {
				name: name,
				val: value,
				timestamp: new Date().getTime()
			};
			sensors[numSensors++] = sensor;

			// Store the bytes
			if (dataType[dataTypeKey].active === 1) {
				// Add to data pack
				dataTx[dataCounter++] = sensor;

			}

			//Update CurrByte
			i+= bytesToRead;
			currByte = datain[i];

			// Reset
			bytesToRead = -1;
			valOut = 0;

			// Next data byte ?
			dataTypeKey = getDataType(dataType,currByte);
			// Valid ?
			if (dataTypeKey !== -1) {
				bytesToRead = (dataType[dataTypeKey].datalength/8);
			}
			// No more data, transmit fetched data to client
			// Store data here and call dataTx
			else {
				// Tx data to all clients
				//console.log("Tx data -------------------------------------------------");
				txData(dataTx);
				dataCounter = 0;
				dataTx = [];

				// flush all collected data to db
				db.sensor.insert(sensors);
				sensors = [];
				numSensors = 0;
			}
		}

		/* Legacy
		// Read Data bytes
		if (bytesToRead > 0) {
			valOut = valOut + (currByte << (8*(bytesToRead-1)));	//Shift bytes
			bytesToRead -= 1; // Databyte counter
			continue;
		} */

		// No more data bytes,
	}
};<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');
var chai = require('chai');

/* jshint unused: false */
var should = chai.should();
var sinon = require('sinon');
var _ = require('lodash');

var bitcore = require('bitcore');
var Random = bitcore.crypto.Random;
var BN = bitcore.crypto.BN;
var BufferUtil = bitcore.util.buffer;
var p2p = require('../');
var Peer = p2p.Peer;
var Pool = p2p.Pool;
var Networks = bitcore.Networks;
var Messages = p2p.Messages;
var Block = bitcore.Block;
var Transaction = bitcore.Transaction;

// config
var network = Networks.livenet;
var blockHash = {
  'livenet': '000000000000000013413cf2536b491bf0988f52e90c476ffeb701c8bfdb1db9',
  'testnet': '0000000058cc069d964711cd25083c0a709f4df2b34c8ff9302ce71fe5b45786'
};
var txHash = {
  'livenet': '22231e8219a0617a0ded618b5dc713fdf9b0db8ebd5bb3322d3011a703119d3b',
  'testnet': '22231e8219a0617a0ded618b5dc713fdf9b0db8ebd5bb3322d3011a703119d3b'
};

// These tests require a running bitcoind instance
describe('Integration with ' + network.name + ' bitcoind', function() {

  this.timeout(5000);
  it('handshakes', function(cb) {
    var peer = new Peer('localhost', network);
    peer.once('version', function(m) {
      m.version.should.be.above(70000);
      m.services.toString().should.equal('1');
      Math.abs(new Date() - m.timestamp).should.be.below(10000); // less than 10 seconds of time difference
      m.nonce.length.should.equal(8);
      m.start_height.should.be.above(300000);
      cb();
    });
    peer.once('verack', function(m) {
      should.exist(m);
      m.command.should.equal('verack');
    });
    peer.connect();
  });
  var connect = function(cb) {
    var peer = new Peer('localhost', network);
    peer.once('ready', function() {
      cb(peer);
    });
    peer.once('error', function(err) {
      should.not.exist(err);
    });
    peer.connect();
  };
  it('connects', function(cb) {
    connect(function(peer) {
      peer.version.should.be.above(70000);
      _.isString(peer.subversion).should.equal(true);
      _.isNumber(peer.bestHeight).should.equal(true);
      cb();
    });
  });
  it('handles inv', function(cb) {
    // assumes there will be at least one transaction/block
    // in the next few seconds
    connect(function(peer) {
      peer.once('inv', function(message) {
        message.inventory[0].hash.length.should.equal(32);
        cb();
      });
    });
  });
  it('handles addr', function(cb) {
    connect(function(peer) {
      peer.once('addr', function(message) {
        message.addresses.forEach(function(address) {
          // console.log(address.ip.v4 + ':' + address.port);
          (address.time instanceof Date).should.equal(true);
          should.exist(address.ip);
          (address.services instanceof BN).should.equal(true);
        });
        cb();
      });
      var message = new Messages.GetAddresses();
      peer.sendMessage(message);
    });
  });
  it('can request inv detailed info', function(cb) {
    connect(function(peer) {
      peer.once('block', function(message) {
        //console.log(message.block.toJSON());
        should.exist(message.block);
        cb();
      });
      peer.once('tx', function(message) {
        //console.log(message.transaction.toJSON());
        should.exist(message.transaction);
        cb();
      });
      peer.once('inv', function(m) {
        var message = new Messages.GetData(m.inventory);
        peer.sendMessage(message);
      });
    });
  });
  it('can send tx inv and receive getdata for that tx', function(cb) {
    connect(function(peer) {
      var type = Messages.Inventory.TYPE.TX;
      var inv = [{
        type: type,
        typeName: Messages.Inventory.TYPE_NAME[type],
        hash: Random.getRandomBuffer(32) // needs to be random for repeatability
      }];
      peer.once('getdata', function(message) {
        message.inventory.should.deep.equal(inv);
        cb();
      });
      var message = new Messages.Inventory(inv);
      message.inventory[0].hash.length.should.equal(32);
      peer.sendMessage(message);
    });
  });
  it('can request block data', function(cb) {
    connect(function(peer) {
      peer.once('block', function(message) {
        (message.block instanceof Block).should.equal(true);
        cb();
      });
      var message = Messages.GetData.forBlock(blockHash[network.name]);
      peer.sendMessage(message);
    });
  });
  it('can handle request tx data not found', function(cb) {
    connect(function(peer) {
      var hash = 'e2dfb8afe1575bfacae1a0b4afc49af7ddda69285857267bae0e22be15f74a3a';
      var expected = Messages.NotFound.forTransaction(hash);
      peer.once('notfound', function(message) {
        (message instanceof Messages.NotFound).should.equal(true);
        message.should.deep.equal(expected);
        cb();
      });
      var message = Messages.GetData.forTransaction(hash);
      peer.sendMessage(message);
    });
  });
});
<commit_msg>Increased the timeout time for integration tests - A busy local peer may need more time to respond - Since the tests are run optionally allowing for more time shouldn't be problematic<commit_after>'use strict';

var _ = require('lodash');
var chai = require('chai');

/* jshint unused: false */
var should = chai.should();
var sinon = require('sinon');
var _ = require('lodash');

var bitcore = require('bitcore');
var Random = bitcore.crypto.Random;
var BN = bitcore.crypto.BN;
var BufferUtil = bitcore.util.buffer;
var p2p = require('../');
var Peer = p2p.Peer;
var Pool = p2p.Pool;
var Networks = bitcore.Networks;
var Messages = p2p.Messages;
var Block = bitcore.Block;
var Transaction = bitcore.Transaction;

// config
var network = Networks.livenet;
var blockHash = {
  'livenet': '000000000000000013413cf2536b491bf0988f52e90c476ffeb701c8bfdb1db9',
  'testnet': '0000000058cc069d964711cd25083c0a709f4df2b34c8ff9302ce71fe5b45786'
};
var txHash = {
  'livenet': '22231e8219a0617a0ded618b5dc713fdf9b0db8ebd5bb3322d3011a703119d3b',
  'testnet': '22231e8219a0617a0ded618b5dc713fdf9b0db8ebd5bb3322d3011a703119d3b'
};

// These tests require a running bitcoind instance
describe('Integration with ' + network.name + ' bitcoind', function() {

  this.timeout(15000);
  it('handshakes', function(cb) {
    var peer = new Peer('localhost', network);
    peer.once('version', function(m) {
      m.version.should.be.above(70000);
      m.services.toString().should.equal('1');
      Math.abs(new Date() - m.timestamp).should.be.below(10000); // less than 10 seconds of time difference
      m.nonce.length.should.equal(8);
      m.start_height.should.be.above(300000);
      cb();
    });
    peer.once('verack', function(m) {
      should.exist(m);
      m.command.should.equal('verack');
    });
    peer.connect();
  });
  var connect = function(cb) {
    var peer = new Peer('localhost', network);
    peer.once('ready', function() {
      cb(peer);
    });
    peer.once('error', function(err) {
      should.not.exist(err);
    });
    peer.connect();
  };
  it('connects', function(cb) {
    connect(function(peer) {
      peer.version.should.be.above(70000);
      _.isString(peer.subversion).should.equal(true);
      _.isNumber(peer.bestHeight).should.equal(true);
      cb();
    });
  });
  it('handles inv', function(cb) {
    // assumes there will be at least one transaction/block
    // in the next few seconds
    connect(function(peer) {
      peer.once('inv', function(message) {
        message.inventory[0].hash.length.should.equal(32);
        cb();
      });
    });
  });
  it('handles addr', function(cb) {
    connect(function(peer) {
      peer.once('addr', function(message) {
        message.addresses.forEach(function(address) {
          // console.log(address.ip.v4 + ':' + address.port);
          (address.time instanceof Date).should.equal(true);
          should.exist(address.ip);
          (address.services instanceof BN).should.equal(true);
        });
        cb();
      });
      var message = new Messages.GetAddresses();
      peer.sendMessage(message);
    });
  });
  it('can request inv detailed info', function(cb) {
    connect(function(peer) {
      peer.once('block', function(message) {
        //console.log(message.block.toJSON());
        should.exist(message.block);
        cb();
      });
      peer.once('tx', function(message) {
        //console.log(message.transaction.toJSON());
        should.exist(message.transaction);
        cb();
      });
      peer.once('inv', function(m) {
        var message = new Messages.GetData(m.inventory);
        peer.sendMessage(message);
      });
    });
  });
  it('can send tx inv and receive getdata for that tx', function(cb) {
    connect(function(peer) {
      var type = Messages.Inventory.TYPE.TX;
      var inv = [{
        type: type,
        typeName: Messages.Inventory.TYPE_NAME[type],
        hash: Random.getRandomBuffer(32) // needs to be random for repeatability
      }];
      peer.once('getdata', function(message) {
        message.inventory.should.deep.equal(inv);
        cb();
      });
      var message = new Messages.Inventory(inv);
      message.inventory[0].hash.length.should.equal(32);
      peer.sendMessage(message);
    });
  });
  it('can request block data', function(cb) {
    connect(function(peer) {
      peer.once('block', function(message) {
        (message.block instanceof Block).should.equal(true);
        cb();
      });
      var message = Messages.GetData.forBlock(blockHash[network.name]);
      peer.sendMessage(message);
    });
  });
  it('can handle request tx data not found', function(cb) {
    connect(function(peer) {
      var hash = 'e2dfb8afe1575bfacae1a0b4afc49af7ddda69285857267bae0e22be15f74a3a';
      var expected = Messages.NotFound.forTransaction(hash);
      peer.once('notfound', function(message) {
        (message instanceof Messages.NotFound).should.equal(true);
        message.should.deep.equal(expected);
        cb();
      });
      var message = Messages.GetData.forTransaction(hash);
      peer.sendMessage(message);
    });
  });
});
<|endoftext|>