(function () {
    'use strict';

     /* ngInject */
    function RowDetail($compile) {
        // TODO: Animate the row appearing
        var template = [
            '
',
                '
',
                    '
',
                        '{{ ::feature.note }}',
                    '
',
                    '
',
                        '{{ ::warning.note }}',
                    '
',
                '
',
            '
'
        ].join('');

        var module = {
            restrict: 'A',
            scope: false,
            link: link,
        };

        return module;

        function link(scope, element) {

            if (!hasDetails(scope.item)) {
                return;
            }

            scope.visible = false;

            var row = $compile(template)(scope);
            element.after(row);

            element.click(function () {
                scope.visible = !scope.visible;
                scope.$apply();
            });

            function hasDetails(item) {
                var directions = item.directions;
                return directions && (directions.features.length || directions.warnings.length);
            }
        }
    }

    angular.module('nih.views.routing')
    .directive('rowDetail', RowDetail);

})();

(function () {
    'use strict';

     /* ngInject */
    function RowDetail($compile) {
        // TODO: Animate the row appearing
        var template = [
            '
',
                '
',
                    '
',
                        ' {{ ::feature.note }}',
                    '
',
                    '
',
                        ' {{ ::warning.note }}',
                    '
',
                '
',
            '
'
        ].join('');

        var module = {
            restrict: 'A',
            scope: false,
            link: link,
        };

        return module;

        function link(scope, element) {

            if (!hasDetails(scope.item)) {
                return;
            }

            scope.visible = false;

            var row = $compile(template)(scope);
            element.find('td:last-child').append(row);

            element.click(function () {
                scope.visible = !scope.visible;
                scope.$apply();
            });

            function hasDetails(item) {
                var directions = item.directions;
                return directions && (directions.features.length || directions.warnings.length);
            }
        }
    }

    angular.module('nih.views.routing')
    .directive('rowDetail', RowDetail);

})();

Move step-by-step direction details into element


'use strict';

/**
 * @note Returns texture immediately, but doesn't load till later.
 */
var ROSETexLoader = {};
ROSETexLoader.load = function(path, callback) {
  var tex = DDS.load(path, function() {
    if (callback) {
      callback();
    }
  });
  tex.minFilter = tex.magFilter = THREE.LinearFilter;
  return tex;
};

'use strict';

/**
 * @note Returns texture immediately, but doesn't load till later.
 */
var ROSETexLoader = {};
ROSETexLoader.load = function(path, callback) {
  var tex = DDS.load(path, function() {
    if (callback) {
      callback();
    }
  });
  tex.minFilter = tex.magFilter = THREE.LinearFilter;
  tex.path = path;
  return tex;
};

Set .path in dds textures for debugging.


var _ = require('lazy.js');

var square = function square (n) {
  return Math.pow(n, 2);
};

var sum = function sum (n, m) {
  return n + m;
};

var sumOfSquares = function sumOfSquares (range) {
  return range.map(square).reduce(sum);
};

var squareOfSum = function squareOfSum (range) {
  return square(range.reduce(sum), 2);
};

var difference = function difference (range) {
  return squareOfSum(range) - sumOfSquares(range);
};

console.log(difference(_.range(1,11)));
console.log(difference(_.range(1,101)));

Add initial global JS solution for problem 6


// Copyright 2011 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview Definition of the disposable interface.  A disposable object
 * has a dispose method to to clean up references and resources.
 * @author nnaze@google.com (Nathan Naze)
 */

goog.provide('goog.disposable.IDisposable');

/**
 * Interface for a disposable object.  If a instance requires cleanup
 * (references COM objects, DOM notes, or other disposable objects), it should
 * implement this interface (it may subclass goog.Disposable).
 * @interface
 */
goog.disposable.IDisposable = function() {};

/**
 * Disposes of the object and its resources.
 * @return {void} Nothing.
 */
goog.disposable.IDisposable.prototype.dispose = goog.abstractMethod;

/**
 * @return {boolean} Whether the object has been disposed of.
 */
goog.disposable.IDisposable.prototype.isDisposed = goog.abstractMethod;

// Copyright 2011 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview Definition of the disposable interface.  A disposable object
 * has a dispose method to to clean up references and resources.
 * @author nnaze@google.com (Nathan Naze)
 */

goog.provide('goog.disposable.IDisposable');

/**
 * Interface for a disposable object.  If a instance requires cleanup
 * (references COM objects, DOM nodes, or other disposable objects), it should
 * implement this interface (it may subclass goog.Disposable).
 * @record
 */
goog.disposable.IDisposable = function() {};

/**
 * Disposes of the object and its resources.
 * @return {void} Nothing.
 */
goog.disposable.IDisposable.prototype.dispose = goog.abstractMethod;

/**
 * @return {boolean} Whether the object has been disposed of.
 */
goog.disposable.IDisposable.prototype.isDisposed = goog.abstractMethod;

Allow object literals to be IDisposable


'use strict';

/**
 * Imports.
 */
const express = require('express');

/**
 * Initialize Express.
 */
const app = express();

// Setup locals variable in config/index.js.
app.locals = require('./config');

// Configure express app based on local configuration.
app.set('env', app.locals.express.env);

/**
 * Routing.
 */
// Root:
app.get('/', (req, res) => {
  res.send('Hi, I\'m Blink!');
});

// Api root:
app.use('/api', require('./api'));

// API Version 1
app.use('/api/v1', require('./api/v1'));

/**
 * Listen.
 */
app.listen(app.locals.express.port, () => {
  app.locals.logger.info(`Blink is listening on port:${app.locals.express.port} env:${app.get('env')}`);
});

module.exports = app;

'use strict';

/**
 * Imports.
 */
const express = require('express');
const http = require('http');

/**
 * Initialize Express.
 */
const app = express();

// Setup locals variable in config/index.js.
app.locals = require('./config');

// Configure express app based on local configuration.
app.set('env', app.locals.express.env);

/**
 * Routing.
 */
// Root:
app.get('/', (req, res) => {
  res.send('Hi, I\'m Blink!');
});

// Api root:
app.use('/api', require('./api'));

// API Version 1
app.use('/api/v1', require('./api/v1'));

/**
 * Create server.
 */
const server = http.createServer(app);
server.listen(app.locals.express.port, () => {
  const address = server.address();
  app.locals.logger.info(`Blink is listening on port:${address.port} env:${app.locals.express.env}`);
});

module.exports = app;

Print actual port number in server init log


#!/usr/bin/env node

/*
* SQL API loader
* ===============
*
* node app [environment]
*
* environments: [development, test, production]
*
*/
var _ = require('underscore');

// sanity check arguments
var ENV = process.argv[2];
if (ENV != 'development' && ENV != 'production' && ENV != 'test') {
  console.error("\n./app [environment]");
  console.error("environments: [development, test, production]");
  process.exit(1);
}

// set Node.js app settings and boot
global.settings  = require(__dirname + '/config/settings');
var env          = require(__dirname + '/config/environments/' + ENV);
_.extend(global.settings, env);

// kick off controller
var app = require(global.settings.app_root + '/app/controllers/app');
app.listen(global.settings.node_port, global.settings.node_host, function() {
  console.log("CartoDB SQL API listening on " + global.settings.node_host + ":" + global.settings.node_port);
});

#!/usr/bin/env node

/*
* SQL API loader
* ===============
*
* node app [environment]
*
* environments: [development, test, production]
*
*/
var _ = require('underscore');

// sanity check arguments
var ENV = process.argv[2];
if (ENV != 'development' && ENV != 'production' && ENV != 'test') {
  console.error("\n./app [environment]");
  console.error("environments: [development, test, production]");
  process.exit(1);
}

// set Node.js app settings and boot
global.settings  = require(__dirname + '/config/settings');
var env          = require(__dirname + '/config/environments/' + ENV);
_.extend(global.settings, env);

// kick off controller
if ( ! global.settings.base_url ) global.settings.base_url = '/api/*';
var app = require(global.settings.app_root + '/app/controllers/app');
app.listen(global.settings.node_port, global.settings.node_host, function() {
  console.log("CartoDB SQL API listening on " +
      global.settings.node_host + ":" + global.settings.node_port +
      " with base_url " + global.settings.base_url);
});

Use a default base_url when not specified in config file


export default [
  'config',
  'hs.common.laymanService',
  function (config, laymanService) {
    const me = this;

    function getItemsPerPageConfig(ds) {
      return angular.isDefined(ds.paging) &&
        angular.isDefined(ds.paging.itemsPerPage)
        ? ds.paging.itemsPerPage
        : config.dsPaging || 20;
    }

    angular.extend(me, {
      endpoints: [
        ...(config.status_manager_url
          ? {
              type: 'statusmanager',
              title: 'Status manager',
              url: config.status_manager_url,
            }
          : []),
        ...(config.datasources || []).map((ds) => {
          return {
            url: ds.url,
            type: ds.type,
            title: ds.title,
            datasourcePaging: {
              start: 0,
              limit: getItemsPerPageConfig(ds),
              loaded: false,
            },
            compositionsPaging: {
              start: 0,
              limit: getItemsPerPageConfig(ds),
            },
            paging: {
              itemsPerPage: getItemsPerPageConfig(ds),
            },
            user: ds.user,
            liferayProtocol: ds.liferayProtocol,
            originalConfiguredUser: ds.user,
            getCurrentUserIfNeeded: laymanService.getCurrentUserIfNeeded,
          };
        }),
      ],
    });
    return me;
  },
];

export default [
  'config',
  'hs.common.laymanService',
  function (config, laymanService) {
    const me = this;

    function getItemsPerPageConfig(ds) {
      return angular.isDefined(ds.paging) &&
        angular.isDefined(ds.paging.itemsPerPage)
        ? ds.paging.itemsPerPage
        : config.dsPaging || 20;
    }

    angular.extend(me, {
      endpoints: [
        ...(config.status_manager_url
          ? [
              {
                type: 'statusmanager',
                title: 'Status manager',
                url: config.status_manager_url,
              },
            ]
          : []),
        ...(config.datasources || []).map((ds) => {
          return {
            url: ds.url,
            type: ds.type,
            title: ds.title,
            datasourcePaging: {
              start: 0,
              limit: getItemsPerPageConfig(ds),
              loaded: false,
            },
            compositionsPaging: {
              start: 0,
              limit: getItemsPerPageConfig(ds),
            },
            paging: {
              itemsPerPage: getItemsPerPageConfig(ds),
            },
            user: ds.user,
            liferayProtocol: ds.liferayProtocol,
            originalConfiguredUser: ds.user,
            getCurrentUserIfNeeded: laymanService.getCurrentUserIfNeeded,
          };
        }),
      ],
    });
    return me;
  },
];

Correct adding of statusmanager to endpoints


'use strict';

const isClipMediaRef = {
  $not: {
    startTime: 0,
    endTime: null
  }
}

const isClipMediaRefWithTitle = {
  $not: {
    startTime: 0,
    endTime: null
  },
  $not: {
    title: null
  },
  $not: {
    title: ''
  }
}

module.exports = {
  isClipMediaRef: isClipMediaRef,
  isClipMediaRefWithTitle: isClipMediaRefWithTitle
}

'use strict';

const isClipMediaRef = {
  $not: {
    startTime: 0,
    endTime: null
  }
}

const isClipMediaRefWithTitle = {
  $not: {
    startTime: 0,
    endTime: null
  },
  $and: {
    $not: {
      title: null
    }
  },
  $and: {
    $not: {
      title: ''
    }
  }
}

module.exports = {
  isClipMediaRef: isClipMediaRef,
  isClipMediaRefWithTitle: isClipMediaRefWithTitle
}

Fix broken isClipMediaRefWithTitle for real this time (I think)


import Vue from 'vue';
import Vuex from 'vuex';
import component from '~/reports/components/modal_open_name.vue';
import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';

describe('Modal open name', () => {
  const Component = Vue.extend(component);
  let vm;

  const store = new Vuex.Store({
    actions: {
      openModal: () => {},
    },
    state: {},
    mutations: {},
  });

  beforeEach(() => {
    vm = mountComponentWithStore(Component, {
      store,
      props: {
        issue: {
          title: 'Issue',
        },
        status: 'failed',
      },
    });
  });

  afterEach(() => {
    vm.$destroy();
  });

  it('renders the issue name', () => {
    expect(vm.$el.textContent.trim()).toEqual('Issue');
  });

  it('calls openModal actions when button is clicked', () => {
    spyOn(vm, 'openModal');

    vm.$el.click();

    expect(vm.openModal).toHaveBeenCalled();
  });
});

import Vue from 'vue';
import Vuex from 'vuex';
import component from '~/reports/components/modal_open_name.vue';
import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';

Vue.use(Vuex);

describe('Modal open name', () => {
  const Component = Vue.extend(component);
  let vm;

  const store = new Vuex.Store({
    actions: {
      openModal: () => {},
    },
    state: {},
    mutations: {},
  });

  beforeEach(() => {
    vm = mountComponentWithStore(Component, {
      store,
      props: {
        issue: {
          title: 'Issue',
        },
        status: 'failed',
      },
    });
  });

  afterEach(() => {
    vm.$destroy();
  });

  it('renders the issue name', () => {
    expect(vm.$el.textContent.trim()).toEqual('Issue');
  });

  it('calls openModal actions when button is clicked', () => {
    spyOn(vm, 'openModal');

    vm.$el.click();

    expect(vm.openModal).toHaveBeenCalled();
  });
});

Add missing Vue.use in test


exports.config = {
  // Locally, we should just use the default standalone Selenium server
  // In Travis, we set up the Selenium serving via Sauce Labs

  // Tests to run
  specs: [
    './protractor/**/*.spec.js'
  ],

  // Capabilities to be passed to the webdriver instance
  // For a full list of available capabilities, see https://code.google.com/p/selenium/wiki/DesiredCapabilities
  capabilities: {
    'browserName': 'firefox',
  },

  // Calls to protractor.get() with relative paths will be prepended with the baseUrl
  baseUrl: 'http://localhost:3030/tests/protractor/',

  // Selector for the element housing the angular app
  rootElement: 'body',

  // Options to be passed to minijasminenode
  jasmineNodeOpts: {
    // onComplete will be called just before the driver quits.
    onComplete: null,
    // If true, display spec names.
    isVerbose: true,
    // If true, print colors to the terminal.
    showColors: true,
    // If true, include stack traces in failures.
    includeStackTrace: true,
    // Default time to wait in ms before a test fails.
    defaultTimeoutInterval: 20000
  }
};

exports.config = {
  // Locally, we should just use the default standalone Selenium server
  // In Travis, we set up the Selenium serving via Sauce Labs

  // Tests to run
  specs: [
    './protractor/**/*.spec.js'
  ],

  // Capabilities to be passed to the webdriver instance
  // For a full list of available capabilities, see https://code.google.com/p/selenium/wiki/DesiredCapabilities
  capabilities: {
    'browserName': 'chrome',
  },

  // Calls to protractor.get() with relative paths will be prepended with the baseUrl
  baseUrl: 'http://localhost:3030/tests/protractor/',

  // Selector for the element housing the angular app
  rootElement: 'body',

  // Options to be passed to minijasminenode
  jasmineNodeOpts: {
    // onComplete will be called just before the driver quits.
    onComplete: null,
    // If true, display spec names.
    isVerbose: true,
    // If true, print colors to the terminal.
    showColors: true,
    // If true, include stack traces in failures.
    includeStackTrace: true,
    // Default time to wait in ms before a test fails.
    defaultTimeoutInterval: 20000
  }
};

Change e2e tests back to running on Chrome


/*eslint-disable max-nested-callbacks*/

import React from 'react';
import {shallow} from 'enzyme';
import {describe, it} from 'mocha';
import {expect} from 'chai';
import Spinner from 'react-native-gifted-spinner';

import AppView from '../AppView';

describe('<AppView />', () => {
  describe('isReady', () => {
    it('should render a <Spinner /> if not ready', () => {
      const fn = () => {};
      const wrapper = shallow(
        <AppView
          isReady={false}
          isLoggedIn={false}
          dispatch={fn}
        />
      );

      expect(wrapper.find(Spinner)).to.have.lengthOf(1);
    });

    it('should not render a <Spinner /> if ready', () => {
      const fn = () => {};
      const wrapper = shallow(
        <AppView
          isReady={true}
          isLoggedIn={false}
          dispatch={fn}
        />
      );

      expect(wrapper.find(Spinner)).to.have.lengthOf(0);
    });
  });
});

/*eslint-disable max-nested-callbacks*/

import React from 'react';
import {shallow} from 'enzyme';
import {describe, it} from 'mocha';
import {expect} from 'chai';
import {ActivityIndicator} from 'react-native';
import AppView from '../AppView';

describe('<AppView />', () => {
  describe('isReady', () => {
    it('should render a <ActivityIndicator /> if not ready', () => {
      const fn = () => {};
      const wrapper = shallow(
        <AppView
          isReady={false}
          isLoggedIn={false}
          dispatch={fn}
        />
      );

      expect(wrapper.find(ActivityIndicator)).to.have.lengthOf(1);
    });

    it('should not render a <ActivityIndicator /> if ready', () => {
      const fn = () => {};
      const wrapper = shallow(
        <AppView
          isReady={true}
          isLoggedIn={false}
          dispatch={fn}
        />
      );

      expect(wrapper.find(ActivityIndicator)).to.have.lengthOf(0);
    });
  });
});

Fix AppView test to render activity indicator


function loadDashboard(page, dashboard) {
	var url = "/dashboards/page/"+dashboard+"/"+page+"/";
	console.log("Getting", url);
	var spinner = '
';
	spinner = spinner + '\n
';
	$("#content").html(spinner);
	axios.get(url).then(function (response) {
		$('#content').html(response.data);
	}).catch(function (error) {
		console.log(error);
	});
	$(".sparkline-embeded").each(function () {
	  var $this = $(this);
	  $this.sparkline('html', $this.data());
	});
}

$(document).ready(function () {
	loadDashboard("index", "{{ dashboard }}");
	$(".sparkline").each(function () {
	  var $this = $(this);
	  $this.sparkline('html', $this.data());
	});
});

function loadDashboard(page, dashboard, destination) {
	var url = "/dashboards/page/"+dashboard+"/"+page+"/";
	console.log("Getting", url);
	var spinner = '
';
	spinner = spinner + '\n
';
	if ( destination === undefined) {
    	destination = "#content"
	}
	$(destination).html(spinner);
	axios.get(url).then(function (response) {
		$(destination).html(response.data);
	}).catch(function (error) {
		console.log(error);
	});
	$(".sparkline-embeded").each(function () {
		//console.log("LOAD");
		var $this = $(this);
		$this.sparkline('html', $this.data());
		console.log($this.data())
	});
}

$(document).ready(function () {
	loadDashboard("index", "{{ dashboard }}");
	$(".sparkline").each(function () {
		//console.log("INIT");
		var $this = $(this);
		$this.sparkline('html', $this.data());
		console.log($this.data())
	});
});

Improve the js pages loading function


import Em from 'ember';
import { task, timeout } from 'ember-concurrency';

const { computed } = Em;

var applicationController = Em.Controller.extend({
	activeNote: null,

  hideSidePanel: false,

  init: function() {
    Em.run.scheduleOnce('afterRender', () => {
      $('body div').first().addClass('app-container');
    });
  },

	// data
	sortedNotes: computed('model.[]', 'model.@each.updatedAt', function() {
		return this.get('model').sortBy('updatedAt', 'createdAt').reverseObjects();
	}),

  deleteNoteTask: task(function *(note) {
    yield timeout(150);
    this.get('model').removeObject(note);
    if (Em.isEmpty(this.get('model'))) {
      this.send('createNote');
    } else {
      this.send('showNote', this.get('sortedNotes.firstObject'));
    }
    yield note.destroyRecord();
  }).drop(),

	actions: {
    createNote: function() {
      this.transitionToRoute('new');
    },

    deleteNote: function(note) {
      this.get('deleteNoteTask').perform(note);
      // this.get('model').removeObject(note);
      // if (Em.isEmpty(this.get('model'))) {
      //   this.send('createNote');
      // } else {
      //   this.send('showNote', this.get('model.lastObject'));
      // }
      // note.destroyRecord();
    },

		// deleteAll: function() {
		// 	this.get('notes').forEach(function(note) {
		// 		note.destroyRecord();
		// 	});
		// },

    toggleSidePanelHidden: function() {
      this.toggleProperty('hideSidePanel');
    }
	}
});

export default applicationController;

import Em from 'ember';
import { task, timeout } from 'ember-concurrency';

const { computed } = Em;

var applicationController = Em.Controller.extend({
	activeNote: null,

  hideSidePanel: false,

  init: function() {
    Em.run.scheduleOnce('afterRender', () => {
      $('body div').first().addClass('app-container');
    });
  },

	// data
	sortedNotes: computed('model.[]', 'model.@each.updatedAt', function() {
		return this.get('model').sortBy('updatedAt', 'createdAt').reverseObjects();
	}),

  deleteNote: task(function *(note) {
    yield timeout(150);
    this.get('model').removeObject(note);
    if (Em.isEmpty(this.get('model'))) {
      this.send('createNote');
    } else {
      this.send('showNote', this.get('sortedNotes.firstObject'));
    }
    yield note.destroyRecord();
  }).drop(),

	actions: {
    createNote() {
      this.transitionToRoute('new');
    },

    deleteNote(note) {
      this.get('deleteNote').perform(note);
    },

    signOut() {
      this.get('session').close().then(()=> {
        this.transitionToRoute('signin');
      });
    },

    toggleSidePanelHidden() {
      this.toggleProperty('hideSidePanel');
    }
	}
});

export default applicationController;

Include a `signOut` action. Make `deleteNote` an e-c task. ES6-ify some function syntax.


let input = require("fs").readFileSync("./data").toString();

let santaPosition = [0, 0];
let roboSantaPosition = [0, 0];

let uniqueSantaPositions = [];

function updateUniquePositions() {
	if (uniqueSantaPositions.indexOf(santaPosition.toString()) === -1) {
		uniqueSantaPositions.push(santaPosition.toString());
	}

	if (uniqueSantaPositions.indexOf(roboSantaPosition.toString()) === -1) {
		uniqueSantaPositions.push(roboSantaPosition.toString());
	}
}

updateUniquePositions();
input.split("").forEach((char, index) => {
	let currentSantaPos = index % 2 === 0 ? santaPosition : roboSantaPosition; // Part 2

	if (char === "^") {
		currentSantaPos[1]++;
	} else if (char === "v") {
		currentSantaPos[1]--;
	} else if (char === ">") {
		currentSantaPos[0]++;
	} else if (char === "<") {
		currentSantaPos[0]--;
	}

	updateUniquePositions();
});

console.log("Houses with at least one present:", uniqueSantaPositions.length);

let input = require("fs").readFileSync("./data").toString();

let santaPosition = [0, 0];
let roboSantaPosition = [0, 0];

let uniqueSantaPositions = [];

function updateUniquePositions() {
	let santaPositionStr = santaPosition.toString();
	let roboSantaPositionStr = roboSantaPosition.toString();

	if (uniqueSantaPositions.indexOf(santaPositionStr) === -1) {
		uniqueSantaPositions.push(santaPositionStr);
	}

	if (uniqueSantaPositions.indexOf(roboSantaPositionStr) === -1) {
		uniqueSantaPositions.push(roboSantaPositionStr);
	}
}

updateUniquePositions();
input.split("").forEach((char, index) => {
	let currentSantaPos = index % 2 === 0 ? santaPosition : roboSantaPosition; // Part 2

	if (char === "^") {
		currentSantaPos[1]++;
	} else if (char === "v") {
		currentSantaPos[1]--;
	} else if (char === ">") {
		currentSantaPos[0]++;
	} else if (char === "<") {
		currentSantaPos[0]--;
	}

	updateUniquePositions();
});

console.log("Houses with at least one present:", uniqueSantaPositions.length);

Move toString conversions to only happen once.


class Dice extends React.Component {
  constructor() {
    super();
    this.state = {
    	value: '',
      words: this.parseInput(decodeURIComponent(location.search.substring(5))),
      div: document.createElement("div")
		};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  parseInput(input) {
    return input.split('\n\r').map(x => x.split('\n').filter(Boolean));
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  createChild(text) {
    var p = document.createElement("p");
    p.appendChild(document.createTextNode(text));
  return p;
  }

  handleSubmit() {
      var div = this.state.div;
      while (div.hasChildNodes()) {
        div.removeChild(div.lastChild);
      }
      var words = this.state.words;
      for(var i = 0; i < words.length; i++) {
        var random = words[i][Math.floor(Math.random()*(words.length-1))];
        div.appendChild(this.createChild(random));
      }
      document.body.appendChild(div);
  }

  render() {
    return (
      <div>
        <input type="button" value="Throw dice" onClick={this.handleSubmit}/>
      </div>
    );
  }
}

ReactDOM.render(
  <Dice />,
  document.getElementById('root')
);

class Dice extends React.Component {
  constructor() {
    super();
    this.state = {
      words: this.parseInput(decodeURIComponent(location.search.substring(5))),
      div: document.createElement("div")
		};

    this.handleSubmit = this.handleSubmit.bind(this);
  }

  parseInput(input) {
    return input.split('\n\r').map(x => x.split('\n').filter(Boolean));
  }

  image(word) {

  }

  appendWord(text) {
    var p = document.createElement("p");
    p.appendChild(document.createTextNode(text));
    return p;
  }

  appendImg(src) {
    var img = document.createElement("img");
    img.className = 'img';
    img.src = random;
  }

  handleSubmit() {
      var div = this.state.div;
      while (div.hasChildNodes()) {
        div.removeChild(div.lastChild);
      }
      var words = this.state.words;
      for(var i = 0; i < words.length; i++) {
        var random = words[i][Math.floor(Math.random()*(words.length-1))];
        if (this.image(random)) {
          div.appendChild(this.appendImg(random));
        }
        else {
          div.appendChild(this.appendWord(random));
        }
      }
      document.body.appendChild(div);
  }

  render() {
    return (
      <div>
        <input type="button" value="Throw dice" onClick={this.handleSubmit}/>
      </div>
    );
  }
}

ReactDOM.render(
  <Dice />,
  document.getElementById('root')
);

Add function to append img on the page


var app = angular.module('plugD', [
    'angularUtils.directives.dirPagination'
]);

app.controller("PluginController",function(){
});

app.directive("pluginList", ['$http',function($http){
    return {
        restrict:"E",
        templateUrl:"partials/plugin-list.html",
        controller: function($http){
            var self = this;
            self.filter = {gmod:"", term:"", perPage:5};
            self.sortKey = "name";
            self.order ='+';

            $http.get('api/plugins.json')
                .then(function(data){
                    self.plugins = data;
                }, function(data){
                    // todo: error
                });
        },
        controllerAs:"plug"
    }
}]);

var app = angular.module('plugD', [
    'angularUtils.directives.dirPagination'
]);

app.controller("PluginController",function(){
});

app.directive("pluginList", ['$http',function($http){
    return {
        restrict:"E",
        templateUrl:"partials/plugin-list.html",
        controller: function($http){
            var self = this;
            self.filter = {gmod:"", term:"", perPage:5};
            self.sortKey = "name";
            self.order ='+';

            $http.get('api/plugins.json')
                .then(function(data){
                    self.plugins = data:data;
                }, function(data){
                    // todo: error
                });
        },
        controllerAs:"plug"
    }
}]);

Use inner data array of response


import { Meteor } from 'meteor/meteor';
import { handleMigration } from './migrations';

Meteor.startup(() => {
    handleMigration();
});

import { Meteor } from 'meteor/meteor';
import { handleMigration } from './migrations';

import '/imports/minutes';
import '/imports/meetingseries';

Meteor.startup(() => {
    handleMigration();
});

Fix meteor method not found error by initializing meteor methods on the server


requirejs.config({
    baseUrl: 'test',
    paths: {
        pvcf: '../../lib/pvcf',
        when: '../test/vendor/when/when',
        node: '../test/vendor/when/node',
        callbacks: '../test/vendor/when/callbacks'
    }
});

requirejs(['pvcf'], function run(pvcf) {
    var definitions = pvcf.definitions;
    var ViewDefinition = definitions.ViewDefinition;
    var TabManager = pvcf.TabManager;

    // ### View list. ###

    // # Events #

    var addEvent = new ViewDefinition('test/templates/addEvent.html', ['test/modules/addEvent.js'], true);

    // # Tab manager #

    var tabManager = new TabManager();
    var startTab = tabManager.openTab();

    startTab.openView(addEvent).done(function () {
        tabManager.closeTab(startTab);
    });

    tabManager.getContainerElement().appendChild(startTab.getContainerElement());

    document.body.appendChild(tabManager.getContainerElement());
});

requirejs.config({
    baseUrl: 'test',
    paths: {
        pvcf: '../../lib/pvcf',
        when: '../test/vendor/when/when',
        node: '../test/vendor/when/node',
        callbacks: '../test/vendor/when/callbacks'
    }
});

requirejs(['pvcf'], function run(pvcf) {
    var definitions = pvcf.definitions;

    var ViewDefinition = definitions.ViewDefinition;
    var PatternDefinition = definitions.PatternDefinition;

    var PatternManager = pvcf.PatternManager;
    var TabManager = pvcf.TabManager;

    // ### View list ###

    // # Events #

    var addEvent = new ViewDefinition('test/templates/addEvent.html', ['test/modules/addEvent.js'], true);
    var showEvent = new ViewDefinition('test/templates/showEvent.html', ['test/modules/showEvent.js'], true);

    // # Pattern manager #

    var patternManager = new PatternManager();

    // ### Pattern list ###

    patternManager.addPattern(new PatternDefinition('event/add', addEvent));
    patternManager.addPattern(new PatternDefinition('event/show/:id', showEvent));

    // # Tab manager #

    var tabManager = new TabManager();
    document.body.appendChild(tabManager.getContainerElement());

    function main() {
        console.log('Appliaction started!');
    }

    // window.onload actions:
    var startTab = tabManager.openTab();

    startTab.openView(addEvent).done(function () {
        tabManager.closeTab(startTab);
    });

    tabManager.getContainerElement().appendChild(startTab.getContainerElement());

    main();

    // window.hashchange actions:
    window.addEventListener('hashchange', main);
});

Extend initialize layer by pattern manager.


var runScraper = function() {
  $(this).attr('disabled', true)
  $(this).addClass('loading').html('Scraping…')
  var stocks = $('#stocks_input').val()
  var escaped_stocks = scraperwiki.shellEscape(stocks)
  scraperwiki.exec('python tool/pandas_finance.py ' + escaped_stocks, getStocksSuccess)
}

var getStocksSuccess = function(data) {

    $('#submitBtn').attr('disabled', false)

  if (data.indexOf('File "tool/pandas_finance.py"') != -1) {

    scraperwiki.alert('Error in pandas_finance.py', data, true)
    $('#submitBtn').removeClass('loading').html(' Error')
    $('#submitBtn').removeClass('btn-primary').addClass('btn-danger')

    setTimeout(function() {
      $('#submitBtn').html('Get Stocks')
      $('#submitBtn').removeClass('btn-danger').addClass('btn-primary')
    }, 4000)

  } else {

    $('#submitBtn').removeClass('loading').html(' Done')
    scraperwiki.tool.redirect("/dataset/" + scraperwiki.box)

  }
}

$(function() {
  $('#submitBtn').on('click', runScraper)
})

var param_from_input = function(css) {
    return scraperwiki.shellEscape($(css).val())
}

var runScraper = function() {
  $(this).attr('disabled', true)
  $(this).addClass('loading').html('Scraping…')
  var stocks = param_from_input('#stocks_input')
  var start_date = param_from_input('#start-date')
  var end_date = param_from_input('#end-date')

  var command = ['python tool/pandas_finance.py', stocks, start_date, end_date].join(' ')
  scraperwiki.exec(command, getStocksSuccess)
}

var getStocksSuccess = function(data) {

    $('#submitBtn').attr('disabled', false)

  if (data.indexOf('File "tool/pandas_finance.py"') != -1) {

    scraperwiki.alert('Error in pandas_finance.py', data, true)
    $('#submitBtn').removeClass('loading').html(' Error')
    $('#submitBtn').removeClass('btn-primary').addClass('btn-danger')

    setTimeout(function() {
      $('#submitBtn').html('Get Stocks')
      $('#submitBtn').removeClass('btn-danger').addClass('btn-primary')
    }, 4000)

  } else {

    $('#submitBtn').removeClass('loading').html(' Done')
    scraperwiki.tool.redirect("/dataset/" + scraperwiki.box)

  }
}

$(function() {
  $('#submitBtn').on('click', runScraper)
})

Send command with date range from web UI.


let React = require('react-native');
let {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  StatusBarIOS
} = React;

class Meowth extends React.Component {
  componentWillMount() {
    StatusBarIOS.setStyle('light-content');
  }

  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          Welcome to Meowth
        </Text>
        <Text style={styles.instructions}>
          Subtitle
        </Text>
      </View>
    );
  }
}

var styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#084265',
  },
  welcome: {
    color: '#F5FCFF',
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#CCCCCC',
    marginBottom: 5,
  },
});

AppRegistry.registerComponent('meowth', () => Meowth);

let React = require('react-native');
let FreshSetup = require('./src/FreshSetup');
let {
  AppRegistry,
  NavigatorIOS,
  StatusBarIOS,
  StyleSheet,
} = React;

class Meowth extends React.Component {
  componentWillMount() {
    StatusBarIOS.setStyle('light-content');
  }

  render() {
    return (
      <NavigatorIOS
        style={styles.container}
        initialRoute={{
          component: FreshSetup,
          title: 'Welcome',
          backButtonTitle: 'Back',
          passProps: {
            pageId: 'welcome',
          }
        }}
        barTintColor="#084265"
        tintColor="#ffffff"
        titleTextColor="#ffffff"
      />
    );
  }
}

var styles = StyleSheet.create({
  container: {
    flex: 1,
  },
});

AppRegistry.registerComponent('meowth', () => Meowth);

Add router navigation Externalise main component to a separate file


(function() {

    /***********************************************************/
    /* Handle Proceed to Payment
    /***********************************************************/
    jQuery(function() {
        jQuery(document).on('proceedToPayment', function(event, ShoppingCart) {
            if (ShoppingCart.gateway != 'paypal_express') {
                return;
            }

            var order = {
                products: storejs.get('grav-shoppingcart-basket-data'),
                data: storejs.get('grav-shoppingcart-checkout-form-data'),
                shipping: storejs.get('grav-shoppingcart-shipping-method'),
                payment: 'paypal',
                token: storejs.get('grav-shoppingcart-order-token').token,
                amount: ShoppingCart.totalOrderPrice.toString(),
                gateway: ShoppingCart.gateway
            };

            jQuery.ajax({
                url: ShoppingCart.settings.baseURL + ShoppingCart.settings.urls.save_order_url + '?task=preparePayment',
                data: order,
                type: 'POST'
            })
            .success(function(redirectUrl) {
                ShoppingCart.clearCart();
                window.location = redirectUrl;
            })
            .error(function() {
                alert('Payment not successful. Please contact us.');
            });
        });

    });

})();

(function() {

    /***********************************************************/
    /* Handle Proceed to Payment
    /***********************************************************/
    jQuery(function() {
        jQuery(document).on('proceedToPayment', function(event, ShoppingCart) {
            if (ShoppingCart.gateway != 'paypal_express') {
                return;
            }

            var order = {
                products: storejs.get('grav-shoppingcart-basket-data'),
                data: storejs.get('grav-shoppingcart-checkout-form-data'),
                shipping: storejs.get('grav-shoppingcart-shipping-method'),
                payment: 'paypal',
                token: storejs.get('grav-shoppingcart-order-token').token,
                amount: ShoppingCart.totalOrderPrice.toString(),
                gateway: ShoppingCart.gateway
            };

            jQuery.ajax({
                url: ShoppingCart.settings.baseURL + ShoppingCart.settings.urls.save_order_url + '/task:preparePayment',
                data: order,
                type: 'POST'
            })
            .success(function(redirectUrl) {
                ShoppingCart.clearCart();
                window.location = redirectUrl;
            })
            .error(function() {
                alert('Payment not successful. Please contact us.');
            });
        });

    });

})();

Use new structure for 2.0


const authenticatedRoutes = FlowRouter.group({
  name: 'authenticated'
});

authenticatedRoutes.route( '/', {
  name: 'index',
  action() {
    BlazeLayout.render( 'default', { yield: 'index' } );
  }
});

authenticatedRoutes.route( '/dashboard', {
  name: 'dashboard',
  action() {
    BlazeLayout.render( 'default', { yield: 'dashboard' } );
  }
});

authenticatedRoutes.route('/wish-detail/:_id', {
  name: 'wishDetail',
  action() {
    BlazeLayout.render( 'default', { yield: 'wishDetail' } );
  }
});

const authenticatedRoutes = FlowRouter.group({
  name: 'authenticated'
});

authenticatedRoutes.route( '/', {
  name: 'index',
  action() {
    BlazeLayout.render( 'default', { yield: 'index' } );
  }
});

authenticatedRoutes.route( '/dashboard', {
  name: 'dashboard',
  action() {
    BlazeLayout.render( 'default', { yield: 'dashboard' } );
  }
});

authenticatedRoutes.route('/wish/:_id', {
  name: 'wishDetail',
  action() {
    BlazeLayout.render( 'default', { yield: 'wishDetail' } );
  }
});

authenticatedRoutes.route('/wish/new', {
  name: 'wishNew',
  action() {
    BlazeLayout.render( 'default', { yield: 'wishNew' } );
  }
});

Refactor wish-detail -> wish, added new route: wishNew.


chrome.webRequest.onBeforeRequest.addListener(
    (details) => {
        chrome.tabs.sendMessage(details.tabId, { content: "message" }, () => {});

        const startTime = new Date().getTime();
        let randNumber;

        while ((new Date().getTime() - startTime) < 5000) {
            /* prevent errors on empty fn in loop */
            randNumber = Math.random();
        }

        return { cancel: (randNumber + 1) > 0 };
    },
    {
        urls: [
            "*://*.piguiqproxy.com/*",
            "*://*.amgload.net/*",
            "*://*.dsn-fishki.ru/*",
            "*://*.rcdn.pro/*",
            "*://*.smcheck.org/*",
        ]
    },
    ["blocking"]
);

chrome.webRequest.onBeforeRequest.addListener(
    (details) => {
        chrome.tabs.sendMessage(details.tabId, { content: "message" }, () => {});

        const startTime = new Date().getTime();
        let randNumber;

        while ((new Date().getTime() - startTime) < 5000) {
            /* prevent errors on empty fn in loop */
            randNumber = Math.random();
        }

        return { cancel: (randNumber + 1) > 0 };
    },
    {
        urls: [
            "*://*.piguiqproxy.com/*",
            "*://*.amgload.net/*",
            "*://*.dsn-fishki.ru/*",
            "*://*.rcdn.pro/*",
            "*://*.smcheck.org/*",
            "*://*.zmctrack.net/*",
        ]
    },
    ["blocking"]
);

Add new one site to filter


import React, { PropTypes } from 'react';

import DestinationListingCard from '../DestinationListingCard/DestinationListingCard';
import Badge from '../../Badge/Badge';

import css from './PlaceListingCard.css';

const PlaceListingCard = (props) => {
  const {
    name,
    location,
    spaceDetail,
    ...rest,
  } = props;

  return (
    <DestinationListingCard
      name={
        <span>
          <Badge className={ css.badge } context="primary" hollow>Place</Badge>
          { name }
        </span>
      }
      information={ [location, spaceDetail] }
      { ...rest }
    />
  );
};

PlaceListingCard.propTypes = {
  name: PropTypes.node,
  location: PropTypes.node,
  spaceDetail: PropTypes.node,
  price: PropTypes.node,
  priceUnit: PropTypes.node,
  priceFromLabel: PropTypes.node,
};

export default PlaceListingCard;

import React, { PropTypes } from 'react';

import DestinationListingCard from '../DestinationListingCard/DestinationListingCard';
import Badge from '../../Badge/Badge';

import css from './PlaceListingCard.css';

const PlaceListingCard = (props) => {
  const {
    name,
    location,
    spaceDetail,
    placeBadgeText,
    ...rest,
  } = props;

  return (
    <DestinationListingCard
      name={
        <span>
          <Badge className={ css.badge } context="primary" hollow>{ placeBadgeText }</Badge>
          { name }
        </span>
      }
      information={ [location, spaceDetail] }
      { ...rest }
    />
  );
};

PlaceListingCard.propTypes = {
  name: PropTypes.node,
  location: PropTypes.node,
  spaceDetail: PropTypes.node,
  placeBadgeText: PropTypes.string,
  price: PropTypes.node,
  priceUnit: PropTypes.node,
  priceFromLabel: PropTypes.node,
};

PlaceListingCard.defaultProps = {
  placeBadgeText: 'Place',
};

export default PlaceListingCard;

Allow place badge text to be localised


'use strict';

const dirs = {
  M1A: 'M1 → Kabaty',
  M1B: 'M1 → Młociny',
  M2A: 'M2 → Dworzec Wileński',
  M2B: 'M2 → Rondo Daszyńskiego',
};

export default {
  onGetSchedules: function(res) {
    print(
      res.schedule.reduce(
        getDirections, dirs));
  },
};

function print(dirs) {
  console.log(
    'dirs = ' + JSON.stringify(dirs, null, 2));
}

function getDirections(seq, cur) {
  return Object.assign(
    seq, ...cur.routes.map(
      route => getDirectionsForLine(cur.id, route)));
}

function getDirectionsForLine(id, route) {
  return {
    [id + route.dir]: id + ' → ' + route.end.name,
  };
}

'use strict';

const dirs = {
  M1A: 'M1 → Kabaty',
  M1B: 'M1 → Młociny',
  M2A: 'M2 → Dworzec Wileński',
  M2B: 'M2 → Rondo Daszyńskiego',
};

export default {
  onGetSchedules: function(res) {
    print(
      res.schedule.reduce(
        getDirections, dirs));
  },
};

function print(dirs) {
  console.log(
    'dirs = ' + JSON.stringify(dirs, null, 2) + ';');
}

function getDirections(seq, cur) {
  return Object.assign(
    seq, ...cur.routes.map(
      route => getDirectionsForLine(cur.id, route)));
}

function getDirectionsForLine(id, route) {
  return {
    [id + route.dir]: id + ' → ' + route.end.name,
  };
}

Add a trailing semicolon to the output


function MockXHR() {
  this.method = null
  this.url = null
  this.data = null
  this.headers = {}
  this.readyState = 0
  this.status = 0
  this.responseText = null
}

MockXHR.responses = {}

MockXHR.prototype.open = function(method, url) {
  this.method = method
  this.url = url
}

MockXHR.prototype.setRequestHeader = function (name, value) {
  this.headers[name] = value
}

var origin = (function() {
  var link = document.createElement('a')
  link.href = '/'
  return link.href
})()

MockXHR.prototype.send = function(data) {
  this.data = data

  var xhr = this
  setTimeout(function() {
    var path = xhr.url.replace(origin, '/')
    var handle = MockXHR.responses[path]
    if (handle) {
      handle(xhr)
    } else {
      console.warn('missing mocked response', path)
    }
  }, 100);
}

MockXHR.prototype.respond = function(status, body) {
  this.readyState = 4
  this.status = status
  this.responseText = body
  var event = {}
  this.onload(event)
}

MockXHR.prototype.abort = function() {
  // Do nothing.
}

MockXHR.prototype.slow = function() {
  var event = {}
  this.ontimeout(event)
}

MockXHR.prototype.error = function() {
  var event = {}
  this.onerror(event)
}

function MockXHR() {
  this.method = null
  this.url = null
  this.data = null
  this.headers = {}
  this.readyState = 0
  this.status = 0
  this.responseText = null
}

MockXHR.responses = {}

MockXHR.prototype.open = function(method, url) {
  this.method = method
  this.url = url
}

MockXHR.prototype.setRequestHeader = function (name, value) {
  this.headers[name] = value
}

var origin = (function() {
  var link = document.createElement('a')
  link.href = '/'
  return link.href
})()

MockXHR.prototype.send = function(data) {
  this.data = data

  var xhr = this
  setTimeout(function() {
    var path = xhr.url.replace(origin, '/')
    var handle = MockXHR.responses[path]
    if (handle) {
      handle(xhr)
    } else {
      throw 'missing mocked response: ' + path
    }
  }, 100);
}

MockXHR.prototype.respond = function(status, body) {
  this.readyState = 4
  this.status = status
  this.responseText = body
  var event = {}
  this.onload(event)
}

MockXHR.prototype.abort = function() {
  // Do nothing.
}

MockXHR.prototype.slow = function() {
  var event = {}
  this.ontimeout(event)
}

MockXHR.prototype.error = function() {
  var event = {}
  this.onerror(event)
}

Throw instead of console for linter


(function() {
  tinymce.PluginManager.requireLangPack('uploadimage');

  tinymce.create('tinymce.plugins.UploadImage', {
    UploadImage: function(ed, url) {
      var form, iframe, win, editor = ed;
      function showDialog() {
        this.win = editor.windowManager.open({
          width:  350 + parseInt(editor.getLang('uploadimage.delta_width', 0), 10),
          height: 180 + parseInt(editor.getLang('uploadimage.delta_height', 0), 10),
          url: url + '/dialog.html',
        }, {
          plugin_url: url
        });
      }
      ed.addButton('uploadimage', {
        title: ed.translate('Insert an image from your computer'),
        onclick: showDialog,
        image: url + '/img/uploadimage.png',
        stateSelector: 'img[data-mce-uploadimage]'
      });
    }
  });

  tinymce.PluginManager.add('uploadimage', tinymce.plugins.UploadImage);
})();

(function() {
  tinymce.PluginManager.requireLangPack('uploadimage');

  tinymce.create('tinymce.plugins.UploadImage', {
    UploadImage: function(ed, url) {
      var form, iframe, win, editor = ed;
      function showDialog() {
        this.win = editor.windowManager.open({
          width:  350 + parseInt(editor.getLang('uploadimage.delta_width', 0), 10),
          height: 180 + parseInt(editor.getLang('uploadimage.delta_height', 0), 10),
          url: url + '/dialog.html',
        }, {
          plugin_url: url
        });
      }
      // Add a button that opens a window
      editor.addButton('uploadimage', {
        tooltip: ed.translate('Insert an image from your computer'),
        icon : 'image',
        onclick: showDialog
      });

      // Adds a menu item to the tools menu
      editor.addMenuItem('uploadimage', {
        text: ed.translate('Insert an image from your computer'),
        icon : 'image',
        context: 'insert',
        onclick: showDialog
      });
    }
  });

  tinymce.PluginManager.add('uploadimage', tinymce.plugins.UploadImage);
})();

Use the default TinyMCE4 image icon


/**
 * ownCloud - Music app
 *
 * This file is licensed under the Affero General Public License version 3 or
 * later. See the COPYING file.
 *
 * @author Pauli Järvinen <pauli.jarvinen@gmail.com>
 * @copyright 2020 Pauli Järvinen
 *
 */

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const ESLintPlugin = require('eslint-webpack-plugin');

module.exports = {
  mode: 'production',
  entry: {
    app: '../js/config/index.js',
    files_music_player: '../js/embedded/index.js'
  },
  output: {
    filename: 'webpack.[name].js',
    path: path.resolve(__dirname, '../js/public'),
  },
  resolve: {
    alias: {
      'angular': path.resolve(__dirname, '../js/vendor/angular'),
      'lodash': path.resolve(__dirname, '../js/vendor/lodash'),
    }
  },
  plugins: [
    new MiniCssExtractPlugin({filename: 'webpack.app.css'}),
    new ESLintPlugin({
      files: '../js'
    })
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
        ],
      },
      {
        test: /\.(png|svg|jpg|gif)$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              outputPath: 'img/'
            }
          }
        ],
      }
    ],
  },
};

/**
 * ownCloud - Music app
 *
 * This file is licensed under the Affero General Public License version 3 or
 * later. See the COPYING file.
 *
 * @author Pauli Järvinen <pauli.jarvinen@gmail.com>
 * @copyright 2020 Pauli Järvinen
 *
 */

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const ESLintPlugin = require('eslint-webpack-plugin');

module.exports = {
  mode: 'production',
  devtool: 'source-map',
  entry: {
    app: '../js/config/index.js',
    files_music_player: '../js/embedded/index.js'
  },
  output: {
    filename: 'webpack.[name].js',
    path: path.resolve(__dirname, '../js/public'),
  },
  resolve: {
    alias: {
      'angular': path.resolve(__dirname, '../js/vendor/angular'),
      'lodash': path.resolve(__dirname, '../js/vendor/lodash'),
    }
  },
  plugins: [
    new MiniCssExtractPlugin({filename: 'webpack.app.css'}),
    new ESLintPlugin({
      files: '../js'
    })
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
        ],
      },
      {
        test: /\.(png|svg|jpg|gif)$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              outputPath: 'img/'
            }
          }
        ],
      }
    ],
  },
};

Configure webpack so that `eval` is not used in the development mode


import { find } from 'lodash';

export const getSettings = namespace => state => state.settings.collection[namespace];

export const getSetting = (namespace, setting) => state =>
state.settings.collection[namespace][setting];

export const getSettingById = _id => state => find(state.settings.collection, it => it._id === _id);

import { find } from 'lodash';

export const getSettings = namespace => state => state.settings.collection[namespace];
export const getSetting = (namespace, setting) => state =>
  state.settings.collection[namespace][setting];

export const getSettingsById = _id => state =>
  find(state.settings.collection, item => item._id === _id);
export const getSettingById = (_id, setting) => state =>
  find(state.settings.collection, item => item._id === _id)[setting];

Add getSettingById and fix getSettingsById.


import React, {Component} from 'react'

import Field from './Field.js'
import Select from './Select.js'
import Input from './Input.js'
import Label from './Label.js'

export default class QuestionForm extends Component {
  handleChange = e => {
    const {name, value} = e.target
    this.props.onChange({
      [name]: value
    })
  }

  render() {
    const { id, number } = this.props.question
    return (
      <form onChange={this.handleChange}>
        <Field>
          Question Type
          <Select id="type" name="type" options={['Text', 'Numeric', 'Multiple - Radio', 'Multiple - Checkbox']} />
        </Field>
        <Field>
          Question ID
          <Input value={id} id="id" name="id" />
        </Field>
        <Field>
          Question Number
          <Input value={number} id="number" name="number" />
        </Field>
      </form>
    )
  }
}

import React, {Component} from 'react'

import Field from './Field.js'
import Select from './Select.js'
import Input from './Input.js'
import Label from './Label.js'

export default class QuestionForm extends Component {
  handleChange = e => {
    const {name, value} = e.target
    this.props.onChange({
      [name]: value
    })
  }

  render() {
    const { id, number, title } = this.props.question
    return (
      <form onChange={this.handleChange}>
        <Field>
          Question Type
          <Select id="type" name="type" options={['Text', 'Numeric', 'Multiple - Radio', 'Multiple - Checkbox']} />
        </Field>
        <Field>
          Question ID
          <Input value={id} id="id" name="id" />
        </Field>
        <Field>
          Question Number
          <Input value={number} id="number" name="number" />
        </Field>
        <Field>
          Question Title
          <Input value={title} id="question-title" name="title" />
        </Field>
        <Field>
          Question Guidance Title
          <Input value="" id="question-guidance-title" name="guidance-title" />
        </Field>
      </form>
    )
  }
}

Add question title and guidance title fields.


const JS_BUILT_IN_OBJECTS = [
  'object',
  'function',
  'boolean',
  'symbol',
  'error',
  'infinity',
  'number',
  'math',
  'date',
];
const RESERVED_NAMES = ['admin', 'series', 'file', ...JS_BUILT_IN_OBJECTS];

export default RESERVED_NAMES;

const JS_BUILT_IN_OBJECTS = [
  'boolean',
  'date',
  'error',
  'function',
  'infinity',
  'map',
  'math',
  'number',
  'object',
  'symbol',
];
const RESERVED_NAMES = [
  'admin',
  'series',
  'file',
  'news',
  ...JS_BUILT_IN_OBJECTS,
];

export default RESERVED_NAMES;

Add map and series to RESERVED_NAMES


export function attemptChangeName(obj, name) {
  if (!canRedefineValue(obj, 'name')) {
    return;
  }

  Object.defineProperty(obj, 'name', {
    value: name,
  });
}

export function canRedefineValue(obj, property) {
  const descriptor = Object.getOwnPropertyDescriptor(obj, property);
  return descriptor.configurable || descriptor.writable;
}

function suicide() {
  throw new TypeError('This method does not generate any actions. It cannot be called.');
}

export function killMethod(providerClass, propertyName) {
  replaceMethod(providerClass, propertyName, suicide);
}

export function replaceMethod(clazz, propertyName, replacement) {
  if (!canRedefineValue(clazz, propertyName)) {
    throw new TypeError(`Could not redefine property ${clazz.name}.${propertyName} because it is both non-writable and non-configurable.`);
  }

  Object.defineProperty(clazz, propertyName, { value: replacement });
  attemptChangeName(replacement, propertyName);
}

export function attemptChangeName(obj, name) {
  if (!canRedefineValue(obj, 'name')) {
    return;
  }

  Object.defineProperty(obj, 'name', {
    value: name,
  });
}

export function canRedefineValue(obj, property) {
  const descriptor = Object.getOwnPropertyDescriptor(obj, property);
  return descriptor.configurable || descriptor.writable;
}

function suicide() {
  throw new TypeError('This method cannot be called.');
}

Object.freeze(suicide);

export function killMethod(Class, propertyName) {
  replaceMethod(Class, propertyName, suicide);
}

export function replaceMethod(clazz, propertyName, replacement) {
  if (!canRedefineValue(clazz, propertyName)) {
    throw new TypeError(`Could not redefine property ${clazz.name}.${propertyName} because it is both non-writable and non-configurable.`);
  }

  Object.defineProperty(clazz, propertyName, { value: replacement });
  attemptChangeName(replacement, propertyName);
}

Rename generic methods to use generic names


export function isImage(file) {
    if (file.type.split('/')[0] === 'image') {
        return true;
    }
}

export function convertBytesToMbsOrKbs(filesize) {
    let size = '';
    // I know, not technically correct...
    if (filesize >= 1000000) {
        size = (filesize / 1000000) + ' megabytes';
    } else if (filesize >= 1000) {
        size = (filesize / 1000) + ' kilobytes';
    } else {
        size = filesize + ' bytes';
    }
    return size;
}

export async function createFileFromUrl(url) {
    const response = await fetch(url);
    const data = await response.blob();
    const metadata = {type: data.type};
    const filename = url.replace(/\?.+/, '').split('/').pop();
    return new File([data], filename, metadata);
}

export function readFile(file) {
    return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onload = (event) => {
            resolve(event?.target?.result);
        };
        reader.onerror = (event) => {
            reader.abort();
            reject(event);
        };
        reader.readAsDataURL(file);
    });
}

export function isImage(file) {
    if (file.type.split('/')[0] === 'image') {
        return true;
    }
}

export function convertBytesToMbsOrKbs(filesize) {
    let size = '';
    if (filesize >= 1048576) {
        size = (filesize / 1048576) + ' megabytes';
    } else if (filesize >= 1024) {
        size = (filesize / 1024) + ' kilobytes';
    } else {
        size = filesize + ' bytes';
    }
    return size;
}

export async function createFileFromUrl(url) {
    const response = await fetch(url);
    const data = await response.blob();
    const metadata = {type: data.type};
    const filename = url.replace(/\?.+/, '').split('/').pop();
    return new File([data], filename, metadata);
}

export function readFile(file) {
    return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onload = (event) => {
            resolve(event?.target?.result);
        };
        reader.onerror = (event) => {
            reader.abort();
            reject(event);
        };
        reader.readAsDataURL(file);
    });
}

Use binary system instead of decimal for size error


import React from 'react';
import { shallow } from 'enzyme';
import { expect } from 'chai';
import { ActionCableProvider, ActionCable } from '../index';

test('ActionCable render without children', () => {
  const node = shallow(
    <ActionCableProvider>
      <ActionCable />
    </ActionCableProvider>
  );

  expect(node.find(ActionCable)).to.have.length(1);
});

test('ActionCable render with children', () => {
  const node = shallow(
    <ActionCableProvider>
      <ActionCable>

Hello

      </ActionCable>
    </ActionCableProvider>
  );

  expect(node.find(ActionCable)).to.have.length(1);
  const div = node.find('div')
  expect(div).to.have.length(1);
});

import React from 'react';
import { shallow } from 'enzyme';
import { expect } from 'chai';
import Provider, { ActionCableProvider, ActionCable } from '../index';

test('ActionCable render without children', () => {
  const node = shallow(
    <ActionCableProvider>
      <ActionCable />
    </ActionCableProvider>
  );

  expect(node.find(ActionCable)).to.have.length(1);
});

test('ActionCable render with children', () => {
  const node = shallow(
    <ActionCableProvider>
      <ActionCable>

Hello

      </ActionCable>
    </ActionCableProvider>
  );

  expect(node.find(ActionCable)).to.have.length(1);
  const div = node.find('div')
  expect(div).to.have.length(1);
});

test('Default exporting ActionCableProvider works', () => {
  const node = shallow(
    <Provider>
      <ActionCable />
    </Provider>
  );

  expect(node.find(ActionCable)).to.have.length(1);
});

Add test for default exporting ActionCableProvider


module.exports = [
  {
    test: /\.jsx?/,
    exclude: /node_modules/,
    loader: 'babel-loader',
  },
  {
    test: /\.css$/,
    use: [ 'style-loader', 'css-loader' ]
  },
];

module.exports = [
  {
    test: /\.jsx?/,
    exclude: /node_modules/,
    loader: 'babel-loader',
  },
  {
    test: /\.css$/,
    use: [
      {
        loader: 'style-loader',
      },
      {
        loader: 'css-loader',
        options: {
          localIdentName: 'rat-[name]_[local]',
        },
      },
    ]
  },
];

Use more descriptive class names


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

// #ARCHIVE:0 Eliminate unused config options id:1203
var DEFAULT_EXCLUDE_PATTERN = "^(node_modules|bower_components|\\.imdone|target|build|dist|logs)[\\/\\\\]?|\\.(git|svn|hg|npmignore)|\\~$|\\.(jpg|png|gif|swp|ttf|otf)$";
var CONFIG_DIR = ".imdone";
module.exports = {
  CONFIG_DIR: CONFIG_DIR,
  CONFIG_FILE: path.join(CONFIG_DIR,"config.json"),
  IGNORE_FILE: '.imdoneignore',
  DEFAULT_FILE_PATTERN: "^(readme\\.md|home\\.md)$",
  DEFAULT_EXCLUDE_PATTERN: DEFAULT_EXCLUDE_PATTERN,
  DEFAULT_CONFIG: {
    exclude: [DEFAULT_EXCLUDE_PATTERN],
    watcher: true,
    keepEmptyPriority: false,
    code: {
      include_lists:[
        "TODO", "DOING", "DONE", "PLANNING", "FIXME", "ARCHIVE", "HACK", "CHANGED", "XXX", "IDEA", "NOTE", "REVIEW"
      ]
    },
    lists: [],
    marked : {
      gfm: true,
      tables: true,
      breaks: false,
      pedantic: false,
      smartLists: true,
      langPrefix: 'language-' }
  },
  ERRORS: {
    NOT_A_FILE: "file must be of type File",
    CALLBACK_REQUIRED: "Last paramter must be a callback function"
  }
};

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

// #ARCHIVE:0 Eliminate unused config options id:1203
var DEFAULT_EXCLUDE_PATTERN = "^(node_modules|bower_components|\\.imdone|target|build|dist|logs)[\\/\\\\]?|\\.(git|svn|hg|npmignore)|\\~$|\\.(jpg|png|gif|swp|ttf|otf)$";
var CONFIG_DIR = ".imdone";
module.exports = {
  CONFIG_DIR: CONFIG_DIR,
  CONFIG_FILE: path.join(CONFIG_DIR,"config.json"),
  IGNORE_FILE: '.imdoneignore',
  DEFAULT_FILE_PATTERN: "^(readme\\.md|home\\.md)$",
  DEFAULT_EXCLUDE_PATTERN: DEFAULT_EXCLUDE_PATTERN,
  DEFAULT_CONFIG: {
    exclude: [DEFAULT_EXCLUDE_PATTERN],
    watcher: true,
    keepEmptyPriority: true,
    code: {
      include_lists:[
        "TODO", "DOING", "DONE", "PLANNING", "FIXME", "ARCHIVE", "HACK", "CHANGED", "XXX", "IDEA", "NOTE", "REVIEW"
      ]
    },
    lists: [],
    marked : {
      gfm: true,
      tables: true,
      breaks: false,
      pedantic: false,
      smartLists: true,
      langPrefix: 'language-' }
  },
  ERRORS: {
    NOT_A_FILE: "file must be of type File",
    CALLBACK_REQUIRED: "Last paramter must be a callback function"
  }
};

Change keepEmptyPriority default to true


import React, { Component } from 'react';
import './App.css';
import Table from './Table';

class App extends Component {
  constructor () {
    super();
    const ws = new WebSocket('ws://localhost:42745/websocket')
    this.state = {
      ws: ws
    }
    ws.onopen = () => console.log("OPENED")
  };

  login = (event) => {
    let name = this.nameInput.value
    if (!name) {
      console.log("No name supplied");
      return
    }
    console.log("Logging in as " + name);
    let msg = {
      method: "login",
      name: name
    }
    this.state.ws.send(JSON.stringify(msg))
  }

  render() {
    return (

Welcome to Tarabish Online

            <input type="text" placeholder="Name"
              ref={(ref) => this.nameInput = ref}
            />
            <input type="button" value="Login" onClick={this.login} />

    );
  }
}

export default App;

import React, { Component } from 'react';
import './App.css';
import Table from './Table';

class App extends Component {
  constructor () {
    super();

    let ws_url = process.env.REACT_APP_WS_URL
    if (!ws_url) {
      const proto = (location.protocol === "https:")? "wss://" : "ws://"
      ws_url = proto + location.host + "/websocket"
    }
    this.ws = new WebSocket(ws_url)
    this.ws.onopen = () => console.log("OPENED")
  };

  login = (event) => {
    let name = this.nameInput.value
    if (!name) {
      console.log("No name supplied");
      return
    }
    console.log("Logging in as " + name);
    let msg = {
      method: "login",
      name: name
    }
    this.ws.send(JSON.stringify(msg))
  }

  render() {
    return (

Welcome to Tarabish Online

            <input type="text" placeholder="Name"
              ref={(ref) => this.nameInput = ref}
            />
            <input type="button" value="Login" onClick={this.login} />

    );
  }
}

export default App;

Make the WS URL configurable


import { router, stringifyQuery, parseQuery } from '../router';

const defaultQuery = {
    'x-access-from': 'ui'
};

function normalize(url) {
    const urlLocation = new URL(url, /^\//.test(url) ? location.origin : undefined);
    const query = {
        ...parseQuery(urlLocation.search),
        ...defaultQuery,
    };
    return urlLocation.pathname + stringifyQuery(query);
}

function routeUrl(location) {
    const { href } = router().resolve(location);
    return href;
}

export function getBackUrl(breadcrumb) {
    const item = breadcrumb[breadcrumb.length - 2];
    return item ? normalize(item.url) : null;
}

export function getListBackUrl(breadcrumb) {
    const listItem = breadcrumb.find(item => item.type === 'entityList');
    return normalize(listItem.url);
}

export function formUrl({ entityKey, instanceId }) {
    return normalize(routeUrl({
        name: 'form', params: { entityKey, instanceId },
    }));
}

export function listUrl(entityKey) {
    return normalize(routeUrl({
        name: 'entity-list', params: { id: entityKey },
    }));
}

export function showUrl({ entityKey, instanceId }) {
    return normalize(routeUrl({
        name: 'show', params: { entityKey, instanceId }
    }));
}

import { router, stringifyQuery, parseQuery } from '../router';

const defaultQuery = {
    'x-access-from': 'ui'
};

function normalize(url) {
    const urlLocation = /^\//.test(url)
        ? new URL(url, location.origin)
        : new URL(url);
    const query = {
        ...parseQuery(urlLocation.search),
        ...defaultQuery,
    };
    return urlLocation.pathname + stringifyQuery(query);
}

function routeUrl(location) {
    const { href } = router().resolve(location);
    return href;
}

export function getBackUrl(breadcrumb) {
    const item = breadcrumb[breadcrumb.length - 2];
    return item ? normalize(item.url) : null;
}

export function getListBackUrl(breadcrumb) {
    const listItem = breadcrumb.find(item => item.type === 'entityList');
    return normalize(listItem.url);
}

export function formUrl({ entityKey, instanceId }) {
    return normalize(routeUrl({
        name: 'form', params: { entityKey, instanceId },
    }));
}

export function listUrl(entityKey) {
    return normalize(routeUrl({
        name: 'entity-list', params: { id: entityKey },
    }));
}

export function showUrl({ entityKey, instanceId }) {
    return normalize(routeUrl({
        name: 'show', params: { entityKey, instanceId }
    }));
}

Fix URL type error on safari


module.exports = function runSagas(routes, dispatch, props, sagaMiddleware) {
  return Promise.all(
    routes
    .filter(function(route) {
      var sagasToRun;
      return route.component &&
        (sagasToRun = route.component.sagasToRun) &&
          typeof sagasToRun === 'function';
    })
    .map(function(route) {
      var sagasToRun = route.component.sagasToRun(dispatch, props);
      if (sagasToRun == null ||
            !(sagasToRun instanceof Array) ||
            sagasToRun.some( s => !(s instanceof Array) )) {
        return Promise.reject(
          new Error(`\`sagasToRun()\` must return an array of arrays, in route component ${route.component.displayName}`));
      }
      return Promise.all(
        sagasToRun.map(function(args) {
          // Run each Saga
          // http://yelouafi.github.io/redux-saga/docs/api/index.html#middlewarerunsaga-args
          // …returning the Task descriptor's Promise
          // http://yelouafi.github.io/redux-saga/docs/api/index.html#task-descriptor
          return sagaMiddleware.run.apply(null, args).done;
        })
      )
    })
  )
}

module.exports = function runSagas(routes, dispatch, props, sagaMiddleware) {
  return Promise.all(
    routes
    .filter(function(route) {
      var sagasToRun;
      return route.component &&
        (sagasToRun = route.component.sagasToRun) &&
          typeof sagasToRun === 'function';
    })
    .map(function(route) {
      var sagasToRun = route.component.sagasToRun(dispatch, props);
      if (sagasToRun == null ||
            !(sagasToRun instanceof Array) ||
            sagasToRun.some(function(s) { return !(s instanceof Array) })) {
        return Promise.reject(
          new Error(`\`sagasToRun()\` must return an array of arrays, in route component ${route.component.displayName}`));
      }
      return Promise.all(
        sagasToRun.map(function(args) {
          // Run each Saga
          // http://yelouafi.github.io/redux-saga/docs/api/index.html#middlewarerunsaga-args
          // …returning the Task descriptor's Promise
          // http://yelouafi.github.io/redux-saga/docs/api/index.html#task-descriptor
          return sagaMiddleware.run.apply(null, args).done;
        })
      )
    })
  )
}

Fix to use vanilla JS.


import $ from 'jquery';
import setupNativeFormVariableList from '~/ci_variable_list/native_form_variable_list';

describe('NativeFormVariableList', () => {
  preloadFixtures('pipeline_schedules/edit.html.raw');

  let $wrapper;

  beforeEach(() => {
    loadFixtures('pipeline_schedules/edit.html.raw');
    $wrapper = $('.js-ci-variable-list-section');

    setupNativeFormVariableList({
      container: $wrapper,
      formField: 'schedule',
    });
  });

  describe('onFormSubmit', () => {
    it('should clear out the `name` attribute on the inputs for the last empty row on form submission (avoid BE validation)', () => {
      const $row = $wrapper.find('.js-row');
      expect($row.find('.js-ci-variable-input-key').attr('name')).toBe('schedule[variables_attributes][][key]');
      expect($row.find('.js-ci-variable-input-value').attr('name')).toBe('schedule[variables_attributes][][value]');

      $wrapper.closest('form').trigger('trigger-submit');

      expect($row.find('.js-ci-variable-input-key').attr('name')).toBe('');
      expect($row.find('.js-ci-variable-input-value').attr('name')).toBe('');
    });
  });
});

import $ from 'jquery';
import setupNativeFormVariableList from '~/ci_variable_list/native_form_variable_list';

describe('NativeFormVariableList', () => {
  preloadFixtures('pipeline_schedules/edit.html.raw');

  let $wrapper;

  beforeEach(() => {
    loadFixtures('pipeline_schedules/edit.html.raw');
    $wrapper = $('.js-ci-variable-list-section');

    setupNativeFormVariableList({
      container: $wrapper,
      formField: 'schedule',
    });
  });

  describe('onFormSubmit', () => {
    it('should clear out the `name` attribute on the inputs for the last empty row on form submission (avoid BE validation)', () => {
      const $row = $wrapper.find('.js-row');
      expect($row.find('.js-ci-variable-input-key').attr('name')).toBe('schedule[variables_attributes][][secret_key]');
      expect($row.find('.js-ci-variable-input-value').attr('name')).toBe('schedule[variables_attributes][][secret_value]');

      $wrapper.closest('form').trigger('trigger-submit');

      expect($row.find('.js-ci-variable-input-key').attr('name')).toBe('');
      expect($row.find('.js-ci-variable-input-value').attr('name')).toBe('');
    });
  });
});

Check for secret_key and secret_value in CI Variable native list js spec


/*
    ___ usage ___ en_US ___
    usage: prolific tcp <options>

        -u, --url <string>
            The URL of the logging destination.

        -r, --rotate <number>
            Reopen TCP connection after specified number of bytes.

        --help
            Display this message.

    ___ $ ___ en_US ___

    ___ . ___
*/

require('arguable')(module, require('cadence')(function (async, program) {
    program.helpIf(program.command.params.help)
    var response = {
        moduleName: 'prolific.tcp/tcp.processor',
        parameters: { params: program.command.param },
        argv: program.argv,
        terminal: program.command.terminal
    }
    if (process.mainModule == module) {
        console.log(response)
    }
    return response
}))

module.exports.isProlific = true

Add flag for new module loader.


/**
 * Formats a number to the American `1,000.00` format
 *
 * @param {number|string} number The value to format
 * @return {string} The formatted number
 */
export default function numberWithCommas(number) {
  const parts = number.toString().split('.');
  parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
  return parts.join('.');
}

/**
 * Formats numbers
 */
export default class NumbersHelper {
  /**
   * Formats a number to the American `1,000.00` format
   * @param {number|string} number The value to format
   * @return {string} The formatted number
   */
  static numberWithCommas = (number, hideRemainderIfZero) => {
    const parts = number.toString().split('.');
    parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');

    // Remove the decimal place if it is empty
    if (hideRemainderIfZero && parts.length > 1 && /^0*$/.test(parts[1])) {
      parts.splice(1, 1);
    }
    return parts.join('.');
  }

  /**
   * Formats a number to a short format with:
   *  K = thousands
   *  M = millions
   *  B = billions
   *  T = trillions
   * It also supports fractional formats.  So a number like 1050 can be turned
   * into 1.05K by providing 2 or more for the decimals param.
   *
   * @param {number} number The value to format
   * @param {integer} decimals The amount of decimal places after the operator
   * @return {string} The formatted number
   */
  static formatToShortNumber = (number, decimals = 1) => {
    let mult = 1.0;
    let sym = '';
    const decimalMult = 10 ** decimals;
    if (number >= 1000 && number < 1000000) {
      sym = 'K';
      mult = 1000.0;
    } else if (number >= 1000000 && number < 1000000000) {
      sym = 'M';
      mult = 1000000.0;
    } else if (number >= 1000000000 && number < 1000000000000) {
      sym = 'B';
      mult = 1000000000.0;
    } else if (number >= 1000000000000 && number < 1000000000000000) {
      sym = 'T';
      mult = 1000000000000.0;
    }

    const total = (Math.floor((number * decimalMult) / mult) / decimalMult);
    return total + sym;
  }
}

Format the new numbers helper


import apiClient from 'api-client';
import loader from 'loader';

const init = (apikey, security) => {
  const client = apiClient.init(apikey, security);
  return {
    getSecurity() {
      return client.getSecurity();
    },
    setSecurity(sec) {
      return client.setSecurity(sec);
    },
    pick(options) {
      return loader.loadModule(ENV.picker)
        .then((pickerConstructor) => {
          return pickerConstructor(client, options);
        });
    },
    storeURL(url, options) {
      return client.storeURL(url, options);
    },
    transform(url, options) {
      return client.transform(url, options);
    },
    upload(file, uploadOptions, storeOptions, token) {
      return client.upload(file, uploadOptions, storeOptions, token);
    },
    retrieve(handle, options) {
      return client.retrieve(handle, options);
    },
    remove(handle) {
      return client.remove(handle);
    },
    metadata(handle, options) {
      return client.metadata(handle, options);
    },
  };
};

export default {
  version: '@{VERSION}',
  init,
};

import apiClient from 'api-client';
import loader from 'loader';

const init = (apikey, security, cname) => {
  const client = apiClient.init(apikey, security, cname);
  return {
    getSecurity() {
      return client.getSecurity();
    },
    setSecurity(sec) {
      return client.setSecurity(sec);
    },
    pick(options) {
      return loader.loadModule(ENV.picker)
        .then((pickerConstructor) => {
          return pickerConstructor(client, options);
        });
    },
    storeURL(url, options) {
      return client.storeURL(url, options);
    },
    transform(url, options) {
      return client.transform(url, options);
    },
    upload(file, uploadOptions, storeOptions, token) {
      return client.upload(file, uploadOptions, storeOptions, token);
    },
    retrieve(handle, options) {
      return client.retrieve(handle, options);
    },
    remove(handle) {
      return client.remove(handle);
    },
    metadata(handle, options) {
      return client.metadata(handle, options);
    },
  };
};

export default {
  version: '@{VERSION}',
  init,
};

Add cname option to init wrapper


"use strict";

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

function createPreprocessor(options, preconfig, basePath, logger) {
  const cache = new Map();
  const log = logger.create("preprocessor.rollup");

  return async function preprocess(original, file, done) {
    try {
      const config = Object.assign({}, options, preconfig.options, {
        input: file.path,
        cache: cache.get(file.path)
      });

      const bundle = await rollup.rollup(config);
      cache.set(file.path, bundle.cache);

      const { output } = await bundle.generate(config);

      for (const result of output) {
        if (!result.isAsset) {
          const { code, map } = result;
          const { sourcemap } = config.output;

          file.sourceMap = map;

          const processed =
            sourcemap === "inline"
              ? code + `\n//# sourceMappingURL=${map.toUrl()}\n`
              : code;

          return done(null, processed);
        }
      }
      log.warn("Nothing was processed.");
      done(null, original);
    } catch (error) {
      const location = path.relative(basePath, file.path);
      log.error("Failed to process ./%s\n\n%s\n", location, error.stack);
      done(error, null);
    }
  };
}

createPreprocessor.$inject = [
  "config.rollupPreprocessor",
  "args",
  "config.basePath",
  "logger"
];

module.exports = { "preprocessor:rollup": ["factory", createPreprocessor] };

"use strict";

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

function createPreprocessor(options, preconfig, basePath, logger) {
  const cache = new Map();
  const log = logger.create("preprocessor.rollup");

  return async function preprocess(original, file, done) {
    const location = path.relative(basePath, file.path);
    try {
      const config = Object.assign({}, options, preconfig.options, {
        input: file.path,
        cache: cache.get(file.path)
      });

      const bundle = await rollup.rollup(config);
      cache.set(file.path, bundle.cache);

      log.info("Generating bundle for ./%s", location);
      const { output } = await bundle.generate(config);

      for (const result of output) {
        if (!result.isAsset) {
          const { code, map } = result;
          const { sourcemap } = config.output;

          file.sourceMap = map;

          const processed =
            sourcemap === "inline"
              ? code + `\n//# sourceMappingURL=${map.toUrl()}\n`
              : code;

          return done(null, processed);
        }
      }
      log.warn("Nothing was processed.");
      done(null, original);
    } catch (error) {
      log.error("Failed to process ./%s\n\n%s\n", location, error.stack);
      done(error, null);
    }
  };
}

createPreprocessor.$inject = [
  "config.rollupPreprocessor",
  "args",
  "config.basePath",
  "logger"
];

module.exports = { "preprocessor:rollup": ["factory", createPreprocessor] };

Add more info log output


/**
 * handle autoplay
 *
 * @param  {element} slideTo     slide to frame function
 * @param  {element} options     slider options
 */
export default function initAutoplay (slide, options) {
    let autoplayTime = (typeof options.autoplay === 'number') ? options.autoplay : 3000;
    let onAutoplayStart = window.setInterval(() => {
        slide(false, true);
    }, autoplayTime);

    return onAutoplayStart;
}

/**
 * handle autoplay
 *
 * @param  {element} slideTo     slide to frame function
 * @param  {element} options     slider options
 */
export default function initAutoplay (slide, options) {
    let autoplayTime = (typeof options.autoplay === 'number') ? options.autoplay : 3000;
    let onAutoplayStart = window.setInterval(() => {
        slide(false, options.direction === 'ltr' ? true : false);
    }, autoplayTime);

    return onAutoplayStart;
}

Fix Autoplay diretion in case of RTL


/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @flow
 */
'use strict';

const path = require('path');

type ProjectInfo = {
  name: string;
  isWorkspace: boolean;
}

function findXcodeProject(files: Array<string>): ?ProjectInfo {
  const sortedFiles = files.sort();
  for (let i = sortedFiles.length - 1; i >= 0; i--) {
    const fileName = files[i];
    const ext = path.extname(fileName);

    if (ext === '.xcworkspace') {
      return {
        name: fileName,
        isWorkspace: true,
      };
    }
    if (ext === '.xcodeproj') {
      return {
        name: fileName,
        isWorkspace: false,
      };
    }
  }

  return null;
}

module.exports = findXcodeProject;

/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @flow
 */
'use strict';

const path = require('path');

function findXcodeProject(files) {
  const sortedFiles = files.sort();
  for (let i = sortedFiles.length - 1; i >= 0; i--) {
    const fileName = files[i];
    const ext = path.extname(fileName);

    if (ext === '.xcworkspace') {
      return {
        name: fileName,
        isWorkspace: true,
      };
    }
    if (ext === '.xcodeproj') {
      return {
        name: fileName,
        isWorkspace: false,
      };
    }
  }

  return null;
}

module.exports = findXcodeProject;

Remove typescript, as it is not compiled by rnpm


const assert = require('assert');
const { crypto, config } = require('./config');

describe("WebCrypto", () => {

    it("get random values", () => {
        var buf = new Uint8Array(16);
        var check = new Buffer(buf).toString("base64");
        assert.notEqual(new Buffer(crypto.getRandomValues(buf)).toString("base64"), check, "Has no random values");
    })

    it("get random values with large buffer", () => {
        var buf = new Uint8Array(65600);
        assert.throws(() => {
            crypto.getRandomValues(buf);
        }, Error);
    })

    it("reset", (done) => {
        var WebCrypto = require("../").WebCrypto;

        const crypto = new WebCrypto(config);
        const currentHandle = crypto.session.handle.toString("hex");
        crypto.reset()
            .then(() => {
                const newHandle = crypto.session.handle.toString("hex");
                assert(currentHandle !== newHandle, true, "handle of session wasn't changed");
            })
            .then(done, done);
    })
})

const assert = require('assert');
const { crypto, config } = require('./config');

describe("WebCrypto", () => {

    it("get random values", () => {
        var buf = new Uint8Array(16);
        var check = new Buffer(buf).toString("base64");
        assert.notEqual(new Buffer(crypto.getRandomValues(buf)).toString("base64"), check, "Has no random values");
    })

    it("get random values with large buffer", () => {
        var buf = new Uint8Array(65600);
        assert.throws(() => {
            crypto.getRandomValues(buf);
        }, Error);
    })

    it("reset", (done) => {
        const currentHandle = crypto.session.handle.toString("hex");
        crypto.reset()
            .then(() => {
                crypto.login(config.pin);
                const newHandle = crypto.session.handle.toString("hex");
                assert(currentHandle !== newHandle, true, "handle of session wasn't changed");
            })
            .then(done, done);
    })
})

Update - cannot open 2 session


(function (module) {
    module.controller("FilesEditController", FilesEditController);

    FilesEditController.$inject = ['file'];

    /* @ngInject */
    function FilesEditController(file) {
        var ctrl = this;
        ctrl.file = file;
    }
}(angular.module('materialscommons')));

(function (module) {
    module.controller("FileEditController", FileEditController);

    FileEditController.$inject = ['file'];

    /* @ngInject */
    function FileEditController(file) {
        var ctrl = this;
        ctrl.file = file;
    }
}(angular.module('materialscommons')));

Change name of controller to match file.


'use strict';

module.exports = {
    'get': {
        'description': 'This endpoint returns information about invoices that have been approved. The response includes basic details of each invoice, such as sender and receiver information.',
        'responses': {
            '200': {
                'description': 'An array of approved invoices',
                'schema': {
                    '$ref': '#/definitions/InvoiceApproved'
                }
            },
            'default': {
                'description': 'Unexpected error',
                'schema': {
                    '$ref': '#/definitions/Error'
                }
            }
        }
    },
    'post': {
        'description': 'This endpoint allows you to send approved invoices.',
        'parameters': [
            {
                'name': 'body',
                'in': 'body',
                'description': 'The Invoice JSON you want to POST',
                'schema': {
                    'pendingInvoices': {
                        'type': 'array',
                        'items': {
                            '$ref': '#/definitions/Id'
                        }
                    }
                },
                'required': true
            }
        ],
        'responses': {
            '200': {
                'description': 'Ids of the approved invoices',
                'schema': {
                    'type': 'array',
                    'items': {
                        '$ref': '#/definitions/Id'
                    }
                }
            },
            'default': {
                'description': 'Unexpected error',
                'schema': {
                    '$ref': '#/definitions/Error'
                }
            }
        }
    }
};

'use strict';

module.exports = {
    'get': {
        'description': 'This endpoint returns information about invoices that have been approved. The response includes basic details of each invoice, such as sender and receiver information.',
        'responses': {
            '200': {
                'description': 'An array of approved invoices',
                'schema': {
                    '$ref': '#/definitions/InvoiceApproved'
                }
            },
            'default': {
                'description': 'Unexpected error',
                'schema': {
                    '$ref': '#/definitions/Error'
                }
            }
        }
    },
    // XXXXX: figure out proper semantics for approving (separate resource?)
    /*
    'post': {
        'description': 'This endpoint allows you to send approved invoices.',
        'parameters': [
            {
                'name': 'body',
                'in': 'body',
                'description': 'The Invoice JSON you want to POST',
                'schema': {
                    'approvedInvoices': {
                        'type': 'array',
                        'items': {
                            '$ref': '#/definitions/Id'
                        }
                    }
                },
                'required': true
            }
        ],
        'responses': {
            '200': {
                'description': 'Ids of the approved invoices',
                'schema': {
                    'type': 'array',
                    'items': {
                        '$ref': '#/definitions/Id'
                    }
                }
            },
            'default': {
                'description': 'Unexpected error',
                'schema': {
                    '$ref': '#/definitions/Error'
                }
            }
        }
    }
    */
};

Disable POST approved invoice for now


import chai, { expect } from 'chai';
import mockery from 'mockery';
import dirtyChai from 'dirty-chai';
chai.use(dirtyChai);

import { shallow } from 'enzyme';

import React from 'react';

describe('WrapperUserIndexPage', () => {
  beforeEach(() => {
    mockery.enable({
      warnOnReplace: false,
      warnOnUnregistered: false,
      useCleanCache: true,
    });
    mockery.registerMock(
      'decorators',
      require('helpers/test/decoratorsMock')
    );
  });

  afterEach(() => {
    mockery.deregisterMock('decorators');
    mockery.disable();
  });

  it('should exists', () => {
    const WrapperUserIndexPage = require('../WrapperUserIndexPage');

    const wrapper = shallow((
      <WrapperUserIndexPage />
    ));

    expect(wrapper).to.have.length(1);
  });

  it('should render inner components', () => {
    const WrapperUserIndexPage = require('../WrapperUserIndexPage');

    const wrapper = shallow((
      <WrapperUserIndexPage />
    ));

    expect(wrapper.find('div')).to.have.length(1);
  });
});

import chai, { expect } from 'chai';
import mockery from 'mockery';
import dirtyChai from 'dirty-chai';
chai.use(dirtyChai);

import { shallow } from 'enzyme';

import React from 'react';

describe('WrapperUserIndexPage', () => {
  beforeEach(() => {
    mockery.enable({
      warnOnReplace: false,
      warnOnUnregistered: false,
      useCleanCache: true,
    });
    mockery.registerMock(
      'decorators',
      require('helpers/test/decoratorsMock')
    );
    mockery.registerMock(
      'components/CardsList',
      require('helpers/test/componentsMock').CardsList
    );
    mockery.registerMock(
      'components/WelcomeCard',
      require('helpers/test/componentsMock').WelcomeCard
    );
  });

  afterEach(() => {
    mockery.deregisterMock('decorators');
    mockery.deregisterMock('components/CardsList');
    mockery.deregisterMock('components/WelcomeCard');
    mockery.disable();
  });

  it('should exists', () => {
    const WrapperUserIndexPage = require('../WrapperUserIndexPage');

    const wrapper = shallow((
      <WrapperUserIndexPage />
    ));

    expect(wrapper).to.have.length(1);
  });

  it('should render inner components', () => {
    const WrapperUserIndexPage = require('../WrapperUserIndexPage');

    const wrapper = shallow((
      <WrapperUserIndexPage />
    ));

    expect(wrapper.find('CardsList')).to.have.length(1);
    expect(wrapper.find('WelcomeCard')).to.have.length(1);
    expect(wrapper.find('div')).to.have.length(1);
  });
});

Update wrapper user index route tests.


'use strict';

var tablesUpdater = require('./tablesUpdater');
var resultsUpdater = require('./resultsUpdater');
var tournamentsUpdater = require('./tournamentsUpdater');
var groupsUpdater = require('./groupsUpdater');
var scorersUpdater = require('./scorersUpdater');
var assistsUpdater = require('./assistsUpdater');

// Updates league data
function updateLeague(leagueArg) {
    leagueArg = leagueArg || true;

    tablesUpdater.update(leagueArg);
    resultsUpdater.update(leagueArg);
    scorersUpdater.update(leagueArg, null);
    assistsUpdater.update(leagueArg, null);
}

// Updates competition data
function updateCompetition(competitionArg) {
    competitionArg = competitionArg || true;

    tournamentsUpdater.update(competitionArg);
    groupsUpdater.update(competitionArg);
    scorersUpdater.update(null, competitionArg);
    assistsUpdater.update(null, competitionArg);
}

module.exports = {
    updateLeague: updateLeague,
    updateCompetition: updateCompetition
};

'use strict';

var tablesUpdater = require('./tablesUpdater');
var resultsUpdater = require('./resultsUpdater');
var tournamentsUpdater = require('./tournamentsUpdater');
// var groupsUpdater = require('./groupsUpdater');
var scorersUpdater = require('./scorersUpdater');
var assistsUpdater = require('./assistsUpdater');

// Updates league data
function updateLeague(leagueArg) {
    leagueArg = leagueArg || true;

    tablesUpdater.update(leagueArg);
    resultsUpdater.update(leagueArg);
    scorersUpdater.update(leagueArg, null);
    assistsUpdater.update(leagueArg, null);
}

// Updates competition data
function updateCompetition(competitionArg) {
    competitionArg = competitionArg || true;

    tournamentsUpdater.update(competitionArg);
    // groupsUpdater.update(competitionArg); Disable groups update until next year
    scorersUpdater.update(null, competitionArg);
    assistsUpdater.update(null, competitionArg);
}

module.exports = {
    updateLeague: updateLeague,
    updateCompetition: updateCompetition
};

Disable groups update until next year


({
    testPropertiesExposed: function(cmp) {
        var testUtils = cmp.get("v.testUtils");

        ["appCodeName", "appName", "appVersion", "cookieEnabled", "geolocation",
         "language", "onLine", "platform", "product", "userAgent"].forEach(function(name) {
        	testUtils.assertTrue(name in window.navigator, "Expected window.navigator." + name + " to be exposed as a property");
    	});
    },

    testLanguage: function(cmp) {
        var testUtils = cmp.get("v.testUtils");
    	testUtils.assertEquals(window.navigator.language, "en-US", "Expect window.navigator.language to be 'en-US'");
    }
})

({
    testPropertiesExposed: function(cmp) {
        var testUtils = cmp.get("v.testUtils");

        ["appCodeName", "appName", "appVersion", "cookieEnabled", "geolocation",
         "language", "onLine", "platform", "product", "userAgent"].forEach(function(name) {
        	testUtils.assertTrue(name in window.navigator, "Expected window.navigator." + name + " to be exposed as a property");
    	});
    },

    testLanguage: function(cmp) {
        var testUtils = cmp.get("v.testUtils");
    	testUtils.assertEquals("en-us", window.navigator.language.toLowerCase(), "Unexpected window.navigator.language value");
    }
})

Fix case sensitivity issue in test