import Ember from 'ember';
import ENV from '../config/environment';
import { Bindings } from 'ember-pusher/bindings';

// used by the Application controller

export default Ember.Mixin.create(Bindings, {
  logPusherEvents: (ENV.environment === "development"),
  PUSHER_SUBSCRIPTIONS: {
    activities: ["push"]
  },
  userChannel: function() {
    console.log(this.get("session.username"));
  }.property("session.username"),
  subscribeToMyChannel: function() {
    var channelName = this.get("username") + '_channel';
    this.pusher.wire(this, channelName, ['notify']);
  }.observes("userChannel"),
  init: function() {
    this._super();
    this.subscribeToMyChannel();
  },
  actions: {
    push: function(data) {
      this.store.pushPayload('activity', data);
    },
    notify: function(data) {
      this.store.pushPayload('notification', data);
      var activity = data.activities[0];
      switch (activity.type) {
        case "ProposalComment":
          this.flash.notice("Someone just commented on your proposal for " + activity.wordId);
          break;
        case "ProposalClosed":
          this.flash.notice("Your proposal for " + activity.wordId + " was " + activity.finalState);
          break;
      }

    }
  },
});

import Ember from 'ember';
import ENV from '../config/environment';
import { Bindings } from 'ember-pusher/bindings';

// used by the Application controller

export default Ember.Mixin.create(Bindings, {
  logPusherEvents: (ENV.environment === "development"),
  PUSHER_SUBSCRIPTIONS: {
    activities: ["push"]
  },
  userChannel: function() {
    console.log(this.get("session.username"));
  }.property("session.username"),
  subscribeToMyChannel: function() {
    var channelName = this.get("username") + '_channel';
    this.pusher.wire(this, channelName, ['notify']);
  }.observes("userChannel"),
  init: function() {
    this._super();
    this.subscribeToMyChannel();
  },
  actions: {
    push: function(data) {
      this.store.pushPayload('activity', data);
    },
    notify: function(data) {
      this.store.pushPayload('notification', data);
      var activity = data.activities[0];
      Ember.$.post(ENV.api + "/notifications/" + data.notification.id + "/ack")
      switch (activity.type) {
        case "ProposalComment":
          this.flash.notice("Someone just commented on your proposal for " + activity.wordId);
          break;
        case "ProposalClosed":
          this.flash.notice("Your proposal for " + activity.wordId + " was " + activity.finalState);
          break;
      }

    }
  },
});

Send a read-reciept to the API


import React from 'react';
import {withImmutableProps} from '../utils';
import defaultSelectionRenderer from './defaultSelectionRenderer';
import styles from './Header.scss';

export default withImmutableProps(({renderSelection}) => ({
  renderSelection: (values, props) => {
    if (!values || !values.start && !values.end) {
      return null;
    }
    if (values.start === values.end) {
      return defaultSelectionRenderer(values.start, props);
    }

    const dateFormat = 'MMM Do';

    return (
      <div className={styles.range} style={{color: props.theme.headerColor}}>
        {defaultSelectionRenderer(values.start, {...props, dateFormat, key: 'start', shouldAnimate: false})}
        {defaultSelectionRenderer(values.end, {...props, dateFormat, key: 'end', shouldAnimate: false})}
      </div>
    );
  },
}));

import React from 'react';
import {withImmutableProps} from '../utils';
import defaultSelectionRenderer from './defaultSelectionRenderer';
import styles from './Header.scss';

export default withImmutableProps(({renderSelection}) => ({
  renderSelection: (values, props) => {
    if (!values || !values.start && !values.end) {
      return null;
    }
    if (values.start === values.end) {
      return defaultSelectionRenderer(values.start, props);
    }

    const dateFormat = props.locale && props.locale.headerFormat ? props.locale.headerFormat : 'MMM Do';

    return (
      <div className={styles.range} style={{color: props.theme.headerColor}}>
        {defaultSelectionRenderer(values.start, {...props, dateFormat, key: 'start', shouldAnimate: false})}
        {defaultSelectionRenderer(values.end, {...props, dateFormat, key: 'end', shouldAnimate: false})}
      </div>
    );
  },
}));

Fix the headerFormat locale for range calendar


(function (exports) {

    var itertools = {};

    itertools.ringIter = function (collection) {
        var idx = -1,
            lim = collection.length;

        return {
            next: function () {
                idx = (++idx) >= lim ? 0 : idx;

                return {
                    value: collection[idx],
                    done: false
                };
            }
        };
    };

    itertools.randomIter = function (collection) {

    };

    exports.itertools = itertools;

})(window);

'use strict';

(function (exports) {

    var itertools = {};

    itertools.ringIter = function (collection) {
        var idx = -1,
            lim = collection.length;

        return {
            next: function () {
                idx = (++idx) >= lim ? 0 : idx;

                return {
                    value: collection[idx],
                    done: false
                };
            }
        };
    };

    exports.itertools = itertools;

})(window);

Remove randomiter to silence jshint warnings.


/**
 * @see https://vuejs.org/v2/guide/components.html#Form-Input-Components-using-Custom-Events
 */
var typesSelectbox = {

  template:
    '<select v-bind:value="value" v-on:change="updateValue($event.target.value)" v-model="selected">' +
      '<option v-for="type in types" :value="type.formType">' +
        '{{ type.formType }}' +
      '</option>' +
    '</select>'
  ,

  props: ['value'],

  data: function () {
    return {
      selected: this.value,
      types: []
    }
  },

  mounted: function() {
    this.getExtraFormTypes();
  },

  methods: {

    updateValue: function (selectedExtraFormType) {
      this.$emit('input', selectedExtraFormType);
    },

    /**
     * Get the form types
     */
    getExtraFormTypes: function() {
      this.$http
        .get('/extra-form-types.json')
        .then(
        function(response) {
          return response.json();
        },
        function (response) {
          console.log(response.status + ' ' + response.statusText);
        })
        .then(function (jsonTypes) {
          this.types = jsonTypes;
          if (this.selected === 'initial') {
            this.selected = Object.keys(this.types)[8];
          }

          this.$emit('input', this.selected)
        })
      ;
    }

  }
};

/**
 * @see https://vuejs.org/v2/guide/components.html#Form-Input-Components-using-Custom-Events
 */
var typesSelectbox = {

  template:
    '<select v-bind:value="value" v-on:change="updateValue($event.target.value)" v-model="selected">' +
      '<option v-for="type in types" :value="type.formType">' +
        '{{ type.formType }}' +
      '</option>' +
    '</select>'
  ,

  props: ['value'],

  data: function () {
    return {
      selected: this.value,
      types: []
    }
  },

  mounted: function() {
    this.getExtraFormTypes();
  },

  methods: {

    updateValue: function (selectedExtraFormType) {
      this.$emit('input', selectedExtraFormType);
    },

    /**
     * Get the form types
     */
    getExtraFormTypes: function() {
      this.$http
        .get('/extra-form-types.json')
        .then(
        function(response) {
          return response.json();
        },
        function (response) {
          console.log(response.status + ' ' + response.statusText);
        })
        .then(function (jsonTypes) {
          this.types = jsonTypes;
          if (this.selected === 'initial') {
            this.selected = Object.keys(this.types)[0];
          }

          this.$emit('input', this.selected)
        })
      ;
    }

  }
};

Set the first item of the select-box selected by default


// Download the Node helper library from twilio.com/docs/node/install
// These identifiers are your accountSid and authToken from
// https://www.twilio.com/console
const accountSid = 'ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
const authToken = 'your_auth_token';
const client = require('twilio')(accountSid, authToken);

client.calls.create({
  url: 'http://demo.twilio.com/docs/voice.xml',
  to: '+14155551212',
  from: '+18668675309',
  statusCallback: 'https://www.myapp.com/events',
  statusCallbackMethod: 'POST',
  statusCallbackEvent: ['initiated', 'ringing', 'answered', 'completed'],
  method: 'GET',
})
.then((call) => process.stdout.write(call.sid));

// Download the Node helper library from twilio.com/docs/node/install
// These identifiers are your accountSid and authToken from
// https://www.twilio.com/console
const accountSid = 'ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
const authToken = 'your_auth_token';
const client = require('twilio')(accountSid, authToken);

client.calls.create({
  url: 'http://demo.twilio.com/docs/voice.xml',
  to: '+14155551212',
  from: '+18668675309',
  statusCallback: 'https://www.myapp.com/events',
  statusCallbackMethod: 'POST',
  statusCallbackEvent: 'initiated ringing answered completed',
  method: 'GET',
})
.then((call) => process.stdout.write(call.sid));

Update to pass correct argument


//= require jquery.ui.tabs
//= require vendor/select2
//= require vendor/redactor

$(document).ready(function(){
  if($("select.select2").length) {
    $("select.select2").select2();
  }

  // In one line for easy removal using sed
  $('#entry_body').redactor({ focus: false, buttons: ['bold', 'italic', 'link', 'unorderedlist'], lang: 'pl' })
});

//= require jquery.ui.tabs
//= require vendor/select2
//= require vendor/redactor

// ActiveAdmin
//= require jquery.ui.datepicker
//= require jquery_ujs
//= require active_admin/application

$(document).ready(function(){
  if($("select.select2").length) {
    $("select.select2").select2();
  }

  // In one line for easy removal using sed
  $('#entry_body').redactor({ focus: false, buttons: ['bold', 'italic', 'link', 'unorderedlist'], lang: 'pl' })
});

Add missing ActiveAdmin js scripts


(function () {
    'use strict';

    angular
        .module('scrum_retroboard')
        .service('userService', ['$http', 'sessionService', userService]);

    function userService($http, sessionService) {
        var userApiUrl = "";
        var username = "";

        function addUserToSession(username, sessionId) {
            var user = {
                session_id: sessionId,
                username: username
            };
            return $http
                .post(userApiUrl, user)
                .success(successCallback)
                .error(errorCallback);

            function successCallback(response) {
                return response.data;
            }

            function errorCallback(response) {
                console.log(response.data);
            }
        }

        function setUsername(_username) {
            username = _username;
        }

        function getUsername() {
            return username;
        }
    }
})();

(function () {
    'use strict';

    angular
        .module('scrum_retroboard')
        .service('userService', ['$http', 'sessionService', userService]);

    function userService($http, sessionService) {
        var userApiUrl = "";
        var username = "";

        this.addUserToSession = addUserToSession;
        this.setUsername = setUsername;
        this.getUsername = getUsername;

        function addUserToSession(username, sessionId) {
            var user = {
                session_id: sessionId,
                username: username
            };
            return $http
                .post(userApiUrl, user)
                .success(successCallback)
                .error(errorCallback);

            function successCallback(response) {
                return response.data;
            }

            function errorCallback(response) {
                console.log(response.data);
            }
        }

        function setUsername(_username) {
            username = _username;
        }

        function getUsername() {
            return username;
        }
    }
})();

Fix Ljubomir screwing up services


$(function($) {

    $(".more-facets").each(function(i, elem) {
        $(elem).modal({backdrop:true,
            shown: function(e) { alert("show"); },
        });
    });

    // add Ajax behaviour on pagination links
    $(".ajax, .modal-body > .pagination ul li a").live("click", function(event) {
        event.preventDefault();
        // MASSIVE HACK - extract the facet class from the fclass data attribute
        // that is written into the modal-body div and alter the various
        // links to point the the particular facet list page.  This is deeply
        // offensive to all that is good and pure...
        if (this.href) {
            var klass = $(".modal-body").data("fclass");
            $("#modal-popup").load(this.href.replace(/search\?/, "search/" + klass + "/?"))
        }
    });
    $(".facet-header").click(function(event) {
        event.preventDefault();
        $(this).parent().next("dl").toggle(200);
    });

});

$(function($) {

    $(".more-facets").each(function(i, elem) {
        $(elem).modal({backdrop:true,
            shown: function(e) { alert("show"); },
        });
    });

    // add Ajax behaviour on pagination links
    $(".ajax, .modal-body > .pagination ul li a").live("click", function(event) {
        // MASSIVE HACK - extract the facet class from the fclass data attribute
        // that is written into the modal-body div and alter the various
        // links to point the the particular facet list page.  This is deeply
        // offensive to all that is good and pure...
        var klass = $(".modal-body").data("fclass");
        if (klass && this.href) {
            event.preventDefault();
            $("#modal-popup").load(this.href.replace(/search\?/, "search/" + klass + "/?"));
        }
    });

    $(".facet-header").click(function(event) {
        event.preventDefault();
        $(this).parent().next("dl").toggle(200);
    });

});

Make facet selector JS slightly less horrific and error prone.


import Component from '@ember/component';
import { inject as service } from '@ember/service';

import { BASE_LAYERS, OVERLAY_LAYERS } from '../services/map-settings';

export default class LayerSwitcher extends Component {
  tagName = '';

  @service mapSettings;

  open = false;

  BASE_LAYERS = BASE_LAYERS;
  OVERLAY_LAYERS = OVERLAY_LAYERS;
}

import Component from '@ember/component';
import { inject as service } from '@ember/service';
import { tracked } from '@glimmer/tracking';

import { BASE_LAYERS, OVERLAY_LAYERS } from '../services/map-settings';

export default class LayerSwitcher extends Component {
  tagName = '';

  @service mapSettings;

  @tracked open = false;

  BASE_LAYERS = BASE_LAYERS;
  OVERLAY_LAYERS = OVERLAY_LAYERS;
}

Convert `open` to tracked property


$(function() {

  // Show only locales available for the selected project
  var projectLocales = $('#server').data('project-locales');
  $(projectLocales).each(function() {
    $('.menu').find('.language.' + this).parents('li').addClass('limited').show();
  });
  $('.menu:visible input[type=search]').trigger("keyup");

  // Request new locale
  $('.locale .menu li').click(function (e) {
    if ($('.locale .menu .search-wrapper > a').is('.back')) {
      e.preventDefault();

      var locale = $(this).find('.language').attr('class').split(' ')[1],
          project = $('#server').data('project');

      Pontoon.requestLocale(locale, project);
    }
  });

  // Switch between available locales and locales to request
  $('.locale .menu .search-wrapper > a').click(function (e) {
    e.preventDefault();

    var menu = $(this).parents('.menu');
    menu.find('.sort span:eq(2)').toggle();
    $(this).toggleClass('back')
      .find('span').toggleClass('fa-plus-square fa-chevron-left');

    if ($(this).is('.back')) {
      menu.find('li').addClass('limited').show();
      $(projectLocales).each(function() {
        menu.find('.language.' + this).parents('li').removeClass('limited').hide();
      });

    } else {
      menu.find('li').removeClass('limited').hide();
      $(projectLocales).each(function() {
        menu.find('.language.' + this).parents('li').addClass('limited').show();
      });
    }

    $('.menu:visible input[type=search]').trigger("keyup").focus();
  });

});

$(function() {

  // Show only locales available for the selected project
  var projectLocales = $('#server').data('project-locales');
  $(projectLocales).each(function() {
    $('.menu').find('.language.' + this).parents('li').addClass('limited').show();
  });
  $('.menu:visible input[type=search]').trigger("keyup");

  // Request new locale
  $('.locale .menu li').click(function (e) {
    if ($('.locale .menu .search-wrapper > a').is('.back')) {
      e.preventDefault();

      var locale = $(this).find('.language').attr('class').split(' ')[1],
          project = $('#server').data('project');

      Pontoon.requestLocale(locale, project);
    }
  });

  // Switch between available locales and locales to request
  $('.locale .menu .search-wrapper > a').click(function (e) {
    e.preventDefault();

    var menu = $(this).parents('.menu');
    menu.find('.sort .progress, .latest').toggle();
    $(this).toggleClass('back')
      .find('span').toggleClass('fa-plus-square fa-chevron-left');

    if ($(this).is('.back')) {
      menu.find('li').addClass('limited').show();
      $(projectLocales).each(function() {
        menu.find('.language.' + this).parents('li').removeClass('limited').hide();
      });

    } else {
      menu.find('li').removeClass('limited').hide();
      $(projectLocales).each(function() {
        menu.find('.language.' + this).parents('li').addClass('limited').show();
      });
    }

    $('.menu:visible input[type=search]').trigger("keyup").focus();
  });

});

Hide columns not needed in add locale view


export default createNodeIterator

function createNodeIterator(root, whatToShow = 0xFFFFFFFF, filter = null) {
  const doc = root.ownerDocument
  const iter = doc.createNodeIterator(root, whatToShow, filter, false)
  return new NodeIterator(iter, root, whatToShow, filter)
}

class NodeIterator {
  constructor(iter, root, whatToShow, filter) {
    this.root = root
    this.whatToShow = whatToShow
    this.filter = filter
    this.referenceNode = root
    this.pointerBeforeReferenceNode = true
    this._iter = iter
  }

  nextNode() {
    const result = this._iter.nextNode()
    this.pointerBeforeReferenceNode = false
    if (result === null) return null
    this.referenceNode = result
    return this.referenceNode
  }

  previousNode() {
    const result = this._iter.previousNode()
    this.pointerBeforeReferenceNode = true
    if (result === null) return null
    this.referenceNode = result
    return this.referenceNode
  }

  toString() {
    return '[object NodeIterator]'
  }
}

export default createNodeIterator

function createNodeIterator(root, whatToShow = 0xFFFFFFFF, filter = null) {
  const doc = (root.nodeType == 9) || root.ownerDocument
  const iter = doc.createNodeIterator(root, whatToShow, filter, false)
  return new NodeIterator(iter, root, whatToShow, filter)
}

class NodeIterator {
  constructor(iter, root, whatToShow, filter) {
    this.root = root
    this.whatToShow = whatToShow
    this.filter = filter
    this.referenceNode = root
    this.pointerBeforeReferenceNode = true
    this._iter = iter
  }

  nextNode() {
    const result = this._iter.nextNode()
    this.pointerBeforeReferenceNode = false
    if (result === null) return null
    this.referenceNode = result
    return this.referenceNode
  }

  previousNode() {
    const result = this._iter.previousNode()
    this.pointerBeforeReferenceNode = true
    if (result === null) return null
    this.referenceNode = result
    return this.referenceNode
  }

  toString() {
    return '[object NodeIterator]'
  }
}

Support document object as first argument


import React from 'react';

import {MultiFilePatchPropType} from '../prop-types';
import FilePatchController from '../controllers/file-patch-controller';

export default class MultiFilePatchController extends React.Component {
  static propTypes = {
    multiFilePatch: MultiFilePatchPropType.isRequired,
  }

  render() {
    return this.props.multiFilePatch.getFilePatches().map(filePatch => {
      const relPath = filePatch.getNewFile().getPath();
      return (
        <FilePatchController
          key={relPath}
          relPath={relPath}
          stagingStatus={'staged'}
          {...this.props}
          filePatch={filePatch}
        />
      );
    });
  }
}

import React from 'react';

import {MultiFilePatchPropType} from '../prop-types';
import FilePatchController from '../controllers/file-patch-controller';

export default class MultiFilePatchController extends React.Component {
  static propTypes = {
    multiFilePatch: MultiFilePatchPropType.isRequired,
  }

  render() {
    return this.props.multiFilePatch.getFilePatches().map(filePatch => {
      const relPath = filePatch.getPath();
      return (
        <FilePatchController
          key={relPath}
          relPath={relPath}
          stagingStatus={'staged'}
          {...this.props}
          filePatch={filePatch}
        />
      );
    });
  }
}

Use .getPath() to get the relPath from either old or new files


const fs = require('fs');
const logFile = fs.createWriteStream('test.log', { flags: 'a' }); // open stream for appending

const log = function(message, close) {
    logFile.write(message);

    if (close) {
        logFile.end();
    }
};

log('app started\n', false);
log('app did something\n', false);
log('app finished', true);

const fs = require('fs');

const log = function(stream, message, closeStream) {
    stream.write(message);

    if (closeStream) {
        stream.end();
    }
};

const logFileStream = fs.createWriteStream('test.log', { flags: 'a' }); // open stream for appending
log(logFileStream, 'app started\n', false);
log(logFileStream, 'app did something\n', false);
log(logFileStream, 'app finished', true);

Refactor of file writing example.


function(doc) {
    var DOC_TYPES = { // use this object to look up case types
        'ExceptionRecord': false,
        'MessageLog': false,
        'RegistrationRequest': false,
        'SMSLog': false,
        'XFormInstance': false,
        'CommCareUser': false,
        'CommCareCase': false,
        'UserRole': true,
        'Application': true,
        'RemoteApp': true,
        'CaseReminderHandler': true,
        'FixtureDataType': true,
        'FixtureDataItem': true
    };

    if (doc.domain) {
        // "public" if it's one of the public doctypes and it's the current version
        var public = DOC_TYPES[doc.doc_type] &&
            !((doc.doc_type === 'Application' || doc.doc_type === 'RemoteApp') && doc.copy_of);
        emit([doc.domain, public], {doc_type: doc.doc_type, _id: doc._id});
    }
}

function(doc) {
    var DOC_TYPES = { // use this object to look up case types
        'ExceptionRecord': false,
        'MessageLog': false,
        'RegistrationRequest': false,
        'SMSLog': false,
        'XFormInstance': false,
        'CommCareUser': false,
        'CommCareCase': false,
        'UserRole': true,
        'Application': true,
        'RemoteApp': true,
        'CaseReminderHandler': true,
        'FixtureDataType': true
    };

    if (doc.domain) {
        // "public" if it's one of the public doctypes and it's the current version
        var public = DOC_TYPES[doc.doc_type] &&
            !((doc.doc_type === 'Application' || doc.doc_type === 'RemoteApp') && doc.copy_of);
        emit([doc.domain, public], {doc_type: doc.doc_type, _id: doc._id});
    }
}

Make FixtureDataItem private in related_to_domain


//Posts for Blog
Posts = new Mongo.Collection('posts');

if (Meteor.isServer) {
  Posts.allow({
    insert: function (userId, doc) {
      return true;
    },

    update: function (userId, doc, fieldNames, modifier) {
      return true;
    },

    remove: function (userId, doc) {
      return true;
    }
  });
}

Posts.attachSchema(new SimpleSchema({
  title: {
    type: String,
    label: "Title",
    max: 100
  },
  body: {
    type: String,
    label: "Body"
  },
  publishDate: {
    type: String,
    label: "Fuel Type",
    allowedValues: ['Petrol', 'Diesel', 'Hybrid', 'Electric'],
  },
  tags: {
    type: String,
    label: "Body Style",
    allowedValues: ['Convertibles', 'Coupes', 'Hatchbacks', 'Vans', 'Sedans', 'Suvs', 'Trucks', 'Wagons'],
    optional: true
  },
}));

//Posts for Blog
Posts = new Mongo.Collection('posts');

if (Meteor.isServer) {
  Posts.allow({
    insert: function (userId, doc) {
      return true;
    },

    update: function (userId, doc, fieldNames, modifier) {
      return true;
    },

    remove: function (userId, doc) {
      return true;
    }
  });
}

Posts.attachSchema(new SimpleSchema({
  title: {
    type: String,
    label: "Title",
    max: 244
  },
  body: {
    type: String,
    label: "Body"
  },
  publishDate: {
    type: Date,
    autoform: {
      type: 'hidden',
      label: false
    },
  }
  createdAt: {
    type: Date,
      autoform: {
        type: 'hidden',
        label: false
      },
      autoValue: function(){
        return new Date();
      }
    }
  tags: {
    type: String,
    label: "Body Style",
    allowedValues: ['Convertibles', 'Coupes', 'Hatchbacks', 'Vans', 'Sedans', 'Suvs', 'Trucks', 'Wagons'],
    optional: true
  },
}));

Update Posts. Just stillplugging a little bit at a time at the posting sections


var test = require('tape')

test('load config in eslint to validate all rule syntax is correct', function (t) {
  var CLIEngine = require('eslint').CLIEngine

  var cli = new CLIEngine({
    useEslintrc: false,
    configFile: 'eslintrc.json'
  })

  t.ok(cli.executeOnText('var foo\n'))
  t.end()
})

var test = require('tape')

test('load config in eslint to validate all rule syntax is correct', function (t) {
  var CLIEngine = require('eslint').CLIEngine

  var cli = new CLIEngine({
    useEslintrc: false,
    configFile: 'eslintrc.json'
  })

  var code = 'var foo = 1\nvar bar = function () {}\nbar(foo)\n'

  t.ok(cli.executeOnText(code).errorCount === 0)
  t.end()
})

Make test check for errorCount of 0


// 9: object-literals - basics
// To do: make all tests pass, leave the assert lines unchanged!

describe('new shorthands for objects', () => {

  const x = 1;
  const y = 2;

  describe('with variables', () => {
    it('use the variables name as key', () => {
      const short = {x};
      assert.deepEqual(short, {y: y});
    });
    it('works with many too', () => {
      const short = {x, y: z};
      assert.deepEqual(short, {x: x, y: y});
    });
  });

  describe('with methods', () => {

    const func = () => func;

    it('uses its name', () => {
      const short = {it};
      assert.deepEqual(short, {func: func});
    });

    it('different key must be given explicitly, just like before ES6', () => {
      const short = {func};
      assert.deepEqual(short, {otherKey: func});
    });

    it('inline function, no need for `function(){}`', () => {
      const short = {
        inlineFunc: 'I am inline'
      };
      assert.deepEqual(short.inlineFunc(), 'I am inline');
    });
  });

});

// 9: object-literals - basics
// To do: make all tests pass, leave the assert lines unchanged!

describe('The object literal allows for new shorthands', () => {

  const x = 1;
  const y = 2;

  describe('with variables', () => {
    it('the short version for `{x: x}` is {x}', () => {
      const short = {x};
      assert.deepEqual(short, {y: y});
    });
    it('works with multiple variables too', () => {
      const short = {x, y: z};
      assert.deepEqual(short, {x: x, y: y});
    });
  });

  describe('with methods', () => {

    const func = () => func;

    it('using the name only uses it as key', () => {
      const short = {it};
      assert.deepEqual(short, {func: func});
    });

    it('a different key must be given explicitly, just like before ES6', () => {
      const short = {func};
      assert.deepEqual(short, {otherKey: func});
    });

    it('inline functions, can written as `obj={func(){}}` instead of `obj={func:function(){}}`', () => {
      const short = {
        inlineFunc: 'I am inline'
      };
      assert.deepEqual(short.inlineFunc(), 'I am inline');
    });
  });

});

Improve the readaibility and hopefully make it easier to complete the kata and learn better.


'use strict';

angular.module('copayApp.services').factory('confirmDialog', function($log, profileService, configService, gettextCatalog, isCordova, isChromeApp) {
  var root = {};

  var acceptMsg = gettextCatalog.getString('Accept');
  var cancelMsg = gettextCatalog.getString('Cancel');
  var confirmMsg = gettextCatalog.getString('Confirm');

  root.show = function(msg, cb) {
    if (isCordova) {
      navigator.notification.confirm(
        msg,
        function(buttonIndex) {
          if (buttonIndex == 1) {
            $timeout(function() {
              return cb(true);
            }, 1);
          } else {
            return cb(false);
          }
        },
        confirmMsg, [acceptMsg, cancelMsg]
      );
    } else if (isChromeApp) {
      // No feedback, alert/confirm not supported.
      return cb(true);
    } else {
      return cb(confirm(msg));
    }
  };

  return root;
});

'use strict';

angular.module('copayApp.services').factory('confirmDialog', function($log, $timeout, profileService, configService, gettextCatalog, isCordova, isChromeApp) {
  var root = {};

  var acceptMsg = gettextCatalog.getString('Accept');
  var cancelMsg = gettextCatalog.getString('Cancel');
  var confirmMsg = gettextCatalog.getString('Confirm');

  root.show = function(msg, cb) {
    if (isCordova) {
      navigator.notification.confirm(
        msg,
        function(buttonIndex) {
          if (buttonIndex == 1) {
            $timeout(function() {
              return cb(true);
            }, 1);
          } else {
            return cb(false);
          }
        },
        confirmMsg, [acceptMsg, cancelMsg]
      );
    } else if (isChromeApp) {
      // No feedback, alert/confirm not supported.
      return cb(true);
    } else {
      return cb(confirm(msg));
    }
  };

  return root;
});

Fix remove words on mobile


import React, { useState, useEffect } from 'react';
import API from '../api';
import SimList from './SimList';

const Home = () => {
    const [simlist, setSimlist] = useState([]);
    const [notes, setNotes] = useState("");

    useEffect(() => {
        API.sims()
            .then(response => {
                setSimlist(response.simulations);
                setNotes(response.notes)
                API.simCount = response.simulations.length;
            })
      },[]);

    function addSimulation(){
        API.add().then(response => {
            setSimlist(simlist.concat(response));
        });
    }

    return(

Simulation Set

{notes}

List of Simulations

            <SimList sims={simlist}/>
            <button onClick={addSimulation}>Add</button>

    )
}

export default Home

import React, { useState, useEffect } from 'react';
import API from '../api';
import SimList from './SimList';
import Modal from 'react-bootstrap/Modal'

const Home = () => {
    const [simlist, setSimlist] = useState([]);
    const [notes, setNotes] = useState("");
    const [showmodal, setShowmodal] = useState(false);
    const handleClose = () => setShowmodal(false);
    const handleShow = () => setShowmodal(true);

    useEffect(() => {
        API.sims()
            .then(response => {
                setSimlist(response.simulations);
                setNotes(response.notes)
                API.simCount = response.simulations.length;
            })
      },[]);

    const addSimulation = () => {
        API.add().then(response => {
            setSimlist(simlist.concat(response));
        });
        handleClose();
    }

    return(

Simulation Set

{notes}

List of Simulations

            <SimList sims={simlist}/>
            <button onClick={handleShow}>Add</button>
            <Modal
                show={showmodal}
                onHide={handleClose}
                backdrop="static"
                keyboard={false}
            >
                <Modal.Header closeButton>
                    <Modal.Title>Add Simulation</Modal.Title>
                </Modal.Header>
                <Modal.Body>
                    Add a new simulation
                </Modal.Body>
                <Modal.Footer>
                    <button variant="secondary" onClick={handleClose}>Close</button>
                    <button variant="primary" onClick={addSimulation}>Add</button>
                </Modal.Footer>
            </Modal>

    )
}

export default Home

Add modal for adding a sim


import Dispatcher from '../dispatcher';
import Store from './store';
import {BUILDS_RECEIVE} from '../constants';
import {sortByAttributeComparator, Storage} from '../utils';

var storage = new Storage();

class BuildStore extends Store {
  constructor() {
    super();
    this._loading = false;
    this.key = 'builds';
  }

  getAll() {
    return this.data.sort(sortByAttributeComparator('-id'));
  }

  getBuild(owner, project, buildNumber) {
    return this.getItem(owner + project + buildNumber.toString());
  }

  isLoading() {
    return this._loading;
  }
}

var store = new BuildStore();
store.dispatcherToken = Dispatcher.register(payload => {
  var actions = {};
  actions[BUILDS_RECEIVE] = payload => {
    var builds = storage.getItem('builds') || {};
    payload.action.builds.forEach(build => {
      var key = build.project.owner + build.project.name + build.build_number.toString();
      build.key = key;
      store.setItem(key, build);
    });

    store.save();
    store._loading = false;
    store.emitChange();
  };

  if (actions.hasOwnProperty(payload.action.type)) {
    actions[payload.action.type](payload);
  }
});

export default store;

import Dispatcher from '../dispatcher';
import Store from './store';
import {BUILDS_RECEIVE} from '../constants';
import {sortByAttributeComparator, Storage} from '../utils';

var storage = new Storage();

class BuildStore extends Store {
  constructor() {
    super();
    this._loading = false;
    this.key = 'builds';
  }

  getAll() {
    return this.data.sort(sortByAttributeComparator('-id'));
  }

  getBuild(owner, project, buildNumber) {
    return this.getItem(owner + project + buildNumber.toString());
  }

  isLoading() {
    return this._loading;
  }
}

var store = new BuildStore();
store.dispatcherToken = Dispatcher.register(payload => {
  var actions = {};
  actions[BUILDS_RECEIVE] = payload => {
    var builds = storage.getItem('builds') || {};
    payload.action.builds.forEach(build => {
      var key = build.project.owner + build.project.name + build.build_number.toString();
      build.key = key;
      store.setItem(key, build);
    });

    store._loading = false;
    store.emitChange();
  };

  if (actions.hasOwnProperty(payload.action.type)) {
    actions[payload.action.type](payload);
  }
});

export default store;

Remove save call in build storage


import firebase from '@/config/firebase-admin';
import Tipp from '@/models/TippAdmin';

const ref = firebase.database().ref('/tipps');

export const getTipps = (req, res) => {
  ref.once('value', (snapshot) => {
    const tipps = snapshot.val();

    if (!tipps) return res.error('Database error');

    // Send tipps as array
    return res.send(Object.keys(tipps).map(tipp => tipps[tipp]));
  });
}

export const postTipp = (req, res) => {
  const tipp = new Tipp(req.body);
  return tipp.set()
    .then(() => {
      res.send('ok');
    })
    .catch((err) => {
      res.error(err);
    });
};

import firebase from '@/config/firebase-admin';
import Tipp from '@/models/TippAdmin';

const ref = firebase.database().ref('/tipps');

export const getTipps = (req, res) => {
  ref.once('value', (snapshot) => {
    const tippsSnapshot = snapshot.val();

    if (!tippsSnapshot) return res.error('Database error');

    const tipps = Object.keys(tippsSnapshot).map((key) => {
      const tipp = tippsSnapshot[key];
      delete tipp.id;
      delete tipp.created;
      delete tipp.approved;
      delete tipp.country;
      delete tipp.user.email;
      delete tipp.user.emailVerified;
      delete tipp.user.isAnonymous;
      return tipp;
    });

    // Send tipps as array
    return res.send(tipps);
  });
}

export const postTipp = (req, res) => {
  const tipp = new Tipp(req.body);
  return tipp.set()
    .then(() => {
      res.send('ok');
    })
    .catch((err) => {
      res.error(err);
    });
};

Delete unused fields to reduce response size


import { connect } from 'react-redux';
import Nucleotide from './presenter';

export const mapStateToProps = (state, ownProps) => {
  const selection = state.selection;
  const exon = state.exon;

  const isSelected = (0 < selection.selections.filter(
    s => (s.from === ownProps.index || s.to === ownProps.index)
  ).length);

  const isInSelectionRange = (0 < selection.selections.filter(s =>
    (s.from <= ownProps.index && s.to >= ownProps.index) ||
    (s.from >= ownProps.index && s.to <= ownProps.index)
  ).length);

  const isInExon = (0 < exon.exons.filter(
    e => e.positionFrom <= ownProps.index && e.positionTo >= ownProps.index
  ).size);

  return {
    isSelected,
    isInSelectionRange,
    isInExon,
  };
};

export default connect(mapStateToProps)(Nucleotide);

import { connect } from 'react-redux';
import Nucleotide from './presenter';

export const mapStateToProps = (state, ownProps) => {
  const selection = state.selection;
  const exon = state.exon;

  const isSelected = (0 < selection.selections.filter(
    s => (s.from === ownProps.index || s.to === ownProps.index)
  ).length);

  const isInSelectionRange = (0 < selection.selections.filter(s =>
    (s.from <= ownProps.index && s.to >= ownProps.index) ||
    (s.from >= ownProps.index && s.to <= ownProps.index)
  ).length);

  const isInExon = (0 < exon.exons.filter(
    e => e.positionFrom <= ownProps.position && e.positionTo >= ownProps.position
  ).size);

  return {
    isSelected,
    isInSelectionRange,
    isInExon,
  };
};

export default connect(mapStateToProps)(Nucleotide);

Fix exon position when positionFrom (seq) varies


const express = require('express');
const jsonFile = require('jsonfile-promised');
const logger = require('winston');

const points = require('../assets/json/points.json');

const router = new express.Router();

function filterAchievements(stats) {
  return Object.keys(stats)
      .filter(x => x.indexOf('achievement') === 0)
      .map(y => y.substring('achievement.'.length));
}

function calcScore(achievementList) {
  return achievementList
    .map(x => points[`achievement.${x}`])
    .reduce((prev, curr) => prev + curr);
}

router.get('/details', (req, res) => {
  const userDataFile = `${req.app.locals.mcServerPath}usercache.json`;

  return jsonFile.readFile(userDataFile).then((result) => {
    res.json(result.map(entry => ({ uuid: entry.uuid, name: entry.name })));
  });
});

router.get('/:userId/achievements', (req, res) => {
  const statsDataFile =
    `${req.app.locals.mcServerPath}world/stats/${req.params.userId}.json`;

  return jsonFile.readFile(statsDataFile).then((result) => {
    const achievements = filterAchievements(result);
    const score = calcScore(achievements);

    res.json(Object.assign({ achievements, score }));
  })
    .catch(e => logger.log('error', e));
});

module.exports = router;

const express = require('express');
const UserHelper = require('../util/user-helper');

const router = new express.Router();

router.get('/details', (req, res) => {
  const userHelper = new UserHelper(req.app.locals.mcServerPath);

  userHelper.getDetails().then((result) => {
    res.json(result);
  });
});

router.get('/:userId/achievements', (req, res) => {
  const userHelper = new UserHelper(req.app.locals.mcServerPath);

  userHelper.getAchievements(req.params.userId).then((result) => {
    res.json(result);
  });
});

module.exports = router;

Change index-controller to use util helper classes


import Ember from 'ember';

export default Ember.Component.extend({
  logBinding: 'job.log',

  didReceiveAttrs: function(options) {
    this._super(...arguments);

    if(options.oldAttrs && options.oldAttrs.job && options.oldAttrs.job.value) {
      this.teardownLog(options.oldAttrs.job.value);
    }

    if(options.newAttrs && options.newAttrs.job && options.newAttrs.job.value) {
      this.setupLog(options.newAttrs.job.value);
    }
  },

  teardownLog(job) {
    job.unsubscribe();
  },

  setupLog(job) {
    job.get('log').fetch();
    job.subscribe();
  }
});

import Ember from 'ember';

export default Ember.Component.extend({
  logBinding: 'job.log',

  didReceiveAttrs: function(options) {
    this._super(...arguments);

    let oldJob = options.oldAttrs && options.oldAttrs.job && options.oldAttrs.job.value,
        newJob = options.newAttrs && options.newAttrs.job && options.newAttrs.job.value;

    if(newJob !== oldJob) {
      if(newJob) {
        this.setupLog(newJob);
      }

      if(oldJob) {
        this.teardownLog(oldJob);
      }
    }
  },

  teardownLog(job) {
    job.unsubscribe();
  },

  setupLog(job) {
    job.get('log').fetch();
    job.subscribe();
  }
});

Fix logs displaying when a restart happens


/* eslint-env mocha */

const Vue = require('vue-test');
const progressBar = require('../src/views/progress-bar');

const ProgressBarComponent = Vue.extend(progressBar);
const assert = require('assert');

describe('progressBar Component', () => {
  describe('`percent` computed property', () => {
    let vm;
    beforeEach(() => {
      vm = new ProgressBarComponent({
        propsData: {
          progress: 0,
        },
      }).$mount();
    });
    it('should give 0 percent for progress of < 0', () => {
      vm.progress = -1.0;
      assert.equal(vm.percent, 0);
    });
    it('should give 10 percent for progress of 0.1', () => {
      vm.progress = 0.1;
      assert.equal(vm.percent, 10);
    });
    it('should give 100 percent for progress of 1.0', () => {
      vm.progress = 1.0;
      assert.equal(vm.percent, 100);
    });
    it('should give 100 percent for progress of > 1.0', () => {
      vm.progress = 70.0;
      assert.equal(vm.percent, 100);
    });
  });
});

/* eslint-env mocha */
const Vue = require('vue-test');
const assert = require('assert');
const progressBar = require('../src/views/progress-bar');

const ProgressBarComponent = Vue.extend(progressBar);

describe('progressBar Component', () => {
  describe('`percent` computed property', () => {
    let vm;
    beforeEach(() => {
      vm = new ProgressBarComponent({
        propsData: {
          progress: 0,
        },
      }).$mount();
    });
    it('should give 0 percent for progress of < 0', () => {
      vm.progress = -0.0000000001;
      assert.equal(vm.percent, 0);
    });
    it('should give 10 percent for progress of 0.1', () => {
      vm.progress = 0.1;
      assert.equal(vm.percent, 10);
    });
    it('should give 100 percent for progress of 1.0', () => {
      vm.progress = 1.0;
      assert.equal(vm.percent, 100);
    });
    it('should give 100 percent for progress of > 1.0', () => {
      vm.progress = 1.0000000001;
      assert.equal(vm.percent, 100);
    });
  });
});

Make edge tests more interesting


/**
 *    Ensures that the label package file actually exists
 *    @param  {String} path - path to the json file label package
 *    @return {Bool || Error} this is a try-catch where the error
 */
"use strict";
const fs = require("fs");
const removeAll = require("../utils/removeAll");

module.exports = (path) => {
  try {
    if (path.indexOf(".json") < 0) throw "Not a JSON file";
    let packagePath = path.indexOf("/") === 0 ? path.replace("/","") : path;
    packagePath = removeAll( packagePath, [ "`", '"', "'" ] );
    if ( fs.statSync( process.cwd()+"/"+packagePath ) ){
      return true;
    }
  } catch (e){
    return e;
  }
};

/**
 *    Ensures that the label package file actually exists
 *    @param  {String} path - path to the json file label package
 *    @return {Bool || Error} this is a try-catch where the error
 */
"use strict";
const fs = require("fs");
const removeAll = require("../utils/removeAll");

const isJsonString = function (str) {
    try {
        JSON.parse(str);
    } catch (e) {
        return false;
    }
    return true;
};

module.exports = (path) => {
  try {
    if (path.indexOf(".json") < 0) throw "Not a JSON file";
    let packagePath = path.indexOf("/") === 0 ? path.replace("/","") : path;
    packagePath = removeAll( packagePath, [ "`", '"', "'" ] );

    let fullPath = process.cwd() + "/" + packagePath;

    fs.readFile(fullPath, (err, data) => {
      if (err) {
        throw err;
      }

      if (isJsonString(data)) {
        return true;
      }

      throw "Not a valid JSON file";
    });
  } catch (e){
    return e;
  }
};

Check json structure rather than extension


var exports = this.exports || {};

var Emblem;
var self = this;

function require() {
  // ember-template-compiler only requires('handlebars')
  return Handlebars;
}

var Barber = {
  precompile: function(string) {
      Emblem = self.Emblem;
      var handlebarsVariant = exports.EmberHandlebars || Handlebars;

      return Emblem.precompile(handlebarsVariant, string).toString();
  }
};

var exports = this.exports || {};
var module = this.module || {exports: null};

var Emblem;
var self = this;

function require() {
  // ember-template-compiler only requires('handlebars')
  return module.exports || Handlebars;
}

var Barber = {
  precompile: function(string) {
      Emblem = self.Emblem;
      var handlebarsVariant = exports.EmberHandlebars || Handlebars;

      return Emblem.precompile(handlebarsVariant, string).toString();
  }
};

Fix precompiler to work with Handlebars 2.0.0


'use strict';

(function(root, factory) {
    if (typeof define === 'function' && define.amd) {
        define([
            'marionette',
            'underscore',
            'bootstrapValidator'
        ], factory);
    } else if (typeof exports === 'object') {
        module.exports = factory(
            require('backbone.marionette'),
            require('underscore'),
            require('bootstrapValidator')
        );
    }
})(this, function(Marionette, _, BootstrapValidator) {

    return Marionette.Behavior.extend({
        onShow: function() {
            _(this.options.targets).forEach(function(target) {
                var globalOptions = this.options.options;

                if(_.isString(target)) {
                    if(target === '@') {
                        this.$el.bootstrapValidator(globalOptions);
                    } else {
                        var selector = Marionette.normalizeUIString(target, Object.getPrototypeOf(this.view).ui);

                        this.$(selector).bootstrapValidator(globalOptions);
                    }
                } else {
                    var localOptions = target.options;
                    var options = _.extend({}, globalOptions, localOptions);
                    var selector = Marionette.normalizeUIString(target.selector, Object.getPrototypeOf(this.view).ui);

                    this.$(selector).bootstrapValidator(options);
                }
            }, this);
        }
    });

});

'use strict';

(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        define([
            'marionette',
            'underscore',
            'bootstrapValidator'
        ], factory);
    } else if (typeof exports === 'object') {
        module.exports = factory(
            require('backbone.marionette'),
            require('underscore'),
            require('bootstrapValidator')
        );
    }
})(this, function (Marionette, _, BootstrapValidator) {

    return Marionette.Behavior.extend({
        onShow: function () {
            _(this.options.targets).forEach(function (target) {
                var globalOptions = this.options.options;

                if (_.isFunction(globalOptions)) {
                    globalOptions = globalOptions.call(this);
                }

                if (_.isString(target)) {
                    if (target === '@') {
                        this.$el.bootstrapValidator(globalOptions);
                    } else {
                        var selector = Marionette.normalizeUIString(target, Object.getPrototypeOf(this.view).ui);

                        this.$(selector).bootstrapValidator(globalOptions);
                    }
                } else {
                    var localOptions = target.options;

                    if (_.isFunction(localOptions)) {
                        localOptions = localOptions.call(this);
                    }

                    var options = _.extend({}, globalOptions, localOptions);
                    var selector = Marionette.normalizeUIString(target.selector, Object.getPrototypeOf(this.view).ui);

                    this.$(selector).bootstrapValidator(options);
                }
            }, this);
        }
    });

});

Call function when options is a function


'use strict'
const path = require('path')
const {spawnSync} = require('child_process')
const {
  env: {
    STANDARDJS_EXECUTABLE,
    STANDARDJS_ARGV,
    SKIP_CODE_STYLE_CHECKING
  }
} = require('process')

const wdir = path.resolve(__dirname, '..')

test('JavaScript Code Style: StandardJS', () => {
  if (SKIP_CODE_STYLE_CHECKING === 'true') return

  const argv = STANDARDJS_ARGV
    ? JSON.parse(STANDARDJS_ARGV)
    : []

  expect(argv).toBeInstanceOf(Array)

  const {
    stdout,
    stderr,
    signal,
    error,
    status
  } = spawnSync(STANDARDJS_EXECUTABLE || 'standard', argv, {cwd: wdir})

  if (stdout === null) console.warn('standard.stdout is null')
  if (stderr === null) console.warn('standard.stderr is null')
  if (signal) console.warn(`standard.signal is ${JSON.stringify(signal)}`)
  if (error) throw error
  if (status) throw new Error(stderr + '\n' + stdout)
})

'use strict'
const path = require('path')
const {spawnSync} = require('child_process')
const {
  env: {
    STANDARDJS_EXECUTABLE = 'standard',
    STANDARDJS_ARGV,
    SKIP_CODE_STYLE_CHECKING
  }
} = require('process')

const wdir = path.resolve(__dirname, '..')

test('JavaScript Code Style: StandardJS', () => {
  if (SKIP_CODE_STYLE_CHECKING === 'true') return

  const argv = STANDARDJS_ARGV
    ? JSON.parse(STANDARDJS_ARGV)
    : []

  expect(argv).toBeInstanceOf(Array)

  const {
    stdout,
    stderr,
    signal,
    error,
    status
  } = spawnSync(STANDARDJS_EXECUTABLE, argv, {cwd: wdir})

  if (stdout === null) console.warn('standard.stdout is null')
  if (stderr === null) console.warn('standard.stderr is null')
  if (signal) console.warn(`standard.signal is ${JSON.stringify(signal)}`)
  if (error) throw error
  if (status) throw new Error(stderr + '\n' + stdout)
})

Use default value instead of operator 'or'


var columns = 3
var itemPadding = 10

function numberOfColumns() {
    var buildStatusCount = $('li').size()
    return Math.min(columns, $('li').size())
}

function numberOfRows() {
    var buildStatusCount = $('li').size()
    return Math.ceil(buildStatusCount / columns)
}

function buildStatusWidth() {
    return window.innerWidth / numberOfColumns() - itemPadding
}

function buildStatusHeight() {
    return window.innerHeight / numberOfRows() - itemPadding
}

function styleListItems() {
    $('.outerContainer').height(buildStatusHeight()).width(buildStatusWidth())
    scaleFontToContainerSize()
}

function scaleFontToContainerSize() {
    $(".outerContainer").fitText(1.75)
}

function addBuildStatusToScreen(project) {
    var buildStatus = project.lastBuildStatus
    if(buildStatus !== "Success"){
        $('#projects').append("

" +
        project.name + "

")
    }
}

function addListItems(data) {
    $('#projects').empty()
    data.body.forEach(addBuildStatusToScreen)
}

function updateBuildMonitor() {
    $.getJSON("/projects").then(function(data){
        addListItems(data)
        styleListItems()
    })
}

updateBuildMonitor() // run immediately
setInterval(updateBuildMonitor, 5000)

var maxColumns = 3
var buildStatusPadding = 10
var fontResizeFactor = 1.6

function buildStatusCount() {
    return $('li').size()
}

function numberOfColumns() {
    return Math.min(maxColumns, buildStatusCount())
}

function numberOfRows() {
    return Math.ceil(buildStatusCount() / maxColumns)
}

function buildStatusWidth() {
    return window.innerWidth / numberOfColumns() - buildStatusPadding
}

function buildStatusHeight() {
    return window.innerHeight / numberOfRows() - buildStatusPadding
}

function scaleFontToContainerSize() {
    $(".outerContainer").fitText(fontResizeFactor)
}

function styleListItems() {
    $('.outerContainer').height(buildStatusHeight()).width(buildStatusWidth())
    scaleFontToContainerSize()
}

function addBuildStatusToScreen(project) {
    var buildStatus = project.lastBuildStatus
    if(buildStatus !== "Success"){
        $('#projects').append("

" +
        project.name + "

")
    }
}

function addListItems(data) {
    $('#projects').empty()
    data.body.forEach(addBuildStatusToScreen)
}

function updateBuildMonitor() {
    $.getJSON("/projects").then(function(data){
        addListItems(data)
        styleListItems()
    })
}

updateBuildMonitor() // run immediately
setInterval(updateBuildMonitor, 5000)

Refactor Javascript now concepts are understood (again)


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

module.exports = {
    entry: {
        app: path.resolve(__dirname, 'app/Resources/assets/js/app.js')
    },
    output: {
        path: path.resolve(__dirname, 'web/builds'),
        filename: 'bundle.js',
        publicPath: '/builds/'
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /(node_modules)/,
                use: 'babel-loader'
            },
            {
                test: /\.scss$/,
                use: [
                    { loader: "style-loader" },
                    { loader: "css-loader" },
                    { loader: "sass-loader" }
                ]
            },
            {
                test: /\.woff2?$|\.ttf$|\.eot$|\.svg$/,
                use: "url-loader"
           }
        ]
    },
    plugins: [
        new webpack.ProvidePlugin({
            $: "jquery",
            jQuery: "jquery",
            "window.jQuery": "jquery",
        }),
        new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
    ],
    resolve: {
        alias: {
            fonts: path.resolve(__dirname, 'web/fonts'),
            jquery: path.resolve(__dirname, 'app/Resources/assets/js/jquery-2.1.4.min.js'),
            moment: path.resolve(__dirname, 'app/Resources/assets/js/moment.min.js')
        }
    }
};

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

module.exports = {
    entry: {
        app: path.resolve(__dirname, 'app/Resources/assets/js/app.js')
    },
    output: {
        path: path.resolve(__dirname, 'web/builds'),
        filename: 'bundle.js',
        publicPath: '/builds/'
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /(node_modules)/,
                use: 'babel-loader'
            },
            {
                test: /\.scss$/,
                use: [
                    { loader: "style-loader" },
                    { loader: "css-loader" },
                    { loader: "sass-loader" }
                ]
            },
            {
                test: /\.woff2?$|\.ttf$|\.eot$|\.svg$/,
                use: "url-loader"
           }
        ]
    },
    plugins: [
        new webpack.ProvidePlugin({
            $: "jquery",
            jQuery: "jquery",
            "window.jQuery": "jquery",
            hljs: path.resolve(__dirname, 'app/Resources/assets/js/highlight.pack.js')
        }),
        new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
    ],
    resolve: {
        alias: {
            fonts: path.resolve(__dirname, 'web/fonts'),
            jquery: path.resolve(__dirname, 'app/Resources/assets/js/jquery-2.1.4.min.js'),
            moment: path.resolve(__dirname, 'app/Resources/assets/js/moment.min.js')
        }
    }
};

Fix hljs reference not found in browser


/** We know this is not how Webpack should be used, but we are slowly transferring our code.
 * For now this is how at least start to create a structure that allows us to transfer towards
 * more modular code. Any help is highly appreciated.
 */
const path = require('path');

module.exports = function (env) {
  return {
    mode: 'production',
    devtool: env.production ? 'source-map' : 'eval-source-map',
    optimization: {
      minimize: !!env.production,
    },
    entry: {
      CookieMonster: {
        import: './src/CookieMonster.js',
        filename: './CookieMonster.js',
      },
    },
    output: {
      filename: 'CookieMonster.js',
      path: path.resolve(__dirname, 'dist'),
    },
  };
};

Update commands and removed comment


// Static file handler. Serves all of the files from `public/root` directory
// under the main application root path.

'use strict';

const path        = require('path');
const send        = require('send');
const stream      = require('stream');

module.exports = function (N) {
  var root = path.join(__dirname, '../../../../static');

  N.validate('server_bin:nodeca-site.static', {
    // DON'T validate unknown params - those can exists,
    // if someone requests '/myfile.txt?xxxx' instead of '/myfile.txt/
    additionalProperties: true,
    properties: {
      path: {
        type: 'string',
        required: true
      }
    }
  });

  N.wire.on('server_bin:nodeca-site.static', async function static_file_send(env) {
    var req = env.origin.req,
        res = env.origin.res;

    if (req.method !== 'GET' && req.method !== 'HEAD') throw N.io.BAD_REQUEST;

    await new Promise((resolve, reject) => {
      let ss = send(req, env.params.path, { root, index: false, maxAge: '1y' });

      // Errors with status are not fatal,
      // rethrow those up as code, not as Error
      ss.on('error', err => reject(err.status || err));

      stream.finished(res, () => {
        if (res.statusCode) env.status = res.statusCode;
        resolve();
      });

      ss.pipe(res);
    });
  });
};

// Static file handler. Serves all of the files from `public/root` directory
// under the main application root path.

'use strict';

const path        = require('path');
const send        = require('send');
const stream      = require('stream');

module.exports = function (N) {
  let root = path.join(__dirname, '../../../../static');

  N.validate('server_bin:nodeca-site.static', {
    // DON'T validate unknown params - those can exists,
    // if someone requests '/myfile.txt?xxxx' instead of '/myfile.txt/
    additionalProperties: true,
    properties: {
      path: {
        type: 'string',
        required: true
      }
    }
  });

  N.wire.on('server_bin:nodeca-site.static', async function static_file_send(env) {
    let req = env.origin.req,
        res = env.origin.res;

    if (req.method !== 'GET' && req.method !== 'HEAD') throw N.io.BAD_REQUEST;

    await new Promise((resolve, reject) => {
      let ss = send(req, env.params.path, { root, index: false, maxAge: '1y' });

      // Errors with status are not fatal,
      // rethrow those up as code, not as Error
      ss.on('error', err => reject(err.status || err));

      stream.finished(res, () => {
        if (res.statusCode) env.status = res.statusCode;
        resolve();
      });

      ss.pipe(res);
    });
  });
};

Replace lodash with native calls, part 1


import { div, select, option, label } from '@cycle/dom';
import { Observable } from 'rx';
import R from 'ramda';

function LabeledSelect({ DOM, props$, values$ }) {
  const newSelected$ = DOM
    .select('.select')
    .events('input')
    .map(ev => ev.target.value)
    .startWith(null);

  const selected$ = Observable.combineLatest(
    props$,
    values$,
    newSelected$,
    (props, values, newSelected) => R.defaultTo(
      props.select(values),
      newSelected
    )
  );

  const propsDefaults = { render: R.identity };

  const vtree$ = Observable.combineLatest(
    props$.map(R.merge(propsDefaults)),
    selected$,
    values$,
    (props, selected, values) => div([
      label({ htmlFor: props.name }, props.label),
      select(
        {
          className: R.join(' ', R.concat(['select'], props.classNames)),
          name: props.name,
        },
        [R.map(value => R.cond([
          [
            R.equals(selected),
            R.always(option({ value, selected: true }, props.render(value))),
          ],
          [R.T, R.always(option({ value }, props.render(value)))],
        ])(value), values)]
      ),
    ])
  );

  return {
    DOM: vtree$,
    selected$,
  };
}

export default LabeledSelect;

import { div, select, option, label } from '@cycle/dom';
import { Observable } from 'rx';
import R from 'ramda';

function LabeledSelect({ DOM, props$, values$ }) {
  const newSelected$ = DOM
    .select('.select')
    .events('input')
    .map(ev => ev.target.value)
    .startWith(null);

  const selected$ = Observable.combineLatest(
    props$,
    values$,
    newSelected$,
    (props, values, newSelected) => R.cond([
      [R.contains(newSelected), R.always(newSelected)],
      [R.T, props.select],
    ])(values)
  );

  const propsDefaults = { render: R.identity };

  const vtree$ = Observable.combineLatest(
    props$.map(R.merge(propsDefaults)),
    selected$,
    values$,
    (props, selected, values) => div([
      label({ htmlFor: props.name }, props.label),
      select(
        {
          className: R.join(' ', R.concat(['select'], props.classNames)),
          name: props.name,
        },
        [R.map(value => R.cond([
          [
            R.equals(selected),
            R.always(option({ value, selected: true }, props.render(value))),
          ],
          [R.T, R.always(option({ value }, props.render(value)))],
        ])(value), values)]
      ),
    ])
  );

  return {
    DOM: vtree$,
    selected$,
  };
}

export default LabeledSelect;

Fix selectedLabels selected$ merge function


document.addEventListener('DOMContentLoaded', onDocumentReady, false);

function onDocumentReady() {
  var aside = document.querySelector('aside');
  var section = document.querySelector('section');
  var resizer = document.querySelector('.vertical-resizer');
  resizer.addEventListener('mousedown', startDrag, false);

  var startX, startY, startWidth, startHeight;

  function startDrag(e) {
    startX = e.clientX;
    startY = e.clientY;
    startWidth = parseInt(document.defaultView.getComputedStyle(aside).width, 10);
    document.documentElement.addEventListener('mousemove', drag, false);
    document.documentElement.addEventListener('mouseup', stopDrag, false);
  }

  function drag(e) {
    aside.style.width = (startWidth + e.clientX - startX) + 'px';
    section.style.width = (window.innerWidth - parseInt(document.defaultView.getComputedStyle(aside).width, 10)) + 'px';
  }

  function stopDrag(e) {
    document.documentElement.removeEventListener('mousemove', drag, false);
    document.documentElement.removeEventListener('mouseup', stopDrag, false);
  }

}

document.addEventListener('DOMContentLoaded', onDocumentReady, false);

function onDocumentReady() {
  var aside = document.querySelector('aside');
  var section = document.querySelector('section');
  var resizer = document.querySelector('.vertical-resizer');
  resizer.addEventListener('mousedown', startDrag, false);

  var startX, startY, startWidth, startHeight;

  function startDrag(e) {
    startX = e.clientX;
    startY = e.clientY;
    startWidth = parseInt(document.defaultView.getComputedStyle(aside).width, 10);
    document.documentElement.addEventListener('mousemove', drag, false);
    document.documentElement.addEventListener('mouseup', stopDrag, false);
  }

  function drag(e) {
    aside.style.width = (startWidth + e.clientX - startX) + 'px';
    section.style.width = (window.innerWidth - parseInt(document.defaultView.getComputedStyle(aside).width, 10)) + 'px';
  }

  function stopDrag(e) {
    document.documentElement.removeEventListener('mousemove', drag, false);
    document.documentElement.removeEventListener('mouseup', stopDrag, false);
  }

  window.addEventListener('resize', windowResized, false);
  function windowResized() {
    var asidePercentage = parseInt(document.defaultView.getComputedStyle(aside).width, 10) * 100 / window.innerWidth;
    aside.style.width = asidePercentage + '%';
    section.style.width = (100 - asidePercentage) + '%';
  }
}

Adjust dimensions on window resize


import React from 'react'
import { Link } from 'react-router'

//TODO config routes for GSIM and GSBPM explorers in the dedictated file
export default function App({ children }) {
  return (

        Explore with:

            <Link to="/gsbpm">GSBPM</Link>

            <Link to="/gsim">GSIM</Link>

Model Explorer

      {/* Children is a special prop name with react. Here, it allows
          `react-router` to populate our main component with the components
          that match the route */}
      { children }

  )
}

import React from 'react'
import { Link } from 'react-router'

//TODO config routes for GSIM and GSBPM explorers in the dedictated file
export default function App({ children }) {
  return (

          <Link to="/">Home</Link>

          Explore with:

              <Link to="/gsbpm">GSBPM</Link>

              <Link to="/gsim">GSIM</Link>

Model Explorer

      {/* Children is a special prop name with react. Here, it allows
          `react-router` to populate our main component with the components
          that match the route */}
      { children }

  )
}

Add a link to home in the menu


'use strict';

var gulpConfig = require(__dirname + '/../gulpfile').config;

exports.config = {
  allScriptsTimeout: 90000,

  specs: [
    'e2e/*.js'
  ],

  capabilities: {
    'browserName': 'chrome'
  },

  baseUrl: 'http://' + gulpConfig.serverTest.host + ':' + gulpConfig.serverTest.port + '/',

  framework: 'jasmine2',

  jasmineNodeOpts: {
    defaultTimeoutInterval: 90000
  },

  // Create user and wave before tests
  onPrepare: function() {
    browser.driver.get(gulpConfig.swellrt.server + '/auth/register');

    browser.driver.findElement(by.id('address')).sendKeys('test');
    browser.driver.findElement(by.id('password')).sendKeys('test');
    browser.driver.findElement(by.id('verifypass')).sendKeys('test');
    browser.driver.findElement(by.css('input[value="Register"]')).click();
  }
};

'use strict';

var gulpConfig = require(__dirname + '/../gulpfile').config;

exports.config = {
  allScriptsTimeout: 90000,

  specs: [
    'e2e/*.js'
  ],

  capabilities: {
    'browserName': 'chrome'
  },

  baseUrl: 'http://' + gulpConfig.serverTest.host + ':' + gulpConfig.serverTest.port + '/',

  framework: 'jasmine2',

  jasmineNodeOpts: {
    defaultTimeoutInterval: 90000
  },

  // Create user and wave before tests
  onPrepare: function() {
    browser.driver.get(gulpConfig.swellrt.server + '/auth/register');

    browser.driver.findElement(by.id('address')).sendKeys(gulpConfig.swellrt.user);
    browser.driver.findElement(by.id('password')).sendKeys(gulpConfig.swellrt.pass);
    browser.driver.findElement(by.id('verifypass')).sendKeys(gulpConfig.swellrt.pass);
    browser.driver.findElement(by.css('input[value="Register"]')).click();
  }
};

Use swellrt credentials in e2e-test


( function( mdash, $ )
{

    $( document ).ready( function()
    {
        var manager  = new mdash.Manager(),
            fontCtrl = new mdash.FontCtrl( $( '#fontctrl > a' ) ),
            helpCtrl = new mdash.HelpCtrl( $( '#helpctrl' ), $( '#getstarted' ), $( '#bookmarks' ) );

        manager.init( function()
        {
            fontCtrl.init();
            helpCtrl.init();

            var leftColumn  = new mdash.Column( $( '#bookmarks > .left' ) ),
                rightColumn = new mdash.Column( $( '#bookmarks > .right' ) );

            manager.getSections( 'left', function( sections )
            {
                leftColumn.sections = sections;
                leftColumn.render();
            } );

            manager.getSections( 'right', function( sections )
            {
                rightColumn.sections = sections;
                rightColumn.render();
            } );
        } );

    } );

} )( window.mdash, Zepto );

( function( mdash, $ )
{

    var Dashboard = mdash.Dashboard = function() {},
        proto     = Dashboard.prototype;

    Dashboard.VERSION = '0.5.1';

    proto.init = function()
    {
        this.$fontSizes  = $( '#fontctrl > a' );
        this.$helpCtrl   = $( '#helpctrl' );
        this.$getStarted = $( '#getstarted' );
        this.$bookmarks  = $( '#bookmarks' );
        this.$version    = $( '#version' );

        this.manager  = new mdash.Manager(),
        this.fontCtrl = new mdash.FontCtrl(  ),
        this.helpCtrl = new mdash.HelpCtrl( $helpCtrl, $getStarted, $bookmarks );

        this.fontCtrl.init();
        this.helpCtrl.init();
        this.manager.init( this.loadBookmarks.bind( this ) );

        this.showVersion();
        this.loadBookmarks();
    };

    proto.loadBookmarks = function()
    {
        var _this = this;

        this.leftColumn  = new mdash.Column( $( '#bookmarks > .left' ) );
        this.rightColumn = new mdash.Column( $( '#bookmarks > .right' ) );

        manager.getSections( 'left', function( sections )
        {
            _this.leftColumn.sections = sections;
            _this.leftColumn.render();
        } );

        manager.getSections( 'right', function( sections )
        {
            _this.rightColumn.sections = sections;
            _this.rightColumn.render();
        } );
    };

    proto.showVersion = function()
    {
        this.$version.html( Dashboard.VERSION );
    };

} )( window.mdash, Zepto );

Refactor the app initialization code into its own class.


"use strict";

var path = require("path");
var clc = require("cli-color");
var _ = require("lodash");

module.exports = function (mPath, moduleIsOptional, opts) {
  var resolvedRequire;
  mPath = mPath.trim();

  var runOpts = _.assign({
    require: require,
    console: console
  }, opts);

  if (mPath.charAt(0) === ".") {
    resolvedRequire = path.resolve(process.cwd() + "/" + mPath);
  } else {
    resolvedRequire = mPath;
  }

  var RequiredModule;
  try {
    /*eslint global-require: 0*/
    RequiredModule = require(resolvedRequire);
  } catch (e) {
    if (e.code === "MODULE_NOT_FOUND" && moduleIsOptional !== true) {
      console.error(clc.redBright("Error loading a module from user configuration."));
      console.error(clc.redBright("Cannot find module: " + resolvedRequire));
      throw new Error(e);
    } else if (e.code === "MODULE_NOT_FOUND" && moduleIsOptional === true) {
      // Do nothing
    } else {
      throw new Error(e);
    }
  }

  return RequiredModule ? new RequiredModule() : null;
};

"use strict";

var path = require("path");
var clc = require("cli-color");
var _ = require("lodash");

module.exports = function (mPath, moduleIsOptional, opts) {
  var resolvedRequire;
  mPath = mPath.trim();

  var runOpts = _.assign({
    require: require,
    console: console
  }, opts);

  if (mPath.charAt(0) === ".") {
    resolvedRequire = path.resolve(process.cwd() + "/" + mPath);
  } else {
    resolvedRequire = mPath;
  }

  var RequiredModule;
  try {
    /*eslint global-require: 0*/
    RequiredModule = runOpts.require(resolvedRequire);
  } catch (e) {
    if (e.code === "MODULE_NOT_FOUND" && moduleIsOptional !== true) {
      runOpts.console.error(clc.redBright("Error loading a module from user configuration."));
      runOpts.console.error(clc.redBright("Cannot find module: " + resolvedRequire));
      throw new Error(e);
    } else if (e.code === "MODULE_NOT_FOUND" && moduleIsOptional === true) {
      // Do nothing
    } else {
      throw new Error(e);
    }
  }

  return RequiredModule ? new RequiredModule() : null;
};

Use latest from master as a base.


(function (factory) {
  if (typeof define === 'function' && define.amd) {
    define(['jquery'], factory);
  } else if (typeof module === 'object' && typeof module.exports === 'object') {
    factory(require('jquery'));
  } else {
    factory(jQuery);
  }
}(function (jQuery) {
  // Turkish shortened
  jQuery.timeago.settings.strings = {
    prefixAgo: null,
    prefixFromNow: null,
    suffixAgo: "",
    suffixFromNow: "",
    seconds: "1sn",
    minute: "1d",
    minutes: "%dd",
    hour: "1s",
    hours: "%ds",
    day: "1g",
    days: "%dg",
    month: "1ay",
    months: "%day",
    year: "1y",
    years: "%dy",
    wordSeparator: " ",
    numbers: []
  };
}));

(function (factory) {
  if (typeof define === 'function' && define.amd) {
    define(['jquery'], factory);
  } else if (typeof module === 'object' && typeof module.exports === 'object') {
    factory(require('jquery'));
  } else {
    factory(jQuery);
  }
}(function (jQuery) {
  // Turkish shortened
  jQuery.timeago.settings.strings = {
    prefixAgo: null,
    prefixFromNow: null,
    suffixAgo: "",
    suffixFromNow: "",
    seconds: "1dk",
    minute: "1d",
    minutes: "%dd",
    hour: "1s",
    hours: "%ds",
    day: "1g",
    days: "%dg",
    month: "1ay",
    months: "%day",
    year: "1y",
    years: "%dy",
    wordSeparator: " ",
    numbers: []
  };
}));

Fix shorthand translation of "seconds"


/**
 * @author xialei <xialeistudio@gmail.com>
 */
angular.module('angular-icheck', [])
    .directive('iCheck', [function () {
        return {
            restrict: 'EA',
            transclude: true,
            require: 'ngModel',
            replace: true,
            template: '
\n

\n

\n
',
            link: function (scope, ele, attrs, ctrl) {
                var box = angular.element(ele[0].querySelector('.checkbox'));
                ele.bind("click", function () {
                    box.toggleClass("checked");
                    ctrl.$setViewValue(box.hasClass("checked"));

                });
                ctrl.$render = function () {
                    if (ctrl.$viewValue) {
                        box.addClass("checked");
                    }else{
                        box.removeClass("checked");
                    }
                };
            }
        }
    }]);

/**
 * @author xialei <xialeistudio@gmail.com>
 */
angular.module('angular-icheck', [])
    .directive('iCheck', [function () {
        return {
            restrict: 'EA',
            transclude: true,
            require: 'ngModel',
            replace: true,
            template: '
\n

\n

\n
',
            link: function (scope, ele, attrs, ctrl) {
                var box = angular.element(ele[0].querySelector('.checkbox'));
                ele.bind("click", function () {
                    box.toggleClass("checked");
                    ctrl.$setViewValue(box.hasClass("checked"));
                });
                ctrl.$render = function () {
                    if (ctrl.$viewValue) {
                        box.addClass("checked");
                    } else {
                        box.removeClass("checked");
                    }
                };
                // https://github.com/angular/angular.js/issues/2594
                // override $isEmpty method
                ctrl.$isEmpty = function(value) {
                    return value === false;
                };
                ctrl.$setViewValue(box.hasClass("checked"));
                ctrl.$validate();
            }
        }
    }]);

FIX - override ngModel.$isEmpty to work as in input type checkbox.


'use strict';

import $ from 'jquery';

export default class ImageLoader {

    constructor(dataSrcAttr) {
        this.dataSrcAttr = dataSrcAttr || 'data-preload-src';
    }

    isInlineImage($el) {
        return $el.is('img');
    }

    getDataSrc($el) {
        return $el.attr(this.dataSrcAttr);
    }

    setInlineImageSrc($el, src) {
        $el.attr('src', src);
    }

    setBackgroundImageUrl($el, src) {
        var val = `url(${src})`;
        $el.css({'background-image': val});
    }

    setSrc($el, src) {
        if (this.isInlineImage($el)) {
            this.setInlineImageSrc($el, src);
        }
        else {
            this.setBackgroundImageUrl($el, src);
        }
    }

    loadImage($image) {

        return new Promise((resolve, reject) => {
            var img = new Image(),
                src = this.getDataSrc($image);

            img.src = src;

            img.addEventListener('load', () => {
                this.setSrc($image, src);
                resolve();
            });

            img.addEventListener('error', () => {
                reject(`Error loading image src ${src}`);
            });
        });
    }

    loadImages($images) {
        var len = $images.length,
            promises = [];

        for (let i = 0; i < len; i++) {
            let $image = $($images[i]);
            var promise = this.loadImage($image);
            promises.push(promise);
        }

        return Promise.all(promises);
    }

}

'use strict';

import $ from 'jquery';

export default class ImageLoader {

    constructor(dataSrcAttr) {
        this.dataSrcAttr = dataSrcAttr || 'data-preload-src';
    }

    isInlineImage($el) {
        return $el.is('img');
    }

    getDataSrc($el) {
        return $el.attr(this.dataSrcAttr);
    }

    setInlineImageSrc($el, src) {
        $el.attr('src', src);
    }

    setBackgroundImageUrl($el, src) {
        var val = `url(${src})`;
        $el.css({'background-image': val});
    }

    setSrc($el, src) {
        if (this.isInlineImage($el)) {
            this.setInlineImageSrc($el, src);
        }
        else {
            this.setBackgroundImageUrl($el, src);
        }
    }

    loadImage($image) {

        return new Promise((resolve, reject) => {
            var img = new Image(),
                src = this.getDataSrc($image);

            img.src = src;

            img.addEventListener('load', () => {
                this.setSrc($image, src);
                resolve($image);
            });

            img.addEventListener('error', () => {
                reject(`Error loading image src ${src}`);
            });
        });
    }

    loadImages($images) {
        var len = $images.length,
            promises = [];

        for (let i = 0; i < len; i++) {
            let $image = $($images[i]);
            var promise = this.loadImage($image);
            promises.push(promise);
        }

        return Promise.all(promises);
    }

}

Resolve promise with image value in image loader


/**
 * Module dependencies.
 */
var path = require('path')
  , rivet = require('..')

/**
 * Executes `tasks` in rivet `file`.
 *
 * @param {String} file
 * @param {Array} tasks
 * @api protected
 */
module.exports = function exec(file, tasks, options) {
  require(file)(rivet);
  rivet.run(tasks, options, function(err) {
    if (err) {
      console.error(err.message);
      if (options.trace) { console.error(err.stack); }
    }
  });
}

/**
 * Module dependencies.
 */
var path = require('path')
  , rivet = require('..')

/**
 * Executes `tasks` in rivet `file`.
 *
 * @param {String} file
 * @param {Array} tasks
 * @api protected
 */
module.exports = function exec(file, tasks, options) {
  require(file)(rivet);
  rivet.run(tasks, options, function(err) {
    if (err) {
      console.error(err.message);
      if (options.trace) { console.error(err.stack); }
      return process.exit(-1);
    }
    return process.exit();
  });
}

Exit with error code on failure.


Ember.LinkView.reopen({
    active: Ember.computed('resolvedParams', 'routeArgs', function () {
        var isActive = this._super();

        Ember.set(this, 'alternateActive', isActive);

        return isActive;
    })
});

Ember.LinkView.reopen({
    active: Ember.computed('resolvedParams', 'routeArgs', function () {
        var isActive = this._super();

        Ember.set(this, 'alternateActive', isActive);

        return isActive;
    }),

    activeClass: Ember.computed('tagName', function () {
        return this.get('tagName') === 'button' ? '' : 'active';
    })
});

Remove active class from buttons.


module.exports = {
  description: 'Generates a controller of the given type.',

  availableOptions: [
    { name: 'type', values: ['basic', 'object', 'array'], default: 'basic' }
  ],

  beforeInstall: function(options) {
    this.lookupBlueprint('controller').beforeInstall(options);
  },
  locals: function(options) {
    return this.lookupBlueprint('controller').locals(options);
  }
};

module.exports = {
  description: 'Generates a controller of the given type.',

  availableOptions: [
    { name: 'type', values: ['basic', 'object', 'array'], default: 'basic' }
  ],

  install: function(options) {
    return this.lookupBlueprint('controller').install(options);
  },

  beforeInstall: function(options) {
    var controllerBlueprint = this.lookupBlueprint('controller');
    return controllerBlueprint.beforeInstall(options);
  },
  locals: function(options) {
    return this.lookupBlueprint('controller').locals(options);
  }
};

Make workaround for controller-generation issue.


/*
 * <%= props.name %>
 * <%= props.homepage %>
 *
 * Copyright (c) <%= currentYear %> <%= props.author_name %>
 * Licensed under the <%= props.license %> license.
 */

(function($) {

  // Collection method.
  $.fn.awesome = function() {
    return this.each(function(i) {
      // Do something awesome to each selected element.
      $(this).html('awesome' + i);
    });
  };

  // Static method.
  $.awesome = function(options) {
    // Override default options with passed-in options.
    options = $.extend({}, $.awesome.options, options);
    // Return something awesome.
    return 'awesome' + options.punctuation;
  };

  // Static method default options.
  $.awesome.options = {
    punctuation: '.'
  };

  // Custom selector.
  $.expr[':'].awesome = function(elem) {
    // Is this element awesome?
    return $(elem).text().indexOf('awesome') !== -1;
  };

}(jQuery));

/*
 * <%= props.name %>
 * <%= props.homepage %>
 *
 * Copyright (c) <%= currentYear %> <%= props.author_name %>
 * Licensed under the <%= props.license %> license.
 */

(function ($) {

  // Collection method.
  $.fn.awesome = function () {
    return this.each(function (i) {
      // Do something awesome to each selected element.
      $(this).html('awesome' + i);
    });
  };

  // Static method.
  $.awesome = function (options) {
    // Override default options with passed-in options.
    options = $.extend({}, $.awesome.options, options);
    // Return something awesome.
    return 'awesome' + options.punctuation;
  };

  // Static method default options.
  $.awesome.options = {
    punctuation: '.'
  };

  // Custom selector.
  $.expr[':'].awesome = function (elem) {
    // Is this element awesome?
    return $(elem).text().indexOf('awesome') !== -1;
  };

}(jQuery));

Adjust template js style to rest


(function() {

	var URL = window.URL || window.webkitURL;

	core.Module("lowland.bom.Binary", {
		createUrl : URL.createObjectURL,

		revokeUrl : URL.revokeObjectURL,

		createBlob : function(content, options) {
			// this is a fix for iOS/iPad as it don't support creating blobs from typed arrays (only from buffer of them)
			var contentLength = content.length;
			var nc = new Array(contentLength);
			for (var i=0; i<contentLength; i++) {
				var c = content[i];
				if (c.buffer) {
					nc[i] = c.buffer;
				} else {
					nc[i] = c;
				}
			}

			return new Blob(nc, options);
		}
	});
})();

(function() {

	var URL = window.URL || window.webkitURL;
	var supported = true;
	if (!URL) {
		supported = false;
		URL = {
			createObjectURL : function() {
				if (jasy.Env.isSet("debug")) {
					console.error("Browser don't support ObjectURL");
				}
			},
			revokeObjectURL : function() {
				if (jasy.Env.isSet("debug")) {
					console.error("Browser don't support ObjectURL");
				}
			}
		};
	}

	core.Module("lowland.bom.Binary", {
		isSupported : supported,

		createUrl : URL.createObjectURL,

		revokeUrl : URL.revokeObjectURL,

		createBlob : function(content, options) {
			// this is a fix for iOS/iPad as it don't support creating blobs from typed arrays (only from buffer of them)
			var contentLength = content.length;
			var nc = new Array(contentLength);
			for (var i=0; i<contentLength; i++) {
				var c = content[i];
				if (c.buffer) {
					nc[i] = c.buffer;
				} else {
					nc[i] = c;
				}
			}

			return new Blob(nc, options);
		}
	});
})();

Fix ObjectURL in browsers that don't support it


/* @flow */
import { applyMiddleware, compose, createStore } from 'redux';
import { persistStore, autoRehydrate } from 'redux-persist';
import type { Config } from 'redux-persist';

import config from '../config';
import rootReducer from './reducers';
import middleware from './middleware';
import ZulipAsyncStorage from './ZulipAsyncStorage';

// AsyncStorage.clear(); // use to reset storage during development

const store = compose(applyMiddleware(...middleware), autoRehydrate())(createStore)(rootReducer);

const reduxPersistConfig: Config = {
  whitelist: [...config.storeKeys, ...config.cacheKeys],
  // $FlowFixMe: https://github.com/rt2zz/redux-persist/issues/823
  storage: ZulipAsyncStorage,
};

export const restore = (onFinished?: () => void) =>
  persistStore(store, reduxPersistConfig, onFinished);

export default store;

/* @flow */
import { applyMiddleware, compose, createStore } from 'redux';
import { persistStore, autoRehydrate } from 'redux-persist';
import type { Config } from 'redux-persist';

import config from '../config';
import rootReducer from './reducers';
import middleware from './middleware';
import ZulipAsyncStorage from './ZulipAsyncStorage';

// AsyncStorage.clear(); // use to reset storage during development

const store = createStore(
  rootReducer,
  undefined,
  compose(applyMiddleware(...middleware), autoRehydrate()),
);

const reduxPersistConfig: Config = {
  whitelist: [...config.storeKeys, ...config.cacheKeys],
  // $FlowFixMe: https://github.com/rt2zz/redux-persist/issues/823
  storage: ZulipAsyncStorage,
};

export const restore = (onFinished?: () => void) =>
  persistStore(store, reduxPersistConfig, onFinished);

export default store;

Rearrange `createStore` call to the form found in docs.


Hide page elements for fadeIn immediately, rather than taking 400.


import React from 'react'
import { List } from 'semantic-ui-react'
import { version } from 'package.json'

export default function Footer() {
  let versionStr

  if (GIT_TAG && GIT_TAG !== 'unknown') versionStr = GIT_TAG
  if (GIT_COMMIT && GIT_COMMIT !== 'unknown') versionStr = GIT_COMMIT
  if (!versionStr) versionStr = version

  return (
    <React.Fragment>
      <List horizontal inverted divided link>
        <List.Item as='a' href='#'>© Server Name Here</List.Item>
        <List.Item as='a' href='#'>Contact Us</List.Item>
        <List.Item as='a' href='#'>Link Example</List.Item>
      </List>
      <List floated='right' horizontal>
        <List.Item as='a' href='https://github.com/BanManagement/BanManager-WebUI' floated='right'>v{versionStr}</List.Item>
      </List>
    </React.Fragment>
  )
}

import React from 'react'
import { List, Responsive } from 'semantic-ui-react'
import { version } from 'package.json'

export default function Footer() {
  let versionStr

  if (GIT_TAG && GIT_TAG !== 'unknown') versionStr = GIT_TAG
  if (GIT_COMMIT && GIT_COMMIT !== 'unknown') versionStr = GIT_COMMIT
  if (!versionStr) versionStr = version

  return (
    <React.Fragment>
      <List horizontal inverted divided link>
        <List.Item as='a' href='#'>© Server Name Here</List.Item>
        <List.Item as='a' href='#'>Contact Us</List.Item>
        <List.Item as='a' href='#'>Link Example</List.Item>
      </List>
      <Responsive
        minWidth={Responsive.onlyTablet.minWidth}
        as={List}
        floated='right'
        horizontal>
          <List.Item as='a' href='https://github.com/BanManagement/BanManager-WebUI' floated='right'>v{versionStr}</List.Item>
      </Responsive>
    </React.Fragment>
  )
}

Hide version in footer on mobile devices


const MAX_INT32 = 2147483647;
const MIN_STD = 16807;

/**
 * Generate a hashed number based on input string
 * Taken from https://github.com/darkskyapp/string-hash/blob/master/index.js
 */
export function hash(str) {
  let hash = 5381;
  let i = str.length;

  while (i) {
    hash = (hash * 33) ^ str.charCodeAt(--i);
  }

  // JavaScript does bitwise operations (like XOR, above) on 32-bit signed
  // integers. Since we want the results to be always positive, convert the
  // signed int to an unsigned by doing an unsigned bitshift.
  return hash >>> 0;
}

export class SeededRandom {
  constructor(seed) {
    let sourceSeed = seed % MAX_INT32;
    if (sourceSeed <= 0) {
      sourceSeed += MAX_INT32 - 1;
    }
    this.seed = sourceSeed;
  }

  next = () => {
    return (this.seed = (this.seed * MIN_STD) % MAX_INT32);
  };

  nextFloat = () => {
    return (this.next() - 1) / (MAX_INT32 - 1);
  };
}

const MAX_INT32 = 2147483647;
const MIN_STD = 16807;

/**
 * Generate a hashed number based on input string
 * Taken from https://github.com/darkskyapp/string-hash/blob/master/index.js
 */
export function hash(str) {
  let hash = 5381;
  let i = str.length;

  while (i) {
    hash = (hash * 33) ^ str.charCodeAt(--i);
  }

  // JavaScript does bitwise operations (like XOR, above) on 32-bit signed
  // integers. Since we want the results to be always positive, convert the
  // signed int to an unsigned by doing an unsigned bitshift.
  return hash >>> 0;
}

export class SeededRandom {
  constructor(seed) {
    let sourceSeed = seed % MAX_INT32;
    if (sourceSeed <= 0) {
      sourceSeed += MAX_INT32 - 1;
    }
    this.seed = sourceSeed;
  }

  next() {
    return (this.seed = (this.seed * MIN_STD) % MAX_INT32);
  }

  nextFloat() {
    return (this.next() - 1) / (MAX_INT32 - 1);
  }
}

Fix class syntax for SeededRandom


import path from 'path'
import webpack from 'webpack'
import StaticSiteGeneratorPlugin from 'static-site-generator-webpack-plugin'
import ExtractTextPlugin from 'extract-text-webpack-plugin'

const config = {
  entry: './index.js',

  output: {
    filename: 'main.[hash].js',
    path: path.join(__dirname, 'build'),
    libraryTarget: 'umd'
  },

  externals: [
    './site/server',
  ],

  devtool: 'source-map',

  module: {
    loaders: [
      {
        test: /\.less$/,
        loader: 'css!autoprefixer!less',
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loaders: ['react-hot', 'babel'],
      },
      {
        test: /\.json$/,
        loader: 'json',
      },
      {
        test: /\/static\/favicon\.png$/,
        loader: 'file?name=[name].[ext]',
      },
      {
        test: /\.mid$/,
        loader: 'file?name=[path][name].[hash].[ext]',
      },
    ]
  },

  plugins: [
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
    }),
    new StaticSiteGeneratorPlugin('main', ['/'], {data: {}}),
  ],
}

if (process.env.NODE_ENV === 'production') {
  const lessLoader = config.module.loaders[0]
  lessLoader.loader = ExtractTextPlugin.extract(lessLoader.loader)
  config.plugins.push(new ExtractTextPlugin('main.[contenthash].css'))
}

export default config

import path from 'path'
import webpack from 'webpack'
import StaticSiteGeneratorPlugin from 'static-site-generator-webpack-plugin'
import ExtractTextPlugin from 'extract-text-webpack-plugin'

const config = {
  entry: './index.js',

  output: {
    filename: 'main.[hash].js',
    path: path.join(__dirname, 'build'),
    publicPath: '/',
    libraryTarget: 'umd'
  },

  externals: [
    './site/server',
  ],

  devtool: 'source-map',

  module: {
    loaders: [
      {
        test: /\.less$/,
        loader: 'css!autoprefixer!less',
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loaders: ['react-hot', 'babel'],
      },
      {
        test: /\.json$/,
        loader: 'json',
      },
      {
        test: /\/static\/favicon\.png$/,
        loader: 'file?name=[name].[ext]',
      },
      {
        test: /\.mid$/,
        loader: 'file?name=[path][name].[hash].[ext]',
      },
    ]
  },

  plugins: [
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
    }),
    new StaticSiteGeneratorPlugin('main', ['/'], {data: {}}),
  ],
}

if (process.env.NODE_ENV === 'production') {
  const lessLoader = config.module.loaders[0]
  lessLoader.loader = ExtractTextPlugin.extract(lessLoader.loader)
  config.plugins.push(new ExtractTextPlugin('main.[contenthash].css'))
}

export default config

Set webpack public path so file URLs become absolute


var cssnext = require('postcss-cssnext');
var postcssFocus = require('postcss-focus');
var postcssReporter = require('postcss-reporter');

module.exports = {
  output: {
    publicPath: '/',
    libraryTarget: 'commonjs2',
  },
  resolve: {
    extensions: ['', '.js', '.jsx'],
    modules: [
      'client',
      'node_modules',
    ],
  },
  module: {
    loaders: [
      {
        test: /\.css$/,
        exclude: /node_modules/,
        loader: 'style-loader!css-loader?localIdentName=[name]__[local]__[hash:base64:5]&modules&importLoaders=1&sourceMap!postcss-loader',
      },
      {
        test: /\.jpe?g$|\.gif$|\.png$|\.svg$/i,
        loader: 'url-loader?limit=10000',
      },
    ],
  },
  postcss: () => [
    postcssFocus(),
    cssnext({
      browsers: ['last 2 versions', 'IE > 10'],
    }),
    postcssReporter({
      clearMessages: true,
    }),
  ],
};

var cssnext = require('postcss-cssnext');
var postcssFocus = require('postcss-focus');
var postcssReporter = require('postcss-reporter');

var cssModulesIdentName = '[name]__[local]__[hash:base64:5]';
if (process.env.NODE_ENV === 'production') {
  cssModulesIdentName = '[hash:base64]';
}

module.exports = {
  output: {
    publicPath: '/',
    libraryTarget: 'commonjs2',
  },
  resolve: {
    extensions: ['', '.js', '.jsx'],
    modules: [
      'client',
      'node_modules',
    ],
  },
  module: {
    loaders: [
      {
        test: /\.css$/,
        exclude: /node_modules/,
        loader: 'style-loader!css-loader?localIdentName=' + cssModulesIdentName + '&modules&importLoaders=1&sourceMap!postcss-loader',
      },
      {
        test: /\.jpe?g$|\.gif$|\.png$|\.svg$/i,
        loader: 'url-loader?limit=10000',
      },
    ],
  },
  postcss: () => [
    postcssFocus(),
    cssnext({
      browsers: ['last 2 versions', 'IE > 10'],
    }),
    postcssReporter({
      clearMessages: true,
    }),
  ],
};

Fix CSS Modules ident name for production build


if (!String.fromCodePoint) {
	String.fromCodePoint = function() {
		var codePoints = [];
		var floor = Math.floor;
		var highSurrogate;
		var lowSurrogate;
		var index = -1;
		var length = arguments.length;
		if (!length) {
			return '';
		}
		while (++index < length) {
			var codePoint = Number(arguments[index]);
			if (
				!isFinite(codePoint) || // `NaN`, `+Infinity`, or `-Infinity`
				codePoint < 0 || // not a valid Unicode code point
				codePoint > 0x10FFFF || // not a valid Unicode code point
				floor(codePoint) != codePoint // not an integer
			) {
				throw RangeError('Invalid code point: ' + codePoint);
			}
			if (codePoint <= 0xFFFF) { // BMP code point
				codePoints.push(codePoint);
			} else { // Astral code point; split in surrogate halves
				// http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
				codePoint -= 0x10000;
				highSurrogate = (codePoint >> 10) + 0xD800;
				lowSurrogate = (codePoint % 0x400) + 0xDC00;
				codePoints.push(highSurrogate, lowSurrogate);
			}
		}
		return String.fromCharCode.apply(null, codePoints);
	};
}

if (!String.fromCodePoint) {
	String.fromCodePoint = function() {
		var codeUnits = [];
		var floor = Math.floor;
		var highSurrogate;
		var lowSurrogate;
		var index = -1;
		var length = arguments.length;
		if (!length) {
			return '';
		}
		while (++index < length) {
			var codePoint = Number(arguments[index]);
			if (
				!isFinite(codePoint) || // `NaN`, `+Infinity`, or `-Infinity`
				codePoint < 0 || // not a valid Unicode code point
				codePoint > 0x10FFFF || // not a valid Unicode code point
				floor(codePoint) != codePoint // not an integer
			) {
				throw RangeError('Invalid code point: ' + codePoint);
			}
			if (codePoint <= 0xFFFF) { // BMP code point
				codeUnits.push(codePoint);
			} else { // Astral code point; split in surrogate halves
				// http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
				codePoint -= 0x10000;
				highSurrogate = (codePoint >> 10) + 0xD800;
				lowSurrogate = (codePoint % 0x400) + 0xDC00;
				codeUnits.push(highSurrogate, lowSurrogate);
			}
		}
		return String.fromCharCode.apply(null, codeUnits);
	};
}

Rename `codePoints` into `codeUnits` as that’s more accurate


/*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
requier(['common'], function() {
    'use strict';
    require(['app/view-boots-all-lab']);
});

/*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
require(['common'], function() {
    'use strict';
    require(['app/view-boots-all-lab']);
});

Fix typo in JavaScript file.


'use strict'

const Ipfs = require('ipfs')
const { createProxyClient } = require('ipfs-postmsg-proxy')
const _Buffer = Buffer

window.Buffer = window.Buffer || _Buffer
window.Ipfs = window.Ipfs || Ipfs
window.ipfs = window.ipfs || createProxyClient()

'use strict'

const { createProxyClient } = require('ipfs-postmsg-proxy')
const _Buffer = Buffer

window.Buffer = window.Buffer || _Buffer
window.ipfs = window.ipfs || createProxyClient()

Remove API client constructor from window.Ipfs