<commit_before>/* global __dirname, process */

const {
    app: APP,
    BrowserWindow,
    Menu,
    shell
} = require('electron');
const isDev = require('electron-is-dev');
const windowStateKeeper = require('electron-window-state');
const {
    setupAlwaysOnTopMain,
    initPopupsConfigurationMain,
    getPopupTarget
} = require('jitsi-meet-electron-utils');
const path = require('path');
const URL = require('url');

/**
 * Load debug utilities (don't open the DevTools window by default though).
 */
require('electron-debug')({ showDevTools: false });

/**
 * Path to root directory
 */
const basePath = isDev ? __dirname : APP.getAppPath();

/**
 * URL for index.html which will be our entry point.
 */
const indexURL = URL.format({
    pathname: path.resolve(basePath, './build/index.html'),
    protocol: 'file:',
    slashes: true
});

/**
 * The window object that will load the iframe with Jitsi Meet.
 * IMPORTANT: Must be defined as global in order to not be garbage collected
 * acidentally.
 */
let jitsiMeetWindow = null;

/**
 * Sets the APP object listeners.
 */
function setAPPListeners() {
    APP.on('ready', createJitsiMeetWindow);
    APP.on('window-all-closed', () => {
        // Don't quit the application for macOS.
        if (process.platform !== 'darwin') {
            APP.quit();
        }
    });
    APP.on('activate', () => {
        if (jitsiMeetWindow === null) {
            createJitsiMeetWindow();
        }
    });
    APP.on('certificate-error',
        // eslint-disable-next-line max-params
        (event, webContents, url, error, certificate, callback) => {
            if (url.startsWith('https://localhost')) {
                event.preventDefault();
                callback(true);
            } else {
                callback(false);
            }
        }
    );
}

/**
 * Opens new window with index.html(Jitsi Meet is loaded in iframe there).
 */
function createJitsiMeetWindow() {
    Menu.setApplicationMenu(null);

    // Load the previous state with fallback to defaults
    const jitsiMeetWindowState = windowStateKeeper({
        defaultWidth: 800,
        defaultHeight: 600
    });

    // Options used when creating the main Jitsi Meet window.
    const jitsiMeetWindowOptions = {
        x: jitsiMeetWindowState.x,
        y: jitsiMeetWindowState.y,
        width: jitsiMeetWindowState.width,
        height: jitsiMeetWindowState.height,
        minWidth: 800,
        minHeight: 600,
        titleBarStyle: 'hidden',
        webPreferences: {
            nativeWindowOpen: true
        }
    };

    jitsiMeetWindow = new BrowserWindow(jitsiMeetWindowOptions);
    jitsiMeetWindowState.manage(jitsiMeetWindow);
    jitsiMeetWindow.loadURL(indexURL);
    initPopupsConfigurationMain(jitsiMeetWindow);

    jitsiMeetWindow.webContents.on('new-window', (event, url, frameName) => {
        const target = getPopupTarget(url, frameName);

        if (!target || target === 'browser') {
            event.preventDefault();
            shell.openExternal(url);
        }
    });

    setupAlwaysOnTopMain(jitsiMeetWindow);

    jitsiMeetWindow.on('closed', () => {
        jitsiMeetWindow = null;
    });
}

//  Start the application:
setAPPListeners();
<commit_msg>Show window once ready-to-show<commit_after>/* global __dirname, process */

const {
    app: APP,
    BrowserWindow,
    Menu,
    shell
} = require('electron');
const isDev = require('electron-is-dev');
const windowStateKeeper = require('electron-window-state');
const {
    setupAlwaysOnTopMain,
    initPopupsConfigurationMain,
    getPopupTarget
} = require('jitsi-meet-electron-utils');
const path = require('path');
const URL = require('url');

/**
 * Load debug utilities (don't open the DevTools window by default though).
 */
require('electron-debug')({ showDevTools: false });

/**
 * Path to root directory
 */
const basePath = isDev ? __dirname : APP.getAppPath();

/**
 * URL for index.html which will be our entry point.
 */
const indexURL = URL.format({
    pathname: path.resolve(basePath, './build/index.html'),
    protocol: 'file:',
    slashes: true
});

/**
 * The window object that will load the iframe with Jitsi Meet.
 * IMPORTANT: Must be defined as global in order to not be garbage collected
 * acidentally.
 */
let jitsiMeetWindow = null;

/**
 * Sets the APP object listeners.
 */
function setAPPListeners() {
    APP.on('ready', createJitsiMeetWindow);
    APP.on('window-all-closed', () => {
        // Don't quit the application for macOS.
        if (process.platform !== 'darwin') {
            APP.quit();
        }
    });
    APP.on('activate', () => {
        if (jitsiMeetWindow === null) {
            createJitsiMeetWindow();
        }
    });
    APP.on('certificate-error',
        // eslint-disable-next-line max-params
        (event, webContents, url, error, certificate, callback) => {
            if (url.startsWith('https://localhost')) {
                event.preventDefault();
                callback(true);
            } else {
                callback(false);
            }
        }
    );
}

/**
 * Opens new window with index.html(Jitsi Meet is loaded in iframe there).
 */
function createJitsiMeetWindow() {
    Menu.setApplicationMenu(null);

    // Load the previous state with fallback to defaults
    const jitsiMeetWindowState = windowStateKeeper({
        defaultWidth: 800,
        defaultHeight: 600
    });

    // Options used when creating the main Jitsi Meet window.
    const jitsiMeetWindowOptions = {
        x: jitsiMeetWindowState.x,
        y: jitsiMeetWindowState.y,
        width: jitsiMeetWindowState.width,
        height: jitsiMeetWindowState.height,
        minWidth: 800,
        minHeight: 600,
        show: false,
        titleBarStyle: 'hidden',
        webPreferences: {
            nativeWindowOpen: true
        }
    };

    jitsiMeetWindow = new BrowserWindow(jitsiMeetWindowOptions);
    jitsiMeetWindowState.manage(jitsiMeetWindow);
    jitsiMeetWindow.loadURL(indexURL);
    initPopupsConfigurationMain(jitsiMeetWindow);

    jitsiMeetWindow.webContents.on('new-window', (event, url, frameName) => {
        const target = getPopupTarget(url, frameName);

        if (!target || target === 'browser') {
            event.preventDefault();
            shell.openExternal(url);
        }
    });

    setupAlwaysOnTopMain(jitsiMeetWindow);

    jitsiMeetWindow.on('closed', () => {
        jitsiMeetWindow = null;
    });
    jitsiMeetWindow.once('ready-to-show', () => {
        jitsiMeetWindow.show();
    });
}

//  Start the application:
setAPPListeners();
<|endoftext|>


<commit_before>var Bind = require("github/jillix/bind");
var Events = require("github/jillix/events");

module.exports = function(config) {

    var self = this;
    Events.call(self, config);

    // TODO
};
<commit_msg>Module logic.<commit_after>var Bind = require("github/jillix/bind");
var Events = require("github/jillix/events");

module.exports = function(config) {

    var self = this;
    Events.call(self, config);

    $(self.dom).on("submit", "form", function (e) {

        e.preventDefault();

        var $form = $(this);
        var serializedForm = {};

        $form.find("[data-field]").each(function () {
            var $element = $(this);
            var how = $element.attr("data-value") || "val";
            var params = $element.attr("data-params");
            var field = $element.attr("data-field");
            var value;

            if (!params) {
                value = $element[how]();
            } else {
                value = $element[how](params);
            }

            serializedForm[field] = value;
        });

        self.emit(config.eventName || "serializedForm", serializedForm);
    });
};
<|endoftext|>


<commit_before>if (typeof web3 !== 'undefined') {
	web3 = new Web3(web3.currentProvider);
} else {
	// set the provider you want from Web3.providers
	web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
}

var TicTacToeRules;
var TicTacToeAdjudicator;
var TicTacToeLockedState;

function sign(signingAddress, hash) {
	var signature = web3.eth.sign(signingAddress, hash);
	return [
		27 + parseInt(signature.slice(130, 132), 16),
		signature.slice(0, 66),
		'0x' + signature.slice(66, 130)
	];
}

function createTicTacToeRules(form) {
	TicTacToeRules = web3.eth.contract(TIC_TAC_TOE_RULES_INTERFACE).new(
		web3.eth.accounts[form.addressX.value],
		web3.eth.accounts[form.addressO.value],
		form.timeout.value,
		{
			from: web3.eth.accounts[$('#sender').val()],
			data: CONTRACT_BYTECODE,
			gas: 4700000
		}, function (e, contract) {
			if (typeof contract.address !== 'undefined') {
				TicTacToeAdjudicator = web3.eth.contract(TIC_TAC_TOE_ADJUDICATOR_INTERFACE).at(contract.getAdjudicatorAddress());
				TicTacToeLockedState = web3.eth.contract(TIC_TAC_TOE_LOCKED_STATE_INTERFACE).at(TicTacToeAdjudicator.getLockedStateAddress());
				$('#existingContract input[name=address]').val(contract.address);
				localStorage.setItem('address', contract.address);
				alert('Contract has been mined at: ' +contract.address);
			} else {
				alert('Contract transaction: ' +contract.transactionHash);
			}
		}
	);
}

function attachToContract(form) {
	TicTacToeRules = web3.eth.contract(TIC_TAC_TOE_RULES_INTERFACE).at(form.address.value);
	TicTacToeAdjudicator = web3.eth.contract(TIC_TAC_TOE_ADJUDICATOR_INTERFACE).at(TicTacToeRules.getAdjudicatorAddress());
	TicTacToeLockedState = web3.eth.contract(TIC_TAC_TOE_LOCKED_STATE_INTERFACE).at(TicTacToeAdjudicator.getLockedStateAddress());
	localStorage.setItem('address', form.address.value);
	alert("Attached to contract!");
}

function signState(form) {
	var state = ('00' + (
			parseInt(form.winner.value)
			| (form.adjudicationDepositX.checked ? parseInt(form.adjudicationDepositX.value) : 0)
			| (form.adjudicationDepositO.checked ? parseInt(form.adjudicationDepositO.value) : 0)
			| (form.disconnectDepositX.checked ? parseInt(form.disconnectDepositX.value) : 0)
			| (form.disconnectDepositO.checked ? parseInt(form.disconnectDepositO.value) : 0)
			| (form.cheating.checked ? parseInt(form.cheating.value) : 0)
		).toString(16)).slice(-2);

	var nonce = '';
	for (var i = 0; i < 64; i++) {
		nonce += '0';
	}
	nonce += parseInt(form.nonce.value).toString(16);

	var toBeHashed = '0x'
		+state
		+nonce.slice(-64)
		+TicTacToeRules.address.slice(2);

	var signature = sign(web3.eth.accounts[$('#sender').val()], web3.sha3(toBeHashed, {encoding: 'hex'}));

	$('#stateTable').append('
'
			+ web3.eth.accounts[$('#sender').val()]
			+ '	'
			+ '0x' +state
			+ '	'
			+ form.nonce.value
			+ '	'
			+ signature[0]
			+ '	'
			+ signature[1]
			+ '	'
			+ signature[2]
		);
}

function signBoard(form) {
	var board = '';

	var boardElements = $(form).find('.boardItem');
	for (var i = 0; i < boardElements.length; i++) {
		board += ('00' + parseInt(boardElements[i].value).toString(16)).slice(-2);
	}
	board += ('00' + parseInt(form.lastPlayer.value).toString(16)).slice(-2);
	board.slice(-20);

	var nonce = '';
	for (var i = 0; i < 64; i++) {
		nonce += '0';
	}
	nonce += parseInt(form.nonce.value).toString(16);

	var toBeHashed = '0x'
		+board
		+nonce.slice(-64)
		+TicTacToeRules.address.slice(2);

	var signature = sign(web3.eth.accounts[$('#sender').val()], web3.sha3(toBeHashed, {encoding: 'hex'}));

	$('#boardTable').append('
'
			+ web3.eth.accounts[$('#sender').val()]
			+ '	'
			+ '0x' +board
			+ '	'
			+ form.nonce.value
			+ '	'
			+ signature[0]
			+ '	'
			+ signature[1]
			+ '	'
			+ signature[2]
		);
}

function stringToNumberArray(string) {
	array = string.match(/.{2}/g);
	array.splice(0, 1);
	for (var i = 0; i < array.length; i++) {
		array[i] = parseInt(array[i], 16);
	}
	return array;
}

function sendState(form) {
	var rXArray = stringToNumberArray(form.rX.value);
	var sXArray = stringToNumberArray(form.sX.value);
	var rOArray = stringToNumberArray(form.rO.value);
	var sOArray = stringToNumberArray(form.sO.value);

	TicTacToeRules.sendState.sendTransaction(
			[parseInt(form.state.value, 16)],
			parseInt(form.nonce.value),
			parseInt(form.vX.value),
			rXArray,
			sXArray,
			parseInt(form.vO.value),
			rOArray,
			sOArray,
			{
				from: web3.eth.accounts[$('#sender').val()]
			}
		);
		alert('Call sent to blockchain.');
}

function getState(form) {
	alert(TicTacToeAdjudicator.getStateAt(0));
}

function getNonce(form) {
	alert(TicTacToeAdjudicator.getNonce());
}

function giveConsent(form) {
	var nonce = '';
	for (var i = 0; i < 64; i++) {
		nonce += '0';
	}
	nonce += parseInt(form.nonce.value).toString(16);

	var toBeHashed = '0x'
		+nonce.slice(-64)
		+TicTacToeRules.address.slice(2);

	var signature = sign(web3.eth.accounts[$('#sender').val()], web3.sha3(toBeHashed, {encoding: 'hex'}));

	TicTacToeAdjudicator.giveConsent.sendTransaction(
			signature[0],
			signature[1],
			signature[2],
			{
				from: web3.eth.accounts[$('#sender').val()]
			}
		);

	$('#consentTable').append('
'
			+ web3.eth.accounts[$('#sender').val()]
			+ '	'
			+ form.nonce.value
			+ '	'
			+ signature[0]
			+ '	'
			+ signature[1]
			+ '	'
			+ signature[2]
		);
}

function finaliseChannel(form) {
	TicTacToeAdjudicator.finaliseChannel.sendTransaction({
			from: web3.eth.accounts[$('#sender').val()]
	});
	alert('Call sent to blockchain.');
}

function depositFunds(form) {
	TicTacToeLockedState.deposit.sendTransaction(
			parseInt(form.account.value),
			{
				from: web3.eth.accounts[$('#sender').val()],
				value: form.amount.value
			}
		);
	alert('Call sent to blockchain.');
}

function getBalance(form) {
	alert(TicTacToeLockedState.getBalance(parseInt(form.account.value)));
}

$(document).ready(function () {
	// Populate address lists with addresses from web3.eth
	for (var i = 0; i < web3.eth.accounts.length; i++) {
		$('.addresses').append("<option value=\"" +i +"\">" +web3.eth.accounts[i] +"</option>");
	}

	// Persistent state
	if (localStorage.getItem('address')) {
		$('#existingContract input[name=address]').val(localStorage.getItem('address'));
	}

});
<commit_msg>FIXED IT<commit_after>if (typeof web3 !== 'undefined') {
	web3 = new Web3(web3.currentProvider);
} else {
	// set the provider you want from Web3.providers
	web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
}

var TicTacToeRules;
var TicTacToeAdjudicator;
var TicTacToeLockedState;

function sign(signingAddress, hash) {
	var signature = web3.eth.sign(signingAddress, hash);
	return [
		27 + parseInt(signature.slice(130, 132), 16),
		signature.slice(0, 66),
		'0x' + signature.slice(66, 130)
	];
}

function createTicTacToeRules(form) {
	TicTacToeRules = web3.eth.contract(TIC_TAC_TOE_RULES_INTERFACE).new(
		web3.eth.accounts[form.addressX.value],
		web3.eth.accounts[form.addressO.value],
		form.timeout.value,
		{
			from: web3.eth.accounts[$('#sender').val()],
			data: CONTRACT_BYTECODE,
			gas: 4700000
		}, function (e, contract) {
			if (typeof contract.address !== 'undefined') {
				TicTacToeAdjudicator = web3.eth.contract(TIC_TAC_TOE_ADJUDICATOR_INTERFACE).at(contract.getAdjudicatorAddress());
				TicTacToeLockedState = web3.eth.contract(TIC_TAC_TOE_LOCKED_STATE_INTERFACE).at(TicTacToeAdjudicator.getLockedStateAddress());
				$('#existingContract input[name=address]').val(contract.address);
				localStorage.setItem('address', contract.address);
				alert('Contract has been mined at: ' +contract.address);
			} else {
				alert('Contract transaction: ' +contract.transactionHash);
			}
		}
	);
}

function attachToContract(form) {
	TicTacToeRules = web3.eth.contract(TIC_TAC_TOE_RULES_INTERFACE).at(form.address.value);
	TicTacToeAdjudicator = web3.eth.contract(TIC_TAC_TOE_ADJUDICATOR_INTERFACE).at(TicTacToeRules.getAdjudicatorAddress());
	TicTacToeLockedState = web3.eth.contract(TIC_TAC_TOE_LOCKED_STATE_INTERFACE).at(TicTacToeAdjudicator.getLockedStateAddress());
	localStorage.setItem('address', form.address.value);
	alert("Attached to contract!");
}

function signState(form) {
	var state = ('00' + (
			parseInt(form.winner.value)
			| (form.adjudicationDepositX.checked ? parseInt(form.adjudicationDepositX.value) : 0)
			| (form.adjudicationDepositO.checked ? parseInt(form.adjudicationDepositO.value) : 0)
			| (form.disconnectDepositX.checked ? parseInt(form.disconnectDepositX.value) : 0)
			| (form.disconnectDepositO.checked ? parseInt(form.disconnectDepositO.value) : 0)
			| (form.cheating.checked ? parseInt(form.cheating.value) : 0)
		).toString(16)).slice(-2);

	var nonce = '';
	for (var i = 0; i < 64; i++) {
		nonce += '0';
	}
	nonce += parseInt(form.nonce.value).toString(16);

	var toBeHashed = '0x'
		+state
		+nonce.slice(-64)
		+TicTacToeRules.address.slice(2);

	var signature = sign(web3.eth.accounts[$('#sender').val()], web3.sha3(toBeHashed, {encoding: 'hex'}));

	$('#stateTable').append('
'
			+ web3.eth.accounts[$('#sender').val()]
			+ '	'
			+ '0x' +state
			+ '	'
			+ form.nonce.value
			+ '	'
			+ signature[0]
			+ '	'
			+ signature[1]
			+ '	'
			+ signature[2]
		);
}

function signBoard(form) {
	var board = '';

	var boardElements = $(form).find('.boardItem');
	for (var i = 0; i < boardElements.length; i++) {
		board += ('00' + parseInt(boardElements[i].value).toString(16)).slice(-2);
	}
	board += ('00' + parseInt(form.lastPlayer.value).toString(16)).slice(-2);
	board.slice(-20);

	var nonce = '';
	for (var i = 0; i < 64; i++) {
		nonce += '0';
	}
	nonce += parseInt(form.nonce.value).toString(16);

	var toBeHashed = '0x'
		+board
		+nonce.slice(-64)
		+TicTacToeRules.address.slice(2);

	var signature = sign(web3.eth.accounts[$('#sender').val()], web3.sha3(toBeHashed, {encoding: 'hex'}));

	$('#boardTable').append('
'
			+ web3.eth.accounts[$('#sender').val()]
			+ '	'
			+ '0x' +board
			+ '	'
			+ form.nonce.value
			+ '	'
			+ signature[0]
			+ '	'
			+ signature[1]
			+ '	'
			+ signature[2]
		);
}

function sendState(form) {
	TicTacToeRules.sendState.sendTransaction(
			form.state.value,
			parseInt(form.nonce.value),
			parseInt(form.vX.value),
			form.rX.value,
			form.sX.value,
			parseInt(form.vO.value),
			form.rO.value,
			form.sO.value,
			{
				from: web3.eth.accounts[$('#sender').val()],
				gas: 4700000
			}
		);
		alert('Call sent to blockchain.');
}

function getState(form) {
	alert(TicTacToeAdjudicator.getStateAt(0));
}

function getNonce(form) {
	alert(TicTacToeAdjudicator.getNonce());
}

function giveConsent(form) {
	var nonce = '';
	for (var i = 0; i < 64; i++) {
		nonce += '0';
	}
	nonce += parseInt(form.nonce.value).toString(16);

	var toBeHashed = '0x'
		+nonce.slice(-64)
		+TicTacToeRules.address.slice(2);

	var signature = sign(web3.eth.accounts[$('#sender').val()], web3.sha3(toBeHashed, {encoding: 'hex'}));

	TicTacToeAdjudicator.giveConsent.sendTransaction(
			signature[0],
			signature[1],
			signature[2],
			{
				from: web3.eth.accounts[$('#sender').val()]
			}
		);

	$('#consentTable').append('
'
			+ web3.eth.accounts[$('#sender').val()]
			+ '	'
			+ form.nonce.value
			+ '	'
			+ signature[0]
			+ '	'
			+ signature[1]
			+ '	'
			+ signature[2]
		);
}

function finaliseChannel(form) {
	TicTacToeAdjudicator.finaliseChannel.sendTransaction({
			from: web3.eth.accounts[$('#sender').val()]
	});
	alert('Call sent to blockchain.');
}

function depositFunds(form) {
	TicTacToeLockedState.deposit.sendTransaction(
			parseInt(form.account.value),
			{
				from: web3.eth.accounts[$('#sender').val()],
				value: form.amount.value
			}
		);
	alert('Call sent to blockchain.');
}

function getBalance(form) {
	alert(TicTacToeLockedState.getBalance(parseInt(form.account.value)));
}

$(document).ready(function () {
	// Populate address lists with addresses from web3.eth
	for (var i = 0; i < web3.eth.accounts.length; i++) {
		$('.addresses').append("<option value=\"" +i +"\">" +web3.eth.accounts[i] +"</option>");
	}

	// Persistent state
	if (localStorage.getItem('address')) {
		$('#existingContract input[name=address]').val(localStorage.getItem('address'));
	}

});
<|endoftext|>


<commit_before>/*!
 * Copyright 2018 Google LLC. 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.
 */

'use strict';

module.exports = {
  opts: {
    readme: './README.md',
    package: './package.json',
    template: './node_modules/jsdoc-fresh',
    recurse: true,
    verbose: true,
    destination: './docs/'
  },
  plugins: [
    'plugins/markdown',
    'jsdoc-region-tag'
  ],
  source: {
    excludePattern: '(^|\\/|\\\\)[._]',
    include: [
      '{{ source_location or 'src' }}'
    ],
    includePattern: '\\.js$'
  },
  templates: {
    copyright: 'Copyright 2018 Google, LLC.',
    includeDate: false,
    sourceFiles: false,
    systemName: '{{ metadata['name'] }}',
    theme: 'lumen'
  },
  markdown: {
    idInHeadings: true
  }
};
<commit_msg>chore: update copyright header to be consistent (#351)<commit_after>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

'use strict';

module.exports = {
  opts: {
    readme: './README.md',
    package: './package.json',
    template: './node_modules/jsdoc-fresh',
    recurse: true,
    verbose: true,
    destination: './docs/'
  },
  plugins: [
    'plugins/markdown',
    'jsdoc-region-tag'
  ],
  source: {
    excludePattern: '(^|\\/|\\\\)[._]',
    include: [
      '{{ source_location or 'src' }}'
    ],
    includePattern: '\\.js$'
  },
  templates: {
    copyright: 'Copyright 2018 Google, LLC.',
    includeDate: false,
    sourceFiles: false,
    systemName: '{{ metadata['name'] }}',
    theme: 'lumen'
  },
  markdown: {
    idInHeadings: true
  }
};
<|endoftext|>


<commit_before>/**
 * Copyright 2014 Google Inc. 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.
 */

var GAPIToken = require('gapitoken'),
  async = require('async'),
  req = require('request');

/**
 * Token represents an access token
 * @param {string} accessToken  Access token.
 * @param {string} expiry       Expiration datetime.
 */
function Token(accessToken, expiry) {
  this.accessToken = accessToken;
  this.expiry = expiry;
}

/**
 * Returns true if token is expired.
 * @return {Boolean}
 */
Token.prototype.isExpired = function() {
  if (!this.accessToken || !this.expiry) {
    return true;
  }
  return new Date().getTime() - this.expiry.getTime() > 0;
};

/**
 * @param  {Object} opts Options. { email, privateKey, scopes }
 */
function Connection(opts) {
  // TODO: If no email and key is provided, use metaserver to retrieve a new token.
  this.email = opts.email; // client email for the service account
  this.privateKey = opts.privateKey; // contains the contents of a pem file
  this.scopes = opts.scopes || [];
  this.token = null; // existing access token, if exists

  this.isConnecting = false;
  this.waitQueue = [];
}

/**
 * Retrieves a token to authorize the requests.

 * @param  {Function} callback  Callback.
 */
Connection.prototype.connect = function(callback) {
  // TODO(jbd): Connect should be context aware,
  // should not require an email and key, if it's
  // running on Google Compute Engine.
  var that = this;
  this.isConnecting = true;
  // retrieves an access token
  this.fetchToken(function(err, token) {
    if (!err) {
      that.token = token;
    }
    that.isConnecting = false;
    callback(err);
  });
};

/**
 * Fetches a new access token.
 * @param  {Function} callback Callback function.
 */
Connection.prototype.fetchToken = function(callback) {
  var that = this;
  var gapi = new GAPIToken({
    iss: this.email,
    keyFile: this.privateKey,
    scope: this.scopes.join(' ')
  }, function(err) {
    if (err) {
      return callback(err);
    }
    gapi.getToken(function(err, t) {
      if (err) {
        return callback(err);
      }
      var exp = new Date(gapi.token_expires * 1000);
      callback(null, new Token(gapi.token, exp));
    });
  });
};

/**
 * Makes an authorized request if the current connection token is
 * still valid. Tries to reconnect and makes a request otherwise.
 * @param  {Object}    Request options.
 * @param  {Function=} opt_callback
 */
Connection.prototype.req = function(reqOpts, opt_callback) {
  var that = this;
  // TODO(jbd): Add user agent.
  if (this.isConnected()) {
    this.requester(that.authorizeReq(reqOpts), opt_callback);
    return;
  }
  if (this.isConnecting) {
    this.waitQueue = this.waitQueue || [];
    this.waitQueue.push({ req: reqOpts, cb: opt_callback });
    return;
  }
  this.connect(function(err) {
    that.waitQueue.push({ req: reqOpts, cb: opt_callback });
    that.waitQueue.forEach(function(v) {
      if (err) {
        return v.cb && v.cb(err);
      }
      that.requester(that.authorizeReq(v.req), v.cb);
    });
    that.waitQueue = [];
  });
};

/**
 * The default requester.
 */
Connection.prototype.requester = req;

/**
 * Returns true if connection is inited with a valid token
 * that is not expired.
 * @return {Boolean} The status of the connection.
 */
Connection.prototype.isConnected = function() {
  return this.token && !this.token.isExpired();
};

/**
 * Adds authorization header to the specified request.
 * @param  {Object} reqOpts Request options.
 * @return {Object} Authorized request options.
 */
Connection.prototype.authorizeReq = function(reqOpts) {
  // TODO(jbd): Clone the request object.
  reqOpts.headers = reqOpts.headers || {};
  reqOpts.headers['Authorization'] = 'Bearer ' + this.token.accessToken;
  return reqOpts;
};

module.exports.Token = Token;

module.exports.Connection = Connection;
<commit_msg>If email and private key is not provided, try to retrieve token from the GCE metaserver. Fixes #3.<commit_after>/**
 * Copyright 2014 Google Inc. 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.
 */

var GAPIToken = require('gapitoken'),
  async = require('async'),
  req = require('request');

var metadataTokenUrl = 'http://metadata/computeMetadata/v1/instance/service-accounts/default/token';

/**
 * Token represents an access token
 * @param {string} accessToken  Access token.
 * @param {string} expiry       Expiration datetime.
 */
function Token(accessToken, expiry) {
  this.accessToken = accessToken;
  this.expiry = expiry;
}

/**
 * Returns true if token is expired.
 * @return {Boolean}
 */
Token.prototype.isExpired = function() {
  if (!this.accessToken || !this.expiry) {
    return true;
  }
  return new Date().getTime() - this.expiry.getTime() > 0;
};

/**
 * @param  {Object} opts Options. { email, privateKey, scopes }
 */
function Connection(opts) {
  // TODO: If no email and key is provided, use metaserver to retrieve a new token.
  this.email = opts.email; // client email for the service account
  this.privateKey = opts.privateKey; // contains the contents of a pem file
  this.scopes = opts.scopes || [];
  this.token = null; // existing access token, if exists

  this.isConnecting = false;
  this.waitQueue = [];
}

/**
 * Retrieves a token to authorize the requests.

 * @param  {Function} callback  Callback.
 */
Connection.prototype.connect = function(callback) {
  // TODO(jbd): Connect should be context aware,
  // should not require an email and key, if it's
  // running on Google Compute Engine.
  var that = this;
  this.isConnecting = true;
  // retrieves an access token
  this.fetchToken(function(err, token) {
    if (!err) {
      that.token = token;
    }
    that.isConnecting = false;
    callback(err);
  });
};

/**
 * Fetches a new access token.
 * @param  {Function} callback Callback function.
 */
Connection.prototype.fetchToken = function(callback) {
  var that = this;
  if (!this.email || !this.privateKey) {
    // We should be on GCE, try to retrieve token from
    // the metadata from server.
    req({
      method: 'get',
      uri: metadataTokenUrl,
      headers: {
        'X-Google-Metadata-Request': 'True'
      },
      json: true
    }, function(err, res, body) {
      if (err || !body.access_token) {
        // TODO: Provide better context about the error here.
        return callback(err);
      }
      var exp = new Date(body.token_expires * 1000);
      callback(null, new Token(body.access_token, exp));
    });
    return;
  }
  var gapi = new GAPIToken({
    iss: this.email,
    keyFile: this.privateKey,
    scope: this.scopes.join(' ')
  }, function(err) {
    if (err) {
      return callback(err);
    }
    gapi.getToken(function(err, t) {
      if (err) {
        return callback(err);
      }
      var exp = new Date(gapi.token_expires * 1000);
      callback(null, new Token(gapi.token, exp));
    });
  });
};

/**
 * Makes an authorized request if the current connection token is
 * still valid. Tries to reconnect and makes a request otherwise.
 * @param  {Object}    Request options.
 * @param  {Function=} opt_callback
 */
Connection.prototype.req = function(reqOpts, opt_callback) {
  var that = this;
  // TODO(jbd): Add user agent.
  if (this.isConnected()) {
    this.requester(that.authorizeReq(reqOpts), opt_callback);
    return;
  }
  if (this.isConnecting) {
    this.waitQueue = this.waitQueue || [];
    this.waitQueue.push({ req: reqOpts, cb: opt_callback });
    return;
  }
  this.connect(function(err) {
    that.waitQueue.push({ req: reqOpts, cb: opt_callback });
    that.waitQueue.forEach(function(v) {
      if (err) {
        return v.cb && v.cb(err);
      }
      that.requester(that.authorizeReq(v.req), v.cb);
    });
    that.waitQueue = [];
  });
};

/**
 * The default requester.
 */
Connection.prototype.requester = req;

/**
 * Returns true if connection is inited with a valid token
 * that is not expired.
 * @return {Boolean} The status of the connection.
 */
Connection.prototype.isConnected = function() {
  return this.token && !this.token.isExpired();
};

/**
 * Adds authorization header to the specified request.
 * @param  {Object} reqOpts Request options.
 * @return {Object} Authorized request options.
 */
Connection.prototype.authorizeReq = function(reqOpts) {
  // TODO(jbd): Clone the request object.
  reqOpts.headers = reqOpts.headers || {};
  reqOpts.headers['Authorization'] = 'Bearer ' + this.token.accessToken;
  return reqOpts;
};

module.exports.Token = Token;

module.exports.Connection = Connection;
<|endoftext|>


<commit_before>import { c } from 'ttag';
import { isIcalAllDay } from 'proton-shared/lib/calendar/vcalConverter';
import { convertZonedDateTimeToUTC, toUTCDate } from 'proton-shared/lib/date/timezone';
import { isIcalRecurring } from 'proton-shared/lib/calendar/recurring';

import { getSnappedDate } from '../../calendar/mouseHelpers/dateHelpers';
import {
    DEFAULT_FULL_DAY_NOTIFICATION,
    DEFAULT_FULL_DAY_NOTIFICATIONS,
    DEFAULT_PART_DAY_NOTIFICATION,
    DEFAULT_PART_DAY_NOTIFICATIONS,
    notificationsToModel
} from '../../../helpers/notifications';
import { DEFAULT_EVENT_DURATION, FREQUENCY, NOTIFICATION_TYPE } from '../../../constants';
import { propertiesToDateTimeModel, propertiesToModel, propertiesToNotificationModel } from './propertiesToModel';
import { modelToGeneralProperties } from './modelToProperties';
import { isSameDay } from 'proton-shared/lib/date-fns-utc';

export const getDateTimeState = (utcDate, tzid) => {
    return {
        // These should be local dates since the mini calendar and time input uses that.
        date: new Date(utcDate.getUTCFullYear(), utcDate.getUTCMonth(), utcDate.getUTCDate(), 0, 0),
        time: new Date(2000, 0, 1, utcDate.getUTCHours(), utcDate.getUTCMinutes()),
        tzid
    };
};

export const getNotificationModels = ({
                                          DefaultPartDayNotifications = DEFAULT_PART_DAY_NOTIFICATIONS,
                                          DefaultFullDayNotifications = DEFAULT_FULL_DAY_NOTIFICATIONS
                                      }) => {
    return {
        defaultPartDayNotification: DEFAULT_PART_DAY_NOTIFICATION,
        defaultFullDayNotification: DEFAULT_FULL_DAY_NOTIFICATION,
        partDayNotifications: notificationsToModel(DefaultPartDayNotifications, false).filter(
            ({ type }) => type === NOTIFICATION_TYPE.DEVICE
        ),
        fullDayNotifications: notificationsToModel(DefaultFullDayNotifications, true).filter(
            ({ type }) => type === NOTIFICATION_TYPE.DEVICE
        )
    };
};

export const getInitialDateTimeModel = (defaultEventDuration, tzid) => {
    const now = new Date();

    const start = getSnappedDate(
        new Date(Date.UTC(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), now.getMinutes() + 30)),
        30
    );

    const end = new Date(
        Date.UTC(
            start.getUTCFullYear(),
            start.getUTCMonth(),
            start.getUTCDate(),
            start.getUTCHours(),
            start.getUTCMinutes() + defaultEventDuration
        )
    );

    return {
        start: getDateTimeState(start, tzid),
        end: getDateTimeState(end, tzid)
    };
};

export const getInitialMemberModel = (Addresses, Members, Member, Address) => {
    const { ID: addressID } = Address;
    const { ID: memberID } = Member;
    return {
        member: {
            addressID,
            memberID
        }
    };
};

const getCalendarsModel = (Calendar, Calendars = []) => {
    if (!Calendars.some(({ ID }) => ID === Calendar.ID)) {
        throw new Error('Calendar not found');
    }
    return {
        calendars: Calendars.map(({ ID, Name, Color }) => ({ text: Name, value: ID, color: Color })),
        calendar: {
            id: Calendar.ID,
            color: Calendar.Color
        }
    };
};

export const getInitialModel = ({ CalendarSettings, Calendar, Calendars, Members, Member, Addresses, Address, isAllDay, tzid }) => {
    const { DefaultEventDuration: defaultEventDuration = DEFAULT_EVENT_DURATION } = CalendarSettings;
    const dateTimeModel = getInitialDateTimeModel(defaultEventDuration, tzid);
    const notificationModel = getNotificationModels(CalendarSettings);
    const memberModel = getInitialMemberModel(Addresses, Members, Member, Address);
    const calendarsModel = getCalendarsModel(Calendar, Calendars);

    return {
        title: '',
        location: '',
        description: '',
        frequency: FREQUENCY.ONCE,
        isAllDay,
        defaultEventDuration,
        ...notificationModel,
        ...memberModel,
        ...dateTimeModel,
        ...calendarsModel
    };
};

export const getExistingEvent = ({ veventComponent, veventValarmComponent, tzid }) => {
    const isAllDay = isIcalAllDay(veventComponent);
    const isRecurring = isIcalRecurring(veventComponent);

    const newModel = propertiesToModel(veventComponent);
    const newDateTime = propertiesToDateTimeModel(veventComponent, isAllDay, tzid);

    const hasDifferingTimezone = newDateTime.start.tzid !== tzid || newDateTime.end.tzid !== tzid;

    // Email notifications are not supported atm.
    const newNotifications = propertiesToNotificationModel(veventValarmComponent, newModel.isAllDay).filter(
        ({ type }) => type === NOTIFICATION_TYPE.DEVICE
    );

    return {
        ...newModel,
        ...newNotifications,
        isAllDay,
        hasMoreOptions: isRecurring || hasDifferingTimezone,
        ...(newModel.isAllDay
            ? {
                fullDayNotifications: newNotifications
            }
            : {
                partDayNotifications: newNotifications
            }),
        start: newDateTime.start,
        end: newDateTime.end
    };
};

export const addItem = (array, item) => array.concat(item);
export const updateItem = (array, index, newItem) => {
    return array.map((item, i) => {
        if (i !== index) {
            return item;
        }
        return newItem;
    });
};
export const removeItem = (array, index) => array.filter((oldValue, i) => i !== index);

export const getTimeInUtc = ({ date, time, tzid }) => {
    return toUTCDate(
        convertZonedDateTimeToUTC(
            {
                year: date.getFullYear(),
                month: date.getMonth() + 1,
                day: date.getDate(),
                hours: time.getHours(),
                minutes: time.getMinutes()
            },
            tzid
        )
    );
};

export const validate = ({ start, end, title }) => {
    const errors = {};

    const generalProperties = modelToGeneralProperties({ title });

    if (!generalProperties.summary.value) {
        errors.title = c('Error').t`Title required`;
    }

    const utcStart = getTimeInUtc(start);
    const utcEnd = getTimeInUtc(end);

    if (utcStart > utcEnd) {
        errors.end = c('Error').t`Start time must be before end time`;
    }

    return errors;
};

const keys = ['title', 'location', 'description'];

export const hasEditedText = (model, otherModel) => {
    return keys.some((key) => {
        return model[key] !== otherModel[key];
    });
};

export const hasEditedDateTime = ({ time, date }, { time: otherTime, date: otherDate }) => {
    return time.getHours() !== otherTime.getHours() || time.getMinutes() !== otherTime.getMinutes() || !isSameDay(date, otherDate);
};

export const hasDoneChanges = (model, otherModel, isEditMode) => {
    return hasEditedText(model, otherModel) ||
        isEditMode && (
            hasEditedDateTime(model.start, otherModel.start) ||
            hasEditedDateTime(model.end, otherModel.end)
        );
};
<commit_msg>Fix CALENDAR-244 - Discard change confirmation for more fields<commit_after>import { c } from 'ttag';
import { isIcalAllDay } from 'proton-shared/lib/calendar/vcalConverter';
import { convertZonedDateTimeToUTC, toUTCDate } from 'proton-shared/lib/date/timezone';
import { isIcalRecurring } from 'proton-shared/lib/calendar/recurring';

import { getSnappedDate } from '../../calendar/mouseHelpers/dateHelpers';
import {
    DEFAULT_FULL_DAY_NOTIFICATION,
    DEFAULT_FULL_DAY_NOTIFICATIONS,
    DEFAULT_PART_DAY_NOTIFICATION,
    DEFAULT_PART_DAY_NOTIFICATIONS,
    notificationsToModel
} from '../../../helpers/notifications';
import { DEFAULT_EVENT_DURATION, FREQUENCY, NOTIFICATION_TYPE } from '../../../constants';
import { propertiesToDateTimeModel, propertiesToModel, propertiesToNotificationModel } from './propertiesToModel';
import { modelToGeneralProperties } from './modelToProperties';
import { isSameDay } from 'proton-shared/lib/date-fns-utc';

export const getDateTimeState = (utcDate, tzid) => {
    return {
        // These should be local dates since the mini calendar and time input uses that.
        date: new Date(utcDate.getUTCFullYear(), utcDate.getUTCMonth(), utcDate.getUTCDate(), 0, 0),
        time: new Date(2000, 0, 1, utcDate.getUTCHours(), utcDate.getUTCMinutes()),
        tzid
    };
};

export const getNotificationModels = ({
    DefaultPartDayNotifications = DEFAULT_PART_DAY_NOTIFICATIONS,
    DefaultFullDayNotifications = DEFAULT_FULL_DAY_NOTIFICATIONS
}) => {
    return {
        defaultPartDayNotification: DEFAULT_PART_DAY_NOTIFICATION,
        defaultFullDayNotification: DEFAULT_FULL_DAY_NOTIFICATION,
        partDayNotifications: notificationsToModel(DefaultPartDayNotifications, false).filter(
            ({ type }) => type === NOTIFICATION_TYPE.DEVICE
        ),
        fullDayNotifications: notificationsToModel(DefaultFullDayNotifications, true).filter(
            ({ type }) => type === NOTIFICATION_TYPE.DEVICE
        )
    };
};

export const getInitialDateTimeModel = (defaultEventDuration, tzid) => {
    const now = new Date();

    const start = getSnappedDate(
        new Date(Date.UTC(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), now.getMinutes() + 30)),
        30
    );

    const end = new Date(
        Date.UTC(
            start.getUTCFullYear(),
            start.getUTCMonth(),
            start.getUTCDate(),
            start.getUTCHours(),
            start.getUTCMinutes() + defaultEventDuration
        )
    );

    return {
        start: getDateTimeState(start, tzid),
        end: getDateTimeState(end, tzid)
    };
};

export const getInitialMemberModel = (Addresses, Members, Member, Address) => {
    const { ID: addressID } = Address;
    const { ID: memberID } = Member;
    return {
        member: {
            addressID,
            memberID
        }
    };
};

const getCalendarsModel = (Calendar, Calendars = []) => {
    if (!Calendars.some(({ ID }) => ID === Calendar.ID)) {
        throw new Error('Calendar not found');
    }
    return {
        calendars: Calendars.map(({ ID, Name, Color }) => ({ text: Name, value: ID, color: Color })),
        calendar: {
            id: Calendar.ID,
            color: Calendar.Color
        }
    };
};

export const getInitialModel = ({
    CalendarSettings,
    Calendar,
    Calendars,
    Members,
    Member,
    Addresses,
    Address,
    isAllDay,
    tzid
}) => {
    const { DefaultEventDuration: defaultEventDuration = DEFAULT_EVENT_DURATION } = CalendarSettings;
    const dateTimeModel = getInitialDateTimeModel(defaultEventDuration, tzid);
    const notificationModel = getNotificationModels(CalendarSettings);
    const memberModel = getInitialMemberModel(Addresses, Members, Member, Address);
    const calendarsModel = getCalendarsModel(Calendar, Calendars);

    return {
        title: '',
        location: '',
        description: '',
        frequency: FREQUENCY.ONCE,
        isAllDay,
        defaultEventDuration,
        ...notificationModel,
        ...memberModel,
        ...dateTimeModel,
        ...calendarsModel
    };
};

export const getExistingEvent = ({ veventComponent, veventValarmComponent, tzid }) => {
    const isAllDay = isIcalAllDay(veventComponent);
    const isRecurring = isIcalRecurring(veventComponent);

    const newModel = propertiesToModel(veventComponent);
    const newDateTime = propertiesToDateTimeModel(veventComponent, isAllDay, tzid);

    const hasDifferingTimezone = newDateTime.start.tzid !== tzid || newDateTime.end.tzid !== tzid;

    // Email notifications are not supported atm.
    const newNotifications = propertiesToNotificationModel(veventValarmComponent, newModel.isAllDay).filter(
        ({ type }) => type === NOTIFICATION_TYPE.DEVICE
    );

    return {
        ...newModel,
        ...newNotifications,
        isAllDay,
        hasMoreOptions: isRecurring || hasDifferingTimezone,
        ...(newModel.isAllDay
            ? {
                  fullDayNotifications: newNotifications
              }
            : {
                  partDayNotifications: newNotifications
              }),
        start: newDateTime.start,
        end: newDateTime.end
    };
};

export const addItem = (array, item) => array.concat(item);
export const updateItem = (array, index, newItem) => {
    return array.map((item, i) => {
        if (i !== index) {
            return item;
        }
        return newItem;
    });
};
export const removeItem = (array, index) => array.filter((oldValue, i) => i !== index);

export const getTimeInUtc = ({ date, time, tzid }) => {
    return toUTCDate(
        convertZonedDateTimeToUTC(
            {
                year: date.getFullYear(),
                month: date.getMonth() + 1,
                day: date.getDate(),
                hours: time.getHours(),
                minutes: time.getMinutes()
            },
            tzid
        )
    );
};

export const validate = ({ start, end, title }) => {
    const errors = {};

    const generalProperties = modelToGeneralProperties({ title });

    if (!generalProperties.summary.value) {
        errors.title = c('Error').t`Title required`;
    }

    const utcStart = getTimeInUtc(start);
    const utcEnd = getTimeInUtc(end);

    if (utcStart > utcEnd) {
        errors.end = c('Error').t`Start time must be before end time`;
    }

    return errors;
};

const keys = ['title', 'location', 'description', 'isAllDay', 'frequency'];

export const hasEdited = (keys, model, otherModel) => {
    return keys.some((key) => {
        return model[key] !== otherModel[key];
    });
};

export const hasEditedTimezone = ({ tzid }, { tzid: otherTzid }) => {
    return tzid !== otherTzid;
};

const hasEditedHourMinutes = (a, b) => a.getHours() !== b.getHours() || a.getMinutes() !== b.getMinutes();

export const hasEditedDateTime = ({ time, date }, { time: otherTime, date: otherDate }) => {
    return hasEditedHourMinutes(time, otherTime) || !isSameDay(date, otherDate);
};

const hasEditedNotification = (notification, otherNotification) => {
    return (
        hasEdited(['type', 'unit', 'when', 'value'], notification, otherNotification) ||
        hasEditedHourMinutes(notification.at, otherNotification.at)
    );
};

const hasEditedNotifications = (
    { isAllDay, partDayNotifications, fullDayNotifications },
    { partDayNotifications: otherPartDayNotifications, fullDayNotifications: otherFullDayNotifications }
) => {
    const notifications = isAllDay ? fullDayNotifications : partDayNotifications;
    const otherNotifications = isAllDay ? otherFullDayNotifications : otherPartDayNotifications;

    return (
        notifications.length !== otherNotifications.length ||
        notifications.some((notification, i) => hasEditedNotification(notification, otherNotifications[i]))
    );
};

export const hasDoneChanges = (model, otherModel, isEditMode) => {
    return (
        hasEdited(keys, model, otherModel) ||
        hasEditedNotifications(model, otherModel) ||
        hasEditedTimezone(model.start, otherModel.start) ||
        hasEditedTimezone(model.end, otherModel.end) ||
        (isEditMode &&
            (hasEditedDateTime(model.start, otherModel.start) || hasEditedDateTime(model.end, otherModel.end)))
    );
};
<|endoftext|>


<commit_before>/*
 * Licensed to the Sakai Foundation (SF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The SF licenses this file
 * to you 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.
 */

// load the master sakai object to access all Sakai OAE API methods
require(["jquery", "sakai/sakai.api.core"], function($, sakai) {

    /**
     * @name sakai_global.participants
     *
     * @class participants
     *
     * @version 0.0.1
     * @param {String} tuid Unique id of the widget
     * @param {Boolean} showSettings Show the settings of the widget or not
     */
    sakai_global.participants = function (tuid, showSettings, widgetData) {

        var rootel = $("#" + tuid);

        /////////////////////////////
        // Configuration variables //
        /////////////////////////////
        var infinityScroll = false;

        // Containers
        var $participantsListContainer = $("#participants_list_container_list", rootel);

        // Templates
        var participantsListTemplate = "participants_list_template";
        var participantsListTemplateEmpty = "participants_list_empty_template";

        // Elements
        var $participantsSearchField = $("#participants_search_field", rootel);
        var participantsListParticipantRequestConnection = ".participants_list_participant_request_connection";
        var $participantsSelectAll = $("#participants_select_all", rootel);
        var participantsListParticipantCheckbox = ".participants_list_participant_checkbox input:checkbox";
        var $participantsSendSelectedMessage = $("#participants_send_selected_message", rootel);
        var $participantsAddPeopleButton = $("#participants_addpeople_button", rootel);
        var participantsListParticipantName = ".participants_list_participant_name";
        var $participants_sort_by = $("#participants_sort_by", rootel);
        var participantsShowGrid = $(".s3d-listview-grid", rootel);
        var participantsShowList = $(".s3d-listview-list", rootel);
        widgetData.listStyle = "list";
        widgetData.query = "";
        widgetData.sortby = "asc";

        var newlyAdded = [],
            roles = false;

        ///////////////////////
        // Utility functions //
        ///////////////////////

        var enableDisableButtons = function(){
            if($(participantsListParticipantCheckbox + ":checked", rootel).length){
                $participantsSendSelectedMessage.removeAttr("disabled");
                $participantsAddPeopleButton.removeAttr("disabled");
            } else {
                $participantsSendSelectedMessage.attr("disabled", "disabled");
                $participantsSelectAll.removeAttr("checked");
                $participantsAddPeopleButton.attr("disabled", "disabled");
            }
        };

        /**
         * Set the attributes needed by the sendmessage widget to send a message to all selected users
         */
        var setSendSelectedMessageAttributes = function(){
            var userArr = [];
            var userIDArr = [];
            $.each($(participantsListParticipantCheckbox + ":checked", rootel), function(index, item){
                userIDArr.push($(item).attr("data-entityid"));
                userArr.push($(item).attr("data-entityname"));
            });
            $participantsSendSelectedMessage.attr("sakai-entitytype", "user");
            $participantsSendSelectedMessage.attr("sakai-entityname", userArr);
            $participantsSendSelectedMessage.attr("sakai-entityid", userIDArr);
            $participantsAddPeopleButton.attr("data-entityname", userArr);
            $participantsAddPeopleButton.attr("data-entityid", userIDArr);
            enableDisableButtons();
        };

        /**
         * Check/Uncheck all items in the members list and enable/disable buttons
         */
        var checkAll = function(){
            if($(this).is(":checked")){
                $(participantsListParticipantCheckbox, rootel).attr("checked","checked");
                setSendSelectedMessageAttributes();
            }else{
                $(participantsListParticipantCheckbox, rootel).removeAttr("checked");
                enableDisableButtons();
            }
        };

        var getRoleTitle = function(result) {
            var ret = "";
            if (result.role && result.role.title) {
                ret = result.role.title;
            } else if (result.role === undefined && newlyAdded && newlyAdded.length && roles && roles.length) {
                $.each(newlyAdded, function(i, member) {
                    if (member.user === result["rep:userId"] || member.user === result["sakai:group-id"]) {
                        $.each(roles, function(i, role) {
                            if (role.id === member.permission) {
                                ret = role.title;
                            }
                        });
                    }
                });
            }
            return ret;
        };

        //////////////////////
        // Render functions //
        //////////////////////

        var processParticipants = function (results, callback){
            var participantsArr = [];
            if (results && results.length){
                sakai.api.User.getContacts(function() {
                    $.each(results, function(i, result){
                        var contentCount = 0;
                        var contactsCount = 0;
                        var membershipsCount = 0;
                        if (result.counts){
                            contentCount = result.counts.contentCount;
                            contactsCount = result.counts.contactsCount;
                            membershipsCount = result.counts.membershipsCount;
                        }
                        var picture = false;
                        var roleTitle = getRoleTitle(result);
                        if (result["sakai:group-id"]) {
                            if(result.basic.elements.picture && result.basic.elements.picture.value){
                                picture = sakai.api.Groups.getProfilePicture(result);
                            }
                            participantsArr.push({
                                "name": result["sakai:group-title"],
                                "id": result["sakai:group-id"],
                                "title": roleTitle,
                                "type": "group",
                                "connected": false,
                                "content": contentCount,
                                "contacts": contactsCount,
                                "memberships": membershipsCount,
                                "profilePicture": picture,
                                "membersCount": result.counts.membersCount
                            });
                        } else {
                            // Check if this user is a friend of us already.
                            var connected = false, invited = false, pending = false, none = false;
                            if (sakai.data.me.mycontacts) {
                                $.each(sakai.data.me.mycontacts, function(ii, contact){
                                    if (contact.target === result["rep:userId"]) {
                                        connected = true;
                                        // if invited state set invited to true
                                        if(contact.details["sakai:state"] === "INVITED"){
                                            invited = true;
                                        } else if(contact.details["sakai:state"] === "PENDING"){
                                            pending = true;
                                        } else if(contact.details["sakai:state"] === "NONE"){
                                            none = true;
                                        }
                                    }
                                });
                            }
                            if(result.basic.elements.picture && result.basic.elements.picture.value){
                                picture = sakai.api.User.getProfilePicture(result);
                            }
                            participantsArr.push({
                                "name": sakai.api.User.getDisplayName(result),
                                "id": result["rep:userId"],
                                "title": roleTitle,
                                "type": "user",
                                "content": contentCount,
                                "contacts": contactsCount,
                                "memberships": membershipsCount,
                                "connected": connected,
                                "invited": invited,
                                "pending": pending,
                                "none": none,
                                "profilePicture": picture
                            });
                        }
                    });
                });
            }
            callback(participantsArr);
        };

        ////////////////////
        // Init functions //
        ////////////////////

        var loadParticipants = function(){
            // Disable the previous infinite scroll
            if (infinityScroll){
                infinityScroll.kill();
            }
            // Set up the infinite scroll for the list of items in the library
            infinityScroll = $participantsListContainer.infinitescroll(function(parameters, callback){
                sakai.api.Groups.searchMembers(widgetData.participants.groupid, widgetData.query, parameters.items, parameters.page, parameters.sortBy, parameters.sortOrder, function(success, data){
                    callback(true, data);
                });
            }, {
                "query": widgetData.query,
                "sortBy": "lastName",
                "sortOrder": widgetData.sortby
            }, function(items, total){
                if (sakai.data.me.user.anon){
                    $(".s3d-page-header-top-row", rootel).show();
                } else {
                    $(".s3d-page-header-top-row", rootel).show();
                    $(".s3d-page-header-bottom-row", rootel).show();
                }
                $participantsSelectAll.removeAttr("checked");
                setSendSelectedMessageAttributes();
                return sakai.api.Util.TemplateRenderer(participantsListTemplate, {
                    "participants": items,
                    "sakai": sakai
                });
            }, function(){
                $participantsListContainer.html(sakai.api.Util.TemplateRenderer(participantsListTemplateEmpty, {}));
            }, sakai.config.URL.INFINITE_LOADING_ICON, processParticipants);
        };

        var handleHashChange = function(){
            $(".s3d-listview-options", rootel).find("div").removeClass("selected");
            var ls = $.bbq.getState("ls") || widgetData.listStyle;
            if (ls === "list"){
                $("#participants_list_container_list", rootel).removeClass("s3d-search-results-grid");
                $(participantsShowList, rootel).addClass("selected");
                $(participantsShowList, rootel).children().addClass("selected");
            } else if (ls === "grid"){
                $(participantsShowGrid, rootel).addClass("selected");
                $(participantsShowGrid, rootel).children().addClass("selected");
                $("#participants_list_container_list", rootel).addClass("s3d-search-results-grid");
            }
            widgetData.query = $.bbq.getState("pq") || "";
            $participantsSearchField.val(widgetData.query);
            widgetData.sortby = $.bbq.getState("psb") || "asc";
            $participants_sort_by.val(widgetData.sortby);
            loadParticipants();
        };

        var addBinding = function(){
            $(window).bind("hashchanged.participants.sakai", handleHashChange);

            $(".participants_widget .s3d-search-button").unbind("click").bind("click", function(){
                currentPage = 1;
                $.bbq.pushState({"pq": $.trim($participantsSearchField.val())});
            });
            $participantsSearchField.unbind("keyup").bind("keyup", function(ev) {
                if (ev.keyCode === 13) {
                    $.bbq.pushState({"pq": $.trim($participantsSearchField.val())});
                }
            });
            $participants_sort_by.unbind("change").bind("change", function(){
                $.bbq.pushState({"psb": $participants_sort_by.val()});
            });
            $participantsSelectAll.unbind("click").bind("click", checkAll);
            $(participantsListParticipantCheckbox, rootel).live("click", setSendSelectedMessageAttributes);

            $(".participants_accept_invitation").live("click", function(ev){
                var userid = $(this).attr("sakai-entityid");
                sakai.api.User.acceptContactInvite(userid, function(){
                    $('.participants_accept_invitation').each(function(index) {
                        if ($(this).attr("sakai-entityid") === userid){
                            $(this).hide();
                        }
                    });
                });
            });

            $(window).bind("sakai.addToContacts.requested", function(ev, userToAdd){
                $('.sakai_addtocontacts_overlay').each(function(index) {
                    if ($(this).attr("sakai-entityid") === userToAdd.uuid){
                        $(this).hide();
                    }
                });
            });

            $(participantsShowList, rootel).click(function(){
                $.bbq.pushState({"ls": "list"});
            });

            $(participantsShowGrid, rootel).click(function(){
                $.bbq.pushState({"ls": "grid"});
            });
        };

        var init = function(){
            var groupData = $.extend(true, {}, sakai_global.group.groupData);
            groupData.roles = groupData["sakai:roles"];
            roles = sakai.api.Groups.getRoles(groupData);
            if (sakai.api.Groups.isCurrentUserAManager(sakai_global.group.groupId, sakai.data.me, groupData)){
                $("#participants_manage_participants").show();
            }
            addBinding();
            handleHashChange();
        };

        $(window).bind("usersselected.addpeople.sakai", function(e, _newlyAdded){
            newlyAdded = _newlyAdded;
            setTimeout(loadParticipants, 1000);
        });

        init();

    };

    // inform Sakai OAE that this widget has loaded and is ready to run
    sakai.api.Widgets.widgetLoader.informOnLoad("participants");
});<commit_msg>SAKIII-4757 Ignore empty object in participants results<commit_after>/*
 * Licensed to the Sakai Foundation (SF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The SF licenses this file
 * to you 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.
 */

// load the master sakai object to access all Sakai OAE API methods
require(["jquery", "sakai/sakai.api.core"], function($, sakai) {

    /**
     * @name sakai_global.participants
     *
     * @class participants
     *
     * @version 0.0.1
     * @param {String} tuid Unique id of the widget
     * @param {Boolean} showSettings Show the settings of the widget or not
     */
    sakai_global.participants = function (tuid, showSettings, widgetData) {

        var rootel = $("#" + tuid);

        /////////////////////////////
        // Configuration variables //
        /////////////////////////////
        var infinityScroll = false;

        // Containers
        var $participantsListContainer = $("#participants_list_container_list", rootel);

        // Templates
        var participantsListTemplate = "participants_list_template";
        var participantsListTemplateEmpty = "participants_list_empty_template";

        // Elements
        var $participantsSearchField = $("#participants_search_field", rootel);
        var participantsListParticipantRequestConnection = ".participants_list_participant_request_connection";
        var $participantsSelectAll = $("#participants_select_all", rootel);
        var participantsListParticipantCheckbox = ".participants_list_participant_checkbox input:checkbox";
        var $participantsSendSelectedMessage = $("#participants_send_selected_message", rootel);
        var $participantsAddPeopleButton = $("#participants_addpeople_button", rootel);
        var participantsListParticipantName = ".participants_list_participant_name";
        var $participants_sort_by = $("#participants_sort_by", rootel);
        var participantsShowGrid = $(".s3d-listview-grid", rootel);
        var participantsShowList = $(".s3d-listview-list", rootel);
        widgetData.listStyle = "list";
        widgetData.query = "";
        widgetData.sortby = "asc";

        var newlyAdded = [],
            roles = false;

        ///////////////////////
        // Utility functions //
        ///////////////////////

        var enableDisableButtons = function(){
            if($(participantsListParticipantCheckbox + ":checked", rootel).length){
                $participantsSendSelectedMessage.removeAttr("disabled");
                $participantsAddPeopleButton.removeAttr("disabled");
            } else {
                $participantsSendSelectedMessage.attr("disabled", "disabled");
                $participantsSelectAll.removeAttr("checked");
                $participantsAddPeopleButton.attr("disabled", "disabled");
            }
        };

        /**
         * Set the attributes needed by the sendmessage widget to send a message to all selected users
         */
        var setSendSelectedMessageAttributes = function(){
            var userArr = [];
            var userIDArr = [];
            $.each($(participantsListParticipantCheckbox + ":checked", rootel), function(index, item){
                userIDArr.push($(item).attr("data-entityid"));
                userArr.push($(item).attr("data-entityname"));
            });
            $participantsSendSelectedMessage.attr("sakai-entitytype", "user");
            $participantsSendSelectedMessage.attr("sakai-entityname", userArr);
            $participantsSendSelectedMessage.attr("sakai-entityid", userIDArr);
            $participantsAddPeopleButton.attr("data-entityname", userArr);
            $participantsAddPeopleButton.attr("data-entityid", userIDArr);
            enableDisableButtons();
        };

        /**
         * Check/Uncheck all items in the members list and enable/disable buttons
         */
        var checkAll = function(){
            if($(this).is(":checked")){
                $(participantsListParticipantCheckbox, rootel).attr("checked","checked");
                setSendSelectedMessageAttributes();
            }else{
                $(participantsListParticipantCheckbox, rootel).removeAttr("checked");
                enableDisableButtons();
            }
        };

        var getRoleTitle = function(result) {
            var ret = "";
            if (result.role && result.role.title) {
                ret = result.role.title;
            } else if (result.role === undefined && newlyAdded && newlyAdded.length && roles && roles.length) {
                $.each(newlyAdded, function(i, member) {
                    if (member.user === result["rep:userId"] || member.user === result["sakai:group-id"]) {
                        $.each(roles, function(i, role) {
                            if (role.id === member.permission) {
                                ret = role.title;
                            }
                        });
                    }
                });
            }
            return ret;
        };

        //////////////////////
        // Render functions //
        //////////////////////

        var processParticipants = function (results, callback){
            var participantsArr = [];
            if (results && results.length){
                sakai.api.User.getContacts(function() {
                    $.each(results, function(i, result){
                        if(!$.isEmptyObject(result)){
                            var contentCount = 0;
                            var contactsCount = 0;
                            var membershipsCount = 0;
                            if (result.counts){
                                contentCount = result.counts.contentCount;
                                contactsCount = result.counts.contactsCount;
                                membershipsCount = result.counts.membershipsCount;
                            }
                            var picture = false;
                            var roleTitle = getRoleTitle(result);
                            if (result["sakai:group-id"]) {
                                if(result.basic.elements.picture && result.basic.elements.picture.value){
                                    picture = sakai.api.Groups.getProfilePicture(result);
                                }
                                participantsArr.push({
                                    "name": result["sakai:group-title"],
                                    "id": result["sakai:group-id"],
                                    "title": roleTitle,
                                    "type": "group",
                                    "connected": false,
                                    "content": contentCount,
                                    "contacts": contactsCount,
                                    "memberships": membershipsCount,
                                    "profilePicture": picture,
                                    "membersCount": result.counts.membersCount
                                });
                            } else {
                                // Check if this user is a friend of us already.
                                var connected = false, invited = false, pending = false, none = false;
                                if (sakai.data.me.mycontacts) {
                                    $.each(sakai.data.me.mycontacts, function(ii, contact){
                                        if (contact.target === result["rep:userId"]) {
                                            connected = true;
                                            // if invited state set invited to true
                                            if(contact.details["sakai:state"] === "INVITED"){
                                                invited = true;
                                            } else if(contact.details["sakai:state"] === "PENDING"){
                                                pending = true;
                                            } else if(contact.details["sakai:state"] === "NONE"){
                                                none = true;
                                            }
                                        }
                                    });
                                }
                                if(result.basic.elements.picture && result.basic.elements.picture.value){
                                    picture = sakai.api.User.getProfilePicture(result);
                                }
                                participantsArr.push({
                                    "name": sakai.api.User.getDisplayName(result),
                                    "id": result["rep:userId"],
                                    "title": roleTitle,
                                    "type": "user",
                                    "content": contentCount,
                                    "contacts": contactsCount,
                                    "memberships": membershipsCount,
                                    "connected": connected,
                                    "invited": invited,
                                    "pending": pending,
                                    "none": none,
                                    "profilePicture": picture
                                });
                            }
                        }
                    });
                });
            }
            callback(participantsArr);
        };

        ////////////////////
        // Init functions //
        ////////////////////

        var loadParticipants = function(){
            // Disable the previous infinite scroll
            if (infinityScroll){
                infinityScroll.kill();
            }
            // Set up the infinite scroll for the list of items in the library
            infinityScroll = $participantsListContainer.infinitescroll(function(parameters, callback){
                sakai.api.Groups.searchMembers(widgetData.participants.groupid, widgetData.query, parameters.items, parameters.page, parameters.sortBy, parameters.sortOrder, function(success, data){
                    callback(true, data);
                });
            }, {
                "query": widgetData.query,
                "sortBy": "lastName",
                "sortOrder": widgetData.sortby
            }, function(items, total){
                if (sakai.data.me.user.anon){
                    $(".s3d-page-header-top-row", rootel).show();
                } else {
                    $(".s3d-page-header-top-row", rootel).show();
                    $(".s3d-page-header-bottom-row", rootel).show();
                }
                $participantsSelectAll.removeAttr("checked");
                setSendSelectedMessageAttributes();
                return sakai.api.Util.TemplateRenderer(participantsListTemplate, {
                    "participants": items,
                    "sakai": sakai
                });
            }, function(){
                $participantsListContainer.html(sakai.api.Util.TemplateRenderer(participantsListTemplateEmpty, {}));
            }, sakai.config.URL.INFINITE_LOADING_ICON, processParticipants);
        };

        var handleHashChange = function(){
            $(".s3d-listview-options", rootel).find("div").removeClass("selected");
            var ls = $.bbq.getState("ls") || widgetData.listStyle;
            if (ls === "list"){
                $("#participants_list_container_list", rootel).removeClass("s3d-search-results-grid");
                $(participantsShowList, rootel).addClass("selected");
                $(participantsShowList, rootel).children().addClass("selected");
            } else if (ls === "grid"){
                $(participantsShowGrid, rootel).addClass("selected");
                $(participantsShowGrid, rootel).children().addClass("selected");
                $("#participants_list_container_list", rootel).addClass("s3d-search-results-grid");
            }
            widgetData.query = $.bbq.getState("pq") || "";
            $participantsSearchField.val(widgetData.query);
            widgetData.sortby = $.bbq.getState("psb") || "asc";
            $participants_sort_by.val(widgetData.sortby);
            loadParticipants();
        };

        var addBinding = function(){
            $(window).bind("hashchanged.participants.sakai", handleHashChange);

            $(".participants_widget .s3d-search-button").unbind("click").bind("click", function(){
                currentPage = 1;
                $.bbq.pushState({"pq": $.trim($participantsSearchField.val())});
            });
            $participantsSearchField.unbind("keyup").bind("keyup", function(ev) {
                if (ev.keyCode === 13) {
                    $.bbq.pushState({"pq": $.trim($participantsSearchField.val())});
                }
            });
            $participants_sort_by.unbind("change").bind("change", function(){
                $.bbq.pushState({"psb": $participants_sort_by.val()});
            });
            $participantsSelectAll.unbind("click").bind("click", checkAll);
            $(participantsListParticipantCheckbox, rootel).live("click", setSendSelectedMessageAttributes);

            $(".participants_accept_invitation").live("click", function(ev){
                var userid = $(this).attr("sakai-entityid");
                sakai.api.User.acceptContactInvite(userid, function(){
                    $('.participants_accept_invitation').each(function(index) {
                        if ($(this).attr("sakai-entityid") === userid){
                            $(this).hide();
                        }
                    });
                });
            });

            $(window).bind("sakai.addToContacts.requested", function(ev, userToAdd){
                $('.sakai_addtocontacts_overlay').each(function(index) {
                    if ($(this).attr("sakai-entityid") === userToAdd.uuid){
                        $(this).hide();
                    }
                });
            });

            $(participantsShowList, rootel).click(function(){
                $.bbq.pushState({"ls": "list"});
            });

            $(participantsShowGrid, rootel).click(function(){
                $.bbq.pushState({"ls": "grid"});
            });
        };

        var init = function(){
            var groupData = $.extend(true, {}, sakai_global.group.groupData);
            groupData.roles = groupData["sakai:roles"];
            roles = sakai.api.Groups.getRoles(groupData);
            if (sakai.api.Groups.isCurrentUserAManager(sakai_global.group.groupId, sakai.data.me, groupData)){
                $("#participants_manage_participants").show();
            }
            addBinding();
            handleHashChange();
        };

        $(window).bind("usersselected.addpeople.sakai", function(e, _newlyAdded){
            newlyAdded = _newlyAdded;
            setTimeout(loadParticipants, 1000);
        });

        init();

    };

    // inform Sakai OAE that this widget has loaded and is ready to run
    sakai.api.Widgets.widgetLoader.informOnLoad("participants");
});<|endoftext|>


<commit_before>export { default as foundReducer } from 'found/lib/foundReducer'
export { default as Route } from 'found/lib/Route'
export { default as Redirect } from 'found/lib/Redirect'
export { default as withRouter } from 'found/lib/withRouter'
export { default as RedirectException } from 'found/lib/RedirectException'

import createMatchEnhancer from 'found/lib/createMatchEnhancer'
import Matcher from 'found/lib/Matcher'
import makeRouteConfig from 'found/lib/makeRouteConfig'
import getStoreRenderArgs from 'found/lib/getStoreRenderArgs'
import HttpError from 'found/lib/HttpError'
import resolver from 'found/lib/resolver'
import FoundActionTypes from 'found/lib/ActionTypes'

import Actions from 'farce/lib/Actions'
import ActionTypes from 'farce/lib/ActionTypes'
import createHistoryEnhancer from 'farce/lib/createHistoryEnhancer'
import createBasenameMiddleware from 'farce/lib/createBasenameMiddleware'
import queryMiddleware from 'farce/lib/queryMiddleware'

import { markImmediateNavigationAsInstantBack } from '../redux/client/instantBack'

export function createRouterStoreEnhancers(routes, createHistoryProtocol, options = {}) {
	const middlewares = [
		queryMiddleware
	]
	if (options.basename) {
		middlewares.push(createBasenameMiddleware({
			basename: options.basename
		}))
	}
	return [
		createHistoryEnhancer({
			protocol: createHistoryProtocol(),
			middlewares
		}),
		createMatchEnhancer(
			// new Matcher(hotRouteConfig(routes), { matchStemRoutes: false })
			new Matcher(routes, { matchStemRoutes: false })
		)
	]
}

export function matchRoutes(store) {
	return getStoreRenderArgs({
		store,
		resolver,
		matchContext: {
			dispatch: store.dispatch,
			getState: store.getState
		}
	})
	.then((renderArgs) => {
		if (renderArgs.error) {
			throw renderArgs.error
		}
		return renderArgs
	})
}

export function getRoutesByPath(routeIndices, routes) {
	const matchedRoutes = []
	for (const i of routeIndices) {
		matchedRoutes.push(routes[i])
		routes = routes[i].children
	}
	return matchedRoutes
}

export function getMatchedRoutes(state, routes) {
	return getRoutesByPath(state.found.match.routeIndices, routes)
}

export function getMatchedRoutesIndices(state) {
	return state.found.match.routeIndices
}

export function getMatchedRoutesParams(state) {
	return state.found.match.routeParams
}

export function getCurrentlyMatchedLocation(state) {
	return state.found.match.location
}

export function getRouteParams(state) {
	return state.found.match.params
}

export function getPreviouslyMatchedLocation(state) {
	return state.found.resolvedMatch &&
		// state.found.resolvedMatch.location.key === undefined &&
		state.found.resolvedMatch.location
}

// Returns a complete route path
// for matched `<Route/>`s chain.
// E.g. returns "/user/:user_id/post/:post_id"
// for matched URL "/user/1/post/123?key=value".
export function getRoutePath(routes)
{
	return routes
		// Select `<Route/>`s having `path` React property set.
		.filter(route => route.path)
		// Trim leading and trailing slashes (`/`)
		// from each `<Route/>` `path` React property.
		.map(route => route.path.replace(/^\//, '').replace(/\/$/, ''))
		// Join `<Route/>` `path`s with slashes (`/`).
		.join('/') || '/'
}

export function convertRoutes(routes) {
	return makeRouteConfig(routes)
}

export function initializeRouter(store) {
	store.dispatch(Actions.init())
}

export const redirect = Actions.replace

export const goto = (location, options = {}) => {
	markImmediateNavigationAsInstantBack(options.instantBack)
	return Actions.push(location)
}

export const REDIRECT_ACTION_TYPE = ActionTypes.REPLACE
export const GOTO_ACTION_TYPE = ActionTypes.PUSH

export function goBack() {
	return Actions.go(-1)
}

export function pushLocation(location, options) {
	window._react_pages_skip_preload_update_location = true
	return goto(location, options)
}

export function replaceLocation(location) {
	window._react_pages_skip_preload_update_location = true
	return redirect(location)
}

export const UPDATE_MATCH = FoundActionTypes.UPDATE_MATCH
export const RESOLVE_MATCH = FoundActionTypes.RESOLVE_MATCH

export const _RESOLVE_MATCH = '@@react-pages/RESOLVE_MATCH'<commit_msg>`matchStemRoutes` has been removed from `found@4`.<commit_after>export { default as foundReducer } from 'found/lib/foundReducer'
export { default as Route } from 'found/lib/Route'
export { default as Redirect } from 'found/lib/Redirect'
export { default as withRouter } from 'found/lib/withRouter'
export { default as RedirectException } from 'found/lib/RedirectException'

import createMatchEnhancer from 'found/lib/createMatchEnhancer'
import Matcher from 'found/lib/Matcher'
import makeRouteConfig from 'found/lib/makeRouteConfig'
import getStoreRenderArgs from 'found/lib/getStoreRenderArgs'
import HttpError from 'found/lib/HttpError'
import resolver from 'found/lib/resolver'
import FoundActionTypes from 'found/lib/ActionTypes'

import Actions from 'farce/lib/Actions'
import ActionTypes from 'farce/lib/ActionTypes'
import createHistoryEnhancer from 'farce/lib/createHistoryEnhancer'
import createBasenameMiddleware from 'farce/lib/createBasenameMiddleware'
import queryMiddleware from 'farce/lib/queryMiddleware'

import { markImmediateNavigationAsInstantBack } from '../redux/client/instantBack'

export function createRouterStoreEnhancers(routes, createHistoryProtocol, options = {}) {
	const middlewares = [
		queryMiddleware
	]
	if (options.basename) {
		middlewares.push(createBasenameMiddleware({
			basename: options.basename
		}))
	}
	return [
		createHistoryEnhancer({
			protocol: createHistoryProtocol(),
			middlewares
		}),
		createMatchEnhancer(
			// new Matcher(hotRouteConfig(routes))
			new Matcher(routes)
		)
	]
}

export function matchRoutes(store) {
	return getStoreRenderArgs({
		store,
		resolver,
		matchContext: {
			dispatch: store.dispatch,
			getState: store.getState
		}
	})
	.then((renderArgs) => {
		if (renderArgs.error) {
			throw renderArgs.error
		}
		return renderArgs
	})
}

export function getRoutesByPath(routeIndices, routes) {
	const matchedRoutes = []
	for (const i of routeIndices) {
		matchedRoutes.push(routes[i])
		routes = routes[i].children
	}
	return matchedRoutes
}

export function getMatchedRoutes(state, routes) {
	return getRoutesByPath(state.found.match.routeIndices, routes)
}

export function getMatchedRoutesIndices(state) {
	return state.found.match.routeIndices
}

export function getMatchedRoutesParams(state) {
	return state.found.match.routeParams
}

export function getCurrentlyMatchedLocation(state) {
	return state.found.match.location
}

export function getRouteParams(state) {
	return state.found.match.params
}

export function getPreviouslyMatchedLocation(state) {
	return state.found.resolvedMatch &&
		// state.found.resolvedMatch.location.key === undefined &&
		state.found.resolvedMatch.location
}

// Returns a complete route path
// for matched `<Route/>`s chain.
// E.g. returns "/user/:user_id/post/:post_id"
// for matched URL "/user/1/post/123?key=value".
export function getRoutePath(routes)
{
	return routes
		// Select `<Route/>`s having `path` React property set.
		.filter(route => route.path)
		// Trim leading and trailing slashes (`/`)
		// from each `<Route/>` `path` React property.
		.map(route => route.path.replace(/^\//, '').replace(/\/$/, ''))
		// Join `<Route/>` `path`s with slashes (`/`).
		.join('/') || '/'
}

export function convertRoutes(routes) {
	return makeRouteConfig(routes)
}

export function initializeRouter(store) {
	store.dispatch(Actions.init())
}

export const redirect = Actions.replace

export const goto = (location, options = {}) => {
	markImmediateNavigationAsInstantBack(options.instantBack)
	return Actions.push(location)
}

export const REDIRECT_ACTION_TYPE = ActionTypes.REPLACE
export const GOTO_ACTION_TYPE = ActionTypes.PUSH

export function goBack() {
	return Actions.go(-1)
}

export function pushLocation(location, options) {
	window._react_pages_skip_preload_update_location = true
	return goto(location, options)
}

export function replaceLocation(location) {
	window._react_pages_skip_preload_update_location = true
	return redirect(location)
}

export const UPDATE_MATCH = FoundActionTypes.UPDATE_MATCH
export const RESOLVE_MATCH = FoundActionTypes.RESOLVE_MATCH

export const _RESOLVE_MATCH = '@@react-pages/RESOLVE_MATCH'<|endoftext|>


<commit_before><commit_msg>Remove src="data:img/png..." from icon images before saving the HTML<commit_after><|endoftext|>


<commit_before>/*global define */
define(['jquery'], function($) {
    'use strict';
    // Loads the specified JSON config file and returns a promise
    // to tell you when it's done.
    return function(url) {
        var loaded = $.Deferred();
        // Uses a HEAD request to figure out if
        // the file exists so it doesn't load it
        // and trigger a fail
        var ajax = $.ajax(url, {
            type: 'HEAD',
            dataType: 'text'
        });
        ajax.then(function(result) {
            console.log(result);
            return $.ajax(url, {
                type: 'GET',
                dataType: 'text'
            });
        }, function(jqXHR, textStatus, errorThrown) {
            console.log('No config, empty file, or bad json. Error: ' + textStatus);
            // It's ok if there's no config file
            // just return an empty object.
            if (errorThrown === 'Not Found') {
                loaded.resolve({});
            } else {
                loaded.reject(jqXHR);
            }
        }).done(function(result, textStatus) {
            console.log('Loaded Config File ' + textStatus);
            try {
                jsonResult = JSON.parse(result);
                loaded.resolve(jsonResult);
            } catch (e) {
                loaded.reject('Unable to parse config.json! Please contact Calamari Admin');
            }
        });
        return loaded.promise();
    };
});
<commit_msg>Fix var. In strict mode<commit_after>/*global define */
define(['jquery'], function($) {
    'use strict';
    // Loads the specified JSON config file and returns a promise
    // to tell you when it's done.
    return function(url) {
        var loaded = $.Deferred();
        // Uses a HEAD request to figure out if
        // the file exists so it doesn't load it
        // and trigger a fail
        var ajax = $.ajax(url, {
            type: 'HEAD',
            dataType: 'text'
        });
        ajax.then(function(result) {
            console.log(result);
            return $.ajax(url, {
                type: 'GET',
                dataType: 'text'
            });
        }, function(jqXHR, textStatus, errorThrown) {
            console.log('No config, empty file, or bad json. Error: ' + textStatus);
            // It's ok if there's no config file
            // just return an empty object.
            if (errorThrown === 'Not Found') {
                loaded.resolve({});
            } else {
                loaded.reject(jqXHR);
            }
        }).done(function(responseText, textStatus) {
            console.log('Loaded Config File ' + textStatus);
            try {
                var jsonResult = JSON.parse(responseText);
                loaded.resolve(jsonResult);
            } catch (e) {
                loaded.reject('Unable to parse config.json! Please contact Calamari Admin');
            }
        });
        return loaded.promise();
    };
});
<|endoftext|>


<commit_before>'use strict';

angular.module('arachne.widgets.map')

/*
 * @author: David Neugebauer
 * @author: Daniel de Oliveira
 */
.factory('placesPainter', ['$compile', function ($compile) {

    var markers = null; // used to keep track of markers for deleting them later
    var map;
    var entityCallback;

    return {

        setMap : function(mp) {
          map = mp;
        },

        setEntityCallback : function (ec) {
            entityCallback = ec;
        },

        clear : function () {
            if (markers) {
                map.removeLayer(markers);
            }
            markers = null;
        },

        // create the actual places' markers
        drawPlaces : function(
            places, // : Place
            scope) {

            if (!places) return;

            markers = L.markerClusterGroup();
            map.addLayer(markers);

            for (var i = 0; i < places.length; i++) {
                var place = places[i];

                if (place.hasCoordinates()) {
                    // Dom-Element für Popup bauen und in Link-Funktion kompilieren
                    var html = '

';
                    var linkFunction = $compile(angular.element(html));
                    var newScope = scope.$new(true);
                    newScope.place = place;
                    newScope.entityCallback = entityCallback;

                    // Marker-Objekt anlegen, mit DOM von ausgeführter Link-Funktion verknüpfen
                    var icon = L.AwesomeMarkers.icon({
                        icon: 'record',
                        markerColor: 'cadetblue'
                    });
                    var marker = L.marker(new L.LatLng(place.location.lat, place.location.lon), {
                        icon: icon,
                        entityCount: place.entityCount,
                        title: place.name
                    });
                    marker.on('click', function(newScope,linkFunction) {
                        var popup;
                        return function(e) {
                            if (!popup) popup = linkFunction(newScope)[0];
                            if (e.target.getPopup()) e.target.unbindPopup();
                            e.target.bindPopup(popup,{ minWidth: 300, autoPan: false });
                            e.target.openPopup();
                        };
                    }(newScope,linkFunction));
                    markers.addLayer(marker);
                }
            }
        }
    }
}]);<commit_msg>red marker clusters appear for lower numbers<commit_after>'use strict';

angular.module('arachne.widgets.map')

/*
 * @author: David Neugebauer
 * @author: Daniel de Oliveira
 */
.factory('placesPainter', ['$compile', function ($compile) {

    var markers = null; // used to keep track of markers for deleting them later
    var map;
    var entityCallback;

    return {

        setMap : function(mp) {
          map = mp;
        },

        setEntityCallback : function (ec) {
            entityCallback = ec;
        },

        clear : function () {
            if (markers) {
                map.removeLayer(markers);
            }
            markers = null;
        },

        // create the actual places' markers
        drawPlaces : function(
            places, // : Place
            scope) {

            if (!places) return;

            markers = L.markerClusterGroup({
                iconCreateFunction: function (cluster) {
                    var childCount = cluster.getChildCount();
                    var c = ' marker-cluster-';
                    if (childCount < 10) c += 'small';
                    else if (childCount < 50) c += 'medium';
                    else c += 'large';
                    return new L.DivIcon({ html: '
' + childCount + '
', className: 'marker-cluster' + c, iconSize: new L.Point(40, 40) });
                }
            });
            map.addLayer(markers);

            for (var i = 0; i < places.length; i++) {
                var place = places[i];

                if (place.hasCoordinates()) {
                    // Dom-Element für Popup bauen und in Link-Funktion kompilieren
                    var html = '

';
                    var linkFunction = $compile(angular.element(html));
                    var newScope = scope.$new(true);
                    newScope.place = place;
                    newScope.entityCallback = entityCallback;

                    // Marker-Objekt anlegen, mit DOM von ausgeführter Link-Funktion verknüpfen
                    var icon = L.AwesomeMarkers.icon({
                        icon: 'record',
                        markerColor: 'cadetblue'
                    });
                    var marker = L.marker(new L.LatLng(place.location.lat, place.location.lon), {
                        icon: icon,
                        entityCount: place.entityCount,
                        title: place.name
                    });
                    marker.on('click', function(newScope,linkFunction) {
                        var popup;
                        return function(e) {
                            if (!popup) popup = linkFunction(newScope)[0];
                            if (e.target.getPopup()) e.target.unbindPopup();
                            e.target.bindPopup(popup,{ minWidth: 300, autoPan: false });
                            e.target.openPopup();
                        };
                    }(newScope,linkFunction));
                    markers.addLayer(marker);
                }
            }
        }
    }
}]);<|endoftext|>


<commit_before>'use strict';
const Config = require('electron-config');

module.exports = new Config({
	defaults: {
		darkMode: false,
    isSyncScroll: false,
    isHtml: false
	}
});
<commit_msg>Fix indentation<commit_after>'use strict';
const Config = require('electron-config');

module.exports = new Config({
	defaults: {
    darkMode: false,
    isSyncScroll: false,
    isHtml: false
	}
});
<|endoftext|>


<commit_before>export {
  isNativeTag,
  isMacroTag,
  isDynamicTag,
  isAttributeTag,
  isTransparentTag,
  isLoopTag,
  getMacroIdentifier,
  getTagDef,
  getFullyResolvedTagName,
  findParentTag,
  findAttributeTags,
  getArgOrSequence,
  loadFileForTag,
  getTemplateId,
  resolveTagImport
} from "./tags";
export {
  assertAllowedAttributes,
  assertNoArgs,
  assertNoAttributes,
  assertNoParams,
  assertNoVar,
  assertNoAttributeTags
} from "./assert";
export { normalizeTemplateString } from "./template-string";

export { getLoc, getLocRange, withLoc } from "./loc";

export { parseScript, parseExpression } from "./parse";

export { resolveRelativePath, importDefault, importNamed } from "./imports";

export { getTaglibLookup, getTagDefForTagName } from "./taglib";
<commit_msg>fix(babel-utils): add missing export<commit_after>export {
  isNativeTag,
  isMacroTag,
  isDynamicTag,
  isAttributeTag,
  isTransparentTag,
  isLoopTag,
  getMacroIdentifier,
  getTagDef,
  getFullyResolvedTagName,
  findParentTag,
  findAttributeTags,
  getArgOrSequence,
  loadFileForTag,
  loadFileForImport,
  getTemplateId,
  resolveTagImport
} from "./tags";
export {
  assertAllowedAttributes,
  assertNoArgs,
  assertNoAttributes,
  assertNoParams,
  assertNoVar,
  assertNoAttributeTags
} from "./assert";
export { normalizeTemplateString } from "./template-string";

export { getLoc, getLocRange, withLoc } from "./loc";

export { parseScript, parseExpression } from "./parse";

export { resolveRelativePath, importDefault, importNamed } from "./imports";

export { getTaglibLookup, getTagDefForTagName } from "./taglib";
<|endoftext|>