var HyperdeckCore = require("./hyperdeck-core.js");

var Hyperdeck = function(ip) {
  //Start by connecting to Hyperdeck via HypderdeckCore
  var Core = new HyperdeckCore(ip);
  Core.makeRequest("notify: remote: true");
  Core.makeRequest("notify: transport: true");
  Core.makeRequest("notify: slot: true");
  Core.makeRequest("notify: configuration: true");

  //Publicise functions from Core
  this.getNotifier = Core.getNotifier();
  this.makeRequest = Core.makeRequest();
  this.onConnected = Core.onConnected();

  //make Easy Access commands
  this.play = function(speed) {
    var commandString;
    if (Math.abs(speed) <= 1600) {
      commandString = "play speed: " + speed;
    } else {
      if (speed) {
        throw "Speed out of range";
      } else {
        commandString = "play";
      }
    }
    return Core.makeRequest(commandString);
  };

  this.stop = function() {
    return Core.makeRequest("stop");
  };

  this.record = function() {
    return Core.makeRequest("record");
  };

  this.goTo = function(timecode) {
    return Core.makeRequest("goto: timecode: " + timecode);
  };

};

module.exports = Hyperdeck;

var HyperdeckCore = require("./hyperdeck-core.js");

var Hyperdeck = function(ip) {
  //Start by connecting to Hyperdeck via HypderdeckCore
  var Core = new HyperdeckCore(ip);
  Core.makeRequest("notify: remote: true");
  Core.makeRequest("notify: transport: true");
  Core.makeRequest("notify: slot: true");
  Core.makeRequest("notify: configuration: true");

  //Publicise functions from Core
  this.getNotifier = Core.getNotifier();
  this.makeRequest = Core.makeRequest();
  this.onConnected = Core.onConnected();

  //make Easy Access commands
  this.play = function(speed) {
    var commandString;
    if (Math.abs(speed) <= 1600) {
      commandString = "play speed: " + speed;
    } else {
      if (speed) {
        throw new Error("Speed value invalid or out of range");
      } else {
        commandString = "play";
      }
    }
    return Core.makeRequest(commandString);
  };

  this.stop = function() {
    return Core.makeRequest("stop");
  };

  this.record = function() {
    return Core.makeRequest("record");
  };

  this.goTo = function(timecode) {
    return Core.makeRequest("goto: timecode: " + timecode);
  };

};

module.exports = Hyperdeck;

Update Speed 'out of range or invalid' error


// Level 2
// Static synchronous definition in a private context shared by all modules
// Implementation of scopeornot API
// https://github.com/eric-brechemier/scopeornot

// This script must be loaded in level 2

/*global scope*/
scope(function(parentContext){
  var
    // private field
    privateContext = {};

  /*
    Function: scope(code[,needs[,name]]): any
    Run code immediately in a private context, and set the return value,
    if any, to a property with given name in the private context

    Parameters:
      code - function(context), the code to run with the private context
             provided as parameter
      needs - optional, array of strings, the names of the properties that
              this code would like to find in the private context
      name - optional, string, name of the private context property to set
             the value that the code may return

    Returns:
      any, the return value of the code
  */
  function scope(code,needs,name){
    var result = code(privateContext);

    if (typeof name !== "string"){
      return result;
    }

    privateContext[name] = value;
    if (name === "scope"){
      // replace the current implementation of scope() in parent context
      parentContext.scope = value;
    }
    return result;
  }

  return scope;
},[],"scope");

// Level 2
// Static synchronous definition in a private context shared by all modules
// Implementation of scopeornot API
// https://github.com/eric-brechemier/scopeornot

// This script must be loaded in level 2

/*global scope*/
scope(function(parentContext){
  var
    // private field
    privateContext = {};

  /*
    Function: scope(code[,needs[,name]]): any
    Run code immediately in a private context, and set the return value,
    if any, to a property with given name in the private context

    Parameters:
      code - function(context), the code to run with the private context
             provided as parameter
      needs - optional, array of strings, the names of the properties that
              this code would like to find in the private context
      name - optional, string, name of the private context property to set
             the value that the code may return

    Returns:
      any, the return value of the code
  */
  function scope(code,needs,name){
    var result = code(privateContext);

    if (typeof name !== "string"){
      return result;
    }

    privateContext[name] = result;
    if (name === "scope"){
      // replace the current implementation of scope() in parent context
      parentContext.scope = result;
    }
    return result;
  }

  return scope;
},[],"scope");

Fix variable name: "result" instead of "value"


/*global define*/
define([], function() {
    'use strict';

    /**
     * Utility function for retrieving the number of components in a given type.
     * As per the spec:
     *     'SCALAR' : 1
     *     'VEC2'   : 2
     *     'VEC3'   : 3
     *     'VEC4'   : 4
     *     'MAT2'  : 4
     *     'MAT3'   : 9
     *     'MAT4'   : 16
     *
     * @param {String} type glTF type
     * @returns {Number} The number of components in that type.
     */
    function numberOfComponentsForType(type) {
        switch (type) {
            case 'SCALAR':
                return 1;
            case 'VEC2':
                return 2;
            case 'VEC3':
                return 3;
            case 'VEC4':
            case 'MAT2':
                return 4;
            case 'MAT3':
                return 9;
            case 'MAT4':
                return 16;
        }
    }
    return numberOfComponentsForType;
});

Update from small pipeline changes


'use strict';
/* jshint -W074 */

const network = require('./network');
const runtime = require('./runtime');

class ChannelDelegate {

  static create (name, options, context) {
    options = options || {};
    let id;
    if (!runtime.auth) throw new Error('Not authenticated.');
    const array = [runtime.auth.identity.organization, name, (options.system ? 1 : 0), (options.consumer ? 1: 0)];
    const string = JSON.stringify(array);
    if (typeof window === 'undefined') id = (new Buffer(string)).toString('base64');
    else id = btoa(string);
    return new this(id, context);
  }

  constructor (id, context) {
    this.id = id;
    this.context = context;
  }

  broadcast (name, payload, timeout) {
    return network.broadcast(name, this.id, payload, timeout);
  }

  listen (name, callback) {
    return network.listen(name, this.id, callback, this.context);
  }

}

module.exports = ChannelDelegate;

'use strict';
/* jshint -W074 */

const network = require('./network');
const runtime = require('./runtime');

class ChannelDelegate {

  static create (name, options, context) {
    options = options || {};
    let id;
    if (!runtime.auth) throw new Error('Not authenticated.');
    const array = [runtime.auth.identity.organization, name, (options.system ? 1 : 0), (options.consumer ? 1: 0)];
    const string = JSON.stringify(array);
    if (typeof window === 'undefined') id = (new Buffer(string)).toString('base64');
    else id = btoa(string);
    return new this(id, context);
  }

  constructor (id, context) {
    this.id = id;
    this.context = context;
  }

  broadcast (name, payload, timeout) {
    return network.broadcast(name, this.id, payload, timeout);
  }

  listen (name, callback) {
    return network.listen(name, this.id, callback, this.context);
  }

  fling (payload) {
    return this.broadcast('fling', payload);
  }

}

module.exports = ChannelDelegate;

Add fling to channel object.


import React from 'react';
import { Container, Row, Col } from 'reactstrap';

export default () => {
  return (

      <Container>
        <Row>
          <Col className="text-center">

          </Col>
        </Row>
      </Container>

  );
};

Add netlify badge to qualify for open source plan


import React from 'react';
import ReactDOM from 'react-dom';
import {Router, Route} from 'react-router';
import { history } from 'react-router/lib/HashHistory';
import AsyncProps from 'react-router/lib/experimental/AsyncProps';

var rootRoute = {
  component: AsyncProps,

  renderInitialLoad() {
    return
loading...

  },

  childRoutes: [{
    path: '/',
    component: require('./components/master'),
    childRoutes: [
      require('./components/routes/gettingstarted'),
      require('./components/routes/core'),
      require('./components/routes/components')
    ]}
  ]
};

ReactDOM.render((
  <Router
    routes={rootRoute}
    history={history}
    createElement={AsyncProps.createElement}
  />
), document.getElementById('hotzone'));

import React from 'react';
import ReactDOM from 'react-dom';
import {Router, Route} from 'react-router';
import AsyncProps from 'react-router/lib/experimental/AsyncProps';

const adapter = 'production' !== process.env.NODE_ENV ? 'HashHistory' : 'BrowserHistory';
let { history } = require('react-router/lib/' + adapter);

var rootRoute = {
  component: AsyncProps,

  renderInitialLoad() {
    return
loading...

  },

  childRoutes: [{
    path: '/',
    component: require('./components/master'),
    childRoutes: [
      require('./components/routes/gettingstarted'),
      require('./components/routes/core'),
      require('./components/routes/components')
    ]}
  ]
};

ReactDOM.render((
  <Router
    routes={rootRoute}
    history={history}
    createElement={AsyncProps.createElement}
  />
), document.getElementById('hotzone'));

Load BrowserHistory adapter in production, HashHistory on webpack-dev-server


import Ember from 'ember';
import config from './config/environment';

var Router = Ember.Router.extend({
  location: config.locationType
});

Router.map(function() {
  this.resource('artists');
  this.resource('resources');
  this.resource('donate');
  this.route('login');
});

export default Router;

import Ember from 'ember';
import config from './config/environment';

var Router = Ember.Router.extend({
  location: config.locationType
});

Router.map(function() {
  this.resource('artists');
  this.route('resources');
  this.route('donate');
  this.route('login');
});

export default Router;

Use route instead of resource for route URLs


'use strict';

const BaseRepository = require('@gfcc/mongo-tenant-repository/BaseRepository');

class SubscriptionRepository extends BaseRepository {
  getSchemaDefinition() {
    return {
      subscriberId: {
        type: String,
        required: true,
        index: true,
      },
      releaseNotesId: {
        type: String,
        required: true,
        index: true,
      },
      releaseNotesScope: {
        type: String,
        required: true,
        index: true,
      },
      releaseNotesName: {
        type: String,
        required: true,
      },
      email: {
        type: String,
      },
    };
  }

  /**
   * Lookup all subscriptions of the given subscriber.
   *
   * @param {string} subscriberId
   * @param {function} callback
   * @return {SubscriptionRepository}
   */
  findBySubscriberId(subscriberId, callback) {
    return this.find({
      subscriberId,
    }, callback);
  }

  /**
   * Lookup all subscriptions of a subscriber on a specific release notes.
   *
   * @param subscriberId
   * @param releaseNotesScope
   * @param releaseNotesName
   * @param callback
   * @return {BaseRepository}
   */
  findBySubscriberAndReleaseNotes({ subscriberId, releaseNotesScope, releaseNotesName }, callback) {
    return this.find({
      subscriberId,
      releaseNotesScope,
      releaseNotesName,
    }, callback);
  }
}

module.exports = SubscriptionRepository;

'use strict';

const BaseRepository = require('@gfcc/mongo-tenant-repository/BaseRepository');

class SubscriptionRepository extends BaseRepository {
  getSchemaDefinition() {
    return {
      subscriberId: {
        type: String,
        required: true,
        index: true,
      },
      releaseNotesId: {
        type: String,
        required: true,
        index: true,
      },
      releaseNotesScope: {
        type: String,
        required: true,
        index: true,
      },
      releaseNotesName: {
        type: String,
        required: true,
      },
      email: {
        type: String,
      },
    };
  }

  /**
   * Lookup all subscriptions of the given subscriber.
   *
   * @param {string} subscriberId
   * @param {function} callback
   * @return {SubscriptionRepository}
   */
  findBySubscriberId(subscriberId, callback) {
    return this.find({
      subscriberId,
    }, callback);
  }

  /**
   * Lookup all subscriptions of a subscriber on a specific release notes.
   *
   * @param subscriberId
   * @param releaseNotesScope
   * @param releaseNotesName
   * @param callback
   * @return {BaseRepository}
   */
  findBySubscriberAndReleaseNotes({ subscriberId, releaseNotesScope, releaseNotesName }, callback) {
    return this.find({
      subscriberId,
      releaseNotesScope,
      releaseNotesName,
    }, callback);
  }

  /**
   * Lookup all subscriptions on the given release notes.
   *
   * @param releaseNotesId
   * @param callback
   * @return {BaseRepository}
   */
  findByReleaseNotesId(releaseNotesId, callback) {
    return this.find({
      releaseNotesId,
    }, callback);
  }
}

module.exports = SubscriptionRepository;

Add support for looking up all subscriptions on a specific release note.


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

import shellMain from 'axiom_shell/main';

// For jshint...
/* global chrome */

// Generic logger of failed promises.
var logCatch = function(err) {
  console.log('logCatch: ' + err.toString(), err);
  if ('stack' in err)
    console.log(err.stack);
};

/**
 * True if we were launched before initialization completed.
 */
var didLaunchEarly = false;

/**
 * A reference to the axiom 'commands' service.
 */
var axiomCommands = null;

/**
 * Called when the user clicks the app-launcher icon.
 */
var onLaunched = function() {
  if (!axiomCommands || !axiomCommands.isReadyState('READY')) {
    didLaunchEarly = true;
    return;
  }

  axiomCommands.dispatch('launch-app').catch(logCatch);
};

// Register onLaunched (for Chrome App) or just launch right away
if (chrome.app && chrome.app.runtime && chrome.app.runtime.onLaunched) {
  chrome.app.runtime.onLaunched.addListener(onLaunched);
} else {
  onLaunched();
}

// And start initialization...
shellMain().then(
  function(moduleManager) {
    axiomCommands = moduleManager.getServiceBinding('commands@axiom');
    return axiomCommands.whenReady().then(
      function() {
        if (didLaunchEarly)
          onLaunched();
      });
  }).catch(logCatch);

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

import shellMain from 'axiom_shell/main';

// For jshint...
/* global chrome */

// Generic logger of failed promises.
var logCatch = function(err) {
  console.log('logCatch: ' + err.toString(), err);
  if ('stack' in err)
    console.log(err.stack);
};

/**
 * True if we were launched before initialization completed.
 */
var didLaunchEarly = false;

/**
 * A reference to the axiom 'commands' service.
 */
var axiomCommands = null;

/**
 * Called when the user clicks the app-launcher icon.
 */
var onLaunched = function() {
  if (!axiomCommands || !axiomCommands.isReadyState('READY')) {
    didLaunchEarly = true;
    return;
  }

  axiomCommands.dispatch('launch-app').catch(logCatch);
};

// Register onLaunched...
chrome.app.runtime.onLaunched.addListener(onLaunched);

// And start initialization...
shellMain().then(
  function(moduleManager) {
    axiomCommands = moduleManager.getServiceBinding('commands@axiom');
    return axiomCommands.whenReady().then(
      function() {
        if (didLaunchEarly)
          onLaunched();
      });
  }).catch(logCatch);

Revert change to chrome app code (not needed anymore).


import React from 'react';
import BigCalendar from 'react-big-calendar';
import moment from 'moment';
import 'react-big-calendar/lib/css/react-big-calendar.css'
BigCalendar.setLocalizer(
  BigCalendar.momentLocalizer(moment)
);

let Basic = React.createClass({
  render(){
    return (
      <div style={{height: '100vh'}}>
        <BigCalendar
          {...this.props}
          events={[
            {
              'title': 'All Day Event',
              'allDay': true,
              'start': new Date(2015, 3, 0),
              'end': new Date(2015, 3, 0)
            },
            {
              'title': 'Long Event',
              'start': new Date(2015, 3, 7),
              'end': new Date(2015, 3, 10)
            }
          ]}
          timeslots={8}
          onSelectEvent={event => alert(event.title)}
          defaultDate={new Date(2015, 3, 1)}
        />
      </div>
    )
  }
})

export default Basic;

import React from 'react';
import BigCalendar from 'react-big-calendar';
import moment from 'moment';
import 'react-big-calendar/lib/css/react-big-calendar.css'
BigCalendar.setLocalizer(
  BigCalendar.momentLocalizer(moment)
);

// TODO take in events as props
let Basic = React.createClass({
  render(){
    return (
      <div style={{height: '100vh'}}>
        <BigCalendar
          {...this.props}
          events={[
            {
              'title': 'All Day Event',
              'allDay': true,
              'start': new Date(2015, 3, 0),
              'end': new Date(2015, 3, 0)
            },
            {
              'title': 'Long Event',
              'start': new Date(2015, 3, 7),
              'end': new Date(2015, 3, 10)
            }
          ]}
          timeslots={8}
          views={['month', 'week']}
          onSelectEvent={event => alert(event.title)}
          defaultDate={new Date(2015, 3, 1)}
        />
      </div>
    )
  }
})

export default Basic;

Set up views to be month and week only


var $ = require('jquery')

module.exports = tabbedMenu

function tabbedMenu (compiler, loadingSpinner, self) {
  $('#options li').click(function (ev) {
    var $el = $(this)
    selectTab($el)
  })

  // initialize tabbed menu
  selectTab($('#options .envView'))

  // add event listeners for loading spinner
  // compiler.event.register('compilationStarted', function compilationStarted () {
  compiler.event.register('loadingCompiler', function compilationStarted () {
    var contractTab = document.querySelector('.envView')
    if (!contractTab.children.length) {
      contractTab.appendChild(loadingSpinner(function cb (finish) {
        // compiler.event.register('compilationFinished', function () {
        compiler.event.register('compilerLoaded', finish)
      }))
    }
  })
  compiler.event.register('loadingCompiler', function loadingCompiler () {
    var settingsTab = document.querySelector('.settingsView')
    if (!settingsTab.children.length) {
      settingsTab.appendChild(loadingSpinner(function cb (finish) {
        compiler.event.register('compilerLoaded', finish)
      }))
    }
  })

  // select tab
  function selectTab (el) {
    var match = /[a-z]+View/.exec(el.get(0).className)
    if (!match) return
    var cls = match[0]
    if (!el.hasClass('active')) {
      el.get(0).parentElement.querySelectorAll('li').forEach(function (li) {
        li.classList.remove('active')
      })
      $('#optionViews').attr('class', '').addClass(cls)
      el.addClass('active')
    }
    self.event.trigger('tabChanged', [cls])
  }
}

var $ = require('jquery')

module.exports = tabbedMenu

function tabbedMenu (compiler, loadingSpinner, self) {
  $('#options li').click(function (ev) {
    var $el = $(this)
    selectTab($el)
  })

  // initialize tabbed menu
  selectTab($('#options .envView'))

  function cb (finish) {
    compiler.event.register('compilerLoaded', finish)
  }
  // add event listeners for loading spinner
  compiler.event.register('loadingCompiler', function compilationStarted () {
    var contractTab = document.querySelector('.envView')
    if (!contractTab.children.length) {
      contractTab.appendChild(loadingSpinner(cb))
    }
    var settingsTab = document.querySelector('.settingsView')
    if (!settingsTab.children.length) {
      settingsTab.appendChild(loadingSpinner(function cb (finish) {
        compiler.event.register('compilerLoaded', finish)
      }))
    }
  })

  // select tab
  function selectTab (el) {
    var match = /[a-z]+View/.exec(el.get(0).className)
    if (!match) return
    var cls = match[0]
    if (!el.hasClass('active')) {
      el.get(0).parentElement.querySelectorAll('li').forEach(function (li) {
        li.classList.remove('active')
      })
      $('#optionViews').attr('class', '').addClass(cls)
      el.addClass('active')
    }
    self.event.trigger('tabChanged', [cls])
  }
}

Remove doubbled register and move cb outside of register function


import _ from 'lodash';
import React from 'react';

export default function canBeAsserted(vdom) {
    return _.all([].concat(vdom), node =>
        React.isValidElement(node) ||
        (node
        && typeof node === 'object'
        && typeof node.type === 'string'
        && (!node.props || typeof node.props === 'object'))
    );
}

import _ from 'lodash';
import React from 'react';

export default function canBeAsserted(vdom) {
    function isCompatibleObject(node) {
        return node
            && typeof node === 'object'
            && typeof node.type === 'string'
            && (!node.props || typeof node.props === 'object');
    }

    return _.all([].concat(vdom), node =>
        React.isValidElement(node) || isCompatibleObject(node));
}

Refactor logic to a separate function


/*
 * Copyright 2014-2015 Fabian Tollenaar <fabian@starting-point.nl>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0

 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var Transform = require('stream').Transform;

var toDelta = require('n2k-signalk').toDelta;

require('util').inherits(ToSignalK, Transform);

function ToSignalK() {
  Transform.call(this, {
    objectMode: true
  });
}

ToSignalK.prototype._transform = function(chunk, encoding, done) {
  var delta = toDelta(chunk);
  if (delta && delta.updates[0].values.length > 0) {
    this.push(delta);
  }
  done();
}

ToSignalK.prototype.start = function() {}
ToSignalK.prototype.stop = function() {}

module.exports = ToSignalK;

/*
 * Copyright 2014-2015 Fabian Tollenaar <fabian@starting-point.nl>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0

 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var Transform = require('stream').Transform;

var toDelta = require('n2k-signalk').toDelta;

require('util').inherits(ToSignalK, Transform);

function ToSignalK() {
  Transform.call(this, {
    objectMode: true
  });
}

ToSignalK.prototype._transform = function(chunk, encoding, done) {
  var delta = toDelta(chunk);
  if (delta && delta.updates[0].values.length > 0) {
    this.push(delta);
  }
  done();
}

module.exports = ToSignalK;

Remove methods left over from previous pipedprovider mechanism


var program = require('../')
  , should = require('should');

program
  .name('test')
  .command('info [options]')

program.commands[0].helpInformation().should.startWith('\n  Usage: test info [options]')

var program = require('../')
  , should = require('should');

program
  .name('test')
  .command('info [options]')

program.commands[0].helpInformation().should.startWith('Usage: test info [options]')

Fix new test for latest help style


require('./Section.css')

var React = require('react')

var Button = require('./Button')
var MarkdownArea = require('./MarkdownArea')
var IdeasStore = require('../store')

var Section = React.createClass({
  handleBlur(e) {
    var {name, value} = e.target
    if (value !== this.props[name]) {
      IdeasStore.editSection({[name]: value}, this.props.index)
    }
  },
  handleRemove(e) {
    if (window.confirm(`Delete ${this.props.section || '[section]'}?`)) {
      IdeasStore.removeSection(this.props.index)
    }
  },
  render() {
    return

        <Button className="Section__remove" onClick={this.handleRemove} title="Remove section">
          –
        </Button>
        <input autoFocus={this.props.isNew}
               className="Section__name"
               value={this.props.section}
               name="section"
               onBlur={this.handleBlur}
               placeholder="[section]"
               spellCheck="false"
               type="text"/>

      <MarkdownArea name="ideas"
                    value={this.props.ideas}
                    onBlur={this.handleBlur}
                    placeholder="[ideas]"/>

  }
})

module.exports = Section

require('./Section.css')

var React = require('react')

var Button = require('./Button')
var MarkdownArea = require('./MarkdownArea')
var IdeasStore = require('../store')

var Section = React.createClass({
  handleBlur(e) {
    var {name, value} = e.target
    if (value !== this.props[name]) {
      IdeasStore.editSection({[name]: value}, this.props.index)
    }
  },
  handleRemove(e) {
    if (window.confirm(`Delete ${this.props.section || '[section]'}?`)) {
      IdeasStore.removeSection(this.props.index)
    }
  },
  render() {
    return

        <Button className="Section__remove" onClick={this.handleRemove} title="Remove section">
          –
        </Button>
        <input autoFocus={this.props.isNew}
               className="Section__name"
               defaultValue={this.props.section}
               name="section"
               onBlur={this.handleBlur}
               placeholder="[section]"
               spellCheck="false"
               type="text"/>

      <MarkdownArea name="ideas"
                    value={this.props.ideas}
                    onBlur={this.handleBlur}
                    placeholder="[ideas]"/>

  }
})

module.exports = Section

Use defaultValue for section name as we're only taking it onBlur


// js/controllers/users-index-controller

(function() {
  "use strict";

  angular.module("NoteWrangler")
    .controller("UsersIndexController", UsersIndexController);

  UsersIndexController.$inject = ["User"];

  function UsersIndexController(User) {
    let vm = this;

    vm.getAvatar = getAvatar;

    getAllUsers();

    function getAllUsers() {
      User.getAllUsers()
      .success( (usersData) => {
        vm.users = usersData;
      });
    }

    function getAvatar(email) {
      return Gravatar.getAvatar(email);
    }
  }

})();

// js/controllers/users-index-controller

(function() {
  "use strict";

  angular.module("NoteWrangler")
    .controller("UsersIndexController", UsersIndexController);

  UsersIndexController.$inject = ["User", "Gravatar"];

  function UsersIndexController(User, Gravatar) {
    let vm = this;

    vm.getAvatar = getAvatar;

    getAllUsers();

    function getAllUsers() {
      User.getAllUsers()
      .success( (usersData) => {
        vm.users = usersData;
      });
    }

    function getAvatar(email) {
      return Gravatar.getAvatar(email);
    }
  }

})();

Add to DI Gravatar provider


"use strict";
module.exports = pool;

/**
 * An allocator as used by {@link util.pool}.
 * @typedef PoolAllocator
 * @type {function}
 * @param {number} size Buffer size
 * @returns {Uint8Array} Buffer
 */

/**
 * A slicer as used by {@link util.pool}.
 * @typedef PoolSlicer
 * @type {function}
 * @param {number} start Start offset
 * @param {number} end End offset
 * @returns {Uint8Array} Buffer slice
 * @this {Uint8Array}
 */

/**
 * A general purpose buffer pool.
 * @memberof util
 * @function
 * @param {PoolAllocator} alloc Allocator
 * @param {PoolSlicer} slice Slicer
 * @param {number} [size=8192] Slab size
 * @returns {PoolAllocator} Pooled allocator
 */
function pool(alloc, slice, size) {
    var SIZE   = size || 8192;
    var MAX    = SIZE >>> 1;
    var slab   = null;
    var offset = SIZE;
    return function pool_alloc(size) {
        if (size > MAX)
            return alloc(size);
        if (offset + size > SIZE) {
            slab = alloc(SIZE);
            offset = 0;
        }
        var buf = slice.call(slab, offset, offset += size);
        if (offset & 7) // align to 32 bit
            offset = (offset | 7) + 1;
        return buf;
    };
}

"use strict";
module.exports = pool;

/**
 * An allocator as used by {@link util.pool}.
 * @typedef PoolAllocator
 * @type {function}
 * @param {number} size Buffer size
 * @returns {Uint8Array} Buffer
 */

/**
 * A slicer as used by {@link util.pool}.
 * @typedef PoolSlicer
 * @type {function}
 * @param {number} start Start offset
 * @param {number} end End offset
 * @returns {Uint8Array} Buffer slice
 * @this {Uint8Array}
 */

/**
 * A general purpose buffer pool.
 * @memberof util
 * @function
 * @param {PoolAllocator} alloc Allocator
 * @param {PoolSlicer} slice Slicer
 * @param {number} [size=8192] Slab size
 * @returns {PoolAllocator} Pooled allocator
 */
function pool(alloc, slice, size) {
    var SIZE   = size || 8192;
    var MAX    = SIZE >>> 1;
    var slab   = null;
    var offset = SIZE;
    return function pool_alloc(size) {
        if (size > MAX || size === 0)
            return alloc(size);
        if (offset + size > SIZE) {
            slab = alloc(SIZE);
            offset = 0;
        }
        var buf = slice.call(slab, offset, offset += size);
        if (offset & 7) // align to 32 bit
            offset = (offset | 7) + 1;
        return buf;
    };
}

Support usage when size = 0


'use strict';

/**
 * @param {import('../models').Playlist} model
 */
function serializePlaylist(model) {
  return {
    _id: model.id || model._id.toString(),
    name: model.name,
    author: model.author,
    createdAt: model.createdAt,
    description: model.description,
    size: model.media.length,
  };
}

/**
 * @param {Pick<import('../models').User,
 *   '_id' | 'username' | 'slug' | 'roles' | 'avatar' |
 *   'createdAt' | 'updatedAt' | 'lastSeenAt'>} model
 */
function serializeUser(model) {
  return {
    _id: model._id.toString(),
    username: model.username,
    slug: model.slug,
    roles: model.roles,
    avatar: model.avatar,
    createdAt: model.createdAt.toString(),
    updatedAt: model.updatedAt.toString(),
    lastSeenAt: model.lastSeenAt.toString(),
  };
}

exports.serializePlaylist = serializePlaylist;
exports.serializeUser = serializeUser;

'use strict';

/**
 * @param {import('../models').Playlist} model
 */
function serializePlaylist(model) {
  return {
    _id: model.id || model._id.toString(),
    name: model.name,
    author: model.author.toString(),
    createdAt: model.createdAt.toISOString(),
    description: model.description,
    size: model.media.length,
  };
}

/**
 * @param {Pick<import('../models').User,
 *   '_id' | 'username' | 'slug' | 'roles' | 'avatar' |
 *   'createdAt' | 'updatedAt' | 'lastSeenAt'>} model
 */
function serializeUser(model) {
  return {
    _id: model._id.toString(),
    username: model.username,
    slug: model.slug,
    roles: model.roles,
    avatar: model.avatar,
    createdAt: model.createdAt.toISOString(),
    updatedAt: model.updatedAt.toISOString(),
    lastSeenAt: model.lastSeenAt.toISOString(),
  };
}

exports.serializePlaylist = serializePlaylist;
exports.serializeUser = serializeUser;

Fix serialization of Dates in user and playlist objects


//= require jquery
//= require jquery.ui.all
//= require jquery_ujs
//= require jquery-fileupload/basic
//= require lightbox
//= require custom_selector
//= require custom_file_input
//= require lazy/admin

//= require jquery
//= require jquery.ui.all
//= require jquery_ujs
//= require jquery-fileupload/basic
//= require lightbox
//= require custom_selector
//= require custom_file_input
//= require tinymce-rails
//= require lazy/admin

Add tinymce js to admin


angular.module('ngWig')
  .config(['ngWigToolbarProvider', function(ngWigToolbarProvider) {
    ngWigToolbarProvider.addCustomButton('clear-styles', 'nw-clear-styles-button');
  }])
  .component('nwClearStylesButton', {
    template: '<button ng-click="clearStyles()" ng-disabled="editMode" class="nw-button clear-styles" title="Clear Styles" ng-disabled="isDisabled">Clear Styles</button>',
    controller: function($scope) {
      $scope.clearStyles = function(){
          var container = angular.element(document.querySelector('#ng-wig-editable'));
          if(container){
              container.text(container[0].textContent);
              container[0].focus();
          }
      }
    }
  });

angular.module('ngWig')
  .config(['ngWigToolbarProvider', function(ngWigToolbarProvider) {
    ngWigToolbarProvider.addCustomButton('clear-styles', 'nw-clear-styles-button');
  }])
  .component('nwClearStylesButton', {
    template: '<button ng-click="$ctrl.clearStyles($event)" ng-disabled="editMode" class="nw-button clear-styles" title="Clear Styles" ng-disabled="isDisabled">Clear Styles</button>',
    controller: function() {
      this.clearStyles = function(e){
          // find the ngWig element that hosts the plugin
          var ngWigElement = e.target.parentElement.parentElement.parentElement.parentElement.parentElement;
          if(ngWigElement){
              var container = angular.element(ngWigElement.querySelector('#ng-wig-editable'));
              container.text(container[0].textContent);
              container[0].focus();
          }
      }
    }
  });

Use this instead of $scope. Use of plugin with multiple editors.


function viewUsers(view) {

  getUsers(
    success = function (data) {
      //console.log(data);
      populateUsersTable(data);
    },
    error = function (jqxhr) {
      handleApiError(jqxhr);
    }
  );

  function populateUsersTable(data) {

    var usersHtml = templates.userList(data);
    var isEditor = false;
    $('.section').html(usersHtml);

    $('.collections-select-table tbody tr').click(function () {
      $('.collections-select-table tbody tr').removeClass('selected');
      $(this).addClass('selected');
      var userId = $(this).attr('data-id');
      viewUserDetails(userId);
    });

    $('.radioBtnDiv').change(function () {
      if($('input:checked').val() === 'publisher') {
        isEditor = true;
        alert(isEditor);
      } else {
        isEditor = false;
        alert(isEditor);
      }
    });

    $('.form-create-user').submit(function (e) {
      e.preventDefault();

      var username = $('#create-user-username').val();
      var email = $('#create-user-email').val();
      var password = $('#create-user-password').val();

      if (username.length < 1) {
        sweetAlert("Please enter the users name.");
        return;
      }

      if (email.length < 1) {
        sweetAlert("Please enter the users name.");
        return;
      }

      if (password.length < 1) {
        sweetAlert("Please enter the users password.");
        return;
      }
      postUser(username, email, password, isEditor);
    });
  }
}

function viewUsers(view) {

  getUsers(
    success = function (data) {
      //console.log(data);
      populateUsersTable(data);
    },
    error = function (jqxhr) {
      handleApiError(jqxhr);
    }
  );

  function populateUsersTable(data) {

    var usersHtml = templates.userList(data);
    var isEditor = false;
    $('.section').html(usersHtml);

    $('.collections-select-table tbody tr').click(function () {
      $('.collections-select-table tbody tr').removeClass('selected');
      $(this).addClass('selected');
      var userId = $(this).attr('data-id');
      viewUserDetails(userId);
    });

    $('.radioBtnDiv').change(function () {
      if($('input:checked').val() === 'publisher') {
        isEditor = true;
      } else {
        isEditor = false;
      }
    });

    $('.form-create-user').submit(function (e) {
      e.preventDefault();

      var username = $('#create-user-username').val();
      var email = $('#create-user-email').val();
      var password = $('#create-user-password').val();

      if (username.length < 1) {
        sweetAlert("Please enter the users name.");
        return;
      }

      if (email.length < 1) {
        sweetAlert("Please enter the users name.");
        return;
      }

      if (password.length < 1) {
        sweetAlert("Please enter the users password.");
        return;
      }
      postUser(username, email, password, isEditor);
      viewUsers();
    });
  }
}

Refresh page after user creation


import Client from '_common/base/client_base';

export const isClientAllowedToVisit = () => !Client.isLoggedIn() || Client.get('is_virtual') ||
    /^CR/.test(Client.get('loginid'));

import Client from '_common/base/client_base';

export const isClientAllowedToVisit = () => !Client.isLoggedIn() || Client.get('is_virtual') ||
    Client.get('landing_company_shortcode') === 'costarica';

Use shortcode instead of loginid


"use blacklist";

/**
 * Bogus plugin. Circularity ftw.
 */
define(function(require, exports, module) {
    "use strict";

    main.consumes = [
        "Plugin", "ui", "myplugin"
    ];
    main.provides = ["myplugin"];
    return main;
    function main(options, imports, register) {
        var Plugin = imports.Plugin;
        var ui = imports.ui;
        // Note: the syntax error below is intentional and used by architect_resolver_test.js
        imports.

        /***** Initialization *****/

        var plugin = new Plugin("Ajax.org", main.consumes);
        var emit = plugin.getEmitter();
        var foo;
        var bar;

        var loaded = false;
        function load() {
            if (loaded) return false;
            loaded = true;

            foo = 1;
            bar = 2;

            // ...
        }
        function myfunc(arg0, arg1) {

        }

        /***** Lifecycle *****/

        plugin.on("load", function(){
            load();
        });

        plugin.on("unload", function(){
            foo = undefined;
        });

        /***** Register and define API *****/

        /**
         *
         **/
        plugin.freezePublicAPI({
            myfunc: myfunc

        });

        //register(null, { "myplugin" : plugin });
        register(null, { "myplugin" : {
            myfunc2: myfunc
        }});
    }
});

Fix another regression in the architect test dummy


import SockJS from 'sockjs-client'
import Stomp from 'webstomp-client'
const wsURL = 'http://localhost:8080/seven-wonders-websocket'

const createConnection = (headers = {}) => new Promise((resolve, reject) => {
    let socket = Stomp.over(new SockJS(wsURL))
    socket.connect(headers, (frame) => {
      return resolve({ frame, socket })
    }, reject)
})

export default createConnection

import SockJS from 'sockjs-client'
import Stomp from 'webstomp-client'
const wsURL = 'http://localhost:8080/seven-wonders-websocket'

const createConnection = (headers = {}) => new Promise((resolve, reject) => {
    let socket = Stomp.over(new SockJS(wsURL), {
      debug: process.env.NODE_ENV !== "production"
    })
    socket.connect(headers, (frame) => {
      return resolve({ frame, socket })
    }, reject)
})

export default createConnection

Disable stomp debug in production


import { routes } from './config-routes.js'
import { connect } from 'react-redux'

export const path = Object.keys(routes).reduce((mapping, route) => {
  mapping[route] = routes[route].pattern
  return mapping
}, {})

export const paramsToProps = Object.keys(routes).reduce((mapping, route) => {
  mapping[routes[route].pattern] = (state, { routeParams }) =>
    routes[route].paramsToProps(state, routeParams)
  return mapping
}, {})

export const uriToLink = Object.keys(routes).reduce((mapping, route) => {
  mapping[route] = routes[route].uriToLink
  return mapping
}, {})

export const connectFromRoute = (...args) => connect(
  (state, ownProps) => {
    const mapRoute = paramsToProps[ownProps.route.path]
    //TODO warning if no mapping is found for the route (because it fails
    //silently but it can hide an error)
    return mapRoute ? mapRoute(state, ownProps) : {}
  })(...args)

import { routes } from './config-routes.js'
import { connect } from 'react-redux'

export const path = Object.keys(routes).reduce((mapping, route) => {
  mapping[route] = routes[route].pattern
  return mapping
}, {})

export const paramsToProps = Object.keys(routes).reduce((mapping, route) => {
  mapping[routes[route].pattern] = (state, { routeParams }) =>
    routes[route].paramsToProps(state, routeParams)
  return mapping
}, {})

export const uriToLink = Object.keys(routes).reduce((mapping, route) => {
  mapping[route] = routes[route].uriToLink
  return mapping
}, {})

export const connectFromRoute = (...args) => connect(
  (state, ownProps) => {
    const mapRoute = paramsToProps[ownProps.route.path]
    if (mapRoute === undefined) throw new ReferenceError(
      `Mapping for route \`${ownProps.route.path}\`is missing`
    )
    return mapRoute ? mapRoute(state, ownProps) : {}
  })(...args)

Throw error where no mapping was found for a route


import { ToggleTool } from '../../kit'

export default class InsertFigureTool extends ToggleTool {
  renderButton ($$) {
    let button = super.renderButton($$)
    let input = $$('input').attr({
      'type': 'file',
      'multiple': 'multiple'
    }).ref('input')
      .on('change', this.onFileSelect)
    return [button, input]
  }

  getClassNames () {
    return 'sc-insert-figure-tool'
  }

  onClick () {
    this.refs.input.click()
  }

  onFileSelect (e) {
    let files = e.currentTarget.files
    this.executeCommand({
      files: Array.prototype.slice.call(files)
    })
  }
}

import { ToggleTool } from '../../kit'

export default class InsertFigureTool extends ToggleTool {
  renderButton ($$) {
    let button = super.renderButton($$)
    let input = $$('input').attr({
      'type': 'file',
      'multiple': 'multiple'
    }).ref('input')
      .on('change', this.onFileSelect)
    return [button, input]
  }

  getClassNames () {
    return 'sc-insert-figure-tool'
  }

  onClick () {
    this.refs.input.val(null)
    this.refs.input.click()
  }

  onFileSelect (e) {
    let files = e.currentTarget.files
    this.executeCommand({
      files: Array.prototype.slice.call(files)
    })
  }
}

Reset the file input before opening the file dialog.


'use strict';

angular.module('ddsCommon').constant('situationsFamiliales', [
    {
        value: 'marie',  // Enum value 1 in OpenFisca
        label: 'Marié·e',
    },
    {
        value: 'pacse',  // Enum value 5 in OpenFisca
        label: 'Pacsé·e',
    },
    {
        value: 'celibataire',  // Enum value 2 in OpenFisca
        label: 'En union libre',
    }
]);

(function() {

    'use strict';

    var situationsFamiliales = [
        {
            value: 'marie',  // Enum value 1 in OpenFisca
            label: 'Marié·e',
        },
        {
            value: 'pacse',  // Enum value 5 in OpenFisca
            label: 'Pacsé·e',
        },
        {
            value: 'celibataire',  // Enum value 2 in OpenFisca
            label: 'En union libre',
        }
    ];

    /* Export either through Angular loader or CommonJS */
    if (typeof global != 'undefined') {  // we're in Node
        module.exports = situationsFamiliales;
    } else {  // we're in the browser
        angular.module('ddsCommon').constant('situationsFamiliales', situationsFamiliales);
    }

})();

Convert constant file for Node usage.


import login from 'plug-login'
import socket from 'plug-socket'
import createDebug from 'debug'

const debug = createDebug('miniplug:connect')

export default function connectPlugin (options = {}) {
  return (mp) => {
      // log in
    const loginOpts = { host: options.host, authToken: true }

    function connect (opts) {
      debug('connecting', opts.email)

      const loginPromise = Promise.resolve(
        opts.email
          ? login.user(opts.email, opts.password, loginOpts)
          : login.guest(loginOpts)
      )

      const ws = socket()

      const connected = loginPromise
        .then((res) => new Promise((resolve, reject) => {
          ws.auth(res.token)
          ws.once('error', reject)

          mp.isConnected = true
          mp.emit('login')

          const me = mp.getMe()
          ws.once('ack', () => {
            resolve({ cookie: res.cookie })
            ws.removeListener('error', reject)

            me.then((user) => mp.emit('connected', user))
          })
        }))
        .catch((err) => {
          mp.emit('error', err)
          throw err
        })

      mp.ws = ws
      mp.connected = connected

      return connected
    }

    mp.connect = connect
  }
}

import login from 'plug-login'
import socket from 'plug-socket'
import createDebug from 'debug'

const debug = createDebug('miniplug:connect')

export default function connectPlugin (options = {}) {
  return (mp) => {
      // log in
    const loginOpts = { host: options.host, authToken: true }

    function connect (opts) {
      debug('connecting', opts.email)

      const loginPromise = Promise.resolve(
        opts.email
          ? login.user(opts.email, opts.password, loginOpts)
          : login.guest(loginOpts)
      )

      const ws = socket()
      ws.setMaxListeners(100)

      const connected = loginPromise
        .then((res) => new Promise((resolve, reject) => {
          ws.auth(res.token)
          ws.once('error', reject)

          mp.isConnected = true
          mp.emit('login')

          const me = mp.getMe()
          ws.once('ack', () => {
            resolve({ cookie: res.cookie })
            ws.removeListener('error', reject)

            me.then((user) => mp.emit('connected', user))
          })
        }))
        .catch((err) => {
          mp.emit('error', err)
          throw err
        })

      mp.ws = ws
      mp.connected = connected

      return connected
    }

    mp.connect = connect
  }
}

Increase max event listeners for the socket, too.


import { RENDERER_SUFFIX } from './constants';
import contentRendererMixin from './mixin';

export default {
  mixins: [contentRendererMixin],
  methods: {
    /**
     * @public
     */
    checkAnswer() {
      if (this.$refs.contentView && this.$refs.contentView.checkAnswer) {
        return this.$refs.contentView.checkAnswer();
        /* eslint-disable no-console */
      } else if (!this.$refs.contentView) {
        console.warn('No content view to check answer of');
      } else if (!this.$refs.contentView.checkAnswer) {
        console.warn('This content renderer has not implemented the checkAnswer method');
        /* eslint-enable */
      }
      this.registerContentActivity();
      return null;
    },
  },
  render: function(createElement) {
    if (this.canRenderContent(this.defaultItemPreset)) {
      const listeners = {
        ...this.$listeners,
      };
      contentRendererMixin.interactionEvents.forEach(event => {
        if (listeners[event]) {
          listeners[event] = [listeners[event], this.registerContentActivity];
        } else {
          listeners[event] = this.registerContentActivity;
        }
      });
      return createElement(this.defaultItemPreset + RENDERER_SUFFIX, {
        props: this.$props,
        attrs: this.$attrs,
        on: listeners,
        ref: 'contentView',
      });
    }
    return createElement('ContentRendererError');
  },
};

import { RENDERER_SUFFIX } from './constants';
import contentRendererMixin from './mixin';

export default {
  mixins: [contentRendererMixin],
  methods: {
    /**
     * @public
     */
    checkAnswer() {
      if (this.$refs.contentView && this.$refs.contentView.checkAnswer) {
        return this.$refs.contentView.checkAnswer();
        /* eslint-disable no-console */
      } else if (!this.$refs.contentView) {
        console.warn('No content view to check answer of');
      } else if (!this.$refs.contentView.checkAnswer) {
        console.warn('This content renderer has not implemented the checkAnswer method');
        /* eslint-enable */
      }
      this.registerContentActivity();
      return null;
    },
  },
  render: function(createElement) {
    if (this.canRenderContent(this.defaultItemPreset)) {
      const listeners = {
        ...this.$listeners,
      };
      contentRendererMixin.interactionEvents.forEach(event => {
        if (listeners[event]) {
          if (Array.isArray(listeners[event])) {
            listeners[event] = [...listeners[event], this.registerContentActivity];
          } else {
            listeners[event] = [listeners[event], this.registerContentActivity];
          }
        } else {
          listeners[event] = this.registerContentActivity;
        }
      });
      return createElement(this.defaultItemPreset + RENDERER_SUFFIX, {
        props: this.$props,
        attrs: this.$attrs,
        on: listeners,
        ref: 'contentView',
      });
    }
    return createElement('ContentRendererError');
  },
};

Handle if the listeners value is already an array.


var express = require('express');
var childProcess = require('child_process');
var middleware = require('./config/initializers/middleware.js');
var router = require('./config/routes.js');
var host = process.env.HOST;
var port = process.env.PORT || 4000;
var app = express();

middleware.configure(app);
router.route(app);

host ? app.listen(port, host) : app.listen(port);
childProcess.exec("node listener.js");
childProcess.exec("node ripple-simple/app.js"); // http://0.0.0.0:5990

console.log('Serving HTTP on', (host || 'localhost')+":"+port);

var express = require('express');
var childProcess = require('child_process');
var middleware = require('./config/initializers/middleware.js');
var router = require('./config/routes.js');
var host = process.env.HOST;
var port = process.env.PORT || 4000;
var app = express();

middleware.configure(app);
router.route(app);

host ? app.listen(port, host) : app.listen(port);
childProcess.exec("node ripple-simple/app.js"); // http://0.0.0.0:5990
childProcess.exec("node workers/outgoing_ripple_payments.js");
childProcess.exec("node listener.js");

console.log('Serving HTTP on', (host || 'localhost')+":"+port);

Add outgoing payment worker as child process.


// commonjs code goes here
import i18next from 'i18next';
import localstorage from './localstorage';

window.i18n = i18next;

i18next.init({
    lng: 'lang',
    resources: {
        lang: {
            translation: page_params.translation_data,
        },
    },
    nsSeparator: false,
    keySeparator: false,
    interpolation: {
        prefix: "__",
        suffix: "__",
    },
    returnEmptyString: false,  // Empty string is not a valid translation.
});

// garbage collect all old-style i18n translation maps in localStorage.
$(function () {
    if (!localstorage.supported()) {
        return;
    }

    // this collects all localStorage keys that match the format of:
    //   i18next:dddddddddd:w+ => 1484902202:en
    // these are all language translation strings.
    const translations = Object.keys(localStorage).filter(function (key) {
        return /^i18next:\d{10}:\w+$/.test(key);
    });

    // remove cached translations of older versions.
    translations.forEach(function (translation_key) {
        localStorage.removeItem(translation_key);
    });
    return this;
});

import i18next from 'i18next';

i18next.init({
    lng: 'lang',
    resources: {
        lang: {
            translation: page_params.translation_data,
        },
    },
    nsSeparator: false,
    keySeparator: false,
    interpolation: {
        prefix: "__",
        suffix: "__",
    },
    returnEmptyString: false,  // Empty string is not a valid translation.
});

window.i18n = i18next;

Remove code to sweep local storage.


var async = require("async");
var mongoose = require("mongoose");
require("ukiyoe-models")(mongoose);

var Artist = mongoose.model("Artist");

mongoose.connect('mongodb://localhost/extract');

mongoose.connection.on('error', function(err) {
    console.error('Connection Error:', err)
});

mongoose.connection.once('open', function() {
    process.stdin.on("data", function(data) {
        var name = data.toString().trim();
        Artist.searchByName(name, function(err, results) {
            if (err) {
                console.error(err);
                return;
            }

            console.log(results.match && results.match.name.name,
                results.matches.map(function(match) {
                    return match.artist.name.name + " " + match.score;
                })
            );
        });
    });
});

var async = require("async");
var mongoose = require("mongoose");
require("ukiyoe-models")(mongoose);

var Artist = mongoose.model("Artist");

mongoose.connect('mongodb://localhost/extract');

mongoose.connection.on('error', function(err) {
    console.error('Connection Error:', err)
});

mongoose.connection.once('open', function() {
    process.stdin.on("data", function(data) {
        var name = data.toString().trim();
        Artist.searchByName(name, function(err, results) {
            if (err) {
                console.error(err);
                return;
            }

            console.log(results.match && results.match.name.name,
                results.matches.map(function(match) {
                    return match.text + " " + match.score;
                })
            );
        });
    });
});

Fix display of artist names in search tool.


import React, { Component, PropTypes } from "react";
import Helmet from "react-helmet";
import { BodyAttributes } from "gluestick-shared";
import "assets/css/normalize.css";

/**
 * The index html will be generated from this file. You can customize things as
 * you see fit. `body` and `head` will be generated by the server and you
 * should not remove those or the application will likely stop working.
 */
export default class Index extends Component {
  static propTypes = {
    head: PropTypes.any,
    body: PropTypes.any
  };

  render () {
    const {
      head,
      body
    } = this.props;

    const helmet = Helmet.rewind();
    const bodyAttributes = BodyAttributes.rewind();

    return (
      <html lang="en-us">
        <head>
          {helmet.base.toComponent()}
          {helmet.title.toComponent()}
          {helmet.meta.toComponent()}
          {helmet.link.toComponent()}
          {helmet.script.toComponent()}
          {head /* DO NOT REMOVE */}
        </head>
        <body {...bodyAttributes}>
          {body /* DO NOT REMOVE */}
        </body>
      </html>
    );
  }
}

import React, { Component, PropTypes } from "react";
import Helmet from "react-helmet";
import { BodyAttributes } from "gluestick-shared";
import "assets/css/normalize.css";

/**
 * The index html will be generated from this file. You can customize things as
 * you see fit. `body` and `head` will be generated by the server and you
 * should not remove those or the application will likely stop working.
 */
export default class Index extends Component {
  static propTypes = {
    head: PropTypes.any,
    body: PropTypes.any
  };

  render () {
    const {
      head,
      body
    } = this.props;

    const helmet = Helmet.rewind();
    const bodyAttributes = BodyAttributes.rewind();

    return (
      <html lang="en-us">
        <head>
          {helmet.base.toComponent()}
          {helmet.title.toComponent()}
          {helmet.meta.toComponent()}
          {helmet.link.toComponent()}
          {helmet.script.toComponent()}
          {helmet.style.toComponent()}
          {head /* DO NOT REMOVE */}
        </head>
        <body {...bodyAttributes}>
          {body /* DO NOT REMOVE */}
        </body>
      </html>
    );
  }
}

Make sure react-helmet styles are rendered


import React from "react";
import { shallow } from "enzyme";
import assert from "assert";
import { App } from "../../src/components/App";

// unit tests for the App component
describe('App component', () => {
  describe('render()', () => {
    it('should render the component', () => {
      const props = {dispatch: ()=>{}, users: []};
      const wrapper = shallow(<App {...props}/>);
      assert.equal(wrapper.length, 1);
    });
  });
});

import React from "react";
import { shallow } from "enzyme";
import assert from "assert";
import App from "../../src/components";

// unit tests for the App component
describe('App component', () => {
  describe('render()', () => {
    it('should render the component', () => {
      const wrapper = shallow(<App />);
      assert.equal(wrapper.length, 1);
    });
  });
});

Fix import module in test component


import { prettyPrintXML, platform } from 'substance'

export default class ArticleSerializer {
  export (doc, config) {
    let articleConfig = config.getConfiguration('article')

    // TODO: depending on the article type we should use a specifc exporter
    let exporter = articleConfig.createExporter('jats')
    let res = exporter.export(doc)
    let jats = res.jats

    // TODO: and depending on the article type we should use a specific transformation
    let transformation = articleConfig.getTransformation('jats')
    transformation.export(jats)

    // for the purpose of debugging
    if (platform.inBrowser) {
      console.info('saving jats', jats.getNativeElement())
    }

    return prettyPrintXML(jats)
  }
}

import { prettyPrintXML, platform } from 'substance'
import { DEFAULT_JATS_SCHEMA_ID } from './ArticleConstants'

export default class ArticleSerializer {
  export (doc, config) {
    let articleConfig = config.getConfiguration('article')

    // EXPERIMENTAL: I am not sure yet, if this is the right way to use
    // exportes and transformers.
    // During import we store the original docType on the document instance
    // Now we use this to create the corresponding exporter
    let docType = doc.docType || DEFAULT_JATS_SCHEMA_ID

    let exporter = articleConfig.createExporter(docType, doc)
    if (!exporter) {
      console.error(`No exporter registered for "${docType}". Falling back to default JATS importer, but with unpredictable result.`)
      // Falling back to default importer
      exporter = articleConfig.createExporter('jats', doc)
    }
    let res = exporter.export(doc)
    let jats = res.jats

    let transformation = articleConfig.getTransformation(docType)
    if (transformation) {
      transformation.export(jats)
    }

    let xmlStr = prettyPrintXML(jats)
    // for the purpose of debugging
    if (platform.inBrowser) {
      console.info('saving jats', { el: jats.getNativeElement(), xml: xmlStr })
    }

    return xmlStr
  }
}

Use the original docType to create a corresponding exporter.


import { Observable } from 'rxjs/Observable'
import { of } from 'rxjs/observable/of'
import { merge } from 'rxjs/observable/merge'
import { delay } from 'rxjs/operator/delay'
import { takeUntil } from 'rxjs/operator/takeUntil'
import { share } from 'rxjs/operator/share'

const makeVirtualEmission = (scheduler, value, delay) => {
  return new Observable(observer => {
    scheduler.schedule(() => {
      observer.next(value)
      observer.complete()
    }, delay, value)
  })
}

const makeVirtualStream = (scheduler, diagram) => {
  const { emissions, completion } = diagram

  const partials = emissions.map(({ x, d }) => (
    makeVirtualEmission(scheduler, d, x)
  ))

  const completion$ = of(null)::delay(completion, scheduler)

  const emission$ = merge(...partials)
    ::takeUntil(completion$)
    ::share()

  return emission$
}

export default makeVirtualStream

import { Observable } from 'rxjs/Observable'
import { of } from 'rxjs/observable/of'
import { merge } from 'rxjs/observable/merge'
import { delay } from 'rxjs/operator/delay'
import { takeUntil } from 'rxjs/operator/takeUntil'
import { share } from 'rxjs/operator/share'

const makeVirtualEmission = (scheduler, value, delay) => {
  return new Observable(observer => {
    scheduler.schedule(() => {
      observer.next(value)
    }, delay, value)
  })
}

const makeVirtualStream = (scheduler, diagram) => {
  const { emissions, completion } = diagram

  const partials = emissions.map(({ x, d }) => (
    makeVirtualEmission(scheduler, d, x)
  ))

  const completion$ = of(null)::delay(completion, scheduler)

  const emission$ = merge(...partials)
    ::takeUntil(completion$)
    ::share()

  return emission$
}

export default makeVirtualStream

Fix input stream completing early


import { hot } from 'react-hot-loader/root';
import React from 'react';
import { ProtonApp, useAuthentication, useInstance } from 'react-components';
import createSecureSessionStorage from 'proton-shared/lib/createSecureSessionStorage';
import { MAILBOX_PASSWORD_KEY, UID_KEY } from 'proton-shared/lib/constants';
import sentry from 'proton-shared/lib/helpers/sentry';

import * as config from './config';
import PrivateApp from './PrivateApp';
import PublicApp from './PublicApp';

import './app.scss';

sentry(config);

const Setup = () => {
    const { UID, login, logout } = useAuthentication();
    if (UID) {
        return <PrivateApp onLogout={logout} />;
    }
    return <PublicApp onLogin={login} />;
};

const App = () => {
    const storage = useInstance(() => createSecureSessionStorage([MAILBOX_PASSWORD_KEY, UID_KEY]));
    return (
        <ProtonApp config={config} storage={storage}>
            <Setup />
        </ProtonApp>
    );
};

export default hot(App);

import { hot } from 'react-hot-loader/root';
import React from 'react';
import { ProtonApp, useAuthentication } from 'react-components';
import sentry from 'proton-shared/lib/helpers/sentry';

import * as config from './config';
import PrivateApp from './PrivateApp';
import PublicApp from './PublicApp';

import './app.scss';

sentry(config);

const Setup = () => {
    const { UID, login, logout } = useAuthentication();
    if (UID) {
        return <PrivateApp onLogout={logout} />;
    }
    return <PublicApp onLogin={login} />;
};

const App = () => {
    return (
        <ProtonApp config={config}>
            <Setup />
        </ProtonApp>
    );
};

export default hot(App);

Move back session storage creation


(function (Joi, util, logger) {
    'use strict';

    var validFormats = ['lcov'];
    var formatValidation = Joi.string().valid(validFormats).required();

    module.exports = {
        getParser: function getParser(coverageFormat) {
            var validFormat = Joi.validate(coverageFormat, formatValidation);

            if (validFormat.error) {
                logger.error(validFormat.error);
                throw new Error(util.format('Expected one of the following supported formats: %j, but got [%s]', validFormats, coverageFormat));
            }

            logger.debug('Creating coverage parser for: ' + coverageFormat);
            return require('./impl/' + coverageFormat);
        }
    };
}(require('joi'), require('util'), require('./logger')()));

(function (Joi, util, logger) {
    'use strict';

    var validFormats = ['lcov','jacoco'];
    var formatValidation = Joi.string().valid(validFormats).required();

    module.exports = {
        getParser: function getParser(coverageFormat) {
            var validFormat = Joi.validate(coverageFormat, formatValidation);

            if (validFormat.error) {
                logger.error(validFormat.error);
                throw new Error(util.format('Expected one of the following supported formats: %j, but got [%s]', validFormats, coverageFormat));
            }

            logger.debug('Creating coverage parser for: ' + coverageFormat);
            return require('./impl/' + coverageFormat);
        }
    };
}(require('joi'), require('util'), require('./logger')()));

Add Jacoco to list of valid formats


'use strict';

exports.config = {
    jasmineNodeOpts: {
        isVerbose: true,
        showColors: true,
        defaultTimeoutInterval: 30000
    },

    specs: [
        'e2e/example.spec.js'
    ],

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

    baseUrl: 'http://localhost:8080'
};

'use strict';

exports.config = {
    jasmineNodeOpts: {
        isVerbose: true,
        showColors: true,
        defaultTimeoutInterval: 30000
    },

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

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

    baseUrl: 'http://localhost:8080'
};

Add glob for protractor specs


/*
tests validator.js implementation.

steps to run this test:
 1. install mocha
 $ npm install -g mocha

 2. run RedPen in server mode
 $ cd $REDPEN_HOME/bin
 $ ./redpen-server

 3. rename validator.js.example to enable the validator implementation
 $ cd $REDPEN_HOME/sample
 $ mv validator.js.example validator.js

 4. run mocha
 $ cd $REDPEN_HOME/sample
 $ mocha

 */
var assert = require('assert');
var redpen = require('./redpen');

describe('redpen-test', function () {
    it('test validator.js', function (done) {
        var request = {
            "document": "This sentence contains toolongword. This sentence doesn't contain too long word.",
            "format": "json2",
            "documentParser": "PLAIN",
            "config": {
                "lang": "en",
                "validators": {
                    "JavaScript": {}

                }
            }
        };
        var assertion = function (errorSentences) {
            // only one sentence contains error
            assert.equal(errorSentences.length, 1);
            firstErrorSentence = errorSentences[0];
            assert.equal(firstErrorSentence.sentence, 'This sentence contains toolongword.');
            // there is one too word exceeds 10 chalacteres long
            assert.equal(1, firstErrorSentence.errors.length);
            assert.equal('[validator.js] word [toolongword.] is too long. length: 12', firstErrorSentence.errors[0].message);
            done();
        };

        redpen.callRedPen(request, assertion);
    });
});

/*
tests validator.js implementation.

steps to run this test:
 1. install mocha
 $ npm install -g mocha

 2. run RedPen in server mode
 $ cd $REDPEN_HOME/bin
 $ ./redpen-server

 3. rename validator.js.example to enable the validator implementation
 $ cd $REDPEN_HOME/js
 $ mv validator.js.example validator.js

 4. run mocha
 $ cd $REDPEN_HOME/js
 $ mocha

 */
var assert = require('assert');
var redpen = require('./redpen');

describe('redpen-test', function () {
    it('test validator.js', function (done) {
        var request = {
            "document": "This sentence contains toolongword. This sentence doesn't contain too long word.",
            "format": "json2",
            "documentParser": "PLAIN",
            "config": {
                "lang": "en",
                "validators": {
                    "JavaScript": {}

                }
            }
        };
        var assertion = function (errorSentences) {
            // only one sentence contains error
            assert.equal(errorSentences.length, 1);
            firstErrorSentence = errorSentences[0];
            assert.equal(firstErrorSentence.sentence, 'This sentence contains toolongword.');
            // there is one too word exceeds 10 chalacteres long
            assert.equal(1, firstErrorSentence.errors.length);
            assert.equal('[validator.js] word [toolongword.] is too long. length: 12', firstErrorSentence.errors[0].message);
            done();
        };

        redpen.callRedPen(request, assertion);
    });
});

Correct directory names in test sample of javascript validator


Spree.ready(function(){
  $('body').popover({selector: '.hint-tooltip', html: true, trigger: 'hover', placement: 'top'});

  $('body').tooltip({selector: '.with-tip'});

  /*
   * Poll tooltips to hide them if they are no longer being hovered.
   *
   * This is necessary to fix tooltips hanging around after their attached
   * element has been removed from the DOM (and will therefore receive no
   * mouseleave event). This may be unnecessary in a future version of
   * bootstrap, which intends to solve this using MutationObserver.
   */
  var removeDesyncedTooltip = function(tooltip) {
    var interval = setInterval(function(){
      if(!$(tooltip.element).is(":hover")) {
        tooltip.hide();
        clearInterval(interval);
      }
    }, 200);
    $(tooltip.element).on('hidden.bs.tooltip', function(){
      clearInterval(interval);
    });
  };

  $('body').on('inserted.bs.tooltip', function(e){
    var $target = $(e.target);
    var tooltip = $target.data('bs.tooltip');
    removeDesyncedTooltip(tooltip);
    var $tooltip = $("#" + $target.attr("aria-describedby"));
    $tooltip.addClass("action-" + $target.data("action"));
  });
});

Spree.ready(function(){
  $('body').popover({selector: '.hint-tooltip', html: true, trigger: 'hover', placement: 'top'});

  $('body').tooltip({selector: '.with-tip'});

  /*
   * Poll tooltips to hide them if they are no longer being hovered.
   *
   * This is necessary to fix tooltips hanging around after their attached
   * element has been removed from the DOM (and will therefore receive no
   * mouseleave event). This may be unnecessary in a future version of
   * bootstrap, which intends to solve this using MutationObserver.
   */
  var removeDesyncedTooltip = function(tooltip) {
    var interval = setInterval(function(){
      if(!$(tooltip.element).is(":hover")) {
        tooltip.dispose();
        clearInterval(interval);
      }
    }, 200);
    $(tooltip.element).on('hidden.bs.tooltip', function(){
      clearInterval(interval);
    });
  };

  $('body').on('inserted.bs.tooltip', function(e){
    var $target = $(e.target);
    var tooltip = $target.data('bs.tooltip');
    removeDesyncedTooltip(tooltip);
    var $tooltip = $("#" + $target.attr("aria-describedby"));
    $tooltip.addClass("action-" + $target.data("action"));
  });
});

Use dispose rather than hide to unbind events.


angular.module('app').factory('LanguageRepository', function(session, $q, $firebase) {

    var ref = new Firebase("https://languagehuntgamehub.firebaseio.com/languages/");
    //var languages = [];
    //ref.on('value', function(snap) { languages = snap.val(); });

    return {

      save: function() {
      },

      getLanguages: function() {
        var deferred = $q.defer();
        ref.once('value', function(data) {
          var languages = data.val();
          deferred.resolve( languages );
        });
        return deferred.promise;
      }

 };

});

angular.module('app').factory('LanguageRepository', function(session, $q, $firebase) {

    var ref = new Firebase("https://languagehuntgamehub.firebaseio.com/");

    return {

      save: function() {
      },

      getLanguages: function() {
        var deferred = $q.defer();
        var languageRef = ref.child('languages');
        languageRef.once('value', function(data) {
          var languages = data.val();
          deferred.resolve( languages );
        });
        return deferred.promise;
      }

 };

});

Refactor ref to point to root


'use strict';

module.exports = {
	module: {
		loaders: [
			{
				test: /\.js$/,
				loader: 'babel',
				query: {
					presets: ['es2015']
				},
				exclude: /node_modules/
			}
		]
	},
	output: {
		library: 'Spin-bike RPM Meter',
		libraryTarget: 'umd'
	}
};

'use strict';

module.exports = {
	module: {
		loaders: [
			{
				test: /\.js$/,
				loader: 'babel',
				query: {
					presets: ['es2015']
				},
				exclude: /node_modules/
			}
		]
	},
	output: {
		library: 'SpinBikeRpmMeter',
		libraryTarget: 'umd'
	}
};

Fix output name of library to work on window obj


/**
 * Base webpack config used across other specific configs
 */

import _ from 'lodash';
import path from 'path';
import { dependencies as externals } from './app/package.json';
import { optionalDependencies as additionalExternals } from './app/package.json';

export default {
  module: {
    rules: [{
      test: /\.jsx?$/,
      use: [{
        loader: 'babel-loader'
      }],
      exclude: /node_modules\/(?!open)/
    }]
  },

  output: {
    path: path.join(__dirname, 'app'),
    filename: 'bundle.js',

    // https://github.com/webpack/webpack/issues/1114
    libraryTarget: 'commonjs2'
  },

  /**
   * Determine the array of extensions that should be used to resolve modules.
   */
  resolve: {
    extensions: ['.js', '.jsx', '.json'],
    mainFields: ['webpack', 'browser', 'web', 'browserify', ['jam', 'main'], 'main'],
    alias: {
      superagent: 'superagent/lib/client.js',
      emitter: 'component-emitter',
      reduce  : 'reduce-component'
    }
  },
  resolveLoader: { },

  plugins: [],

  externals: _.keys(_.merge({}, externals, additionalExternals) || {})
};

/**
 * Base webpack config used across other specific configs
 */

import _ from 'lodash';
import path from 'path';
import { dependencies as externals } from './app/package.json';
import { optionalDependencies as additionalExternals } from './app/package.json';

export default {
  module: {
    rules: [{
      test: /\.jsx?$/,
      use: [{
        loader: 'babel-loader'
      }],
      exclude: /node_modules/
    }]
  },

  output: {
    path: path.join(__dirname, 'app'),
    filename: 'bundle.js',

    // https://github.com/webpack/webpack/issues/1114
    libraryTarget: 'commonjs2'
  },

  /**
   * Determine the array of extensions that should be used to resolve modules.
   */
  resolve: {
    extensions: ['.js', '.jsx', '.json'],
    mainFields: ['webpack', 'browser', 'web', 'browserify', ['jam', 'main'], 'main'],
    alias: {
      superagent: 'superagent/lib/client.js',
      emitter: 'component-emitter',
      reduce  : 'reduce-component'
    }
  },
  resolveLoader: { },

  plugins: [],

  externals: _.keys(_.merge({}, externals, additionalExternals) || {})
};

Revert "don't exclude open dependency from babel-loader, as it contains ES6 spread operator"


$(document).ready(function($) {

    // header dropdowns
    $('.mainItem a, ul.dropdown').hover(function() {
        $(this).parent().toggleClass('hover');
    });

    // sticky footer
    $(window).bind('load resize', function() {
        var h = $(window).height();
        var a = $('#about').outerHeight();
        $('#wrapper').css({ 'min-height' : (h-a) });
        $('#ohnoes').css({'height': (h-a-71) });
    });

    // vertical align class
    $(window).bind('load resize', function() {
        $('.vAlign').each(function() {
            var h = $(this).parent().siblings('.vMaster').outerHeight();
            var w = $(this).parent().width();
            $(this).css({ height : h });
            $(this).css({ width : w });
        });
    });

    // keyboard shortcuts
    $(document.documentElement).keyup(function (event) {
        if (event.keyCode == 71) {
            $('#grid').fadeToggle(100); // G toggles grid
        }
    });

});

$(document).ready(function($) {

    // header dropdowns
    $('.mainItem a, ul.dropdown').hover(function() {
        $(this).parent().toggleClass('hover');
    });

    // sticky footer
    $(window).bind('load resize', function() {
        var h = $(window).height();
        var a = $('#about').outerHeight();
        $('#wrapper').css({ 'min-height' : (h-a) });
        $('#ohnoes').css({'height': (h-a-71) });
    });

    // vertical align class
    $(window).bind('load resize', function() {
        $('.vAlign').each(function() {
            var h = $(this).parent().siblings('.vMaster').outerHeight();
            var w = $(this).parent().width();
            $(this).css({ height : h });
            $(this).css({ width : w });
        });
    });
});

Remove keyboard shortcuts used in design


import buble from "rollup-plugin-buble"

export default {
  entry: "acorn-walk/src/index.js",
  moduleName: "acorn.walk",
  plugins: [
    buble({transforms: {dangerousForOf: true}})
  ],
  sourceMap: true,
  targets: [
    {dest: "acorn-walk/dist/walk.js", format: "umd"},
    {dest: "acorn-walk/dist/walk.es.js", format: "es"}
  ]
}

import buble from "rollup-plugin-buble"

export default {
  entry: "acorn-walk/src/index.js",
  moduleName: "acorn.walk",
  plugins: [
    buble({transforms: {dangerousForOf: true}})
  ],
  sourceMap: true,
  targets: [
    {dest: "acorn-walk/dist/walk.js", format: "umd"},
    {dest: "acorn-walk/dist/walk.mjs", format: "es"}
  ]
}

Fix walk ECMAScript module file name


import React from 'react'
import { connect } from 'react-redux'
import List from './List'
import Filter from './Filter'
import Pager from './Pager'
import Footer from './Footer'
import { loadFilterTransactions } from '../../actions/entities/transactions'

class Transactions extends React.Component {
  componentWillMount() {
    this.props.loadFilterTransactions()
  }

  render() {
    return (
      <div className="container-full-page flat search-page">
        <Filter />
        <List />
        <Pager />
        <Footer />
      </div>
    )
  }
}

export default connect(undefined, { loadFilterTransactions })(Transactions)

import React from 'react'
import { connect } from 'react-redux'
import List from './List'
import Filter from './Filter'
import Pager from './Pager'
import Footer from './Footer'
import { loadAccounts } from '../../actions/entities/accounts'
import { loadFilterTransactions } from '../../actions/entities/transactions'

class Transactions extends React.Component {
  componentWillMount() {
    this.props.loadAccounts()
    this.props.loadFilterTransactions()
  }

  render() {
    return (
      <div className="container-full-page flat search-page">
        <Filter />
        <List />
        <Pager />
        <Footer />
      </div>
    )
  }
}

export default connect(undefined, { loadAccounts, loadFilterTransactions })(
  Transactions
)

Load account on transactions page


'use strict';

var hg = require('../../index.js');
var document = require('global/document');

// Copied from examples/count.js
function App() {
    return hg.state({
        count: hg.value(0),
        _hotVersion: hg.value(0), // This is new - see below
        channels: {
            clicks: incrementCount
        }
    });
}

function incrementCount(state) {
    state.count.set(state.count() + 1);
}

// This render function may be replaced!
var render = require('./render.js');
App.render = function renderApp(state) {
    return render(state);
};

// Need a reference to this below.
var appState = App();

hg.app(document.body, appState, App.render);

// Special sauce for webpack and browserify:
// Detect changes to the rendering code and swap the rendering
// function out without reloading the page.
if (module.hot) {
    module.hot.accept('./render.js', function swapModule() {
        render = require('./render.js');
        forceRerender(appState);
        return true;
    });
}

// Otherwise, using amok, an event is fired when a file changes.
window.addEventListener('patch', function() {
    forceRerender(appState);
});

// Force a re-render by changing the application state.
function forceRerender(appState) {
    appState._hotVersion.set(appState._hotVersion() + 1);
}

'use strict';

var hg = require('../../index.js');
var document = require('global/document');
var window = require('global/window');

// Copied from examples/count.js
function App() {
    return hg.state({
        count: hg.value(0),
        _hotVersion: hg.value(0), // This is new - see below
        channels: {
            clicks: incrementCount
        }
    });
}

function incrementCount(state) {
    state.count.set(state.count() + 1);
}

// This render function may be replaced by webpack and browserify!
var render = require('./render.js');
App.render = function renderApp(state) {
    return render(state);
};

// Need a reference to this below.
var appState = App();

hg.app(document.body, appState, App.render);

// Special sauce for webpack and browserify:
// Detect changes to the rendering code and swap the rendering
// function out without reloading the page.
if (module.hot) {
    module.hot.accept('./render.js', function swapModule() {
        render = require('./render.js');
        forceRerender(appState);
        return true;
    });
}

// Otherwise, if using amok, an event is fired when a file changes.
window.addEventListener('patch', function() {
    forceRerender(appState);
});

// Force a re-render by changing the application state.
function forceRerender(appState) {
    appState._hotVersion.set(appState._hotVersion() + 1);
}

Use require for window object. Better comments.


'use strict';

const Firebase = require('firebase');

const firebase_secrets = require('../common/secrets/scrape-img.js').firebase;

const dbRef = new Firebase(firebase_secrets.url);

function pushAndAddUID(sourceObj) {
  console.log(`attempt to pushAndAddUID`)
  let targetBucket = sourceObj.s3_key.split('/').slice(0,2).join('/');
  // console.log('targetBucket', targetBucket);
  let targetRef = dbRef.child(targetBucket) || dbRef.child('img_ref3');
  const item = sourceObj;
  return new Promise((resolve, reject) => {
    targetRef.push(item)
    .then(tempRef => {
      tempRef.update({ uid: tempRef.key() });
      // console.log('tempRef', tempRef);
      resolve(tempRef); // return updated obj ref
    })
    // .then(() => {
    //   // console.log(`item at ${targetRef.path.u[0]}/${item.uid} updated`);
    // })
    .catch((err) => {
      sendErr(err);
      reject(err);
    });
  });
}

function postImgObjSetToFirebase(allImgData) {
  // console.log('returned final promise result', allImgData);
  return Promise.all(allImgData.map(imgData => pushAndAddUID(imgData, dbRef.child(imgRef))));
}

module.exports = {
  dbRef: dbRef,
  pushAndAddUID: pushAndAddUID,
}

'use strict';

const Firebase = require('firebase');

const firebase_secrets = require('../common/secrets/scrape-img.js').firebase;

const dbRef = new Firebase(firebase_secrets.url);

function pushAndAddUID(sourceObj) {
  console.log(`attempt to pushAndAddUID`)
  let targetBucket = sourceObj.s3_key.split('/').slice(0,2).join('/');
  // console.log('targetBucket', targetBucket);
  let targetRef = dbRef.child(targetBucket) || dbRef.child('img_ref3');
  const item = sourceObj;
  return new Promise((resolve, reject) => {
    targetRef.push(item)
    .then(tempRef => {
      tempRef.update({ uid: tempRef.key() });
      console.log('pushAndAddUID success', typeof tempRef);
      resolve(tempRef); // return updated obj ref
    })
    .catch(err => {
      resolve(null);
    })
  });
}

function postImgObjSetToFirebase(allImgData) {
  // console.log('returned final promise result', allImgData);
  return Promise.all(allImgData.map(imgData => pushAndAddUID(imgData, dbRef.child(imgRef))));
}

module.exports = {
  dbRef: dbRef,
  pushAndAddUID: pushAndAddUID,
}

Clarify log, add error handler


import BaseModel from '../models/base'

export default (app) => {
  const service = {
    create: async () =>
      BaseModel.raw('REFRESH MATERIALIZED VIEW CONCURRENTLY entries_search'),
  }

  app.use('/searchindex', service)

  app
    .service('searchindex')
    .hooks({
      before: {
        all: [],
        find: [],
      },
      after: {
        all: [],
        find: [],
      },
      error: {
        all: [],
        find: [],
      },
    })
    .hooks({
      after: {
        all: [],
      },
    })
}

import BaseModel from '../models/base'

export default (app) => {
  const service = {
    create: async () =>
      BaseModel.knex().raw(
        'REFRESH MATERIALIZED VIEW CONCURRENTLY entries_search'
      ),
  }

  app.use('/searchindex', service)

  app
    .service('searchindex')
    .hooks({
      before: {
        all: [],
        find: [],
      },
      after: {
        all: [],
        find: [],
      },
      error: {
        all: [],
        find: [],
      },
    })
    .hooks({
      after: {
        all: [],
      },
    })
}

Use knex for search index raw query.


import React from 'react'

export default (email, submit) =>

Write out your custom email

    <form onSubmit={submit}>
      <input type='text' className='custom-email-subject' placeholder='Subject' defaultValue={email ? email[0] : ''} />
      <textarea className='custom-email' placeholder='Email body' defaultValue={email ? email[1] : ''} />
      <button type='submit'>Preview Email</button>
    </form>

import React from 'react'

export default (email, submit) =>

Please include the message in the text box as the last line of your email

    <form onSubmit={submit}>
      <input type='text' className='custom-email-subject' placeholder='Subject' defaultValue={email ? email[0] : ''} />
      <textarea className='custom-email' placeholder='Email body' defaultValue={email ? email[1] : do_not_reply} />
      <button type='submit'>Preview Email</button>
    </form>

const do_not_reply =
  'Please do not reply to messenger@friendsch.org which is not a monitored email address. If you have any queries please reply to membership@friendsch.org  .'

Add ‘do not reply’ message to custom email.


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

const sourceDir = path.join(__dirname, "src");
const distDir = path.join(__dirname, "dist");

module.exports = {
    target: "electron",
    context: sourceDir,
    entry: "./index.ts",
    output: {
        filename: "[name].js",
        path: distDir,
        libraryTarget: "commonjs-module"
    },
    resolve: {
        extensions: [".js", ".ts"],
        modules: ["node_modules"],
    },
    externals: [
        (context, request, callback) => {
            // Resolve `delir-core` module from Delir runtime version
            if (/^delir-core/.test(request)) {
                callback(null, `require("${request}")`);
                return;
            }

            callback();
        }
    ],
    module: {
        rules: [
            {
                test: /\.ts$/,
                loader: "awesome-typescript-loader",
                options: {
                    // If you want to type checking, please set 'false' to this option.
                    // But delir-core typings is very broken. TypeScript warns to delir-core's broken typing...
                    transpileOnly: true
                }
            }
        ]
    }
};

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

const sourceDir = path.join(__dirname, "src");
const distDir = path.join(__dirname, "dist");

module.exports = {
    target: "electron",
    context: sourceDir,
    entry: {
        index: "./index.ts",
    },
    output: {
        filename: "[name].js",
        path: distDir,
        libraryTarget: "commonjs-module"
    },
    resolve: {
        extensions: [".js", ".ts"],
        modules: ["node_modules"],
    },
    externals: [
        (context, request, callback) => {
            // Resolve `delir-core` module from Delir runtime version
            if (/^delir-core/.test(request)) {
                callback(null, `require("${request}")`);
                return;
            }

            callback();
        }
    ],
    module: {
        rules: [
            {
                test: /\.ts$/,
                loader: "awesome-typescript-loader",
                options: {
                    // If you want to type checking, please set 'false' to this option.
                    // But delir-core typings is very broken. TypeScript warns to delir-core's broken typing...
                    transpileOnly: true
                }
            }
        ]
    }
};

Fix can't build entry point by incorrect build setting


$(document).ready(function(){
  function floatingLabel() {
    var PARENT_CLASS = '.form-group',
        INPUT_CLASS = '.form-group.floating-labels input, .form-group.floating-labels textarea',
        CHZN_SELECT_CLASS = '.chzn-select';

    $(document).on('focus blur', INPUT_CLASS, function (e) {
      $(this).parents(PARENT_CLASS)
          .toggleClass('focus', e.type === 'focus' ||  e.type === 'focusin')
          .toggleClass('filled', this.value.length > 0)
          .toggleClass('focused', (e.type === 'focus'||  e.type === 'focusin' || this.value.length > 0 || (this.value.length === 0 && typeof this.getAttribute('placeholder') === 'string')));
    });

    $(INPUT_CLASS).trigger('blur');

    $(document).on('liszt:showing_dropdown liszt:hiding_dropdown', CHZN_SELECT_CLASS, function (e) {
      $(this).parents(PARENT_CLASS)
          .toggleClass('focus', e.type === 'liszt:showing_dropdown')
    });
  }

  floatingLabel();
});

$(document).ready(function(){
  function floatingLabel() {
    var PARENT_CLASS = '.form-group',
        INPUT_CLASS = '.form-group.floating-labels input, .form-group.floating-labels textarea',
        CHZN_SELECT_CLASS = '.chzn-select';

    $(document).on('focus blur', INPUT_CLASS, function (e) {
      var valLength = this.value.length;
      var placeholder = this.getAttribute('placeholder');

      $(this).parents(PARENT_CLASS)
          .toggleClass('focus', e.type === 'focus' ||  e.type === 'focusin')
          .toggleClass('filled', valLength > 0)
          .toggleClass('focused', (e.type === 'focus'||
            e.type === 'focusin' ||
            valLength > 0 ||
            (valLength === 0 && typeof placeholder === 'string' && placeholder.length > 0)));
    });

    $(INPUT_CLASS).trigger('blur');

    $(document).on('liszt:showing_dropdown liszt:hiding_dropdown', CHZN_SELECT_CLASS, function (e) {
      $(this).parents(PARENT_CLASS)
          .toggleClass('focus', e.type === 'liszt:showing_dropdown')
    });
  }

  floatingLabel();
});

Add focused class if placeholder length is greather than 0


Template.messages.messages = function(){
  return Messages.find({channel: "fakechannel"},{ sort: {time: 1}});
}

Template.input.events = {
  'keydown input#message' : function (event) {
    if (event.which == 13) { // 13 is the enter key event
      var name = 'Anonymous';
      var message = document.getElementById('message');

      if (message.value != '') {
        Messages.insert({
          name: name,
          message: message.value,
          time: Date.now(),
          channel: "fakechannel",
        });

        document.getElementById('message').value = '';
        message.value = '';
      }
    }
  }
}

Template.messages.messages = function(){
  return Messages.find({},{ sort: {time: 1}});
}

Template.input.events = {
  'keydown input#message' : function (event) {
    if (event.which == 13) { // 13 is the enter key event
      var name = 'Anonymous';
      var message = document.getElementById('message');

      if (message.value != '') {
        Messages.insert({
          name: name,
          message: message.value,
          time: Date.now(),
        });

        document.getElementById('message').value = '';
        message.value = '';
      }
    }
  }
}

Revert "hard coding in fake channel"


Template.registerHelper("layoutHidden", function (type) {
  return (!!Session.get('fullApi')) ^ (type === 'full') ? 'hidden' : '';
});

Template.basicOrFullSelect.events({
  "change .basic-or-full": function (event) {
    // XXX might not work in IE9?
    navigate("#/" + event.target.value + "/");
  }
});

Template.basicOrFullSelect.helpers({
  isBasic: function () {
    return ! Session.get("fullApi");
  },
  isFull: function () {
    return Session.get("fullApi");
  }
});

Template.sidebar.helpers({
  topLink: function () {
    var docsType = Session.get("fullApi") ? "full" : "basic";
    return "#/" + docsType + "/";
  }
});

Template.registerHelper("layoutHidden", function (type) {
  return (!!Session.get('fullApi')) ^ (type === 'full') ? 'hidden' : '';
});

Template.basicOrFullSelect.events({
  "change .basic-or-full": function (event) {
    // XXX might not work in IE9?
    window.location.hash = "#/" + event.target.value + "/";
  }
});

Template.basicOrFullSelect.helpers({
  isBasic: function () {
    return ! Session.get("fullApi");
  },
  isFull: function () {
    return Session.get("fullApi");
  }
});

Template.sidebar.helpers({
  topLink: function () {
    var docsType = Session.get("fullApi") ? "full" : "basic";
    return "#/" + docsType + "/";
  }
});

Make back button work when switching from basic to full via dropdown


import { TASK_TYPE_HIGH_PRIORITY } from "@buttercup/channel-queue";
import log from "../../shared/library/log.js";
import { getDefaultStorageAdapter } from "./BrowserStorageInterface.js";

export function migrateLocalStorageToChromeStorage(queue) {
    log.info("(Migration)", "Beginning data migration for new storage");
    return queue
        .channel("archiveManager")
        .enqueue(() => {
            const browserStorage = getDefaultStorageAdapter();
            const payload = Object.keys(window.localStorage).reduce(
                (current, key) => ({
                    ...current,
                    [key]: window.localStorage[key]
                }),
                {}
            );
            log.info("(Migration)", `Migrating ${Object.keys(payload).length} keys`);
            return new Promise(resolve => {
                browserStorage.set(payload, resolve);
            });
        }, TASK_TYPE_HIGH_PRIORITY)
        .then(() => {
            log.info("(Migration)", "Migration complete");
        });
}

import { TASK_TYPE_HIGH_PRIORITY } from "@buttercup/channel-queue";
import log from "../../shared/library/log.js";
import { getDefaultStorageAdapter } from "./BrowserStorageInterface.js";

export function migrateLocalStorageToChromeStorage(queue) {
    log.info("(Migration)", "Beginning data migration for new storage");
    return queue
        .channel("archiveManager")
        .enqueue(() => {
            const browserStorage = getDefaultStorageAdapter();
            const keys = Object.keys(window.localStorage).filter(key => /^bcup_archivemgr/.test(key));
            const payload = keys.reduce(
                (current, key) => ({
                    ...current,
                    [key]: window.localStorage[key]
                }),
                {}
            );
            log.info("(Migration)", `Migrating ${keys.length} keys`);
            return new Promise(resolve => {
                browserStorage.set(payload, resolve);
            });
        }, TASK_TYPE_HIGH_PRIORITY)
        .then(() => {
            log.info("(Migration)", "Migration complete");
        });
}

Migrate only archive manager keys


import Controller from '@ember/controller';
import RSVP from 'rsvp';
import { run } from '@ember/runloop';

export default Controller.extend({
  asyncContent: null,
  showTooltips: false,

  actions: {
    setAsyncContent() {
      return new RSVP.Promise((resolve) => {
        run.later(() => {
          this.set('asyncContent', 'Some model');
          resolve();
        }, 2000);
      });
    },
  },

  init() {
    this._super(...arguments);

    run.scheduleOnce('afterRender', () => {
      run.later(() => {
        this.set('showLogoTooltip', true);
      }, 1000);
    });
  },

});

import Controller from '@ember/controller';
import RSVP from 'rsvp';
import { later, scheduleOnce } from '@ember/runloop';

export default Controller.extend({
  asyncContent: null,
  showTooltips: false,

  actions: {
    setAsyncContent() {
      return new RSVP.Promise((resolve) => {
        later(() => {
          this.set('asyncContent', 'Some model');
          resolve();
        }, 2000);
      });
    },
  },

  init() {
    this._super(...arguments);

    scheduleOnce('afterRender', () => {
      later(() => {
        this.set('showLogoTooltip', true);
      }, 1000);
    });
  },

});

Use more specific imports in demo site controller


import Ember from 'ember';
import mixinUnderTest from 'sl-ember-components/mixins/sl-component-input-id';
import { module, test } from 'qunit';

module( 'Unit | Mixin | sl component input id' );

test( 'Successfully mixed', function( assert ) {
    const testObject = Ember.Object.extend( mixinUnderTest );
    const subject = testObject.create();

    assert.ok(
        mixinUnderTest.detect( subject )
    );
});

test( 'inputId is set on component', function( assert ) {
    const component = Ember.Component.extend( mixinUnderTest ).create();

    assert.equal(
        component.get( 'inputId' ),
        component.get( 'elementId' ) + '-input'
    );
});

test( 'inputId value is preserved if it is already set', function( assert ) {
    const id = 'preset-id';
    const component = Ember.Component.extend( mixinUnderTest ).create( {
        inputId: id
    });

    assert.equal(
        component.get( 'inputId' ),
        id
    );
});

import Ember from 'ember';
import mixinUnderTest from 'sl-ember-components/mixins/sl-component-input-id';
import { module, test } from 'qunit';

module( 'Unit | Mixin | sl component input id' );

test( 'Successfully mixed', function( assert ) {
    const testObject = Ember.Object.extend( mixinUnderTest );
    const subject = testObject.create();

    assert.ok(
        subject
    );
});

test( 'inputId is set on component', function( assert ) {
    const component = Ember.Component.extend( mixinUnderTest ).create();

    assert.equal(
        component.get( 'inputId' ),
        component.get( 'elementId' ) + '-input'
    );
});

test( 'inputId value is preserved if it is already set', function( assert ) {
    const id = 'preset-id';
    const component = Ember.Component.extend( mixinUnderTest ).create( {
        inputId: id
    });

    assert.equal(
        component.get( 'inputId' ),
        id
    );
});

Use mixin.detect for components not within mixin unit tests.


import progressiveTimeout from '../progressiveTimeout';

describe('progressiveTimeout', () => {
  test('first timeout is 0 seconds, then increase by 2 seconds', async () => {
    const start = Date.now();
    await progressiveTimeout();
    const duration = Date.now() - start;
    expect(duration < 50).toBeTruthy();

    const start2 = Date.now();
    await progressiveTimeout();
    const duration2 = Date.now() - start2;
    expect(duration2 > 2000 && duration2 < 2050).toBeTruthy();
  });
});

import progressiveTimeout from '../progressiveTimeout';

describe('progressiveTimeout', () => {
  test('first timeout is 0 seconds, then increase by 2 seconds', async () => {
    const start = Date.now();
    await progressiveTimeout();
    const duration = Date.now() - start;
    expect(duration < 100).toBeTruthy();

    const start2 = Date.now();
    await progressiveTimeout();
    const duration2 = Date.now() - start2;
    expect(duration2 > 1900).toBeTruthy();
  });
});

Make progressiveTimeout test less flaky