import * as React from "react";
import {BuildSnapshot} from "./BuildSnapshot";

/**
 * Container Component for BuildSnapshots.
 *
 * @param {*} props input property containing an array of build data to be
 * rendered through BuildSnapshot.
 */
export const BuildSnapshotContainer = React.memo((props) =>
  {
    // Number of entries to be pulled.
    // TODO: Determine true value for number of entries.
    const NUMBER = 2;

    // The starting point from which the entries are selected.
    // Relative to the starting index.
    // TODO: vary multiplier based on page number.
    const OFFSET = NUMBER * 0;

    const SOURCE = `/builders/number=${NUMBER}/offset=${OFFSET}`;

    const [data, setData] = React.useState([]);

    /**
     * Fetch data when component is mounted.
     * Pass in empty array as second paramater to prevent
     * infinite callbacks as component refreshes.
     * @see Fetching
     */
    React.useEffect(() => {
      fetch(SOURCE)
        .then(res => res.json())
        .then(json => setData(json))
        .catch(setData([]));
    }, []);

    return (

        {data.map(snapshotData => <BuildSnapshot buildData={snapshotData}/>)}

    );
  }
);

Add classname to container instance


(function() {
  // construct Headroom
  var headroom  = new Headroom(document.querySelector(".js-nvbr"), {
    "offset": Options.navbarOffset,
    "tolerance": Options.navbarTolerance
  });

  // initialize Headroom
  headroom.init();
})();

(function() {
  // construct Headroom
  var headroom  = new Headroom(document.querySelector(".js-nvbr"), {
    "offset": Options.navbarOffset,
    "tolerance": Options.navbarTolerance
  });

  // initialize Headroom
  headroom.init();

  // add click events to sidebar nav links
  document.addEventListener("DOMContentLoaded", function() {
    // choose elements to add click event to
    var elements = document.getElementsByClassName("x2b-sdbr-lnk");

    // iterate over elements
    for (var i = 0; elements[i]; ++i) {
      // add click event
      elements[i].onclick = function() {
        // fudge scroll position to make Headroom pin
        headroom.lastKnownScrollY = headroom.getScrollerHeight();
      }
    }
  });
})();

Add click events to sidebar nav links for making Headroom pin


Files = new FS.Collection("files", {
  stores: [
    new FS.Store.GridFS("files", {})
  ]
});

Files.allow({
  "insert": function (userId, doc) {
    return true;
  }
});

Add Files allow insert rule


export const initialFuzzySettings = {
  enableFuzzySearch: true,
  shouldSort: true,
  threshold: 0.5,
  maxPattenLength: 32,
  minMatchCharLength: 1,
  keys: ['title', 'url'],
};

export const initialGeneralSettings = {
  showTabCountBadgeText: true,
  showBookmarks: false,
  showRecentlyClosed: false,
  searchAllWindows: false,
  enableOverlay: false,
};

export const initialFuzzySettings = {
  enableFuzzySearch: true,
  shouldSort: true,
  threshold: 0.5,
  maxPatternLength: 32,
  minMatchCharLength: 1,
  keys: ['title', 'url'],
};

export const initialGeneralSettings = {
  showTabCountBadgeText: true,
  searchAllWindows: false,
  enableOverlay: false,
  showRecentlyClosed: true,
  alwaysShowRecentlyClosedAtTheBottom: true,
  recentlyClosedLimit: 5,
};

Add showRecentlyClosedAtBottom key and fix typo


// @flow

import {
  homePage,
  helloPage,
  helloAsyncPage,
  helloEndpoint,
} from './controller'
import {
  HOME_PAGE_ROUTE,
  HELLO_PAGE_ROUTE,
  HELLO_ASYNC_PAGE_ROUTE,
  helloEndpointRoute,
} from '../shared/routes'
import renderApp from './render-app'

export default (app: Object) => {
  app.get(HOME_PAGE_ROUTE, (req, res) => {
    res.send(renderApp(req.url, homePage()))
  })

  app.get(HELLO_PAGE_ROUTE, (req, res) => {
    res.send(renderApp(req.url, helloPage()))
  })

  app.get(HELLO_ASYNC_PAGE_ROUTE, (req, res) => {
    res.send(renderApp(req.url, helloAsyncPage()))
  })

  app.get(helloEndpointRoute(), (req, res) => {
    res.json(helloEndpoint(req.params.num))
  })

  app.get('*', (req, res) => {
    res.status(404).send(renderApp(req.url))
  })

  /* eslint-disable no-unused-vars, no-console */
  app.use((err, req, res, next) => {
    console.error(err.stack)
    res.status(500).send('Something went wrong!')
  })
  /* eslint-disable no-unused-vars, no-console */
}

// @flow

import {
  homePage,
  helloPage,
  helloAsyncPage,
  helloEndpoint,
} from './controller'
import {
  HOME_PAGE_ROUTE,
  HELLO_PAGE_ROUTE,
  HELLO_ASYNC_PAGE_ROUTE,
  helloEndpointRoute,
} from '../shared/routes'
import renderApp from './render-app'

export default (app: Object) => {
  app.get(HOME_PAGE_ROUTE, (req, res) => {
    res.send(renderApp(req.url, homePage()))
  })

  app.get(HELLO_PAGE_ROUTE, (req, res) => {
    res.send(renderApp(req.url, helloPage()))
  })

  app.get(HELLO_ASYNC_PAGE_ROUTE, (req, res) => {
    res.send(renderApp(req.url, helloAsyncPage()))
  })

  app.get(helloEndpointRoute(), (req, res) => {
    res.json(helloEndpoint(req.params.num))
  })

  app.get('/500', () => {
    throw Error('Fake Internal Server Error')
  })

  app.get('*', (req, res) => {
    res.status(404).send(renderApp(req.url))
  })

  /* eslint-disable no-unused-vars, no-console */
  app.use((err, req, res, next) => {
    console.error(err.stack)
    res.status(500).send('Something went wrong!')
  })
  /* eslint-disable no-unused-vars, no-console */
}

Add Fake Internal Server Error


const gulp = require('gulp');

gulp.task('watch', ['watch:sass', 'watch:scripts', 'watch:app', 'watch:components']);

gulp.task('watch:sass', function() {
    return gulp.watch('timestrap/static_src/sass/**/*.scss', ['styles:sass']);
});

gulp.task('watch:scripts', function() {
    return gulp.watch('timestrap/static_src/scripts/**/*.js', ['scripts:vendor']);
});

gulp.task('watch:app', function() {
    return gulp.watch('timestrap/static_src/app.js', ['scripts:app']);
});

gulp.task('watch:components', function() {
    return gulp.watch('timestrap/static_src/components/**/*.vue', ['scripts:app']);
});

const gulp = require('gulp');

gulp.task('watch', ['watch:sass', 'watch:scripts', 'watch:app', 'watch:components', 'watch:plugins']);

gulp.task('watch:sass', function() {
    return gulp.watch('timestrap/static_src/sass/**/*.scss', ['styles:sass']);
});

gulp.task('watch:scripts', function() {
    return gulp.watch('timestrap/static_src/scripts/**/*.js', ['scripts:vendor']);
});

gulp.task('watch:app', function() {
    return gulp.watch('timestrap/static_src/app.js', ['scripts:app']);
});

gulp.task('watch:components', function() {
    return gulp.watch('timestrap/static_src/components/**/*.vue', ['scripts:app']);
});

gulp.task('watch:plugins', function() {
    return gulp.watch('timestrap/static_src/plugins/**/*.js', ['scripts:app']);
});

Watch for changes to plugin files.


import Property from "./Property";
import properties from "./properties";
import Component from "./Component";
import components from "./components";

export default class ICS {
  static Property = Property;
  static properties = properties;

  static Component = Component;
  static components = components;

  static MIME_TYPE = "text/calendar";

  static toBlob(string) {
    return new Blob([string], { type: ICS.MIME_TYPE });
  }

  static toBase64(string, callback) {
    const blob = this.toBlob(string);
    const reader = new window.FileReader();

    return Promise((resolve, reject) => {
      reader.readAsDataURL(blob);
      reader.onloadend = () => resolve(reader.result);
      reader.onerror = () => reject(reader.error);
      reader.onabort = () => reject();
    });
  }
}

import Property from "./Property";
import properties from "./properties";
import Component from "./Component";
import components from "./components";

export default class ICS {
  static Property = Property;
  static properties = properties;

  static Component = Component;
  static components = components;

  static MIME_TYPE = "text/calendar";

  static toBlob(string) {
    return new Blob([string], { type: ICS.MIME_TYPE });
  }

  static toBase64(string, callback) {
    const blob = this.toBlob(string);
    const reader = new window.FileReader();

    return new Promise((resolve, reject) => {
      reader.readAsDataURL(blob);
      reader.onloadend = () => resolve(reader.result);
      reader.onerror = () => reject(reader.error);
      reader.onabort = () => reject();
    });
  }
}

Call new on Promise constructor


var APIError = require('./exceptions').APIError;

var utils = module.exports = {};

utils.subEntity = function(Parent, name, Constructor){
  var underlyingFieldName = '__' + name;
  Object.defineProperty(Parent.prototype, name, {
    enumerable: true,
    get: function(){
      if (!this[underlyingFieldName]){
        this[underlyingFieldName] = new Constructor(this);
      }

      return this[underlyingFieldName];
    }
  });
};

utils.wrapPropertyMethod = function (Parent, name, propertyMethod) {
  var path = propertyMethod.split('.');
  var method = path.pop();
  var property = null;

  Object.defineProperty(Parent.prototype, name, {
    enumerable: false,
    get: function () {
      property = this;

      while(path.length > 0) {
        property = property[path.shift()];
      }

      return property[method].bind(property);
    }
  });
}

utils.responseHandler = function(onError, onSuccess){
  return function(err, resp){
    if (err) {
      var error = err;
      if (err.response && err.response.body){
        var body = err.response.body;
        error = new APIError(body.statusCode, body.error, body.message);
      }

      return onError(error);
    }

    onSuccess(resp);
  };
};

utils.successCallback = function(cb, res){
  return function(x){
    if (cb){
      return cb(null, x);
    } else {
      return res(x);
    }
  };
};

var APIError = require('./exceptions').APIError;

var utils = module.exports = {};

utils.subEntity = function(Parent, name, Constructor){
  var underlyingFieldName = '__' + name;
  Object.defineProperty(Parent.prototype, name, {
    enumerable: true,
    get: function(){
      if (!this[underlyingFieldName]){
        this[underlyingFieldName] = new Constructor(this);
      }

      return this[underlyingFieldName];
    }
  });
};

utils.wrapPropertyMethod = function (Parent, name, propertyMethod) {
  var path = propertyMethod.split('.');
  var property = path.shift();
  var method = path.pop();

  Object.defineProperty(Parent.prototype, name, {
    enumerable: false,
    get: function () {

      return this[property][method].bind(this[property]);
    }
  });
}

utils.responseHandler = function(onError, onSuccess){
  return function(err, resp){
    if (err) {
      var error = err;
      if (err.response && err.response.body){
        var body = err.response.body;
        error = new APIError(body.statusCode, body.error, body.message);
      }

      return onError(error);
    }

    onSuccess(resp);
  };
};

utils.successCallback = function(cb, res){
  return function(x){
    if (cb){
      return cb(null, x);
    } else {
      return res(x);
    }
  };
};

Fix bug in wrapPropertyMethod utility


(function() {
  'use strict';

  let path = require('path');
  let express = require('express');
  let http = require('http');
  let _ = require('lodash');
  let bodyParser = require('body-parser');
  let sseExpress = require('sse-express');

  let app = express();
  let globalId = 1;
  let connections = [];

  app.use(bodyParser.urlencoded({     // to support URL-encoded bodies
    extended: true
  }));
  app.use((req, res, next) => {
    // cors
    res.setHeader('Access-Control-Allow-Origin', '*');
    next();
  });

  app.post('/sendMessage', (req, res) => {
    res.writeHead(200, {
      'Access-Control-Allow-Origin': '*'
    });

    connections.forEach(function(connection) {
      connection.sse('message', {
        text: req.body.message,
        userId: req.body.userId
      });
    });

    res.end();
  });

  app.get('/updates', sseExpress, function(req, res) {
    connections.push(res);
    // send id to user
    res.sse('connected', {
      id: globalId
    });
    globalId++;

    req.on("close", function() {
      _.remove(connections, res);
      console.log('clients: ' + connections.length);
    });

    console.log(`Hello, ${globalId}!`);
  });

  app.listen(99, function () {
    console.log('Example app listening on port 99!');
  });
})();

(function() {
  'use strict';

  let path = require('path');
  let express = require('express');
  let http = require('http');
  let _ = require('lodash');
  let bodyParser = require('body-parser');
  let sseExpress = require('sse-express');

  let app = express();
  let globalId = 1;
  let connections = [];

  app.use(bodyParser.urlencoded({     // to support URL-encoded bodies
    extended: true
  }));
  app.use((req, res, next) => {
    // setup cors
    res.setHeader('Access-Control-Allow-Origin', '*');
    next();
  });

  app.post('/sendMessage', (req, res) => {
    res.writeHead(200, {
      'Access-Control-Allow-Origin': '*'
    });

    connections.forEach(function(connection) {
      connection.sse('message', {
        text: req.body.message,
        userId: req.body.userId
      });
    });

    res.end();
  });

  app.get('/updates', sseExpress, function(req, res) {
    connections.push(res);
    // send id to user
    res.sse('connected', {
      id: globalId
    });
    globalId++;

    req.on("close", function() {
      _.remove(connections, res);
      console.log('clients: ' + connections.length);
    });

    console.log(`Hello, ${globalId}!`);
  });

  app.listen(99, function () {
    console.log('Example app listening on port 99!');
  });
})();

Test closing github issue automatically?


const DEFAULT_SUBMIT_URL = 'pebblejs://close#';

window.onload = function() {
  var submitButton = document.getElementById('submitButton');
  submitButton.addEventListener('click', onSubmit);
};

function onSubmit() {
  var config = {
    'SETTINGS_SHOW_DATE': document.getElementById('showDate').checked
  };

  var submitUrl = getQueryParam('return_to', DEFAULT_SUBMIT_URL);
  location.href = submitUrl + encodeURIComponent(JSON.stringify(config));
}

// Get query variables
// Pasted from https://developer.pebble.com/guides/pebble-apps/pebblekit-js/app-configuration/
function getQueryParam(variable, defaultValue) {
  // Find all URL parameters
  var query = location.search.substring(1);
  var vars = query.split('&');
  for (var i = 0; i < vars.length; i++) {
    var pair = vars[i].split('=');

    // If the query variable parameter is found, decode it to use and return it for use
    if (pair[0] === variable) {
      return decodeURIComponent(pair[1]);
    }
  }
  return defaultValue || false;
}

const DEFAULT_SUBMIT_URL = 'pebblejs://close#';

window.onload = function() {
  var submitButton = document.getElementById('submitButton');
  submitButton.addEventListener('click', onSubmit);

  var config = JSON.parse(getQueryParam('config', '{}'));
  for (key in config) {
    switch(key) {
      case 'SETTINGS_SHOW_DATE':
        document.getElementById('showDate').checked = config[key];
        break;
      default:
        break;
    }
  }
};

function onSubmit() {
  var config = {
    'SETTINGS_SHOW_DATE': document.getElementById('showDate').checked
  };

  var submitUrl = getQueryParam('return_to', DEFAULT_SUBMIT_URL);
  location.href = submitUrl + encodeURIComponent(JSON.stringify(config));
}

// Get query variables
// Pasted from https://developer.pebble.com/guides/pebble-apps/pebblekit-js/app-configuration/
function getQueryParam(variable, defaultValue) {
  // Find all URL parameters
  var query = location.search.substring(1);
  var vars = query.split('&');
  for (var i = 0; i < vars.length; i++) {
    var pair = vars[i].split('=');

    // If the query variable parameter is found, decode it to use and return it for use
    if (pair[0] === variable) {
      return decodeURIComponent(pair[1]);
    }
  }
  return defaultValue || false;
}

Load the existing configuration into the UI


var image = new Image();
image.src = "img/BlueMarbleNasa.png";

// create shatter object after image has loaded
image.addEventListener("load", function() {
    var div = document.querySelectorAll('.shatter');
    var shatter = new Shatter(image, 14, 1.5);
    // adjustment to center image on screen
    var adjustment = (window.innerWidth / 2) - image.width / 2;

    // loop through images in shatter object and insert into
    // dom at correct position
    for (var i = 0; i < shatter.images.length; i++) {
        shatter.images[i][0].style.position = 'absolute';
        shatter.images[i][0].style.left = shatter.images[i][1][0] + adjustment + 'px';
        shatter.images[i][0].style.top = shatter.images[i][1][1] + 75 + 'px';
        div[0].appendChild(shatter.images[i][0]);
    }

    // wait a bit to add dom gravity
    window.setTimeout(function() {
        $('body').jGravity({
            target: 'everything',
            ignoreClass: 'ignoreMe',
            weight: 25,
            depth: 5,
            drag: true
        });
    }, 1000);

}, false);

var image = new Image();
image.src = "img/BlueMarbleNasa.png";

// create shatter object after image has loaded
image.addEventListener("load", function() {
    var div = document.querySelectorAll('.shatter');
    var shatter = new Shatter(image, 14, 1.5);
    // adjustment to center image on screen
    var adjustment = (window.innerWidth / 2) - image.width / 2;

    // loop through images in shatter object and insert into
    // dom at correct position
    for (var i = 0; i < shatter.images.length; i++) {
        shatter.images[i].image.style.position = 'absolute';
        shatter.images[i].image.style.left = shatter.images[i].x + adjustment + 'px';
        shatter.images[i].image.style.top = shatter.images[i].y + 75 + 'px';
        div[0].appendChild(shatter.images[i].image);
    }

    // wait a bit to add dom gravity
    window.setTimeout(function() {
        $('body').jGravity({
            target: 'everything',
            ignoreClass: 'ignoreMe',
            weight: 25,
            depth: 5,
            drag: true
        });
    }, 1000);

}, false);

Fix DOM example after breaking change to Shatter images array


import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { Alert } from '@blueprintjs/core';

export default class GenAlert extends PureComponent {
  static propTypes = {
    cancelText: PropTypes.string,
    confirmText: PropTypes.string.isRequired,
    intent: PropTypes.number.isRequired,
    isOpen: PropTypes.bool.isRequired,
    message: PropTypes.string.isRequired,
    closeGeneralAlert: PropTypes.func.isRequired,
    onConfirm: PropTypes.func
  };

  whenConfirm = () => {
    const { closeGeneralAlert, onConfirm } = this.props;
    if (typeof onConfirm === 'function') onConfirm();
    closeGeneralAlert();
  };

  render() {
    const {
      cancelText,
      confirmText,
      intent,
      isOpen,
      message,
      closeGeneralAlert
    } = this.props;

    return (
      <Alert
        isOpen={isOpen}
        cancelButtonText={cancelText}
        confirmButtonText={confirmText}
        intent={intent}
        onCancel={closeGeneralAlert}
        onConfirm={this.whenConfirm}
      >
        {message}
      </Alert>
    );
  }
}

import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { Alert } from '@blueprintjs/core';

export default class GenAlert extends PureComponent {
  static propTypes = {
    cancelText: PropTypes.string,
    confirmText: PropTypes.string.isRequired,
    intent: PropTypes.number.isRequired,
    isOpen: PropTypes.bool.isRequired,
    message: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.node
    ]).isRequired,
    closeGeneralAlert: PropTypes.func.isRequired,
    onConfirm: PropTypes.func
  };

  whenConfirm = () => {
    const { closeGeneralAlert, onConfirm } = this.props;
    if (typeof onConfirm === 'function') onConfirm();
    closeGeneralAlert();
  };

  render() {
    const {
      cancelText,
      confirmText,
      intent,
      isOpen,
      message,
      closeGeneralAlert
    } = this.props;

    return (
      <Alert
        isOpen={isOpen}
        cancelButtonText={cancelText}
        confirmButtonText={confirmText}
        intent={intent}
        onCancel={closeGeneralAlert}
        onConfirm={this.whenConfirm}
      >
        {message}
      </Alert>
    );
  }
}

Add extra accepted props for general alerts


// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

this.onpush = function(event) {
  // TODO(peter): Remove this check once Blink supports PushMessageData.json().
  var data = event.data;
  if (typeof data !== 'string')
    data = data.text();

  if (data !== 'shownotification') {
    sendMessageToClients('push', data);
    return;
  }

  // TODO(peter): Switch to self.registration.showNotification once implemented.
  event.waitUntil(showLegacyNonPersistentNotification('Push test title', {
    body: 'Push test body',
    tag: 'push_test_tag'
  }).then(function(notification) {
    sendMessageToClients('push', data);
  }, function(ex) {
    sendMessageToClients('push', String(ex));
  }));
};

function sendMessageToClients(type, data) {
  var message = JSON.stringify({
    'type': type,
    'data': data
  });
  clients.getAll().then(function(clients) {
    clients.forEach(function(client) {
      client.postMessage(message);
    });
  }, function(error) {
    console.log(error);
  });
}

function showLegacyNonPersistentNotification(title, options) {
  return new Promise(function(resolve, reject) {
    var notification = new Notification(title, options);
    notification.onshow = function() { resolve(notification); };
    notification.onerror = function() { reject(new Error('Failed to show')); };
  });
}

// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

this.onpush = function(event) {
  var data = event.data.text();
  if (data !== 'shownotification') {
    sendMessageToClients('push', data);
    return;
  }

  event.waitUntil(registration.showNotification('Push test title', {
    body: 'Push test body',
    tag: 'push_test_tag'
  }).then(function(notification) {
    sendMessageToClients('push', data);
  }, function(ex) {
    sendMessageToClients('push', String(ex));
  }));
};

function sendMessageToClients(type, data) {
  var message = JSON.stringify({
    'type': type,
    'data': data
  });
  clients.getAll().then(function(clients) {
    clients.forEach(function(client) {
      client.postMessage(message);
    });
  }, function(error) {
    console.log(error);
  });
}

Move the PushMessagingBrowserTest to use SWR.showNotification().


import React from 'react';
import { processLicenseFile } from 'util';
import { processingDelay } from 'data/constants';

let fileReader;

export default class FileHandler extends React.Component {
  static propTypes = {
    requestResults: React.PropTypes.func
  };

  render() {
    return (
      <input type="file" onChange={this.onChange} style={style.fileInput} />
    );
  }

  componentDidMount() {
    fileReader = new FileReader();
    fileReader.addEventListener('load', this.handleNewFile);
  }

  componentWillUnmount() {
    fileReader.removeEventListener('load', this.handleNewFile);
  }

  onChange = (event) => {
    if (event.target.files[0]) {
      fileReader.readAsText(event.target.files[0]);
    }
  };

  handleNewFile = (event) => {
    setTimeout(() => {
      this.props.requestResults(
        processLicenseFile(event.target.result)
      );
    }, processingDelay);
  };
}

const style = {
  fileInput: {
    opacity: 0,
    top: 0,
    bottom: 0,
    left: 0,
    right: 0,
    cursor: 'pointer',
    position: 'absolute',
    width: '100%'
  }
};

import React from 'react';
import { processLicenseFile } from 'util';
import { processingDelay } from 'data/constants';

let fileReader;

export default class FileHandler extends React.Component {
  static propTypes = {
    requestResults: React.PropTypes.func
  };

  render() {
    return (
      <input type="file" onChange={this.onChange} style={style.fileInput} />
    );
  }

  componentDidMount() {
    fileReader = new FileReader();
    fileReader.addEventListener('load', this.handleNewFile);
  }

  componentWillUnmount() {
    fileReader.removeEventListener('load', this.handleNewFile);
  }

  onChange = (event) => {
    if (event.target.files[0]) {
      fileReader.readAsText(event.target.files[0]);
    }
  };

  handleNewFile = (event) => {
    setTimeout(() => {
      this.props.requestResults(
        processLicenseFile(event.target.result)
      );
    }, processingDelay);
  };
}

const style = {
  fileInput: {
    opacity: 0,
    top: 0,
    bottom: 0,
    left: 0,
    right: 0,
    cursor: 'pointer',
    position: 'absolute',
    width: '100%',
    zIndex: 999
  }
};

Bring file input above OPEN label


var gulp = require('gulp');

var sass = require('gulp-sass');
var sourcemaps = require('gulp-sourcemaps');

var paths = {
    sass: {
        src: ['static/sass/**/*.scss'],
        dest: 'static/css'
    }
};

gulp.task('sass', function() {
    return gulp.src(paths.sass.src)
               .pipe(sourcemaps.init())
               .pipe(sass())
               .pipe(sourcemaps.write(
                   paths.sass.dest,
                   {sourceRoot: '/static/sass'}
               ))
               .pipe(gulp.dest(paths.sass.dest));
});

gulp.task('default', []);

var gulp = require('gulp');

var sass = require('gulp-sass');
var sourcemaps = require('gulp-sourcemaps');

var paths = {
    sass: {
        src: ['static/sass/**/*.scss'],
        dest: 'static/css'
    }
};

gulp.task('sass', function() {
    return gulp.src(paths.sass.src)
               .pipe(sourcemaps.init())
               .pipe(sass())
               .pipe(sourcemaps.write(
                   paths.sass.dest,
                   {sourceRoot: '/static/sass'}
               ))
               .pipe(gulp.dest(paths.sass.dest));
});

gulp.task('watch', function() {
    gulp.watch(paths.sass.src, ['sass']);
});

gulp.task('default', []);

Add the watch task, which watches for sass changes and recompiles


/*
 * Copyright (C) 2015 SUSE Linux
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE.txt file for details.
 */

(function () {
  'use strict';

  angular.module('janusHangouts')
    .directive('jhChat', jhChatDirective);

  function jhChatDirective() {
    return {
      restrict: 'EA',
      templateUrl: 'app/components/chat/jh-chat.html',
      scope: {
        messages: '='
      },
      link: jhChatLink,
      controllerAs: 'vm',
      bindToController: true,
      controller: JhChatCtrl
    };

    function jhChatLink(scope) {
      scope.$watch('vm.messages.length', function(newVal) {
        /* Update messages */
        if (newVal !== undefined) {
          // Scroll to bottom of messages list.
          var messagesList = document.getElementById('jh-chat-messages');
          setTimeout(function() {
            messagesList.scrollTop = messagesList.scrollHeight;
          }, 100);
        }
      });
    }

    function JhChatCtrl() {}
  }
})();

/*
 * Copyright (C) 2015 SUSE Linux
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE.txt file for details.
 */

(function () {
  'use strict';

  angular.module('janusHangouts')
    .directive('jhChat', jhChatDirective);

  function jhChatDirective() {
    return {
      restrict: 'EA',
      templateUrl: 'app/components/chat/jh-chat.html',
      scope: {
        messages: '='
      },
      link: jhChatLink,
      controllerAs: 'vm',
      bindToController: true,
      controller: JhChatCtrl
    };

    function jhChatLink(scope) {
      scope.$watch('vm.messages.length', function(newVal) {
        /* Update messages */
        if (newVal !== undefined) {
          // Scroll to bottom of messages list.
          var messagesList = document.getElementById('jh-chat-messages');
          setTimeout(function() {
            if (messagesList) {
              messagesList.scrollTop = messagesList.scrollHeight;
            }
          }, 100);
        }
      });
    }

    function JhChatCtrl() {}
  }
})();

Fix another spurious on quick logout


// commonjs code goes here

(function () {
    var i18n = window.i18n = require('i18next');
    var XHR = require('i18next-xhr-backend');
    var lngDetector = require('i18next-browser-languagedetector');
    var Cache = require('i18next-localstorage-cache');

    var backendOptions = {
        loadPath: '/static/locale/__lng__/translations.json',
    };
    var callbacks = [];
    var initialized = false;

    var detectionOptions = {
        order: ['htmlTag'],
        htmlTag: document.documentElement,
    };

    var cacheOptions = {
        enabled: true,
        prefix: page_params.server_generation + ':',
    };

    i18n.use(XHR)
        .use(lngDetector)
        .use(Cache)
        .init({
            nsSeparator: false,
            keySeparator: false,
            interpolation: {
                prefix: "__",
                suffix: "__",
            },
            backend: backendOptions,
            detection: detectionOptions,
            cache: cacheOptions,
            fallbackLng: 'en',
        }, function () {
            var i;
            initialized = true;
            for (i=0; i<callbacks.length; i += 1) {
                callbacks[i]();
            }
        });

    i18n.ensure_i18n = function (callback) {
        if (initialized) {
            callback();
        } else {
            callbacks.push(callback);
        }
    };

}());

// commonjs code goes here

(function () {
    var i18n = window.i18n = require('i18next');
    var XHR = require('i18next-xhr-backend');
    var lngDetector = require('i18next-browser-languagedetector');
    var Cache = require('i18next-localstorage-cache');

    var backendOptions = {
        loadPath: '/static/locale/__lng__/translations.json',
    };
    var callbacks = [];
    var initialized = false;

    var detectionOptions = {
        order: ['htmlTag'],
        htmlTag: document.documentElement,
    };

    var cacheOptions = {
        enabled: true,
        prefix: 'i18next:' + page_params.server_generation + ':',
    };

    i18n.use(XHR)
        .use(lngDetector)
        .use(Cache)
        .init({
            nsSeparator: false,
            keySeparator: false,
            interpolation: {
                prefix: "__",
                suffix: "__",
            },
            backend: backendOptions,
            detection: detectionOptions,
            cache: cacheOptions,
            fallbackLng: 'en',
        }, function () {
            var i;
            initialized = true;
            for (i=0; i<callbacks.length; i += 1) {
                callbacks[i]();
            }
        });

    i18n.ensure_i18n = function (callback) {
        if (initialized) {
            callback();
        } else {
            callbacks.push(callback);
        }
    };

}());

Add i18next: to the local storage cache prefix.


var config = require('../config');
var mongoose = require('mongoose');
var schemaManager;

describe('SchemaManager', function() {
  before(function() {
    mongoose.connect(config.db.url);
    var db = mongoose.connection;
    db.once('open', function() {
      schemaManager = require('../util/schema-manager')(mongoose);
    });
  });

  describe('add-invalid-schema', function() {
    var name = "";
    var schema = "";
    schemaManager.addSchema({"name" : name, "definition" : schema}, function(err, entity) {
      should.exist(err);
    });
  });

  describe('add-valid-schema', function() {
    var name = "network_element";
    var schema = "{    \
      ne_type: String, \
      cid: String,     \
      ip: String,      \
      ip6: String,     \
      bgpip: String,   \
      bgpip6: String,  \
    }";
    schemaManager.addSchema({"name" : name, "definition" : schema}, function(err, entity) {
      should.not.exist(err);
      // figure out how to check the entity
    });
  });
});

var config = require('../config');
var mongoose = require('mongoose');
var schemaManager;
var should = require('should');

describe('SchemaManager', function() {
  before(function(done) {
    mongoose.connect(config.db.url);
    var db = mongoose.connection;
    db.once('open', function() {
      schemaManager = require('../util/schema-manager')(mongoose);
      done();
    });
  });

  describe('add-invalid-schema', function() {
    it("should error adding an empty string ", function(done) {
      var name = "";
      var schema = "";
        schemaManager.addSchema({"name" : name, "definition" : schema}, function(err, entity) {
          should.exist(err);
          done();
        });
      });
  });

  describe('add-valid-schema', function() {
    it("should add a valid json schema object", function(done) {
      var name = "network_element";
      var schema = {
        ne_type: "String",
        cid: "String",
        ip: "String",
        ip6: "String",
        bgpip: "String",
        bgpip6: "String",
    }
    schemaManager.addSchema({"name" : name, "definition" : schema}, function(err, entity) {
      should.not.exist(err);
      done();
      // figure out how to check the entity
    });
    });
  });
});

Update schema manager test to run. Need to blow away test db before each run


(function(app) {
  'use strict';

  var dom = app.dom || require('../dom.js');
  var Events = app.Events || require('../base/events.js');

  var GeoNamesData = function(url) {
    this._url = url;
    this._loadPromise = null;
    this._data = [];
    this._events = new Events();
  };

  GeoNamesData.prototype.on = function() {
    return Events.prototype.on.apply(this._events, arguments);
  };

  GeoNamesData.prototype.load = function() {
    if (!this._loadPromise) {
      this._events.emit('loading');
      this._loadPromise = dom.loadJSON(this._url).then(function(data) {
        this._data = data;
        this._events.emit('loaded');
      }.bind(this));
    }
    return this._loadPromise;
  };

  if (typeof module !== 'undefined' && module.exports) {
    module.exports = GeoNamesData;
  } else {
    app.GeoNamesData = GeoNamesData;
  }
})(this.app || (this.app = {}));

(function(app) {
  'use strict';

  var dom = app.dom || require('../dom.js');
  var Events = app.Events || require('../base/events.js');

  var GeoNamesData = function(url) {
    this._url = url;
    this._loadPromise = null;
    this._data = [];
    this._events = new Events();
  };

  GeoNamesData.prototype.on = function() {
    return Events.prototype.on.apply(this._events, arguments);
  };

  GeoNamesData.prototype.load = function() {
    if (!this._loadPromise) {
      this._events.emit('loading');
      this._loadPromise = dom.loadJSON(this._url).then(function(data) {
        this._data = data;
        this._events.emit('loaded');
      }.bind(this)).catch(function() {
        this._loadPromise = null;
        this._events.emit('error');
      }.bind(this));
    }
    return this._loadPromise;
  };

  if (typeof module !== 'undefined' && module.exports) {
    module.exports = GeoNamesData;
  } else {
    app.GeoNamesData = GeoNamesData;
  }
})(this.app || (this.app = {}));

Make error event of GeoNames data


'use strict';

// Simple per-route rate limiter.

module.exports = function(hyper, req, next, options, specInfo) {
    if (!hyper.ratelimiter) {
        return next(hyper, req);
    }

    var requestClass = hyper._rootReq.headers['x-request-class'];
    if (!options || !options.limits || !options.limits[requestClass]) {
        return next(hyper, req);
    }

    // By default, ignore the domain for limiting purposes.
    var pathKey = hyper.config.service_name + '.'
                    + specInfo.path.replace(/\/[^\/]+\//, '') + '.'
                    + req.method.toUpperCase();

    var key = pathKey + '|' + hyper._rootReq.headers['x-client-ip'];
    if (hyper.ratelimiter.isAboveLimit(key, options.limits[requestClass])) {
        // TODO: Actually throw an HTTPError once we have verified limits to
        // work well.
        hyper.log('warn/ratelimit/' + pathKey, {
            key: key,
            limit: options.limits[requestClass],
            message: 'Rate limit exceeded'
        });
    }
    return next(hyper, req);
};

'use strict';

var HTTPError = require('../exports').HTTPError;

// Simple per-route rate limiter.

module.exports = function(hyper, req, next, options, specInfo) {
    if (!hyper.ratelimiter) {
        return next(hyper, req);
    }

    var requestClass = hyper._rootReq.headers['x-request-class'];
    if (!options || !options.limits || !options.limits[requestClass]) {
        return next(hyper, req);
    }

    // By default, ignore the domain for limiting purposes.
    var pathKey = hyper.config.service_name + '.'
                    + specInfo.path.replace(/\/[^\/]+\//, '') + '.'
                    + req.method.toUpperCase();

    var key = pathKey + '|' + hyper._rootReq.headers['x-client-ip'];
    if (hyper.ratelimiter.isAboveLimit(key, options.limits[requestClass])) {
        if (options.log_only) {
            hyper.log('warn/ratelimit/' + pathKey, {
                key: key,
                rate_limit_per_second: options.limits[requestClass],
                message: 'Rate limit exceeded'
            });
        } else {
            throw new HTTPError({
                status: 429,
                body: {
                   type: 'request_rate_exceeded',
                   title: 'HyperSwitch request rate limit exceeded',
                   key: key,
                   rate_limit_per_second: options.limits[requestClass],
                }
            });
        }
    }
    return next(hyper, req);
};

Support enforcing of request limits


'use strict';

var path = require('path');

/**
 * Extract the React Native module paths
 *
 * @return {Promise<Object>} A promise which resolves with
 *                           a webpack 'externals' configuration object
 */
function getReactNativeExternals() {
  var reactNativeRoot = path.dirname(require.resolve('react-native/package'));
  var blacklist = require('react-native/packager/blacklist');
  var ReactPackager = require('react-native/packager/react-packager');
  var reactNativePackage = require('react-native/package');

  return ReactPackager.getDependencies({
    assetRoots: [reactNativeRoot],
    blacklistRE: blacklist(false /* don't blacklist any platform */),
    projectRoots: [reactNativeRoot],
    transformModulePath: require.resolve('react-native/packager/transformer')
  }, reactNativePackage.main).then(function(dependencies) {
    return dependencies.filter(function(dependency) {
      return !dependency.isPolyfill;
    });
  }).then(function(dependencies) {
    return dependencies.reduce(function(externals, dependency) {
      externals[dependency.id] = 'commonjs ' + dependency.id;
      return externals;
    }, {});
  });
}

module.exports = getReactNativeExternals;

'use strict';

var path = require('path');

/**
 * Extract the React Native module paths
 *
 * @return {Promise<Object>} A promise which resolves with
 *                           a webpack 'externals' configuration object
 */
function getReactNativeExternals() {
  var reactNativeRoot = path.dirname(require.resolve('react-native/package'));
  var blacklist = require('react-native/packager/blacklist');
  var ReactPackager = require('react-native/packager/react-packager');
  var reactNativePackage = require('react-native/package');

  return ReactPackager.getDependencies({
    assetRoots: [reactNativeRoot],
    blacklistRE: blacklist(false /* don't blacklist any platform */),
    projectRoots: [reactNativeRoot],
    transformModulePath: require.resolve('react-native/packager/transformer')
  }, reactNativePackage.main).then(function(dependencies) {
    return dependencies.filter(function(dependency) {
      return !dependency.isPolyfill();
    });
  }).then(function(dependencies) {
    return Promise.all(dependencies.map(function(dependency) {
      return dependency.getName();
    }));
  }).then(function(moduleIds) {
    return moduleIds.reduce(function(externals, moduleId) {
      externals[moduleId] = 'commonjs ' + moduleId
      return externals;
    }, {});
  });
}

module.exports = getReactNativeExternals;

Update to support new module API


import { request } from '../../http';

export function attempt(id, startedAt, win) {
  return request(`/training/${id}/attempt`, {
    method: 'POST',
    data: {
      win: win ? 1 : 0,
      time: new Date().getTime() - (startedAt || new Date()).getTime()
    }
  });
}

export function vote(id, v) {
  return request(`/training/${id}/vote`, {
    method: 'POST',
    data: {
      vote: v
    }
  });
}

export function loadPuzzle(id) {
  return request(`/training/${id}/load`);
}

export function setDifficulty(d) {
  return request('/training/difficulty', {
    method: 'POST',
    data: {
      difficulty: d
    }
  });
}

export function newPuzzle() {
  return request('/training/new');
}

export function history() {
  return request('/training/history');
}

import { request } from '../../http';

export function attempt(id, startedAt, win) {
  return request(`/training/${id}/attempt`, {
    method: 'POST',
    data: {
      win: win ? 1 : 0,
      time: new Date().getTime() - (startedAt || new Date()).getTime()
    }
  });
}

export function vote(id, v) {
  return request(`/training/${id}/vote`, {
    method: 'POST',
    data: {
      vote: v
    }
  });
}

export function setDifficulty(d) {
  return request('/training/difficulty', {
    method: 'POST',
    data: {
      difficulty: d
    }
  });
}

export function loadPuzzle(id) {
  return request(`/training/${id}/load`, { background: true });
}

export function newPuzzle() {
  return request('/training/new', { background: true });
}

export function history() {
  return request('/training/history');
}

Load training data in bg


$( document ).ready(function() {
    let $uploadCrop;
    let $preview = $('#uploadPreview');

    function readFile(input) {
        if (input.files && input.files[0]) {
            let reader = new FileReader();
            reader.onload = function (evt) {
                $uploadCrop.croppie('bind', {
                    url: evt.target.result
                });
            };
            reader.readAsDataURL(input.files[0]);
        }
    }

    $uploadCrop = $preview.croppie({
        viewport: {
            width: $preview.width() || 300,
            height: $preview.height() || 300,
            type: $preview.hasClass('crop-circle') ? 'circle' : 'square'
        },
        boundary: {
            width: $preview.width() || 300,
            height: $preview.height() || 300
        },
        update: function (data) {
            $("#x1").val(Math.floor(data.points[0]));
            $("#y1").val(Math.floor(data.points[1]));
            $("#x2").val(Math.floor(data.points[2]));
            $("#y2").val(Math.floor(data.points[3]));
        }
    });

    $('#avatar').on('change', function () { readFile(this); });
});

if ($('#uploadPreview').length) {
    let $uploadCrop;
    let $preview = $('#uploadPreview');

    function readFile(input) {
        if (input.files && input.files[0]) {
            let reader = new FileReader();
            reader.onload = function (evt) {
                $uploadCrop.croppie('bind', {
                    url: evt.target.result
                });
            };
            reader.readAsDataURL(input.files[0]);
        }
    }

    $uploadCrop = $preview.croppie({
        viewport: {
            width: $preview.width() || 300,
            height: $preview.height() || 300,
            type: $preview.hasClass('crop-circle') ? 'circle' : 'square'
        },
        boundary: {
            width: $preview.width() || 300,
            height: $preview.height() || 300
        },
        update: function (data) {
            $("#x1").val(Math.floor(data.points[0]));
            $("#y1").val(Math.floor(data.points[1]));
            $("#x2").val(Math.floor(data.points[2]));
            $("#y2").val(Math.floor(data.points[3]));
        }
    });

    $('#avatar').on('change', function () { readFile(this); });
}

Check before attempting to init image cropping


import React from 'react'
import getPlayer from '../api/getPlayer'

import NBA from 'nba'

module.exports = ({state, dispatch}) => {
  const customClass = state.form.suggestions.length > 0 ? "suggestions" : "hidden"
    if (state.form.name && state.form.suggestions === 0) {
      return noSuggestions()
    }
    else {
      return (

          {state.form.suggestions
            .filter((suggestion, index) => index < 14)
            .map((thisSuggestion, index) => {
              function handleClickSuggestion () {
                getPlayer(thisSuggestion.fullName, dispatch)
              }
              return (

{thisSuggestion.fullName}

              )
            })}

        )
    }
}

function noSuggestions () {
  return (

No suggestions. Try refining your search

  )
}

import React from 'react'
import getPlayer from '../api/getPlayer'

import NBA from 'nba'

module.exports = ({state, dispatch}) => {
  const suggestionLimit = 14
  const shouldShow = state.form.suggestions.length > 0 ? "suggestions" : "hidden"
    if (state.form.name === null) {
      return null
    }
    else {
      return (

          {state.form.suggestions.length > 0 ?
            state.form.suggestions
            .filter((suggestion, index) => index < suggestionLimit)
            .map((thisSuggestion, index) => {
              function handleClickSuggestion () {
                getPlayer(thisSuggestion.fullName, dispatch)
              }
              return (

{thisSuggestion.fullName}

              )
            })
            :
            noSuggestions()
          }

        )
    }
}

function noSuggestions () {
  return (

No suggestions. Try refining your search

  )
}

Change the rendering of suggestions so that a more helpful 'refine results' bit is shown when there are no suggestions


// Ajax request passing User form values to php

	    $(document).ready(function(){
			  $(document).on('submit', '#details_for_email', function(){
				   event.preventDefault();
				   var form = $('#details_for_email');
				   var data_string = $(form).serialize();

				   $.ajax({
					    type: 'POST',
					    url: 'https://vmssecurepay.jkamradcliffe.net/index.php/email/htmlmail',
					    data: data_string,
					    success: function(json){
                var obj = jQuery.parseJSON(json);
                console.log(obj.success);
                console.log(obj.error);
                $('#error').html(obj.error);
                $('#email_sent_alert').html(obj.success);

                if ((obj.success) = "Email Sent!!"){
                  document.getElementById('email_sent').style.display='block';
                }

                //form.find('#submit').prop('disabled', true);
              }
				    });
				    return false;
			   });
		  });

// Ajax request passing User form values to php

	    $(document).ready(function(){
			  $(document).on('submit', '#details_for_email', function(){
				   event.preventDefault();
				   var form = $('#details_for_email');
				   var data_string = $(form).serialize();

				   $.ajax({
					    type: 'POST',
					    url: 'https://vmssecurepay.jkamradcliffe.net/index.php/email/htmlmail',
					    data: data_string,
					    success: function(json){
                var obj = jQuery.parseJSON(json);
                console.log(obj.success);
                console.log(obj.error);
                $('#error').html(obj.error);
                $('#email_sent_alert').html(obj.success);

                $('#email_sent').show();
                //document.getElementById('email_sent').style.display='block';

                //form.find('#submit').prop('disabled', true);
              }
				    });
				    return false;
			   });
		  });

Swap JS for terse jQuery


var http = require('http');
var httpProxy = require('http-proxy');

/**
 * Creates a proxy server to handle nodejitsu requests based on subdomain
 */

module.exports = function (config, callback) {
  // skip this server if not on nodejitsu
  if (!config.run_nodejitsu_server) {
    return callback();
  }

  var proxy = httpProxy.createProxyServer({});

  return http.createServer(function (req, res) {
    var host = req.headers.host;
    var subdomain = host.split('.')[0];
    var options = { target: 'http://' + config.host + ':' };
    switch (subdomain) {
      case 'admin':
        options.target += config.admin_port;
        break;
      case 'couch':
        options.target = config.couch.url;
        break;
      default:
        options.target += config.www_port;
        break;
    }
    proxy.web(req, res, options);
  }).listen(80, config.host, callback);

};

Tweak indentation to pass jshint


import Typography from '@material-ui/core/Typography'
import { withStyles } from '@material-ui/core/styles'
import { facebookSignInUrl } from 'client/src/urls'

const styles = theme => ({
  container: {
    backgroundColor: theme.palette.grey[200],
    alignItems: 'center',
    justifyContent: 'center',
    display: 'flex',
    padding: theme.spacing.unit,
    width: '100%',
  },
  image: {
    width: '30px',
    marginRight: theme.spacing.unit,
  },
})

const handleClick = event => {
  event.preventDefault()
  window.location = facebookSignInUrl()
}

const FacebookLoginButton = ({ classes }) => (

    <Typography variant="body2">
      Sign in with Facebook
    </Typography>

)

export default withStyles(styles)(FacebookLoginButton)

import Button from '@material-ui/core/Button'
import Typography from '@material-ui/core/Typography'
import { withStyles } from '@material-ui/core/styles'
import { facebookSignInUrl } from 'client/src/urls'

const styles = theme => ({
  image: {
    width: 21,
    marginRight: theme.spacing.unit,
  },
})

const handleClick = event => {
  event.preventDefault()
  window.location.replace(facebookSignInUrl())
}

const FacebookLoginButton = ({ classes }) => (
  <Button fullWidth variant="contained" onClick={handleClick}>
    <img src="/static/fb-f-logo.png" className={classes.image} />
    <Typography variant="body2">
      Sign in with Facebook
    </Typography>
  </Button>
)

export default withStyles(styles)(FacebookLoginButton)

Use a material UI button for facebook login


#!/usr/bin/env node
var Cucumber = require('../lib/cucumber');
var cli = Cucumber.Cli(process.argv);
cli.run(function (succeeded) {
  var code = succeeded ? 0 : 1;

  process.on('exit', function () {
    process.exit(code);
  });

  var timeoutId = setTimeout(function () {
    console.error('Cucumber process timed out after waiting 60 seconds for the node.js event loop to empty.  There may be a resource leak.  Have all resources like database connections and network connections been closed properly?');
    process.exit(code);
  }, 60 * 1000);

  if (timeoutId.unref) {
    timeoutId.unref();
  }
  else {
    clearTimeout(timeoutId);
  }
});

#!/usr/bin/env node
var Cucumber = require('../lib/cucumber');
var cli = Cucumber.Cli(process.argv);
cli.run(function (succeeded) {
  var code = succeeded ? 0 : 1;

  function exitNow() {
    process.exit(code);
  }

  if (process.stdout.write('')) {
    exitNow();
  } else {
    // write() returned false, kernel buffer is not empty yet...
    process.stdout.on('drain', exitNow);
  }
});

Remove CLI resource leak timeout


(function() {
  'use strict';

  // Model

  var Task = Backbone.Model.extend({
    defaults: {
      title: 'do something!',
      completed: false
    }
  });

  var task = new Task();

  // View

  var TaskView = Backbone.View.extend({
    tagName: 'li'
  });

  var taskView = new TaskView({
    model: task
  });

  console.log(taskView.$el);

}());

(function() {
  'use strict';

  // Model

  var Task = Backbone.Model.extend({
    defaults: {
      title: 'do something!',
      completed: false
    }
  });

  var task = new Task();

  // View

  var TaskView = Backbone.View.extend({
    tagName: 'li',
    className: 'liClass',
    id: 'liId'
  });

  var taskView = new TaskView({
    model: task
  });

  console.log(taskView.el);

}());

Add id and class attributes.


var Game = require('../../src/js/models/game.js');

describe('Game', function () {
    beforeEach(function () {
        this.model = new Game();
    });

    describe('#createFrames', function () {
        it('should return an array of 10 frames', function () {
            expect(this.model.createFrames().length).toEqual(10);
        });

        it('should return an array of linked frames', function () {
            var frames = this.model.createFrames(),
                iterator;

            for (iterator = 0; iterator < (frames.length - 1); iterator++) {
                expect(frames[iterator].getNextFrame() === null).toEqual(false);
            }
        });

        it('should set the first frame to be current', function () {
            var frames = this.model.createFrames();
            expect(frames[0].isCurrentFrame()).toEqual(true);
        });

        it('should leave the final frames "next" reference as null', function () {
            var frames = this.model.createFrames();
            expect(frames[9].getNextFrame()).toBeNull();
        });
    });

    describe('#getScore', function () {
        it('should return zero for a new game', function () {
            expect(this.model.getScore()).toEqual(0);
        });

        it('should return the combined total of its child frames scores', function () {
            var frames = this.model.createFrames();
            frames[0].roll(3);
            frames[0].roll(3);
            frames[0].roll(3);
            this.model.setFrames(frames);
            expect(this.model.getScore()).toEqual(9);
        });
    });

    describe('#getRandomRoll', function () {
        it('should always return a number from 0 to the given maximum', function () {
            var max = 7,
                maxPlusOne = max + 1,
                iterator,
                roll;

            for (iterator = 0; iterator < 500; iterator++) {
                roll = this.model.getRandomRoll(max);

                expect(roll).toBeLessThan(maxPlusOne);
                expect(roll).toBeGreaterThan(-1);
            }
        });
    });

    describe('#isFinished', function () {
        it('should return false for a new game', function () {
            expect(this.model.isFinished()).toEqual(false);
        });

        it('should return true for a finished game', function () {
            var frames = this.model.createFrames(),
                isFinished,
                iterator;

            // Roll 9 strikes
            for (iterator = 0; iterator < 9; iterator++) {
                frames[0].roll(10);
            }

            this.model.setFrames(frames);

            // Finish the game with 1 or 2 more rolls
            this.model.roll();
            if (!this.model.isFinished()) {
                this.model.roll();
            }

            // Check that game state is finished
            expect(this.model.isFinished()).toEqual(true);
        });
    });
});

Add spec for game model


"use strict";

(function() {
  var filters = {
    age: function() {
      this
        .saturation(20)
        .gamma(1.4)
        .vintage()
        .contrast(5)
        .exposure(15)
        .vignette(300, 60)
        .render()
    },

    lomo: function() {
      this
        .brightness(15)
        .exposure(15)
        .curves('rgb', [0, 0], [200, 0], [155, 255], [255, 255])
        .saturation(-20)
        .gamma(1.8)
        .vignette("50%", 60)
        .brightness(5)
        .render()
    }
  };

  $('#filters button').click(function(e) {
    e.preventDefault();
    Caman("#canvas", filters[$(this).data('filter')]);
  });

})()

"use strict";

(function() {
  var filters = {
    age: function() {
      this
        .saturation(20)
        .gamma(1.4)
        .vintage()
        .contrast(5)
        .exposure(15)
        .vignette(300, 60)
        .render()
    },

    lomo: function() {
      this
        .brightness(15)
        .exposure(15)
        .curves('rgb', [0, 0], [200, 0], [155, 255], [255, 255])
        .saturation(-20)
        .gamma(1.8)
        .vignette("50%", 60)
        .brightness(5)
        .render()
    }
  };

  $('#filters button').click(function(e) {
    e.preventDefault();

    Caman("#canvas").revert();
    Caman("#canvas", filters[$(this).data('filter')]);
  });

})()

Use Caman revert for filters


"use strict";

define(['jquery',
        'underscore',
        'backbone',
        'jquery.tagsinput'
], function ($, _, Backbone, TagsInput) {
    var Recipe = Backbone.Model.extend({
        urlRoot: '/',
        defaults: {
            ingredients: [],
            method: ''
        }
    });

    var Recipes = Backbone.Collection.extend({
        model: Recipe,
        urlRoot: '/'
    });

    var SearchTags = Backbone.Model.extend({
        defaults: {
            tags: []
        }
    });

    var App = Backbone.View.extend({
        el: '#container',

        initialize: function () {
            _.bindAll(this, 'tagAdded', 'tagRemoved');

            var self = this;
            this.recipeCollection = new Recipes;
            this.searchTags = new SearchTags;

            this.$el.find('.search-input').tagsInput({
                'defaultText': 'Search',
                'width': '99%',
                'height': '100%',
                'delimiter': [',',';'],
                'onAddTag': self.tagAdded,
                'onRemoveTag': self.tagRemoved
            });
        },

        tagAdded: function (tag) {
            this.searchTags.attributes.tags.push(tag);
        },

        tagRemoved: function (tag) {
            this.searchTags.attributes.tags.pop();
        },

        render: function () {
            console.log('bar');
        }
    });

    return App;
});

"use strict";

define(['jquery',
        'underscore',
        'backbone',
        'jquery.tagsinput'
], function ($, _, Backbone, TagsInput) {
    var Recipe = Backbone.Model.extend({
        urlRoot: '/',
        defaults: {
            ingredients: [],
            method: ''
        }
    });

    var Recipes = Backbone.Collection.extend({
        model: Recipe,
        urlRoot: '/'
    });

    var SearchTag = Backbone.Model.extend({
        defaults: {
            value: ''
        }
    });

    var SearchTags = Backbone.Collection.extend({
        model: SearchTag
    });

    var App = Backbone.View.extend({
        el: '#container',

        initialize: function () {
            _.bindAll(this, 'tagAdded', 'tagRemoved');

            var self = this;
            this.recipeCollection = new Recipes;
            this.searchTags = new SearchTags;

            this.$el.find('.search-input').tagsInput({
                'defaultText': 'Search',
                'width': '99%',
                'height': '100%',
                'delimiter': [',',';'],
                'onAddTag': self.tagAdded,
                'onRemoveTag': self.tagRemoved
            });
        },

        tagAdded: function (tag) {
            this.searchTags.add({tag: tag});
        },

        tagRemoved: function (tag) {
            this.searchTags.remove(this.searchTags.where({tag: tag}));
        }
    });

    return App;
});

Use collections instead of models for tags


var _        = require('underscore')
,   Epg      = require('./lib/epg')
,   conf     = require('./conf')
,   epg      = new Epg(conf.epg)
,   importer = require('./importer/' + conf.importer.type)(conf.importer.options)
,   status   = require('./status_client')('importer')
,   moment   = require('moment')
;

importer.on("error", function(err) {
    console.error("Error importing data", err);
//    status.set('status', 'error');
});
importer.on("services", function(services) {
    console.log("Received services from importer");
    epg.addServices(services);
});
importer.on("events", function(events) {
    console.log("Received events from importer");
    epg.addEvents(events);
    console.log("Updating EIT after import");
    var ret = epg.updateEit();
    if (ret) {
        console.log("EIT updated, updating Carousel");
        epg.updateCarousel();
    }
});
importer.on("run", function() {
    console.log("Starting import");
    status.set('status', 'running');
});
importer.on("done", function() {
    console.log("Import finished");
    status.set('status', 'idle');
    status.set('lastRun', moment().toISOString());
    status.set('nextRun', moment().add(importer.options.interval_hours, 'hours').toISOString());
});

status.set('status', 'running', function() {
    console.log("Starting importer");
    importer.run();
});

var _        = require('underscore')
,   Epg      = require('./lib/epg')
,   conf     = require('./conf')
,   epg      = new Epg(conf.epg)
,   importer = require('./importer/' + conf.importer.type)(conf.importer.options)
,   status   = require('./status_client')('importer')
,   moment   = require('moment')
;

importer.on("error", function(err) {
    console.error("Error importing data", err);
//    status.set('status', 'error');
});
importer.on("services", function(services) {
    console.log("Received services from importer");
    epg.addServices(services);
});
importer.on("events", function(events) {
    console.log("Received events from importer");
    epg.addEvents(events);
    console.log("Updating EIT after import");
    var ret = epg.updateEit();
    if (ret) {
        console.log("EIT updated, updating Carousel");
        epg.updateCarousel();
    }
    importer.importFinished();
});
importer.on("run", function() {
    console.log("Starting import");
    status.set('status', 'running');
});
importer.on("done", function() {
    console.log("Import finished");
    status.set('status', 'idle');
    status.set('lastRun', moment().toISOString());
    status.set('nextRun', moment().add(importer.options.interval_hours, 'hours').toISOString());
});

status.set('status', 'running', function() {
    console.log("Starting importer");
    importer.run();
});

Call importer.importFinished after inserting events into db


function compose() {
    fs = arguments;
    return function(x) {
        for(var i in fs) {
            var f = fs[fs.length - i - 1];
            x = f.apply(null, [x]);
        }
        return x;
    }
}

function squish(x1, x2) {
    for (var key in x2) {
        x1[key] = x2[key];
    }
    return x1;
}

function merge(x1, x2) {
    return squish(squish({}, x1), x2);
}

function Person(name) {
    return {_name: name};
}

function Titled(x) {
    return merge(x, {_title: "Dr"});
}

function Friendly(x) {
    return merge(x, {hello: function() { return "Hi, I'm " + x._title + " " + x._name;}});
}

var TitledPerson = compose(Titled, Person);
var Friend = compose(Friendly, Titled, Person);
var tony = new Friend("Tony");

console.log(tony);
console.log(tony.hello());

function compose() {
    fs = arguments;
    return function(x) {
        for(var i in fs) {
            var f = fs[fs.length - i - 1];
            x = f.apply(null, [x]);
        }
        return x;
    }
}

function merge(x1, x2) {
    function squish(under, over) {
        for (var key in over) {
            under[key] = over[key];
        }
        return under;
    }
    return squish(squish({}, x1), x2);
}

function Person(name) {
    return {_name: name};
}

function Titled(x) {
    return merge(x, {_title: "Dr"});
}

function Friendly(x) {
    return merge(x, {hello: function() { return "Hi, I'm " + x._title + " " + x._name;}});
}

var TitledPerson = compose(Titled, Person);
var Friend = compose(Friendly, Titled, Person);
var tony = new Friend("Tony");

console.log(tony);
console.log(tony.hello());

Make squish private as it's dangerous.


import MarkdownArea from 'mdarea';
import BasicEditorDriver from 'flarum/utils/BasicEditorDriver';

export default class MarkdownAreaEditorDriver extends BasicEditorDriver {
  build(dom, params) {
    super.build(dom, params);
    this.el.id = params.textareaId;

    // We can't bind shortcutHandler directly in case `build`
    // runs before MarkdownToolbar's `oninit`.
    this.el.addEventListener('keydown', function (e) {
      return params.shortcutHandler(...arguments);
    });

    this.mdarea = new MarkdownArea(this.el, {
      keyMap: {
        indent: ['Ctrl+m'],
        outdent: ['Ctrl+M'],
        inline: []
      }
    });
  }

  destroy() {
    this.mdarea.destroy();
    super.destroy();
  }
}

import MarkdownArea from 'mdarea';
import BasicEditorDriver from 'flarum/utils/BasicEditorDriver';

export class MarkdownEditorFlarumExtension {
  constructor(oninput, callInputListeners, onsubmit) {
    this.oninput = oninput;
    this.callInputListeners = callInputListeners;
    this.onsubmit = onsubmit;
  }

  handleKey(
    prefix,
    selection,
    postfix,
    evt
  ) {
    // setTimeout without a time executes after the call stack has cleared,
    // so any DOM changes originating from mdarea (e.g. executing an undo)
    // will be finished by then. At that time, `e.target.value` will represent
    // the updated value of the textarea in response to the keypress.
    setTimeout(() => {
      this.oninput(evt.target.value);

      if ((evt.metaKey || evt.ctrlKey) && evt.key === 'Enter') {
        return this.onsubmit();
      }

      this.callInputListeners(evt);
    });
  }
}

export default class MarkdownAreaEditorDriver extends BasicEditorDriver {
  build(dom, params) {
    this.el.className = params.classNames.join(' ');
    this.el.disabled = params.disabled;
    this.el.placeholder = params.placeholder;
    this.el.value = params.value;
    this.el.id = params.textareaId;

    dom.append(this.el);

    // We can't bind shortcutHandler directly in case `build`
    // runs before MarkdownToolbar's `oninit`.
    this.el.addEventListener('keydown', function (e) {
      return params.shortcutHandler(...arguments);
    });

    const callInputListeners = (e) => {
      params.inputListeners.forEach((listener) => {
        listener();
      });

      e.redraw = false;
    };

    // This one can't be run through mdarea, but that doesn't matter
    // because mdarea doesn't change value in response to clicks.
    this.el.addEventListener('click', callInputListeners);

    this.mdarea = new MarkdownArea(this.el, {
      keyMap: {
        indent: ['Ctrl+m'],
        outdent: ['Ctrl+M'],
        inline: []
      },
      extensions: [
        new MarkdownEditorFlarumExtension(params.oninput, callInputListeners, params.onsubmit)
      ]
    });
  }

  destroy() {
    this.mdarea.destroy();
    super.destroy();
  }
}

Attach oninput, inputListeners, onsubmit via mdarea extension


(function () {
	if (typeof define == "function" && define.amd)
		define(["graphjs/Graph"], factory);
	else if (typeof exports == "object")
		module.exports = factory(require("graphjs/Graph"));
	else
		root.Collection = factory(root.Graph);
}(this, function (Graph) {
	return function Collection(Generic) {
		return function(array) {
			return array.map(function (entry) {
				return Graph.parse(Generic, entry);
			});
		}
	};
}));

(function (root, factory) {
	if (typeof define == "function" && define.amd)
		define(["graphjs/Graph"], factory);
	else if (typeof exports == "object")
		module.exports = factory(require("graphjs/Graph"));
	else
		root.Collection = factory(root.Graph);
}(this, function (Graph) {
	return function Collection(Generic) {
		return function(array) {
			return array.map(function (entry) {
				return Graph.parse(Generic, entry);
			});
		}
	};
}));

Fix bug with root parameter


import cx from 'classnames';
import {noop} from 'lodash';
import React from 'react';

import menuItemContainer from './containers/menuItemContainer';

class BaseMenuItem extends React.Component {
  displayName = 'BaseMenuItem';

  constructor(props) {
    super(props);

    this._handleClick = this._handleClick.bind(this);
  }

  render() {
    const {active, children, className, disabled} = this.props;

    return (
      <li
        className={cx({
          'active': active,
          'disabled': disabled,
        }, className)}>
        <a onClick={this._handleClick} role="button">
          {children}
        </a>
      </li>
    );
  }

  _handleClick(e) {
    const {disabled, onClick} = this.props;

    e.preventDefault();
    !disabled && onClick(e);
  }
}

BaseMenuItem.defaultProps = {
  onClick: noop,
};

const MenuItem = menuItemContainer(BaseMenuItem);

export {BaseMenuItem};
export default MenuItem;

import cx from 'classnames';
import {noop} from 'lodash';
import React from 'react';

import menuItemContainer from './containers/menuItemContainer';

class BaseMenuItem extends React.Component {
  displayName = 'BaseMenuItem';

  constructor(props) {
    super(props);

    this._handleClick = this._handleClick.bind(this);
  }

  render() {
    const {active, children, className, disabled} = this.props;
    const conditionalClassNames = {
      'active': active,
      'disabled': disabled,
    };

    return (
      <li
        className={cx(conditionalClassNames, className)}>
        <a
          className={cx('dropdown-item', conditionalClassNames)}
          href="#"
          onClick={this._handleClick}
          role="button">
          {children}
        </a>
      </li>
    );
  }

  _handleClick(e) {
    const {disabled, onClick} = this.props;

    e.preventDefault();
    !disabled && onClick(e);
  }
}

BaseMenuItem.defaultProps = {
  onClick: noop,
};

const MenuItem = menuItemContainer(BaseMenuItem);

export {BaseMenuItem};
export default MenuItem;

Add support for BS4 menu items


'use strict';

let styles = {

  overlay(isOpen) {
    return {
      position: 'fixed',
      zIndex: 1,
      width: '100%',
      height: '100%',
      background: 'rgba(0, 0, 0, 0.3)',
      opacity: isOpen ? 1 : 0,
      transform: isOpen ? '' : 'translate3d(-100%, 0, 0)',
      transition: isOpen ? 'opacity 0.3s' : 'opacity 0.3s, transform 0s 0.3s'
    };
  },

  menuWrap(isOpen, width, right) {
    return {
      position: 'fixed',
      right: right ? 0 : 'inherit',
      zIndex: 2,
      width,
      height: '100%',
      transform: isOpen ? '' : right ? 'translate3d(100%, 0, 0)' : 'translate3d(-100%, 0, 0)',
      transition: 'all 0.5s'
    };
  },

  menu() {
    return {
      height: '100%',
      boxSizing: 'border-box'
    };
  },

  itemList() {
    return {
      height: '100%'
    };
  },

  item() {
    return {
      display: 'block',
      outline: 'none'
    };
  }

};

export default styles;

'use strict';

let styles = {

  overlay(isOpen) {
    return {
      position: 'fixed',
      zIndex: 1,
      width: '100%',
      height: '100%',
      background: 'rgba(0, 0, 0, 0.3)',
      opacity: isOpen ? 1 : 0,
      transform: isOpen ? '' : 'translate3d(-100%, 0, 0)',
      transition: isOpen ? 'opacity 0.3s' : 'opacity 0.3s, transform 0s 0.3s'
    };
  },

  menuWrap(isOpen, width, right) {
    return {
      position: 'fixed',
      right: right ? 0 : 'inherit',
      zIndex: 2,
      width,
      height: '100%',
      transform: isOpen ? '' : right ? 'translate3d(100%, 0, 0)' : 'translate3d(-100%, 0, 0)',
      transition: 'all 0.5s'
    };
  },

  menu() {
    return {
      height: '100%',
      boxSizing: 'border-box',
      overflow: 'auto'
    };
  },

  itemList() {
    return {
      height: '100%'
    };
  },

  item() {
    return {
      display: 'block',
      outline: 'none'
    };
  }

};

export default styles;

Add overflow: auto to allow scrolling of menu items by default


$(function () {
	var data = JSON.parse(localStorage.data);

	$('#graph').highcharts({
		chart: {
			zoomType: 'x'
		},
		title: {
			text: 'Tab Count'
		},
		xAxis: {
			type: 'datetime',
		},
		yAxis: [{
			title: {
				text: 'Tab Count',
				style: {
					color: Highcharts.getOptions().colors[0]
				}
			}
		}, {
			title: {
				text: 'Window Count',
				style: {
					color: Highcharts.getOptions().colors[1]
				}
			},
			opposite: true
		}],
		legend: {
			enabled: false,
		},
		series: [{
			name: 'Tabs',
			data: _.map(data, function(datum) {
				return [Date.parse(datum.date), datum.tabCount];
			}),
		},
		{
			name: 'Windows',
			data: _.map(data, function(datum) {
				return [Date.parse(datum.date), datum.windowCount];
			}),
			yAxis: 1,
		}],
	});
});

$(function () {
	var data = JSON.parse(localStorage.data);

	Highcharts.setOptions({
		global: {
			timezoneOffset: (new Date()).getTimezoneOffset(),
		}
	});

	$('#graph').highcharts({
		chart: {
			zoomType: 'x'
		},
		title: {
			text: 'Tab Count'
		},
		xAxis: {
			type: 'datetime',
		},
		yAxis: [{
			title: {
				text: 'Tab Count',
				style: {
					color: Highcharts.getOptions().colors[0]
				}
			}
		}, {
			title: {
				text: 'Window Count',
				style: {
					color: Highcharts.getOptions().colors[1]
				}
			},
			opposite: true
		}],
		legend: {
			enabled: false,
		},
		series: [{
			name: 'Tabs',
			data: _.map(data, function(datum) {
				return [Date.parse(datum.date), datum.tabCount];
			}),
		},
		{
			name: 'Windows',
			data: _.map(data, function(datum) {
				return [Date.parse(datum.date), datum.windowCount];
			}),
			yAxis: 1,
		}],
	});
});

Set Highcharts global.timezoneOffset in order to have the x-axis values displayed in the user's timezone (instead of UTC).


/*
 * Define the main Huna JS module
 */
var app = angular.module('HunaJS', ['ngRoute'])

/**
 * Get some routes in place
 */
.config(function($routeProvider, $locationProvider) {

  // Root
  $routeProvider
  .when('/', {
	  controller: 'MainController',
	  templateUrl: '/js/app/modules/main/main.html'
	})

  // error
  .when('/error', {
    controller: 'ErrorController',
    templateUrl: '/js/app/modules/error/error.html'
  })

  // User Routes
	.when('/user/register', {
		controller: 'RegisterController',
		templateUrl: '/js/app/modules/user/register.html'
	})
  .when('/user/register/activate', {
    controller: 'ActivateController',
    templateUrl: '/js/app/modules/user/activate.html'
  })
  .when('/user/login', {
    controller: 'LoginController',
    templateUrl: '/js/app/modules/user/login.html'
  })
  .when('/user/password', {
    controller: 'RecoverPasswordController',
    templateUrl: '/js/app/modules/user/recoverpassword.html'
  })

  // Dashboards
  .when('/dashboard', {
    controller: 'DashboardController',
    templateUrl: '/js/app/modules/dashboard/dashboard.html'
  });

  $locationProvider.html5Mode('true');

}); // end config

/*
 * Define the main Huna JS module
 */
var app = angular.module('HunaJS', ['ngRoute'])

/**
 * Get some routes in place
 */
.config(function($routeProvider, $locationProvider) {

  // Root
  $routeProvider
  .when('/', {
	  controller: 'MainController',
	  templateUrl: '/js/app/modules/main/main.html'
	})

  // error
  .when('/error', {
    controller: 'ErrorController',
    templateUrl: '/js/app/modules/error/error.html'
  })

  // User Routes
	.when('/user/register', {
		controller: 'RegisterController',
		templateUrl: '/js/app/modules/user/register.html'
	})
  .when('/user/register/activate', {
    controller: 'ActivateController',
    templateUrl: '/js/app/modules/user/activate.html'
  })
  .when('/user/login', {
    controller: 'LoginController',
    templateUrl: '/js/app/modules/user/login.html'
  })
  .when('/user/password', {
    controller: 'RecoverPasswordController',
    templateUrl: '/js/app/modules/user/recoverpassword.html'
  })

  // Dashboards
  .when('/dashboard', {
    controller: 'DashboardController',
    templateUrl: '/js/app/modules/dashboard/dashboard.html',
    resolve: {
      auth: ["$q", "LoginService", function($q, LoginService) {
        var userInfo = LoginService.getUserInfo();

        if (userInfo) {
          return $q.when(userInfo);
        } else {
          return $q.reject({ authenticated: false });
        }
      }]
    }
  });

  $locationProvider.html5Mode('true');

}); // end config

Use login service to secure route


/**
 * @ngdoc service
 * @name belajarOauth2ImplicitClientApp.BarangFactory
 * @description
 * # BarangFactory
 * Factory in the belajarOauth2ImplicitClientApp.
 */

(function() {
  'use strict';
  angular.module('belajarOauth2ImplicitClientApp')
    .factory('BarangFactory', BarangFactory);

  BarangFactory.$inject = ['$resource'];

  function BarangFactory($resource) {
    var barangFactory = this;

    barangFactory.getAllBarang = function() {
      return $resource('/api/barang');
    };

    return barangFactory;
  }
})();

/**
 * @ngdoc service
 * @name belajarOauth2ImplicitClientApp.BarangFactory
 * @description
 * # BarangFactory
 * Factory in the belajarOauth2ImplicitClientApp.
 */

(function() {
  'use strict';
  angular.module('belajarOauth2ImplicitClientApp')
    .factory('BarangFactory', BarangFactory);

  BarangFactory.$inject = ['$resource', 'BarangService'];

  function BarangFactory($resource, BarangService) {
    var barangFactory = this;

    barangFactory.getAllBarang = function(token) {
      console.log(BarangService.getBarangAll);
      return $resource(BarangService.getBarangAll + token);
    };

    return barangFactory;
  }
})();

Update Barang Factory with token


var mongoose, Schema, UserSchema;

mongoose = require('mongoose');

// criando um schema para o banco de dados
// ==========

Schema = mongoose.Schema;

UserSchema = new Schema ({
  name: {
    type: String,
    default: '',
    required: true
  },
  email: {
    type: String,
    default: '',
    required: true,
    uniq: true
  },
  gender: {
    type: String,
    default: '',
    required: true
  },
  social: {
    type: String,
    default: ''
  },
  social_id: {
    type: String,
    default: ''
  },
  picture: {
    type: String,
    default: ''
  },
  created_at: {
    type: Date,
    default: Date.now
  }
});

module.exports = mongoose.model('User', UserSchema);

var mongoose, Schema, UserSchema;

mongoose = require('mongoose');

// criando um schema para o banco de dados
// ==========

Schema = mongoose.Schema;

UserSchema = new Schema ({
  name: {
    type: String,
    default: '',
    required: true
  },
  email: {
    type: String,
    default: '',
    required: true,
    unique: true
  },
  gender: {
    type: String,
    default: ''
  },
  social: {
    type: String,
    default: ''
  },
  social_id: {
    type: String,
    default: ''
  },
  picture: {
    type: String,
    default: ''
  },
  created_at: {
    type: Date,
    default: Date.now
  }
});

module.exports = mongoose.model('User', UserSchema);

Remove o required do gender para modelo de usuário.


moj.Modules.ReAllocation = {
  $CheckedClaimIdTemplate: $('<input type="hidden" name="allocation[claim_ids][]">'),
  $form: $('#new_allocation'),

  init: function (){
    var self = this;

    //Only work on the re-allocation page
    if($('.js-reallocation-report').length > 0){
      $('.report').on('change', ':checkbox', function (){
        var $element = $(this);

        if($element.is(':checked')){
          self.addCheckedClaim($element.val());
        }else{
          //TODO investigate why this was needed?
          self.removeUnCheckedClaim($element.val());
        }
      });
    }
  },

  addCheckedClaim: function (claim_id){
    var $clonedElement = this.$CheckedClaimIdTemplate.clone();
    var id = 'allocation_claim_ids_' + claim_id;

    $clonedElement
      .attr('id', id)
      .val(claim_id);

    //Add the new element to the form
    this.$form.append($clonedElement);
  },

  removeUnCheckedClaim: function (claim_id){
    var $form = this.$form;

    $form
      .find('#allocation_claim_ids_' + claim_id)
      .remove();
  }

};

moj.Modules.ReAllocation = {
  $CheckedClaimIdTemplate: $('<input type="hidden" name="allocation[claim_ids][]">'),
  $form: $('#new_allocation'),
  $ReAllocationRadioButtons: $('.js-re-allocation-options'),
  $CaseWorkerList: $('.js-case-worker-list'),

  init: function (){
    var self = this;

    //Only work on the re-allocation page
    if($('.js-re-allocation-page').length > 0){

      //Show/Hide Case Workers select lists
      self.showHideCaseWorkersList();

      this.$ReAllocationRadioButtons.on('change', ':radio', function(){
        self.showHideCaseWorkersList();
      });

      //Selecting claims to be re-allocated functionality
      $('.report').on('change', ':checkbox', function (){
        var $element = $(this);

        if($element.is(':checked')){
          self.addCheckedClaim($element.val());
        }else{
          //TODO investigate why this was needed?
          self.removeUnCheckedClaim($element.val());
        }
      });
    }
  },

  showHideCaseWorkersList: function(){
    if(this.$ReAllocationRadioButtons.find(':checked').val() === 'false' ){
      this.$CaseWorkerList.slideDown();
    }else{
      this.$CaseWorkerList.slideUp();
    }
  },

  addCheckedClaim: function (claim_id){
    var $clonedElement = this.$CheckedClaimIdTemplate.clone();
    var id = 'allocation_claim_ids_' + claim_id;

    $clonedElement
      .attr('id', id)
      .val(claim_id);

    //Add the new element to the form
    this.$form.append($clonedElement);
  },

  removeUnCheckedClaim: function (claim_id){
    var $form = this.$form;

    $form
      .find('#allocation_claim_ids_' + claim_id)
      .remove();
  }

};

Add show/hide case worker select list based on what allocation option the user selected


(function ($) {
    'use strict';

    setNextActivities();
    function setNextActivities() {

        $.ajax({
            url: "/next_activities"
        }).done(function (data) {
            var str = '';
            $.each(data, function (index, activity) {

                if(new Date(activity.field_date_1)>new Date()) {
                    str += '
';
                    str += '' + activity.field_date + '	';
                    if (activity.field_lien && activity.field_lien != '') {
                        str += '' + activity.title + '	';
                    } else {
                        str += '' + activity.title + '	';
                    }
                    str += '' + activity.field_room + '	';
                    str += '
';
                }
            });
            $('.tournaments-home-next table').html(str);
            setTimeout(setNextActivities, 10 * 60 * 1000); //each 10 min
        });

    }
})(jQuery);

(function ($) {
    'use strict';

    setNextActivities();
    function setNextActivities() {

        $.ajax({
            url: "/next_activities"
        }).done(function (data) {
            var count = 0;
            var str = '';
            $.each(data, function (index, activity) {

                if(new Date(activity.field_date_1)>new Date() && count <=10) {
                    str += '
';
                    str += '' + activity.field_date + '	';
                    if (activity.field_lien && activity.field_lien != '') {
                        str += '' + activity.title + '	';
                    } else {
                        str += '' + activity.title + '	';
                    }
                    str += '' + activity.field_room + '	';
                    str += '
';
                    count ++;
                }
            });
            $('.tournaments-home-next table').html(str);
            setTimeout(setNextActivities, 10 * 60 * 1000); //each 10 min
        });

    }
})(jQuery);

Fix next activities bug (date filter D8 not operational)


Handlebars.registerHelper(
	"modes", function(mode) {
		var modes = {
			"~": "owner",
			"&": "admin",
			"@": "op",
			"%": "half-op",
			"+": "voice",
			"" : "normal"
		};
		return modes[mode];
	}
);

Handlebars.registerHelper(
	"modes", function(mode) {
		var modes = {
			"~": "owner",
			"&": "admin",
			"!": "admin",
			"@": "op",
			"%": "half-op",
			"+": "voice",
			"" : "normal"
		};
		return modes[mode];
	}
);

Add ! modechar to admin


import * as pdfjs from 'pdfjs-dist/build/pdf';
// eslint-disable-next-line import/no-unresolved
import pdfjsWorker from './pdf.worker.entry?url';

import Document from './Document';
import Outline from './Outline';
import Page from './Page';

import { displayWorkerWarning } from './shared/utils';

displayWorkerWarning();

pdfjs.GlobalWorkerOptions.workerSrc = pdfjsWorker;

export { pdfjs, Document, Outline, Page };

import * as pdfjs from 'pdfjs-dist/build/pdf';

import Document from './Document';
import Outline from './Outline';
import Page from './Page';

import { displayWorkerWarning } from './shared/utils';

displayWorkerWarning();

pdfjs.GlobalWorkerOptions.workerSrc = new URL('pdfjs-dist/build/pdf.worker.js', import.meta.url);

export { pdfjs, Document, Outline, Page };

Fix Vite specific entry causing fake worker to be initialized


if (document.head.dataset.piwikHost) {
  var _paq = _paq || [];
  /* tracker methods like "setCustomDimension" should be called before "trackPageView" */
  _paq.push(['trackPageView']);
  _paq.push(['enableLinkTracking']);
  (function() {
    var u=document.head.dataset.piwikHost;
    _paq.push(['setTrackerUrl', u+'piwik.php']);
    _paq.push(['setSiteId', '6']);
    var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
    g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
  })();

  document.addEventListener('click', function (e) {
    e = e || window.event;
    var target = e.target || e.srcElement;

    if (target.dataset.piwikAction) {
      _paq.push(['trackEvent', target.dataset.piwikAction, target.dataset.piwikName, target.dataset.piwikValue]);
    }
  }, false);
}

if (document.head.dataset.piwikHost) {
  window._paq = window._paq || [];
  /* tracker methods like "setCustomDimension" should be called before "trackPageView" */
  window._paq.push(['trackPageView']);
  window._paq.push(['enableLinkTracking']);
  (function() {
    var u=document.head.dataset.piwikHost;
    window._paq.push(['setTrackerUrl', u+'piwik.php']);
    window._paq.push(['setSiteId', '6']);
    var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
    g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
  })();

  document.addEventListener('click', function (e) {
    e = e || window.event;
    var target = e.target || e.srcElement;

    if (target.dataset.piwikAction) {
      window._paq.push(['trackEvent', target.dataset.piwikAction, target.dataset.piwikName, target.dataset.piwikValue]);
    }
  }, false);
}

Fix piwik by using window._paq


module.exports = function(r) {
    'use strict';
    return {
        getProjectsForUser: getProjectsForUser
    };

    ///////////////

    function *getProjectsForUser(user, isAdmin) {
        let rql;
        if (isAdmin) {
            rql = r.table('projects')
                .filter(r.row('owner').ne('delete@materialscommons.org'))
                .pluck('name').orderBy('name');
        } else {
            rql = r.table('access').getAll(user, {index: 'user_id'})
                .eqJoin('project_id', r.table('projects'))
                .zip()
                .pluck('name')
                .orderBy('name');
        }

        return yield rql.run();
    }
};

module.exports = function(r) {
    'use strict';
    let run = require('./run');
    return {
        forUser: forUser
    };

    ///////////////

    function forUser(user) {
        let rql;
        if (user.isAdmin) {
            rql = r.table('projects')
                .filter(r.row('owner').ne('delete@materialscommons.org'));
        } else {
            rql = r.table('access').getAll(user.id, {index: 'user_id'})
                .eqJoin('project_id', r.table('projects'))
                .zip();
        }

        rql = addComputed(rql);

        return run(rql);
    }

    // addComputed adds additional attributes to the rql that
    // that are computed from other tables.
    function addComputed(rql) {
        rql = rql.merge(function(project) {
            return {
                users: r.table('access')
                    .getAll(project('id'), {index: 'project_id'})
                    .map(function(access) {
                        return access.merge({
                            'user': access('user_id')
                        });
                    })
                    .pluck('user', 'permissions')
                    .coerceTo('array'),
                reviews: r.table('reviews')
                    .getAll(project('id'), {index: 'project_id'})
                    .coerceTo('array'),
                notes: r.table('notes')
                    .getAll(project('id'), {index: 'project_id'})
                    .coerceTo('array'),
                events: r.table('events')
                    .getAll(project('id'), {index: 'project_id'})
                    .coerceTo('array')
            };
        });

        return rql;
    }
};

Return a promise rather than a generator since you can always pass a promise to a generator. Rely on module name (golang style) for method names. The module makes the method name more understandable. General cleanup of the interface.


(function () {
    'use strict';

    angular
        .module('${moduleName}')
        .directive('${directiveName}', ${directiveName});

    ${directiveName}.${DS}inject = [${dependencyInjection}];

    /* @ngInject */
    function ${directiveName}(${dependency}) {
        var directive = {
            bindToController: true,
            controller: ${ControllerName},
            controllerAs: '${vm}',
            link: link,
            restrict: 'A',
            scope: {}
        };
        return directive;

        function link(scope, element, attrs) {

        }
    }

    ${ControllerName}.${DS}inject = [${dependencyInjection}];

    /* @ngInject */
    function ${ControllerName}(${dependency}) {

    }

})();

(function() {
    'use strict';

    angular
        .module('${moduleName}')
        .directive('${directiveName}', ${directiveName});

    ${directiveName}.${DS}inject = [${dependencyInjection}];

    /* @ngInject */
    function ${directiveName}(${dependency}) {
        var directive = {
            bindToController: true,
            controller: ${ControllerName},
            controllerAs: '${vm}',
            link: link,
            restrict: 'A',
            scope: {}
        };
        return directive;

        function link(scope, element, attrs) {

        }
    }

    ${ControllerName}.${DS}inject = [${dependencyInjection}];

    /* @ngInject */
    function ${ControllerName}(${dependency}) {

    }

})();

Remove unnecessary line break and space in IIFE


export default class FileLoader {
  // This returns ArrayBuffer
  static load (url, on_progress = () => {
  }) {
    return new Promise((resolve, reject) => {
      fetch(url).then(async (response) => {
        // Handle HTTP error
        if (response.status === 404) {
          reject('NOT_FOUND')
        } else if (response.status !== 200) {
          reject('HTTP_ERROR')
        }

        // For length calculation
        const length = +response.headers.get('Content-Length')
        const chunks = []
        let received = 0

        // Read incoming chunk
        const reader = response.body.getReader()
        while (true) {
          const { done, value } = await reader.read()
          if (done) {
            break
          }
          chunks.push(value)
          received += value.length

          // Progress event handler
          on_progress(received / length)
        }

        // Loaded, reassemble file
        const file = new Uint8Array(length)
        let offset = 0
        for (let chunk of chunks) {
          file.set(chunk, offset)
          offset += chunk.length
        }

        // Resolve promise to the file content array buffer
        resolve(file.buffer)
      }).catch(() => {
        reject('NETWORK_ERROR')
      })
    })
  }

  // Utility to decode ArrayBuffer to text
  static decode (buffer) {
    const decoder = new TextDecoder('utf8')
    return decoder.decode(buffer)
  }
}

export default class FileLoader {
  // This returns ArrayBuffer
  static load (url, on_progress = () => {
  }) {
    return new Promise((resolve, reject) => {
      fetch(url).then(async (response) => {
        // Handle HTTP error
        if (response.status === 404) {
          reject('NOT_FOUND')
        } else if (response.status !== 200) {
          reject('HTTP_ERROR')
        }

        // For length calculation
        const length = +response.headers.get('Content-Length')
        const chunks = []
        let received = 0

        // Read incoming chunk
        const reader = response.body.getReader()
        while (true) {
          const { done, value } = await reader.read()
          if (done) {
            break
          }
          chunks.push(value)
          received += value.length

          // Progress event handler
          on_progress(received / length)
        }

        // Loaded, reassemble file
        const file = new Uint8Array(received)
        let offset = 0
        for (let chunk of chunks) {
          file.set(chunk, offset)
          offset += chunk.length
        }

        // Resolve promise to the file content array buffer
        resolve(file.buffer)
      }).catch(() => {
        reject('NETWORK_ERROR')
      })
    })
  }

  // Utility to decode ArrayBuffer to text
  static decode (buffer) {
    const decoder = new TextDecoder('utf8')
    return decoder.decode(buffer)
  }
}

Fix error when no content-length is sent.


/*global chrome*/
export function init() {
  chrome.runtime.sendMessage({
    from: 'contentScript',
    type: 'loadEvent',
    loadState: 'load'
  });

  window.addEventListener('unload', () => {
    chrome.runtime.sendMessage({
      from: 'contentScript',
      type: 'loadEvent',
      loadState: 'unload'
    });
  });
}

function dispatchMouseEvent(target, ..args) {
  let e = document.createEvent('MouseEvents');
  e.initEvent.apply(e, args);
  target.dispatchEvent(e);
}

export function simulateClick(el) {
  let events = ['mouseover', 'mousedown', 'click', 'mouseup'];
  for (let event of events) {
    dispatchMouseEvent(el[0], event, true, true);
  }
}

/*global chrome*/
export function init() {
  chrome.runtime.sendMessage({
    from: 'contentScript',
    type: 'loadEvent',
    loadState: 'load'
  });

  window.addEventListener('unload', () => {
    chrome.runtime.sendMessage({
      from: 'contentScript',
      type: 'loadEvent',
      loadState: 'unload'
    });
  });
}

function dispatchMouseEvent(target) {
  let e = document.createEvent('MouseEvents');
  e.initEvent.apply(e, Array.prototype.slice.call(arguments, this.length));
  target.dispatchEvent(e);
}

export function simulateClick(el) {
  let events = ['mouseover', 'mousedown', 'click', 'mouseup'];
  for (let event of events) {
    dispatchMouseEvent(el[0], event, true, true);
  }
}

Use older variable arguments syntax


describe('Inverted Index Tests: ', function() {

  var index = new Index();
  var results;

  beforeEach(function(done) {
    index.createIndex('books.json', index.populateIndex).done(function(data) {
      results = data;

      // Invoke jasmine's done callback
      done();
    });
  });

  describe('Reads book data', function() {

    it('reads the JSON file successfully', function() {
      expect(results).not.toBeUndefined();
      expect(results.length).not.toEqual(0);
      expect(results.length).toEqual(2);
    })
  });

  describe('Populates Index', function() {

    it('creates the index once JSON file has been read', function() {
      expect(index.results).not.toEqual({});
    })
  });
});

describe('Inverted Index Tests: ', function() {

  var index = new Index();
  var filter = new Filter();
  var results;

  beforeEach(function(done) {
    index.createIndex('books.json', index.populateIndex).done(function(data) {
      results = data;

      // Invoke jasmine's done callback
      done();
    });
  });

  describe('Reads book data', function() {

    it('reads the JSON file successfully', function() {
      expect(results).not.toBeUndefined();
      expect(results.length).not.toEqual(0);
      expect(results.length).toEqual(2);
    });
  });

  describe('Populates Index', function() {

    it('creates the index once JSON file has been read', function() {
      expect(index.results).not.toEqual({});
    });

    it('creates an index containing only lowercase terms', function() {
      var keys = Object.keys(index.results);
      for (var i = 0, len = keys.length; i < len; i++) {
        expect(keys[i]).toBe(keys[i].toLowerCase());
      }
    });

    it('creates index strings without punctuation', function() {
      var keys = Object.keys(index.results);
      for (var i = 0, len = keys.length; i < len; i++) {
        expect(keys[i].indexOf('.')).toBe(-1);
        expect(keys[i].indexOf(',')).toBe(-1);
      }
    });

    it('creates index strings without stop words', function() {
      var keys = Object.keys(index.results);
      for (var i = 0, len = keys.length; i < len; i++) {
        expect(filter.stopWords.indexOf(keys[i])).toBe(-1);
      }
    });
  });
});

Add extra tests to verify index is correct


/*eslint-disable no-unused-vars*/
import "babel-polyfill"

import React from 'react'
import ReactDOM from 'react-dom'
import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'
// import sagaMonitor from './sagaMonitor'

import Counter from './components/Counter'
import reducer from './reducers'
import rootSaga from './sagas'

const sagaMiddleware = createSagaMiddleware(/* {sagaMonitor} */)
const store = createStore(
  reducer,
  applyMiddleware(sagaMiddleware)
)
sagaMiddleware.run(rootSaga)

const action = type => store.dispatch({type})

function render() {
  ReactDOM.render(
    <Counter
      value={store.getState()}
      onIncrement={() => action('INCREMENT')}
      onDecrement={() => action('DECREMENT')}
      onIncrementIfOdd={() => action('INCREMENT_IF_ODD')}
      onIncrementAsync={() => action('INCREMENT_ASYNC')} />,
    document.getElementById('root')
  )
}

render()
store.subscribe(render)

/*eslint-disable no-unused-vars*/
import "babel-polyfill"

import React from 'react'
import ReactDOM from 'react-dom'
import { createStore, applyMiddleware, compose } from 'redux'
import createSagaMiddleware from 'redux-saga'
// import sagaMonitor from './sagaMonitor'

import Counter from './components/Counter'
import reducer from './reducers'
import rootSaga from './sagas'

const sagaMiddleware = createSagaMiddleware(/* {sagaMonitor} */)
const store = createStore(
  reducer,
  compose(
    applyMiddleware(sagaMiddleware),
    window.devToolsExtension && window.devToolsExtension()
  )
)
sagaMiddleware.run(rootSaga)

const action = type => store.dispatch({type})

function render() {
  ReactDOM.render(
    <Counter
      value={store.getState()}
      onIncrement={() => action('INCREMENT')}
      onDecrement={() => action('DECREMENT')}
      onIncrementIfOdd={() => action('INCREMENT_IF_ODD')}
      onIncrementAsync={() => action('INCREMENT_ASYNC')} />,
    document.getElementById('root')
  )
}

render()
store.subscribe(render)

Apply extension's enhancer to saga-counter example