module.exports = function (context, statusChain) {
  context.log.debug(`Reducing status chain ${statusChain}`);

  const reduced = statusChain.reduce((overallStatus, currentStatus) => {
    if (currentStatus === 'error' || overallStatus === 'error') {
      return 'error';
    }

    if (overallStatus === 'failure') {
      return 'failure';
    }

    return currentStatus;
  });

  context.log.debug(`Reduced status chain to ${reduced}`);

  return reduced;
};

module.exports = function (context, statusChain) {
  context.log.debug(`Reducing status chain [${statusChain.join(', ')}]`);

  const reduced = statusChain.reduce((overallStatus, currentStatus) => {
    if (currentStatus === 'error' || overallStatus === 'error') {
      return 'error';
    }

    if (overallStatus === 'failure') {
      return 'failure';
    }

    return currentStatus;
  });

  context.log.debug(`Reduced status chain to ${reduced}`);

  return reduced;
};

Improve status chain logging output


"use strict";

var URLS = (function() {
    var BASE = 'https://raw.githubusercontent.com/robertpainsi/robertpainsi.github.data/master/';

    return {
        BASE: BASE,
        PROGRAM_STATISTICS: BASE + 'catrobat/statistics/statistics.json'
    };
}());

"use strict";

var URLS = (function() {
    var BASE = 'https://raw.githubusercontent.com/robertpainsi/robertpainsi.github.data/master/';
    if (location.hostname === 'robertpainsi.localhost.io') {
        BASE = 'http://localhost/robertpainsi.github.data/';
    }

    return {
        BASE: BASE,
        PROGRAM_STATISTICS: BASE + 'catrobat/statistics/statistics.json'
    };
}());

Add local base url (only used for hostname robertpainsi.localhost.io)


'use strict';

var conjoiners = require('../lib/conjoiners');

exports['simple inter-process communication'] = function(test) {
    test.expect(3);

    var value = 'test_value';
    var cj1 = {};
    var cj1Name = 'test';
    var cj2 = {
        onTransenlightenment: function (event) {
            test.equal(event.property, 'val');
            test.equal(this[event.property], value);
        }
    };

    conjoiners.implant(cj1, 'test/conf.json', cj1Name).then(function() {
        return conjoiners.implant(cj2, 'test/conf.json', 'test2');
    }).then(function () {
        cj1.val = value;

        setTimeout(function() {
            test.equal(cj2.val, value);
            test.done();
        }, 1500);
    }).done();
};

'use strict';

var conjoiners = require('../lib/conjoiners');

exports['simple inter-process communication'] = function(test) {
    test.expect(3);

    var value = 'test_value';
    var cj1 = {};
    var cj1Name = 'test';
    var cj2 = {
        onTransenlightenment: function (event) {
            test.equal(event.property, 'val');
            test.equal(this[event.property], value);
            test.equal(cj2.val, value);
            test.done();
        }
    };

    conjoiners.implant(cj1, 'test/conf.json', cj1Name).then(function() {
        return conjoiners.implant(cj2, 'test/conf.json', 'test2');
    }).then(function () {
        cj1.val = value;
    }).done();
};

Test should not rely on setTimeout


var mongoose = require('mongoose');

var captionSchema = new mongoose.Schema({
  _id: { type: String, unique: true},
  url: String,
  captions: [{
      start: Number,
      dur: Number,
      value: String,
      extra_data: Array
  }]
});

module.exports = mongoose.model('Caption', captionSchema);

var mongoose = require('mongoose');

var captionSchema = new mongoose.Schema({
  _id: { type: String, unique: true},
  title: String,
  url: String,
  captions: [{
      start: Number,
      dur: Number,
      value: String,
      extra_data: Array
  }]
});

module.exports = mongoose.model('Caption', captionSchema);

Add title to mongoose caption schema


"use strict";

const dedent = require("dedent");

module.exports = filterOptions;

function filterOptions(yargs) {
  // Only for 'run', 'exec', 'clean', 'ls', and 'bootstrap' commands
  const opts = {
    scope: {
      describe: "Include only packages with names matching the given glob.",
      type: "string",
    },
    ignore: {
      describe: "Exclude packages with names matching the given glob.",
      type: "string",
    },
    private: {
      describe: "Include private packages.\nPass --no-private to exclude private packages.",
      type: "boolean",
      default: true,
    },
    since: {
      describe: dedent`
        Only include packages that have been updated since the specified [ref].
        If no ref is passed, it defaults to the most-recent tag.
      `,
      type: "string",
    },
    "include-filtered-dependents": {
      describe: dedent`
        Include all transitive dependents when running a command
        regardless of --scope, --ignore, or --since.
      `,
      type: "boolean",
    },
    "include-filtered-dependencies": {
      describe: dedent`
        Include all transitive dependencies when running a command
        regardless of --scope, --ignore, or --since.
      `,
      type: "boolean",
    },
  };

  return yargs.options(opts).group(Object.keys(opts), "Filter Options:");
}

"use strict";

const dedent = require("dedent");

module.exports = filterOptions;

function filterOptions(yargs) {
  // Only for 'run', 'exec', 'clean', 'ls', and 'bootstrap' commands
  const opts = {
    scope: {
      describe: "Include only packages with names matching the given glob.",
      type: "string",
    },
    ignore: {
      describe: "Exclude packages with names matching the given glob.",
      type: "string",
    },
    private: {
      describe: "Include private packages.\nPass --no-private to exclude private packages.",
      type: "boolean",
      defaultDescription: "true",
    },
    since: {
      describe: dedent`
        Only include packages that have been updated since the specified [ref].
        If no ref is passed, it defaults to the most-recent tag.
      `,
      type: "string",
    },
    "include-filtered-dependents": {
      describe: dedent`
        Include all transitive dependents when running a command
        regardless of --scope, --ignore, or --since.
      `,
      type: "boolean",
    },
    "include-filtered-dependencies": {
      describe: dedent`
        Include all transitive dependencies when running a command
        regardless of --scope, --ignore, or --since.
      `,
      type: "boolean",
    },
  };

  return yargs.options(opts).group(Object.keys(opts), "Filter Options:");
}

Allow --private to be configured from file


import express from 'express';
import extensions from './extensions';

const app = express();

app.use('/extensions', extensions);

export default app;

import express from 'express';
import extensions from './extensions';

const app = express();

app.use('/extensions', extensions);
app.get('/', function (req, res) {
  res.send('The CDN is working');
});

export default app;

Add text to test the server is working


/* @flow */

import path from 'path'

import State from '../State'
import File from '../File'
import Rule from '../Rule'

import type { Command, OptionsInterface, Phase } from '../types'

export default class CopyTargetsToRoot extends Rule {
  static parameterTypes: Array<Set<string>> = [new Set(['*'])]
  static description: string = 'Copy targets to root directory.'
  static alwaysEvaluate: boolean = true

  static async appliesToParameters (state: State, command: Command, phase: Phase, options: OptionsInterface, ...parameters: Array<File>): Promise<boolean> {
    return !!options.copyTargetsToRoot &&
      parameters.every(file => state.targets.has(file.filePath) && path.dirname(file.filePath) !== '.')
  }

  async initialize () {
    this.removeTarget(this.firstParameter.filePath)
    await this.addResolvedTarget('$BASE_0')
  }

  async run () {
    const filePath = this.resolvePath('$ROOTDIR/$BASE_0')
    await this.firstParameter.copy(filePath)
    await this.getOutput(filePath)
    return true
  }
}

/* @flow */

import path from 'path'

import State from '../State'
import File from '../File'
import Rule from '../Rule'

import type { Command, OptionsInterface, Phase } from '../types'

export default class CopyTargetsToRoot extends Rule {
  static parameterTypes: Array<Set<string>> = [new Set(['*'])]
  static description: string = 'Copy targets to root directory.'
  static alwaysEvaluate: boolean = true

  static async appliesToParameters (state: State, command: Command, phase: Phase, options: OptionsInterface, ...parameters: Array<File>): Promise<boolean> {
    return !!options.copyTargetsToRoot &&
      parameters.every(file => !file.virtual && state.targets.has(file.filePath) && path.dirname(file.filePath) !== '.')
  }

  async initialize () {
    // Remove the old target and replace with the new one.
    this.removeTarget(this.firstParameter.filePath)
    await this.addResolvedTarget('$BASE_0')
  }

  async run () {
    // Copy the target to it's new location and add the result as an output.
    const filePath = this.resolvePath('$ROOTDIR/$BASE_0')
    await this.firstParameter.copy(filePath)
    await this.getOutput(filePath)
    return true
  }
}

Add comments and check for virtual files


(function() {
  'use strict';

  angular.module('dimApp').component('dimItemTag', {
    controller: ItemTagController,
    bindings: {
      item: '='
    },
    template: `
      <select ng-options="tag as tag.label | translate for tag in $ctrl.settings.itemTags track by tag.type" ng-model="$ctrl.selected" ng-change="$ctrl.updateTag()"></select>
    `
  });

  ItemTagController.$inject = ['$scope', '$rootScope', 'dimSettingsService'];

  function ItemTagController($scope, $rootScope, dimSettingsService) {
    var vm = this;

    vm.settings = dimSettingsService;
    $scope.$watch(() => vm.item.dimInfo.tag, function() {
      vm.selected = _.find(vm.settings.itemTags, function(tag) {
        return tag.type === vm.item.dimInfo.tag;
      });
    });

    vm.updateTag = function() {
      vm.item.dimInfo.tag = vm.selected.type;
      $rootScope.$broadcast('dim-filter-invalidate');
      vm.item.dimInfo.save();
    };

    $scope.$on('dim-item-tag', (e, args) => {
      if (vm.item.dimInfo.tag === args.tag) {
        delete vm.item.dimInfo.tag;
      } else {
        vm.item.dimInfo.tag = args.tag;
      }
      $rootScope.$broadcast('dim-filter-invalidate');
      vm.item.dimInfo.save();
    });
  }
})();

(function() {
  'use strict';

  angular.module('dimApp').component('dimItemTag', {
    controller: ItemTagController,
    bindings: {
      item: '='
    },
    template: `
      <select ng-options="tag as tag.label | translate for tag in $ctrl.settings.itemTags track by tag.type" ng-model="$ctrl.selected" ng-change="$ctrl.updateTag()"></select>
    `
  });

  ItemTagController.$inject = ['$scope', '$rootScope', 'dimSettingsService'];

  function ItemTagController($scope, $rootScope, dimSettingsService) {
    var vm = this;

    vm.settings = dimSettingsService;
    $scope.$watch('$ctrl.item.dimInfo.tag', function() {
      vm.selected = _.find(vm.settings.itemTags, function(tag) {
        return tag.type === vm.item.dimInfo.tag;
      });
    });

    vm.updateTag = function() {
      vm.item.dimInfo.tag = vm.selected.type;
      $rootScope.$broadcast('dim-filter-invalidate');
      vm.item.dimInfo.save();
    };

    $scope.$on('dim-item-tag', (e, args) => {
      if (vm.item.dimInfo.tag === args.tag) {
        delete vm.item.dimInfo.tag;
      } else {
        vm.item.dimInfo.tag = args.tag;
      }
      $rootScope.$broadcast('dim-filter-invalidate');
      vm.item.dimInfo.save();
    });
  }
})();

Fix tag update when there's no tag


/*
 * Serve content over a socket
 */

module.exports = function (socket) {
  socket.emit('send:name', {
    name: 'Bob'
  });
};

/*
 * Serve content over a socket
 */

module.exports = function (socket) {
  // socket.emit('send:name', {
  //   name: 'Bob'
  // });
};

Comment this out for now


var Tracer = require('tracer');

function Logger (options) {
  this.options = options;
  this.tracer = this._setupTracer(options.enabled);
}

Logger.prototype.info = function () {
  this.tracer.info.apply(this.tracer, arguments);
};

Logger.prototype.error = function (message) {
  this.tracer.error(message);
}

Logger.prototype._setupTracer = function (enabled) {
  var noopTransport = function () {};
  var options       = {};

  options.format = "{{message}}";

  if (!enabled) {
    options.transport = noopTransport;
  }

  return Tracer.colorConsole(options);
}

function initTheLogger (options) {
  return new Logger(options);
}

var logger;
exports.logger = function (options) {
  if (!logger) {
    logger = initTheLogger(options);
  }

  return logger;
}

'use strict';

var Tracer = require('tracer');

function Logger (options) {
  this.options = options;
  this.tracer = this._setupTracer(options.enabled);
}

Logger.prototype.info = function () {
  this.tracer.info.apply(this.tracer, arguments);
};

Logger.prototype.error = function (message) {
  this.tracer.error(message);
};

Logger.prototype._setupTracer = function (enabled) {
  var noopTransport = function () {};
  var options       = {};

  options.format = '{{message}}';

  if (!enabled) {
    options.transport = noopTransport;
  }

  return Tracer.colorConsole(options);
};

function initTheLogger (options) {
  return new Logger(options);
}

var logger;
exports.logger = function (options) {
  if (!logger) {
    logger = initTheLogger(options);
  }

  return logger;
};

Make the syntax checker happy


'use strict';

const { getOr, toNumber } = require('lodash/fp');
const bcrypt = require('bcrypt');

const transforms = {
  password(value, context) {
    const { action, attribute } = context;

    if (action !== 'create' && action !== 'update') {
      return value;
    }

    const rounds = toNumber(getOr(10, 'encryption.rounds', attribute));

    return bcrypt.hashSync(value, rounds);
  },
};

module.exports = transforms;

'use strict';

const { getOr, toNumber, isString, isBuffer } = require('lodash/fp');
const bcrypt = require('bcrypt');

const transforms = {
  password(value, context) {
    const { action, attribute } = context;

    if (!isString(value) && !isBuffer(value)) {
      return value;
    }

    if (action !== 'create' && action !== 'update') {
      return value;
    }

    const rounds = toNumber(getOr(10, 'encryption.rounds', attribute));

    return bcrypt.hashSync(value, rounds);
  },
};

module.exports = transforms;

Handle non-string & non-buffer scenarios for the password transform


import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import BaseChart from '../../../src/components/BaseChart';
import { createMockChart } from '../../test-utils';

describe('<BaseChart />', function ()  {
  describe('on mount', function () {
    let clock;
    let chart;

    beforeEach(function () {
      chart = createMockChart();
      this.chartCreationFunc = sinon.stub();
      this.chartCreationFunc.returns(chart);
      clock = sinon.useFakeTimers();
    });

    afterEach(function () {
      clock.restore();
    });

    describe('when mounted', function () {
      it('should create a Highcharts chart', function () {
        const wrapper = mount(<BaseChart chartCreationFunc={this.chartCreationFunc} />);
        clock.tick(1);
        expect(this.chartCreationFunc).to.have.been.calledWith(wrapper.getDOMNode());
      });

      it('should create a chart context', function () {
        const wrapper = mount(<BaseChart chartCreationFunc={this.chartCreationFunc} />);
        clock.tick(1);
        const context = wrapper.instance().getChildContext();
        expect(context.chart).to.eql(chart);
      });
    });

    describe('when unmounted', function () {
      it('destroys the chart instance', function () {
        const wrapper = mount(<BaseChart chartCreationFunc={this.chartCreationFunc} />);
        clock.tick(1);
        expect(chart.destroy).not.to.have.been.called;
        wrapper.unmount();
        clock.tick(1);
        expect(chart.destroy).to.have.been.called;
      });
    });
  });
});

import React, { Component } from 'react';
import BaseChart from '../../../src/components/BaseChart';
import { createMockChart } from '../../test-utils';

describe('<BaseChart />', function ()  {
  let clock;
  let chart;

  beforeEach(function () {
    chart = createMockChart();
    this.chartCreationFunc = sinon.stub();
    this.chartCreationFunc.returns(chart);
    clock = sinon.useFakeTimers();
  });

  afterEach(function () {
    clock.restore();
  });

  describe('when mounted', function () {
    it('should create a Highcharts chart', function () {
      const wrapper = mount(<BaseChart chartCreationFunc={this.chartCreationFunc} />);
      clock.tick(1);
      expect(this.chartCreationFunc).to.have.been.calledWith(wrapper.getDOMNode());
    });

    it('should create a chart context', function () {
      const wrapper = mount(<BaseChart chartCreationFunc={this.chartCreationFunc} />);
      clock.tick(1);
      const context = wrapper.instance().getChildContext();
      expect(context.chart).to.eql(chart);
    });
  });

  describe('when unmounted', function () {
    it('destroys the chart instance', function () {
      const wrapper = mount(<BaseChart chartCreationFunc={this.chartCreationFunc} />);
      clock.tick(1);
      expect(chart.destroy).not.to.have.been.called;
      wrapper.unmount();
      clock.tick(1);
      expect(chart.destroy).to.have.been.called;
    });
  });
});

Remove unnecessary BaseChart test code


var LegendFactory = require('../editor/layers/layer-content-views/legend/legend-factory');

var onChange = function (layerDefModel) {
  var styleModel = layerDefModel.styleModel;
  var canSuggestLegends = LegendFactory.hasMigratedLegend(layerDefModel);
  var fill;
  var color;
  var size;

  if (!styleModel) return;
  if (!canSuggestLegends) return;

  LegendFactory.removeAllLegend(layerDefModel);

  fill = styleModel.get('fill');
  color = fill.color;
  size = fill.size;

  if (size && size.attribute !== undefined) {
    LegendFactory.createLegend(layerDefModel, 'bubble');
  }

  if (color && color.attribute !== undefined) {
    if (color.attribute_type && color.attribute_type === 'string') {
      LegendFactory.createLegend(layerDefModel, 'category');
    } else {
      LegendFactory.createLegend(layerDefModel, 'choropleth');
    }
  }
};

module.exports = {
  track: function (layerDefModel) {
    layerDefModel.on('change:cartocss', onChange);

    layerDefModel.on('destroy', function () {
      layerDefModel.off('change:cartocss', onChange);
    });
  }
};

var LegendFactory = require('../editor/layers/layer-content-views/legend/legend-factory');

var onChange = function (layerDefModel) {
  var styleModel = layerDefModel.styleModel;
  var canSuggestLegends = !LegendFactory.hasMigratedLegend(layerDefModel);
  var fill;
  var color;
  var size;

  if (!styleModel) return;
  if (!canSuggestLegends) return;

  LegendFactory.removeAllLegend(layerDefModel);

  fill = styleModel.get('fill');

  if (!fill) return;

  color = fill.color;
  size = fill.size;

  if (size && size.attribute !== undefined) {
    LegendFactory.createLegend(layerDefModel, 'bubble');
  }

  if (color && color.attribute !== undefined) {
    if (color.attribute_type && color.attribute_type === 'string') {
      LegendFactory.createLegend(layerDefModel, 'category');
    } else {
      LegendFactory.createLegend(layerDefModel, 'choropleth');
    }
  }
};

module.exports = {
  track: function (layerDefModel) {
    layerDefModel.on('change:cartocss', onChange);

    layerDefModel.on('destroy', function () {
      layerDefModel.off('change:cartocss', onChange);
    });
  }
};

Fix bug in legends magic.


function redirectKeyword(keyword) {
    document.location.href = '/keyword/' + keyword;
}

$(document).ready(function() {
    $('#keyword-form #keyword-input').focus();

    $('#keyword-form').submit(function() {
        redirectKeyword($('#keyword-form #keyword-input').val());
    });

    $('#keyword-form #keyword-input').focus(function() {
        if (this.setSelectionRange) {
            var len = $(this).val().length * 2;
            this.setSelectionRange(len, len);
        } else {
            $(this).val($(this).val());
        }
    });

    $('#keyword-form #keyword-input').trigger('focus');
});

function redirectKeyword(keyword) {
    document.location.href = '/keyword/' + keyword;
}

$(document).ready(function() {
    $('#keyword-form #keyword-input').focus();

    $('#keyword-form').submit(function() {
        redirectKeyword($('#keyword-form #keyword-input').val());
    });

    $('#keyword-form #keyword-input').focus(function() {
        $(this).select();
    });

    $('#keyword-form #keyword-input').trigger('focus');
});

Select text instead of move cursor to the end


define( function( require, exports, module ) {
	'use strict';

	module.exports = {
		root: true,
		de: true,
		es: true,
		fr: true,
		gl: true,
		it: true,
		sv: true,
		uk: true,
		ru: true
	};
} );

define( function( require, exports, module ) {
	'use strict';

	module.exports = {
		root: true,
		de: true,
		es: true,
		fr: true,
		gl: true,
		it: true,
		ru: true,
		sv: true,
		uk: true
	};
} );

Use alphabetic ordering of nls.


describe("Core Bliss", function () {
	"use strict";

	before(function() {
		fixture.setBase('tests/fixtures');
	});

	beforeEach(function () {
		this.fixture = fixture.load('core.html');
	});

	// testing setup
	it("has the fixture on the dom", function () {
		expect($('#fixture-container')).to.not.be.null;
	});

	it("is a valid testing environment", function () {
		// PhantomJS fails this one by default
		expect(Element.prototype.matches).to.not.be.undefined;
	});

	it("has global methods and aliases", function() {
		expect(Bliss).to.exist;
		expect($).to.exist;
		expect($$).to.exist;

		expect($).to.equal(Bliss);
		expect($$).to.equal($.$);
	});
});

describe("Core Bliss", function () {
	"use strict";

	before(function() {
		fixture.setBase('tests/fixtures');
	});

	beforeEach(function () {
		this.fixture = fixture.load('core.html');
	});

	// testing setup
	it("has the fixture on the dom", function () {
		expect($('#fixture-container')).to.not.be.null;
	});

	it("has global methods and aliases", function() {
		expect(Bliss).to.exist;
		expect($).to.exist;
		expect($$).to.exist;

		expect($).to.equal(Bliss);
		expect($$).to.equal($.$);
	});
});

Revert "Check testing environment has appropriate features"


var path = require('path')
var marked = require('marked')
var nunjucks = require('nunjucks')

var parseDocumentation = function (files) {
  files.forEach(function (file) {
    nunjucks.configure([
      path.join(__dirname, '..', 'macros'),
      path.parse(file.path).dir
    ])

    marked.setOptions({
      gfm: false
    })

    var fileContents = [
      `{% from 'example.html' import example %}`,
      `{% from 'markup.html' import markup %}`,
      file.contents.toString()
    ].join('\n')

    var markdown = nunjucks.renderString(fileContents)
    var html = marked(markdown)

    file.contents = Buffer.from(html)
  })

  return files
}

module.exports = parseDocumentation

var path = require('path')
var marked = require('marked')
var nunjucks = require('nunjucks')

var parseDocumentation = function (files) {
  files.forEach(function (file) {
    nunjucks.configure([
      path.join(__dirname, '..', 'macros'),
      path.parse(file.path).dir
    ])

    var fileContents = [
      `{% from 'example.html' import example %}`,
      `{% from 'markup.html' import markup %}`,
      file.contents.toString()
    ].join('\n')

    var markdown = nunjucks.renderString(fileContents)
    var html = marked(markdown)

    file.contents = Buffer.from(html)
  })

  return files
}

module.exports = parseDocumentation

Use github flavoured markdown as before


function clearEOIMarkers(scope) {
    // Remove existing markers.
    scope.placesOfInterestMarkers.forEach(function (markerObj) {
        scope.map.removeLayer(markerObj.marker);
    });

    scope.placesOfInterestMarkers = [];
}

function clampWithinBounds(value, lowerBound, upperBound) {
    value = Math.max(value, lowerBound);
    value = Math.min(value, upperBound);

    return value;
}

function createPoiPopupContent(poiAndMarkerObj, scope, compile) {
    var content = compile(angular.element('{{poi.DisplayName}}
'
        + ''
        + 'Remove'
        + ''));

    var tempScope = scope.$new();
    tempScope.poi = poiAndMarkerObj.poi;

    return content(tempScope)[0];
}

function updateOrigin(latlng, scope) {
    scope.originMarker.setLatLng(latlng).update();

    // If have already drawn radius circle then remove it and re-set radius.
    if (scope.map.hasLayer(scope.circleOverlay)) {
        scope.map.removeLayer(scope.circleOverlay);
        scope.radius = 0;
        scope.$root.$broadcast('searchAreaCleared');
    }
}

function clearEOIMarkers(scope) {
    // Remove existing markers.
    scope.placesOfInterestMarkers.forEach(function (markerObj) {
        scope.map.removeLayer(markerObj.marker);
    });

    scope.placesOfInterestMarkers = [];
}

function clampWithinBounds(value, lowerBound, upperBound) {
    value = Math.max(value, lowerBound);
    value = Math.min(value, upperBound);

    return value;
}

function createPoiPopupContent(poiAndMarkerObj, scope, compile) {
    var content = compile(angular.element('{{poi.DisplayName}}
'
        + ''
        + 'Remove'
        + ''));

    var tempScope = scope.$new();
    tempScope.poi = poiAndMarkerObj.poi;

    return content(tempScope)[0];
}

function updateOrigin(latlng, scope) {
    scope.originMarker.setLatLng(latlng).update();

    // If have already drawn radius circle then remove it and re-set radius.
    if (scope.map.hasLayer(scope.circleOverlay)) {
        scope.map.removeLayer(scope.circleOverlay);
        scope.radius = 0;
        scope.circleOverlay.setRadius(0);
        scope.$root.$broadcast('searchAreaCleared');
    }
}

Set search area circle size to 0 on reset.


module.exports.unit = {
  environment: 'node',
  rootPath: process.cwd(),
  sources: [
    'lib/**/*.js'
  ],
  tests: [
    'test/**/*.js'
  ],
  extensions: [
    require('buster-istanbul')
  ],
  'buster-istanbul': {
    outputDirectory: '.bob/report/coverage'
  }
};

module.exports.unit = {
  environment: 'node',
  rootPath: process.cwd(),
  sources: [
    'lib/**/*.js'
  ],
  tests: [
    'test/**/*.js'
  ],
  extensions: [
    require('buster-istanbul')
  ],
  'buster-istanbul': {
    outputDirectory: '.bob/report/coverage/buster-istanbul/'
  }
};

Add buster-istanbul subdir as output directory.


(function() {
  var app = angular.module('notely', [
    'ui.router',
    'notely.notes',
    'notely.notes.service'
  ]);

  function config($urlRouterProvider) {
    $urlRouterProvider.otherwise('/notes');
  }

  config['$inject'] = ['$urlRouterProvider'];
  app.config(config);
})();

(function() {
  var app = angular.module('notely', [
    'ui.router',
    'notely.notes',
    'notely.notes.service'
  ]);

  function config($urlRouterProvider) {
    $urlRouterProvider.otherwise('/notes/');
  }

  config['$inject'] = ['$urlRouterProvider'];
  app.config(config);
})();

Update default route to include trailing slash.


export const siteMapData = [
  {
    title: 'Tools',
    links: [
      { title: 'Country Profiles', href: '/countries' },
      { title: 'NDCs', href: '/ndcs-content' },
      { title: 'NDC-SDG Linkages', href: '/ndcs-sdg' },
      { title: 'Historical GHG Emissions', href: '/ghg-emissions' },
      { title: 'Pathways', href: '/pathways' }
    ]
  },
  {
    title: 'Data',
    links: [
      { title: 'Explore Datasets', href: '/data-explorer' },
      { title: 'My Climate Watch', href: '/my-climate-watch' }
    ]
  },
  {
    title: 'Country Platforms',
    links: [
      // { title: 'India', href: '/' },
      // { title: 'Indonesia', href: '/' },
      { title: 'South Africa', href: 'http://southafricaclimateexplorer.org/' }
    ]
  },
  {
    title: 'About',
    links: [
      { title: 'About Climate Watch', href: '/about' },
      { title: 'Climate Watch Partners', href: '/about/partners' },
      { title: 'Contact Us & Feedback', href: '/about/contact' },
      { title: 'Permissions & Licensing', href: '/about/permissions' },
      { title: 'FAQ', href: '/about/faq' }
    ]
  }
];

export const siteMapData = [
  {
    title: 'Tools',
    links: [
      { title: 'Country Profiles', href: '/countries' },
      { title: 'NDCs', href: '/ndcs-content' },
      { title: 'NDC-SDG Linkages', href: '/ndcs-sdg' },
      { title: 'Historical GHG Emissions', href: '/ghg-emissions' },
      { title: 'Pathways', href: '/pathways' }
    ]
  },
  {
    title: 'Data',
    links: [
      { title: 'Explore Datasets', href: '/data-explorer' },
      { title: 'My Climate Watch', href: '/my-climate-watch' }
    ]
  },
  {
    title: 'Country Platforms',
    links: [
      // { title: 'India', href: '/' },
      // { title: 'Indonesia', href: '/' },
      { title: 'South Africa', href: 'http://southafricaclimateexplorer.org/' }
    ]
  },
  {
    title: 'About',
    links: [
      { title: 'About Climate Watch', href: '/about' },
      { title: 'Climate Watch Partners', href: '/about/partners' },
      { title: 'Contact Us & Feedback', href: '/about/contact' },
      { title: 'Permissions & Licensing', href: '/about/permissions' },
      { title: 'FAQ', href: '/about/faq/general_questions' }
    ]
  }
];

Correct FAQ link on footer


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

chromespec.registerJasmineTest('chrome.app.runtime');
chromespec.registerJasmineTest('chrome.app.window');
chromespec.registerJasmineTest('chrome.fileSystem');
chromespec.registerJasmineTest('chrome.i18n');
chromespec.registerJasmineTest('chrome.runtime');
chromespec.registerJasmineTest('chrome.storage');
chromespec.registerJasmineTest('chrome.socket');
chromespec.registerJasmineTest('chrome.syncFileSystem');
chromespec.registerJasmineTest('pageload');

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

chromespec.registerJasmineTest('chrome.app.runtime');
chromespec.registerJasmineTest('chrome.app.window');
chromespec.registerJasmineTest('chrome.fileSystem');
chromespec.registerJasmineTest('chrome.i18n');
chromespec.registerJasmineTest('chrome.runtime');
chromespec.registerJasmineTest('chrome.storage');
chromespec.registerJasmineTest('chrome.socket');
chromespec.registerJasmineTest('pageload');

Remove test.syncFileSystem.js from the load list as it doesn't exist.


// @flow
export opaque type WideBar = "W"
export const WIDE_BAR: WideBar = "W"

export opaque type NarrowBar = "N"
export const NARROW_BAR: NarrowBar = "N"

export opaque type WideSpace = "w"
export const WIDE_SPACE: WideSpace = "w"

export opaque type NarrowSpace = "n"
export const NARROW_SPACE: NarrowSpace = "n"

export opaque type DiscreteSymbologySymbol = WideBar | NarrowBar | WideSpace | WideBar
export opaque type SymbologySymbol = DiscreteSymbologySymbol

// @flow
export opaque type WideBar = "W"
export const WIDE_BAR: WideBar = "W"

export opaque type NarrowBar = "N"
export const NARROW_BAR: NarrowBar = "N"

export opaque type WideSpace = "w"
export const WIDE_SPACE: WideSpace = "w"

export opaque type NarrowSpace = "n"
export const NARROW_SPACE: NarrowSpace = "n"

export type DiscreteSymbologySymbol = WideBar | NarrowBar | WideSpace | WideBar
export type SymbologySymbol = DiscreteSymbologySymbol

Make symbol unions non opaque types


const path = require("path");

module.exports = {
    entry: path.resolve(__dirname, "./source/index.js"),

    externals: {
        buttercup: "buttercup"
    },

    module: {
        rules : [
            {
                test: /\.(js|esm)$/,
                use: {
                    loader: "babel-loader",
                    options: {
                        "presets": [
                            ["@babel/preset-env", {
                                "corejs": 3,
                                "useBuiltIns": "entry",
                                "targets": {
                                    "node": "6"
                                }
                            }]
                        ],
                        "plugins": [
                            ["@babel/plugin-proposal-object-rest-spread", {
                                "useBuiltIns": true
                            }]
                        ]
                    }
                }
            }
        ]
    },

    output: {
        filename: "buttercup-importer.js",
        libraryTarget: "commonjs2",
        path: path.resolve(__dirname, "./dist")
    },

    target: "node"
};

const path = require("path");

module.exports = {
    entry: path.resolve(__dirname, "./source/index.js"),

    externals: {
        argon2: "argon2",
        buttercup: "buttercup",
        kdbxweb: "kdbxweb"
    },

    module: {
        rules : [
            {
                test: /\.(js|esm)$/,
                use: {
                    loader: "babel-loader",
                    options: {
                        "presets": [
                            ["@babel/preset-env", {
                                "corejs": 3,
                                "useBuiltIns": "entry",
                                "targets": {
                                    "node": "6"
                                }
                            }]
                        ],
                        "plugins": [
                            ["@babel/plugin-proposal-object-rest-spread", {
                                "useBuiltIns": true
                            }]
                        ]
                    }
                }
            }
        ]
    },

    output: {
        filename: "buttercup-importer.js",
        libraryTarget: "commonjs2",
        path: path.resolve(__dirname, "./dist")
    },

    target: "node"
};

Split dependencies out in webpack to reduce bundle size


var webpack = require('webpack');

module.exports = {
  entry: [
    'webpack-dev-server/client?http://localhost:3000',
    'webpack/hot/dev-server',
    './scripts/index'
  ],
  output: {
    path: __dirname,
    filename: 'bundle.js',
    publicPath: '/scripts/'
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin()
  ],
  resolve: {
    extensions: ['', '.js']
  },
  module: {
    loaders: [
      { test: /\.js$/, loaders: ['react-hot', 'jsx?harmony'] },
    ]
  }
};

var webpack = require('webpack');

module.exports = {
  devtool: 'eval',
  entry: [
    'webpack-dev-server/client?http://localhost:3000',
    'webpack/hot/dev-server',
    './scripts/index'
  ],
  output: {
    path: __dirname,
    filename: 'bundle.js',
    publicPath: '/scripts/'
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin()
  ],
  resolve: {
    extensions: ['', '.js']
  },
  module: {
    loaders: [
      { test: /\.js$/, loaders: ['react-hot', 'jsx?harmony'] },
    ]
  }
};

Add devtool: 'eval' for Chrome DevTools


const TerserJsPlugin = require('terser-webpack-plugin');

const serverBuild = {
    mode: 'production',
    entry: './src/twig.js',
    target: 'node',
    node: false,
    output: {
        path: __dirname,
        filename: 'twig.js',
        library: 'Twig',
        libraryTarget: 'umd'
    },
    optimization: {
        minimize: false
    }
};

const clientBuild = {
    mode: 'production',
    entry: './src/twig.js',
    target: 'web',
    node: {
        __dirname: false,
        __filename: false
    },
    module: {
        rules: [
            {
                exclude: /(node_modules)/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env'],
                        plugins: [
                            '@babel/plugin-transform-modules-commonjs',
                            '@babel/plugin-transform-runtime'
                        ]
                    }
                }
            }
        ]
    },
    output: {
        path: __dirname,
        filename: 'twig.min.js',
        library: 'Twig',
        libraryTarget: 'umd'
    },
    optimization: {
        minimize: true,
        minimizer: [new TerserJsPlugin({
            include: /\.min\.js$/
        })]
    }
};

module.exports = [serverBuild, clientBuild];

const TerserJsPlugin = require('terser-webpack-plugin');

const commonModule = {
    exclude: /(node_modules)/,
    use: {
        loader: "babel-loader",
        options: {
            presets: ["@babel/preset-env"],
            plugins: [
                "@babel/plugin-transform-modules-commonjs",
                "@babel/plugin-transform-runtime"
            ]
        }
    }
};

const serverBuild = {
    mode: 'production',
    entry: './src/twig.js',
    target: 'node',
    node: false,
    output: {
        path: __dirname,
        filename: 'twig.js',
        library: 'Twig',
        libraryTarget: 'umd'
    },
    module: {
        rules: [commonModule],
    },
    optimization: {
        minimize: false
    }
};

const clientBuild = {
    mode: 'production',
    entry: './src/twig.js',
    target: 'web',
    node: {
        __dirname: false,
        __filename: false
    },
    module: {
        rules: [commonModule]
    },
    output: {
        path: __dirname,
        filename: 'twig.min.js',
        library: 'Twig',
        libraryTarget: 'umd'
    },
    optimization: {
        minimize: true,
        minimizer: [new TerserJsPlugin({
            include: /\.min\.js$/
        })]
    }
};

module.exports = [serverBuild, clientBuild];

Add babel preset on serverBuild


module.exports = {
  navigateFallback: '/index.html',
  runtimeCaching: [{
    urlPattern: /\/api\/search\/.*/,
    handler: 'networkFirst',
    options: {
      cache: {
        maxEntries: 100,
        name: 'search-cache',
      },
    },
    urlPattern: /\/api\/.*/,
    handler: 'networkFirst',
    options: {
      cache: {
        maxEntries: 1000,
        name: 'api-cache',
      },
    },
  }],
}

module.exports = {
  navigateFallback: '/index.html',
  runtimeCaching: [{
    urlPattern: /\/api\/search\/.*/,
    handler: 'networkFirst',
    options: {
      cache: {
        maxEntries: 100,
        name: 'search-cache',
      },
    },
    urlPattern: /\/api\/.*/,
    handler: 'fastest',
    options: {
      cache: {
        maxEntries: 1000,
        name: 'api-cache',
      },
    },
  }],
}

Use fastest method for api calls other than search


import { ActionTypes } from '../config/constants';
import { ref } from '../config/constants';

export const addItem = payload => {
	return {
		type: ActionTypes.BUDGETS_ADD_ITEM,
		payload,
	}
}

export const removeItem = payload => {
	return {
		type: ActionTypes.BUDGETS_REMOVE_ITEM,
		payload,
	}
}

export const subscribe = payload => {
	return dispatch => {

		dispatch({
			type: ActionTypes.BUDGETS_SUBSCRIBE,
		})

    const itemsRef = ref.child(`items`);
    itemsRef.on('value', snapshot => {
      const itemList = snapshot.val();

      dispatch({
        type: ActionTypes.BUDGETS_UPDATE,
        payload: {
          itemList: itemList,
        }
      });
    });
	}
}

export const search = payload => {
	return {
		type: ActionTypes.BUDGETS_SEARCH,
		payload,
	}
}

export const toggleColumn = payload => {
	return {
		type: ActionTypes.BUDGETS_TOGGLE_COL,
		payload,
	}
}

export const updateItem = payload => {
	return {
		type: ActionTypes.BUDGETS_UPDATE_ITEM,
		payload,
	}
}

import { ActionTypes } from '../config/constants';
import { ref } from '../config/constants';

export const addItem = payload => {
	return dispatch => {
		const itemRef =	ref.child(`items/${payload.item.id}`);

		itemRef.set( payload.item );

		dispatch({
			type: ActionTypes.BUDGETS_ADD_ITEM,
			payload,
		});
	}
}

export const removeItem = payload => {
	return dispatch => {
		const itemRef =	ref.child(`items/${payload.itemId}`);

		itemRef.remove();

		dispatch({
			type: ActionTypes.BUDGETS_REMOVE_ITEM,
			payload,
		});
	}
}

export const subscribe = payload => {
	return dispatch => {

		dispatch({
			type: ActionTypes.BUDGETS_SUBSCRIBE,
		})

    const itemsRef = ref.child(`items`);
    itemsRef.on('value', snapshot => {
      const itemList = snapshot.val();

      dispatch({
        type: ActionTypes.BUDGETS_UPDATE,
        payload: {
          itemList: itemList,
        }
      });
    });
	}
}

export const search = payload => {
	return {
		type: ActionTypes.BUDGETS_SEARCH,
		payload,
	}
}

export const toggleColumn = payload => {
	return {
		type: ActionTypes.BUDGETS_TOGGLE_COL,
		payload,
	}
}

export const updateItem = payload => {
	return dispatch => {
		const itemRef =	ref.child(`items/${payload.updatedItem.id}`);

		itemRef.update(payload.updatedItem);

		dispatch({
			type: ActionTypes.BUDGETS_UPDATE_ITEM,
			payload,
		})
	}
}

Update items in Firebase on change


module.exports = {
  scenarios: [
    {
      name: 'default',
      dependencies: { }
    },
    {
      name: 'ember-1.13.8',
      dependencies: {
        'ember': '1.13.8',
        'ember-data': '1.13.9'
      }
    },
    {
      name: 'ember-release',
      dependencies: {
        'ember': 'components/ember#release',
        'ember-data': 'v2.0.0-beta.2'
      },
      resolutions: {
        'ember': 'release'
      }
    },
    {
      name: 'ember-beta',
      dependencies: {
        'ember': 'components/ember#beta'
      },
      resolutions: {
        'ember': 'beta'
      }
    },
    {
      name: 'ember-canary',
      dependencies: {
        'ember': 'components/ember#canary'
      },
      resolutions: {
        'ember': 'canary'
      }
    }
  ]
};

module.exports = {
  scenarios: [
    {
      name: 'default',
      dependencies: { }
    },
    {
      name: 'ember-1.13.8',
      dependencies: {
        'ember': '1.13.8',
        'ember-data': '1.13.9'
      }
    },
    {
      name: 'ember-release',
      dependencies: {
        'ember': 'components/ember#release',
        'ember-data': 'v2.0.0-beta.2'
      },
      resolutions: {
        'ember': 'release'
      }
    },
    {
      name: 'ember-beta',
      dependencies: {
        'ember': 'components/ember#beta',
        'ember-data': 'v2.0.0-beta.2'
      },
      resolutions: {
        'ember': 'beta'
      }
    },
    {
      name: 'ember-canary',
      dependencies: {
        'ember': 'components/ember#canary',
        'ember-data': 'v2.0.0-beta.2'
      },
      resolutions: {
        'ember': 'canary'
      }
    }
  ]
};

Use 2.x flavor of ember data in canary and beta.


new Vue({
  el: '#app',

  // Using ES6 string litteral delimiters because
  // Go uses {{ . }} for its templates
  delimiters: ['${', '}'],

  data: {
    ws: null,
    message: '',
    chat: [],
  },

  created: function() {
    let protocol = (window.location.protocol === 'https') ? 'wss' : 'ws'
    this.ws = new WebSocket(`${protocol}://${window.location.host}/ws`)
    this.ws.addEventListener('message', (e) => {
      let { user, avatar, content } = JSON.parse(e.data)
      this.chat.push({ user, avatar, content })
    })
  },

  methods: {
    send: function() {
      if (!this.message) {
        return
      }

      this.ws.send(
        JSON.stringify({
          content: this.message,
        })
      )
      this.message = ''
    },
  }
})

new Vue({
  el: '#app',

  // Using ES6 string litteral delimiters because
  // Go uses {{ . }} for its templates
  delimiters: ['${', '}'],

  data: {
    ws: null,
    message: '',
    chat: [],
  },

  created: function() {
    let protocol = (window.location.protocol === 'https:') ? 'wss:' : 'ws:'
    this.ws = new WebSocket(`${protocol}//${window.location.host}/ws`)
    this.ws.addEventListener('message', (e) => {
      let { user, avatar, content } = JSON.parse(e.data)
      this.chat.push({ user, avatar, content })
    })
  },

  methods: {
    send: function() {
      if (!this.message) {
        return
      }

      this.ws.send(
        JSON.stringify({
          content: this.message,
        })
      )
      this.message = ''
    },
  }
})

Set protocol correctly ... oops


'use strict';

const path = require('path');
const encryptor = require('./src/encryptor');
const argv = process.argv.slice(2);

if(argv.length != 2) {
  console.log('Usage: node create-user.js USERNAME PASSWORD');
  process.exit(1);
}

encryptor
  .encrypt('{}', path.join('./data', `${argv[0]}.dat`), argv[1])
  .then(() => {
    console.log('User file created!');
    process.exit(0);
  })
  .catch(err => {
    console.log('Error: ', e);
    process.exit(1);
  });

'use strict';

const path = require('path');
const encryptor = require('./src/encryptor');
const argv = process.argv.slice(2);

if(argv.length != 2) {
  console.log('Usage: node create-user.js USERNAME PASSWORD');
  process.exit(1);
}

encryptor
  .encrypt(JSON.stringify({
    services: []
  }), path.join('./data', `${argv[0]}.dat`), argv[1])
  .then(() => {
    console.log('User file created!');
    process.exit(0);
  })
  .catch(err => {
    console.log('Error: ', e);
    process.exit(1);
  });

Create user adiciona json básico no arquivo


let db;
try {
    db = require("rethinkdbdash")(require("../config").connectionOpts);
} catch (err) {
    console.error("You haven't installed rethinkdbdash npm module or you didn't have configured the bot yet! Please do so.");
}
(async function () {
    if (!db) return;
    const tables = await db.tableList();
    if (!tables.includes("blacklist")) await db.tableCreate("blacklist");
    if (!tables.includes("session")) await db.tableCreate("session");
    if (!tables.includes("configs")) await db.tableCreate("configs");
    if (!tables.includes("feedback")) await db.tableCreate("feedback");
    if (!tables.includes("tags")) await db.tableCreate("tags");
    if (!tables.includes("profile")) await db.tableCreate("profile");
    if (!tables.includes("modlog")) await db.tableCreate("modlog");
    if (!tables.includes("extensions")) await db.tableCreate("extensions");
    if (!tables.includes("extension_store")) await db.tableCreate("extension_store");
    console.log("All set up!");
    process.exit(0);
})();

let db;
try {
    db = require("rethinkdbdash")(require("../config").connectionOpts);
} catch (err) {
    console.error("You haven't installed rethinkdbdash npm module or you didn't have configured the bot yet! Please do so.");
}
(async function () {
    if (!db) return;
    const tables = await db.tableList();
    if (!tables.includes("blacklist")) await db.tableCreate("blacklist");
    if (!tables.includes("session")) await db.tableCreate("session");
    if (!tables.includes("configs")) await db.tableCreate("configs");
    if (!tables.includes("feedback")) await db.tableCreate("feedback");
    if (!tables.includes("tags")) await db.tableCreate("tags");
    if (!tables.includes("profile")) await db.tableCreate("profile");
    if (!tables.includes("modlog")) await db.tableCreate("modlog");
    if (!tables.includes("extensions")) await db.tableCreate("extensions");
    if (!tables.includes("extension_store")) await db.tableCreate("extension_store");
    if (!tables.includes("phone")) await db.tableCreate("phone");
    console.log("All set up!");
    process.exit(0);
})();

Create the phone table when it existn't


jQuery(function( $ ) {

    var mOptions = $.extend({}, mOpt);
    console.log(mOptions);

    $('#cmessage').atwho({
        at: "@",
        displayTpl: "
${username} ${name}
",
        insertTpl: "${atwho-at}${username}",
        callbacks: {
            remoteFilter: function(query, callback) {
                console.log('Query: ' + query);

                 //
                 if(query === null || query.length < 1){
                    return callback(null);
                 }

                $.ajax({
                    url: mOptions.path + "index.php?mq=" + query,
                    type: 'GET',
                    dataType: 'json',

                    success: function(data) {
                        callback(data);
                        console.log('Data: ' + data);
                    },

                    error: function() {
                        console.warn('Didn\'t Work');
                    },

                    beforeSend: function(xhr) {
                        //xhr.setRequestHeader('Authorization', localStorageService.get('authToken'));
                    }
                });
            }
        },
        searchKey: "username",
        limit: 5,
        maxLen: 15,
        displayTimeout: 300,
        highlightFirst: true,
        delay: 50,

    });

});

jQuery(function( $ ) {

    var mOptions = $.extend({}, mOpt);
    console.log(mOptions);

    $('#cmessage').atwho({
        at: "@",
        displayTpl: "
${username} ${name}
",
        insertTpl: "${atwho-at}${username}",
        callbacks: {
            remoteFilter: function(query, callback) {
                console.log('Query: ' + query);

                 //
                /*
                 if(query === null || query.length < 1){
                    return callback(null);
                 }*/

                $.ajax({
                    url: mOptions.path + "index.php?mq=" + query,
                    type: 'GET',
                    dataType: 'json',

                    success: function(data) {
                        callback(data);
                        console.log('Data: ' + data);
                    },

                    error: function() {
                        console.warn('Didn\'t Work');
                    },

                    beforeSend: function(xhr) {
                        //xhr.setRequestHeader('Authorization', localStorageService.get('authToken'));
                    }
                });
            }
        },
        searchKey: "username",
        limit: 5,
        maxLen: 15,
        minLen: 1,
        displayTimeout: 300,
        highlightFirst: true,

    });

});

Set atwho.js minLen param to 1 default was 0.


import _ from 'lodash';

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

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

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

Add validation for local component classes as well First try the official method, then fall-back to duck typing.


'use babel';

import { TextEditor } from 'atom';

export default class EditorInserter {
    setText(text) {
        this.insertionText = text;
    }

    performInsertion() {
        if (!this.insertionText) {
            console.error("No text to insert.");
            return;
        }
        this.textEditor = atom.workspace.getActiveTextEditor();
        //Break up lines
        var lines = this.insertionText.split("\n");
        //Start by inserting the first line
        this.textEditor.insertText(lines[0]);
        //Go back to the very beginning of the line and save the tab spacing
        var tabSpacing = "      ";
        //Now prepend this tab spacing to all other lines and concatenate them
        var concatenation = "";
        for (let i = 1; i < lines.length; i++) {
            concatenation += tabSpacing + lines[i];
        }
        //Finally, insert this concatenation to complete the insertion
        this.textEditor.insertText(concatenation);
    }
}

'use babel';

import { TextEditor } from 'atom';

export default class EditorInserter {
    setText(text) {
        this.insertionText = text;
    }

    performInsertion() {
        //Make sure there's actually text to insert before continuing
        if (!this.insertionText) {
            console.error("No text to insert.");
            return;
        }
        //Get the active text editor
        var textEditor = atom.workspace.getActiveTextEditor();
        //Get all current selections
        var selections = textEditor.getSelections();
        //Perform an insertion for each selection taking into account its own tabbing
        for (let i = 0; i < selections.length; i++) {
            //Break up lines
            var lines = this.insertionText.split("\n");
            //Go back to the very beginning of the line and save the tab spacing
            selections[i].selectToBeginningOfLine();
            var selectedText = selections[i].getText();
            var tabSpacing = "";
            for (let j = 0; j < selectedText.length; j++) {
                //Stop collecting tab characters when the first non-(hard/soft)tab character is reached
                if (selectedText[j] != "\t" && selectedText[j] != " ") {
                    break;
                }
                tabSpacing += selectedText[j];
            }
            //Place selection back to where it was initially
            selections[i].selectRight(selectedText.length);
            //Start by inserting the first line
            selections[i].insertText(lines[0]);
            //Now prepend this tab spacing to all other lines and concatenate them
            var concatenation = "";
            for (let j = 1; j < lines.length; j++) {
                concatenation += tabSpacing + lines[j];
            }
            //Finally, insert this concatenation to complete the insertion
            selections[i].insertText(concatenation);
        }
    }
}

Add tabbing to example insertion.


module.exports = function buildDistanceInWordsLocalizeFn () {
  var distanceInWordsLocale = {
    lessThanXSeconds: {
      one: 'less than a second',
      other: 'less than ${count} seconds'
    },

    halfAMinute: 'half a minute',

    lessThanXMinutes: {
      one: 'less than a minute',
      other: 'less than ${count} minutes'
    },

    xMinutes: {
      one: '1 minute',
      other: '${count} minutes'
    },

    aboutXHours: {
      one: 'about 1 hour',
      other: 'about ${count} hours'
    },

    xDays: {
      one: '1 day',
      other: '${count} days'
    },

    aboutXMonths: {
      one: 'about 1 month',
      other: 'about ${count} months'
    },

    xMonths: {
      one: '1 month',
      other: '${count} months'
    },

    aboutXYears: {
      one: 'about 1 year',
      other: 'about ${count} years'
    },

    overXYears: {
      one: 'over 1 year',
      other: 'over ${count} years'
    },

    almostXYears: {
      one: 'almost 1 year',
      other: 'almost ${count} years'
    }
  }

  return function (token, count) {
    if (count === undefined) {
      return distanceInWordsLocale[token]
    } else if (count === 1) {
      return distanceInWordsLocale[token].one
    } else {
      return distanceInWordsLocale[token].other.replace('${count}', count)
    }
  }
}

module.exports = function buildDistanceInWordsLocalizeFn () {
  var distanceInWordsLocale = {
    lessThanXSeconds: {
      one: 'less than a second',
      other: 'less than {{count}} seconds'
    },

    halfAMinute: 'half a minute',

    lessThanXMinutes: {
      one: 'less than a minute',
      other: 'less than {{count}} minutes'
    },

    xMinutes: {
      one: '1 minute',
      other: '{{count}} minutes'
    },

    aboutXHours: {
      one: 'about 1 hour',
      other: 'about {{count}} hours'
    },

    xDays: {
      one: '1 day',
      other: '{{count}} days'
    },

    aboutXMonths: {
      one: 'about 1 month',
      other: 'about {{count}} months'
    },

    xMonths: {
      one: '1 month',
      other: '{{count}} months'
    },

    aboutXYears: {
      one: 'about 1 year',
      other: 'about {{count}} years'
    },

    overXYears: {
      one: 'over 1 year',
      other: 'over {{count}} years'
    },

    almostXYears: {
      one: 'almost 1 year',
      other: 'almost {{count}} years'
    }
  }

  return function (token, count) {
    if (count === undefined) {
      return distanceInWordsLocale[token]
    } else if (count === 1) {
      return distanceInWordsLocale[token].one
    } else {
      return distanceInWordsLocale[token].other.replace('{{count}}', count)
    }
  }
}

Use handlebars string format in distanceInWords localize function


var PointView = Backbone.View.extend({

  initialize:function (options) {
    this.model = options.model;
    this.gmap = options.gmap;

    this.marker = new google.maps.Marker({
      position:this.model.getGLocation(),
      poiType:this.model.getPoiType(),
      visible:true,
      icon:this.model.get('pin'),
      map:null
    });

    // render the initial point state
    this.render();
  },

  render:function () {
    this.marker.setMap(this.gmap);
  },

  remove:function () {
    this.marker.setMap(null);
    this.marker = null;
  }
});

var PointView = Backbone.View.extend({

  initialize:function (options) {
    this.model = options.model;
    this.gmap = options.gmap;

    this.marker = new google.maps.Marker({
      position:this.model.getGLocation(),
      poiType:this.model.getPoiType(),
      visible:true,
      icon:this.model.get('pin'),
      map:null
    });

    var self = this;

    this.model.on('change:locations', function () {
      self.marker.setPosition(self.model.getGLocation());
    });

    // render the initial point state
    this.render();
  },

  render:function () {
    this.marker.setMap(this.gmap);
  },

  remove:function () {
    this.marker.setMap(null);
    this.marker = null;
  }
});

Change position on model location change.


'use strict';

const poolified = Symbol('poolified');

const mixFlag = { [poolified]: true };

const duplicate = (factory, n) => (
  new Array(n).fill().map(() => {
    const instance = factory();
    return Object.assign(instance, mixFlag);
  })
);

const provide = callback => item => {
  setImmediate(() => {
    callback(item);
  });
};

const poolify = (factory, min, norm, max) => {
  let allocated = norm;
  const pool = (par) => {
    if (par && par[poolified]) {
      const delayed = pool.delayed.shift();
      if (delayed) delayed(par);
      else pool.items.push(par);
      return;
    }
    if (pool.items.length < min && allocated < max) {
      const grow = Math.min(max - allocated, norm - pool.items.length);
      allocated += grow;
      const items = duplicate(factory, grow);
      pool.items.push(...items);
    }
    const res = pool.items.pop();
    if (!par) return res;
    const callback = provide(par);
    if (res) callback(res);
    else pool.delayed.push(callback);
  };
  return Object.assign(pool, {
    items: duplicate(factory, norm),
    delayed: []
  });
};

module.exports = {
  poolify,
};

'use strict';

const poolified = Symbol('poolified');

const mixFlag = { [poolified]: true };

const duplicate = (factory, n) => Array
  .from({ length: n }, factory)
  .map(instance => Object.assign(instance, mixFlag));

const provide = callback => item => {
  setImmediate(() => {
    callback(item);
  });
};

const poolify = (factory, min, norm, max) => {
  let allocated = norm;
  const pool = (par) => {
    if (par && par[poolified]) {
      const delayed = pool.delayed.shift();
      if (delayed) delayed(par);
      else pool.items.push(par);
      return;
    }
    if (pool.items.length < min && allocated < max) {
      const grow = Math.min(max - allocated, norm - pool.items.length);
      allocated += grow;
      const items = duplicate(factory, grow);
      pool.items.push(...items);
    }
    const res = pool.items.pop();
    if (!par) return res;
    const callback = provide(par);
    if (res) callback(res);
    else pool.delayed.push(callback);
  };
  return Object.assign(pool, {
    items: duplicate(factory, norm),
    delayed: []
  });
};

module.exports = {
  poolify,
};

Use Array.from({ length: n }, factory)


module.exports = function(grunt) {

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    qunit: {
      files: ['jstests/index.html']
    },
    jshint: {
      all: [
        'Gruntfile.js',
        'nbdiff/server/static/*.js'
      ],
      options: {
        jshintrc: '.jshintrc'
      }
    }
  });

  grunt.loadNpmTasks('grunt-contrib-qunit');
  grunt.loadNpmTasks('grunt-contrib-jshint');

  grunt.registerTask('test', ['qunit']);
  // Travis CI task.
  grunt.registerTask('travis', ['jshint', 'qunit']);

  grunt.registerTask('default', ['qunit', 'jshint']);

};

module.exports = function(grunt) {

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    qunit: {
      files: ['jstests/index.html']
    },
    jshint: {
      all: [
        'Gruntfile.js',
        'jstests/*.js',
        'nbdiff/server/static/*.js'
      ],
      options: {
        jshintrc: '.jshintrc'
      }
    }
  });

  grunt.loadNpmTasks('grunt-contrib-qunit');
  grunt.loadNpmTasks('grunt-contrib-jshint');

  grunt.registerTask('test', ['qunit']);
  // Travis CI task.
  grunt.registerTask('travis', ['jshint', 'qunit']);

  grunt.registerTask('default', ['qunit', 'jshint']);

};

Add jstests to jshint checks


module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json')
  });

  grunt.loadNpmTasks('grunt-release');

  grunt.registerTask('message', function() {
    grunt.log.writeln("use grunt release:{patch|minor|major}");
  });

  grunt.registerTask('default', ['message']);
};

module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    release: {
      options: {
        tagName: 'v<%= version %>'
      }
    }
  });

  grunt.loadNpmTasks('grunt-release');

  grunt.registerTask('message', function() {
    grunt.log.writeln("use grunt release:{patch|minor|major}");
  });

  grunt.registerTask('default', ['message']);
};

Use correct tag with grunt-release


module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    shell: {
      rebuild: {
        command: 'npm install --build-from-source'
      }
    },
    jasmine_node: {
      all: ['spec/']
    }
  });
  grunt.loadNpmTasks('grunt-jasmine-node');
  grunt.loadNpmTasks('grunt-shell');
  grunt.registerTask('default', ['shell:rebuild', 'jasmine_node:all']);
};

module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    shell: {
      rebuild: {
        command: 'npm install --build-from-source'
      }
    },
    jasmine_node: {
      all: ['spec/']
    }
  });
  grunt.loadNpmTasks('grunt-jasmine-node');
  grunt.loadNpmTasks('grunt-shell');

  grunt.registerTask('build', ['shell:rebuild']);
  grunt.registerTask('test', ['jasmine_node:all']);

  grunt.registerTask('default', ['build', 'test']);
};

Split Grunt tasks into “build” and “test”


module.exports = function(grunt) {
  grunt.initConfig({
    qunit: {
      files: ['test/index.html']
    }
  });

  grunt.loadNpmTasks('grunt-contrib-qunit');

  // Default task.
  grunt.registerTask('test', 'qunit');
  // Travis CI task.
  grunt.registerTask('travis', 'test');
};
// vim:ts=2:sw=2:et:

module.exports = function(grunt) {
  grunt.initConfig({
    qunit: {
      files: ['test/index.html']
    },
    jshint: {
      all: ['Gruntfile.js', 'yt-looper.js', 'test/**/*.js'],
      options: {
        '-W014': true, // ignore [W014] Bad line breaking
      },
    },
  });

  grunt.loadNpmTasks('grunt-contrib-qunit');
  grunt.loadNpmTasks('grunt-contrib-jshint');

  // Default task.
  grunt.registerTask('test', 'qunit');
  // Travis CI task.
  grunt.registerTask('travis', ['test','jshint']);
};
// vim:ts=2:sw=2:et:

Add jshint to a test suite


'use strict';

/**
 * Finder Model - Main model that uses FinderJS
 *
 * @constructor
 */
var FinderModel = function (arg) {

    // Assign `this` through `riot.observable()`
    var self = riot.observable(this);

    // Store args
    self.args = arg;

    // Create an instance of `Applait.Finder`
    self.finder = new Applait.Finder({
        type: "sdcard",
        minSearchLength: 2,
        debugMode: arg.debug
    });

    // Current search results
    self.searchResults = [];

    /**
     * Reset all internals
     */
    self.reset = function () {
        self.searchResults = [];
        self.finder.reset();
    };

    /**
     * Initiate search
     *
     * @param {string} key - The search string
     */
    self.search = function (key) {
        self.reset();
        self.finder.search(key);
    };

    /**
     * Subscribe to Finder's fileFound event
     */
    self.finder.on("fileFound", function (file) {
        self.searchResults.push(file);
    });

    /**
     * Subscribe to Finder's searchComplete event
     *
     * The `resultsFound` is triggered if any file is matched.
     * Else, `noResults` is triggered
     */
    self.finder.on("searchComplete", function () {
        if (self.searchResults.length && self.finder.filematchcount) {
            self.trigger("resultsFound");
        } else {
            self.trigger("noResults");
        }
    });

};

'use strict';

/**
 * Finder Model - Main model that uses FinderJS
 *
 * @constructor
 */
var FinderModel = function (arg) {

    // Assign `this` through `riot.observable()`
    var self = riot.observable(this);

    // Store args
    self.args = arg;

    // Create an instance of `Applait.Finder`
    self.finder = new Applait.Finder({
        type: "sdcard",
        minSearchLength: 2,
        debugMode: arg.debug
    });

    // Current search results
    self.searchResults = [];

    /**
     * Reset all internals
     */
    self.reset = function () {
        self.searchResults = [];
        self.finder.reset();
    };

    /**
     * Initiate search
     *
     * @param {string} key - The search string
     */
    self.search = function (key) {
        self.reset();
        self.finder.search(key);
    };

    /**
     * Subscribe to Finder's fileFound event
     */
    self.finder.on("fileFound", function (file) {
        self.searchResults.push(file);
    });

    /**
     * Subscribe to Finder's searchComplete event
     *
     * The `resultsFound` is triggered if any file is matched.
     * Else, `noResults` is triggered
     */
    self.finder.on("searchComplete", function () {
        if (self.searchResults.length && self.finder.filematchcount) {
            self.trigger("resultsFound");
        } else {
            self.trigger("noResults");
        }
    });

    /**
     * Provide a generic "load" method for routing
     */
    self.load = function (path) {
        self.trigger("load:" + path);
    };
};

Add a generic load method for routes


'use strict';

module.exports = {
  input:  'src/*.css',
  dir:    'dist',
  use:    [
    'postcss-import',
    'postcss-import-url',
    'postcss-custom-properties',
    'postcss-normalize-charset',
    'autoprefixer',
    'postcss-reporter'
  ],
  'postcss-import': {
    plugins: [
      require('postcss-copy')({
        src:  './src',
        dest: './dist',
        template: (file) => {
          return `assets/${file.name}.${file.ext}`;
        },
        relativePath: (dir, file, result, opts) => {
          return opts.dest;
        }
      })
    ]
  },
  'postcss-custom-properties': {
    preserve: true
  },
  autoprefixer: {
    browsers: [
      '> 1% in JP'
    ]
  },
  'postcss-reporter': {
    clearMessages: true
  }
}

'use strict';

module.exports = {
  input:  'src/*.css',
  dir:    'dist',
  use:    [
    'postcss-import',
    'postcss-custom-properties',
    'postcss-normalize-charset',
    'autoprefixer',
    'postcss-reporter'
  ],
  'postcss-import': {
    plugins: [
      require('postcss-import-url'),
      require('postcss-copy')({
        src:  './src',
        dest: './dist',
        template: file => {
          return `assets/${file.name}.${file.ext}`;
        },
        relativePath: (dir, file, result, opts) => {
          return opts.dest;
        }
      })
    ]
  },
  'postcss-custom-properties': {
    preserve: true
  },
  autoprefixer: {
    browsers: [
      '> 1% in JP'
    ]
  },
  'postcss-reporter': {
    clearMessages: true
  }
}

Change the time of loading external CSS


/*
 * A diff to be uploaded to a server.
 *
 * For now, this is used only for uploading new diffs.
 *
 * It is expected that parentObject will be set to a ReviewRequest instance.
 */
RB.Diff = RB.BaseResource.extend({
    rspNamespace: 'diff',

    getErrorString: function(rsp) {
        if (rsp.err.code == 207) {
            return 'The file "' + rsp.file + '" (revision ' + rsp.revision +
                    ') was not found in the repository';
        }

        return rsp.err.msg;
    }
});

/*
 * A diff to be uploaded to a server.
 *
 * For now, this is used only for uploading new diffs.
 *
 * It is expected that parentObject will be set to a ReviewRequest instance.
 */
RB.Diff = RB.BaseResource.extend({
    defaults: {
        diff: null,
        parentDiff: null,
        basedir: ''
    },

    payloadFileKeys: ['path', 'parent_diff_path'],

    rspNamespace: 'diff',

    getErrorString: function(rsp) {
        if (rsp.err.code == 207) {
            return 'The file "' + rsp.file + '" (revision ' + rsp.revision +
                    ') was not found in the repository';
        }

        return rsp.err.msg;
    },

    toJSON: function() {
        var payload;

        if (this.isNew()) {
            payload = {
                basedir: this.get('basedir'),
                path: this.get('diff'),
                parent_diff_path: this.get('parentDiff')
            };
        } else {
            payload = RB.BaseResource.prototype.toJSON.apply(this, arguments);
        }

        return payload;
    }
});

Enhance RB.Diff to be able to create new diffs.


//= require_self
//= require_tree ./volume_fading

pageflow.mediaPlayer.volumeFading = function(player) {
  if (!pageflow.browser.has('volume control support')) {
    return pageflow.mediaPlayer.volumeFading.noop(player);
  }
  else if (pageflow.audioContext.get()) {
    return pageflow.mediaPlayer.volumeFading.webAudio(
      player,
      pageflow.audioContext.get()
    );
  }
  else {
    return pageflow.mediaPlayer.volumeFading.interval(player);
  }
};

//= require_self
//= require_tree ./volume_fading

pageflow.mediaPlayer.volumeFading = function(player) {
  if (!pageflow.browser.has('volume control support')) {
    return pageflow.mediaPlayer.volumeFading.noop(player);
  }
  else if (pageflow.audioContext.get() && player.getMediaElement) {
    return pageflow.mediaPlayer.volumeFading.webAudio(
      player,
      pageflow.audioContext.get()
    );
  }
  else {
    return pageflow.mediaPlayer.volumeFading.interval(player);
  }
};

Use Web Audio volume fading only if player has getMediaElement


Spree.ready(function() {
  $('.store-credit-memo-row').each(function() {
    var row = this;
    var field = row.querySelector('[name="store_credit[memo]"]')
    var textDisplay = row.querySelector('.store-credit-memo-text')

    $(row).on('ajax:success', function(event, data) {
      row.classList.remove('editing');
      field.defaultValue = field.value;
      textDisplay.textContent = field.value;

      show_flash('success', data.message);
    }).on('ajax:error', function(event, xhr, status, error) {
      show_flash('error', xhr.responseJSON.message);
    });

    row.querySelector('.edit-memo').addEventListener('click', function() {
      row.classList.add('editing');
    });

    row.querySelector('.cancel-memo').addEventListener('click', function() {
      row.classList.remove('editing');
    });
  });
});

Spree.ready(function() {
  $('.store-credit-memo-row').each(function() {
    var row = this;
    var field = row.querySelector('[name="store_credit[memo]"]')
    var textDisplay = row.querySelector('.store-credit-memo-text')

    $(row).on('ajax:success', function(event, data) {
      row.classList.remove('editing');
      field.defaultValue = field.value;
      textDisplay.textContent = field.value;

      if (typeof data !== "undefined") {
        // we are using jquery_ujs
        message = data.message
      } else {
        // we are using rails-ujs
        message = event.detail[0].message
      }

      show_flash('success', message);
    }).on('ajax:error', function(event, xhr, status, error) {
      if (typeof xhr !== "undefined") {
        // we are using jquery_ujs
        message = xhr.responseJSON.message
      } else {
        // we are using rails-ujs
        message = event.detail[0].message
      }

      show_flash('error', message);
    });

    row.querySelector('.edit-memo').addEventListener('click', function() {
      row.classList.add('editing');
    });

    row.querySelector('.cancel-memo').addEventListener('click', function() {
      row.classList.remove('editing');
    });
  });
});

Allow admin store credit memo to be changed using rails-ujs


var Bot = require( 'nodemw' ),
	readline = require( 'readline' ),
	c = require( 'ansi-colors' ),
	rl = readline.createInterface( {
		input: process.stdin,
		output: process.stdout
	} ),
	client = new Bot( {
		protocol: 'https',
		server: 'dev.fandom.com',
		path: ''
	} ),
	params = {
		action: 'scribunto-console',
		title: 'Module:CLI/testcases/title',
		clear: true
	};

function call( err, info, next, data ) {
	if ( err ) {
		console.error( err );
	} else if ( data.type === 'error' ) {
		console.error( data.message );
	} else {
		console.log( data.print );
	}
}

function cli( input ) {
	params.question = input;
	client.api.call( params, call );
}

function session( err, content ) {
	params.content = content;
	console.log( c.gray('* The module exports are available as the variable "p", including unsaved modifications.' ) );
	console.log( c.gray('* Precede a line with "=" to evaluate it as an expression, or use print().' ) );
	console.log( c.gray('* Use mw.log() in module code to send messages to this console.' ) );
	rl.on( 'line', cli );
}

client.getArticle( params.title, session );

var Bot = require( 'nodemw' ),
	readline = require( 'readline' ),
	c = require( 'ansicolors' ),
	rl = readline.createInterface( {
		input: process.stdin,
		output: process.stdout
	} ),
	client = new Bot( {
		protocol: 'https',
		server: 'dev.fandom.com',
		path: ''
	} ),
	params = {
		action: 'scribunto-console',
		title: 'Module:CLI/testcases/title',
		clear: true
	};

function call( err, info, next, data ) {
	if ( err ) {
		console.error( err );
	} else if ( data.type === 'error' ) {
		console.error( data.message );
	} else {
		console.log( data.print );
	}
}

function cli( input ) {
	params.question = input;
	client.api.call( params, call );
}

function session( err, content ) {
	params.content = content;
	console.log( c.green('* The module exports are available as the variable "p", including unsaved modifications.' ) );
	console.log( c.green('* Precede a line with "=" to evaluate it as an expression, or use print().' ) );
	console.log( c.green('* Use mw.log() in module code to send messages to this console.' ) );
	rl.on( 'line', cli );
}

client.getArticle( params.title, session );

Use green for console intro instead


// ==UserScript==
// @name        Google Service List
// @namespace   wsmwason.google.service.list
// @description List all Google services on support page, and auto redirect to product url.
// @include     https://support.google.com/*
// @require     http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js
// @auther      wsmwason
// @version     1.0
// @license     MIT
// @grant       none
// ==/UserScript==

// hide show all icon
$('.show-all').hide();

// display hide container
$('.all-hc-container').addClass('all-hc-container-shown');

// add link redirect param for next page
$('a').each(function(){
    var stid = $(this).attr('st-id');
    if (typeof stid !== typeof undefined && stid !== false) {
        $(this).attr('href', $(this).attr('href')+'&redirect=1').attr('target', '_blank');
    }
});

// auto redirect to product url
if (location.search.indexOf('redirect=1') > 0) {
    // find product-icon link
    var productIcon = $('a.product-icon');
    if (productIcon.length == 1) {
        var productUrl = productIcon.attr('href');
        location.href = productUrl;
    }
}

// ==UserScript==
// @name        Google Services List
// @namespace   wsmwason.google.services.list
// @description List all Google services on support page, and auto redirect to product url.
// @include     https://support.google.com/*
// @require     http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js
// @auther      wsmwason
// @version     1.1
// @license     MIT
// @grant       none
// ==/UserScript==

// hide show all icon
$('.show-all').hide();

// display hide container
$('.all-hc-container').addClass('all-hc-container-shown');

// add link redirect param for next page
$('a').each(function(){
    var stid = $(this).attr('st-id');
    if (typeof stid !== typeof undefined && stid !== false) {
        $(this).attr('href', $(this).attr('href')+'&redirect=1').attr('target', '_blank');
    }
});

// auto redirect to product url
if (location.search.indexOf('redirect=1') > 0) {
    // find product-icon link
    var productIcon = $('a.product-icon');
    if (productIcon.length == 1) {
        var productUrl = productIcon.attr('href');
        location.href = productUrl;
    }
}

Update script name and namespace


$.jlm.component('WriteNew', 'wild_posts.wf_index, wild_posts.wf_edit, wild_pages.wf_index, wild_pages.wf_edit', function() {

    $('#sidebar .add').click(function() {
        var buttonEl = $(this);
        var formAction = buttonEl.attr('href');

        var templatePath = 'posts/new_post';
        var parentPageOptions = null;
        if ($.jlm.params.controller == 'wild_pages') {
            templatePath = 'pages/new_page';
            parentPageOptions = $('.all-page-parents').html();
            parentPageOptions = parentPageOptions.replace('[parent_id_options]', '[parent_id]');
        }

        var dialogEl = $($.jlm.template(templatePath, { action: formAction, parentPageOptions: parentPageOptions }));

        var contentEl = $('#content-pad');

        contentEl.append(dialogEl);

        var toHeight = 230;

        var hiddenContentEls = contentEl.animate({
            height: toHeight
        }, 600).children().not(dialogEl).hide();

        $('.input input', dialogEl).focus();

        // Bind cancel link
        $('.cancel-edit a', dialogEl).click(function() {
            dialogEl.remove();
            hiddenContentEls.show();
            contentEl.height('auto');
            return false;
        });

        // Create link
        $('.submit input', dialogEl).click(function() {
            $(this).attr('disabled', 'disabled').attr('value', '<l18n>Saving...</l18n>');
            return true;
        });

        return false;
    });

});

$.jlm.component('WriteNew', 'wild_posts.wf_index, wild_posts.wf_edit, wild_pages.wf_index, wild_pages.wf_edit', function() {

    $('#sidebar .add').click(function() {
        var buttonEl = $(this);
        var formAction = buttonEl.attr('href');

        var templatePath = 'posts/new_post';
        var parentPageOptions = null;
        if ($.jlm.params.controller == 'wild_pages') {
            templatePath = 'pages/new_page';
            parentPageOptions = $('.all-page-parents').html();
            parentPageOptions = parentPageOptions.replace('[Page]', '[WildPage]');
            parentPageOptions = parentPageOptions.replace('[parent_id_options]', '[parent_id]');
        }

        var dialogEl = $($.jlm.template(templatePath, { action: formAction, parentPageOptions: parentPageOptions }));

        var contentEl = $('#content-pad');

        contentEl.append(dialogEl);

        var toHeight = 230;

        var hiddenContentEls = contentEl.animate({
            height: toHeight
        }, 600).children().not(dialogEl).hide();

        $('.input input', dialogEl).focus();

        // Bind cancel link
        $('.cancel-edit a', dialogEl).click(function() {
            dialogEl.remove();
            hiddenContentEls.show();
            contentEl.height('auto');
            return false;
        });

        // Create link
        $('.submit input', dialogEl).click(function() {
            $(this).attr('disabled', 'disabled').attr('value', '<l18n>Saving...</l18n>');
            return true;
        });

        return false;
    });

});

Write new page parent select box bug fixed


(function($) {
/*
		======== A Handy Little QUnit Reference ========
		http://docs.jquery.com/QUnit

		Test methods:
			expect(numAssertions)
			stop(increment)
			start(decrement)
		Test assertions:
			ok(value, [message])
			equal(actual, expected, [message])
			notEqual(actual, expected, [message])
			deepEqual(actual, expected, [message])
			notDeepEqual(actual, expected, [message])
			strictEqual(actual, expected, [message])
			notStrictEqual(actual, expected, [message])
			raises(block, [expected], [message])
*/

	module("jQuery#idle-timer");

	asyncTest( "default behavior", function() {
		expect( 1 );

		$( document ).on( "idle.idleTimer", function(){
			ok( true, "idleTime fires at document by default" );
			start();
		});
		$.idleTimer( 100 );
	});

}(jQuery));

(function($) {
/*
		======== A Handy Little QUnit Reference ========
		http://docs.jquery.com/QUnit

		Test methods:
			expect(numAssertions)
			stop(increment)
			start(decrement)
		Test assertions:
			ok(value, [message])
			equal(actual, expected, [message])
			notEqual(actual, expected, [message])
			deepEqual(actual, expected, [message])
			notDeepEqual(actual, expected, [message])
			strictEqual(actual, expected, [message])
			notStrictEqual(actual, expected, [message])
			raises(block, [expected], [message])
*/

	module("jQuery#idle-timer");

	asyncTest( "default behavior", function() {
		expect( 1 );

		$( document ).on( "idle.idleTimer", function(){
			ok( true, "idleTime fires at document by default" );
			start();
			$.idleTimer( "destroy" );
		});
		$.idleTimer( 100 );
	});

}(jQuery));

Destroy idle timer instance after test has finished


'use strict';

var assert = require('assert');
var buildTemplateCompiler = require('../helpers/template-compiler');

describe('Ember template compiler', function() {
  var templateCompiler;

  beforeEach(function() {
    templateCompiler = buildTemplateCompiler();
  });

  it('sanity: compiles templates', function() {
    var template = templateCompiler.precompile('

');
    assert.ok(template, 'template is created from precompile');
  });

  it('sanity: loads plugins on the template compiler', function() {
    var instanceCount = 0;
    var NoopPlugin = function(){
      instanceCount++;
    };
    NoopPlugin.prototype.transform = function(ast) {
      return ast;
    };
    templateCompiler.registerPlugin('ast', NoopPlugin);
    templateCompiler.precompile('

');
    assert.equal(instanceCount, 1, 'registered plugins are instantiated');
  });
});

'use strict';

var assert = require('assert');
var _compile = require('htmlbars').compile;

describe('Ember template compiler', function() {
  var astPlugins;

  function compile(template) {
    return _compile(template, {
      plugins: {
        ast: astPlugins
      }
    });
  }

  beforeEach(function() {
    astPlugins = [];
  });

  it('sanity: compiles templates', function() {
    var template = compile('

');
    assert.ok(template, 'template is created');
  });

  it('sanity: loads plugins on the template compiler', function() {
    var instanceCount = 0;
    var NoopPlugin = function(){
      instanceCount++;
    };
    NoopPlugin.prototype.transform = function(ast) {
      return ast;
    };
    astPlugins.push(NoopPlugin);
    compile('

');

    assert.equal(instanceCount, 1, 'registered plugins are instantiated');
  });
});

Fix issues with HTMLBars parser transition.


'use strict'
var path = require('path')

module.exports = function (top, buildpath, pkg, log, next) {
  if (pkg.package.version !== pkg.oldPkg.package.version)
    log.warn('update-linked', path.relative(top, pkg.path), 'needs updating to', pkg.package.version,
      'from', pkg.oldPkg.package.version, "but we can't, as it's a symlink")
  next()
}

'use strict'
var path = require('path')

module.exports = function (top, buildpath, pkg, log, next) {
  if (pkg.package.version !== pkg.oldPkg.package.version) {
    log.warn('update-linked', path.relative(top, pkg.path), 'needs updating to', pkg.package.version,
      'from', pkg.oldPkg.package.version, "but we can't, as it's a symlink")
  }
  next()
}

Fix up style to meet requirements for build


exports.runWhenTableIs = function (tableName, desiredStatus, db, callback) {

    var describeParams = {};
    describeParams.TableName = tableName;

    db.describeTable(describeParams, function (err, data) {
        if (data.Table.TableStatus === desiredStatus) {
            callback();
        } else{
            var waitTimeMs = 1000;
            setTimeout(function () {
                console.log('table status is ' + data.Table.TableStatus);
                console.log('waiting ' + waitTimeMs + 'ms for table status to be ' + desiredStatus);
                exports.runWhenTableIs(tableName, desiredStatus, db, callback);
            }, waitTimeMs);

        }
    });

}

exports.runWhenTableIs = function (tableName, desiredStatus, db, callback) {

    var describeParams = {};
    describeParams.TableName = tableName;

    db.describeTable(describeParams, function (err, data) {
        if (err) {
            console.log(err);
        } else if (data.Table.TableStatus === desiredStatus) {
            callback();
        } else{
            var waitTimeMs = 1000;
            setTimeout(function () {
                console.log('table status is ' + data.Table.TableStatus);
                console.log('waiting ' + waitTimeMs + 'ms for table status to be ' + desiredStatus);
                exports.runWhenTableIs(tableName, desiredStatus, db, callback);
            }, waitTimeMs);

        }
    });

}

Check for error and print when waiting for a particular table status.


/**
 * Finnish translation for bootstrap-datepicker
 * Jaakko Salonen <https://github.com/jsalonen>
 */
;(function($){
	$.fn.datepicker.dates['fi'] = {
		days: ["sunnuntai", "maanantai", "tiistai", "keskiviikko", "torstai", "perjantai", "lauantai", "sunnuntai"],
		daysShort: ["sun", "maa", "tii", "kes", "tor", "per", "lau", "sun"],
		daysMin: ["su", "ma", "ti", "ke", "to", "pe", "la", "su"],
		months: ["tammikuu", "helmikuu", "maaliskuu", "huhtikuu", "toukokuu", "kesäkuu", "heinäkuu", "elokuu", "syyskuu", "lokakuu", "marraskuu", "joulukuu"],
		monthsShort: ["tam", "hel", "maa", "huh", "tou", "kes", "hei", "elo", "syy", "lok", "mar", "jou"],
		today: "tänään"
	};
}(jQuery));

/**
 * Finnish translation for bootstrap-datepicker
 * Jaakko Salonen <https://github.com/jsalonen>
 */
;(function($){
	$.fn.datepicker.dates['fi'] = {
		days: ["sunnuntai", "maanantai", "tiistai", "keskiviikko", "torstai", "perjantai", "lauantai", "sunnuntai"],
		daysShort: ["sun", "maa", "tii", "kes", "tor", "per", "lau", "sun"],
		daysMin: ["su", "ma", "ti", "ke", "to", "pe", "la", "su"],
		months: ["tammikuu", "helmikuu", "maaliskuu", "huhtikuu", "toukokuu", "kesäkuu", "heinäkuu", "elokuu", "syyskuu", "lokakuu", "marraskuu", "joulukuu"],
		monthsShort: ["tam", "hel", "maa", "huh", "tou", "kes", "hei", "elo", "syy", "lok", "mar", "jou"],
		today: "tänään",
		weekStart: 1,
		format: "d.m.yyyy"
	};
}(jQuery));

Fix Finnish week start to Monday, add format


/**
 * Macedonian translation for bootstrap-datepicker
 * Marko Aleksic <psybaron@gmail.com>
 */
;(function($){
    $.fn.datepicker.dates['mk'] = {
        days: ["Недела", "Понеделник", "Вторник", "Среда", "Четврток", "Петок", "Сабота", "Недела"],
        daysShort: ["Нед", "Пон", "Вто", "Сре", "Чет", "Пет", "Саб", "Нед"],
        daysMin: ["Не", "По", "Вт", "Ср", "Че", "Пе", "Са", "Не"],
        months: ["Јануари", "Февруари", "Март", "Април", "Мај", "Јуни", "Јули", "Август", "Септември", "Октомври", "Ноември", "Декември"],
        monthsShort: ["Јан", "Фев", "Мар", "Апр", "Мај", "Јун", "Јул", "Авг", "Сеп", "Окт", "Ное", "Дек"],
        today: "Денес",
        format: "dd.MM.yyyy"
    };
}(jQuery));

/**
 * Macedonian translation for bootstrap-datepicker
 * Marko Aleksic <psybaron@gmail.com>
 */
;(function($){
    $.fn.datepicker.dates['mk'] = {
        days: ["Недела", "Понеделник", "Вторник", "Среда", "Четврток", "Петок", "Сабота", "Недела"],
        daysShort: ["Нед", "Пон", "Вто", "Сре", "Чет", "Пет", "Саб", "Нед"],
        daysMin: ["Не", "По", "Вт", "Ср", "Че", "Пе", "Са", "Не"],
        months: ["Јануари", "Февруари", "Март", "Април", "Мај", "Јуни", "Јули", "Август", "Септември", "Октомври", "Ноември", "Декември"],
        monthsShort: ["Јан", "Фев", "Мар", "Апр", "Мај", "Јун", "Јул", "Авг", "Сеп", "Окт", "Ное", "Дек"],
        today: "Денес",
        format: "dd.mm.yyyy"
    };
}(jQuery));

Change month format from 'MM' to 'mm'.


import { assertThat, equalTo, containsString } from 'hamjest';

describe('The core function, `assertThat()`', () => {
  it('is a function', () => {
    const typeOfAssertThat = typeof assertThat;
    assertThat(typeOfAssertThat, equalTo('function'));
  });
  describe('requires at least two params', () => {
    it('1st: the actual value', () => {
      assertThat('actual', equalTo('actual'));
    });
    it('2nd: a matcher for the expected value', () => {
      const matcher = equalTo('expected');
      assertThat('expected', matcher);
    });
    describe('the optional 3rd param', () => {
      it('goes first(!), and is the assertion reason', () => {
        const reason = 'This is the reason, the first `assertThat()` throws as part of its message.';
        try {
          assertThat(reason, true, equalTo(false));
        } catch (e) {
          assertThat(e.message, containsString(reason));
        }
      });
    });
  });
});

import {
  assertThat, equalTo,
  containsString, throws, returns,
} from 'hamjest';

describe('The core function, `assertThat()`', () => {
  it('is a function', () => {
    const typeOfAssertThat = typeof assertThat;
    assertThat(typeOfAssertThat, equalTo('function'));
  });
  describe('requires at least two params', () => {
    it('1st: the actual value', () => {
      assertThat('actual', equalTo('actual'));
    });
    it('2nd: a matcher for the expected value', () => {
      const matcher = equalTo('expected');
      assertThat('expected', matcher);
    });
    describe('the optional 3rd param', () => {
      it('goes first(!), and is the assertion reason', () => {
        const reason = 'This is the reason, the first `assertThat()` throws as part of its message.';
        try {
          assertThat(reason, true, equalTo(false));
        } catch (e) {
          assertThat(e.message, containsString(reason));
        }
      });
    });
  });

  describe('does under the hood', () => {
    it('nothing when actual and expected match (using the given matcher)', () => {
      const passingTest = () => assertThat(true, equalTo(true));
      assertThat(passingTest, returns());
    });
    it('throws an assertion, when actual and expected don`t match', () => {
      const failingTest = () => assertThat(false, equalTo(true));
      assertThat(failingTest, throws());
    });
  });
});

Add a little under-the-hood tests.


import Project from '../../src/project/project'
import Asset from '../../src/project/asset'
import Composition from '../../src/project/composition'
import TimeLane from '../../src/project/timelane'
import Layer from '../../src/project/layer'

describe('project structure specs', () => {
    describe('Project', () => {
        let project

        beforeEach(() => {
            project = new Project()
        })

        afterEach(() => {
            project = null
        })

        it('project construction flow', () => {
            project.assets.add(new Asset)
            project.assets.add(new Asset)

            const comp1 = new Composition
            project.compositions.add(comp1)

            const lane1 = new TimeLane
            comp1.timelanes.add(lane1)

            lane1.layers.add(new Layer)
        })

        it('correctry serialize/deserialize the project', () => {
            const comp1 = new Composition
            project.compositions.add(comp1)

            const lane1 = new TimeLane
            comp1.timelanes.add(lane1)

            const pbson = project.serialize()
            expect(Project.deserialize(pbson).toJSON()).to.eql(project.toJSON())
        })
    })
})

import Project from '../../src/project/project'
import Asset from '../../src/project/asset'
import Composition from '../../src/project/composition'
import TimeLane from '../../src/project/timelane'
import Clip from '../../src/project/clip'

describe('project structure specs', () => {
    describe('Project', () => {
        let project

        beforeEach(() => {
            project = new Project()
        })

        afterEach(() => {
            project = null
        })

        it('project construction flow', () => {
            project.assets.add(new Asset)
            project.assets.add(new Asset)

            const comp1 = new Composition
            project.compositions.add(comp1)

            const lane1 = new TimeLane
            comp1.timelanes.add(lane1)

            lane1.layers.add(new Clip)
        })

        it('correctry serialize/deserialize the project', () => {
            const comp1 = new Composition
            project.compositions.add(comp1)

            const lane1 = new TimeLane
            comp1.timelanes.add(lane1)

            const pbson = project.serialize()
            expect(Project.deserialize(pbson).toJSON()).to.eql(project.toJSON())
        })
    })
})

Replace `Layer` to `Clip` on Project structure spec`


module.exports = {
    "open index page": function(browser) {
        browser
            .url(browser.globals.url)
            .waitForElementVisible("#index-view", 3000)
            .assert.containsText("#header .logo:nth-child(2)", "Color Themes")
            .waitForElementVisible(".pending-icon", 100)
            .waitForElementVisible(".preview-list", 5000)
            .assert.containsText(".pager .current", "1")
            .end();
    }
};

module.exports = {
    "open index page": function(browser) {
        browser
            .url(browser.globals.url)
            .waitForElementVisible("#index-view", 3000)
            .assert.containsText("#header .logo:nth-child(2)", "Color Themes")
            .waitForElementVisible(".pending-icon", 100)
            .waitForElementVisible(".preview-list", 5000)
            .assert.containsText(".pager .current", "1")
            .end();
    },

    "open next page": function(browser) {
        browser
            .url(browser.globals.url)
            .waitForElementVisible(".pager a:nth-child(2)",5000)
            .click(".pager a:nth-child(2)")
            .waitForElementVisible(".preview-list", 5000)
            .assert.containsText(".pager .current", "2")
            .end();
    }
};

Add new test for opening pages


/**
 * WordPress dependencies
 */
import { activatePlugin, visitAdminPage } from '@wordpress/e2e-test-utils';

/**
 * The allow list of AMP modes.
 */
export const allowedAMPModes = {
	standard: 'standard',
	transitional: 'transitional',
	reader: 'disabled',
};

/**
 * Activate AMP and set it to the correct mode.
 *
 * @param {string} mode The mode to set AMP to. Possible value of standard, transitional or reader.
 */
export const activateAMPWithMode = async ( mode ) => {
	await activatePlugin( 'amp' );
	await setAMPMode( mode );
};

/**
 * Set AMP Mode
 *
 * @param {string} mode The mode to set AMP to. Possible value of standard, transitional or reader.
 */
export const setAMPMode = async ( mode ) => {
	// Test to be sure that the passed mode is known.
	expect( allowedAMPModes ).toHaveProperty( mode );
	// Set the AMP mode
	await visitAdminPage( 'admin.php', 'page=amp-options' );
	await expect( page ).toClick( `#theme_support_${ allowedAMPModes[ mode ] }` );
	await expect( page ).toClick( '#submit' );
	await page.waitForNavigation();
};

/**
 * WordPress dependencies
 */
import { activatePlugin, visitAdminPage } from '@wordpress/e2e-test-utils';

/**
 * The allow list of AMP modes.
 */
export const allowedAMPModes = {
	primary: 'standard',
	secondary: 'transitional',
	standard: 'standard',
	transitional: 'transitional',
	reader: 'disabled',
};

/**
 * Activate AMP and set it to the correct mode.
 *
 * @param {string} mode The mode to set AMP to. Possible value of standard, transitional or reader.
 */
export const activateAMPWithMode = async ( mode ) => {
	await activatePlugin( 'amp' );
	await setAMPMode( mode );
};

/**
 * Set AMP Mode
 *
 * @param {string} mode The mode to set AMP to. Possible value of standard, transitional or reader.
 */
export const setAMPMode = async ( mode ) => {
	// Test to be sure that the passed mode is known.
	expect( allowedAMPModes ).toHaveProperty( mode );
	// Set the AMP mode
	await visitAdminPage( 'admin.php', 'page=amp-options' );
	await expect( page ).toClick( `#theme_support_${ allowedAMPModes[ mode ] }` );
	await expect( page ).toClick( '#submit' );
	await page.waitForNavigation();
};

Add aliase for primary and secondary AMP modes.