var pingPongType = function(i) {
  if ((i % 5 === 0) || (i % 3 === 0) || (i % 15 === 0)) {
    if (i % 15 === 0) {
        return "pingpong";
    } else if (i % 5 === 0) {
        return "pong";
    } else {
        return "ping";
    }
  } else {
    return i;
  }
}

$(function() {
  $("#game").submit(function(event) {
    $('#outputList').empty();

    var number = parseInt($("#userNumber").val());
    var warning = "Whoops! Please enter an integer!"

    if (Number.isNaN(number) === true) {
      alert(warning);
    }

    for (var i = 1; i <= number; i += 1) {
      var output = pingPongType(i)
      $('#outputList').append("<li>" + output + "</li>")
    }

    event.preventDefault();
  });
});

var pingPongType = function(i) {
  if ((i % 5 === 0) || (i % 3 === 0) || (i % 15 === 0)) {
    if (i % 15 === 0) {
        return "pingpong";
    } else if (i % 5 === 0) {
        return "pong";
    } else {
        return "ping";
    }
  } else {
    return i;
  }
}

$(function() {
  $("#game").submit(function(event) {
    $('#outputList').empty();

    var number = parseInt($("#userNumber").val());
    var warning = "Whoops! Please enter a number (decimals will be rounded)!"

    if (Number.isNaN(number) === true) {
      alert(warning);
    }

    for (var i = 1; i <= number; i += 1) {
      var output = pingPongType(i)
      $('#outputList').append("<li>" + output + "</li>")
    }

    event.preventDefault();
  });
});

Change wording on warning for accuracy


// @flow
import UserState from './UserState';
import Raven from 'raven-js';
import storageService from '../storageService';

class UserStateService {

    userState: UserState;

    constructor() {
        const data = this.load();
        const name = data && data.name;
        const age = data && data.age;
        this.userState = new UserState(name, age);

        this.updateUserContext();
    }

    getUserState(): UserState {
        return this.userState;
    }

    hasNameAndAge(): boolean {
        return !!this.userState.name && !!this.userState.age;
    }

    save() {
        storageService.set('UserState', this.userState);
        this.updateUserContext();
    }

    load(): ?Object {
        return storageService.get('UserState');
    }

    reset() {
        this.userState.name = null;
        this.userState.age = null;

        storageService.remove('UserState');
    }

    updateUserContext() {
        const { name, age } = this.userState;

        if (name) {
            Raven.setUserContext({
                username: this.userState.name
            });
        }

        if (age) {
            Raven.setExtraContext({
                age: this.userState.age
            });
        }
    }

}

export default new UserStateService();

// @flow
import UserState from './UserState';
import Raven from 'raven-js';
import storageService from '../storageService';

class UserStateService {

    userState: UserState;

    constructor() {
        const data = this.load();
        const name = data && data.name;
        const age = data && data.age;
        this.userState = new UserState(name, age);

        this.updateUserContext();
    }

    getUserState(): UserState {
        return this.userState;
    }

    hasNameAndAge(): boolean {
        return !!this.userState.name && !!this.userState.age;
    }

    save() {
        storageService.set('UserState', this.userState);
        this.updateUserContext();
    }

    load(): ?Object {
        return storageService.get('UserState');
    }

    reset() {
        this.userState.name = null;
        this.userState.age = null;

        storageService.remove('UserState');
        storageService.remove('splash.skippable');
    }

    updateUserContext() {
        const { name, age } = this.userState;

        if (name) {
            Raven.setUserContext({
                username: this.userState.name
            });
        }

        if (age) {
            Raven.setExtraContext({
                age: this.userState.age
            });
        }
    }

}

export default new UserStateService();

Remove skippable key in local storage on reset


Router.configure({
  layoutTemplate: '_layout',
  loadingTemplate: '_loading',
  progressDelay: 100,
  progressSpinner: false
});

//From what I can tell this has been depreciated
//https://github.com/iron-meteor/iron-router/blob/739bcc1445db3ad6bf90bda4e0cab3203a0ae526/lib/router.js#L88
Router.map(function() {
  // App routes
  this.route('profile', {
    path: '/profile',
    template: 'profileSelf',
    data: function() {
      return {
        user: Meteor.user()
      }
    }
  });

  // login routes
  this.route('login', {
    path: '/login',
    template: 'publicLogin'
  });

  this.route('signup', {
    path: '/signup',
    template: 'publicSignup'
  });

  this.route('about');
});

var loggedInAction = function(){
  if(!Meteor.userId()){
    this.redirect('login');
  } else {
    this.next();
  }
}

Router.onBeforeAction(loggedInAction, {except: ['initiatives', 'category', 'login', 'signup', 'about']})

Router.configure({
  layoutTemplate: '_layout',
  loadingTemplate: '_loading',
  progressDelay: 100,
  progressSpinner: false
});

//From what I can tell this has been depreciated
//https://github.com/iron-meteor/iron-router/blob/739bcc1445db3ad6bf90bda4e0cab3203a0ae526/lib/router.js#L88
Router.map(function() {
  // App routes
  this.route('profile', {
    path: '/profile',
    template: 'profileSelf',
    data: function() {
      return {
        user: Meteor.user()
      }
    }
  });

  // login routes
  this.route('login', {
    path: '/login',
    template: 'publicLogin'
  });

  this.route('signup', {
    path: '/signup',
    template: 'publicSignup'
  });

  this.route('about');
});

var loggedInAction = function(){
  if(!Meteor.userId()){
    this.redirect('login');
  } else {
    this.next();
  }
}

Router.onBeforeAction(loggedInAction, {except: ['initiatives', 'initiative', 'category', 'login', 'signup', 'about']})

Fix - remove route check to initiative


$(document).ready(function() {
   $("#create_filter").on("click", function(ev) {
       var checkboxes = $("input[type=checkbox]");
       var bits = new Array(checkboxes.length);

       $.each(checkboxes, function(index, item) {
           var $item = $(item);
           var arrayIndex = $item.data("index");
           if ($item.is(":checked")) {
               bits[arrayIndex] = 1;
           }
           else {
               bits[arrayIndex] = 0;
           }
       });

       var unsafeFilter = "";
       while (bits.length) {
           var nextByte = bits.splice(0, 8).join("");
           var charCode = parseInt(nextByte.toString(), 2);
           unsafeFilter += String.fromCharCode(charCode);
           console.log(nextByte, charCode, unsafeFilter);
       }

       var filter = encodeURIComponent(unsafeFilter);
       prompt("Calculated, URL-encoded filter:", filter);
   });
});

$(document).on('ready turbolinks:load', function() {
   $("#create_filter").on("click", function(ev) {
       var checkboxes = $("input[type=checkbox]");
       var bits = new Array(checkboxes.length);

       $.each(checkboxes, function(index, item) {
           var $item = $(item);
           var arrayIndex = $item.data("index");
           if ($item.is(":checked")) {
               bits[arrayIndex] = 1;
           }
           else {
               bits[arrayIndex] = 0;
           }
       });

       var unsafeFilter = "";
       while (bits.length) {
           var nextByte = bits.splice(0, 8).join("");
           var charCode = parseInt(nextByte.toString(), 2);
           unsafeFilter += String.fromCharCode(charCode);
           console.log(nextByte, charCode, unsafeFilter);
       }

       var filter = encodeURIComponent(unsafeFilter);
       prompt("Calculated, URL-encoded filter:", filter);
   });
});

Update to use Turbolinks events


const path = require('path');

module.exports = (storybookBaseConfig, configType, defaultConfig) => {
  defaultConfig.module.rules.push({
    test: [/\.stories\.js$/, /index\.js$/],
    loaders: [require.resolve('@storybook/addon-storysource/loader')],
    include: [path.resolve(__dirname, '../src')],
    enforce: 'pre',
  });

  return defaultConfig;
};

const path = require('path');
const webpack = require('webpack');

module.exports = (storybookBaseConfig, configType, defaultConfig) => {
  defaultConfig.module.rules.push({
    test: [/\.stories\.js$/, /index\.js$/],
    loaders: [require.resolve('@storybook/addon-storysource/loader')],
    include: [path.resolve(__dirname, '../src')],
    enforce: 'pre',
  });

  // TEMP fix: https://github.com/plotly/plotly.js/issues/2466#issuecomment-372924684
  defaultConfig.plugins.push(new webpack.IgnorePlugin(/vertx/));

  return defaultConfig;
};

FIX a warning in polymer storybook


import Ember from 'ember';

export default function() {
  Ember.Test.registerAsyncHelper('select', function(app, selector, text) {
    const $el = app.testHelpers.findWithAssert(`${selector} option:contains("${text}")`);

    $el.each(function() {
      Ember.run(() => {
        this.selected = true;
        Ember.$(this).trigger('change');
      });
    });

    return app.testHelpers.wait();
  });
}

import Ember from 'ember';

export default function() {
  Ember.Test.registerAsyncHelper('select', function(app, selector, text) {
    var $el = app.testHelpers.findWithAssert(selector + "option:contains('" + text + "')");

    $el.each(function() {
      var _this = this;

      Ember.run(function() {
        _this.selected = true;
        Ember.$(_this).trigger('change');
      });
    });

    return app.testHelpers.wait();
  });
}

Stop using ES6 due to PhantomJS issues


/**
 * Created by sylva on 30/05/2017.
 */

$(document).ready(function () {
    $('#lastFMSearch').autocomplete({
        source: function( request, response ) {
            $.ajax({
                url: "https://ws.audioscrobbler.com/2.0/?method=artist.search&artist="+$('#lastFMSearch').val()+"&api_key=f6734ae2b9887488059fc9507f2c6c60&format=json&limit=10",
                dataType: "json",
                success: function(data) {
                    var results = [];
                    for(var cptArtist = 0; cptArtist < data.results.artistmatches.artist.length; cptArtist++)
                    {
                        results.push({
                            label: data.results.artistmatches.artist[cptArtist].name,
                            icon: data.results.artistmatches.artist[cptArtist].image[1]["#text"]
                        })
                    }
                    response(results);
                }
            });
        },
        minLength: 2
    } )
    .autocomplete( "instance" )._renderItem = function( ul, item ) {
        return $( "
" )
            .append( "
" + item.label + "
" )
            .appendTo( ul );
    };
});

Add autocomplete search with lastFM


var fs = require('fs'),
	tasks = fs.readdirSync('./gulp/tasks'),
	gulp = require('gulp');

tasks.forEach(function(task) {
	require('./tasks/' + task);
});

Fix indentation in entry file


import React from 'react'
import Autosuggest from 'react-autosuggest'

import style from './bpk-autosuggest.scss'

const defaultTheme = {
  container:            'bpk-autosuggest__container',
  containerOpen:        'bpk-autosuggest__container--open',
  input:                'bpk-autosuggest__input',
  suggestionsContainer: 'bpk-autosuggest__suggestions-container',
  suggestionsList:      'bpk-autosuggest__suggestions-list',
  suggestion:           'bpk-autosuggest__suggestion',
  suggestionFocused:    'bpk-autosuggest__suggestion--focused',
  sectionContainer:     'bpk-autosuggest__section-container',
  sectionTitle:         'bpk-autosuggest__section-title'
}

const BpkAutosuggest = (props) => {
  const inputProps = {
    value: props.value,
    onChange: props.onChange
  }

  return (
    <Autosuggest
      suggestions={props.suggestions}
      onSuggestionsUpdateRequested={props.onSuggestionsUpdateRequested}
      onSuggestionsClearRequested={props.onSuggestionsClearRequested || (() => {})}
      getSuggestionValue={props.getSuggestionValue}
      renderSuggestion={props.renderSuggestion}
      onSuggestionSelected={props.onSuggestionSelected || (() => {})}
      inputProps={inputProps}
      theme={defaultTheme}
    />
  )
}

export default BpkAutosuggest

import React from 'react'
import Autosuggest from 'react-autosuggest'

import style from './bpk-autosuggest.scss'

const defaultTheme = {
  container:            'bpk-autosuggest__container',
  containerOpen:        'bpk-autosuggest__container--open',
  input:                'bpk-autosuggest__input',
  suggestionsContainer: 'bpk-autosuggest__suggestions-container',
  suggestionsList:      'bpk-autosuggest__suggestions-list',
  suggestion:           'bpk-autosuggest__suggestion',
  suggestionFocused:    'bpk-autosuggest__suggestion--focused',
  sectionContainer:     'bpk-autosuggest__section-container',
  sectionTitle:         'bpk-autosuggest__section-title'
}

const BpkAutosuggest = (props) => {
  const inputProps = {
    value: props.value,
    onChange: props.onChange
  }

  return (
    <Autosuggest
      suggestions={props.suggestions}
      onSuggestionsUpdateRequested={props.onSuggestionsUpdateRequested}
      onSuggestionsClearRequested={props.onSuggestionsClearRequested || (() => {})}
      getSuggestionValue={props.getSuggestionValue || ((suggestion) => suggestion)}
      renderSuggestion={props.renderSuggestion}
      onSuggestionSelected={props.onSuggestionSelected || (() => {})}
      inputProps={inputProps}
      theme={defaultTheme}
    />
  )
}

export default BpkAutosuggest

Make autosuggest getSuggestionalValue with 'id' style default


import Ember from 'ember';
import layout from '../templates/components/yebo-checkout';
/**
  A single page checkout that reactively responds to changes in the
  `yebo.checkouts` service.

  **To Override:** You'll need to run the components generator:

  ```bash
  ember g yebo-ember-storefront-components
  ```

  This will install all of the Yebo Ember Storefront component files into your
  host application at `app/components/yebo-*.js`, ready to be extended or
  overriden.

  @class YeboCheckout
  @namespace Component
  @extends Ember.Component
*/
export default Ember.Component.extend({
  layout: layout,
  action: 'transitionCheckoutState',

  /**
   *
   */
  init() {
    // Call the super
    this._super();
    // Set initialize it
    // TODO: Move this to an initialzer
    this.get('yebo')._restoreCurrentOrder().then(()=> {
      this.get('yebo').get('checkouts').trigger('checkoutCalled');
    });
  },

  actions: {
    transitionCheckoutState: function(stateName) {
      this.sendAction('action', stateName);
    },
    setShipment: function(rateId) {
      // Trigger the event
      this.get('yebo').get('checkouts').trigger('setShipment', rateId);
    },
    checkout: function() {
      // Trigger the event
      this.get('yebo').get('checkouts').trigger('checkout');
    },
    editAddress: function(name) {
      // Trigger the event
      this.get('yebo').get('checkouts').trigger('editAddress', name);
    }
  }
});

import Ember from 'ember';
import layout from '../templates/components/yebo-checkout';
/**
  A single page checkout that reactively responds to changes in the
  `yebo.checkouts` service.

  **To Override:** You'll need to run the components generator:

  ```bash
  ember g yebo-ember-storefront-components
  ```

  This will install all of the Yebo Ember Storefront component files into your
  host application at `app/components/yebo-*.js`, ready to be extended or
  overriden.

  @class YeboCheckout
  @namespace Component
  @extends Ember.Component
*/
export default Ember.Component.extend({
  layout: layout,
  action: 'transitionCheckoutState',

  /**
   *
   */
  init() {
    // Call the super
    this._super();
    // Set initialize it
    // TODO: Move this to an initialzer
    this.get('yebo').on('orderLoaded', () => {
      this.get('yebo').get('checkouts').trigger('checkoutCalled');
    });
  },

  actions: {
    transitionCheckoutState: function(stateName) {
      this.sendAction('action', stateName);
    },
    setShipment: function(rateId) {
      // Trigger the event
      this.get('yebo').get('checkouts').trigger('setShipment', rateId);
    },
    checkout: function() {
      // Trigger the event
      this.get('yebo').get('checkouts').trigger('checkout');
    },
    editAddress: function(name) {
      // Trigger the event
      this.get('yebo').get('checkouts').trigger('editAddress', name);
    }
  }
});

Initialize the checkout when the order is loaded


/**
 * For each our own defined models, handle 'created' and 'lastUpdated' automatically.
 * Refer to: http://docs.strongloop.com/display/public/LB/Operation+hooks
 * Created by Ting on 2015/5/18.
 */
module.exports = function (app) {
  var builtInModels = ['User', 'Email', 'ACL', 'AccessToken', 'Role', 'RoleMapping'];
  app.models().forEach(function (Model) {
    if (builtInModels.indexOf(Model.modelName) == -1) {
      var myModel = app.models[Model.modelName];
      myModel.observe('before save', function updateTimestamp(ctx, next) {
        var now = new Date();
        if (ctx.instance) {
          ctx.instance.created = now;
          ctx.instance.lastUpdated = now;
        } else {
          ctx.data.lastUpdated = now;
        }
        next();
      });
    }
  });
};

/**
 * For each our own defined models, handle 'created' and 'lastUpdated' automatically.
 * Refer to: http://docs.strongloop.com/display/public/LB/Operation+hooks
 * Created by Ting on 2015/5/18.
 */
var loopback = require('loopback');
module.exports = function (app) {
  var builtInModels = ['User', 'Email', 'ACL', 'AccessToken', 'Role', 'RoleMapping'];
  app.models().forEach(function (Model) {
    if (builtInModels.indexOf(Model.modelName) == -1) {
      var myModel = app.models[Model.modelName];
      myModel.observe('before save', function updateTimestamp(ctx, next) {
        var context = loopback.getCurrentContext();
        var accessToken = context.get('accessToken');
        var now = new Date();

        if (ctx.instance) {
          // Update time of 'created' and 'lastUpdated'.
          ctx.instance.created = now;
          ctx.instance.lastUpdated = now;
          // Replace 'memberId' according to current accessToken, avoid user change it in client.
          ctx.instance.memberId = accessToken.userId;
        } else {
          ctx.data.lastUpdated = now;
          ctx.data.memberId = accessToken.userId;
        }
        next();
      });
    }
  });
};


$(function() {
  CKEDITOR.replace('description');

  /**
   * Make the sidebar sticky
  */
  var $sticky = $('.sticky');

  if (!!$sticky.offset()) { // Execute all this only if we have an element with the .sticky class

    var stickyTop = $sticky.offset().top; // Top offset position of the element

    // Fix its width to avoid a bug
    $sticky.css('width', $sticky.width());

    $(window).scroll(function() {
      var windowTop = $(window).scrollTop();

      if (stickyTop < windowTop){
        $sticky.css({ position: 'fixed', top: 0 });
      } else {
        $sticky.css('position','static');
      }
    });
  }
});

$(function() {
  CKEDITOR.replace('description');

  /**
   * Make the sidebar sticky
  */
  var $sticky = $('.sticky');

  if (!!$sticky.offset()) { // Execute all this only if we have an element with the .sticky class

    var stickyTop = $sticky.offset().top; // Top offset position of the element

    // Fix its width to avoid a bug
    $sticky.css('width', $sticky.width());

    $(window).scroll(function() {
      var windowTop = $(window).scrollTop();

      if (stickyTop < (windowTop + 15)){
        $sticky.css({ position: 'fixed', top: '15px' });
      } else {
        $sticky.css('position','static');
      }
    });
  }
});

Add a top margin on the sticky block


define([
    'underscore',
    'views/proto/paged-collection',
    'views/event/box',
    'text!templates/event-collection.html'
], function (_, PagedCollection, EventBox, html) {
    return PagedCollection.extend({
        template: _.template(html),

        tag: 'div',

        listSelector: '.events-list',

        generateItem: function (model) {
            return new EventBox({ model: model });
        },

        events: {
            "click .filter": "filter"
        },

        filter: function () {
            var types=[];
            $('.filter:checked').each(function() {
                types.push($(this).val());
            });

            var filterString = types.join();

            this.options.types = types;

            this.collection.setTypes(types);

            /* Set queryString to URL */
            var url = '/feed';
            if ( types.length > 0 ) {
                url += '?types=' + filterString;
            }

            Backbone.trigger('pp:navigate', url, { replace: true });

        },
        serialize: function() {
            var types = this.options.types || [];

            var filterList = [
                {
                    value: 'add-comment',
                    description: 'add-comment',
                },
                {
                    value: 'add-quest',
                    description: 'add-quest',
                }
            ]

            /* Add status for each filter */
            for( var i = 0; i < filterList.length ; i++ ){
                if( types.indexOf(filterList[i].value) != -1 ){
                    filterList[i].status = 'checked';
                }else{
                    filterList[i].status = '';
                }
            }

            return { filterList: filterList };
        }

    });
});

define([
    'underscore',
    'views/proto/paged-collection',
    'views/event/box',
    'text!templates/event-collection.html'
], function (_, PagedCollection, EventBox, html) {
    return PagedCollection.extend({
        template: _.template(html),

        tag: 'div',

        listSelector: '.events-list',

        generateItem: function (model) {
            return new EventBox({ model: model });
        },

        events: {
            "click .filter": "filter"
        },

        filter: function () {
            var types=[];
            $('.filter:checked').each(function() {
                types.push($(this).val());
            });

            var filterString = types.join();

            this.options.types = types;

            this.collection.setTypes(types);

            /* Set queryString to URL */
            var url = '/feed';
            if ( types.length > 0 ) {
                url += '?types=' + filterString;
            }

            Backbone.trigger('pp:navigate', url, { replace: true });

        },
        serialize: function() {
            var types = this.options.types || [];

            var filterList = [
                {
                    value: 'add-comment',
                    description: 'add-comment',
                },
                {
                    value: 'add-quest',
                    description: 'add-quest',
                }
            ]

            /* Add status for each filter */
            _.each( filterList, function(list){
                if( types.indexOf(list.value) != -1 ){
                    list.status = 'checked';
                }else{
                    list.status = '';
                }
            });

            return { filterList: filterList };
        }

    });
});

Change to _.each that is awesome!!!.


module.exports = {
  extends: ['airbnb-base', 'plugin:flowtype/recommended'],
  plugins: ['flowtype'],
  env: { es6: true, jest: true, node: true },
  // globals: { Generator: true },
  rules: {
    // Rule to enforce function return types. We disable this because Flow will check our function return types.
    'consistent-return': 'off',
    // Code style rule to enforce import ordering. We disable this because we use absolute imports for types sometimes after relative imports.
    'import/first': 'off',
    // Code style rule to prefer a default export instead of a single named export, currently we disable this to allow this behavior. We can decide later to turn this on again if we want.
    'import/prefer-default-export': 'off',
    // Rule to restrict usage of confusing code style with mixed boolean operators. We disable this because Prettier removes "unnecessary parentheses" here and breaks this.
    'no-mixed-operators': 'off',
    // Rule to prevent prefixing of underscores on variable names. We disable this because we use some underscore prefixes in our code.
    'no-underscore-dangle': 'off',
  },
};

module.exports = {
  extends: ['airbnb-base', 'plugin:flowtype/recommended'],
  plugins: ['flowtype'],
  env: { es6: true, jest: true, node: true },
  // globals: { Generator: true },
  rules: {
    // Rule to enforce function return types. We disable this because Flow will check our function return types.
    'consistent-return': 'off',
    // Code style rule to enforce import ordering. We disable this because we use absolute imports for types sometimes after relative imports.
    'import/first': 'off',
    // Code style rule to prefer a default export instead of a single named export, currently we disable this to allow this behavior. We can decide later to turn this on again if we want.
    'import/prefer-default-export': 'off',
    // Prettier works better with its default 80 character max-length
    'max-len': 'off',
    // Conflicts with Prettier's stripping of unnecessary parentheses
    'no-confusing-arrow': 'off',
    // Rule to restrict usage of confusing code style with mixed boolean operators. We disable this because Prettier removes "unnecessary parentheses" here and breaks this.
    'no-mixed-operators': 'off',
    // Rule to prevent prefixing of underscores on variable names. We disable this because we use some underscore prefixes in our code.
    'no-underscore-dangle': 'off',
  },
};

Disable ESLint rules conflicting with Prettier


'use strict';

var m = require('mithril');
var Fangorn = require('js/fangorn');

function _fangornLazyLoadError(item) {
    item.notify.update('Google Drive couldn\'t load, please try again later.', 'deleting', undefined, 3000);
    return true;
}

Fangorn.config.googledrive = {
    lazyLoadError : _fangornLazyLoadError
};

'use strict';

var m = require('mithril');
var Fangorn = require('js/fangorn');

Fangorn.config.googledrive = {};

Remove notify from google drive fangorn


'use strict';

var helpers = require('../helpers'),
    os = require('os');

module.exports = {
  'name': 'final-newline',
  'defaults': {
    'include': true
  },
  'detect': function (ast, parser) {
    var result = [],
        last = ast.content[ast.content.length - 1],
        error = {
          'ruleId': parser.rule.name,
          'severity': parser.severity
        };

    if (last.type !== 'space') {
      if (parser.options.include) {
        error.line = last.end.line;
        error.column = last.end.column;
        error.message = 'Files must end with a new line';
        result = helpers.addUnique(result, error);
      }
    }
    else {
      if (last.content !== os.EOL && parser.options.include) {
        error.line = last.start.line;
        error.column = last.start.column;
        error.message = 'Files must end with a new line';
        result = helpers.addUnique(result, error);
      }
      else if (last.content === os.EOL && !parser.options.include) {
        error.line = last.start.line;
        error.column = last.start.column;
        error.message = 'Files must not end with a new line';
        result = helpers.addUnique(result, error);
      }
    }

    return result;
  }
};

'use strict';

var helpers = require('../helpers'),
    os = require('os');

module.exports = {
  'name': 'final-newline',
  'defaults': {
    'include': true
  },
  'detect': function (ast, parser) {
    var result = [],
        last = ast.content[ast.content.length - 1],
        error = {
          'ruleId': parser.rule.name,
          'severity': parser.severity
        };

    if (ast.syntax === 'sass') {
      if (typeof last.last('block') === 'object') {
        var lastBlock = last.last('block');

        if (lastBlock.content.length > 0) {
          if (lastBlock.content[lastBlock.length - 1]) {
            last = lastBlock.content[lastBlock.length - 1];
          }
        }
      }
    }

    if (last.type !== 'space') {
      if (parser.options.include) {
        error.line = last.end.line;
        error.column = last.end.column;
        error.message = 'Files must end with a new line';
        result = helpers.addUnique(result, error);
      }
    }
    else {
      if (last.content !== os.EOL && parser.options.include) {
        error.line = last.start.line;
        error.column = last.start.column;
        error.message = 'Files must end with a new line';
        result = helpers.addUnique(result, error);
      }
      else if (last.content === os.EOL && !parser.options.include) {
        error.line = last.start.line;
        error.column = last.start.column;
        error.message = 'Files must not end with a new line';
        result = helpers.addUnique(result, error);
      }
    }

    return result;
  }
};

Fix Sass syntax final newline rule bug


'use strict';

const Promise = require('bluebird');
const path = require('path');
const write = Promise.promisify(require('fs').writeFile);

module.exports = function addRoute(options) {
  const file = path.resolve(options.dir, './hof.settings.json');
  const settings = require(file);
  if (settings.routes.indexOf(`./apps/${options.name}`) === -1) {
    settings.routes.push(`./apps/${options.name}`);
  }

  return write(file, JSON.stringify(settings, null, '  '));
};

'use strict';

const Promise = require('bluebird');
const path = require('path');
const write = Promise.promisify(require('fs').writeFile);

module.exports = function addRoute(options) {

  const file = path.resolve(options.dir, './hof.settings.json');
  try {
    const settings = require(file);
  } catch (e) {
    console.log('WARNING: Could not create new route automatically.');
    console.log(`Add './apps/${options.name}' to your routes manually`);
    return Promise.resolve();
  }
  if (settings.routes.indexOf(`./apps/${options.name}`) === -1) {
    settings.routes.push(`./apps/${options.name}`);
  }

  return write(file, JSON.stringify(settings, null, '  '));
};

Handle case where config was not created by generator


var emscriptenMemory = require("./emscripten_memory");
var space = require("../../public/space");

function EmscriptenSpacesApi(apiPointer, cwrap) {

    var _apiPointer = apiPointer;

    var _worldToScreenWrap = cwrap(
        "worldToScreen", "number", ["number", "number", "number", "number", "number"]);

    var _worldToScreen = function(lat, long, alt) {
        var screenPos = [0, 0, 0];
        emscriptenMemory.passDoubles(screenPos, function(resultArray, arraySize) {
            _worldToScreenWrap(_apiPointer, lat, long, alt, resultArray);
            screenPos = emscriptenMemory.readDoubles(resultArray, 3);
        });
        return new space.Vector3(screenPos);
    };

    this.worldToScreen = function(position) {
        var point = L.latLng(position);
        return _worldToScreen(point.lat, point.lng, point.alt || 0);
    };
}

module.exports = EmscriptenSpacesApi;

var emscriptenMemory = require("./emscripten_memory");
var space = require("../../public/space");

function EmscriptenSpacesApi(apiPointer, cwrap) {

    var _apiPointer = apiPointer;

    var _worldToScreenWrap = cwrap(
        "worldToScreen", null, ["number", "number", "number", "number", "number"]);

    var _screenToTerrainPointWrap = cwrap(
        "screenToTerrainPoint", "number", ["number", "number", "number", "number"]);

    var _worldToScreen = function(lat, long, alt) {
        var screenPos = [0, 0, 0];
        emscriptenMemory.passDoubles(screenPos, function(resultArray, arraySize) {
            _worldToScreenWrap(_apiPointer, lat, long, alt, resultArray);
            screenPos = emscriptenMemory.readDoubles(resultArray, 3);
        });
        return new space.Vector3(screenPos);
    };

    var _screenToTerrainPoint = function(screenX, screenY) {
        var latLngAltArray = [0, 0, 0];
        var foundWorldPoint = false;
        emscriptenMemory.passDoubles(latLngAltArray, function(resultArray, arraySize) {
            var success = _screenToTerrainPointWrap(_apiPointer, screenX, screenY, resultArray);
            foundWorldPoint = !!success;
            latLngAltArray = emscriptenMemory.readDoubles(resultArray, 3);
        })
        return (foundWorldPoint) ? L.latLng(latLngAltArray) : null;
    };

    this.worldToScreen = function(position) {
        var point = L.latLng(position);
        return _worldToScreen(point.lat, point.lng, point.alt || 0);
    };

    this.screenToTerrainPoint = function(screenPoint) {
        var point = L.point(screenPoint);
        return _screenToTerrainPoint(point.x, point.y);
    };
}

module.exports = EmscriptenSpacesApi;

Add internal API point for picking terrain points


module.exports.in  = function(_msg) {
  if (_msg.properties.contentType === 'application/json') {
    try {
      return JSON.parse(_msg.content.toString());
    } catch(e) {}
  }

  return _msg.content.toString();
};

module.exports.out = function(content, options) {
  if (content !== undefined) {
    content = JSON.stringify(content);
    options.contentType = 'application/json';
  }

  return new Buffer(content);
};

module.exports.in  = function(_msg) {
  if (_msg.properties.contentType === 'application/json') {
    try {
      return JSON.parse(_msg.content.toString());
    } catch(e) {}
  }

  return _msg.content.toString();
};

module.exports.out = function(content, options) {
  if (content !== undefined && typeof content !== 'string') {
    content = JSON.stringify(content);
    options.contentType = 'application/json';
  }

  return new Buffer(content);
};

Fix bad formatting for string messages


const rules = require("./rules");
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const CleanWebpackPlugin = require("clean-webpack-plugin");
const UglifyJSPlugin = require("uglifyjs-webpack-plugin");
const path = require("path");

module.exports = {
    context: path.join(__dirname, ".."),
    entry: ["./src/index.ts"],
    output: {
        filename: "build.js",
        path: path.resolve("./dist"),
        publicPath: "/"
    },
    devtool: "",
    resolve: {
        extensions: [
            ".ts",
            ".js",
            ".json"
        ]
    },
    resolveLoader: {
        modules: ["node_modules"]
    },
    plugins: [
        new CleanWebpackPlugin(["dist"], {
            root: path.join(__dirname, "..")
        }),

        new UglifyJSPlugin({
            uglifyOptions: {
                ecma: 5,
                compress: {
                    dead_code: true,
                    drop_console: true,
                    toplevel: true,
                    unsafe_proto: true,
                    warnings: true
                },
                output: {
                    indent_level: 2,
                    beautify: false
                }
            }
        }),

        new webpack.optimize.CommonsChunkPlugin({
            name: "manifest",
            filename: "manifest.js",
            minChunks: Infinity
        }),

        new HtmlWebpackPlugin({
            template: "./src/index.html",
            inject: "body",
            hash: true
        })
    ],
    module: {
        rules: rules
    }
};

const rules = require("./rules");
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const CleanWebpackPlugin = require("clean-webpack-plugin");
const UglifyJSPlugin = require("uglifyjs-webpack-plugin");
const path = require("path");

module.exports = {
    context: path.join(__dirname, ".."),
    entry: ["./src/index.ts"],
    output: {
        filename: "build.js",
        path: path.resolve("./dist"),
        publicPath: "/"
    },
    devtool: "source-map",
    resolve: {
        extensions: [
            ".ts",
            ".js",
            ".json"
        ]
    },
    module: {
        rules: rules
    },
    plugins: [
        new CleanWebpackPlugin(["dist"], {
            root: path.join(__dirname, "..")
        }),

        new UglifyJSPlugin({
            uglifyOptions: {
                ecma: 5,
                compress: {
                    dead_code: true,
                    drop_console: true,
                    toplevel: true,
                    unsafe_proto: true,
                    warnings: true
                },
                output: {
                    indent_level: 2,
                    beautify: false
                }
            }
        }),

        new webpack.optimize.CommonsChunkPlugin({
            name: "manifest",
            filename: "manifest.js",
            minChunks: Infinity
        }),

        new HtmlWebpackPlugin({
            template: "./src/index.html",
            inject: "body",
            hash: true
        })
    ]
};

Delete useless configs. Replace module section. Added devtool


import React from 'react';
import PropTypes from 'prop-types';
import { Table } from 'components';

function WidgetsPage(props) {
  return (

  );
}

WidgetsPage.propTypes = {
  widgets: PropTypes.array.isRequired
};

export default WidgetsPage;

import React from 'react';
import PropTypes from 'prop-types';
import { Table } from 'components';

function WidgetsPage(props) {
  const rows = props.widgets.map(widget => ({
    id: { value: widget.id },
    name: { value: widget.name, sortable: true },
    description: { value: widget.description }
  }));

  return (

 console.log(params)}
        />

  );
}

WidgetsPage.propTypes = {
  widgets: PropTypes.array.isRequired
};

export default WidgetsPage;

Configure the table component for the widgets listing


angular.module('ng-oboe', [])
	.provider('oboe', function () {
		// TODO: Add defaults.

		var oboe = function () {
			var configDefaults = {
				url: ''
				method: 'GET'
				headers: {}
				body: ''
				cached: false
				withCredentials: false
			};

			var get = function (url, config) {
				return new Oboe({});
			};
			var post = function (url, data, config) {
				return new Oboe({});
			};
			var put = function (url, data, config) {
				return new Oboe({});
			};
			var patch = function (url, data, config) {
				return new Oboe({});
			};
			var del = function (url, config) {
				return new Oboe({});
			};

			return {
				get: get,
				post: post,
				put: put,
				patch: patch,
				"delete": del
			};
		};

		this.$get = oboe;
	});

angular.module('myApp', ['ng-oboe'])
	.controller('myCtrl', function ($scope, oboe) {
		$scope.test = "Yo.";
	});

angular.module('ng-oboe', [])
	.provider('oboe', function () {
		// TODO: Add defaults.

		var oboe = function () {
			var configDefaults = {
				url: '',
				method: 'GET',
				headers: {},
				body: null,
				cached: false,
				withCredentials: false
			};

			var request = function (url, data, config, method) {
				var params = config || configDefaults; // TODO: Merge these two.
				params.method = method;
				params.body = data;
				params.url = url;
				return new Oboe(params);
			};

			// TODO: Wrap oboe methods in angular parameters that update digest and return that object.

			return {
				get: function (url, config) {
					return request(url, null, config, 'GET');
				},
				post: function (url, data, config) {
					return request(url, data, config, 'POST');
				},
				put: function (url, data, config) {
					return request(url, data, config, 'PUT');
				},
				patch: function (url, data, config) {
					return request(url, data, config, 'PATCH');
				},
				"delete": function (url, config) {
					return request(url, null, config, 'DELETE');
				}
			};
		};

		this.$get = oboe;
	});

angular.module('myApp', ['ng-oboe'])
	.controller('myCtrl', function ($scope, oboe) {
		$scope.test = "Yo.";
	});

Refactor oboe provider. Fix syntax issues.


var dbm  = require('db-migrate'),
    type = dbm.dataType;

exports.up = function (db, callback) {
    db.createTable('guests', {
        columns: {
            id: {
                autoIncrement: true,
                primaryKey   : true,
                type         : 'int'
            },

            invitation_id: 'int',

            title: 'text',
            name : 'text',
            email: 'text',

            is_attending: 'boolean',
            is_plusone  : {type: 'boolean', defaultValue: false}
        },

        ifNotExists: true
    }, function (err) {
        if (err) { return callback(err); }

        db.runSql(
            'ALTER TABLE guests ' +
            'ADD FOREIGN KEY (invitation_id) REFERENCES invitations;', callback);
    });
};

exports.down = function (db, callback) {
    db.dropTable('guests', {ifExists: true}, callback);
};

var dbm  = require('db-migrate'),
    type = dbm.dataType;

exports.up = function (db, callback) {
    db.createTable('guests', {
        columns: {
            id: {
                autoIncrement: true,
                primaryKey   : true,
                type         : 'int'
            },

            invitation_id: 'int',

            title: {type: 'string', length: 8},
            name : {type: 'string', length: 64},
            email: {type: 'string', length: 128},

            is_attending: 'boolean',
            is_plusone  : {type: 'boolean', defaultValue: false}
        },

        ifNotExists: true
    }, function (err) {
        if (err) { return callback(err); }

        db.runSql(
            'ALTER TABLE guests ' +
            'ADD FOREIGN KEY (invitation_id) REFERENCES invitations;', callback);
    });
};

exports.down = function (db, callback) {
    db.dropTable('guests', {ifExists: true}, callback);
};

Update guests table migation to use varchars.


miqHttpInject(angular.module('alertsCenter', [
  'ui.bootstrap',
  'patternfly',
  'miq.util',
  'miq.api'
]));

miqHttpInject(angular.module('alertsCenter', [
  'ui.bootstrap',
  'patternfly',
  'miq.util',
  'miq.api',
  'miqStaticAssets.miqSelect'
]));

Use miqStaticAssets.miqSelect in alerts center angular module


function compressStr(str) {
	var output = "", // will return this variable as final compressed string
		currChar = "", // represents current character being looked at
		currCount = "", // counts number of times character is repeated
		maxCount = ""; // maximum count

}

// Method that performs basic string compression using the counts of repeated characters

function compressStr(str) {
	var output = "", // will return this variable as final compressed string
		currChar = "", // represents character we are searching for in string
		currCount = "", // counts number of times character is repeated
		maxCount = ""; // maximum count

	for (i = 0; i < str.length; i++) {
		// if the character we are searching for in string matches the character being looked at
		if ( currChar !== str[i]) {
			output = output + currChar + currCount; // output is sum of the output we have so far plus the character we have been searching for in our string plus the number of times that character is repeated
			maxCount = Math.max(maxCount, currCount); // choose the larger value between maxCount and currCount
			currChar = str[i]; // move on to the next character
			currCount = 1; // count resets to 1 since we have moved on to the next character
		}
	}

}

Write what happens when we are searching for doesn't match the character in the string being looked at


app = app || {};

app.QuestionView = Backbone.View.extend({

  events: {
    'submit .question_form': 'submitQuestion',
  },

  initializeTemplates: function(){
    _.templateSettings = { interpolate: /\{\{(.+?)\}\}/g };
    this.template = _.template( $('#question_template').html() )
  },

  initialize: function(){
    this.initializeTemplates();
  },

  render: function(){},

  submitQuestion: function(e){
    e.preventDefault()
  }

});

app = app || {};

app.QuestionView = Backbone.View.extend({

  events: {
    'submit .question_form': 'submitQuestion',
  },

  initializeTemplates: function(){
    _.templateSettings = { interpolate: /\{\{(.+?)\}\}/g };
    this.template = _.template( $('#question_template').html() )
  },

  initialize: function(){
    this.initializeTemplates();
    this.$title = $(".new_question");
    this.$content = $(".new_content");
    this.$form = $(".question_form");
    this.$show = $(".formulated_question")
    this.model = new app.Question();
  },

  render: function(){},

  submitQuestion: function(e){
    e.preventDefault()
    this.model.save({title: this.$title.val(), content: this.$content.val()})
    console.log('geez')
  }

});

Add selector properties to Backbone question view initialization and write function for ajax submission of model


var exec = require('child_process').exec;
var fs = require('fs');
var temp = require('temp');

module.exports = function (options, data, key, callback) {
    var tmpFile = temp.path();
    var cmdline = options.opensslPath + ' rsautl';

    cmdline += ' -' + options.operation;
    cmdline += ' -' + options.padding;
    cmdline += ' -inkey ' + tmpFile;

    if (options.operation === 'encrypt' || options.operation === 'verify') {
        cmdline += ' -pubin';
    }

    fs.writeFile(tmpFile, key, function (err) {
        if (err) throw err;

        var execOpts = { encoding: 'utf8' };
        if (options.operation === 'encrypt' || options.operation === 'sign') {
            execOpts.encoding = 'base64';
        }

        var proc = exec(cmdline, execOpts, function (err, stdout, stderr) {
            if (err) throw err;

            callback(null, stdout);
            fs.unlink(tmpFile);
        });

        if (options.operation === 'decrypt' || options.operation === 'verify') {
            proc.stdin.write(new Buffer(data, 'base64'));
        } else {
            proc.stdin.write(data);
        }

        proc.stdin.end();
    });
};

var exec = require('child_process').exec;
var fs = require('fs');
var temp = require('temp');

module.exports = function (options, data, key, callback) {
    var tmpFile = temp.path();
    var cmdline = options.opensslPath + ' rsautl';

    cmdline += ' -' + options.operation;
    cmdline += ' -' + options.padding;
    cmdline += ' -inkey ' + tmpFile;

    if (options.operation === 'encrypt' || options.operation === 'verify') {
        cmdline += ' -pubin';
    }

    fs.writeFile(tmpFile, key, function (err) {
        if (err) {
            fs.unlink(tmpFile);
            return callback(err);
        }

        var execOpts = { encoding: 'utf8' };
        if (options.operation === 'encrypt' || options.operation === 'sign') {
            execOpts.encoding = 'base64';
        }

        var proc = exec(cmdline, execOpts, function (err, stdout, stderr) {
            if (err) {
                fs.unlink(tmpFile);
                return callback(err);
            }

            callback(null, stdout);
            fs.unlink(tmpFile);
        });

        if (options.operation === 'decrypt' || options.operation === 'verify') {
            proc.stdin.write(new Buffer(data, 'base64'));
        } else {
            proc.stdin.write(data);
        }

        proc.stdin.end();
    });
};

Remove temp files on errors


import path from 'path';

import {assign} from 'bound-native-methods/object';
import Vorpal from 'vorpal';
import JSONFilePlus from 'json-file-plus';

const cli = Vorpal();

cli.command('config', 'Configures this project.')
   .action(function (args, callback) {
     const {log, prompt} = this;

     log();

     prompt(
       [{
         name: 'name',
         message: 'Enter the package name: '
       }, {
         name: 'description',
         message: 'Enter the package description: '
       }, {
         name: 'url',
         message: 'Enter the URL of the repository: '
       }, {
         type: 'confirm',
         name: 'private',
         message: 'Is the project private?'
       }],

       async ({url, ...answers}) => {
         const file = await JSONFilePlus(path.join(process.cwd(), 'package.json'));

         file.set(answers::assign({
           repository: {url}
         }));

         log();

         try {
           await file.save(callback);

           log('Configuration was successfully saved.');
         } catch (error) {
           log(`Error while saving the configuration! ${error}`);
         }

         log();
       }
     );
   });

cli.delimiter('backtier>').show().exec('config');

import path from 'path';

import {assign} from 'bound-native-methods/object';
import Vorpal from 'vorpal';
import JSONFilePlus from 'json-file-plus';

const cli = Vorpal();

cli.command('config', 'Configures this project.')
   .action(function (args, callback) {
     const {log, prompt} = this;

     log();

     prompt(
       [{
         name: 'name',
         message: 'Enter the package name: '
       }, {
         name: 'description',
         message: 'Enter the package description: '
       }, {
         name: 'url',
         message: 'Enter the URL of the repository: '
       }, {
         type: 'confirm',
         name: 'private',
         message: 'Is the project private?'
       }],

       async ({url, ...answers}) => {
         const file = await JSONFilePlus(path.join(process.cwd(), 'package.json'));

         file.set(answers::assign({
           repository: {url}
         }));

         log();

         try {
           await file.save();

           log('Configuration was successfully saved.');
         } catch (error) {
           log(`Error while saving the configuration! ${error}`);
         }

         log();

         cli.ui.cancel();
       }
     );
   });

cli.delimiter('backtier>').show().exec('config');

Terminate the interactive prompt upon completion


const defaultPort = 3001
const defaultHost = 'localhost'
const httpURL = `http://${defaultHost}:${defaultPort}`
const wsURL = `ws://${defaultHost}:${defaultPort}`

export {
  defaultPort,
  defaultHost,
  httpURL,
  wsURL
}

const defaultPort = 3001
const defaultHost = 'localhost'
// Uglify can't seem to handle ES6 string templating yet
const defaultAddr = defaultHost + ':' + parseInt(defaultPort)
const httpURL = 'http://' + defaultAddr // `http://${defaultHost}:${defaultPort}`
const wsURL =  'ws://' + defaultAddr // `ws://${defaultHost}:${defaultPort}`

export {
  defaultPort,
  defaultHost,
  httpURL,
  wsURL
}

Convert ES6 string templates to ordinary + expressions because of Uglify


'use strict';
var generators = require('yeoman-generator');

module.exports = generators.Base.extend({
  constructor: function () {
    generators.Base.apply(this, arguments);

    this.option('ui', {
      desc: 'Choose your style of DSL (bdd, tdd)',
      type: String
    });

    this.option('rjs', {
      desc: 'Add support for RequireJS',
      type: Boolean
    });
  },

  prompting: function () {
    var done = this.async();

    var prompts = [{
      type: 'list',
      name: 'ui',
      message: 'Choose your style of DSL',
      choices: ['BDD', 'TDD'],
      default: 'BDD',
      when: !this.options.ui
    }];

    this.prompt(prompts, function (answers) {
      this.options.ui = (this.options.ui || answers.ui).toLowerCase();
      done();
    }.bind(this));
  },

  configuring: function () {
    this.config.set('ui', this.options.ui);
    this.config.set('rjs', !!this.options.rjs);
  },

  writing: function () {
    this.template('test.js', 'test/spec/test.js');
    this.template('index.html', 'test/index.html');
  },

  install: function () {
    if (this.options['skip-install']) {
      return;
    }

    var dependencies = [
      'chai',
      'mocha'
    ];

    if (this.options.rjs) {
      dependencies.push('requirejs');
    }

    this.bowerInstall(dependencies, {saveDev: true});
  }
});

'use strict';
var generators = require('yeoman-generator');

module.exports = generators.Base.extend({
  constructor: function () {
    generators.Base.apply(this, arguments);

    this.option('ui', {
      desc: 'Choose your style of test DSL for Mocha (bdd, tdd)',
      type: String
    });

    this.option('rjs', {
      desc: 'Add support for RequireJS',
      type: Boolean
    });
  },

  prompting: function () {
    var done = this.async();

    var prompts = [{
      type: 'list',
      name: 'ui',
      message: 'Choose your style of DSL',
      choices: ['BDD', 'TDD'],
      default: 'BDD',
      when: !this.options.ui
    }];

    this.prompt(prompts, function (answers) {
      this.options.ui = (this.options.ui || answers.ui).toLowerCase();
      done();
    }.bind(this));
  },

  configuring: function () {
    this.config.set('ui', this.options.ui);
    this.config.set('rjs', !!this.options.rjs);
  },

  writing: function () {
    this.template('test.js', 'test/spec/test.js');
    this.template('index.html', 'test/index.html');
  },

  install: function () {
    if (this.options['skip-install']) {
      return;
    }

    var dependencies = [
      'chai',
      'mocha'
    ];

    if (this.options.rjs) {
      dependencies.push('requirejs');
    }

    this.bowerInstall(dependencies, {saveDev: true});
  }
});

Make the question more clear (for composition)


// Abuse reports
//
'use strict';

const Mongoose = require('mongoose');
const Schema   = Mongoose.Schema;

module.exports = function (N, collectionName) {

  let AbuseReport = new Schema({

    // _id of reported content
    src: Schema.ObjectId,

    // N.shared.content_type (FORUM_POST, BLOG_ENTRY, ...)
    type: Number,

    // Report text
    text: String,

    // Parser options
    params_ref: Schema.ObjectId,

    // User _id
    from: Schema.ObjectId
  }, {
    versionKey: false
  });

  N.wire.on('init:models', function emit_init_AbuseReport() {
    return N.wire.emit('init:models.' + collectionName, AbuseReport);
  });

  N.wire.on('init:models.' + collectionName, function init_model_AbuseReport(schema) {
    N.models[collectionName] = Mongoose.model(collectionName, schema);
  });
};

// Abuse reports
//
'use strict';

const Mongoose = require('mongoose');
const Schema   = Mongoose.Schema;

module.exports = function (N, collectionName) {

  let AbuseReport = new Schema({

    // _id of reported content
    src: Schema.ObjectId,

    // N.shared.content_type (FORUM_POST, BLOG_ENTRY, ...)
    type: Number,

    // Report text
    text: String,

    // Flag defining whether this report was created automatically
    auto_reported: Boolean,

    // Parser options
    params_ref: Schema.ObjectId,

    // User _id
    from: Schema.ObjectId
  }, {
    versionKey: false
  });

  // Duplicate check for auto-created abuse reports
  AbuseReport.index({ src: 1, auto_reported: 1 }, { sparse: true });

  N.wire.on('init:models', function emit_init_AbuseReport() {
    return N.wire.emit('init:models.' + collectionName, AbuseReport);
  });

  N.wire.on('init:models.' + collectionName, function init_model_AbuseReport(schema) {
    N.models[collectionName] = Mongoose.model(collectionName, schema);
  });
};

Add auto abuse reports after multiple downvotes


'use strict';

/**
 * Module dependencies.
 */
var _ = require('lodash'),
	errorHandler = require('../errors.server.controller'),
	mongoose = require('mongoose'),
	passport = require('passport'),
	User = mongoose.model('User');

/**
 * View all users
 */
exports.all = function(req, res) {
  User.find({_id: {$ne: req.user._id}}, {displayName: 1}, function(err, docs) {
    return res.status(200).send(docs);
  });
};

exports.sendMessage = function(req, res) {
	var newMessage = {text: req.body.message[0], from: req.user.displayName};
	var user = req.user;
	user.messages.push(newMessage);
	user.save(function(err) {
		console.log(err);
	});
	return res.status(200).send('ok');
};

'use strict';

/**
 * Module dependencies.
 */
var _ = require('lodash'),
	errorHandler = require('../errors.server.controller'),
	mongoose = require('mongoose'),
	passport = require('passport'),
	User = mongoose.model('User');

/**
 * View all users
 */
exports.all = function(req, res) {
  User.find({_id: {$ne: req.user._id}}, {displayName: 1}, function(err, docs) {
    return res.status(200).send(docs);
  });
};

exports.sendMessage = function(req, res) {
	var newMessage = {text: req.body.message[0], from: req.user.displayName};
	var user = req.user;
	user.messages.push(newMessage);
	user.save(function(err) {
		return res.status(400).send('Проблемы при отправке сообщения.');
	});
	return res.status(200).send('Сообщение добавлено.');
};

Add message about problems while saving message


var command = {
  command: "console",
  description:
    "Run a console with contract abstractions and commands available",
  builder: {},
  help: {
    usage: "truffle console [--network <name>] [--verbose-rpc]",
    options: [
      {
        option: "--network <name>",
        description:
          "Specify the network to use. Network name must exist in the configuration."
      },
      {
        option: "--verbose-rpc",
        description:
          "Log communication between Truffle and the Ethereum client."
      }
    ]
  },
  run: function(options, done) {
    var Config = require("truffle-config");
    var Console = require("../console");
    var Environment = require("../environment");

    var config = Config.detect(options);

    // This require a smell?
    var commands = require("./index");
    var excluded = ["console", "init", "watch", "develop"];

    var available_commands = Object.keys(commands).filter(function(name) {
      return excluded.indexOf(name) == -1;
    });

    var console_commands = {};
    available_commands.forEach(function(name) {
      console_commands[name] = commands[name];
    });

    Environment.detect(config, function(err) {
      if (err) return done(err);

      var c = new Console(
        console_commands,
        config.with({
          noAliases: true
        })
      );
      c.start(done);
    });
  }
};

module.exports = command;

var command = {
  command: "console",
  description:
    "Run a console with contract abstractions and commands available",
  builder: {},
  help: {
    usage: "truffle console [--network <name>] [--verbose-rpc]",
    options: [
      {
        option: "--network <name>",
        description:
          "Specify the network to use. Network name must exist in the configuration."
      },
      {
        option: "--verbose-rpc",
        description:
          "Log communication between Truffle and the Ethereum client."
      }
    ]
  },
  run: function(options, done) {
    var Config = require("truffle-config");
    var Console = require("../console");
    var Environment = require("../environment");

    var config = Config.detect(options);

    // This require a smell?
    var commands = require("./index");
    var excluded = ["console", "init", "watch", "develop"];

    var available_commands = Object.keys(commands).filter(function(name) {
      return excluded.indexOf(name) === -1;
    });

    var console_commands = {};
    available_commands.forEach(function(name) {
      console_commands[name] = commands[name];
    });

    Environment.detect(config, function(err) {
      if (err) return done(err);

      var c = new Console(
        console_commands,
        config.with({
          noAliases: true
        })
      );
      c.start(done);
    });
  }
};

module.exports = command;

Convert Non-strict to strict equality checking Convert non-strict equality checking, using `==`, to the strict version, using `===`.


'use strict';

var morphopedia = angular.module( 'morphopedia',
[
	'ngResource',

	'matchMedia',

	'angular-preload-image',

	'leaflet-directive',

	'angular.filter',

	'root',

	'mobile-angular-ui'

] );

morphopedia.config( function(  $urlRouterProvider, $locationProvider, $sceDelegateProvider, $logProvider )
{

	$logProvider.debugEnabled(false);

	$urlRouterProvider.otherwise( '/' );
	$locationProvider.html5Mode( true );

	// Whitelist AWS for asset-loading
	$sceDelegateProvider.resourceUrlWhitelist([
		// Allow same origin resource loads.
		'self',
		// Allow loading from our assets domain.
		'https://morphmorphupdated2.s3.amazonaws.com/**'
		]);

} );

morphopedia.run( [ '$rootScope', function( $rootScope )
{
	// For Mobile Angular UI compatibility
	$rootScope.$on( '$stateChangeStart', function(  )
	{
		$rootScope.$broadcast( '$routeChangeStart' );
	} );

	$rootScope.$on( '$stateChangeSuccess', function( event, toState, toParams, fromState, fromParams )
	{
		$rootScope.fromState = fromState;
		$rootScope.fromParams = fromParams;

		$rootScope.toState = toState;
		$rootScope.toParams = toParams;
	} );

} ] );

'use strict';

var morphopedia = angular.module( 'morphopedia',
[
	'ngResource',

	'matchMedia',

	'angular-preload-image',

	'leaflet-directive',

	'angular.filter',

	'root',

	'mobile-angular-ui'

] );

morphopedia.config( function(  $urlRouterProvider, $locationProvider, $sceDelegateProvider, $logProvider )
{

	$logProvider.debugEnabled(false);

	$urlRouterProvider.otherwise( '/' );
	$locationProvider.html5Mode( true );

	// Whitelist AWS for asset-loading
	$sceDelegateProvider.resourceUrlWhitelist([
		// Allow same origin resource loads.
		'self',
		// Allow loading from our assets domain.
		'https://morphassets.s3.amazonaws.com/**'
		]);

} );

morphopedia.run( [ '$rootScope', function( $rootScope )
{
	// For Mobile Angular UI compatibility
	$rootScope.$on( '$stateChangeStart', function(  )
	{
		$rootScope.$broadcast( '$routeChangeStart' );
	} );

	$rootScope.$on( '$stateChangeSuccess', function( event, toState, toParams, fromState, fromParams )
	{
		$rootScope.fromState = fromState;
		$rootScope.fromParams = fromParams;

		$rootScope.toState = toState;
		$rootScope.toParams = toParams;
	} );

} ] );

Add new resource URL to whitelist


var http = require('http');
var url = require('url');

var renderPng = function (req, res) {
  var options = url.parse(renderPng.getScreenshotPath(req));
  http.get(options, function (screenshot) {
    res.status(screenshot.statusCode);
    for (var i in screenshot.headers) {
      res.setHeader(i, screenshot.headers[i]);
    }
    screenshot.pipe(res);
  }).on('error', function (e) {
    res.status(500);
    res.send('Got error: ' + e.message);
  });
};

if (global.config) {
  renderPng.screenshotServiceUrl = config.screenshotServiceUrl;
  renderPng.screenshotTargetUrl = config.screenshotTargetUrl;
}

renderPng.getScreenshotPath = function (req) {
  var url = req.url;
  var selector = req.query.selector || '.visualisation-inner figure';
  return [
    renderPng.screenshotServiceUrl,
    '?readyExpression=!!document.querySelector(".loaded")',
    '&forwardCacheHeaders=true',
    '&clipSelector=' + selector,
    '&url=',
    renderPng.screenshotTargetUrl,
    url.replace(/.png/g, '')
  ].join('');
};

module.exports = renderPng;

var http = require('http');
var url = require('url');

var requirejs = require('requirejs');

var ErrorController = require('./server/controllers/error');
var Model = requirejs('extensions/models/model');
var PageConfig = requirejs('page_config');

var renderPng = function (req, res) {
  var options = url.parse(renderPng.getScreenshotPath(req));
  http.get(options, function (screenshot) {
    if (screenshot.statusCode > 399) {
      return renderError(screenshot.statusCode, req, res);
    } else {
      res.status(screenshot.statusCode);
      for (var i in screenshot.headers) {
        res.setHeader(i, screenshot.headers[i]);
      }
      screenshot.pipe(res);
    }
  }).on('error', function () {
    renderError(500, req, res);
  });
};

var renderError = function (status, req, res) {
  var model = new Model({
    status: status || 500
  });
  model.set(PageConfig.commonConfig(req));
  var error = new ErrorController({
    model: model
  });
  error.render();
  res.statusCode = status;
  res.send(error.html);
};

if (global.config) {
  renderPng.screenshotServiceUrl = config.screenshotServiceUrl;
  renderPng.screenshotTargetUrl = config.screenshotTargetUrl;
}

renderPng.getScreenshotPath = function (req) {
  var url = req.url;
  var selector = req.query.selector || '.visualisation-inner figure';
  return [
    renderPng.screenshotServiceUrl,
    '?readyExpression=!!document.querySelector(".loaded")',
    '&forwardCacheHeaders=true',
    '&clipSelector=' + selector,
    '&url=',
    renderPng.screenshotTargetUrl,
    url.replace(/.png/g, '')
  ].join('');
};

module.exports = renderPng;

Add "nice" error pages for SSaaS failures


var smash = require("smash"),
    d3 = require("d3");

module.exports = function() {
  var files = [].slice.call(arguments),
      sandbox = {d3: d3};
  files.unshift("start");
  files.push("end");

  function topic() {
    smash.load(files, "d3.geo", sandbox, this.callback);
  }

  topic.sandbox = function(_) {
    sandbox = _;
    return topic;
  };

  return topic;
};

var smash = require("smash"),
    d3 = require("d3");

module.exports = function() {
  var files = [].slice.call(arguments).map(function(d) { return "src/" + d; }),
      sandbox = {d3: d3};
  files.unshift("src/start");
  files.push("src/end");

  function topic() {
    smash.load(files, "d3.geo", sandbox, this.callback);
  }

  topic.sandbox = function(_) {
    sandbox = _;
    return topic;
  };

  return topic;
};

Fix tests for relocated source files.


import {
  TICK,
} from './ActionTypes';

class RunLoop {
  constructor() {
    this.store = null;
    this._listeners = [];

    let prev = Date.now();

    let runLoop = () => {
      const now = Date.now();
      const dt = now - prev;
      prev = now;

      this.store.dispatch({
        type: TICK,
        dt,
      });

      this._listeners.forEach((listener) => listener());

      window.requestAnimationFrame(runLoop);
    };

    this.unsetPlaybackRunLoop = () => {
      runLoop = () => {};
    };

    window.requestAnimationFrame(runLoop);
  }

  setStore(store) {
    this.store = store;
  }

  subscribe(listener) {
    this._listeners.push(listener);
  }

  unsubscribe(listener) {
    const idx = this._listeners.indexOf(listener);

    if (idx === -1) {
      throw new Error('tried to unsubscribe listener that wasn\'t subscribed');
    }

    this._listeners.splice(idx, 1);
  }
}

const runLoop = new RunLoop();

export default runLoop;

import {
  TICK,
} from './ActionTypes';

class RunLoop {
  constructor() {
    this.store = null;
    this._listeners = [];

    this._lastTickMs = Date.now();

    window.requestAnimationFrame(this._runLoop.bind(this));
  }

  _runLoop() {
    const now = Date.now();
    const dt = now - this._lastTickMs;
    this._lastTickMs = now;

    this.store.dispatch({
      type: TICK,
      dt,
    });

    this._listeners.forEach((listener) => listener());

    window.requestAnimationFrame(this._runLoop.bind(this));
  }

  setStore(store) {
    this.store = store;
  }

  subscribe(listener) {
    this._listeners.push(listener);
  }

  unsubscribe(listener) {
    const idx = this._listeners.indexOf(listener);

    if (idx === -1) {
      throw new Error('tried to unsubscribe listener that wasn\'t subscribed');
    }

    this._listeners.splice(idx, 1);
  }
}

const runLoop = new RunLoop();

export default runLoop;

Clean up runloop now that it's always on


var Limit = require('../../ratelimiter/limit');

var l1 = new Limit("{sourceName}:global:daily", 86400, 4000);
var l2 = new Limit("{sourceName}:global:hourly", 3600, 2000);
var l3 = new Limit("{sourceName}:{userID}:daily", 86400, 500);
var l4 = new Limit("{sourceName}:{userID}:hourly", 3600, 100);

var limits = [l1,l2,l3,l4];

module.exports = limits;

var Limit = require('../../ratelimiter/limit');

// First limit set for multi limit tests
var l1 = new Limit("{sourceName}:global:daily", 86400, 4000),
	l2 = new Limit("{sourceName}:global:hourly", 3600, 2000),
	l3 = new Limit("{sourceName}:{userID}:daily", 86400, 500),
	l4 = new Limit("{sourceName}:{userID}:hourly", 3600, 100);

// Second limit set for expire tests
var l5 = new Limit("{sourceName}:global:daily", 86400, 4000),
	l6 = new Limit("{sourceName}:global:hourly", 3600, 2000),
	l7 = new Limit("{sourceName}:{userID}:daily", 86400, 500),
	l8 = new Limit("{sourceName}:{userID}:hourly", 3600, 100);

var limits = [[l1,l2,l3,l4],[l5,l6,l7,l8]];

module.exports = limits;

Add more to the test data set


"use strict";

var _ = require('underscore')
  , Project = require('./project')

/*
 * Mixin for models which require a project to be set.
 *
 * Looks for attribute, collection, and explicit passing in options. Will raise
 * an error if no project is found, or if different projects are found.
 */
module.exports = {
  constructor: function (attributes, options) {
    var candidates, results, slug;

    candidates = {
      options: options && options.project,
      collection: options && options.collection && options.collection.project,
      attributes: attributes && attributes.project
    }

    if (candidates.attributes) {
      slug = candidates.attributes.url.match(/[^\/]+/g).slice(-1);
      candidates.attributes = new Project({
        name: candidates.attributes.name,
        slug: slug
      });
    }

    results = _.chain(candidates).filter(function (p) { return p instanceof Project });

    if (!results.value().length) {
      throw new Error('Must pass a project object, either in options, collection, or attributes.');
    }

    if (results.map(function (p) { return p.get('slug') }).uniq().value().length > 1) {
      throw new Error('Two different projects passed. Not possible.')
    }

    // Take the first result
    this.project = results.first().value();
  }
}

"use strict";

var _ = require('underscore')
  , Project = require('./project')

/*
 * Mixin for models which require a project to be set.
 *
 * Looks for attribute, collection, and explicit passing in options. Will raise
 * an error if no project is found, or if different projects are found.
 */
module.exports = {
  constructor: function (attributes, options) {
    var candidates, results, slug;

    candidates = {
      options: options && options.project,
      collection: options && options.collection && options.collection.project,
      attributes: attributes && attributes.project
    }

    if (candidates.attributes) {
      slug = candidates.attributes.url.match(/[^\/]+/g).slice(-1);
      candidates.attributes = new Project({
        name: candidates.attributes.name,
        slug: slug
      });
    }

    results = _.chain(candidates).filter(function (p) { return p instanceof Project });

    if (!results.value().length) {
      throw new Error('Must pass a project object, either in options, collection, or attributes.');
    }

    if (results.map(function (p) { return p.get('slug') }).flatten().uniq().value().length > 1) {
      throw new Error('Two different projects passed.')
    }

    // Take the first result
    this.project = results.first().value();
  }
}

Fix bug in project-specific backbone model mixin


var flash = require('connect-flash');

module.exports = function (app, security) {
    var isAuthenticated = security.middleware.isAuthenticated({redirect:'/backend/login'});
    var hasPermission   = security.middleware.hasPermission;

    app.get('/', [isAuthenticated, hasPermission(['backend:login'], {redirect:'/backend/login'})], function (req, res) {
        res.render(__dirname + '/build/templates/index.ejs', { env: process.env.NODE_ENV, username: req.user.username });
    });

    // Login Form
    app.get('/login', function (req, res) {
        if (req.user) return res.redirect('/backend/');

        res.render(__dirname + '/templates/login.ejs', { error: req.flash('error'), info: req.flash('info') });
    });

    // Logout request
    app.get('/logout', function (req, res) {
        flash('info','Logged out');
        req.logout();
        req.session.user = null;
        res.redirect('/backend/login');
    });
}

var flash = require('connect-flash');

module.exports = function (app, security) {
    var isAuthenticated = security.middleware.isAuthenticated({redirect:'/backend/login'});
    var hasPermission   = security.middleware.hasPermission;

    // update install before requesting permissions
    // hasPermission(['backend:login'], {redirect:'/backend/login'})
    app.get('/', [isAuthenticated], function (req, res) {
        res.render(__dirname + '/build/templates/index.ejs', { env: process.env.NODE_ENV, username: req.user.username });
    });

    // Login Form
    app.get('/login', function (req, res) {
        if (req.user) return res.redirect('/backend/');

        res.render(__dirname + '/templates/login.ejs', { error: req.flash('error'), info: req.flash('info') });
    });

    // Logout request
    app.get('/logout', function (req, res) {
        flash('info','Logged out');
        req.logout();
        req.session.user = null;
        res.redirect('/backend/login');
    });
}

Remove permission check on backend


var path = require('path'),
    rootPath = path.normalize(__dirname + '/..');

var dotenv = require('dotenv').config({path: rootPath + '/.env'});

var config = {
    root: rootPath,
    app: {
      name: 'chatbot'
    },
    port: process.env.PORT || 3000,
    db: process.env.DB_CONNECTION_STRING || '',
    page_access_token: process.env.PAGE_ACCESS_TOKEN || '',
    verify_token: process.env.VERIFY_TOCKEN || ''
  };

module.exports = config;

var path = require('path'),
    rootPath = path.normalize(__dirname + '/..');

if (!process.env.IS_HEROKU) {
    var dotenv = require('dotenv').config({path: rootPath + '/.env'});
}

var config = {
    root: rootPath,
    app: {
      name: 'chatbot'
    },
    port: process.env.PORT || 3000,
    db: process.env.DB_CONNECTION_STRING || '',
    page_access_token: process.env.PAGE_ACCESS_TOKEN || '',
    verify_token: process.env.VERIFY_TOCKEN || ''
  };

module.exports = config;

Handle the case where we deploy to Heroku.


'use strict';

const winston = require('winston');

// Setup winston default instance
const LOGGER_LEVEL = process.env.LOGGER_LEVEL || 'info';
const LOGGER_TIMESTAMP = !(process.env.LOGGER_TIMESTAMP === 'false');
const LOGGER_COLORIZE = process.env.LOGGER_COLORIZE === 'true'
const LOGGER_PRETTY_PRINT = process.env.LOGGER_PRETTY_PRINT === 'true';

winston.configure({
  transports: [
    new winston.transports.Console({
      prettyPrint: LOGGER_PRETTY_PRINT,
      colorize: LOGGER_COLORIZE,
      level: LOGGER_LEVEL,
      timestamp: LOGGER_TIMESTAMP,
    }),
  ],
  exceptionHandlers: [
    new winston.transports.Console({
      prettyPrint: LOGGER_PRETTY_PRINT,
      colorize: LOGGER_COLORIZE,
    }),
  ],
});

'use strict';

const winston = require('winston');

// Setup winston default instance
const LOGGER_LEVEL = process.env.LOGGER_LEVEL || 'info';
const LOGGER_TIMESTAMP = !(process.env.LOGGER_TIMESTAMP === 'false');
const LOGGER_COLORIZE = !(process.env.LOGGER_COLORIZE === 'false');
const LOGGER_PRETTY_PRINT = !(process.env.LOGGER_PRETTY_PRINT === 'false');

winston.configure({
  transports: [
    new winston.transports.Console({
      prettyPrint: LOGGER_PRETTY_PRINT,
      colorize: LOGGER_COLORIZE,
      level: LOGGER_LEVEL,
      timestamp: LOGGER_TIMESTAMP,
    }),
  ],
  exceptionHandlers: [
    new winston.transports.Console({
      prettyPrint: LOGGER_PRETTY_PRINT,
      colorize: LOGGER_COLORIZE,
    }),
  ],
});

Enable colorization and pretty printing be default


imagespace.views.FrontPageView = girder.views.FrontPageView.extend({
    initialize: function () {
        girder.cancelRestRequests('fetch');
        this.render();
    },

    render: function () {
        this.$el.html(imagespace.templates.frontPage());
        imagespace.headerView.render();

        var searchBar = new imagespace.views.SearchBarView({
            el: this.$('#search-bar'),
            parentView: this,
            dropzone: girder.currentUser,
            advancedSearch: true,
            frontPage: true
        }).render();

        // Remove search bar from header on front page
        // This is a hack, because at the time of layout instantiation I can't obtain
        // the route from Backbone. The header search bar should never be rendered (on the front page)
        // in the first place.
        $('#header-search-bar').remove();

        $('#search-bar input.im-search').focus();

        return this;
    }
});

imagespace.router.route('', 'index', function (query) {
    girder.events.trigger('g:navigateTo', imagespace.views.FrontPageView);
});

imagespace.views.FrontPageView = girder.views.FrontPageView.extend({
    initialize: function () {
        girder.cancelRestRequests('fetch');
        this.render();
    },

    render: function () {
        this.$el.html(imagespace.templates.frontPage());

        var searchBar = new imagespace.views.SearchBarView({
            el: this.$('#search-bar'),
            parentView: this,
            dropzone: girder.currentUser,
            advancedSearch: true,
            frontPage: true
        }).render();

        // Remove search bar from header on front page
        // This is a hack, because at the time of layout instantiation I can't obtain
        // the route from Backbone. The header search bar should never be rendered (on the front page)
        // in the first place.
        $('#header-search-bar').remove();

        $('#search-bar input.im-search').focus();

        return this;
    }
});

imagespace.router.route('', 'index', function (query) {
    girder.events.trigger('g:navigateTo', imagespace.views.FrontPageView);
});

Fix double rendering of headerView on front page


import React, {PropTypes} from 'react'
import ObjectContainer from '../../../src/state/ObjectContainer'
import Field from '../../../src/Field'
import Fieldset from '../../../src/Fieldset'

export default class MyCustomLatLonInput extends React.Component {
  constructor(props, context) {
    super(props, context)
    this.handleLatChange = this.handleLatChange.bind(this)
    this.handleLonChange = this.handleLonChange.bind(this)
  }

  static valueContainer = ObjectContainer;

  static propTypes = {
    value: PropTypes.object,
    onChange: PropTypes.func
  };

  static defaultProps = {
    onChange() {}
  };

  static contextTypes = {
    resolveInputComponent: PropTypes.func,
    schema: PropTypes.object
  };

  handleLatChange(event) {
    this.handleFieldChange('lat', event.target.value)
  }

  handleLonChange(event) {
    this.handleFieldChange('lon', event.target.value)
  }

  handleFieldChange(fieldName, value) {
    this.props.onChange({
      patch: {
        [fieldName]: {$set: value}
      }
    })
  }

  render() {
    const {value} = this.props
    return (
      <Fieldset title="Langitude and Latitude">
        <Field label="Latitude" role="inFieldset">
          <input type="number" value={value && value.getFieldValue('lat')} onChange={this.handleLatChange} />
        </Field>
        <Field label="Longitude" role="inFieldset">
          <input type="number" value={value && value.getFieldValue('lon')} onChange={this.handleLonChange} />
        </Field>
      </Fieldset>
    )
  }
}

import React, {PropTypes} from 'react'

export default class MyCustomLatLonInput extends React.Component {

  static propTypes = {
    value: PropTypes.object,
    onChange: PropTypes.func
  };

  static defaultProps = {
    onChange() {},
    value: {}
  };

  static contextTypes = {
    resolveInputComponent: PropTypes.func,
    schema: PropTypes.object
  };

  constructor(props, context) {
    super(props, context)
    this.handleLatChange = this.handleLatChange.bind(this)
    this.handleLonChange = this.handleLonChange.bind(this)
  }

  handleLatChange(event) {
    this.handleFieldChange('lat', event.target.value)
  }

  handleLonChange(event) {
    this.handleFieldChange('lon', event.target.value)
  }

  handleFieldChange(fieldName, fieldValue) {
    const {value, onChange} = this.props
    const nextValue = Object.assign({}, value, {
      [fieldName]: fieldValue.trim() ? Number(fieldValue) : void 0
    })
    onChange({patch: {$set: nextValue}})
  }

  render() {
    const {value} = this.props
    return (
      <div>
        lat: <input type="number" value={value.lat || ''} onChange={this.handleLatChange} />
        lon: <input type="number" value={value.lon || ''} onChange={this.handleLonChange} />
      </div>
    )
  }
}

Simplify custom lat lon input


var Minimitter = function Minimitter() {
  listeners = {};
};

Minimitter.prototype.on = function(name, callback) {
  this.listeners[name] = this.listeners[name] || [];
  this.listeners[name].push(callback);
};

Minimitter.prototype.off = function(name, callback) {
  this.listeners[name] = this.listeners[name] || [];
  var indexOfCallback = this.listeners[name].indexOf(callback);
  if(~indexOfCallback) {
    this.listeners[name].slice(indexOfCallback, 1);
  }
};

Minimitter.prototype.emit = function(name, data) {
  this.listeners[name].forEach(function(callback) {
    try {
      callback(data);
    } catch {
      console.error('A callback has failed.');
    }
  });
};

Minimitter = function Minimitter() {
  this.listeners = {};
};

Minimitter.prototype.on = function(name, callback) {
  this.listeners[name] = this.listeners[name] || [];
  this.listeners[name].push(callback);
};

Minimitter.prototype.off = function(name, callback) {
  this.listeners[name] = this.listeners[name] || [];
  var indexOfCallback = this.listeners[name].indexOf(callback);
  if(~indexOfCallback) {
    this.listeners[name].slice(indexOfCallback, 1);
  }
};

Minimitter.prototype.emit = function(name, data) {
  this.listeners[name].forEach(function(callback) {
    try {
      callback(data);
    } catch(err) {
      console.error('A callback has failed.');
    }
  });
};

Fix missing argument from catch and globalize Minimitter.


import {
  FETCHING_RETURN_REQUESTS,
  RETURN_REQUESTS_SUCCESS,
  RETURN_REQUESTS_FAILURE,
}
  from '../actions/types';

const initialState = {};

const returnRequests = (state = initialState, action) => {
  switch (action.type) {
    case FETCHING_RETURN_REQUESTS: {
      return {
        ...state,
        isFetchingReturnRequests: true,
        returnRequests: null,
        returnRequestsError: null,
      };
    }
    case RETURN_REQUESTS_SUCCESS: {
      return {
        ...state,
        isFetchingReturnRequests: false,
        returnRequests: action.returnRequests,
        returnRequestsError: null,
      };
    }
    case RETURN_REQUESTS_FAILURE: {
      return {
        ...state,
        isFetchingReturnRequests: false,
        returnRequestsError: action.error,
      };
    }
    default: {
      return state;
    }
  }
};

export default returnRequests;

import {
  FETCHING_RETURN_REQUESTS,
  RETURN_REQUESTS_SUCCESS,
  RETURN_REQUESTS_FAILURE,
  HANDLING_RETURN_REQUEST,
  HANDLE_RETURN_REQUEST_SUCCESS,
  HANDLE_RETURN_REQUEST_FAILURE,
}
  from '../actions/types';

const initialState = {};

const returnRequests = (state = initialState, action) => {
  switch (action.type) {
    case FETCHING_RETURN_REQUESTS: {
      return {
        ...state,
        isFetchingReturnRequests: true,
        returnRequests: null,
        returnRequestsError: null,
      };
    }
    case RETURN_REQUESTS_SUCCESS: {
      return {
        ...state,
        isFetchingReturnRequests: false,
        returnRequests: action.returnRequests,
        returnRequestsError: null,
      };
    }
    case RETURN_REQUESTS_FAILURE: {
      return {
        ...state,
        isFetchingReturnRequests: false,
        returnRequestsError: action.error,
      };
    }
    case HANDLING_RETURN_REQUEST: {
      return {
        ...state,
        isHandlingReturnRequest: true,
      };
    }
    case HANDLE_RETURN_REQUEST_SUCCESS: {
      return {
        ...state,
        returnRequests: [...state.returnRequests.slice(0, action.index),
          { ...state.returnRequests[action.index], status: action.status.split(' ')[1] },
          ...state.returnRequests.slice(action.index + 1)],
        isHandlingReturnRequest: false,
      };
    }
    case HANDLE_RETURN_REQUEST_FAILURE: {
      Materialize.toast(`Failed to handle request. ${action.error}`, 2000);
      return {
        ...state,
        isHandlingReturnRequest: false,
      };
    }
    default: {
      return state;
    }
  }
};

export default returnRequests;

Add reducer for handling return request


import { createRequestTypes, actionCreators, action } from '../../actions';

export const FETCH_USERS = createRequestTypes('FETCH_USERS');
export const fetchUsers = actionCreators(FETCH_USERS);

export const USER_LOGIN = createRequestTypes('USER_LOGIN');
export const userLogin = actionCreators(USER_LOGIN);

export const CREATE_USER = createRequestTypes('CREATE_USER');
export const createUser = actionCreators(CREATE_USER);

export const EDIT_USER = createRequestTypes('EDIT_USER');
export const editUser = actionCreators(EDIT_USER);

export const ADD_TRUST = createRequestTypes('ADD_TRUST');
export const addTrust = actionCreators(ADD_TRUST);

export const REVOKE_TRUST = createRequestTypes('REVOKE_TRUST');
export const revokeTrust = actionCreators(REVOKE_TRUST);

export const ADD_WATCH = createRequestTypes('ADD_WATCH');
export const addWatch = actionCreators(ADD_WATCH);

export const REVOKE_WATCH = createRequestTypes('REVOKE_WATCH');
export const revokeWatch = actionCreators(REVOKE_WATCH);

export const ADD_SUPPORT = createRequestTypes('ADD_SUPPORT');
export const addSupport = actionCreators(ADD_SUPPORT);

export const REVOKE_SUPPORT = createRequestTypes('REVOKE_SUPPORT');
export const revokeSupport = actionCreators(REVOKE_SUPPORT);

export const SET_SNACKBAR = 'SET_SNACKBAR';
export const setSnackbar = (toast) => action(SET_SNACKBAR, { toast });

export const REMOVE_TOAST = 'REMOVE_TOAST';
export const removeToast = () => action(REMOVE_TOAST);

import { createRequestTypes, actionCreators, action } from '../../actions';

export const SET_SNACKBAR = 'SET_SNACKBAR';
export const setSnackbar = (toast) => action(SET_SNACKBAR, { toast });

export const REMOVE_TOAST = 'REMOVE_TOAST';
export const removeToast = () => action(REMOVE_TOAST);

Move user profile actions out of core


/* @flow */

import React, {Component} from 'react';

import StyleKeeper from '../style-keeper';

export default class StyleSheet extends Component {
  // $FlowStaticPropertyWarning
  static contextTypes = {
    _radiumStyleKeeper: React.PropTypes.instanceOf(StyleKeeper)
  };

  constructor() {
    super();

    this.state = this._getCSSState();
  }

  componentDidMount() {
    this._subscription = this.context._radiumStyleKeeper.subscribe(
      this._onChange
    );
    this._onChange();
  }

  componentWillUnmount() {
    if (this._subscription) {
      this._subscription.remove();
    }
  }

  _getCSSState(): {css: string} {
    return {css: this.context._radiumStyleKeeper.getCSS()};
  }

  _onChange() {
    this.setState(this._getCSSState());
  }

  render(): ReactElement {
    return (
      <style dangerouslySetInnerHTML={{__html: this.state.css}} />
    );
  }
}

/* @flow */

import React, {Component} from 'react';

import StyleKeeper from '../style-keeper';

export default class StyleSheet extends Component {
  // $FlowStaticPropertyWarning
  static contextTypes = {
    _radiumStyleKeeper: React.PropTypes.instanceOf(StyleKeeper)
  };

  constructor() {
    super(...arguments);

    this.state = this._getCSSState();
  }

  componentDidMount() {
    this._subscription = this.context._radiumStyleKeeper.subscribe(
      this._onChange
    );
    this._onChange();
  }

  componentWillUnmount() {
    if (this._subscription) {
      this._subscription.remove();
    }
  }

  _getCSSState(): {css: string} {
    return {css: this.context._radiumStyleKeeper.getCSS()};
  }

  _onChange() {
    this.setState(this._getCSSState());
  }

  render(): ReactElement {
    return (
      <style dangerouslySetInnerHTML={{__html: this.state.css}} />
    );
  }
}

Fix StyleKeeper context after ES6 conversion


'use strict';

Connector.playerSelector = '#new_ajax-player';

Connector.artistSelector = '#hotmixPlayerSongArtist .ajax';

Connector.getArtist = () => getScrollableText('#hotmixPlayerSongArtist');

Connector.getTrack = () => getScrollableText('#hotmixPlayerSongTitle');

//Connector.getAlbum = () => getScrollableText('#songalbum');

Connector.getTrackArt = () => {
	let trackArtUrl = $('#hotmixPlayerSongCover').attr('src');
	return trackArtUrl !== null ? `${trackArtUrl}` : null;
};

Connector.isPlaying = () => $('#play-pause .fa-pause').length > 0;

function getScrollableText(selector) {
	return $(`${selector} a`).first().text() || $(selector).text();
}

'use strict';

Connector.playerSelector = '#new_ajax-player';

Connector.artistSelector = '#hotmixPlayerSongArtist .ajax';

Connector.getArtist = () => getScrollableText('#hotmixPlayerSongArtist');

Connector.getTrack = () => getScrollableText('#hotmixPlayerSongTitle');

Connector.getTrackArt = () => {
	let trackArtUrl = $('#hotmixPlayerSongCover').attr('src');
	return trackArtUrl !== null ? `${trackArtUrl}` : null;
};

Connector.isPlaying = () => $('#play-pause .fa-pause').length > 0;

function getScrollableText(selector) {
	return $(`${selector} a`).first().text() || $(selector).text();
}

Remove commented out code to prevent merge issues


function download(filename, text) {
  const element = document.createElement('a');
  element.setAttribute('href', `data:text/plain;charset=utf-8,${encodeURIComponent(text)}`);
  element.setAttribute('download', filename);
  element.style.display = 'none';
  document.body.appendChild(element);
  element.click();
  document.body.removeChild(element);
}

module.exports = { download };

function download(filename, text, click = true) {
  const element = document.createElement('a');
  element.setAttribute('href', `data:text/plain;charset=utf-8,${encodeURIComponent(text)}`);
  element.setAttribute('download', filename);
  element.style.display = 'none';
  document.body.appendChild(element);
  if (click) {
    element.click();
  }
  document.body.removeChild(element);
  return document
}

module.exports = { download };

Return documents to make it easier to test


var fs = require('fs');
var join = require('path').join;
var getShiny = function (name) {
  name = name.charAt(0).toUpperCase() + name.substring(1).toLowerCase();
  name = name.replace('.mp3', '');
  name = name.replace(/-/g, ' ');
  return name;
};
var getUnShiny = function (name) {
  name = name.toLowerCase();
  name = name.replace(/ /g, '-');
  name = name.replace(/_/g, '-');
  return name;
};
module.exports = function (root) {
  var result = [];
  var queue = ['/'];
  while (queue.length) {
    var d = queue.shift();
    fs.readdirSync(join(root, d)).sort().forEach(function (entry) {
      var f = join(root, d, entry);
      var stat = fs.statSync(f);
      if (stat.isDirectory() && entry != 'node_modules') {
        queue.push(join(d, entry));
      } else {
        if (/.mp3/.test(entry)) {
          var filename = getUnShiny(entry);
          fs.renameSync(f, join(root, d, filename));
          result.push({
            uri: d + '/' + filename,
            name: getShiny(entry)
          });
        }
      }
    });
  }
  return result;
};

var fs = require('fs');
var join = require('path').join;
var getShiny = function (name) {
  name = name.charAt(0).toUpperCase() + name.substring(1).toLowerCase();
  name = name.replace('.mp3', '');
  name = name.replace(/-/g, ' ');
  return name;
};
var getUnShiny = function (name) {
  name = name.toLowerCase();
  name = name.replace(/ /g, '-');
  name = name.replace(/_/g, '-');
  return name;
};
module.exports = function (root) {
  var id = 0
  var result = [];
  var queue = ['/'];
  while (queue.length) {
    var d = queue.shift();
    fs.readdirSync(join(root, d)).sort().forEach(function (entry) {
      var f = join(root, d, entry);
      var stat = fs.statSync(f);
      if (stat.isDirectory() && entry != 'node_modules') {
        queue.push(join(d, entry));
      } else {
        if (/.mp3/.test(entry)) {
          var filename = getUnShiny(entry);
          fs.renameSync(f, join(root, d, filename));
          if (d == '/') {
            result.push({
                uri: d + filename,
                name: getShiny(entry),
                id: id
            });
          } else {
            result.push({
                uri: d + '/' + filename,
                name: getShiny(entry),
                id: id
            });
          }
          id++;
        }
      }
    });
  }
  return result;
};

Fix uri bug of root musics


const defaultState = {
  items: [],
}

const store = {
  state: JSON.parse(localStorage.getItem('userCollections')) || defaultState,

  getItems() {
    return this.state.items
  },

  addItem(itemId) {
    this.state.items.push({id: itemId})
    this._saveState()
  },

  removeItem(itemId) {
    this.state.items = this.state.items.filter(({id}) => id !== itemId)
    this._saveState()
  },

  hasItem(itemId) {
    return this.state.items.findIndex(({id}) => id === itemId) > -1
  },

  toggleItem(itemId) {
    this.hasItem(itemId) ? this.removeItem(itemId) : this.addItem(itemId)
  },

  clearAllItems() {
    this.state.items = []
    this._saveState()
  },

  _saveState() {
    localStorage.setItem('userCollections', JSON.stringify(this.state))
  },
}

module.exports = store

const defaultState = {
  items: [],
}

const store = {
  state: JSON.parse(localStorage.getItem('userCollections')) || defaultState,

  getItems() {
    return this.state.items
  },

  addItem(itemId) {
    this.state.items.push({id: itemId})
    this._saveState()
    this._track('addItem', itemId)
  },

  removeItem(itemId) {
    this.state.items = this.state.items.filter(({id}) => id !== itemId)
    this._saveState()
    this._track('removeItem', itemId)
  },

  hasItem(itemId) {
    return this.state.items.findIndex(({id}) => id === itemId) > -1
  },

  toggleItem(itemId) {
    this.hasItem(itemId) ? this.removeItem(itemId) : this.addItem(itemId)
  },

  clearAllItems() {
    this.state.items = []
    this._saveState()
    this._track('clearAllItems', null)
  },

  _saveState() {
    localStorage.setItem('userCollections', JSON.stringify(this.state))
  },

  // GTM Tracking
  _track(actionName, itemId) {
    if (!window.dataLayer) return

    window.dataLayer.push({
      'event': 'UserCollectionEvent',
      'eventCategory': 'UserCollection',
      'eventAction': actionName,
      'eventLabel': itemId,
      'userCollection': this.state.items
    })
  }
}

module.exports = store

Add basic GTM tracking for user collections


( function( window, define, require, requirejs, undefined ) {
    'use strict';

    define( [
        'jquery',
        'lodash',
        'utils/event'
    ], function( $, _, Event ) {

        /**
         * Global Breakpoint system
         */
        return {

            isGlobal: true,
            currentBreakpoint: '',

            defaults: function() {
                return {
                    selector: 'body',
                    defaultBreakpoint: 'default'
                };
            },

            ready: function(  element, options ) {
                this.settings = _.extend( this.defaults(), options );

                this.currentBreakpoint = this.get();
            },

            events: function( element, options ) {
                this.settings = _.extend( this.defaults(), options );

                $( window ).on( 'resize', _.bind( function() {
                    var breakpoint = this.get();
                    if ( this.currentBreakpoint !== breakpoint ) {
                        this.currentBreakpoint = breakpoint;
                        Event.trigger( 'Breakpoint/change', [ breakpoint ] );
                    }
                }, this ) );
            },

            get: function() {
                var breakpoint = this.settings.defaultBreakpoint;
                if ( 'getComputedStyle' in window ) {
                    breakpoint = window.getComputedStyle(
                        document.querySelector( this.settings.selector ),
                            ':after'
                        ).getPropertyValue( 'content' );
                    breakpoint = breakpoint.replace( /"/g, '' );
                }
                return breakpoint;
            }

        };
    } );

} )( this, this.define, this.require, this.requirejs );

( function( window, define, require, requirejs, undefined ) {
    'use strict';

    define( [
        'jquery',
        'lodash',
        'utils/event'
    ], function( $, _, Event ) {

        /**
         * Global Breakpoint system
         */

        var EVENT_BREAKPOINT_CHANGE = 'Breakpoint/change';

        return {

            isGlobal: true,
            currentBreakpoint: '',

            defaults: function() {
                return {
                    selector: 'body',
                    defaultBreakpoint: 'default'
                };
            },

            ready: function(  element, options ) {
                this.settings = _.extend( this.defaults(), options );

                this.currentBreakpoint = this.get();
            },

            events: function( element, options ) {
                this.settings = _.extend( this.defaults(), options );

                $( window ).on( 'resize', _.bind( function() {
                    var breakpoint = this.get();
                    if ( this.currentBreakpoint !== breakpoint ) {
                        this.currentBreakpoint = breakpoint;
                        Event.trigger( EVENT_BREAKPOINT_CHANGE, [ breakpoint ] );
                        $( this.settings.selector ).trigger( EVENT_BREAKPOINT_CHANGE, [ breakpoint ] );
                    }
                }, this ) );
            },

            get: function() {
                var breakpoint = this.settings.defaultBreakpoint;
                if ( 'getComputedStyle' in window ) {
                    breakpoint = window.getComputedStyle(
                        document.querySelector( this.settings.selector ),
                            ':after'
                        ).getPropertyValue( 'content' );
                    breakpoint = breakpoint.replace( /"/g, '' );
                }
                return breakpoint;
            }

        };
    } );

} )( this, this.define, this.require, this.requirejs );

Add event on selected element