<commit_before>import _ from 'lodash';
import { postJSON } from '../core-data/api';
import { UPDATE_PAGE_LIST_DATA } from './mutationTypes';
import { searchRoute } from '../utils/references';

/**
 * update page list with data provided from pubsub
 * note: if called without data, this just updates the updateTime and user
 * (e.g. when saving components in the page)
 * @param  {object} store
 * @param  {object} [data]
 * @return {Promise}
 */
export function updatePageList(store, data = {}) {
  const prefix = _.get(store, 'state.site.prefix'),
    uri = _.get(store, 'state.page.uri');

  let title = _.get(store, 'state.page.listData.title'),
    authors = _.get(store, 'state.page.listData.authors'),
    updateTime = new Date(),
    users = _.get(store, 'state.page.listData.users') || [],
    currentUser = _.get(store, 'state.user.username');

  if (data.title) {
    title = data.title;
  }

  if (data.authors) {
    authors = data.authors;
  }

  if (!_.includes(users, currentUser)) {
    users.push(currentUser);
  }

  store.commit(UPDATE_PAGE_LIST_DATA, { title, authors, updateTime });

  return postJSON(`${prefix}/_pagelist`, {
    url: uri,
    value: {
      title,
      authors,
      updateTime,
      users
    }
  });
}

/**
 * get the list data for a specific page
 * note: if prefix is specified, this does NOT commit the data (only returns it),
 * allowing the preloader to use it when doing the initial preload of data
 * @param {object} store
 * @param  {string} uri
 * @param {string} [prefix] passed in when preloading (e.g. if site isn't in store yet)
 * @return {Promise}
 */
export function getListData(store, { uri, prefix }) {
  const sitePrefix = prefix || _.get(store, 'state.site.prefix');

  let query = {
    index: 'pages',
    type: 'general',
    body: {
      size: 1,
      query: {
        term: {
          uri // match page uri
        }
      }
    }
  };

  return postJSON(sitePrefix + searchRoute, query)
    .then((res) => _.get(res, 'hits.hits[0]._source'))
    .then((rawData = {}) => {
      let listData = _.pick(rawData, ['title', 'authors', 'updateTime', 'publishTime']);

      if (prefix) {
        return listData; // called by preloader, so it'll do a single big commit with the whole state
      } else {
        // simply commit the data into the store
        store.commit(UPDATE_PAGE_LIST_DATA, listData);
      }
    })
    .catch(() => {
      if (prefix) {
        return {};
      } else {
        store.commit(UPDATE_PAGE_LIST_DATA, {});
      }
    });
}
<commit_msg>save full user data<commit_after>import _ from 'lodash';
import { postJSON } from '../core-data/api';
import { UPDATE_PAGE_LIST_DATA } from './mutationTypes';
import { searchRoute } from '../utils/references';

/**
 * update page list with data provided from pubsub
 * note: if called without data, this just updates the updateTime and user
 * (e.g. when saving components in the page)
 * @param  {object} store
 * @param  {object} [data]
 * @return {Promise}
 */
export function updatePageList(store, data = {}) {
  const prefix = _.get(store, 'state.site.prefix'),
    uri = _.get(store, 'state.page.uri');

  let title = _.get(store, 'state.page.listData.title'),
    authors = _.get(store, 'state.page.listData.authors'),
    updateTime = new Date(),
    users = _.get(store, 'state.page.listData.users') ? _.cloneDeep(_.get(store, 'state.page.listData.users')) : [],
    currentUser = _.get(store, 'state.user'),
    userIndex = _.findIndex(users, (user) => user.username === currentUser.username);

  if (data.title) {
    title = data.title;
  }

  if (data.authors) {
    authors = data.authors;
  }

  if (userIndex === -1) {
    users.push(currentUser);
  } else if (userIndex > -1 && userIndex !== users.length - 1) {
    // if the current user is in the list, but isn't at the end, we should move it to the end
    users.splice(userIndex, 1); // remove user from list
    users.push(currentUser); // and add it to the end
  }

  store.commit(UPDATE_PAGE_LIST_DATA, { title, authors, updateTime, users });

  return postJSON(`${prefix}/_pagelist`, {
    url: uri,
    value: {
      title,
      authors,
      updateTime,
      users
    }
  });
}

/**
 * get the list data for a specific page
 * note: if prefix is specified, this does NOT commit the data (only returns it),
 * allowing the preloader to use it when doing the initial preload of data
 * @param {object} store
 * @param  {string} uri
 * @param {string} [prefix] passed in when preloading (e.g. if site isn't in store yet)
 * @return {Promise}
 */
export function getListData(store, { uri, prefix }) {
  const sitePrefix = prefix || _.get(store, 'state.site.prefix');

  let query = {
    index: 'pages',
    type: 'general',
    body: {
      size: 1,
      query: {
        term: {
          uri // match page uri
        }
      }
    }
  };

  return postJSON(sitePrefix + searchRoute, query)
    .then((res) => _.get(res, 'hits.hits[0]._source'))
    .then((rawData = {}) => {
      let listData = _.pick(rawData, ['title', 'authors', 'updateTime', 'publishTime', 'users']);

      if (prefix) {
        return listData; // called by preloader, so it'll do a single big commit with the whole state
      } else {
        // simply commit the data into the store
        store.commit(UPDATE_PAGE_LIST_DATA, listData);
      }
    })
    .catch(() => {
      if (prefix) {
        return {};
      } else {
        store.commit(UPDATE_PAGE_LIST_DATA, {});
      }
    });
}
<|endoftext|>


<commit_before>module.exports = {
  "root": true,
  "env": {
    "commonjs": true,
    "node": true,
    "es6": true,
    "mocha": true
  },
  "parserOptions": {
    "sourceType": "script",
    "allowImportExportEverywhere": true
  },
  "extends": ["eslint:recommended"],
  "rules": {
    "no-console": ["warn", {"allow": ["warn", "error"]}],
    "semi": ["error", "never"],
    "strict": ["error", "global"],
    "no-trailing-spaces": ["error", {"skipBlankLines": true}],
    "quotes": ["error", "single"],
    "no-template-curly-in-string": "error",
    "no-caller": "error",
    "yoda": "error",
    "eqeqeq": ["error", "always"],
    "brace-style": "error",
    "eol-last": "error",
    "indent": ["error", 2, {
      "SwitchCase": 1,
      "MemberExpression": 1,
      "outerIIFEBody": 1,
      "CallExpression": {"arguments": 1},
      "ArrayExpression": 1,
      "ObjectExpression": 1,
      "FunctionExpression": {"body": 1, "parameters": "first"}
    }],
    "comma-style": ["error", "last"],
    "no-var": "error",
    "array-bracket-spacing": ["error", "never"],
    "comma-spacing": ["error", {"before": false, "after": true}],
    "one-var": ["error", "never"],
    "no-extra-bind": "error",
    "no-multiple-empty-lines": "error",
    "no-multi-spaces": "error",
    "no-process-exit": "error",
    "space-in-parens": "error",
    "no-unused-vars": ["error", {"caughtErrors": "all"}],
    "key-spacing": "error",
    "space-infix-ops": ["error", {"int32Hint": false}],
    "no-loop-func": "error",
    "space-before-function-paren": ["error", "never"],
    "space-before-blocks": "error",
    "object-curly-spacing": "error",
    "keyword-spacing": ["error", {"after": true}],
  }
};<commit_msg>Update .eslintrc.js<commit_after>module.exports = {
  "root": true,
  "env": {
    "commonjs": true,
    "node": true,
    "es6": true,
    "mocha": true
  },
  "parserOptions": {
    "sourceType": "script",
    "allowImportExportEverywhere": true
  },
  "extends": ["eslint:recommended"],
  "rules": {
    "no-console": ["warn", {"allow": ["warn", "error"]}],
    "semi": ["error", "never"],
    "strict": ["error", "global"],
    "no-trailing-spaces": ["error", {"skipBlankLines": true}],
    "quotes": ["error", "single"],
    "no-template-curly-in-string": "error",
    "no-caller": "error",
    "yoda": "error",
    "eqeqeq": ["error", "always"],
    "brace-style": "error",
    "eol-last": "error",
    "indent": ["error", 2, {
      "SwitchCase": 1,
      "MemberExpression": 1,
      "outerIIFEBody": 1,
      "CallExpression": {"arguments": 1},
      "ArrayExpression": 1,
      "ObjectExpression": 1,
      "FunctionExpression": {"body": 1, "parameters": "first"}
    }],
    "comma-style": ["error", "last"],
    "no-var": "error",
    "array-bracket-spacing": ["error", "never"],
    "comma-spacing": ["error", {"before": false, "after": true}],
    "one-var": ["error", "never"],
    "no-extra-bind": "error",
    "no-multiple-empty-lines": "error",
    "no-multi-spaces": "error",
    "no-process-exit": "error",
    "space-in-parens": "error",
    "no-unused-vars": ["error", {"caughtErrors": "all", "args": "none"}],
    "key-spacing": "error",
    "space-infix-ops": ["error", {"int32Hint": false}],
    "no-loop-func": "error",
    "space-before-function-paren": ["error", "never"],
    "space-before-blocks": "error",
    "object-curly-spacing": "error",
    "keyword-spacing": ["error", {"after": true}],
  }
};<|endoftext|>


<commit_before>function bubbleSort(data) {
    var n = data.length;
    while (n != 0){
        var newn = 0;
        for (var i = 1; i <= n-1; i++){
            if (data[i-1] > data[i]) {
                //swap data[i-1] and data[i]
                var tmp = data[i-1];
                data[i-1] = data[i];
                data[i] = tmp;
                newn = i;
            }
        }
        n = newn;
    }
    return data;
}

function selectionSort(data) {
    var n = data.length;
    for (var i = 0; i < n - 1; i++) {
        var min = i;
        for (var j = i + 1; j < n; j++) {
            if (data[j] < data[min]){
                min = j;
            }
        }
        if (min != i) {
            var tmp = data[i];
            data[i] = data[min];
            data[min] = tmp;
        }
    }
    return data;
}

function mergeSort(data) {
    if (data.length < 2) {
        return data;
    }
    var mid = parseInt(data.length/2);
    var left = data.slice(0,mid);
    var right = data.slice(mid, data.length);

    return merge(mergeSort(left), mergeSort(right));
}

function merge(left, right) {
    var result = [];

    while (left.length && right.length) {
        if (left[0] <= right[0]) {
            result.push(left.shift());
        }
        else {
            result.push(right.shift());
        }
    }

    while(left.length) {
        result.push(left.shift());
    }

    while(right.length) {
        result.push(right.shift());
    }

    return result;
}

function insertionSort(data) {
    for (var i = 1; i < data.length; i++) {
        var x = data[i];
        var j = i - 1;
        while (j >= 0 && data[j] > x) {
            data[j+1] = data[j];
            j = j - 1;
        }
        data[j+1] = x;
    }
    return data;
}

function quickSort(data, left, right) {
    var len = data.length, pivot, partitionIndex;

    if (left < right) {
        pivot = right;
        partitionIndex = partition(data, pivot, left, right);
        quickSort(data, left, partitionIndex - 1);
        quickSort(data, partitionIndex + 1, right);
    }

    return data;
}

function partition(data, pivot, left, right) {
    var pivotValue = data[pivot], partitionIndex = left;

    for (var i = left; i < right; i++) {
        if (data[i] < pivotValue) {
            var tmp = data[i];
            data[i] = data[partitionIndex];
            data[partitionIndex] = tmp;
            partitionIndex++;
        }
    }
    var tmp = data[right];
    data[right] = data[partitionIndex];
    data[partitionIndex] = tmp;
    return partitionIndex;
}<commit_msg>Added CocktailShaker Sort<commit_after>function bubbleSort(data) {
    var n = data.length;
    while (n != 0){
        var newn = 0;
        for (var i = 1; i <= n-1; i++){
            if (data[i-1] > data[i]) {
                //swap data[i-1] and data[i]
                var tmp = data[i-1];
                data[i-1] = data[i];
                data[i] = tmp;
                newn = i;
            }
        }
        n = newn;
    }
    return data;
}

function selectionSort(data) {
    var n = data.length;
    for (var i = 0; i < n - 1; i++) {
        var min = i;
        for (var j = i + 1; j < n; j++) {
            if (data[j] < data[min]){
                min = j;
            }
        }
        if (min != i) {
            var tmp = data[i];
            data[i] = data[min];
            data[min] = tmp;
        }
    }
    return data;
}

function mergeSort(data) {
    if (data.length < 2) {
        return data;
    }
    var mid = parseInt(data.length/2);
    var left = data.slice(0,mid);
    var right = data.slice(mid, data.length);

    return merge(mergeSort(left), mergeSort(right));
}

function merge(left, right) {
    var result = [];

    while (left.length && right.length) {
        if (left[0] <= right[0]) {
            result.push(left.shift());
        }
        else {
            result.push(right.shift());
        }
    }

    while(left.length) {
        result.push(left.shift());
    }

    while(right.length) {
        result.push(right.shift());
    }

    return result;
}

function insertionSort(data) {
    for (var i = 1; i < data.length; i++) {
        var x = data[i];
        var j = i - 1;
        while (j >= 0 && data[j] > x) {
            data[j+1] = data[j];
            j = j - 1;
        }
        data[j+1] = x;
    }
    return data;
}

function quickSort(data, left, right) {
    var len = data.length, pivot, partitionIndex;

    if (left < right) {
        pivot = right;
        partitionIndex = partition(data, pivot, left, right);
        quickSort(data, left, partitionIndex - 1);
        quickSort(data, partitionIndex + 1, right);
    }

    return data;
}

/*
* Cocktail Shaker Sorting algorithm
*/
function cocktailShakerSort(data){
    //Main loop
    for(var i = 0; i < data.length/2; i++){
        var swapped = false;

        //Sorts next, unsorted largest-value
        for(var j = i; j < data.length - i - 1; j++){
            if(data[i] < data[j+1]){
                var tmp = data[j];
                data[j] = data[j+1];
                data[j+1] = tmp;
                swapped = true;
            }
        }

        //Sorts next, unsorted smallest-value
        for(var j = data.length - i - 2; j > i; j--){
            if(data[j] > data[j-1]){
                var tmp = data[j];
                data[j] = data[j-1];
                data[j-1] = tmp;
                swapped = true;
            }
        }

        if(!swapped) break; //If finished sorting
    }
}

function partition(data, pivot, left, right) {
    var pivotValue = data[pivot], partitionIndex = left;

    for (var i = left; i < right; i++) {
        if (data[i] < pivotValue) {
            var tmp = data[i];
            data[i] = data[partitionIndex];
            data[partitionIndex] = tmp;
            partitionIndex++;
        }
    }
    var tmp = data[right];
    data[right] = data[partitionIndex];
    data[partitionIndex] = tmp;
    return partitionIndex;
}<|endoftext|>


<commit_before>export default `
14-09-2017 - Added atonement sources breakdown. This isn't very accurate yet, it's just showing the way it's assigned right now. Improved accuracy will come later. (by Zerotorescue)
22-07-2017 - Added mana saved from the legendary Inner Hallation. (by hassebewlen)
18-06-2017 - Added Touch of the Grave statistic. (by Zerotorescue)
17-06-2017 - Evangelism casts are now also shown under the cooldowns tab. Rapture now shows the total abosrbs applied and the amount of damage absorbed. Fixed a few issues that caused too much healing to be assigned to Evangelism. (by Zerotorescue)
15-06-2017 - Fixed Wasted Penance bolts always assumed user had the Castigation talent. (by Zerotorescue)
15-06-2017 - Disabled suggestions for Pain Suppression and Power Word: Barrier. (by Zerotorescue)
11-06-2017 - Added extra suggestion to Power Word: Shield description to add distinction to casts during Rapture. (by milesoldenburg)
05-06-2017 - Fix Atonement duration in cast efficiency not accounting for the Doomsayer trait. (by Zerotorescue)
29-05-2017 - Show Rapture PW:S casts seperate from regular PW:S casts in Cast Efficiency. (by Zerotorescue)
28-05-2017 - Added unused Power Word: Shield absorb statistic.
25-05-2017 - Added Early Atonement refreshes statistic. Fixed Skjoldr sometimes not working properly. Both contributions were created by @Shadowdrizz. Thanks a lot for your contribution!
Fix Shadowfiend showing with the Mindbender talent.
18-05-2017 - Added Shadow Word: Pain/Purge the Wicked global uptime statistic.
17-05-2017 - Added Twist of Fate healing statistic (damage gain is in the tooltip).
16-05-2017 - Added N'ero, Band of Promises statistic.
15-05-2017 - Added Xalan the Feared's Clench statistic.
15-05-2017 - Skjoldr, Sanctuary of Ivagont statistic now includes the healing gained via Share in the Light.
14-05-2017 - Added Skjoldr, Sanctuary of Ivagont statistic. This does not yet include the healing gained via Share in the Light.
14-05-2017 - Added Cord of Maiev, Priestess of the Moon statistic.
14-05-2017 - Added Disc Priest 2 set bonus statistic.
14-05-2017 - Renamed Missed penance hits to Wasted Penance bolts. Wasted Penance bolts now accounts for (combat log) latency. Fixed Glyph of the Sha's Shadowfiend not being counted towards Shadowfiend casts. Fixed healing increases (most notably the 15% from Velen's) not working with Disc priest spells.
05-06-2017 - Discipline Priest: Fixed issue where critical atonement healing was not being counted, fixed Nero's Band of Promises being broken. (By Reglitch)
14-05-2017 - Added Discipline Priest spec. Currently includes basic statistics for Dead GCD time (should be fully operational), shared legendaries, missed Penance hits, cast efficiencies and the other build in tools.
`;
<commit_msg>Update changelog<commit_after>export default `
14-09-2017 - Fix issue where friendly fire damage was considered a valid Atonement damage source. (by Zerotorescue)
14-09-2017 - Added atonement sources breakdown. This isn't very accurate yet, it's just showing the way it's assigned right now. Improved accuracy will come later. (by Zerotorescue)
22-07-2017 - Added mana saved from the legendary Inner Hallation. (by hassebewlen)
18-06-2017 - Added Touch of the Grave statistic. (by Zerotorescue)
17-06-2017 - Evangelism casts are now also shown under the cooldowns tab. Rapture now shows the total abosrbs applied and the amount of damage absorbed. Fixed a few issues that caused too much healing to be assigned to Evangelism. (by Zerotorescue)
15-06-2017 - Fixed Wasted Penance bolts always assumed user had the Castigation talent. (by Zerotorescue)
15-06-2017 - Disabled suggestions for Pain Suppression and Power Word: Barrier. (by Zerotorescue)
11-06-2017 - Added extra suggestion to Power Word: Shield description to add distinction to casts during Rapture. (by milesoldenburg)
05-06-2017 - Fix Atonement duration in cast efficiency not accounting for the Doomsayer trait. (by Zerotorescue)
29-05-2017 - Show Rapture PW:S casts seperate from regular PW:S casts in Cast Efficiency. (by Zerotorescue)
28-05-2017 - Added unused Power Word: Shield absorb statistic.
25-05-2017 - Added Early Atonement refreshes statistic. Fixed Skjoldr sometimes not working properly. Both contributions were created by @Shadowdrizz. Thanks a lot for your contribution!
Fix Shadowfiend showing with the Mindbender talent.
18-05-2017 - Added Shadow Word: Pain/Purge the Wicked global uptime statistic.
17-05-2017 - Added Twist of Fate healing statistic (damage gain is in the tooltip).
16-05-2017 - Added N'ero, Band of Promises statistic.
15-05-2017 - Added Xalan the Feared's Clench statistic.
15-05-2017 - Skjoldr, Sanctuary of Ivagont statistic now includes the healing gained via Share in the Light.
14-05-2017 - Added Skjoldr, Sanctuary of Ivagont statistic. This does not yet include the healing gained via Share in the Light.
14-05-2017 - Added Cord of Maiev, Priestess of the Moon statistic.
14-05-2017 - Added Disc Priest 2 set bonus statistic.
14-05-2017 - Renamed Missed penance hits to Wasted Penance bolts. Wasted Penance bolts now accounts for (combat log) latency. Fixed Glyph of the Sha's Shadowfiend not being counted towards Shadowfiend casts. Fixed healing increases (most notably the 15% from Velen's) not working with Disc priest spells.
05-06-2017 - Discipline Priest: Fixed issue where critical atonement healing was not being counted, fixed Nero's Band of Promises being broken. (By Reglitch)
14-05-2017 - Added Discipline Priest spec. Currently includes basic statistics for Dead GCD time (should be fully operational), shared legendaries, missed Penance hits, cast efficiencies and the other build in tools.
`;
<|endoftext|>


<commit_before>/* VisibilityIcon - visually represent public vs private */
import PropTypes from 'prop-types';
import React from 'react';

import MIcon from 'components/MIcon';

const toIcon = {
  private: 'lock',
  team: 'people',
};

export default function VisibilityIcon({ drop = false, label }) {
  return (
    <MIcon drop={drop ? '4px' : undefined} style={{ opacity: 0.5 }} title={label}>
      {toIcon[label]}
    </MIcon>
  );
}

VisibilityIcon.propTypes = {
  drop: PropTypes.bool,
  label: PropTypes.string,
};
<commit_msg>tweak transparency on privacy icons<commit_after>/* VisibilityIcon - visually represent public vs private */
import PropTypes from 'prop-types';
import React from 'react';

import MIcon from 'components/MIcon';

const toIcon = {
  private: 'lock',
  team: 'people',
};

export default function VisibilityIcon({ drop = false, label }) {
  return (
    <MIcon drop={drop ? '4px' : undefined} style={{ opacity: 0.3 }} title={label}>
      {toIcon[label]}
    </MIcon>
  );
}

VisibilityIcon.propTypes = {
  drop: PropTypes.bool,
  label: PropTypes.string,
};
<|endoftext|>


<commit_before>/**
 * @param  {Grunt} grunt
 */
module.exports = function(grunt) {
  grunt.initConfig({
    uglify: {
      main: {
        files: {
          'bin/minjector.min.js': ['src/minjector.js']
        },
        options: {
          'screw-ie8': true,
          'mangle': {
            'sort': true,
            'toplevel': true
          },
          'compress': true,
          'lint': true
        }
      }
    },

    exec: {
      rmgzip: {
        command: 'rm bin/minjector.min.js.gz'
      },
      gzip: {
        command: '7z a -aoa -tgzip bin/minjector.min.js.gz bin/minjector.min.js'
      }
    },

    watch: {
      configFiles: {
        files: ['Gruntfile.js'],
        options: {
          reload: true
        }
      },
      options: {
        livereload: {
          port: 35732
        }
      },
      files: [
        'tests/specs/MinjectorSpec.js',
        'src/minjector.js'
        // 'bin/jsx.js'
      ],
      tasks: [
        'uglify',
        'exec:rmgzip',
        'exec:gzip'
      ]
    }
  });

  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-exec');

  grunt.registerTask('default', ['watch']);
  grunt.registerTask('compile', ['uglify', 'exec']);

};
<commit_msg>Gruntfile: Rename task "compile" to "build".<commit_after>/**
 * @param  {Grunt} grunt
 */
module.exports = function(grunt) {
  grunt.initConfig({
    uglify: {
      main: {
        files: {
          'bin/minjector.min.js': ['src/minjector.js']
        },
        options: {
          'screw-ie8': true,
          'mangle': {
            'sort': true,
            'toplevel': true
          },
          'compress': true,
          'lint': true
        }
      }
    },

    exec: {
      rmgzip: {
        command: 'rm bin/minjector.min.js.gz'
      },
      gzip: {
        command: '7z a -aoa -tgzip bin/minjector.min.js.gz bin/minjector.min.js'
      }
    },

    watch: {
      configFiles: {
        files: ['Gruntfile.js'],
        options: {
          reload: true
        }
      },
      options: {
        livereload: {
          port: 35732
        }
      },
      files: [
        'tests/specs/MinjectorSpec.js',
        'src/minjector.js'
        // 'bin/jsx.js'
      ],
      tasks: [
        'uglify',
        'exec:rmgzip',
        'exec:gzip'
      ]
    }
  });

  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-exec');

  grunt.registerTask('default', ['watch']);
  grunt.registerTask('build', ['uglify', 'exec']);

};
<|endoftext|>


<commit_before>'use strict';

var gcm     = require('./chat/gcm'),
    request = require('request'),
    colog   = require('colog');

var pushMessage = function(id, payload) {
  // Define Cypher query for user's tokens
  var query = 'MATCH (:User {id: {id}})<-[:PUSH_TO]-(token:Push) ' +
              'RETURN token';
  var params = { id: id };

  // Query neo4j instance
  request.post({
    uri: process.env['NEO4J_URL'] || 'http://localhost:7474/',
    json: { query: query, params: params }
  }, function(err, r, body) {
    //Collect tokens
    var tokens = [];
    for (var i = 0; i < body.data.length; i++) {
      if (body.data[i][0].data.type === 'gcm') {
        tokens.push(body.data[i][0].data.token);
      }
    }

    // Push message to all tokens
    var message = new gcm.Message({
      collapseKey: 'demo',
      delayWhileIdle: true,
      timeToLive: 3,
      data: payload
    });

    gcm.sender.send(message, tokens, 4, function (err) {
      colog.warning('Sent push over GCM: ' + err);
    });
  });

};

var postChat = function(req, res) {
  var destinationId = req.params.destination;
  var payload = {
    message: req.body.message,
    timestamp: req.body.timestamp
  };

  pushMessage(destinationId, payload);

  res.status(200);
  res.end();
};

var match = function(rick, morty) {
  pushMessage(rick, { match: morty });
  pushMessage(morty, { match: rick });
};

module.exports = function (app) {
  // TODO: Integrate into swagger API/docs
  app.post('/chat/:destination', postChat);
  app.post('/api/v0/chat/:destination', postChat);
};

module.exports.match = match;
<commit_msg>Fixed db uri in chat.js<commit_after>'use strict';

var gcm     = require('./chat/gcm'),
    request = require('request'),
    colog   = require('colog');

var pushMessage = function(id, payload) {
  // Define Cypher query for user's tokens
  var query = 'MATCH (:User {id: {id}})<-[:PUSH_TO]-(token:Push) ' +
              'RETURN token';
  var params = { id: id };

  // Query neo4j instance
  request.post({
    uri: (process.env['NEO4J_URL'] || 'http://localhost:7474/') + 'db/data/cypher',
    json: { query: query, params: params }
  }, function(err, r, body) {
    //Collect tokens
    var tokens = [];
    for (var i = 0; i < body.data.length; i++) {
      if (body.data[i][0].data.type === 'gcm') {
        tokens.push(body.data[i][0].data.token);
      }
    }

    // Push message to all tokens
    var message = new gcm.Message({
      collapseKey: 'demo',
      delayWhileIdle: true,
      timeToLive: 3,
      data: payload
    });

    gcm.sender.send(message, tokens, 4, function (err) {
      colog.warning('Sent push over GCM: ' + err);
    });
  });

};

var postChat = function(req, res) {
  var destinationId = req.params.destination;
  var payload = {
    message: req.body.message,
    timestamp: req.body.timestamp
  };

  pushMessage(destinationId, payload);

  res.status(200);
  res.end();
};

var match = function(rick, morty) {
  pushMessage(rick, { match: morty });
  pushMessage(morty, { match: rick });
};

module.exports = function (app) {
  // TODO: Integrate into swagger API/docs
  app.post('/chat/:destination', postChat);
  app.post('/api/v0/chat/:destination', postChat);
};

module.exports.match = match;
<|endoftext|>


<commit_before>const {generateDoc} = require('./grunt/grunt-generate-doc');
const {generateTsd} = require('./grunt/grunt-generate-ts');
const {copyExamples} = require('./grunt/grunt-copy-examples');

module.exports = function(grunt) {

  let pkg = grunt.file.readJSON('package.json');
  let version = pkg.version;
  let release = grunt.option('release');
  if (!release) {
    version += '-dev.' + grunt.template.today('yyyymmdd+HHMM');
  }
  let banner = blockComment('Tabris.js ' + version + '\n\n' + grunt.file.read('LICENSE'));

  grunt.log.writeln('Building version ' + version);

  grunt.initConfig({
    version,
    clean: ['build'],
    concat: {
      tabris: {
        options: {
          banner,
          process: src => src.replace(/\${VERSION}/g, version)
        },
        src: ['build/tabris-transpiled.js'],
        dest: 'build/tabris/tabris.js'
      },
      boot: {
        options: {
          banner,
          process: src => src.replace(/\${VERSION}/g, version)
        },
        src: ['build/boot-transpiled.js'],
        dest: 'build/boot.js'
      },
      typings: {
        src: [
          'typings/whatwg-fetch.d.ts',
          'typings/JSX.d.ts',
          'typings/timer.d.ts',
          'typings/console.d.ts',
          'typings/localStorage.d.ts',
          'typings/XMLHttpRequest.d.ts',
          'typings/Event.d.ts',
          'typings/WebSocket.d.ts',
          'typings/EventObject.d.ts'
        ],
        dest: 'build/tabris/globals.d.ts'
      },
    },
    doc: {
      api: 'doc/api/**/*.json',
      typings: 'typings/propertyTypes.d.ts',
      target: 'build/doc/'
    },
    copy: {
      doc: {
        expand: true,
        cwd: 'doc/',
        src: ['*.md', 'api/*.md', 'api/img/**/*.*', 'img/*.*', 'toc.yml'],
        dest: 'build/doc/'
      },
      readme: {
        src: 'README.md',
        dest: 'build/tabris/'
      },
      test_ts: {
        expand: true,
        cwd: 'test/typescript/',
        src: ['**/*.test.ts', '**/*.test.tsx', 'package.json', 'tsconfig.json'],
        dest: 'build/typescript/'
      }
    },
    exec: {
      verify_typings: {
        cmd: 'npm install && node node_modules/typescript/bin/tsc -p . --noImplicitAny',
        cwd: 'build/typescript'
      },
      test_boot: {
        cmd: 'node node_modules/mocha/bin/mocha --colors --compilers js:babel-core/register "test/boot/**/*.test.js"'
      },
      verify_tabris: {
        cmd: 'node node_modules/mocha/bin/mocha --colors "test/**/*.verify.js"'
      },
      test_tabris: {
        cmd: 'node node_modules/mocha/bin/mocha --colors --compilers js:babel-core/register "test/tabris/**/*.test.js"'
      },
      test_spec: {
        cmd: `node node_modules/mocha/bin/mocha --colors --compilers js:babel-core/register "${grunt.option('spec')}"`
      },
      eslint: {
        cmd: 'node node_modules/eslint/bin/eslint.js --color .'
      },
      tslint: {
        cmd: 'node node_modules/tslint/bin/tslint --exclude "**/*.d.ts" "examples/**/*.ts" "test/**/*.ts"'
      },
      bundle_tabris: {
        cmd: 'node node_modules/rollup/bin/rollup --config rollup.config.js --format=cjs ' +
          '--output=build/tabris-bundle.js -- src/tabris/main.js'
      },
      bundle_boot: {
        cmd: 'node node_modules/rollup/bin/rollup --format=cjs --output=build/boot-bundle.js -- src/boot/main.js'
      },
      transpile_tabris: {
        cmd: 'node node_modules/babel-cli/bin/babel.js --compact false ' +
          '--out-file build/tabris-transpiled.js build/tabris-bundle.js',
        options: {
          env: Object.assign({}, process.env, {BABEL_ENV: 'build'})
        }
      },
      uglify_tabris: {
        cmd: 'node node_modules/uglify-es/bin/uglifyjs --mangle --keep-fnames --compress ' +
          '-o build/tabris/tabris.min.js build/tabris/tabris.js'
      },
      transpile_boot: {
        cmd: 'node node_modules/babel-cli/bin/babel.js --compact false ' +
          '--out-file build/boot-transpiled.js build/boot-bundle.js',
        options: {
          env: Object.assign({}, process.env, {BABEL_ENV: 'build'})
        }
      },
      uglify_boot: {
        cmd: 'node node_modules/uglify-es/bin/uglifyjs --mangle --compress -o build/boot.min.js build/boot.js'
      }
    }
  });

  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-copy');
  grunt.loadNpmTasks('grunt-exec');

  grunt.registerTask('copy-examples', () => {
    copyExamples('examples', 'build/examples');
  });

  grunt.registerTask('generate-doc', () => {
    let targetPath = grunt.config('doc').target;
    let files = grunt.file.expand(grunt.config('doc').api);
    generateDoc({files, targetPath, version});
  });

  grunt.registerTask('generate-tsd', () => {
    let files = grunt.file.expand(grunt.config('doc').api);
    let typings = grunt.file.read(grunt.config('doc').typings);
    generateTsd({files, typings, version});
  });

  /* runs static code analysis tools */
  grunt.registerTask('lint', [
    'exec:eslint',
    'exec:tslint'
  ]);

  grunt.registerTask('package', 'create package.json', () => {
    let stringify = require('format-json');
    let pack = grunt.file.readJSON('package.json');
    delete pack.devDependencies;
    pack.main = 'tabris.min.js';
    pack.typings = 'tabris.d.ts';
    pack.version = version;
    grunt.file.write('build/tabris/package.json', stringify.plain(pack));
  });

  /* concatenates and minifies code */
  grunt.registerTask('build', [
    'exec:bundle_tabris',
    'exec:transpile_tabris',
    'concat:tabris',
    'exec:uglify_tabris',
    'exec:bundle_boot',
    'exec:transpile_boot',
    'concat:boot',
    'exec:uglify_boot',
    'package',
    'copy:readme',
    'concat:typings',
    'generate-tsd'
  ]);

  grunt.registerTask('test', [
    'exec:test_boot',
    'exec:test_tabris'
  ]);

  /* runs tests against the build output */
  grunt.registerTask('verify', [
    'exec:verify_tabris',
    'copy:test_ts',
    'exec:verify_typings'
  ]);

  /* generates reference documentation */
  grunt.registerTask('doc', [
    'copy:doc',
    'generate-doc'
  ]);

  grunt.registerTask('default', [
    'clean',
    'lint',
    'test',
    'build',
    'verify',
    'doc',
    'copy-examples'
  ]);

  function blockComment(text) {
    let commented = text.trim().split('\n').map(line => ' * ' + line).join('\n');
    return '/*!\n' + commented + '\n */\n';
  }

};
<commit_msg>Include boot.js in tabris module<commit_after>const {generateDoc} = require('./grunt/grunt-generate-doc');
const {generateTsd} = require('./grunt/grunt-generate-ts');
const {copyExamples} = require('./grunt/grunt-copy-examples');

module.exports = function(grunt) {

  let pkg = grunt.file.readJSON('package.json');
  let version = pkg.version;
  let release = grunt.option('release');
  if (!release) {
    version += '-dev.' + grunt.template.today('yyyymmdd+HHMM');
  }
  let banner = blockComment('Tabris.js ' + version + '\n\n' + grunt.file.read('LICENSE'));

  grunt.log.writeln('Building version ' + version);

  grunt.initConfig({
    version,
    clean: ['build'],
    concat: {
      tabris: {
        options: {
          banner,
          process: src => src.replace(/\${VERSION}/g, version)
        },
        src: ['build/tabris-transpiled.js'],
        dest: 'build/tabris/tabris.js'
      },
      boot: {
        options: {
          banner,
          process: src => src.replace(/\${VERSION}/g, version)
        },
        src: ['build/boot-transpiled.js'],
        dest: 'build/tabris/boot.js'
      },
      typings: {
        src: [
          'typings/whatwg-fetch.d.ts',
          'typings/JSX.d.ts',
          'typings/timer.d.ts',
          'typings/console.d.ts',
          'typings/localStorage.d.ts',
          'typings/XMLHttpRequest.d.ts',
          'typings/Event.d.ts',
          'typings/WebSocket.d.ts',
          'typings/EventObject.d.ts'
        ],
        dest: 'build/tabris/globals.d.ts'
      },
    },
    doc: {
      api: 'doc/api/**/*.json',
      typings: 'typings/propertyTypes.d.ts',
      target: 'build/doc/'
    },
    copy: {
      doc: {
        expand: true,
        cwd: 'doc/',
        src: ['*.md', 'api/*.md', 'api/img/**/*.*', 'img/*.*', 'toc.yml'],
        dest: 'build/doc/'
      },
      readme: {
        src: 'README.md',
        dest: 'build/tabris/'
      },
      test_ts: {
        expand: true,
        cwd: 'test/typescript/',
        src: ['**/*.test.ts', '**/*.test.tsx', 'package.json', 'tsconfig.json'],
        dest: 'build/typescript/'
      }
    },
    exec: {
      verify_typings: {
        cmd: 'npm install && node node_modules/typescript/bin/tsc -p . --noImplicitAny',
        cwd: 'build/typescript'
      },
      test_boot: {
        cmd: 'node node_modules/mocha/bin/mocha --colors --compilers js:babel-core/register "test/boot/**/*.test.js"'
      },
      verify_tabris: {
        cmd: 'node node_modules/mocha/bin/mocha --colors "test/**/*.verify.js"'
      },
      test_tabris: {
        cmd: 'node node_modules/mocha/bin/mocha --colors --compilers js:babel-core/register "test/tabris/**/*.test.js"'
      },
      test_spec: {
        cmd: `node node_modules/mocha/bin/mocha --colors --compilers js:babel-core/register "${grunt.option('spec')}"`
      },
      eslint: {
        cmd: 'node node_modules/eslint/bin/eslint.js --color .'
      },
      tslint: {
        cmd: 'node node_modules/tslint/bin/tslint --exclude "**/*.d.ts" "examples/**/*.ts" "test/**/*.ts"'
      },
      bundle_tabris: {
        cmd: 'node node_modules/rollup/bin/rollup --config rollup.config.js --format=cjs ' +
          '--output=build/tabris-bundle.js -- src/tabris/main.js'
      },
      bundle_boot: {
        cmd: 'node node_modules/rollup/bin/rollup --format=cjs --output=build/boot-bundle.js -- src/boot/main.js'
      },
      transpile_tabris: {
        cmd: 'node node_modules/babel-cli/bin/babel.js --compact false ' +
          '--out-file build/tabris-transpiled.js build/tabris-bundle.js',
        options: {
          env: Object.assign({}, process.env, {BABEL_ENV: 'build'})
        }
      },
      uglify_tabris: {
        cmd: 'node node_modules/uglify-es/bin/uglifyjs --mangle --keep-fnames --compress ' +
          '-o build/tabris/tabris.min.js build/tabris/tabris.js'
      },
      transpile_boot: {
        cmd: 'node node_modules/babel-cli/bin/babel.js --compact false ' +
          '--out-file build/boot-transpiled.js build/boot-bundle.js',
        options: {
          env: Object.assign({}, process.env, {BABEL_ENV: 'build'})
        }
      },
      uglify_boot: {
        cmd: 'node node_modules/uglify-es/bin/uglifyjs --mangle --compress ' +
          '-o build/tabris/boot.min.js build/tabris/boot.js'
      }
    }
  });

  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-copy');
  grunt.loadNpmTasks('grunt-exec');

  grunt.registerTask('copy-examples', () => {
    copyExamples('examples', 'build/examples');
  });

  grunt.registerTask('generate-doc', () => {
    let targetPath = grunt.config('doc').target;
    let files = grunt.file.expand(grunt.config('doc').api);
    generateDoc({files, targetPath, version});
  });

  grunt.registerTask('generate-tsd', () => {
    let files = grunt.file.expand(grunt.config('doc').api);
    let typings = grunt.file.read(grunt.config('doc').typings);
    generateTsd({files, typings, version});
  });

  /* runs static code analysis tools */
  grunt.registerTask('lint', [
    'exec:eslint',
    'exec:tslint'
  ]);

  grunt.registerTask('package', 'create package.json', () => {
    let stringify = require('format-json');
    let pack = grunt.file.readJSON('package.json');
    delete pack.devDependencies;
    pack.main = 'tabris.min.js';
    pack.typings = 'tabris.d.ts';
    pack.version = version;
    grunt.file.write('build/tabris/package.json', stringify.plain(pack));
  });

  /* concatenates and minifies code */
  grunt.registerTask('build', [
    'exec:bundle_tabris',
    'exec:transpile_tabris',
    'concat:tabris',
    'exec:uglify_tabris',
    'exec:bundle_boot',
    'exec:transpile_boot',
    'concat:boot',
    'exec:uglify_boot',
    'package',
    'copy:readme',
    'concat:typings',
    'generate-tsd'
  ]);

  grunt.registerTask('test', [
    'exec:test_boot',
    'exec:test_tabris'
  ]);

  /* runs tests against the build output */
  grunt.registerTask('verify', [
    'exec:verify_tabris',
    'copy:test_ts',
    'exec:verify_typings'
  ]);

  /* generates reference documentation */
  grunt.registerTask('doc', [
    'copy:doc',
    'generate-doc'
  ]);

  grunt.registerTask('default', [
    'clean',
    'lint',
    'test',
    'build',
    'verify',
    'doc',
    'copy-examples'
  ]);

  function blockComment(text) {
    let commented = text.trim().split('\n').map(line => ' * ' + line).join('\n');
    return '/*!\n' + commented + '\n */\n';
  }

};
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2016, Globo.com (https://github.com/globocom)
 *
 * License: MIT
 */

import React from "react";

import { MegadraftEditor } from "../../src/Megadraft";
import { editorStateToJSON, editorStateFromRaw } from "../../src/utils";
import { highlightCode } from "./highlightCode";

import INITIAL_CONTENT from "./contentExample";

import relatedArticles from "megadraft-related-articles-plugin";
import image from "../../src/plugins/image/plugin";
import video from "../../src/plugins/video/plugin";

class Example extends React.Component {
  constructor(props) {
    super(props);
    const content = editorStateFromRaw(INITIAL_CONTENT);
    this.keyBindings = [
      {
        name: "save",
        isKeyBound: e => {
          return e.keyCode === 83 && e.ctrlKey;
        },
        action: () => {
          this.onSave();
        }
      }
    ];
    this.resetStyleNewLine = true;
    this.state = {
      value: content
    };
    this.onChange = ::this.onChange;
    this.maxSidebarButtons = null;
  }

  onAction = args => {
    console.log("onAction fired with args:", args);
  };

  componentDidMount() {
    highlightCode(this);
  }

  onChange(value) {
    this.setState({
      value
    });
  }

  onSave() {
    console.log("save");
  }

  renderEditor() {
    return (

        <MegadraftEditor
          plugins={[image, video, relatedArticles]}
          editorState={this.state.value}
          placeholder="Text"
          onChange={this.onChange}
          keyBindings={this.keyBindings}
          resetStyleNewLine={this.resetStyleNewLine}
          maxSidebarButtons={this.maxSidebarButtons}
          onAction={this.onAction}
        />

    );
  }

  renderJsonPreview() {
    return (

        <link
          rel="stylesheet"
          href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.4.0/styles/gruvbox-dark.min.css"
        />

              {editorStateToJSON(this.state.value)}

    );
  }

  render() {
    if (this.props.activeContent) {
      return this.renderEditor();
    }
    return this.renderJsonPreview();
  }
}

/* global hljs */
hljs.initHighlightingOnLoad();

export default Example;
<commit_msg>Enabling movableBlocks in Website demo<commit_after>/*
 * Copyright (c) 2016, Globo.com (https://github.com/globocom)
 *
 * License: MIT
 */

import React from "react";

import { MegadraftEditor } from "../../src/Megadraft";
import { editorStateToJSON, editorStateFromRaw } from "../../src/utils";
import { highlightCode } from "./highlightCode";

import INITIAL_CONTENT from "./contentExample";

import relatedArticles from "megadraft-related-articles-plugin";
import image from "../../src/plugins/image/plugin";
import video from "../../src/plugins/video/plugin";

class Example extends React.Component {
  constructor(props) {
    super(props);
    const content = editorStateFromRaw(INITIAL_CONTENT);
    this.keyBindings = [
      {
        name: "save",
        isKeyBound: e => {
          return e.keyCode === 83 && e.ctrlKey;
        },
        action: () => {
          this.onSave();
        }
      }
    ];
    this.resetStyleNewLine = true;
    this.state = {
      value: content
    };
    this.onChange = ::this.onChange;
    this.maxSidebarButtons = null;
  }

  onAction = args => {
    console.log("onAction fired with args:", args);
  };

  componentDidMount() {
    highlightCode(this);
  }

  onChange(value) {
    this.setState({
      value
    });
  }

  onSave() {
    console.log("save");
  }

  renderEditor() {
    return (

        <MegadraftEditor
          plugins={[image, video, relatedArticles]}
          editorState={this.state.value}
          placeholder="Text"
          onChange={this.onChange}
          keyBindings={this.keyBindings}
          resetStyleNewLine={this.resetStyleNewLine}
          maxSidebarButtons={this.maxSidebarButtons}
          onAction={this.onAction}
          movableBlocks={true}
        />

    );
  }

  renderJsonPreview() {
    return (

        <link
          rel="stylesheet"
          href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.4.0/styles/gruvbox-dark.min.css"
        />

              {editorStateToJSON(this.state.value)}

    );
  }

  render() {
    if (this.props.activeContent) {
      return this.renderEditor();
    }
    return this.renderJsonPreview();
  }
}

/* global hljs */
hljs.initHighlightingOnLoad();

export default Example;
<|endoftext|>


<commit_before>import 'whatwg-fetch';
import '@babel/polyfill';

import Vue from 'vue';
import { format as formatDate } from 'date-fns';
import router from './router.js';

Vue.filter('formatDate', function(date, format) {
  return formatDate(date, format);
});

new Vue({
  el: '#root',
  router,
  render(h) {
    return h('router-view', {
      key: this.$route.fullPath,
    });
  },
});
<commit_msg>remove babel/polyfill<commit_after>import 'whatwg-fetch';

import Vue from 'vue';
import { format as formatDate } from 'date-fns';
import router from './router.js';

Vue.filter('formatDate', function(date, format) {
  return formatDate(date, format);
});

new Vue({
  el: '#root',
  router,
  render(h) {
    return h('router-view', {
      key: this.$route.fullPath,
    });
  },
});
<|endoftext|>


<commit_before>class FormOptionsWidget extends Widget {
    constructor() {
        super();

        // Input boxes which fail validation will have their background color changed to this color
        this.failed_validation_color = getComputedStyle(document.documentElement).getPropertyValue('--alert-invalid-entry-pink');
    }

    render() {
        const heading_container = this.getHeadingContainer('Form Options');

        const interactive_container = this.getInteractiveContainer();
        interactive_container.innerHTML = this.getFormOptionsTemplate();

        const container = document.createElement('div');
        container.classList.add('notebook-builder-widget');
        container.classList.add('form-options-widget');
        container.appendChild(heading_container);
        container.appendChild(interactive_container);

        const save_button = container.querySelector('.save-button');
        save_button.addEventListener('click', event => {
            this.saveButtonAction();
        })

        return container;
    }

    /**
     * Get the template used in the form options widget
     *
     * @returns {string}
     */
    getFormOptionsTemplate() {
        return `

            <input type="button" class="save-button" value="Save">

`;
    }

    /**
     * Pack up all the configuration data from the GUI into a nice config.json and submit it to the server.
     * Additionally perform validation on inputs and prevent saving if validation errors were found.
     */
    saveButtonAction() {
        // Clear any previous validation errors
        this.clearStatusMessages();
        this.resetInputColors();

        // Only continue if validation passed
        if(!this.validate()) {
            return;
        }

        const config_json_file = new File([JSON.stringify(notebook_builder.getJSON(), null, 2)], 'config.json', {type: "text/plain"});
        const url = buildCourseUrl(['notebook_builder', 'save']);

        const form_data = new FormData();
        form_data.append('config_upload', config_json_file, 'config.json');
        form_data.append('csrf_token', csrfToken);
        form_data.append('g_id', builder_data.g_id);

        const makeRequest = async () => {
            this.appendStatusMessage('Saving...');

            // Currently only deals with uploading images, will rework later to accommodate other files/directories as needed
            // Wait for all files to be uploaded before continuing
            const file_selectors = document.querySelectorAll('input[type=file]');
            await uploadFiles(file_selectors, builder_data.g_id, 'test_input');

            const response = await fetch(url, {method: 'POST', body: form_data});
            const result = await response.json();

            this.clearStatusMessages();

            if (result.status === 'success') {
                const gradeable_submission_url = buildCourseUrl(['gradeable', builder_data.g_id]);
                const edit_gradeable_url = buildCourseUrl(['gradeable', builder_data.g_id, 'update']);

                this.appendStatusMessage(`Your gradeable is being installed.  To view it visit the <a href="${gradeable_submission_url}">submission page</a>.`);
                this.appendStatusMessage(`To make other changes to the gradeable configuration visit the <a href="${edit_gradeable_url}">edit gradeable page</a>.`);
            }
            else {
                this.appendStatusMessage(result.message);
            }
        };

        makeRequest().catch(err => console.error(err));
    }

    /**
     * Validate current form configuration/data
     *
     * @returns {boolean}
     */
    validate() {
        return this.validateFileNames();
    }

    /**
     * Append a message to the status div
     *
     * @param {string} message Text to be displayed
     */
    appendStatusMessage(message) {
        const status_div = document.querySelector('.form-options-widget .status');
        const p = document.createElement('p');
        p.innerHTML = message;
        status_div.appendChild(p);
    }

    /**
     * Remove all messages from the status div
     */
    clearStatusMessages() {
        const status_div = document.querySelector('.form-options-widget .status');
        status_div.innerHTML = '';
    }

    /**
     * Reset the all input boxes to have a white background
     */
    resetInputColors() {
        document.querySelectorAll('input').forEach(elem => {
            elem.style.backgroundColor = '';
        });
    }

    /**
     * Validates to see if all filename boxes contain unique values.  If validation errors are found then a message
     * is added to the status div and the offending input boxes will have their background color changed to indicate
     * they are the ones that failed validation.
     *
     * @returns {boolean} True if validation was successful, false otherwise
     */
    validateFileNames() {
        const duplicated_filenames = this.getDuplicatedFileNames();

        duplicated_filenames.forEach(filename => {
            this.appendStatusMessage(`Filename: '${filename}' was found to be duplicated.  All filenames must be unique.`);

            document.querySelectorAll(`.filename-input`).forEach(elem => {
                if (elem.value === filename) {
                    elem.style.backgroundColor = this.failed_validation_color;
                }
            });
        });

        return duplicated_filenames.size === 0;
    }

    /**
     * Collects and returns the set of strings which were found to be duplicated in more than one filename input box.
     *
     * @returns {Set<string>}
     */
    getDuplicatedFileNames() {
        const json = notebook_builder.getJSON();

        const filenames = [];
        const duplicated_filenames = new Set();

        json.notebook.forEach(cell => {
            if (cell.filename) {
                if (filenames.includes(cell.filename)) {
                    duplicated_filenames.add(cell.filename);
                }
                else {
                    filenames.push(cell.filename);
                }
            }
        });

        return duplicated_filenames;
    }
}
<commit_msg>[Feature:InstructorUI] notebook filename check (#5790)<commit_after>class FormOptionsWidget extends Widget {
    constructor() {
        super();

        // Input boxes which fail validation will have their background color changed to this color
        this.failed_validation_color = getComputedStyle(document.documentElement).getPropertyValue('--alert-invalid-entry-pink');
    }

    render() {
        const heading_container = this.getHeadingContainer('Form Options');

        const interactive_container = this.getInteractiveContainer();
        interactive_container.innerHTML = this.getFormOptionsTemplate();

        const container = document.createElement('div');
        container.classList.add('notebook-builder-widget');
        container.classList.add('form-options-widget');
        container.appendChild(heading_container);
        container.appendChild(interactive_container);

        const save_button = container.querySelector('.save-button');
        save_button.addEventListener('click', event => {
            this.saveButtonAction();
        })

        return container;
    }

    /**
     * Get the template used in the form options widget
     *
     * @returns {string}
     */
    getFormOptionsTemplate() {
        return `

            <input type="button" class="save-button" value="Save">

`;
    }

    /**
     * Pack up all the configuration data from the GUI into a nice config.json and submit it to the server.
     * Additionally perform validation on inputs and prevent saving if validation errors were found.
     */
    saveButtonAction() {
        // Clear any previous validation errors
        this.clearStatusMessages();
        this.resetInputColors();

        // Only continue if validation passed
        if(!this.validate()) {
            return;
        }

        const config_json_file = new File([JSON.stringify(notebook_builder.getJSON(), null, 2)], 'config.json', {type: "text/plain"});
        const url = buildCourseUrl(['notebook_builder', 'save']);

        const form_data = new FormData();
        form_data.append('config_upload', config_json_file, 'config.json');
        form_data.append('csrf_token', csrfToken);
        form_data.append('g_id', builder_data.g_id);

        const makeRequest = async () => {
            this.appendStatusMessage('Saving...');

            // Currently only deals with uploading images, will rework later to accommodate other files/directories as needed
            // Wait for all files to be uploaded before continuing
            const file_selectors = document.querySelectorAll('input[type=file]');
            await uploadFiles(file_selectors, builder_data.g_id, 'test_input');

            const response = await fetch(url, {method: 'POST', body: form_data});
            const result = await response.json();

            this.clearStatusMessages();

            if (result.status === 'success') {
                const gradeable_submission_url = buildCourseUrl(['gradeable', builder_data.g_id]);
                const edit_gradeable_url = buildCourseUrl(['gradeable', builder_data.g_id, 'update']);

                this.appendStatusMessage(`Your gradeable is being installed.  To view it visit the <a href="${gradeable_submission_url}">submission page</a>.`);
                this.appendStatusMessage(`To make other changes to the gradeable configuration visit the <a href="${edit_gradeable_url}">edit gradeable page</a>.`);
            }
            else {
                this.appendStatusMessage(result.message);
            }
        };

        makeRequest().catch(err => console.error(err));
    }

    /**
     * Validate current form configuration/data
     *
     * @returns {boolean}
     */
    validate() {
        return this.validateFileNames();
    }

    /**
     * Append a message to the status div
     *
     * @param {string} message Text to be displayed
     */
    appendStatusMessage(message) {
        const status_div = document.querySelector('.form-options-widget .status');
        const p = document.createElement('p');
        p.innerHTML = message;
        status_div.appendChild(p);
    }

    /**
     * Remove all messages from the status div
     */
    clearStatusMessages() {
        const status_div = document.querySelector('.form-options-widget .status');
        status_div.innerHTML = '';
    }

    /**
     * Reset the all input boxes to have a white background
     */
    resetInputColors() {
        document.querySelectorAll('input').forEach(elem => {
            elem.style.backgroundColor = '';
        });
    }

    /**
     * Validates to see if all filename boxes contain unique values.  If validation errors are found then a message
     * is added to the status div and the offending input boxes will have their background color changed to indicate
     * they are the ones that failed validation.
     *
     * @returns {boolean} True if validation was successful, false otherwise
     */
    validateFileNames() {
        // Duplicated filename check
        const duplicated_filenames = this.getDuplicatedFileNames();
        duplicated_filenames.forEach(filename => {
            this.appendStatusMessage(`Filename: '${filename}' was found to be duplicated.  All filenames must be unique.`);
        });

        // Invalid substrings check
        const illegal_substrings = {
            '..': '..',
            '/': '/',
            ' ': 'spaces',
        };

        const illegal_filenames = this.getFileNamesWithIllegalSubstrings(Object.keys(illegal_substrings));
        illegal_filenames.forEach(filename => {
            this.appendStatusMessage(`Filename: '${filename}' was found to contain illegal substrings. Filenames may not contain ${Object.values(illegal_substrings).join(' or ')}.`);
        });

        this.colorFailedFileNames(duplicated_filenames.concat(illegal_filenames));

        return duplicated_filenames.length === 0 && illegal_filenames.length === 0;
    }

    /**
     * Locate the set of inputs that contain filenames which failed validation and color their background red.
     *
     * @param {Array<string>} failed_filenames
     */
    colorFailedFileNames(failed_filenames) {
        document.querySelectorAll(`.filename-input`).forEach(elem => {
            if (failed_filenames.includes(elem.value)) {
                elem.style.backgroundColor = this.failed_validation_color;
            }
        });
    }

    /**
     * Determine if any of the form's filenames contain illegal substrings.
     *
     * @param {Array<string>} illegal_substrings An array of substrings filenames must not contain.
     * @returns {Array<string>} An array of filenames that do contain illegal substrings.
     */
    getFileNamesWithIllegalSubstrings(illegal_substrings) {
        const json = notebook_builder.getJSON();

        const illegal_filenames = new Set();

        json.notebook.forEach(cell => {
            if (cell.filename) {
                illegal_substrings.forEach(substring => {
                    if (cell.filename.includes(substring)) {
                        illegal_filenames.add(cell.filename);
                    }
                });
            }
        });

        return Array.from(illegal_filenames);
    }

    /**
     * Collects and returns the set of strings which were found to be duplicated in more than one filename input box.
     *
     * @returns {Array<string>}
     */
    getDuplicatedFileNames() {
        const json = notebook_builder.getJSON();

        const filenames = [];
        const duplicated_filenames = new Set();

        json.notebook.forEach(cell => {
            if (cell.filename) {
                if (filenames.includes(cell.filename)) {
                    duplicated_filenames.add(cell.filename);
                }
                else {
                    filenames.push(cell.filename);
                }
            }
        });

        return Array.from(duplicated_filenames);
    }
}
<|endoftext|>


<commit_before>import React from 'react'
import Header from './Header'
import axios from 'axios'
import config from '../config'

class NewCat extends React.Component {
  constructor() {
    super()
    this.state = {
      cats: []
    }
  }
  componentWillMount() {
    axios.get(`${config.host}/cats`).then(res => {
      this.setState({
        cats: res.data.cats
      })
    })
  }
  render() {
    let catList = this.state.cats.map((item, i) => {
      return (

          {item.name} -- {item._id}

      )
    })
    return (

        {catList}

    )
  }
}

export default NewCat<commit_msg>add cats add and cats list code<commit_after>import React from 'react'
import Header from './Header'
import axios from 'axios'
import config from '../config'

class NewCat extends React.Component {
  constructor() {
    super()
    this.state = {
      cats: []
    }
  }
  componentWillMount() {
    axios.get(`${config.host}/cats`).then(res => {
      this.setState({
        cats: res.data.cats
      })
    })
  }
  _updateCatList() {
    axios.get(`${config.host}/cats`)
      .then((res) => this.setState({cats: res.data.cats}))
      .catch(err => console.log(err))
  }
  _handleSubmit(e) {
    e.preventDefault()
    let catName = this.refs.catName.value
    this.refs.catName.value = ''
    let data = {name: catName}
    axios.post(`${config.host}/category`, data)
      .then((res) => {
        this._updateCatList()
      })
  }
  render() {
    let catList = this.state.cats.map((item, i) => {
      return (

          {item.name} -- {item._id}

      )
    })
    return (

          {catList}

        <form onSubmit={this._handleSubmit.bind(this)}>
          <input ref='catName' type='text' />
          <input type='submit' />
        </form>

    )
  }
}

export default NewCat<|endoftext|>


<commit_before>'use strict';

const gulp = require('gulp');
const jshint = require('gulp-jshint');
const less = require('gulp-less');
const sh = require('shelljs');
const runSequence = require('run-sequence');
const mocha = require('gulp-spawn-mocha');
const _ = require('underscore');
const childProcess = require('child_process');
const karma = require('karma').server;
const babel = require('gulp-babel');
const electronPackager = require('electron-packager');
const symlink = require('gulp-symlink');
const electron = require('electron-prebuilt');
// const fontcustom = require('fontcustom');
const fs = require('fs');

const appConfig = require('./src/config/appConfig');

require('gulp-task-list')(gulp);

/* =========================================================================
 * Constants
 * ========================================================================= */
const SRC_DIR = 'src';
const DOCS_DIR = 'docs';
const RELEASE_IGNORE_PKGS = [ //any npm packages that should not be included in the release
  'electron-packager',
  'electron-prebuilt',
  'gulp|gulp-jshint',
  'gulp-less',
  'gulp-mocha',
  'gulp-task-list',
  'jshint-stylish',
  'run-sequence',
  'bower',
  'babel',
  'should',
  'sinon',
  'supertest'
];
const RELEASE_IMAGE_ICON = __dirname + '/resources/icon/logo_icon';
const RELEASE_OSX_IMAGE_ICON = RELEASE_IMAGE_ICON + '.icns';
const RELEASE_WIN_IMAGE_ICON = RELEASE_IMAGE_ICON + '.ico';
const RELEASE_SETTINGS = {
  dir: '.',
  name: appConfig.name,
  out: appConfig.releasePath,
  version: '0.36.0',
  ignore: RELEASE_IGNORE_PKGS.map((ignore) => {
    return '/node_modules/' + ignore + '($|/)';
  }),
  appPath: 'build/browser/main.js',
  force: true
};

const LESSOPTIONS = {
  compress: false
};

const MOCHA_SETTINGS = {
  reporter: 'spec',
  growl: true,
  env: {
    NODE_ENV: 'test'
  }
};

/* =========================================================================
 * Tasks
 * ========================================================================= */
/**
 * List gulp tasks
 */
gulp.task('?', (next) => {
  sh.exec('gulp task-list');
  next();
});

gulp.task('clean', (next) => {
  sh.rm('-rf', appConfig.releasePath);
  sh.rm('-rf', appConfig.buildPath);
  next();
});

gulp.task('css', () => {
  return gulp.src(SRC_DIR + '/ui/less/main.less')
    .pipe(less(LESSOPTIONS))
    .pipe(gulp.dest(SRC_DIR + '/ui/css'));
});

gulp.task('site-css', () => {
  return gulp.src(DOCS_DIR + '/less/docs.less')
    .pipe(less(LESSOPTIONS))
    .pipe(gulp.dest(DOCS_DIR + '/css'));
});

// gulp.task('fonts', function() {
//   return fontcustom({
//     path: 'resources/font-glyphs',
//     output: 'src/ui/font-glyphs',
//     noisy: true,
//     force: true
//   });
// });

gulp.task('jshint', () => {
  return _init(gulp.src(['src/**/*.js', '!src/ui/vendor/**/*.js']))
    .pipe(jshint())
    .pipe(jshint.reporter('jshint-stylish'))
    .pipe(jshint.reporter('fail'));
});

gulp.task('release-osx', ['pre-release'], (next) => {
  electronPackager(_.extend(RELEASE_SETTINGS, {
    platform: 'darwin',
    arch: 'x64',
    icon: RELEASE_OSX_IMAGE_ICON,
  }), next);
});

gulp.task('release-win', ['pre-release'], (next) => {
  electronPackager(_.extend(RELEASE_SETTINGS, {
    platform: 'win32',
    arch: 'all',
    icon: RELEASE_WIN_IMAGE_ICON,
  }), next);
});

gulp.task('release-lin', ['pre-release'], (next) => {
  electronPackager(_.extend(RELEASE_SETTINGS, {
    platform: 'linux',
    arch: 'all',
  }), next);
});

gulp.task('release', ['pre-release'], (next) => {
  electronPackager(_.extend(RELEASE_SETTINGS, {
    platform: 'all',
    arch: 'all',
    icon: RELEASE_IMAGE_ICON,
  }), next);
});

gulp.task('pre-release', (next) => {
  // Build Steps:
  //-------------------------------------
  // run build to cleanup dirs and compile less
  // run babel to compile ES6 => ES5
  // run prod-sym-links to change symlinks in node_modules that point to src dir to the build dir (which will contain the compiled ES5 code)
  //-------------------------------------
  runSequence('build', 'babel', 'prod-sym-links', next);
});

gulp.task('babel', () => {
  return gulp.src('./src/**/*.js')
    .pipe(babel({
      presets: ['es2015'],
      ignore: 'src/ui/vendor/*',

    }))
    .pipe(gulp.dest(appConfig.buildPath));
});

gulp.task('remove-link-src', (next) => {
  unlink('./node_modules/src', next);
});

gulp.task('remove-link-lib', (next) => {
  unlink('./node_modules/lib', next);
});

gulp.task('remove-link-tests', (next) => {
  unlink('./node_modules/tests', next);
});

gulp.task('prod-sym-links', ['remove-link-src', 'remove-link-lib'], () => {
  return gulp.src(['build/', 'build/lib/'])
    .pipe(symlink(['./node_modules/src', './node_modules/lib'], {
      force: true
    }));
});

gulp.task('dev-sym-links', ['remove-link-src', 'remove-link-lib', 'remove-link-tests'], () => {
  return gulp.src(['src/', 'src/lib/', 'tests/', 'package.json'])
    .pipe(symlink(['./node_modules/src', './node_modules/lib', './node_modules/tests', './node_modules/package.json'], {
      force: true
    }));
});

gulp.task('build', ['clean', 'css', 'dev-sym-links']);

gulp.task('serve', ['build'], (next) => {

  var env = _.extend({}, process.env);
  // env.NODE_ENV = 'production';

  var child = childProcess.spawn(electron, ['./'], {
    env: env
  });

  child.stdout.on('data', (data) => {
    console.log('tail output: ' + data);
  });

  child.on('exit', (exitCode) => {
    console.log('Child exited with code: ' + exitCode);
    return next(exitCode === 1 ? new Error('Error running serve task') : null);
  });
});

gulp.task('serve-site', ['site-css'], () => {
  gulp.watch(DOCS_DIR + '/less/*.less', () => {
    gulp.src(DOCS_DIR + '/less/docs.less')
      .pipe(less(LESSOPTIONS))
      .pipe(gulp.dest(DOCS_DIR + '/css'));
  });
});

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

gulp.task('test', () => {
  runSequence('jshint', 'test-int', 'test-unit', 'test-unit-ui', () => {
    process.exit(1);
  });
});

gulp.task('test-int', () => {
  return gulp.src('tests/integration/**/**/**-test.js')
    .pipe(mocha(MOCHA_SETTINGS));
});

gulp.task('test-unit', () => {
  return gulp.src('tests/unit/**/**/**-test.js')
    .pipe(mocha(MOCHA_SETTINGS));
});

gulp.task('test-unit-ui', (done) => {
  karma.start({
    configFile: __dirname + '/tests/ui/karma.conf.js',
    singleRun: true,
    files: [
      'karma.shim.js',
      '../../src/ui/vendor/jquery/dist/jquery.min.js',
      '../../src/ui/vendor/toastr/toastr.min.js',
      '../../src/ui/vendor/jquery-ui/jquery-ui.min.js',
      '../../src/ui/vendor/bootstrap/dist/js/bootstrap.js',
      '../../src/ui/vendor/angular/angular.js',
      '../../src/ui/vendor/angular-ui-sortable/sortable.js',
      '../../src/ui/vendor/angular-bootstrap/ui-bootstrap-tpls.js',
      '../../src/ui/vendor/underscore/underscore.js',
      '../../src/ui/vendor/angular-sanitize/angular-sanitize.js',
      '../../src/ui/vendor/angular-auto-grow-input/dist/angular-auto-grow-input.js',
      '../../src/ui/vendor/jquery.splitter/js/jquery.splitter-0.15.0.js',
      '../../src/ui/vendor/moment/moment.js',
      '../../src/ui/vendor/ng-prettyjson/src/ng-prettyjson.js',
      '../../src/ui/vendor/ng-prettyjson/src/ng-prettyjson-tmpl.js',
      '../../src/ui/vendor/Keypress/keypress.js',
      '../../src/ui/vendor/codemirror/lib/codemirror.js',
      '../../src/ui/vendor/codemirror/mode/javascript/javascript.js',
      '../../src/ui/vendor/codemirror/addon/hint/show-hint.js',
      '../../src/ui/vendorCustom/codemirror-formatting.js',
      '../../src/ui/vendorCustom/ng-bs-animated-button.js',
      '../../src/ui/vendor/angular-mocks/angular-mocks.js',
      '../../src/ui/app.js',
      '../../src/ui/components/**/*.js',
      '../../src/ui/directives/**/*.js',
      '../../src/ui/filters/**/*.js',
      '../../src/ui/services/**/*.js',
      './**/*-test.js'
    ]
  }, done);
});

/* =========================================================================
 * Helper Functions
 * ========================================================================= */
function _init(stream) {
  stream.setMaxListeners(0);
  return stream;
}

function unlink(symlink, next) {
  fs.lstat(symlink, function(lerr, lstat) {
    if (lerr || !lstat.isSymbolicLink()) {
      return next();
    }

    fs.unlink(symlink, function() {
      return next();
    });
  });
}
<commit_msg>oops wrong exit code<commit_after>'use strict';

const gulp = require('gulp');
const jshint = require('gulp-jshint');
const less = require('gulp-less');
const sh = require('shelljs');
const runSequence = require('run-sequence');
const mocha = require('gulp-spawn-mocha');
const _ = require('underscore');
const childProcess = require('child_process');
const karma = require('karma').server;
const babel = require('gulp-babel');
const electronPackager = require('electron-packager');
const symlink = require('gulp-symlink');
const electron = require('electron-prebuilt');
// const fontcustom = require('fontcustom');
const fs = require('fs');

const appConfig = require('./src/config/appConfig');

require('gulp-task-list')(gulp);

/* =========================================================================
 * Constants
 * ========================================================================= */
const SRC_DIR = 'src';
const DOCS_DIR = 'docs';
const RELEASE_IGNORE_PKGS = [ //any npm packages that should not be included in the release
  'electron-packager',
  'electron-prebuilt',
  'gulp|gulp-jshint',
  'gulp-less',
  'gulp-mocha',
  'gulp-task-list',
  'jshint-stylish',
  'run-sequence',
  'bower',
  'babel',
  'should',
  'sinon',
  'supertest'
];
const RELEASE_IMAGE_ICON = __dirname + '/resources/icon/logo_icon';
const RELEASE_OSX_IMAGE_ICON = RELEASE_IMAGE_ICON + '.icns';
const RELEASE_WIN_IMAGE_ICON = RELEASE_IMAGE_ICON + '.ico';
const RELEASE_SETTINGS = {
  dir: '.',
  name: appConfig.name,
  out: appConfig.releasePath,
  version: '0.36.0',
  ignore: RELEASE_IGNORE_PKGS.map((ignore) => {
    return '/node_modules/' + ignore + '($|/)';
  }),
  appPath: 'build/browser/main.js',
  force: true
};

const LESSOPTIONS = {
  compress: false
};

const MOCHA_SETTINGS = {
  reporter: 'spec',
  growl: true,
  env: {
    NODE_ENV: 'test'
  }
};

/* =========================================================================
 * Tasks
 * ========================================================================= */
/**
 * List gulp tasks
 */
gulp.task('?', (next) => {
  sh.exec('gulp task-list');
  next();
});

gulp.task('clean', (next) => {
  sh.rm('-rf', appConfig.releasePath);
  sh.rm('-rf', appConfig.buildPath);
  next();
});

gulp.task('css', () => {
  return gulp.src(SRC_DIR + '/ui/less/main.less')
    .pipe(less(LESSOPTIONS))
    .pipe(gulp.dest(SRC_DIR + '/ui/css'));
});

gulp.task('site-css', () => {
  return gulp.src(DOCS_DIR + '/less/docs.less')
    .pipe(less(LESSOPTIONS))
    .pipe(gulp.dest(DOCS_DIR + '/css'));
});

// gulp.task('fonts', function() {
//   return fontcustom({
//     path: 'resources/font-glyphs',
//     output: 'src/ui/font-glyphs',
//     noisy: true,
//     force: true
//   });
// });

gulp.task('jshint', () => {
  return _init(gulp.src(['src/**/*.js', '!src/ui/vendor/**/*.js']))
    .pipe(jshint())
    .pipe(jshint.reporter('jshint-stylish'))
    .pipe(jshint.reporter('fail'));
});

gulp.task('release-osx', ['pre-release'], (next) => {
  electronPackager(_.extend(RELEASE_SETTINGS, {
    platform: 'darwin',
    arch: 'x64',
    icon: RELEASE_OSX_IMAGE_ICON,
  }), next);
});

gulp.task('release-win', ['pre-release'], (next) => {
  electronPackager(_.extend(RELEASE_SETTINGS, {
    platform: 'win32',
    arch: 'all',
    icon: RELEASE_WIN_IMAGE_ICON,
  }), next);
});

gulp.task('release-lin', ['pre-release'], (next) => {
  electronPackager(_.extend(RELEASE_SETTINGS, {
    platform: 'linux',
    arch: 'all',
  }), next);
});

gulp.task('release', ['pre-release'], (next) => {
  electronPackager(_.extend(RELEASE_SETTINGS, {
    platform: 'all',
    arch: 'all',
    icon: RELEASE_IMAGE_ICON,
  }), next);
});

gulp.task('pre-release', (next) => {
  // Build Steps:
  //-------------------------------------
  // run build to cleanup dirs and compile less
  // run babel to compile ES6 => ES5
  // run prod-sym-links to change symlinks in node_modules that point to src dir to the build dir (which will contain the compiled ES5 code)
  //-------------------------------------
  runSequence('build', 'babel', 'prod-sym-links', next);
});

gulp.task('babel', () => {
  return gulp.src('./src/**/*.js')
    .pipe(babel({
      presets: ['es2015'],
      ignore: 'src/ui/vendor/*',

    }))
    .pipe(gulp.dest(appConfig.buildPath));
});

gulp.task('remove-link-src', (next) => {
  unlink('./node_modules/src', next);
});

gulp.task('remove-link-lib', (next) => {
  unlink('./node_modules/lib', next);
});

gulp.task('remove-link-tests', (next) => {
  unlink('./node_modules/tests', next);
});

gulp.task('prod-sym-links', ['remove-link-src', 'remove-link-lib'], () => {
  return gulp.src(['build/', 'build/lib/'])
    .pipe(symlink(['./node_modules/src', './node_modules/lib'], {
      force: true
    }));
});

gulp.task('dev-sym-links', ['remove-link-src', 'remove-link-lib', 'remove-link-tests'], () => {
  return gulp.src(['src/', 'src/lib/', 'tests/', 'package.json'])
    .pipe(symlink(['./node_modules/src', './node_modules/lib', './node_modules/tests', './node_modules/package.json'], {
      force: true
    }));
});

gulp.task('build', ['clean', 'css', 'dev-sym-links']);

gulp.task('serve', ['build'], (next) => {

  var env = _.extend({}, process.env);
  // env.NODE_ENV = 'production';

  var child = childProcess.spawn(electron, ['./'], {
    env: env
  });

  child.stdout.on('data', (data) => {
    console.log('tail output: ' + data);
  });

  child.on('exit', (exitCode) => {
    console.log('Child exited with code: ' + exitCode);
    return next(exitCode === 1 ? new Error('Error running serve task') : null);
  });
});

gulp.task('serve-site', ['site-css'], () => {
  gulp.watch(DOCS_DIR + '/less/*.less', () => {
    gulp.src(DOCS_DIR + '/less/docs.less')
      .pipe(less(LESSOPTIONS))
      .pipe(gulp.dest(DOCS_DIR + '/css'));
  });
});

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

gulp.task('test', () => {
  runSequence('jshint', 'test-int', 'test-unit', 'test-unit-ui', () => {
    process.exit(0);
  });
});

gulp.task('test-int', () => {
  return gulp.src('tests/integration/**/**/**-test.js')
    .pipe(mocha(MOCHA_SETTINGS));
});

gulp.task('test-unit', () => {
  return gulp.src('tests/unit/**/**/**-test.js')
    .pipe(mocha(MOCHA_SETTINGS));
});

gulp.task('test-unit-ui', (done) => {
  karma.start({
    configFile: __dirname + '/tests/ui/karma.conf.js',
    singleRun: true,
    files: [
      'karma.shim.js',
      '../../src/ui/vendor/jquery/dist/jquery.min.js',
      '../../src/ui/vendor/toastr/toastr.min.js',
      '../../src/ui/vendor/jquery-ui/jquery-ui.min.js',
      '../../src/ui/vendor/bootstrap/dist/js/bootstrap.js',
      '../../src/ui/vendor/angular/angular.js',
      '../../src/ui/vendor/angular-ui-sortable/sortable.js',
      '../../src/ui/vendor/angular-bootstrap/ui-bootstrap-tpls.js',
      '../../src/ui/vendor/underscore/underscore.js',
      '../../src/ui/vendor/angular-sanitize/angular-sanitize.js',
      '../../src/ui/vendor/angular-auto-grow-input/dist/angular-auto-grow-input.js',
      '../../src/ui/vendor/jquery.splitter/js/jquery.splitter-0.15.0.js',
      '../../src/ui/vendor/moment/moment.js',
      '../../src/ui/vendor/ng-prettyjson/src/ng-prettyjson.js',
      '../../src/ui/vendor/ng-prettyjson/src/ng-prettyjson-tmpl.js',
      '../../src/ui/vendor/Keypress/keypress.js',
      '../../src/ui/vendor/codemirror/lib/codemirror.js',
      '../../src/ui/vendor/codemirror/mode/javascript/javascript.js',
      '../../src/ui/vendor/codemirror/addon/hint/show-hint.js',
      '../../src/ui/vendorCustom/codemirror-formatting.js',
      '../../src/ui/vendorCustom/ng-bs-animated-button.js',
      '../../src/ui/vendor/angular-mocks/angular-mocks.js',
      '../../src/ui/app.js',
      '../../src/ui/components/**/*.js',
      '../../src/ui/directives/**/*.js',
      '../../src/ui/filters/**/*.js',
      '../../src/ui/services/**/*.js',
      './**/*-test.js'
    ]
  }, done);
});

/* =========================================================================
 * Helper Functions
 * ========================================================================= */
function _init(stream) {
  stream.setMaxListeners(0);
  return stream;
}

function unlink(symlink, next) {
  fs.lstat(symlink, function(lerr, lstat) {
    if (lerr || !lstat.isSymbolicLink()) {
      return next();
    }

    fs.unlink(symlink, function() {
      return next();
    });
  });
}
<|endoftext|>


<commit_before>/*jslint bitwise: true */
'use strict';

(function() {

    angular
        .module('simpleArmoryApp')
        .controller('ApplicationCtrl' , ApplicationCtrl);

    function ApplicationCtrl($scope, LoginService, $location, $filter, KeyboardService, KonamiService) {

        // default to not logged in
        $scope.isLoggedIn = false;
        $scope.isUsingDarkTheme = localStorage.getItem('darkTheme') === 'true';

        // Listen for path changed and then parse and fetch the character
        $scope.$on('$locationChangeSuccess', function(){
            // If there was an error we need to reset everything
            if ($location.$$path.lastIndexOf('/error', 0) === 0 ) {
                $scope.character = null;
                $scope.isLoggedIn = false;
            } else if ($location.$$path === '/login') {
                // Leave current character logged in, just force the login controller execute
                $location.url('/');
            } else if ($location.$$path !== '' && $location.$$path !== '/' && !$location.$$path.startsWith('/admin')) {
                // "us/proudmoore/marko"
                // [0]: us/proudmoore/marko
                // [1]: proudmoore
                // [2]: marko
                // [3]: location part
                var rgr = new RegExp('([^\/]+)/([^\/]+)/([^\/]+)/?([^\/]+)?').exec($location.$$path);
                rgr = rgr ? rgr : {};

                LoginService.getProfile({'region': rgr[1], 'realm':rgr[2], 'character':rgr[3]})
                    .then(function(character) {
                        $scope.character = character;
                        $scope.region = rgr[1];
                        $scope.realm = rgr[2];
                        $scope.isLoggedIn = true;

                    // fetch the profile image for the header as a seperate call as to not block initial render
                    LoginService.getProfileMedia({'region': rgr[1], 'realm':rgr[2], 'character':rgr[3]}).then(function(pMedia) {
                        var avatarFallback = '?alt=/shadow/avatar/1-1.jpg';
                        if(pMedia.data.avatar_url) {
                            $scope.characterMedia = pMedia.data.avatar_url + avatarFallback
                        } else {
                            $scope.characterMedia = pMedia.data.assets[0].value + avatarFallback;
                        }
                    });
                });
            }
        });

        // hookup global hotkeys
        KeyboardService.init(KonamiService.trigger);

        // Helper function for percentage numbers.  Used in a lot of screens
        $scope.percent = function(n, d) {

            return $filter('number')(((n / d) * 100), 0);
        };

        $scope.achFormater = function(n, d) {
            if (!n || !d) {
                return '';
            }

            var perc = $scope.percent(n, d);

            // if the percentage is low enough, don't print the numbers, just use the percentage
            if (perc < 18) {
                return perc + '%';
            }

            return '' + n + ' / ' + d + ' (' + perc + '%)';
        };

        $scope.updateTheme = function() {
            $scope.isUsingDarkTheme = localStorage.getItem('darkTheme') === 'true';
        };

        // Helper to get the image id off an item
        $scope.getImageSrc = function(item, renderIcon) {

            renderIcon = renderIcon || item.collected;
            if (renderIcon) {
                // wowhead img
                return '//wow.zamimg.com/images/wow/icons/medium/' + item.icon.toLowerCase() + '.jpg';
            } else {
                // 1x1 gif
                return 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==';
            }
        };

        $scope.createSimpleGuid = function() {
            return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1) +
                   (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
        };

        $scope.qualityToBackground = function(item) {
            var bgColor = '#fff';

            switch(item.quality) {
                case 'poor':
                    bgColor = '#7F7F7F';
                    break;
                case 'common':
                    bgColor = '#F0F0F0';
                    break;
                case 'uncommon':
                    bgColor = '#22B14C';
                    break;
                case 'rare':
                    bgColor = '#3F48CC';
                    break;
            }

            return 'background:' + bgColor;
        };
  }

})();
<commit_msg>Inherit `prefers-color-scheme` value on load if no theme stored<commit_after>/*jslint bitwise: true */
'use strict';

(function() {

    angular
        .module('simpleArmoryApp')
        .controller('ApplicationCtrl' , ApplicationCtrl);

    function ApplicationCtrl($scope, $window, LoginService, $location, $filter, KeyboardService, KonamiService) {

        // default to not logged in
        $scope.isLoggedIn = false;

        var darkTheme = localStorage.getItem('darkTheme');
        if (darkTheme) {
            $scope.isUsingDarkTheme = darkTheme === 'true';
        } else {
            $scope.isUsingDarkTheme = $window.matchMedia ?
                $window.matchMedia('(prefers-color-scheme: dark)').matches : false;
        }

        // Listen for path changed and then parse and fetch the character
        $scope.$on('$locationChangeSuccess', function(){
            // If there was an error we need to reset everything
            if ($location.$$path.lastIndexOf('/error', 0) === 0 ) {
                $scope.character = null;
                $scope.isLoggedIn = false;
            } else if ($location.$$path === '/login') {
                // Leave current character logged in, just force the login controller execute
                $location.url('/');
            } else if ($location.$$path !== '' && $location.$$path !== '/' && !$location.$$path.startsWith('/admin')) {
                // "us/proudmoore/marko"
                // [0]: us/proudmoore/marko
                // [1]: proudmoore
                // [2]: marko
                // [3]: location part
                var rgr = new RegExp('([^\/]+)/([^\/]+)/([^\/]+)/?([^\/]+)?').exec($location.$$path);
                rgr = rgr ? rgr : {};

                LoginService.getProfile({'region': rgr[1], 'realm':rgr[2], 'character':rgr[3]})
                    .then(function(character) {
                        $scope.character = character;
                        $scope.region = rgr[1];
                        $scope.realm = rgr[2];
                        $scope.isLoggedIn = true;

                    // fetch the profile image for the header as a seperate call as to not block initial render
                    LoginService.getProfileMedia({'region': rgr[1], 'realm':rgr[2], 'character':rgr[3]}).then(function(pMedia) {
                        var avatarFallback = '?alt=/shadow/avatar/1-1.jpg';
                        if(pMedia.data.avatar_url) {
                            $scope.characterMedia = pMedia.data.avatar_url + avatarFallback;
                        } else {
                            $scope.characterMedia = pMedia.data.assets[0].value + avatarFallback;
                        }
                    });
                });
            }
        });

        // hookup global hotkeys
        KeyboardService.init(KonamiService.trigger);

        // Helper function for percentage numbers.  Used in a lot of screens
        $scope.percent = function(n, d) {

            return $filter('number')(((n / d) * 100), 0);
        };

        $scope.achFormater = function(n, d) {
            if (!n || !d) {
                return '';
            }

            var perc = $scope.percent(n, d);

            // if the percentage is low enough, don't print the numbers, just use the percentage
            if (perc < 18) {
                return perc + '%';
            }

            return '' + n + ' / ' + d + ' (' + perc + '%)';
        };

        $scope.updateTheme = function() {
            $scope.isUsingDarkTheme = localStorage.getItem('darkTheme') === 'true';
        };

        // Helper to get the image id off an item
        $scope.getImageSrc = function(item, renderIcon) {

            renderIcon = renderIcon || item.collected;
            if (renderIcon) {
                // wowhead img
                return '//wow.zamimg.com/images/wow/icons/medium/' + item.icon.toLowerCase() + '.jpg';
            } else {
                // 1x1 gif
                return 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==';
            }
        };

        $scope.createSimpleGuid = function() {
            return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1) +
                   (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
        };

        $scope.qualityToBackground = function(item) {
            var bgColor = '#fff';

            switch(item.quality) {
                case 'poor':
                    bgColor = '#7F7F7F';
                    break;
                case 'common':
                    bgColor = '#F0F0F0';
                    break;
                case 'uncommon':
                    bgColor = '#22B14C';
                    break;
                case 'rare':
                    bgColor = '#3F48CC';
                    break;
            }

            return 'background:' + bgColor;
        };
  }

})();
<|endoftext|>


<commit_before>'use strict';

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var mediaSchema = new Schema({
	title: String
});

module.exports = exports = mongoose.model('Media', mediaSchema);<commit_msg>Media model mongoose validation<commit_after>'use strict';

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var mediaSchema = new Schema({
	title: { type: String, required: true }
});

module.exports = exports = mongoose.model('Media', mediaSchema);<|endoftext|>


<commit_before>
var expect = require('expect.js')
  , assert = require('assert')
  , ref = require('ref')
  , Struct = require('ref-struct')
  , ffi = require('../')
  , bindings = require('./build/Release/ffi_tests')

describe('ForeignFunction', function () {

  afterEach(gc)

  // same struct as defined in ffi_tests.cc
  var box = Struct({
      width: ref.types.int
    , height: ref.types.int
  })

  it('should call the static "abs" bindings', function () {
    var _abs = bindings.abs
    var abs = ffi.ForeignFunction(_abs, 'int', [ 'int' ])
    expect(abs).to.be.a('function')
    expect(abs(-1234)).to.equal(1234)
  })

  it('should call the static "atoi" bindings', function () {
    var _atoi = bindings.atoi
    var atoi = ffi.ForeignFunction(_atoi, 'int', [ 'string' ])
    expect(atoi).to.be.a('function')
    expect(atoi('1234')).to.equal(1234)
  })

  it('should call the static "double_box" bindings', function () {
    var double_box = ffi.ForeignFunction(bindings.double_box, box, [ box ])
    var b = new box
    assert(b instanceof box)
    b.width = 4
    b.height = 5
    var out = double_box(b)
    assert(out instanceof box)
    assert.equal(8, out.width)
    assert.equal(10, out.height)
  })

  describe('async', function () {

    it('should call the static "abs" bindings asynchronously', function (done) {
      var _abs = bindings.abs
      var abs = ffi.ForeignFunction(_abs, 'int', [ 'int' ])
      expect(abs).to.be.a('function')

      // invoke asynchronously
      abs.async(-1234, function (err, res) {
        expect(err).to.equal(null)
        expect(res).to.equal(1234)
        done()
      })
    })

  })
})
<commit_msg>test: add a test around the "area_box" native test function<commit_after>
var expect = require('expect.js')
  , assert = require('assert')
  , ref = require('ref')
  , Struct = require('ref-struct')
  , ffi = require('../')
  , bindings = require('./build/Release/ffi_tests')

describe('ForeignFunction', function () {

  afterEach(gc)

  // same struct as defined in ffi_tests.cc
  var box = Struct({
      width: ref.types.int
    , height: ref.types.int
  })

  it('should call the static "abs" bindings', function () {
    var _abs = bindings.abs
    var abs = ffi.ForeignFunction(_abs, 'int', [ 'int' ])
    expect(abs).to.be.a('function')
    expect(abs(-1234)).to.equal(1234)
  })

  it('should call the static "atoi" bindings', function () {
    var _atoi = bindings.atoi
    var atoi = ffi.ForeignFunction(_atoi, 'int', [ 'string' ])
    expect(atoi).to.be.a('function')
    expect(atoi('1234')).to.equal(1234)
  })

  it('should call the static "double_box" bindings', function () {
    var double_box = ffi.ForeignFunction(bindings.double_box, box, [ box ])
    var b = new box
    assert(b instanceof box)
    b.width = 4
    b.height = 5
    var out = double_box(b)
    assert(out instanceof box)
    assert.equal(8, out.width)
    assert.equal(10, out.height)
  })

  it('should call the static "area_box" bindings', function () {
    var area_box = ffi.ForeignFunction(bindings.area_box, ref.types.int, [ box ])
    var b = new box({ width: 5, height: 20 })
    var rtn = area_box(b)
    assert.equal('number', typeof rtn)
    assert.equal(100, rtn)
  })

  describe('async', function () {

    it('should call the static "abs" bindings asynchronously', function (done) {
      var _abs = bindings.abs
      var abs = ffi.ForeignFunction(_abs, 'int', [ 'int' ])
      expect(abs).to.be.a('function')

      // invoke asynchronously
      abs.async(-1234, function (err, res) {
        expect(err).to.equal(null)
        expect(res).to.equal(1234)
        done()
      })
    })

  })
})
<|endoftext|>


<commit_before>var stage = null
var points = [];
var group_colours = ["rgba(255,0,0,1)","rgba(0,255,0,1)","rgba(0,0,255,1)", "rgba(0,255,255,1)", "rgba(255,0,255,1)", "rgba(255,255,0,1)"];

var buttons = {
				"list_of" : [
					{
						"text" : "Randomise, k = 2",
						"function" : "points = random_partition(2, points); draw();",
						"toggle" : null,
					},
					{
						"text" : "k = 3",
						"function" : "points = random_partition(3, points); draw();",
						"toggle" : null,
					},
					{
						"text" : "regroup",
						"function" : "points = regroup_points(points); draw();",
						"toggle" : null,
					}
				],
				"setup" : function() {
					var buttonWrapper = document.getElementById("buttonWrapper");

					for (var i = this.list_of.length - 1; i >= 0; i--) {
						var button = document.createElement('button');
						button.type = "button";
						button.innerText = this.list_of[i].text;
						button.code = this.list_of[i].function || this.list_of[i].text;
						button.onclick = function() {eval(this.code);};
						buttonWrapper.appendChild(button);

					};
				},
				"catch_mouse_click" : function(click) {
					return;
				}

			};

var setup = function() {
	stage = new Kinetic.Stage({
        container: 'container',
        width: window.innerWidth,
        height: window.innerHeight
      });

      var button_layer = new Kinetic.Layer();

      var box = new Kinetic.Rect({x:0, y:0,
      	width: stage.getWidth(),
      	height: stage.getHeight(),
      	fill: "rgba(255,0,0,0)",
      	stroke: "rgba(1,1,1,1)"
      })

      button_layer.add(box)

      // add the layer to the stage
      stage.add(button_layer);

	box.on("click", function(evt) {
		//console.log(evt)
		var circle = new Kinetic.Circle({
        x: evt.offsetX,
        y: evt.offsetY,
        radius: 10,
        fill: 'blue',
        stroke: 'black',
        strokeWidth: 1,
        draggable: true
      })
		circle.on("mouseenter", function(evt) {this.setOpacity(1);  button_layer.draw()});
		circle.on("mouseleave", function(evt) {this.setOpacity(0.2); button_layer.draw()});
      button_layer.add(circle);
      circle.draw();

	});

	buttons.setup();

};

window.onload = setup;
<commit_msg>add location<commit_after>var stage = null
var points = [];
var group_colours = ["rgba(255,0,0,1)","rgba(0,255,0,1)","rgba(0,0,255,1)", "rgba(0,255,255,1)", "rgba(255,0,255,1)", "rgba(255,255,0,1)"];

var shoutposition = function() {
	var shout = function(pos) {
		alert("lat: " + pos.coords.latitude);
		console.log(pos);
	}
	navigator.geolocation.getCurrentPosition(shout);
}

var buttons = {
				"list_of" : [
					{
						"text" : "Get location",
						"function" : "shoutposition()",
						"toggle" : null,
					}
				],
				"setup" : function() {
					var buttonWrapper = document.getElementById("buttonWrapper");

					for (var i = this.list_of.length - 1; i >= 0; i--) {
						var button = document.createElement('button');
						button.type = "button";
						button.innerText = this.list_of[i].text;
						button.code = this.list_of[i].function || this.list_of[i].text;
						button.onclick = function() {eval(this.code);};
						buttonWrapper.appendChild(button);

					};
				},
				"catch_mouse_click" : function(click) {
					return;
				}

			};

var setup = function() {
	stage = new Kinetic.Stage({
        container: 'container',
        width: window.innerWidth,
        height: window.innerHeight
      });

      var button_layer = new Kinetic.Layer();

      var box = new Kinetic.Rect({x:0, y:0,
      	width: stage.getWidth(),
      	height: stage.getHeight(),
      	fill: "rgba(255,0,0,0)",
      	stroke: "rgba(1,1,1,1)"
      })

      button_layer.add(box)

      // add the layer to the stage
      stage.add(button_layer);

	box.on("click", function(evt) {
		//console.log(evt)
		var circle = new Kinetic.Circle({
        x: evt.offsetX,
        y: evt.offsetY,
        radius: 10,
        fill: 'blue',
        stroke: 'black',
        strokeWidth: 1,
        draggable: true
      })
		circle.on("mouseenter", function(evt) {this.setOpacity(1);  button_layer.draw()});
		circle.on("mouseleave", function(evt) {this.setOpacity(0.2); button_layer.draw()});
      button_layer.add(circle);
      circle.draw();

	});

	buttons.setup();

};

window.onload = setup;
<|endoftext|>