'use strict';

var _ = require('underscore');

module.exports = function extend (Superclass, prototypeFragment) {
    prototypeFragment = prototypeFragment || {};

    var Subclass = function () {
        if (_.isFunction(prototypeFragment.initialize)) {
            prototypeFragment.initialize.apply(this, arguments);
        } else {
            Superclass.apply(this, arguments);
        }
    };
    Subclass.prototype = Object.create(Superclass.prototype);
    _.extend(Subclass.prototype, prototypeFragment);

    return Subclass;
};

'use strict';

var _ = require('underscore');

module.exports = function extend (Superclass, prototypeFragment) {
    prototypeFragment = prototypeFragment || {};

    var Subclass = function () {
        Superclass.apply(this, arguments);
        if (_.isFunction(prototypeFragment.initialize)) {
            prototypeFragment.initialize.apply(this, arguments);
        }
    };
    Subclass.prototype = Object.create(Superclass.prototype);
    _.extend(Subclass.prototype, prototypeFragment);

    return Subclass;
};

Fix the issue by calling constructor and checking for initialize


'use strict';

// internal
var mixin = require('./mincer/common');

module.exports = {
  VERSION:        '0.0.0',

  EngineTemplate: require('./mincer/engine_template'),
  Environment:    require('./mincer/environment'),
  Manifest:       require('./mincer/manifest')
};

mixin(module.exports, require('./mincer/engines'));
mixin(module.exports, require('./mincer/paths'));

'use strict';

// internal
var mixin = require('./mincer/common').mixin;
var prop  = require('./mincer/common').prop;

module.exports = {
  VERSION:        '0.0.0',

  EngineTemplate: require('./mincer/engine_template'),
  Environment:    require('./mincer/environment'),
  Manifest:       require('./mincer/manifest')
};

prop(module.exports, '__engines__', {});

mixin(module.exports, require('./mincer/engines'));
mixin(module.exports, require('./mincer/paths'));

Add some mixins to main lib


import { createStore as _createStore, applyMiddleware } from 'redux';

import fetchMiddleware from './middleware/fetchMiddleware';
import reducer from './modules/reducer';

export default function createStore(client, data) {
  const middlewares = [fetchMiddleware(client)];
  const finalCreateStore = applyMiddleware(...middlewares)(_createStore);
  const store = finalCreateStore(reducer, data);

  /* global module, require */
  if (module.hot) {
    module.hot.accept('./modules/reducer', () => {
      store.replaceReducer(require('./modules/reducer'));
    });
  }

  return store;
}

import { createStore as _createStore, applyMiddleware } from 'redux';
import { browserHistory } from 'react-router';
import { routerMiddleware, } from 'react-router-redux';

import fetchMiddleware from './middleware/fetchMiddleware';
import reducer from './modules/reducer';

export default function createStore(client, data) {
  const middlewares = [
    fetchMiddleware(client),
    routerMiddleware(browserHistory),
  ];
  const finalCreateStore = applyMiddleware(...middlewares)(_createStore);
  const store = finalCreateStore(reducer, data);

  /* global module, require */
  if (module.hot) {
    module.hot.accept('./modules/reducer', () => {
      store.replaceReducer(require('./modules/reducer'));
    });
  }

  return store;
}

Connect router history to redux.


import ExecutorBase from '../../action/Executor';
import types from '../../action/types';

export default class Executor extends ExecutorBase {

    _onAttack(action, world) {

    }

}

import ExecutorBase from '../../action/Executor';

export default class Executor extends ExecutorBase {

}

Remove empty code to prevent jslint errors


// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html

module.exports = function (config) {
  config.set({
    basePath: '',
    frameworks: ['jasmine', '@angular-devkit/build-angular'],
    plugins: [
      require('karma-jasmine'),
      require('karma-chrome-launcher'),
      require('karma-jasmine-html-reporter'),
      require('karma-coverage-istanbul-reporter'),
      require('@angular-devkit/build-angular/plugins/karma')
    ],
    client:{
      clearContext: false, // leave Jasmine Spec Runner output visible in browser
      jasmine: {
        random: false
      }
    },
    coverageIstanbulReporter: {
      reports: [ 'html', 'lcovonly' ],
      fixWebpackSourcePaths: true
    },
    angularCli: {
      environment: 'dev'
    },
    reporters: ['progress', 'kjhtml'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
    singleRun: false
  });
};

// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html

module.exports = function (config) {
  config.set({
    basePath: '',
    frameworks: ['jasmine', '@angular-devkit/build-angular'],
    plugins: [
      require('karma-jasmine'),
      require('karma-chrome-launcher'),
      require('karma-jasmine-html-reporter'),
      require('karma-coverage-istanbul-reporter'),
      require('@angular-devkit/build-angular/plugins/karma')
    ],
    client:{
      clearContext: false, // leave Jasmine Spec Runner output visible in browser
      jasmine: {
        random: false
      }
    },
    coverageIstanbulReporter: {
      reports: [ 'html', 'lcovonly' ],
      fixWebpackSourcePaths: true
    },
    angularCli: {
      environment: 'dev'
    },
    reporters: ['progress', 'kjhtml'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
    singleRun: false,
    browserNoActivityTimeout: 30000
  });
};

Increase browser inactivity timeout for test runs.


/* eslint-env node */

module.exports = function (config) {
  config.set({
    basePath: 'public',
    browsers: ['ChromeHeadlessCustom'],
    files: [
      'styles/index.css',
      'scripts/mithril.min.js',
      'scripts/underscore-min.js',
      'scripts/tinyemitter.min.js',
      'scripts/sw-update-manager.js',
      'scripts/socket.io.slim.js',
      'scripts/clipboard.min.js',
      'scripts/test.js'
    ],
    reporters: ['dots'].concat(process.env.COVERAGE ? ['coverage'] : []),
    frameworks: ['mocha', 'chai-dom', 'sinon-chai'],
    preprocessors: {
      '**/*.js': ['sourcemap'],
      'scripts/test.js': process.env.COVERAGE ? ['coverage'] : []
    },
    coverageReporter: {
      type: 'json',
      dir: '../coverage/',
      subdir: '.',
      file: 'coverage-unmapped.json'
    },
    customLaunchers: {
      ChromeHeadlessCustom: {
        base: 'ChromeHeadless',
        flags: ['--no-sandbox']
      }
    }
  });
};

/* eslint-env node */

module.exports = function (config) {
  config.set({
    basePath: 'public',
    browsers: ['ChromeHeadlessCustom'],
    files: [
      'styles/index.css',
      'scripts/mithril.min.js',
      'scripts/underscore-min.js',
      'scripts/tinyemitter.min.js',
      'scripts/sw-update-manager.js',
      'scripts/socket.io.min.js',
      'scripts/clipboard.min.js',
      'scripts/test.js'
    ],
    reporters: ['dots'].concat(process.env.COVERAGE ? ['coverage'] : []),
    frameworks: ['mocha', 'chai-dom', 'sinon-chai'],
    preprocessors: {
      '**/*.js': ['sourcemap'],
      'scripts/test.js': process.env.COVERAGE ? ['coverage'] : []
    },
    coverageReporter: {
      type: 'json',
      dir: '../coverage/',
      subdir: '.',
      file: 'coverage-unmapped.json'
    },
    customLaunchers: {
      ChromeHeadlessCustom: {
        base: 'ChromeHeadless',
        flags: ['--no-sandbox']
      }
    }
  });
};

Correct SocketIO path for Karma tests


'use strict';

(function () {

  var expect = require('chai').expect;

  var collect = require('../src/collect');

  describe('collect.js', function () {

    it('should exist', function () {
      expect(collect).to.be.ok;
      expect(typeof collect).to.equal('function');
    });

  });

})();

'use strict';

(function () {

  var expect = require('chai').expect;

  var collect = require('../src/collect');
  var Jarg = require('../src/jarg');
  var Command = require('../src/command');

  describe('collect.js', function () {

    it('should exist', function () {
      expect(collect).to.be.ok;
      expect(typeof collect).to.equal('function');
    });

    it('should return a Jarg instance', function () {
      var boundCollect = collect.bind(null, 'node', 'npm', ['install', 'jargs', '--save']);

      var result = boundCollect(
        Command(
          'npm'
        )
      );

      expect(result instanceof Jarg).to.be.true;
    });

  });

})();

Test that collect returns a Jarg instance


goog.provide('Main');
// Core
// Either require 'Blockly.requires', or just the components you use:
goog.require('Blockly');
goog.require('Blockly.FieldDropdown');
goog.require('Blockly.FieldImage');
goog.require('Blockly.FieldNumber');
goog.require('Blockly.FieldTextInput');
goog.require('Blockly.FieldVariable');
goog.require('Blockly.geras.Renderer');
// Blocks
goog.require('Blockly.Constants.Logic');
goog.require('Blockly.Constants.Loops');
goog.require('Blockly.Constants.Math');
goog.require('Blockly.Constants.Text');
goog.require('Blockly.Constants.Lists');
goog.require('Blockly.Constants.Colour');
goog.require('Blockly.Constants.Variables');
goog.require('Blockly.Constants.VariablesDynamic');
goog.require('Blockly.Blocks.procedures');

Main.init = function() {
  Blockly.inject('blocklyDiv', {
    'toolbox': document.getElementById('toolbox')
  });
};
window.addEventListener('load', Main.init);

goog.provide('Main');
// Core
// Either require 'Blockly.requires', or just the components you use:
goog.require('Blockly');
goog.require('Blockly.geras.Renderer');
// Blocks
goog.require('Blockly.Constants.Logic');
goog.require('Blockly.Constants.Loops');
goog.require('Blockly.Constants.Math');
goog.require('Blockly.Constants.Text');
goog.require('Blockly.Constants.Lists');
goog.require('Blockly.Constants.Colour');
goog.require('Blockly.Constants.Variables');
goog.require('Blockly.Constants.VariablesDynamic');
goog.require('Blockly.Blocks.procedures');

Main.init = function() {
  Blockly.inject('blocklyDiv', {
    'toolbox': document.getElementById('toolbox')
  });
};
window.addEventListener('load', Main.init);

Remove now unneeded requires from compile test.


$(document).ready(function(){
  var i=0;
  var $td;
  var state;

  function checkDocumentsStatuses(){
    $.getJSON("/api/documents_states", function(data){
      var $bars = $(".bar");
      for(i=0;i<$bars.length;i++){
        $($bars[i]).css("width", data[i] + "%");
      }
    });
    setTimeout(checkDocumentsStatuses, 15000 );
  }

  $(".link a").popover();
  $(".link a").click(function(event){
    event.preventDefault();
    var $this = $(this);
    $.get($this.attr("href"),
      null,
      function(data){
        $this.parent().siblings(".content").html(data.p);
      },
      'json');
  });

  $("#stillProcessing").alert().css("display", "block");
  setTimeout(checkDocumentsStatuses, 15000 );
  if($(".tablesorter").length !== 0) {
    $(".tablesorter").tablesorter();
  }
  $(".documents tbody tr").click(function() {
    $(this).siblings().removeClass("selected");
    $(this).addClass("selected");
    var url = "/api/" + $(this).data("id") + "/context";
    var template = $("#documentContext").html();
    $("#document").html("").spin();
    $.getJSON(url, null, function(data) {
      $("#document").html(Mustache.render(template, data));
    }).error(function() {
      $("#document").html(Mustache.render($("#documentContextError").html()));
    });
    return false;
  });
});

$(document).ready(function(){
  var i=0;
  var $td;
  var state;

  function checkDocumentsStatuses(){
    $.getJSON("/api/documents_states", function(data){
      var $bars = $(".bar");
      for(i=0;i<$bars.length;i++){
        $($bars[i]).css("width", data[i] + "%");
      }
    });
    setTimeout(checkDocumentsStatuses, 15000 );
  }

  $(".link a").popover();
  $(".link a").click(function(event){
    event.preventDefault();
    var $this = $(this);
    $.get($this.attr("href"),
      null,
      function(data){
        $this.parent().siblings(".content").html(data.p);
      },
      'json');
  });

  $("#stillProcessing").alert().css("display", "block");
  setTimeout(checkDocumentsStatuses, 15000 );
  if($(".tablesorter").length !== 0) {
    $(".tablesorter").tablesorter();
  }
  $(".documents tbody tr").click(function(e) {
    $(this).siblings().removeClass("selected");
    $(this).addClass("selected");
    var url = "/api/" + $(this).data("id") + "/context";
    var template = $("#documentContext").html();
    $("#document").html("").spin();
    $.getJSON(url, null, function(data) {
      $("#document").html(Mustache.render(template, data));
    }).error(function() {
      $("#document").html(Mustache.render($("#documentContextError").html()));
    });
  });
  $(".documents .tools a").click(function(e) {
    e.stopPropagation();
  });
});

Stop event bubbling when clicking on document toolbar


var sigintHooked = false;

module.exports = function sigintHook( fn ) {
	if ( sigintHooked ) {
		return;
	}
	sigintHooked = true;
	// ctrl+c should stop this task and quit grunt gracefully
	// (process.on("SIGINT", fn) doesn't behave correctly on Windows):
	var rl = require( "readline" ).createInterface( {
		input: process.stdin,
		output: process.stdout
	} );
	rl.on( "SIGINT", function() {
		fn();
		rl.close();
	} );
};

var sigintHooked = false;

module.exports = function sigintHook( fn ) {
	if ( sigintHooked ) {
		return;
	}
	sigintHooked = true;
	// ctrl+c should stop this task and quit grunt gracefully
	// (process.on("SIGINT", fn) doesn't behave correctly on Windows):
	var rl = require( "readline" ).createInterface( {
		input: process.stdin,
		output: process.stdout
	} );
	rl.on( "SIGINT", function() {
		fn();
		rl.close();
		sigintHooked = false;
	} );
};

Reset sigint hook when readline closes.


customElements.define('declarative-shadow-dom', class extends HTMLTemplateElement {
    static get observedAttributes() {
        return [];
    }

    constructor(self) {
        // assignment required by polyfill
        self = super(self);
    }
    connectedCallback(){
        this.appendToParentsShadowRoot();
    }
    appendToParentsShadowRoot(){
        const parentElement = this.parentElement;
        if(!parentElement){
            throw 'declarative-shadow-dom needs a perentElement to stamp to';
        }
        if(!parentElement.shadowRoot){
            parentElement.attachShadow({mode: 'open'});
        }
        let fragment = document.importNode(this.content, true);
        this.stampedNodes = Array.prototype.slice.call(fragment.childNodes);
        parentElement.shadowRoot.appendChild(fragment);
        // debugger
        ShadyCSS && ShadyCSS.styleElement(parentElement);
        parentElement.dispatchEvent(new CustomEvent('declarative-shadow-dom-stamped', {detail: {stampedNodes: this.stampedNodes}}));
        this.parentNode.removeChild(this);
    }
}, {
    extends: 'template'
});

customElements.define('declarative-shadow-dom', class extends HTMLTemplateElement {
    static get observedAttributes() {
        return [];
    }

    constructor(self) {
        // assignment required by polyfill
        self = super(self);
    }
    connectedCallback(){
        this.appendToParentsShadowRoot();
    }
    appendToParentsShadowRoot(){
        const parentElement = this.parentElement;
        if(!parentElement){
            throw 'declarative-shadow-dom needs a perentElement to stamp to';
        }
        if(!parentElement.shadowRoot){
            parentElement.attachShadow({mode: 'open'});
        }
        let fragment = document.importNode(this.content, true);
        this.stampedNodes = Array.prototype.slice.call(fragment.childNodes);
        parentElement.shadowRoot.appendChild(fragment);
        // debugger
        typeof ShadyCSS !== 'undefined' && ShadyCSS.styleElement(parentElement);
        parentElement.dispatchEvent(new CustomEvent('declarative-shadow-dom-stamped', {detail: {stampedNodes: this.stampedNodes}}));
        this.parentNode.removeChild(this);
    }
}, {
    extends: 'template'
});

Fix check for ShadyCSS polyfill


import React from 'react'
import PropTypes from 'prop-types'

/* Component with a state */
export default class AddChatMessage extends React.Component {
	constructor(props) {
		super(props)
		this.state = {message: ''}
	}
	onChangeMessage = (e) => {
		this.setState({message: e.target.value})
	}

	onSendCick = () => {
		this.props.onClick({message: this.state.message})
		this.setState({message:''})
	}

	handleKeyPress = (e) => {
		if (e.key === 'Enter'){
			e.preventDefault()
			this.onSendCick()
			return false
		}
	}

	render() {
		return (
			<div>
				<textarea onChange={this.onChangeMessage} onKeyPress={this.handleKeyPress} value={this.state.message}/>
				<button onClick={this.onSendCick}>Send</button>
			</div>
		)
	}
}

AddChatMessage.propTypes = {
	onClick: PropTypes.func
}

import React from 'react'
import PropTypes from 'prop-types'

/* Component with a state */
export default class AddChatMessage extends React.Component {
	constructor(props) {
		super(props)
		this.state = {message: ''}
	}
	onChangeMessage = (e) => {
		this.setState({message: e.target.value})
	}

	onSendCick = () => {
		this.props.onClick({message: this.state.message})
		this.setState({message:''})
		this.textInput.focus()
	}

	handleKeyPress = (e) => {
		if (e.key === 'Enter'){
			e.preventDefault()
			this.onSendCick()
			return false
		}
	}

	render() {
		return (
			<div>
				<textarea ref={(e) => { this.textInput = e }} onChange={this.onChangeMessage} onKeyPress={this.handleKeyPress} value={this.state.message}/>
				<button onClick={this.onSendCick}>Send</button>
			</div>
		)
	}
}

AddChatMessage.propTypes = {
	onClick: PropTypes.func
}

Set focus back to textarea after button was pressed


Add failing beta version test


describe("m.withAttr()", function () {
	"use strict"

	it("calls the handler with the right value/context without callbackThis", function () { // eslint-disable-line
		var spy = sinon.spy()
		var object = {}
		m.withAttr("test", spy).call(object, {currentTarget: {test: "foo"}})
		expect(spy).to.be.calledOn(object).and.calledWith("foo")
	})

	it("calls the handler with the right value/context with callbackThis", function () { // eslint-disable-line
		var spy = sinon.spy()
		var object = {}
		m.withAttr("test", spy, object)({currentTarget: {test: "foo"}})
		expect(spy).to.be.calledOn(object).and.calledWith("foo")
	})
})

describe("m.withAttr()", function () {
	"use strict"

	it("calls the handler with the right value/context without callbackThis", function () { // eslint-disable-line
		var spy = sinon.spy()
		var object = {}
		m.withAttr("test", spy).call(object, {currentTarget: {test: "foo"}})
		expect(spy).to.be.calledOn(object).and.calledWith("foo")
	})

	it("calls the handler with the right value/context with callbackThis", function () { // eslint-disable-line
		var spy = sinon.spy()
		var object = {}
		m.withAttr("test", spy, object)({currentTarget: {test: "foo"}})
		expect(spy).to.be.calledOn(object).and.calledWith("Ofoo")
	})
})

Break a test of the new suite


var path = require('path'),
    assert = require('assert'),
    fs = require('fs');

var carto = require('../lib/carto');
var tree = require('../lib/carto').tree;
var helper = require('./support/helper');

function cleanupItem(key, value) {
    if (key === 'rules') return;
    else if (key === 'ruleIndex') return;
    else if (key === 'elements') return value.map(function(item) { return item.value; });
    else if (key === 'filters') {
        var arr = [];
        for (var id in value.filters) arr.push(id + value.filters[id].val);
        if (arr.length) return arr;
    }
    else if (key === 'attachment' && value === '__default__') return;
    else if (key === 'zoom') {
        if (value != tree.Zoom.all) return tree.Zoom.toString(value);
    }
    else return value;
}

describe('Specificity', function() {
helper.files('specificity', 'mss', function(file) {
    it('should handle spec correctly in ' + file, function(done) {
        helper.file(file, function(content) {
            var tree = (new carto.Parser({
                paths: [ path.dirname(file) ],
                filename: file
            })).parse(content);

            var mss = tree.toList({});
            mss = helper.makePlain(mss, cleanupItem);

            helper.compareToFile(mss, file, helper.resultFile(file));
            done();
        });
    });
});
});

var path = require('path'),
    assert = require('assert'),
    fs = require('fs');

var carto = require('../lib/carto');
var tree = require('../lib/carto').tree;
var helper = require('./support/helper');

function cleanupItem(key, value) {
    if (key === 'rules') return;
    else if (key === 'ruleIndex') return;
    else if (key === 'elements') return value.map(function(item) { return item.value; });
    else if (key === 'filters') {
        var arr = [];
        for (var id in value.filters) arr.push(id + value.filters[id].val);
        if (arr.length) return arr;
    }
    else if (key === 'attachment' && value === '__default__') return;
    else if (key === 'zoom') {
        if (value != tree.Zoom.all) return (new tree.Zoom()).setZoom(value).toString();
    }
    else return value;
}

describe('Specificity', function() {
helper.files('specificity', 'mss', function(file) {
    it('should handle spec correctly in ' + file, function(done) {
        helper.file(file, function(content) {
            var tree = (new carto.Parser({
                paths: [ path.dirname(file) ],
                filename: file
            })).parse(content);

            var mss = tree.toList({});
            mss = helper.makePlain(mss, cleanupItem);

            helper.compareToFile(mss, file, helper.resultFile(file));
            done();
        });
    });
});
});

Fix zoom interpretation in helper


import gulp from 'gulp'
import path from 'path'
import BrowserSync from 'browser-sync'
import { compiler, handleWebpackResults } from '../webpack/compiler'

const browserSync = BrowserSync.create()
const args = global.__args
const themeDir = path.resolve(__pkg._themepath)
const themeRelPath = themeDir.replace(process.cwd()+'/', '')

gulp.task('dev', ['build'], ()=> {
  compiler.watch({}, handleWebpackResults(true))
  gulp.watch(`${themeRelPath}/scss/**/*.scss`, ['styles'])
  gulp.watch(`${themeRelPath}/images/**/*`, ['images'])
  gulp.watch(`${themeRelPath}/fonts/**/*`, ['static'])

  if (args.sync) {
    browserSync.init({
      proxy: __pkg._criticalUrl,
      files: [
        `${themeRelPath}/assets/js/*.js`,
        `${themeRelPath}/**/*.php`
      ]
    })
  }
})

import gulp from 'gulp'
import path from 'path'
import BrowserSync from 'browser-sync'
import { compiler, handleWebpackResults } from '../webpack/compiler'

const browserSync = BrowserSync.create()
const args = global.__args
const themeDir = path.resolve(__pkg._themepath)
const themeRelPath = themeDir.replace(process.cwd()+'/', '')

gulp.task('dev', ['build'], ()=> {
  compiler.watch({}, handleWebpackResults(true))
  gulp.watch(`${themeRelPath}/scss/**/*.scss`, ['styles'])
  gulp.watch(`${themeRelPath}/images/**/*`, ['images'])
  gulp.watch(`${themeRelPath}/fonts/**/*`, ['static'])

  if (args.sync) {
    browserSync.init({
      proxy: __pkg._criticalUrl,
      files: [
        `${themeRelPath}/assets/js/*.js`,
        `${themeRelPath}/assets/css/*.css`,
        `${themeRelPath}/**/*.php`
      ]
    })
  }
})

Make sure CSS files are watched


const prompts = require('prompts')
const addHook = require('../lib/add-hook')
const { onCancel } = require('../lib/utils')
const { blue, yellow } = require('kleur')

module.exports = async (argv, globalOpts) => {
  const res = await prompts({
    type: 'confirm',
    name: 'addHook',
    message: `This will modify your app.json. Is that ok?`,
    initial: true
  }, { onCancel })
  if (res.addHook) {
    console.log(blue(`> Inserting hook config into app.json`))
    const msg = await addHook(globalOpts['project-root'])
    if (msg) console.log(yellow(`  ${msg}`))
  }
}

const prompts = require('prompts')
const addHook = require('../lib/add-hook')
const { onCancel } = require('../lib/utils')
const { blue, yellow } = require('kleur')

module.exports = async (argv, globalOpts) => {
  const res = await prompts({
    type: 'confirm',
    name: 'addHook',
    message,
    initial: true
  }, { onCancel })
  if (res.addHook) {
    console.log(blue(`> Inserting hook config into app.json`))
    const msg = await addHook(globalOpts['project-root'])
    if (msg) console.log(yellow(`  ${msg}`))
  }
}

const message = `Do you want to automatically upload source maps to Bugsnag? (this will modify your app.json)`

Add API hook command should say what it's going to do


import { NodeComponent } from 'substance'
import { renderEntity } from '../../entities/entityHelpers'

export default class RefComponent extends NodeComponent {

  render($$) {
    const db = this.context.pubMetaDbSession.getDocument()
    const ref = this.props.node
    let label = _getReferenceLabel(ref)
    let entityHtml = renderEntity(_getEntity(ref, db))

    // TODO: do we want to display something like this
    // if so, use the label provider
    entityHtml = entityHtml || '<i>Not available</i>'

    return $$('div').addClass('sc-ref-component').append(
      $$('div').addClass('se-label').append(label),
      $$('div').addClass('se-text').html(entityHtml)
    )
  }
}

function _getReferenceLabel(ref) {
  if (ref.state && ref.state.label) {
    return ref.state.label
  }
  return '?'
}

function _getEntity(ref, db) {
  if (ref.state && ref.state.entity) {
    return ref.state.entity
  }
  return db.get(ref.id)
}

import { NodeComponent } from 'substance'
import { renderEntity } from '../../entities/entityHelpers'

export default class RefComponent extends NodeComponent {

  render($$) {
    const db = this.context.pubMetaDbSession.getDocument()
    const ref = this.props.node
    let label = _getReferenceLabel(ref)
    let entityHtml = renderEntity(_getEntity(ref, db))

    // TODO: do we want to display something like this
    // if so, use the label provider
    entityHtml = entityHtml || '<i>Not available</i>'

    return $$('div').addClass('sc-ref-component').append(
      $$('div').addClass('se-label').append(label),
      $$('div').addClass('se-text').html(entityHtml)
    ).attr('data-id', ref.id)
  }
}

function _getReferenceLabel(ref) {
  if (ref.state && ref.state.label) {
    return ref.state.label
  }
  return '?'
}

function _getEntity(ref, db) {
  if (ref.state && ref.state.entity) {
    return ref.state.entity
  }
  return db.get(ref.id)
}

Set data-id on ref component.


var words = 'Hello Camp JS'.split(' ');
words.forEach((word) => document.body.innerHTML += '<p>' + word + '</p>');

var words = 'Hello Camp JS'.split(' ');
words.forEach(word => document.body.innerHTML += '<p>' + word + '</p>');

Drop parens for arrow function


#!/usr/bin/env node

var packageJson = require('../package.json');

var program = require('commander');

program
  .version(packageJson.version)
  .description(packageJson.description)
  .command('convert', 'convert Avails between various formats', {
    isDefault: true
  })
  .parse(process.argv);

#!/usr/bin/env node

var packageJson = require('../package.json');

var program = require('commander');

program
  .version(packageJson.version)
  .description(packageJson.description)
  .command('convert', 'convert Avails between various formats')
  .command('merge', 'merge historical Avails into one')
  .parse(process.argv);

Remove default setting for Avails command


const BinaryPjax         = require('../base/binary_pjax');
const Client             = require('../base/client');
const defaultRedirectUrl = require('../base/url').defaultRedirectUrl;
const getLoginToken      = require('../common_functions/common_functions').getLoginToken;
const DeskWidget         = require('../common_functions/attach_dom/desk_widget');
const BinarySocket       = require('../websocket_pages/socket');

const VideoFacility = (() => {
    const onLoad = () => {
        BinarySocket.send({ get_account_status: 1 }).then((response) => {
            if (response.error) {
                $('#error_message').setVisibility(1).text(response.error.message);
            } else {
                const status = response.get_account_status.status;
                if (/authenticated/.test(status)) {
                    BinaryPjax.load(defaultRedirectUrl());
                } else {
                    DeskWidget.showDeskLink('', '#facility_content');
                    if (!Client.isFinancial()) {
                        $('#not_authenticated').setVisibility(1);
                    }
                    $('.msg_authenticate').setVisibility(1);
                    $('#generated_token').text(getLoginToken().slice(-4));
                }
            }
        });
    };

    return {
        onLoad: onLoad,
    };
})();

module.exports = VideoFacility;

const BinaryPjax         = require('../base/binary_pjax');
const Client             = require('../base/client');
const localize           = require('../base/localize').localize;
const defaultRedirectUrl = require('../base/url').defaultRedirectUrl;
const getLoginToken      = require('../common_functions/common_functions').getLoginToken;
const DeskWidget         = require('../common_functions/attach_dom/desk_widget');
const BinarySocket       = require('../websocket_pages/socket');

const VideoFacility = (() => {
    const onLoad = () => {
        if (Client.get('loginid_array').find(obj => obj.id === Client.get('loginid')).non_financial) {
            $('#loading').replaceWith($('<p/>', { class: 'notice-msg center-text', text: localize('Sorry, this feature is not available in your jurisdiction.') }));
            return;
        }

        BinarySocket.send({ get_account_status: 1 }).then((response) => {
            if (response.error) {
                $('#error_message').setVisibility(1).text(response.error.message);
            } else {
                const status = response.get_account_status.status;
                if (/authenticated/.test(status)) {
                    BinaryPjax.load(defaultRedirectUrl());
                } else {
                    DeskWidget.showDeskLink('', '#facility_content');
                    if (!Client.isFinancial()) {
                        $('#not_authenticated').setVisibility(1);
                    }
                    $('.msg_authenticate').setVisibility(1);
                    $('#generated_token').text(getLoginToken().slice(-4));
                }
            }
        });
    };

    return {
        onLoad: onLoad,
    };
})();

module.exports = VideoFacility;

Hide video facility contents from non-financial clients


import md from 'components/md'

const WhenToUseAttrs = () => md`
  ## When to use attrs?

  You can pass in attributes to styled components using \`attrs\`, but
  it is not always sensible to do so.

  The rule of thumb is to use \`attrs\` when you want every instance of a styled
  component to have that prop, and pass props directly when every instance needs a
  different one:

  \`\`\`js
  const PasswordInput = styled.input.attrs({
    // Every <PasswordInput /> should be type="password"
    type: 'password'
  })\`\`

  // This specific one is hidden, so let's set aria-hidden
  <PasswordInput aria-hidden="true" />
  \`\`\`
`

export default WhenToUseAttrs

import md from 'components/md'

const WhenToUseAttrs = () => md`
  ## When to use attrs?

  You can pass in attributes to styled components using [attrs](/docs/basics#attaching-additional-props), but
  it is not always sensible to do so.

  The rule of thumb is to use \`attrs\` when you want every instance of a styled
  component to have that prop, and pass props directly when every instance needs a
  different one:

  \`\`\`js
  const PasswordInput = styled.input.attrs({
    // Every <PasswordInput /> should be type="password"
    type: 'password'
  })\`\`

  // This specific one is hidden, so let's set aria-hidden
  <PasswordInput aria-hidden="true" />
  \`\`\`
`

export default WhenToUseAttrs

Add a link to attrs in the docs


import {
  DEFAULT_LIMIT,
  DEFAULT_OFFSET,
} from './base'

import {
  EventBelongsToManyImage,
  EventBelongsToPlace,
  EventHasManySlots,
} from '../database/associations'

import Event from '../models/event'

export default {
  findAll: (req, res, next) => {
    const {
      limit = DEFAULT_LIMIT,
      offset = DEFAULT_OFFSET,
    } = req.query

    return Event.findAndCountAll({
      include: [
        EventBelongsToManyImage,
        EventHasManySlots, {
          association: EventBelongsToPlace,
          required: true,
          where: {
            isPublic: true,
          },
        },
      ],
      limit,
      offset,
      where: {
        isPublic: true,
      },
      order: [
        [
          EventHasManySlots,
          'from',
          'ASC',
        ],
      ],
    })
      .then(result => {
        res.json({
          data: result.rows,
          limit: parseInt(limit, 10),
          offset: parseInt(offset, 10),
          total: result.count,
        })
      })
      .catch(err => next(err))
  },
}

import {
  DEFAULT_LIMIT,
  DEFAULT_OFFSET,
} from './base'

import {
  EventBelongsToManyImage,
  EventBelongsToPlace,
  EventHasManySlots,
} from '../database/associations'

import Event from '../models/event'

export default {
  findAll: (req, res, next) => {
    const {
      limit = DEFAULT_LIMIT,
      offset = DEFAULT_OFFSET,
    } = req.query

    return Event.findAndCountAll({
      distinct: true,
      include: [
        EventBelongsToManyImage,
        EventHasManySlots, {
          association: EventBelongsToPlace,
          required: true,
          where: {
            isPublic: true,
          },
        },
      ],
      limit,
      offset,
      where: {
        isPublic: true,
      },
      order: [
        [
          EventHasManySlots,
          'from',
          'ASC',
        ],
      ],
    })
      .then(result => {
        res.json({
          data: result.rows,
          limit: parseInt(limit, 10),
          offset: parseInt(offset, 10),
          total: result.count,
        })
      })
      .catch(err => next(err))
  },
}

Enable distinct results for calendar preview


var _ = require('lodash');

module.exports = function (methodName, config) {

	// Ensure the minimum parameters have been passed
	if (!methodName || !_.isString(methodName)) {
		throw new Error('The first parameter passed to `addMethod` should be a string.');
	}
	// If a function is inputted as the `config`, then just return - there's
	// really not much to validate.
	if (_.isFunction(config)) {
		return;
	}
	if (!config || !_.isObject(config)) {
		throw new Error('The `config` object should be an object.');
	}

	// Check to see if the method has already been declared
	if (!_.isUndefined(this[methodName])) {
		throw new Error('Method `'+methodName+'` has already been declared.');
	}

	// Ensure the config parameters have been specified correctly
	if (!config.url) {
		throw new Error('The `url` config parameter should be declared.');
	}
	if (!config.method || !_.isString(config.method)) {
		throw new Error('The `method` config parameter should be declared as string.');
	}
	var method = config.method.toLowerCase();
	var allowedMethods = [ 'get', 'put', 'post', 'delete', 'head', 'patch' ];
	if (allowedMethods.indexOf(method) === -1) {
		throw new Error('The `method` "'+method+'" is not a valid method. Allowed methods are: '+allowedMethods.join(', '));
	}

};

var _ = require('lodash');

module.exports = function (methodName, config) {

	// Ensure the minimum parameters have been passed
	if (!methodName || !_.isString(methodName)) {
		throw new Error('The first parameter passed to `addMethod` should be a string.');
	}
	// If a function is inputted as the `config`, then just return - there's
	// really not much to validate.
	if (_.isFunction(config)) {
		return;
	}
	if (!config || !_.isObject(config)) {
		throw new Error('The `config` object should be an object.');
	}

	// Check to see if the method has already been declared
	if (!_.isUndefined(this[methodName])) {
		throw new Error('Method `'+methodName+'` has already been declared.');
	}

	// Ensure the config parameters have been specified correctly
	if (!config.url && config.url !== '') {
		throw new Error('The `url` config parameter should be declared.');
	}
	if (!config.method || !_.isString(config.method)) {
		throw new Error('The `method` config parameter should be declared as string.');
	}
	var method = config.method.toLowerCase();
	var allowedMethods = [ 'get', 'put', 'post', 'delete', 'head', 'patch' ];
	if (allowedMethods.indexOf(method) === -1) {
		throw new Error('The `method` "'+method+'" is not a valid method. Allowed methods are: '+allowedMethods.join(', '));
	}

};

Allow method URL to be an empty string


'use strict';

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

let teamSchema = new Schema({
    name : {
        type: String,
        required: true,
        unique: true
    },
    email : {
        type: String,
        required: true,
        unique: true
    },
    description : {
        type: String
    },
    logisticsRequirements : {
        type: String
    },
    openForApplications : {
        type: Boolean,
        default: true
    },
    members : {
        leader : {
            type: mongoose.Schema.Types.ObjectId,
            ref: 'User'
        },
        list : [{
            type: mongoose.Schema.Types.ObjectId,
            ref: 'User'
        }]
    },
    cremiRoom : {
        type: String
    },
    data : mongoose.Schema.Types.Mixed
});

require('./team.controller')(teamSchema);

module.exports = mongoose.model('Team', teamSchema);

'use strict';

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

let teamSchema = new Schema({
    name : {
        type: String,
        required: true,
        unique: true
    },
    email : {
        type: String,
        required: true
    },
    description : {
        type: String
    },
    logisticsRequirements : {
        type: String
    },
    openForApplications : {
        type: Boolean,
        default: true
    },
    members : {
        leader : {
            type: mongoose.Schema.Types.ObjectId,
            ref: 'User'
        },
        list : [{
            type: mongoose.Schema.Types.ObjectId,
            ref: 'User'
        }]
    },
    cremiRoom : {
        type: String
    },
    data : mongoose.Schema.Types.Mixed
});

require('./team.controller')(teamSchema);

module.exports = mongoose.model('Team', teamSchema);

Remove unique emails for teams


ListController = RouteController.extend({
  layoutTemplate: 'Layout',

  subscriptions: function () {
    this.subscribe('hosts');
  },

  action: function () {
    this.render('HostList');
  }

});

ListController = RouteController.extend({
  layoutTemplate: 'Layout',

  subscriptions: function () {
    this.subscribe('hosts', {
      sort: {sort: 1}
    });
  },

  action: function () {
    this.render('HostList');
  }

});

Add sort for host list subscription.


(function() {
  "use strict";
  GOVUK.Tracker.load();
  var cookieDomain = (document.domain === 'www.beta.digitalmarketplace.service.gov.uk') ? '.digitalmarketplace.service.gov.uk' : document.domain;
  GOVUK.analytics = new GOVUK.Tracker({
    universalId: 'UA-49258698-3',
    cookieDomain: cookieDomain
  });
  GOVUK.analytics.trackPageview();
})();

(function() {
  "use strict";
  GOVUK.Tracker.load();
  var cookieDomain = (document.domain === 'www.beta.digitalmarketplace.service.gov.uk') ? '.digitalmarketplace.service.gov.uk' : document.domain;
  var property = (document.domain === 'www.digitalmarketplace.service.gov.uk') ? 'UA-49258698-1' : 'UA-49258698-3';
  GOVUK.analytics = new GOVUK.Tracker({
    universalId: property,
    cookieDomain: cookieDomain
  });
  GOVUK.analytics.trackPageview();
})();

Use correct analytics properties for live/other


/**
 * Chip Component Stories.
 *
 * Site Kit by Google, Copyright 2022 Google LLC
 *
 * 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
 *
 *     https://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.
 */

/**
 * Internal dependencies
 */
import Chip from './Chip';

const Template = ( args ) => <Chip { ...args } />;

export const DefaultButton = Template.bind( {} );
DefaultButton.storyName = 'Default Chip';
DefaultButton.args = {
	id: 'default',
	label: 'Default Chip',
};

export default {
	title: 'Components/Chip',
	component: Chip,
};

/**
 * Chip Component Stories.
 *
 * Site Kit by Google, Copyright 2022 Google LLC
 *
 * 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
 *
 *     https://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.
 */

/**
 * Internal dependencies
 */
import Chip from './Chip';

const Template = ( args ) => <Chip { ...args } />;

export const DefaultChip = Template.bind( {} );
DefaultChip.storyName = 'Default Chip';
DefaultChip.args = {
	id: 'default',
	label: 'Default Chip',
};

export const SelectedChip = Template.bind( {} );
SelectedChip.storyName = 'Selected Chip';
SelectedChip.args = {
	id: 'selected',
	label: 'Selected Chip',
	selected: true,
};

export default {
	title: 'Components/Chip',
	component: Chip,
};

Add story for a selected chip.


'use strict';

/**
 * @ngdoc filter
 * @name dockstore.ui.filter:PreviewFilter
 * @function
 * @description
 * # PreviewFilter
 * Filter in the dockstore.ui.
 */
angular.module('dockstore.ui')
  .filter('PreviewFilter', [function () {
    return function (containers, contLimit) {
      if (!contLimit) return containers;
      var sortedByBuildTime = containers.sort(function(a, b) {
      	if (!a.lastBuild) a.lastBuild.lastBuild = Number.MAX_VALUE;
      	if (!b.lastBuild) b.lastBuild = Number.MAX_VALUE;
        return a.lastBuild - b.lastBuild;
      });
      return sortedByBuildTime.slice(0, contLimit - 1);
    };
  }]);

'use strict';

/**
 * @ngdoc filter
 * @name dockstore.ui.filter:PreviewFilter
 * @function
 * @description
 * # PreviewFilter
 * Filter in the dockstore.ui.
 */
angular.module('dockstore.ui')
  .filter('PreviewFilter', [function () {
    return function (containers, contLimit) {
      if (!contLimit) return containers;
      var sortedByBuildTime = containers.sort(function(a, b) {
      	if (!a.lastBuild) a.lastBuild = Number.MAX_VALUE;
      	if (!b.lastBuild) b.lastBuild = Number.MAX_VALUE;
        return a.lastBuild - b.lastBuild;
      });
      return sortedByBuildTime.slice(0, contLimit - 1);
    };
  }]);

Update PreviewFilter for invalid/non-returned timestamps (v3).


// Promisify synchronous function

'use strict';

var callable         = require('es5-ext/lib/Object/valid-callable')
  , deferred         = require('../../deferred')
  , isPromise        = require('../../is-promise')
  , processArguments = require('../_process-arguments')

  , apply = Function.prototype.apply

  , applyFn;

applyFn = function (fn, args, resolve) {
	var value;
	try {
		value = apply.call(fn, this, args);
	} catch (e) {
		value = e;
	}
	resolve(value);
};

module.exports = function (length) {
	var fn, result;
	fn = callable(this);
	if (fn.returnsPromise) {
		return fn;
	}
	if (length != null) {
		length = length >>> 0;
	}
	result = function () {
		var args, def;
		args = processArguments(arguments, length);

		if (isPromise(args)) {
			if (args.failed) {
				return args;
			}
			def = deferred();
			args.end(function (args) {
				apply.call(this, fn, args, def.resolve);
			}.bind(this), def.resolve);
		} else {
			def = deferred();
			applyFn.call(this, fn, args, def.resolve);
		}

		return def.promise;
	};
	result.returnsPromise = true;
	return result;
};

// Promisify synchronous function

'use strict';

var callable         = require('es5-ext/lib/Object/valid-callable')
  , deferred         = require('../../deferred')
  , isPromise        = require('../../is-promise')
  , processArguments = require('../_process-arguments')

  , apply = Function.prototype.apply

  , applyFn;

applyFn = function (fn, args, resolve) {
	var value;
	try {
		value = apply.call(fn, this, args);
	} catch (e) {
		value = e;
	}
	resolve(value);
};

module.exports = function (length) {
	var fn, result;
	fn = callable(this);
	if (fn.returnsPromise) {
		return fn;
	}
	if (length != null) {
		length = length >>> 0;
	}
	result = function () {
		var args, def;
		args = processArguments(arguments, length);

		if (isPromise(args)) {
			if (args.failed) {
				return args;
			}
			def = deferred();
			args.end(function (args) {
				applyFn.call(this, fn, args, def.resolve);
			}.bind(this), def.resolve);
		} else {
			def = deferred();
			applyFn.call(this, fn, args, def.resolve);
		}

		return def.promise;
	};
	result.returnsPromise = true;
	return result;
};

Fix promisifySync case of promise arguments


var ACCEPTANCE_TESTS_ENDPOINT = 'http://paie.sgmap.fr/tests/api/acceptance-tests',
	ACCEPTANCE_TESTS_GUI_URL = 'http://paie.sgmap.fr/tests/';

function createTest() {
	var formattedResults = Object.keys(window.lastResult).map(function(key) {
		return {
			code: key,
			expectedValue: window.lastResult[key]
		}
	});

	var form = document.getElementsByTagName('form')[0];

	var data = {
		expectedResults: formattedResults,
		scenario: form.action + '?' + serialize(form)
	}

	var request = new XMLHttpRequest();

	request.withCredentials = true;

	request.open('POST', ACCEPTANCE_TESTS_ENDPOINT);

	request.onload = function() {
		if (request.status >= 300)
			throw request;

		var data = JSON.parse(request.responseText);

		document.location = [ ACCEPTANCE_TESTS_GUI_URL, data._id, 'edit' ].join('/');
	};

	request.onerror = function() {
		throw request;
	}

	request.setRequestHeader('Content-Type', 'application/json');
	request.send(JSON.stringify(data));
}

var ACCEPTANCE_TESTS_ENDPOINT = 'http://paie.sgmap.fr/tests/api/public/acceptance-tests',
	ACCEPTANCE_TESTS_GUI_URL = 'http://paie.sgmap.fr/tests/';

function createTest() {
	var formattedResults = Object.keys(window.lastResult).map(function(key) {
		return {
			code: key,
			expectedValue: window.lastResult[key]
		}
	});

	var form = document.getElementsByTagName('form')[0];

	var data = {
		expectedResults: formattedResults,
		scenario: form.action + '?' + serialize(form)
	}

	var request = new XMLHttpRequest();

	request.open('POST', ACCEPTANCE_TESTS_ENDPOINT);

	request.onload = function() {
		if (request.status >= 300)
			throw request;

		var data = JSON.parse(request.responseText);

		document.location = [ ACCEPTANCE_TESTS_GUI_URL, data._id, 'edit' ].join('/');
	};

	request.onerror = function() {
		throw request;
	}

	request.setRequestHeader('Content-Type', 'application/json');
	request.send(JSON.stringify(data));
}

Use public API route to add tests


const autoUpdater = require('electron').autoUpdater
const Menu = require('electron').Menu

var state = 'checking'

exports.initialize = function () {
  autoUpdater.on('checking-for-update', function () {
    state = 'checking'
    exports.updateMenu()
  })

  autoUpdater.on('update-available', function () {
    state = 'checking'
    exports.updateMenu()
  })

  autoUpdater.on('update-downloaded', function () {
    state = 'installed'
    exports.updateMenu()
  })

  autoUpdater.on('update-not-available', function () {
    state = 'no-update'
    exports.updateMenu()
  })

  autoUpdater.on('error', function () {
    state = 'no-update'
    exports.updateMenu()
  })

  autoUpdater.setFeedURL('https://electron-api-demos.githubapp.com/updates')
  autoUpdater.checkForUpdates()
}

exports.updateMenu = function () {
  var menu = Menu.getApplicationMenu()
  if (!menu) return

  menu.items.forEach(function (item) {
    if (item.submenu) {
      item.submenu.items.forEach(function (item) {
        switch (item.key) {
          case 'checkForUpdate':
            item.visible = state === 'no-update'
            break
          case 'checkingForUpdate':
            item.visible = state === 'checking'
            break
          case 'restartToUpdate':
            item.visible = state === 'installed'
            break
        }
      })
    }
  })
}

const app = require('electron').app
const autoUpdater = require('electron').autoUpdater
const Menu = require('electron').Menu

var state = 'checking'

exports.initialize = function () {
  autoUpdater.on('checking-for-update', function () {
    state = 'checking'
    exports.updateMenu()
  })

  autoUpdater.on('update-available', function () {
    state = 'checking'
    exports.updateMenu()
  })

  autoUpdater.on('update-downloaded', function () {
    state = 'installed'
    exports.updateMenu()
  })

  autoUpdater.on('update-not-available', function () {
    state = 'no-update'
    exports.updateMenu()
  })

  autoUpdater.on('error', function () {
    state = 'no-update'
    exports.updateMenu()
  })

  autoUpdater.setFeedURL(`https://electron-api-demos.githubapp.com/updates?version=${app.getVersion()}`)
  autoUpdater.checkForUpdates()
}

exports.updateMenu = function () {
  var menu = Menu.getApplicationMenu()
  if (!menu) return

  menu.items.forEach(function (item) {
    if (item.submenu) {
      item.submenu.items.forEach(function (item) {
        switch (item.key) {
          case 'checkForUpdate':
            item.visible = state === 'no-update'
            break
          case 'checkingForUpdate':
            item.visible = state === 'checking'
            break
          case 'restartToUpdate':
            item.visible = state === 'installed'
            break
        }
      })
    }
  })
}

Add version to update url


// Lint and build CSS
module.exports = function (grunt) {
  'use strict';

  grunt.registerTask('default', [
    'clean:build',
    'phantomjs',
    'webpack:dev',
  ]);

  grunt.registerTask('test', [
    'sasslint',
    'tslint',
    'typecheck',
    "exec:jest",
    'no-only-tests'
  ]);

  grunt.registerTask('tslint', [
    'newer:exec:tslintPackages',
    'newer:exec:tslintRoot',
  ]);

  grunt.registerTask('typecheck', [
    'newer:exec:typecheckPackages',
    'newer:exec:typecheckRoot',
  ]);

  grunt.registerTask('precommit', [
    'newer:sasslint',
    'typecheck',
    'tslint',
    'no-only-tests'
  ]);

  grunt.registerTask('no-only-tests', function () {
    var files = grunt.file.expand('public/**/*_specs\.ts', 'public/**/*_specs\.js');

    files.forEach(function (spec) {
      var rows = grunt.file.read(spec).split('\n');
      rows.forEach(function (row) {
        if (row.indexOf('.only(') > 0) {
          grunt.log.errorlns(row);
          grunt.fail.warn('found only statement in test: ' + spec)
        }
      });
    });
  });
};

// Lint and build CSS
module.exports = function (grunt) {
  'use strict';

  grunt.registerTask('default', [
    'clean:build',
    'phantomjs',
    'webpack:dev',
  ]);

  grunt.registerTask('test', [
    'sasslint',
    'tslint',
    'typecheck',
    "exec:jest",
    'no-only-tests'
  ]);

  grunt.registerTask('tslint', [
    'newer:exec:tslintPackages',
    'newer:exec:tslintRoot',
  ]);

  grunt.registerTask('typecheck', [
    'newer:exec:typecheckPackages',
    'newer:exec:typecheckRoot',
  ]);

  grunt.registerTask('precommit', [
    'newer:sasslint',
    'typecheck',
    'tslint',
    'no-only-tests'
  ]);

  grunt.registerTask('no-only-tests', function () {
    var files = grunt.file.expand(
      'public/**/*@(_specs|\.test)\.@(ts|js|tsx|jsx)',
      'packages/grafana-ui/**/*@(_specs|\.test)\.@(ts|js|tsx|jsx)'
    );

    files.forEach(function (spec) {
      var rows = grunt.file.read(spec).split('\n');
      rows.forEach(function (row) {
        if (row.indexOf('.only(') > 0) {
          grunt.log.errorlns(row);
          grunt.fail.warn('found only statement in test: ' + spec);
        }
      });
    });
  });
};

Add more patterns to no-only-test task


import { configure, addDecorator } from '@storybook/react';
import { setDefaults } from '@storybook/addon-info';
import { setOptions } from '@storybook/addon-options';
import backgroundColor from 'react-storybook-decorator-background';

// addon-info
setDefaults({
  header: false,
  inline: true,
  source: true,
  propTablesExclude: [],
});

setOptions({
  name: `Version ${process.env.__VERSION__}`,
  url: 'https://teamleader.design'
});

addDecorator(backgroundColor(['#ffffff', '#e6f2ff', '#ffeecc', '#d3f3f3', '#ffe3d9', '#e1edfa', '#f1f0ff', '#2a3b4d']));

const req = require.context('../stories', true, /\.js$/);

configure(() => {
  req.keys().forEach(filename => req(filename));
}, module);

import { configure, addDecorator } from '@storybook/react';
import { setDefaults } from '@storybook/addon-info';
import { setOptions } from '@storybook/addon-options';
import backgroundColor from 'react-storybook-decorator-background';

// addon-info
setDefaults({
  header: true,
  inline: true,
  source: true,
  propTablesExclude: [],
});

setOptions({
  name: `Version ${process.env.__VERSION__}`,
  url: 'https://teamleader.design'
});

addDecorator(backgroundColor(['#ffffff', '#e6f2ff', '#ffeecc', '#d3f3f3', '#ffe3d9', '#e1edfa', '#f1f0ff', '#2a3b4d']));

const req = require.context('../stories', true, /\.js$/);

configure(() => {
  req.keys().forEach(filename => req(filename));
}, module);

Enable header for every story


const presets = [
  [
    "@babel/env",
    {
      targets: {
        edge: "18",
        firefox: "66",
        chrome: "73",
        safari: "12",
        ie: "9"
      }
    },
  ],
];

module.exports = {presets};

const presets = [
  [
    "@babel/env",
    {
      targets: {
        edge: "18",
        firefox: "66",
        chrome: "73",
        safari: "12",
        ie: "11"
      }
    },
  ],
];

module.exports = {presets};

Update babel IE target version to 11


"use strict";

angular
  .module('app')
  .run([
    '$document',
    '$rootScope',
    function($document, $rootScope) {
      function visibilitychanged() {
        var d = $document[0],
            isHidden = d.hidden || d.webkitHidden || d.mozHidden || d.msHidden;
        $rootScope.$emit('visibility:change', isHidden);
      }

      $document.on('visibilitychange',visibilitychanged);
      $document.on('webkitvisibilitychange', visibilitychanged);
      $document.on('msvisibilitychange', visibilitychanged);
    }
  ]);

"use strict";

angular
  .module('app')
  .run([
    '$document',
    '$rootScope',
    function($document, $rootScope) {
      var last;

      function visibilitychanged() {
        var d = $document[0],
            isHidden = d.hidden || d.webkitHidden || d.mozHidden || d.msHidden;
        if (isHidden !== last) {
          $rootScope.$emit('visibility:change', isHidden);
          last = isHidden;
        }
      }

      $document.on('visibilitychange',visibilitychanged);
      $document.on('webkitvisibilitychange', visibilitychanged);
      $document.on('msvisibilitychange', visibilitychanged);
    }
  ]);

Fix doubled event of visibility change


const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
    entry: {
        app: './app.js'
    },

    output: {
        filename: '[name].bundle.js',
        path: path.resolve(__dirname, 'dist'),
        publicPath: '/'
    },

    context: path.resolve(__dirname, 'src'),

    module: {
        rules: [
            {
                test: /\.jsx?$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: [['env', {modules: false}], 'stage-3'],
                        plugins: ['transform-runtime', 'check-es2015-constants']
                    }
                }
            },
            {
                test: /\.sass$/,
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                    use: ['css-loader', 'sass-loader']
                })
            },
            {
                test: /\.pug$/,
                use: ['html-loader', 'pug-html-loader']
            },
            {
                test: /\.(png|jpg|gif|svg|eot|ttf|woff|woff2)$/,
                loader: 'file-loader'
            }
        ]
    },

    plugins: [
        new HtmlWebpackPlugin({
            template: 'app.pug',
            filename: 'app.html'
        }),
        new ExtractTextPlugin({
            filename: '[name].bundle.css',
        })
    ],

    devServer: {
        publicPath: '/',
        contentBase: path.join(__dirname, 'dist')
    }
}

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
    entry: {
        app: './app.js'
    },

    output: {
        filename: '[name].bundle.js',
        path: path.resolve(__dirname, 'dist'),
        publicPath: '/'
    },

    context: path.resolve(__dirname, 'src'),

    module: {
        rules: [
            {
                test: /\.jsx?$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: [['env', {modules: false}], 'stage-3'],
                        plugins: ['transform-runtime', 'check-es2015-constants']
                    }
                }
            },
            {
                test: /\.sass$/,
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                    use: ['css-loader', 'sass-loader']
                })
            },
            {
                test: /\.pug$/,
                use: ['html-loader', 'pug-html-loader']
            },
            {
                test: /\.(png|jpg|gif|svg|eot|ttf|woff|woff2)$/,
                loader: 'file-loader'
            }
        ]
    },

    plugins: [
        new HtmlWebpackPlugin({
            template: 'app.pug',
            filename: 'app.html'
        }),
        new ExtractTextPlugin({
            filename: '[name].bundle.css',
        })
    ],

    devServer: {
        publicPath: '/',
        contentBase: path.join(__dirname, 'dist')
    },

    devtool: "source-map"
}

Add source maps to get better debugging in the browser


var webpack = require('webpack');
var fs = require('fs');

var config = {
  entry: {
    'app': './app/index.js'
  },
  devtool: 'source-map',
  output: {
    path: __dirname + '/dist',
    filename: `[name].[hash].js`,
    publicPath: __dirname + '/dist'
  },
  module: {
    loaders: [
      {
        test: /(\.js)$/,
        loader: 'babel',
        exclude: /(node_modules)/,
        query: {
          presets: ['es2015', 'react']
        }
      }
    ]
  },
  plugins: [
    function() {
       this.plugin("done", function(stats) {
         const hash = stats.toJson().hash;
         fs.readFile('./index.html', 'utf8', function (err,data) {
           if (err) {
             return console.log('ERR', err);
           }
           var result = data.replace(/dist\/app.*.js/g, `dist/app.${hash}.js`);
           fs.writeFile('./index.html', result, 'utf8', function (err) {
              if (err) return console.log('ERR', err);
           });
         });
       });
    }
  ]
};

module.exports = config;

var webpack = require('webpack');
var fs = require('fs');

var config = {
  entry: {
    'app': './app/index.js'
  },
  devtool: 'source-map',
  output: {
    path: __dirname + '/dist',
    filename: `[name].[hash].js`,
    publicPath: __dirname + '/dist'
  },
  module: {
    loaders: [
      {
        test: /(\.js)$/,
        loader: 'babel',
        exclude: /(node_modules)/,
        query: {
          presets: ['es2015', 'react']
        }
      }
    ]
  },
  plugins: [
    function() {
       this.plugin("compile", function() {
         require( 'child_process' ).exec('rm -rf ./dist');
       });
       this.plugin("done", function(stats) {
         const hash = stats.toJson().hash;
         fs.readFile('./index.html', 'utf8', function (err,data) {
           if (err) {
             return console.log('ERR', err);
           }
           var result = data.replace(/dist\/app.*.js/g, `dist/app.${hash}.js`);
           fs.writeFile('./index.html', result, 'utf8', function (err) {
              if (err) return console.log('ERR', err);
           });
         });
       });
    }
  ]
};

module.exports = config;

Clear dist on every build


module.exports = ServerFactory => class HttpsServerFactory extends ServerFactory {
  create (options) {
    const fs = require('fs')
    const https = require('https')
    const t = require('typical')
    const serverOptions = {}
    if (options.pfx) {
      serverOptions.pfx = fs.readFileSync(options.pfx)
    } else {
      if (!(options.key && options.cert)) {
        serverOptions.key = this.getDefaultKeyPath()
        serverOptions.cert = this.getDefaultCertPath()
      }
      serverOptions.key = fs.readFileSync(serverOptions.key, 'utf8')
      serverOptions.cert = fs.readFileSync(serverOptions.cert, 'utf8')
    }
    if (t.isDefined(options.maxConnections)) serverOptions.maxConnections = options.maxConnections
    if (t.isDefined(options.keepAliveTimeout)) serverOptions.keepAliveTimeout = options.keepAliveTimeout
    this.emit('verbose', 'server.config', serverOptions)
    return https.createServer(serverOptions)
  }
}

module.exports = ServerFactory => class HttpsServerFactory extends ServerFactory {
  create (options) {
    const fs = require('fs')
    const https = require('https')
    const t = require('typical')
    const serverOptions = {}
    if (options.pfx) {
      serverOptions.pfx = fs.readFileSync(options.pfx)
    } else {
      if (!(options.key && options.cert)) {
        serverOptions.key = this.getDefaultKeyPath()
        serverOptions.cert = this.getDefaultCertPath()
      } else {
        serverOptions.key = fs.readFileSync(options.key, 'utf8')
        serverOptions.cert = fs.readFileSync(options.cert, 'utf8')
      }
    }
    if (t.isDefined(options.maxConnections)) serverOptions.maxConnections = options.maxConnections
    if (t.isDefined(options.keepAliveTimeout)) serverOptions.keepAliveTimeout = options.keepAliveTimeout
    this.emit('verbose', 'server.config', serverOptions)
    return https.createServer(serverOptions)
  }
}

Fix key and cert file params when using personal version


const path = require('path');

const webpack = require('webpack');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const Dotenv = require('dotenv-webpack');

module.exports = {
  entry: {
    'facebook-messenger/handler': './src/facebook-messenger/handler.js',
  },
  target: 'node',
  externals: [
    'aws-sdk'
  ],
  module: {
    loaders: [
      {
        test: /\.js$/,
        exclude: /(node_modules)/,
        loader: 'babel-loader',
        query: {
          presets: ['es2015']
        }
      },
      { test: /\.json/, loader: 'json-loader' }
    ]
  },
  plugins: [
    new CopyWebpackPlugin([
      { from: '.env' }
    ]),
    new Dotenv({
      safe: true
    }),
    new webpack.optimize.DedupePlugin(),
    new webpack.optimize.OccurenceOrderPlugin(),
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        unused: true,
        dead_code: true,
        warnings: false,
        drop_debugger: true
      }
    })
  ],
  output: {
    libraryTarget: 'commonjs',
    path: path.join(__dirname, '.webpack'),
    filename: '[name].js'
  },
  devtool: "cheap-module-source-map"
};

const path = require('path');

const webpack = require('webpack');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const Dotenv = require('dotenv-webpack');

module.exports = {
  entry: {
    'facebook-messenger/handler': './src/facebook-messenger/handler.js',
  },
  target: 'node',
  externals: [
    'aws-sdk'
  ],
  module: {
    loaders: [
      {
        test: /\.js$/,
        exclude: /(node_modules)/,
        loader: 'babel-loader',
        query: {
          presets: ['es2015']
        }
      },
      { test: /\.json/, loader: 'json-loader' }
    ]
  },
  plugins: [
    new CopyWebpackPlugin([
      { from: '.env' }
    ]),
    new Dotenv({
      safe: true
    }),
    new webpack.optimize.DedupePlugin(),
    new webpack.optimize.OccurenceOrderPlugin(),
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        unused: true,
        dead_code: true,
        warnings: false,
        drop_debugger: true
      }
    })
  ],
  output: {
    libraryTarget: 'commonjs',
    path: path.join(__dirname, '.webpack'),
    filename: '[name].js'
  },
  devtool: "source-map"
};

Use full source map in production


import { message, danger } from "danger"
message(":tada:, this worked @" + danger.github.pr.user.login)

import { message, danger } from "danger";
import prettier from 'prettier';

const srcDir = `${__dirname}/src/**/*.js`;
const options = {
  singleQuote: true,
  printWidth: 100
};

if (prettier.check(srcDir, options)) {
  message(':tada: Your code is formatted correctly');
} else {
  warn('You haven\'t formated the code using prettier. Please run `npm run format` before merging the PR');
}

Check if src dir has been formatted


// this Proxy handler will be used to preserve the unaltered behavior of the window global by default
const doNothingHandler = {
  get(target, prop) {
    const value = Reflect.get(target, prop);

    // make sure the function receives the original window as the this context! (e.g. alert will throw an invalid invocation error)
    if (typeof window[prop] === 'function') {
      return new Proxy(value, {
        apply(t, _thisArg, argumentsList) {
          return Reflect.apply(value, target, argumentsList);
        }
      });
    }

    return value;
  },
  set: Reflect.set,
  has: Reflect.has,
  deleteProperty: Reflect.deleteProperty
}

let currentHandler = doNothingHandler;

// private function to replace the default handler in tests
export function _setCurrentHandler(handler = doNothingHandler) {
  currentHandler = handler;
}

const proxyHandler = {
  get() {
    return currentHandler.get(...arguments);
  },
  set() {
    return currentHandler.set(...arguments);
  },
  has() {
    return currentHandler.has(...arguments);
  },
  deleteProperty() {
    return currentHandler.deleteProperty(...arguments);
  },
}

export default new Proxy(window, proxyHandler);

import { DEBUG } from '@glimmer/env';

let exportedWindow;
let _setCurrentHandler;

if (DEBUG) {
  // this Proxy handler will be used to preserve the unaltered behavior of the window global by default
  const doNothingHandler = {
    get(target, prop) {
      const value = Reflect.get(target, prop);

      // make sure the function receives the original window as the this context! (e.g. alert will throw an invalid invocation error)
      if (typeof value === 'function') {
        return new Proxy(value, {
          apply(t, _thisArg, argumentsList) {
            return Reflect.apply(value, target, argumentsList);
          }
        });
      }

      return value;
    },
    set: Reflect.set,
    has: Reflect.has,
    deleteProperty: Reflect.deleteProperty
  }

  let currentHandler = doNothingHandler;

  // private function to replace the default handler in tests
  _setCurrentHandler = (handler = doNothingHandler) => currentHandler = handler;

  const proxyHandler = {
    get() {
      return currentHandler.get(...arguments);
    },
    set() {
      return currentHandler.set(...arguments);
    },
    has() {
      return currentHandler.has(...arguments);
    },
    deleteProperty() {
      return currentHandler.deleteProperty(...arguments);
    },
  }

  exportedWindow = new Proxy(window, proxyHandler);
} else {
  exportedWindow = window;
}

export default exportedWindow;
export { _setCurrentHandler };

Exclude Proxy code from production build


#!/usr/bin/env node
'use strict';

var program = require('commander'),
    meta = require('../package.json'),
    lint = require('../index');

var detects;

program
  .version(meta.version)
  .usage('[options] \'<file or glob>\'')
  .option('-q, --no-exit', 'do not exit on errors')
  .parse(process.argv);

detects = lint.lintFiles(program.args[0]);
detects = lint.format(detects);

lint.outputResults(detects);

if (program.exit) {
  lint.failOnError(detects);
}

#!/usr/bin/env node
'use strict';

var program = require('commander'),
    meta = require('../package.json'),
    lint = require('../index');

var detects,
    formatted;

program
  .version(meta.version)
  .usage('[options] \'<file or glob>\'')
  .option('-q, --no-exit', 'do not exit on errors')
  .parse(process.argv);

detects = lint.lintFiles(program.args[0]);
formatted = lint.format(detects);

lint.outputResults(formatted);

if (program.exit) {
  lint.failOnError(detects);
}

Fix fail on error for CLI


const MuxDemux = require('mux-demux')
const debug    = require('../debug').sub('shoe')
const pull     = require('pull-stream')
const stps     = require('stream-to-pull-stream')
const psts     = require('pull-stream-to-stream')
const EE       = require('events').EventEmitter

exports = module.exports = new EE

const conn = require('reconnect-core')(require('shoe'))(require('client-reloader')(function(stream) {
	const mx = MuxDemux()
	stream.pipe(mx).pipe(stream)

	mx.source = function(meta) {
		return stps.source(mx.createReadStream(meta))
	}

	mx.sink = function(meta) {
		return stps.sink(mx.createWriteStream(meta))
	}

	mx.duplex = function(meta) {
		const stream = mx.createStream(meta)
		return {
			sink: stps.sink(stream),
			source: stps.source(stream)
		}
	}

	mx.through = pull.Through(function(read, meta) {
		const stream = mx.createStream(meta)
		psts(read).pipe(stream)
		return stps.source(stream)
	})

	exports.emit('connect', mx)
})).connect('/shoe')

const MuxDemux = require('mux-demux')
const debug    = require('../debug').sub('shoe')
const pull     = require('pull-stream')
const stps     = require('stream-to-pull-stream')
const psts     = require('pull-stream-to-stream')
const EE       = require('events').EventEmitter

exports = module.exports = new EE

const conn = require('reconnect-core')(require('shoe'))(require('client-reloader')(function(stream) {
	const mx = MuxDemux()
	stream.pipe(mx).pipe(stream)

	mx.source = function(meta) {
		return stps.source(mx.createReadStream(meta))
	}

	mx.sink = function(meta) {
		return stps.sink(mx.createWriteStream(meta))
	}

	mx.duplex = function(meta) {
		const stream = mx.createStream(meta)
		return {
			sink: stps.sink(stream),
			source: stps.source(stream)
		}
	}

	mx.through = pull.Through(function(read, meta) {
		const stream = mx.createStream(meta)
		psts(read).pipe(stream)
		return stps.source(stream)
	})

	pull(
		mx.source('reset'),
		pull.drain(function() {
			window.location.reload()
		})
	)

	exports.emit('connect', mx)
})).connect('/shoe')

Make the client use the reset channel (Let the server reload the client)


angular.module("Prometheus.services").factory('UrlConfigDecoder', function($location) {
  return function(defaultHash) {
    var hash = $location.hash() || defaultHash;
    if (!hash) {
      return {};
    }
    // Decodes UTF-8
    // https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64.btoa#Unicode_Strings
    var configJSON = unescape(decodeURIComponent(window.atob(hash)));
    var config = JSON.parse(configJSON);
    return config;
  };
});

angular.module("Prometheus.services").factory('UrlHashEncoder', ["UrlConfigDecoder", function(UrlConfigDecoder) {
  return function(config) {
    var urlConfig = UrlConfigDecoder();
    for (var o in config) {
      urlConfig[o] = config[o];
    }
    var configJSON = JSON.stringify(urlConfig);
    // Encodes UTF-8
    // https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64.btoa#Unicode_Strings
    return window.btoa(encodeURIComponent(escape(configJSON)));
  };
}]);

angular.module("Prometheus.services").factory('UrlConfigEncoder', ["$location", "UrlHashEncoder", function($location, UrlHashEncoder) {
  return function(config) {
    $location.hash(UrlHashEncoder(config));
  };
}]);

angular.module("Prometheus.services").factory('UrlVariablesDecoder', function($location) {
  return function() {
    return $location.search();
  };
});

angular.module("Prometheus.services").factory('UrlConfigDecoder', function($location) {
  return function(defaultHash) {
    var hash = $location.hash() || defaultHash;
    if (!hash) {
      return {};
    }
    // Decodes UTF-8.
    // https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64.btoa#Unicode_Strings
    var configJSON = unescape(decodeURIComponent(window.atob(hash)));
    var config = JSON.parse(configJSON);
    return config;
  };
});

angular.module("Prometheus.services").factory('UrlHashEncoder', ["UrlConfigDecoder", function(UrlConfigDecoder) {
  return function(config) {
    var urlConfig = UrlConfigDecoder();
    for (var o in config) {
      urlConfig[o] = config[o];
    }
    var configJSON = JSON.stringify(urlConfig);
    // Encodes UTF-8.
    // https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64.btoa#Unicode_Strings
    return window.btoa(encodeURIComponent(escape(configJSON)));
  };
}]);

angular.module("Prometheus.services").factory('UrlConfigEncoder', ["$location", "UrlHashEncoder", function($location, UrlHashEncoder) {
  return function(config) {
    $location.hash(UrlHashEncoder(config));
  };
}]);

angular.module("Prometheus.services").factory('UrlVariablesDecoder', function($location) {
  return function() {
    return $location.search();
  };
});

Add periods to end of encode/decode comments.


var Boom = require('boom');

exports.register = function (server, conf, next) {

	const validate = function(req, decodedToken, callback){
        var exs = server.methods.executionserver.getExecutionServer(decodedToken.executionserver);
		if(exs){
			exs.scope = ['executionserver'];
			callback(undefined, true, exs);
		}else{
			callback(Boom.unauthorized(exs));
		}
	}

	conf.validate = validate;

	return require('hapi-jwt-couch').register(server, conf, next);

};

exports.register.attributes = {
  pkg: require('./package.json')
};

var Boom = require('boom');

exports.register = function (server, conf, next) {

	const validate = function(req, decodedToken, callback){
        var exs = server.methods.executionserver.getExecutionServer(decodedToken.executionserver);
		if(exs){
			exs.scope = ['executionserver'];
			callback(undefined, true, exs);
		}else{
			callback(Boom.unauthorized(exs));
		}
	}

	conf.validate = validate;

	server.register({
		register: require('hapi-jwt-couch'),
		options: conf
	}, function(err){

		if(err){
			throw err;
		}

		server.method({
			name: 'clusterpostauth.verify',
			method: server.methods.jwtauth.verify,
			options: {}
		});

	});

	return next();

};

exports.register.attributes = {
  pkg: require('./package.json')
};

Use the register function from server when discovering the hapi-jwt-couch plugin


// Validation for bookmark payloads.
'use strict';

var Hoek = require('hoek');
var Joi = require('joi');

var validators = {};

validators.bookmarkID = Joi.string().guid();

// Bookmark without ID. Used for the update and save payloads.
validators.bookmarkWithoutID = {
  id: validators.bookmarkID.allow(null),
  url: Joi.string()
    .required()
    .max(200, 'utf-8'),
  title: Joi.string()
    .max(200, 'utf-8')
    .allow(null),
  description: Joi.string().allow(null),
  added_at: Joi.string().allow(null),
  created_at: Joi.string().allow(null),
  updated_at: Joi.string().allow(null),
};

// The full bookmark payload, requiring the ID.
validators.bookmark = Hoek.clone(validators.bookmarkWithoutID);
validators.bookmark.id = validators.bookmarkID.required();

module.exports = validators;

// Validation for bookmark payloads.
'use strict';

var Hoek = require('hoek');
var Joi = require('joi');

var validators = {};

validators.bookmarkID = Joi.string().guid();

// Bookmark without ID. Used for the update and save payloads.
validators.bookmarkWithoutID = {
  id: validators.bookmarkID.allow(null),
  url: Joi.string()
    .required()
    .max(200, 'utf-8'),
  title: Joi.string()
    .max(200, 'utf-8')
    .allow(null),
  description: Joi.string().allow(null),
  added_at: Joi.date().iso().allow(null),
  created_at: Joi.date().iso().allow(null),
  updated_at: Joi.date().iso().allow(null),
};

// The full bookmark payload, requiring the ID.
validators.bookmark = Hoek.clone(validators.bookmarkWithoutID);
validators.bookmark.id = validators.bookmarkID.required();

module.exports = validators;

Validate date fields as ISO-format dates.


(function() {
  'use strict';

  angular
    .module('app.auth')
    .controller('AuthController', AuthController);

  AuthController.$inject = ['$location', 'authService'];

  function AuthController($location, authService) {
    var vm = this;

    vm.user = {
      email: '',
      password: ''
    };

    vm.register = register;
    vm.login = login;

    function register(user) {
      return authService.register(user)
        .then(function() {
          return vm.login(user);
        })
        .then(function() {
          return authService.sendWelcomeEmail(user.email);
        })
        .catch(function(error) {
          console.log(error);
        });
    }

    function login(user) {
      return authService.login(user)
        .then(function(loggedInUser) {
          console.log(loggedInUser);
          $location.path('/waitlist');
        })
        .catch(function(error) {
          console.log(error);
        });
    }
  }

})();

(function() {
  'use strict';

  angular
    .module('app.auth')
    .controller('AuthController', AuthController);

  AuthController.$inject = ['$location', 'authService'];

  function AuthController($location, authService) {
    var vm = this;

    vm.user = {
      email: '',
      password: ''
    };
    vm.error = null;

    vm.register = register;
    vm.login = login;

    function register(user) {
      return authService.register(user)
        .then(function() {
          return vm.login(user);
        })
        .then(function() {
          return authService.sendWelcomeEmail(user.email);
        })
        .catch(function(error) {
          vm.error = error;
        });
    }

    function login(user) {
      return authService.login(user)
        .then(function() {
          $location.path('/waitlist');
        })
        .catch(function(error) {
          vm.error = error;
        });
    }
  }

})();

Set vm.error in register and login on AuthController.


$(function() {
  var $loadMore = $('#load-more');

  if ($loadMore.length) {

    var $spinner = $('#spinner');

    function init() {
      $spinner.hide();
      $.ajaxPrefilter(function(options, _, xhr) {
        if (!xhr.crossDomain) {
          xhr.setRequestHeader('X-CSRF-Token', securityToken);
        }
      });
    }

    function updateUI() {
      $(this).hide();
      $spinner.show();
    }

    $loadMore.on('click', function() {
      updateUI.call(this);
//      $.ajax({
//        url: '/api/videos',
//        type: 'POST',
//        dataType: 'json',
//        complete: function() {
//          $('#spinner').hide();
//        }
//      });
    });

    init();
  }
});

$(function() {
  var $loadMore = $('#load-more');

  if ($loadMore.length) {

    var $spinner = $('#spinner');

    var init = function() {
      $spinner.hide();
      $.ajaxPrefilter(function(options, _, xhr) {
        if (!xhr.crossDomain) {
          xhr.setRequestHeader('X-CSRF-Token', securityToken);
        }
      });
    };

    var updateUI = function() {
      $(this).hide();
      $spinner.show();
    };

    $loadMore.on('click', function() {
      updateUI.call(this);
//      $.ajax({
//        url: '/api/videos',
//        type: 'POST',
//        dataType: 'json',
//        complete: function() {
//          $('#spinner').hide();
//        }
//      });
    });

    init();
  }
});

Change function declaration to function expression


import _ from 'lodash';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchPosts } from '../actions';

class PostsIndex extends Component {
  componentDidMount() {
    this.props.fetchPosts();
  }

  renderPosts() {
    return _.map(this.props.posts, post => {
      return (

          {post.title}

      );
    });
  }

  render() {
    return (

Posts

          {this.renderPosts()}

    );
  }
}

function mapStateToProps(state) {
  return { posts: state.posts };
}

export default connect(mapStateToProps, { fetchPosts })(PostsIndex);

import _ from 'lodash';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchPosts } from '../actions';
import { Link } from 'react-router-dom';

class PostsIndex extends Component {
  componentDidMount() {
    this.props.fetchPosts();
  }

  renderPosts() {
    return _.map(this.props.posts, post => {
      return (

          {post.title}

      );
    });
  }

  render() {
    return (

          <Link className="btn btn-primary" to="/posts/new">
            Add a Post
          </Link>

Posts

          {this.renderPosts()}

    );
  }
}

function mapStateToProps(state) {
  return { posts: state.posts };
}

export default connect(mapStateToProps, { fetchPosts })(PostsIndex);

Add button to new post page


import initialState from './initialState';
import {
    BOARD_REQUESTED,
    BOARD_LOADED,
    BOARD_DESTROYED,
    BOARD_SCROLLED_BOTTOM
} from '../constants'

export default function (state = initialState.board, action) {
    switch (action.type) {

        case BOARD_REQUESTED:
            return Object.assign({}, state, {
                isFetching: true,
                requestType: action.type  // for logging error to user...?
            })

        case BOARD_LOADED:
            return Object.assign({}, state, {
                posts: action.payload,
                isFetching: false
            })

        case BOARD_DESTROYED:
            return Object.assign({}, state, {
                posts: []
            })

        case BOARD_SCROLLED_BOTTOM:
            return Object.assign({}, state, {
                limit: action.payload
            })

        default:
            return state
    }
}

import initialState from './initialState';
import {
    BOARD_REQUESTED,
    BOARD_LOADED,
    BOARD_DESTROYED,
    BOARD_SCROLLED_BOTTOM,
    BOARD_FILTER
} from '../constants'

export default function (state = initialState.board, action) {
    switch (action.type) {

        case BOARD_REQUESTED:
            return Object.assign({}, state, {
                isFetching: true,
                requestType: action.type  // for logging error to user...?
            })

        case BOARD_LOADED:
            return Object.assign({}, state, {
                posts: action.payload,
                isFetching: false
            })

        case BOARD_DESTROYED:
            return Object.assign({}, state, {
                posts: []
            })

        case BOARD_SCROLLED_BOTTOM:
            return Object.assign({}, state, {
                limit: action.payload
            })

        case BOARD_FILTER:
            return Object.assign({}, state, {
                filterWord: action.payload || null
            })

        default:
            return state
    }
}

Add board reducer case for filtering


// This module loads and exposes an object of all available schemas, using
// the schema key as the object key.
//
// The schemas are loaded from the extension plugin directory. We therefore need
// to have a checkout of the extension alongside the web service in order to use
// this module.
'use strict';
let jsonfile = require('jsonfile');
let fs = require('fs');
let path = require('path');
let pluginDir = '../extension/src/plugins';
let dirs = getDirectories(pluginDir);
let schemas = {};
for (let dir of dirs) {
  try {
    let schema = jsonfile.readFileSync(`${pluginDir}/${dir}/schema.json`);
    if (schema.hasOwnProperty('schema') && schema.schema.hasOwnProperty('key')) {
      schemas[schema.schema.key] = schema;
    } else {
      console.log(`schema.json for plugin "${dir}" does not have a valid schema header. Skipping.`);
    }
  } catch (e) {
    console.log(`Problem reading schema.json for plugin "${dir}". Skipping.`);
  }
}

function getDirectories(srcpath) {
  return fs.readdirSync(srcpath).filter(function(file) {
    return fs.statSync(path.join(srcpath, file)).isDirectory();
  });
}

module.exports = schemas;

// This module loads and exposes an object of all available schemas, using
// the schema key as the object key. It adds the site metadata to the schema
// header for convenient access.
//
// The schemas and site metadata are loaded from the extension plugin directory.
// We therefore need to have a checkout of the extension alongside the web
// service in order to use this module.
'use strict';
let jsonfile = require('jsonfile');
let sitesDir = '../extension';
let pluginDir = '../extension/src/plugins';
let sites = jsonfile.readFileSync(`${sitesDir}/sites.json`);
let schemas = {};
for (let site of sites) {
    try {
      let schema = jsonfile.readFileSync(`${pluginDir}/${site.plugin}/schema.json`);
      if (schema.hasOwnProperty('schema') && schema.schema.hasOwnProperty('key')) {
        schemas[schema.schema.key] = schema;
        schema.schema.site = site;
      } else {
        console.log(`schema.json for plugin "${site.plugin}" does not have a valid schema header. Skipping.`);
      }
    } catch (e) {
      console.log(`Problem reading schema.json for plugin "${site.plugin}". Skipping. Error was: ${e}`);
    }
}
module.exports = schemas;

Use site metadata to find registered plugins/schemas.


<!--[if lt IE 9]>
document.createElement('video');
<!--[endif]-->

$(window).unload(function() {
  $.rails.enableFormElements($($.rails.formSubmitSelector));
});

// Cool title effect for "community favorites"
$(window).load(function() {
  // get the  height of the hero
  var pageHeight = $($('.hero')[0]).height();
  // get the height including margins of the featured crops title
  var titleHeight = $($('.explore-community-favorites')[0]).outerHeight(true);

  // On resize, recalculate the above values
  $(window).resize(function() {
    pageHeight = $($('.hero')[0]).height();
    titleHeight = $($('.explore-community-favorites')[0]).outerHeight(true);
  })

  $(window).scroll(function() {
    updateTitleBackground($(window).scrollTop(), pageHeight, titleHeight);
  })
});

// Darken the title background when the user scrolls to the featured crops header
function updateTitleBackground(scrollPos, pageHeight, titleHeight) {
  $exploreCommunityFavoritesTitle = $($('.explore-community-favorites')[0]);
  // The extra 1px lets smooth scrolling still trigger the change
  if (scrollPos >= (pageHeight - titleHeight - 1)) {
    $exploreCommunityFavoritesTitle.addClass('full-black');
  }
  else {
    $exploreCommunityFavoritesTitle.removeClass('full-black');
  }
}

<!--[if lt IE 9]>
document.createElement('video');
<!--[endif]-->

$(window).unload(function() {
  $.rails.enableFormElements($($.rails.formSubmitSelector));
});

// Cool title effect for "community favorites"
$(window).load(function() {
  // get the  height of the hero
  var pageHeight = $($('.hero')[0]).height();
  // get the height including margins of the featured crops title
  var titleHeight = $($('.explore-community-favorites')[0]).outerHeight(true);

  // On resize, recalculate the above values
  $(window).resize(function() {
    pageHeight = $($('.hero')[0]).height();
    titleHeight = $($('.explore-community-favorites')[0]).outerHeight(true);
  })

  $(window).scroll(function() {
    updateTitleBackground($(window).scrollTop(), pageHeight, titleHeight);
  })
});

// Darken the title background when the user scrolls to the featured crops header
function updateTitleBackground(scrollPos, pageHeight, titleHeight) {
  var exploreCommunityFavoritesTitle = $($('.explore-community-favorites')[0]);
  // The extra 1px lets smooth scrolling still trigger the change
  if (scrollPos >= (pageHeight - titleHeight - 1)) {
    exploreCommunityFavoritesTitle.addClass('full-black');
  }
  else {
    exploreCommunityFavoritesTitle.removeClass('full-black');
  }
}

Change var from jquery to normal js var


/* eslint-disable react/no-danger */

import React, { Component, PropTypes } from 'react'
import Block from './Block'

export function reloadPlayers() {
  if (typeof window !== 'undefined' && window.embetter) {
    window.embetter.reloadPlayers()
  }
}

class EmbedBlock extends Component {

  static propTypes = {
    data: PropTypes.object,
  }

  static defaultProps = {
    data: {},
  }

  componentDidMount() {
    reloadPlayers()
  }

  componentDidUpdate() {
    reloadPlayers()
  }

  render() {
    const { data: { service, url, thumbnailLargeUrl, id } } = this.props
    const children = typeof window !== 'undefined' ?
      window.embetter.utils.playerHTML(
        window.embetter.services[service],
        url,
        thumbnailLargeUrl,
        id,
      ) :
      null
    return (
      <Block {...this.props}>
        <div
          className="editable embed"
          dangerouslySetInnerHTML={{ __html: children }}
        />
      </Block>
    )
  }

}

export default EmbedBlock

/* eslint-disable react/no-danger */

import React, { Component, PropTypes } from 'react'
import Block from './Block'

export function reloadPlayers() {
  if (typeof window !== 'undefined' && window.embetter) {
    window.embetter.reloadPlayers()
  }
}

class EmbedBlock extends Component {

  static propTypes = {
    data: PropTypes.object,
  }

  static defaultProps = {
    data: {},
  }

  componentDidMount() {
    reloadPlayers()
  }

  componentDidUpdate() {
    reloadPlayers()
  }

  render() {
    const dataJS = this.props.data.toJS()
    const { service, url, thumbnailLargeUrl, id } = dataJS
    const children = typeof window !== 'undefined' ?
      window.embetter.utils.playerHTML(
        window.embetter.services[service],
        url,
        thumbnailLargeUrl,
        id,
      ) :
      null
    return (
      <Block {...this.props}>
        <div
          className="editable embed"
          dangerouslySetInnerHTML={{ __html: children }}
        />
      </Block>
    )
  }

}

export default EmbedBlock

Fix an issue with embed previews.


/*
 * Copyright 2014-2015 Hewlett-Packard Development Company, L.P.
 * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
 */

define([
    'find/app/find-pages',
    'find/app/page/find-search',
    'find/app/page/find-settings-page',
    'i18n!find/nls/bundle'
], function(FindPages, FindSearch, SettingsPage) {
    return FindPages.extend({

        initializePages: function() {
            this.pages = [
                {
                    constructor: SettingsPage
                    , pageName: 'settings'
                    , classes: 'hide-from-non-useradmin'
                }
            ];
        }
    });
});

/*
 * Copyright 2014-2015 Hewlett-Packard Development Company, L.P.
 * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
 */

define([
    'find/app/find-pages',
    'find/app/page/find-settings-page',
    'i18n!find/nls/bundle'
], function(FindPages, SettingsPage) {
    return FindPages.extend({

        initializePages: function() {
            this.pages = [
                {
                    constructor: SettingsPage
                    , pageName: 'settings'
                    , classes: 'hide-from-non-useradmin'
                }
            ];
        }
    });
});

Remove unnecessary import [rev: minor]


function resizeTaelContainer() {
    $('.tael-container').css('bottom', 16);
}

$(document).ready(function () {
    var header_bbox = $('.header')[0].getBoundingClientRect();
    $('.tael-container').css('top', header_bbox.bottom + 8);
    resizeTaelContainer();
    $(window).resize(resizeTaelContainer);
});

function resizeTaelContainer() {
    var
        header_bbox = $('.header')[0].getBoundingClientRect(),
        tael_container = $('.tael-container');
    tael_container.css('top', header_bbox.bottom);
    tael_container.css('bottom', 16);
}

$(document).ready(function () {
    resizeTaelContainer();
    $(window).resize(resizeTaelContainer);
});

Fix Tael container top resizing


import Service from '@ember/service';
import { computed } from '@ember/object';
import { inject as service } from '@ember/service';

export default Service.extend({
  session: service(),

  init() {
    let routeName = window.location.href.split('/').pop();
    if (routeName == 'logout') {
      if (this.get('session.isAuthenticated')) {
        return this.get('session').invalidate();
      }
    }

    this.set('data', computed('session.session.content.authenticated', function() {
      // console.log('session', this.get('session.session.content.authenticated'));
      return this.get('session.session.content.authenticated');
    }));
  }
});

import Service from '@ember/service';
import { computed } from '@ember/object';
import { inject as service } from '@ember/service';
import ENV from '../config/environment';
import RSVP from 'rsvp';

export default Service.extend({
  session: service(),
  ajax: service(),
  endpoint: `${ENV.APP.apiHost}/account/login`,

  details: computed('data', function() {
    let options = {
      headers: {
        'Authorization': this.get('data').access_token
      },
      method: 'GET'
    };

    let self = this;
    return new RSVP.Promise((resolve, reject) => {
      fetch(this.get('endpoint'), options).then((response) => {
        return self.set('user', response);
      }).catch(reject);
    });
  }),

  init() {
    let routeName = window.location.href.split('/').pop();
    if (routeName == 'logout') {
      if (this.get('session.isAuthenticated')) {
        return this.get('session').invalidate();
      }
    }

    this.set('data', computed('session.session.content.authenticated', function() {
      // console.log('session', this.get('session.session.content.authenticated'));
      let data = this.get('session.session.content.authenticated');
      return data;
    }));

  }
});

Add logged in user details


import { connect } from 'react-redux';

import { buildJKFPlayerFromState } from "../playerUtils";
import { inputMove, changeComments } from '../actions';
import BoardSet from '../components/BoardSet';

const mapStateToProps = (state) => {
  console.log(state);
  const player = buildJKFPlayerFromState(state);

  return {
    player: player,
    reversed: state.reversed,
  }
};

const mapDispatchToProps = {
  onInputMove: inputMove,
  onChangeComments: changeComments,
};

const BoardSetContainer = connect(
  mapStateToProps,
  mapDispatchToProps
)(BoardSet);
export default BoardSetContainer;

import { connect } from 'react-redux';

import { buildJKFPlayerFromState } from "../playerUtils";
import { inputMove, changeComments } from '../actions';
import BoardSet from '../components/BoardSet';

const mapStateToProps = (state) => {
  //console.log(state);
  const player = buildJKFPlayerFromState(state);

  return {
    player: player,
    reversed: state.reversed,
  }
};

const mapDispatchToProps = {
  onInputMove: inputMove,
  onChangeComments: changeComments,
};

const BoardSetContainer = connect(
  mapStateToProps,
  mapDispatchToProps
)(BoardSet);
export default BoardSetContainer;

Comment out console.log for debug


import SocketIONamespace from "./namespace";

export default class SocketIOServer {
    constructor(io) {
        this._io = io;
        this._sockets = this.of("/");
        this._nsps = Object.create(null);
    }

    get rawServer() {
        return this._io;
    }

    //
    // Delegation methods
    //

    of(name, fn) {
        var nsp = this._nsps[name];

        if (! nsp) {
            nsp = new SocketIONamespace(this._io.of(name, fn));
            this._nsps[name] = nsp;
        }

        return nsp;
    }
}

// pass through methods
[
    "serveClient", "set", "path", "adapter", "origins",
    "listen", "attach", "bind", "onconnection",
    /* "of", */ "close"
].forEach(methodName => {
    SocketIOServer.prototype[methodName] = function (...args) {
        this._io[methodName](...args);
        return this;
    };
});

[
    /* "on" */, "to", "in", "use", "emit", "send",
    "write", "clients", "compress",

    // mayajs-socketio-wrapper methods
    "on", "off", "receive", "offReceive"
].forEach(methodName => {
    SocketIOServer.prototype[methodName] = function (...args) {
        return this._sockets[methodName](...args);
    }
});

import SocketIONamespace from "./namespace";

export default class SocketIOServer {
    constructor(io) {
        this._io = io;
        this._nsps = Object.create(null);
        this._sockets = this.of("/");
    }

    get rawServer() {
        return this._io;
    }

    //
    // Delegation methods
    //

    of(name, fn) {
        var nsp = this._nsps[name];

        if (! nsp) {
            nsp = new SocketIONamespace(this._io.of(name, fn));
            this._nsps[name] = nsp;
        }

        return nsp;
    }
}

// pass through methods
[
    "serveClient", "set", "path", "adapter", "origins",
    "listen", "attach", "bind", "onconnection",
    /* "of", */ "close"
].forEach(methodName => {
    SocketIOServer.prototype[methodName] = function (...args) {
        this._io[methodName](...args);
        return this;
    };
});

[
    /* "on" */, "to", "in", "use", "emit", "send",
    "write", "clients", "compress",

    // mayajs-socketio-wrapper methods
    "on", "off", "receive", "offReceive"
].forEach(methodName => {
    SocketIOServer.prototype[methodName] = function (...args) {
        return this._sockets[methodName](...args);
    }
});

Fix annot read property '/' of undefined