<commit_before>/*jshint globalstrict:true, node:true*/

// working on this file

"use strict";

var _ = require("lodash");
var fs = require("fs-extra");
var trash = require("trash");
var utils = require("../utils");

var remote = require("remote");
var dialog = remote.require("dialog");

var ERR_CONSTANTS = {
    ERR_BROWSER_NOT_INSTALLED: 11,
    ERR_CANT_READ: 4,
    ERR_CANT_WRITE: 6,
    ERR_FILE_EXISTS: 10,
    ERR_INVALID_PARAMS: 2,
    ERR_NOT_DIRECTORY: 9,
    ERR_NOT_FILE: 8,
    ERR_NOT_FOUND: 3,
    ERR_OUT_OF_SPACE: 7,
    ERR_UNKNOWN: 1,
    ERR_UNSUPPORTED_ENCODING: 5,
    NO_ERROR: 0
};

// TODO: there's a complete map of these in node
var ERR_MAP = {
    ENOENT: "ERR_NOT_FOUND",
    EPERM: "ERR_CANT_READ",
    EACCES: "ERR_CANT_READ",
    EROFS: "ERR_CANT_WRITE",
    ENOSPC: "ERR_OUT_OF_SPACE"
};

function _mapError(isWriting, callback, err, result) {
    if (err) {
        var mapping = ERR_MAP[err.code];
        if (mapping) {
            if (isWriting && mapping === "ERR_CANT_READ") {
                mapping = "ERR_CANT_WRITE";
            }
            err = ERR_CONSTANTS[mapping];
        } else {
            console.log("Unmapped error code received in fs: " + err.code + "\n" + err.stack);
        }
    }
    callback(err, result);
}

function chmod(path, mode, callback) {
    fs.chmod(path, mode, _.partial(_mapError, true, callback));
}

/*
function copyFile(src, dest, callback) {
    fs.copy(src, dest, _.partial(_mapError, true, callback));
}
*/

/*
function isNetworkDrive(path, callback) {
    // TODO: implement
    callback(null, false);
}
*/

/*
function makedir(path, mode, callback) {
    fs.ensureDir(path, function (err) {
        if (!err && mode) {
            fs.chmod(path, mode, _.partial(_mapError, true, callback));
        } else {
            _mapError(true, callback, err);
        }
    });
}
*/

/*
function moveToTrash(path, callback) {
    trash([path], _.partial(_mapError, true, callback));
}
*/

function readdir(path, callback) {
    fs.readdir(path, _.partial(_mapError, false, callback));
}

function readFile(path, encoding, callback) {
    fs.readFile(path, {
        encoding: encoding || "utf8"
    }, _.partial(_mapError, false, callback));
}

function rename(oldPath, newPath, callback) {
    fs.rename(oldPath, newPath, _.partial(_mapError, true, callback));
}

function showOpenDialog(allowMultipleSelection, chooseDirectory, title, initialPath, fileTypes, callback) {
    var properties = [];
    if (chooseDirectory) {
        properties.push("openDirectory");
    } else {
        properties.push("openFile");
    }
    if (allowMultipleSelection) {
        properties.push("multiSelections");
    }

    // TODO: I don't think defaultPath and filters work right now - we should test that
    // Also, it doesn't return an error code on failure any more (and doesn't pass one to the callback as well)
    return dialog.showOpenDialog({
        title: title,
        defaultPath: initialPath,
        filters: fileTypes,
        properties: properties
    }, function (paths) {
        callback(null, paths.map(utils.convertWindowsPathToUnixPath));
    });
}

function showSaveDialog(title, initialPath, proposedNewFilename, callback) {
    // TODO: Implement proposedNewFilename
    // TODO: I don't think defaultPath works right now - we should test that
    // Also, it doesn't return an error code on failure any more (and doesn't pass one to the callback as well)
    return dialog.showSaveDialog({
        title: title,
        defaultPath: initialPath
    }, function (path) {
        callback(null, utils.convertWindowsPathToUnixPath(path));
    });
}

function stat(path, callback) {
    fs.lstat(path, function (err, stat) {
        if (stat) {
            // TODO: Implement realPath. If "filename" is a symlink,
            // realPath should be the actual path to the linked object.
            if (stat.isSymbolicLink()) {
                return callback(new Error("realPath for symbolic link is not implemented in appshell.fs.stat"));
            }
            stat.realPath = null;
        }
        _mapError(false, callback, err, stat);
    });
}

function unlink(path, callback) {
    fs.unlink(path, _.partial(_mapError, true, callback));
}

function writeFile(path, data, encoding, callback) {
    fs.writeFile(path, data, {
        encoding: encoding || "utf8"
    }, _.partial(_mapError, true, callback));
}

module.exports = _.assign({
    chmod: chmod,
    // copyFile: copyFile,
    // isNetworkDrive: isNetworkDrive,
    // makedir: makedir,
    // moveToTrash: moveToTrash,
    readdir: readdir,
    readFile: readFile,
    rename: rename,
    showOpenDialog: showOpenDialog,
    showSaveDialog: showSaveDialog,
    stat: stat,
    unlink: unlink,
    writeFile: writeFile
}, ERR_CONSTANTS);
<commit_msg>chmod done<commit_after>/*jshint globalstrict:true, node:true*/

// working on this file

"use strict";

var _ = require("lodash");
var fs = require("fs-extra");
var trash = require("trash");
var utils = require("../utils");

var remote = require("remote");
var dialog = remote.require("dialog");

var ERR_CONSTANTS = {
    ERR_BROWSER_NOT_INSTALLED: 11,
    ERR_CANT_READ: 4,
    ERR_CANT_WRITE: 6,
    ERR_FILE_EXISTS: 10,
    ERR_INVALID_PARAMS: 2,
    ERR_NOT_DIRECTORY: 9,
    ERR_NOT_FILE: 8,
    ERR_NOT_FOUND: 3,
    ERR_OUT_OF_SPACE: 7,
    ERR_UNKNOWN: 1,
    ERR_UNSUPPORTED_ENCODING: 5,
    NO_ERROR: 0
};

// TODO: there's a complete map of these in node
var ERR_MAP = {
    ENOENT: "ERR_NOT_FOUND",
    EPERM: "ERR_CANT_READ",
    EACCES: "ERR_CANT_READ",
    EROFS: "ERR_CANT_WRITE",
    ENOSPC: "ERR_OUT_OF_SPACE"
};

function _mapError(isWriting, callback, err, result) {
    if (err) {
        var mapping = ERR_MAP[err.code];
        if (mapping) {
            if (isWriting && mapping === "ERR_CANT_READ") {
                mapping = "ERR_CANT_WRITE";
            }
            err = ERR_CONSTANTS[mapping];
        } else {
            console.log("Unmapped error code received in fs: " + err.code + "\n" + err.stack);
        }
    }
    callback(err, result);
}

/*
function chmod(path, mode, callback) {
    fs.chmod(path, mode, _.partial(_mapError, true, callback));
}
*/

/*
function copyFile(src, dest, callback) {
    fs.copy(src, dest, _.partial(_mapError, true, callback));
}
*/

/*
function isNetworkDrive(path, callback) {
    // TODO: implement
    callback(null, false);
}
*/

/*
function makedir(path, mode, callback) {
    fs.ensureDir(path, function (err) {
        if (!err && mode) {
            fs.chmod(path, mode, _.partial(_mapError, true, callback));
        } else {
            _mapError(true, callback, err);
        }
    });
}
*/

/*
function moveToTrash(path, callback) {
    trash([path], _.partial(_mapError, true, callback));
}
*/

function readdir(path, callback) {
    fs.readdir(path, _.partial(_mapError, false, callback));
}

function readFile(path, encoding, callback) {
    fs.readFile(path, {
        encoding: encoding || "utf8"
    }, _.partial(_mapError, false, callback));
}

function rename(oldPath, newPath, callback) {
    fs.rename(oldPath, newPath, _.partial(_mapError, true, callback));
}

function showOpenDialog(allowMultipleSelection, chooseDirectory, title, initialPath, fileTypes, callback) {
    var properties = [];
    if (chooseDirectory) {
        properties.push("openDirectory");
    } else {
        properties.push("openFile");
    }
    if (allowMultipleSelection) {
        properties.push("multiSelections");
    }

    // TODO: I don't think defaultPath and filters work right now - we should test that
    // Also, it doesn't return an error code on failure any more (and doesn't pass one to the callback as well)
    return dialog.showOpenDialog({
        title: title,
        defaultPath: initialPath,
        filters: fileTypes,
        properties: properties
    }, function (paths) {
        callback(null, paths.map(utils.convertWindowsPathToUnixPath));
    });
}

function showSaveDialog(title, initialPath, proposedNewFilename, callback) {
    // TODO: Implement proposedNewFilename
    // TODO: I don't think defaultPath works right now - we should test that
    // Also, it doesn't return an error code on failure any more (and doesn't pass one to the callback as well)
    return dialog.showSaveDialog({
        title: title,
        defaultPath: initialPath
    }, function (path) {
        callback(null, utils.convertWindowsPathToUnixPath(path));
    });
}

function stat(path, callback) {
    fs.lstat(path, function (err, stat) {
        if (stat) {
            // TODO: Implement realPath. If "filename" is a symlink,
            // realPath should be the actual path to the linked object.
            if (stat.isSymbolicLink()) {
                return callback(new Error("realPath for symbolic link is not implemented in appshell.fs.stat"));
            }
            stat.realPath = null;
        }
        _mapError(false, callback, err, stat);
    });
}

function unlink(path, callback) {
    fs.unlink(path, _.partial(_mapError, true, callback));
}

function writeFile(path, data, encoding, callback) {
    fs.writeFile(path, data, {
        encoding: encoding || "utf8"
    }, _.partial(_mapError, true, callback));
}

module.exports = _.assign({
    // chmod: chmod,
    // copyFile: copyFile,
    // isNetworkDrive: isNetworkDrive,
    // makedir: makedir,
    // moveToTrash: moveToTrash,
    readdir: readdir,
    readFile: readFile,
    rename: rename,
    showOpenDialog: showOpenDialog,
    showSaveDialog: showSaveDialog,
    stat: stat,
    unlink: unlink,
    writeFile: writeFile
}, ERR_CONSTANTS);
<|endoftext|>


<commit_before><commit_msg>Adds support for new format of preview subdomains<commit_after><|endoftext|>


<commit_before>/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 New Vector Ltd
Copyright 2018 New Vector Ltd

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 store/session/webstorage
 */

const utils = require("../../utils");

const DEBUG = false;  // set true to enable console logging.
const E2E_PREFIX = "session.e2e.";

/**
 * Construct a web storage session store, capable of storing account keys,
 * session keys and access tokens.
 * @constructor
 * @param {WebStorage} webStore A web storage implementation, e.g.
 * 'window.localStorage' or 'window.sessionStorage' or a custom implementation.
 * @throws if the supplied 'store' does not meet the Storage interface of the
 * WebStorage API.
 */
function WebStorageSessionStore(webStore) {
    this.store = webStore;
    if (!utils.isFunction(webStore.getItem) ||
        !utils.isFunction(webStore.setItem) ||
        !utils.isFunction(webStore.removeItem) ||
        !utils.isFunction(webStore.key) ||
        typeof(webStore.length) !== 'number'
       ) {
        throw new Error(
            "Supplied webStore does not meet the WebStorage API interface",
        );
    }
}

WebStorageSessionStore.prototype = {
    /**
     * Remove the stored end to end account for the logged-in user.
     */
    removeEndToEndAccount: function() {
        this.store.removeItem(KEY_END_TO_END_ACCOUNT);
    },

    /**
     * Load the end to end account for the logged-in user.
     * Note that the end-to-end account is now stored in the
     * crypto store rather than here: this remains here so
     * old sessions can be migrated out of the session store.
     * @return {?string} Base64 encoded account.
     */
    getEndToEndAccount: function() {
        return this.store.getItem(KEY_END_TO_END_ACCOUNT);
    },

    /**
     * Retrieves the known devices for all users.
     * @return {object} A map from user ID to map of device ID to keys for the device.
     */
    getAllEndToEndDevices: function() {
        const prefix = keyEndToEndDevicesForUser('');
        const devices = {};
        for (let i = 0; i < this.store.length; ++i) {
            const key = this.store.key(i);
            const userId = key.substr(prefix.length);
            if (key.startsWith(prefix)) devices[userId] = getJsonItem(this.store, key);
        }
        return devices;
    },

    getEndToEndDeviceTrackingStatus: function() {
        return getJsonItem(this.store, KEY_END_TO_END_DEVICE_LIST_TRACKING_STATUS);
    },

    /**
     * Get the sync token corresponding to the device list.
     *
     * @return {String?} token
     */
    getEndToEndDeviceSyncToken: function() {
        return getJsonItem(this.store, KEY_END_TO_END_DEVICE_SYNC_TOKEN);
    },

    /**
     * Removes all end to end device data from the store
     */
    removeEndToEndDeviceData: function() {
        removeByPrefix(this.store, keyEndToEndDevicesForUser(''));
        removeByPrefix(this.store, KEY_END_TO_END_DEVICE_LIST_TRACKING_STATUS);
        removeByPrefix(this.store, KEY_END_TO_END_DEVICE_SYNC_TOKEN);
    },

    /**
     * Retrieve the end-to-end sessions between the logged-in user and another
     * device.
     * @param {string} deviceKey The public key of the other device.
     * @return {object} A map from sessionId to Base64 end-to-end session.
     */
    getEndToEndSessions: function(deviceKey) {
        return getJsonItem(this.store, keyEndToEndSessions(deviceKey));
    },

    /**
     * Retrieve all end-to-end sessions between the logged-in user and other
     * devices.
     * @return {object} A map of {deviceKey -> {sessionId -> session pickle}}
     */
    getAllEndToEndSessions: function() {
        const deviceKeys = getKeysWithPrefix(this.store, keyEndToEndSessions(''));
        const results = {};
        for (const k of deviceKeys) {
            const unprefixedKey = k.substr(keyEndToEndSessions('').length);
            results[unprefixedKey] = getJsonItem(this.store, k);
        }
        return results;
    },

    /**
     * Remove all end-to-end sessions from the store
     * This is used after migrating sessions awat from the sessions store.
     */
    removeAllEndToEndSessions: function() {
        removeByPrefix(this.store, keyEndToEndSessions(''));
    },

    /**
     * Retrieve a list of all known inbound group sessions
     *
     * @return {{senderKey: string, sessionId: string}}
     */
    getAllEndToEndInboundGroupSessionKeys: function() {
        const prefix = E2E_PREFIX + 'inboundgroupsessions/';
        const result = [];
        for (let i = 0; i < this.store.length; i++) {
            const key = this.store.key(i);
            if (!key.startsWith(prefix)) {
                continue;
            }
            // we can't use split, as the components we are trying to split out
            // might themselves contain '/' characters. We rely on the
            // senderKey being a (32-byte) curve25519 key, base64-encoded
            // (hence 43 characters long).

            result.push({
                senderKey: key.substr(prefix.length, 43),
                sessionId: key.substr(prefix.length + 44),
            });
        }
        return result;
    },

    getEndToEndInboundGroupSession: function(senderKey, sessionId) {
        const key = keyEndToEndInboundGroupSession(senderKey, sessionId);
        return this.store.getItem(key);
    },

    removeAllEndToEndInboundGroupSessions: function() {
        removeByPrefix(this.store, E2E_PREFIX + 'inboundgroupsessions/');
    },

    /**
     * Get the end-to-end state for all rooms
     * @return {object} roomId -> object with the end-to-end info for the room.
     */
    getAllEndToEndRooms: function() {
        const roomKeys = getKeysWithPrefix(this.store, keyEndToEndRoom(''));
        const results = {};
        for (const k of roomKeys) {
            const unprefixedKey = k.substr(keyEndToEndSessions('').length);
            results[unprefixedKey] = getJsonItem(this.store, k);
        }
        return results;
    },

    removeAllEndToEndRooms: function() {
        removeByPrefix(this.store, keyEndToEndRoom(''));
    },
};

const KEY_END_TO_END_ACCOUNT = E2E_PREFIX + "account";
const KEY_END_TO_END_DEVICE_SYNC_TOKEN = E2E_PREFIX + "device_sync_token";
const KEY_END_TO_END_DEVICE_LIST_TRACKING_STATUS = E2E_PREFIX + "device_tracking";

function keyEndToEndDevicesForUser(userId) {
    return E2E_PREFIX + "devices/" + userId;
}

function keyEndToEndSessions(deviceKey) {
    return E2E_PREFIX + "sessions/" + deviceKey;
}

function keyEndToEndInboundGroupSession(senderKey, sessionId) {
    return E2E_PREFIX + "inboundgroupsessions/" + senderKey + "/" + sessionId;
}

function keyEndToEndRoom(roomId) {
    return E2E_PREFIX + "rooms/" + roomId;
}

function getJsonItem(store, key) {
    try {
        // if the key is absent, store.getItem() returns null, and
        // JSON.parse(null) === null, so this returns null.
        return JSON.parse(store.getItem(key));
    } catch (e) {
        debuglog("Failed to get key %s: %s", key, e);
        debuglog(e.stack);
    }
    return null;
}

function setJsonItem(store, key, val) {
    store.setItem(key, JSON.stringify(val));
}

function getKeysWithPrefix(store, prefix) {
    const results = [];
    for (let i = 0; i < store.length; ++i) {
        const key = store.key(i);
        if (key.startsWith(prefix)) results.push(key);
    }
    return results;
}

function removeByPrefix(store, prefix) {
    const toRemove = [];
    for (let i = 0; i < store.length; ++i) {
        const key = store.key(i);
        if (key.startsWith(prefix)) toRemove.push(key);
    }
    for (const key of toRemove) {
        store.removeItem(key);
    }
}

function debuglog() {
    if (DEBUG) {
        console.log(...arguments);
    }
}

/** */
module.exports = WebStorageSessionStore;
<commit_msg>Use the right prefix function to remove prefix<commit_after>/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 New Vector Ltd
Copyright 2018 New Vector Ltd

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 store/session/webstorage
 */

const utils = require("../../utils");

const DEBUG = false;  // set true to enable console logging.
const E2E_PREFIX = "session.e2e.";

/**
 * Construct a web storage session store, capable of storing account keys,
 * session keys and access tokens.
 * @constructor
 * @param {WebStorage} webStore A web storage implementation, e.g.
 * 'window.localStorage' or 'window.sessionStorage' or a custom implementation.
 * @throws if the supplied 'store' does not meet the Storage interface of the
 * WebStorage API.
 */
function WebStorageSessionStore(webStore) {
    this.store = webStore;
    if (!utils.isFunction(webStore.getItem) ||
        !utils.isFunction(webStore.setItem) ||
        !utils.isFunction(webStore.removeItem) ||
        !utils.isFunction(webStore.key) ||
        typeof(webStore.length) !== 'number'
       ) {
        throw new Error(
            "Supplied webStore does not meet the WebStorage API interface",
        );
    }
}

WebStorageSessionStore.prototype = {
    /**
     * Remove the stored end to end account for the logged-in user.
     */
    removeEndToEndAccount: function() {
        this.store.removeItem(KEY_END_TO_END_ACCOUNT);
    },

    /**
     * Load the end to end account for the logged-in user.
     * Note that the end-to-end account is now stored in the
     * crypto store rather than here: this remains here so
     * old sessions can be migrated out of the session store.
     * @return {?string} Base64 encoded account.
     */
    getEndToEndAccount: function() {
        return this.store.getItem(KEY_END_TO_END_ACCOUNT);
    },

    /**
     * Retrieves the known devices for all users.
     * @return {object} A map from user ID to map of device ID to keys for the device.
     */
    getAllEndToEndDevices: function() {
        const prefix = keyEndToEndDevicesForUser('');
        const devices = {};
        for (let i = 0; i < this.store.length; ++i) {
            const key = this.store.key(i);
            const userId = key.substr(prefix.length);
            if (key.startsWith(prefix)) devices[userId] = getJsonItem(this.store, key);
        }
        return devices;
    },

    getEndToEndDeviceTrackingStatus: function() {
        return getJsonItem(this.store, KEY_END_TO_END_DEVICE_LIST_TRACKING_STATUS);
    },

    /**
     * Get the sync token corresponding to the device list.
     *
     * @return {String?} token
     */
    getEndToEndDeviceSyncToken: function() {
        return getJsonItem(this.store, KEY_END_TO_END_DEVICE_SYNC_TOKEN);
    },

    /**
     * Removes all end to end device data from the store
     */
    removeEndToEndDeviceData: function() {
        removeByPrefix(this.store, keyEndToEndDevicesForUser(''));
        removeByPrefix(this.store, KEY_END_TO_END_DEVICE_LIST_TRACKING_STATUS);
        removeByPrefix(this.store, KEY_END_TO_END_DEVICE_SYNC_TOKEN);
    },

    /**
     * Retrieve the end-to-end sessions between the logged-in user and another
     * device.
     * @param {string} deviceKey The public key of the other device.
     * @return {object} A map from sessionId to Base64 end-to-end session.
     */
    getEndToEndSessions: function(deviceKey) {
        return getJsonItem(this.store, keyEndToEndSessions(deviceKey));
    },

    /**
     * Retrieve all end-to-end sessions between the logged-in user and other
     * devices.
     * @return {object} A map of {deviceKey -> {sessionId -> session pickle}}
     */
    getAllEndToEndSessions: function() {
        const deviceKeys = getKeysWithPrefix(this.store, keyEndToEndSessions(''));
        const results = {};
        for (const k of deviceKeys) {
            const unprefixedKey = k.substr(keyEndToEndSessions('').length);
            results[unprefixedKey] = getJsonItem(this.store, k);
        }
        return results;
    },

    /**
     * Remove all end-to-end sessions from the store
     * This is used after migrating sessions awat from the sessions store.
     */
    removeAllEndToEndSessions: function() {
        removeByPrefix(this.store, keyEndToEndSessions(''));
    },

    /**
     * Retrieve a list of all known inbound group sessions
     *
     * @return {{senderKey: string, sessionId: string}}
     */
    getAllEndToEndInboundGroupSessionKeys: function() {
        const prefix = E2E_PREFIX + 'inboundgroupsessions/';
        const result = [];
        for (let i = 0; i < this.store.length; i++) {
            const key = this.store.key(i);
            if (!key.startsWith(prefix)) {
                continue;
            }
            // we can't use split, as the components we are trying to split out
            // might themselves contain '/' characters. We rely on the
            // senderKey being a (32-byte) curve25519 key, base64-encoded
            // (hence 43 characters long).

            result.push({
                senderKey: key.substr(prefix.length, 43),
                sessionId: key.substr(prefix.length + 44),
            });
        }
        return result;
    },

    getEndToEndInboundGroupSession: function(senderKey, sessionId) {
        const key = keyEndToEndInboundGroupSession(senderKey, sessionId);
        return this.store.getItem(key);
    },

    removeAllEndToEndInboundGroupSessions: function() {
        removeByPrefix(this.store, E2E_PREFIX + 'inboundgroupsessions/');
    },

    /**
     * Get the end-to-end state for all rooms
     * @return {object} roomId -> object with the end-to-end info for the room.
     */
    getAllEndToEndRooms: function() {
        const roomKeys = getKeysWithPrefix(this.store, keyEndToEndRoom(''));
        const results = {};
        for (const k of roomKeys) {
            const unprefixedKey = k.substr(keyEndToEndRoom('').length);
            results[unprefixedKey] = getJsonItem(this.store, k);
        }
        return results;
    },

    removeAllEndToEndRooms: function() {
        removeByPrefix(this.store, keyEndToEndRoom(''));
    },
};

const KEY_END_TO_END_ACCOUNT = E2E_PREFIX + "account";
const KEY_END_TO_END_DEVICE_SYNC_TOKEN = E2E_PREFIX + "device_sync_token";
const KEY_END_TO_END_DEVICE_LIST_TRACKING_STATUS = E2E_PREFIX + "device_tracking";

function keyEndToEndDevicesForUser(userId) {
    return E2E_PREFIX + "devices/" + userId;
}

function keyEndToEndSessions(deviceKey) {
    return E2E_PREFIX + "sessions/" + deviceKey;
}

function keyEndToEndInboundGroupSession(senderKey, sessionId) {
    return E2E_PREFIX + "inboundgroupsessions/" + senderKey + "/" + sessionId;
}

function keyEndToEndRoom(roomId) {
    return E2E_PREFIX + "rooms/" + roomId;
}

function getJsonItem(store, key) {
    try {
        // if the key is absent, store.getItem() returns null, and
        // JSON.parse(null) === null, so this returns null.
        return JSON.parse(store.getItem(key));
    } catch (e) {
        debuglog("Failed to get key %s: %s", key, e);
        debuglog(e.stack);
    }
    return null;
}

function setJsonItem(store, key, val) {
    store.setItem(key, JSON.stringify(val));
}

function getKeysWithPrefix(store, prefix) {
    const results = [];
    for (let i = 0; i < store.length; ++i) {
        const key = store.key(i);
        if (key.startsWith(prefix)) results.push(key);
    }
    return results;
}

function removeByPrefix(store, prefix) {
    const toRemove = [];
    for (let i = 0; i < store.length; ++i) {
        const key = store.key(i);
        if (key.startsWith(prefix)) toRemove.push(key);
    }
    for (const key of toRemove) {
        store.removeItem(key);
    }
}

function debuglog() {
    if (DEBUG) {
        console.log(...arguments);
    }
}

/** */
module.exports = WebStorageSessionStore;
<|endoftext|>


<commit_before>import _ from "underscore";
import _t from "../../../core/i18n-wrapper";

const menuOptions = {
    "openItem": {
        url: "#",
        title: _t("Open"),
        category: "button",
        icon: "plone-view",
        css: "",
        modal: false,
    },
    "editItem": {
        url: "#",
        title: _t("Edit"),
        category: "button",
        icon: "plone-edit",
        css: "",
        modal: false,
    },
    "cutItem": {
        method: "cutClicked",
        url: "#",
        title: _t("Cut"),
        category: "dropdown",
        icon: "plone-cut",
        css: "",
        modal: false,
    },
    "copyItem": {
        method: "copyClicked",
        url: "#",
        title: _t("Copy"),
        category: "dropdown",
        icon: "plone-copy",
        css: "",
        modal: false,
    },
    "pasteItem": {
        method: "pasteClicked",
        url: "#",
        title: _t("Paste"),
        category: "dropdown",
        icon: "plone-paste",
        css: "",
        modal: false,
    },
    "move-top": {
        method: "moveTopClicked",
        url: "#",
        title: _t("Move to top of folder"),
        category: "dropdown",
        icon: "arrow-bar-up",
        css: "",
        modal: false,
    },
    "move-bottom": {
        method: "moveBottomClicked",
        url: "#",
        title: _t("Move to bottom of folder"),
        category: "dropdown",
        icon: "arrow-bar-down",
        css: "",
        modal: false,
    },
    "set-default-page": {
        method: "setDefaultPageClicked",
        url: "#",
        title: _t("Set as default page"),
        category: "dropdown",
        icon: "check-circle",
        css: "",
        modal: false,
    },
    "selectAll": {
        method: "selectAll",
        url: "#",
        title: _t("Select all contained items"),
        category: "dropdown",
        icon: "check-all",
        css: "",
        modal: false,
    },
};

const ActionMenu = function (menu) {
    // If an explicit menu was specified as an option to AppView, this
    // constructor will not override that.
    if (menu.menuOptions !== null) {
        return menu.menuOptions;
    }

    const model = menu.model.attributes;
    const app = menu.app;

    const result = _.clone(menuOptions);
    if (!(app.pasteAllowed() && model.is_folderish)) {
        delete result.pasteItem;
    }
    if (app.inQueryMode() || menu.options.canMove === false) {
        delete result["move-top"];
        delete result["move-bottom"];
    }
    if (
        app.defaultPageTypes.indexOf(model.portal_type) === -1 ||
        !app.setDefaultPageUrl
    ) {
        delete result["set-default-page"];
    }

    if (!model.is_folderish) {
        delete result.selectAll;
    }

    const typeToViewAction = app.options.typeToViewAction;
    const viewAction = (typeToViewAction && typeToViewAction[model.portal_type]) || "";
    result.openItem.url = model.getURL + viewAction;
    result.editItem.url = model.getURL + "/edit";

    return result;
};

export default ActionMenu;
<commit_msg>use .cloneDeep from lodash instead of .clone from underscore<commit_after>import _ from "lodash";
import _t from "../../../core/i18n-wrapper";

const menuOptions = {
    "openItem": {
        url: "#",
        title: _t("Open"),
        category: "button",
        icon: "plone-view",
        css: "",
        modal: false,
    },
    "editItem": {
        url: "#",
        title: _t("Edit"),
        category: "button",
        icon: "plone-edit",
        css: "",
        modal: false,
    },
    "cutItem": {
        method: "cutClicked",
        url: "#",
        title: _t("Cut"),
        category: "dropdown",
        icon: "plone-cut",
        css: "",
        modal: false,
    },
    "copyItem": {
        method: "copyClicked",
        url: "#",
        title: _t("Copy"),
        category: "dropdown",
        icon: "plone-copy",
        css: "",
        modal: false,
    },
    "pasteItem": {
        method: "pasteClicked",
        url: "#",
        title: _t("Paste"),
        category: "dropdown",
        icon: "plone-paste",
        css: "",
        modal: false,
    },
    "move-top": {
        method: "moveTopClicked",
        url: "#",
        title: _t("Move to top of folder"),
        category: "dropdown",
        icon: "arrow-bar-up",
        css: "",
        modal: false,
    },
    "move-bottom": {
        method: "moveBottomClicked",
        url: "#",
        title: _t("Move to bottom of folder"),
        category: "dropdown",
        icon: "arrow-bar-down",
        css: "",
        modal: false,
    },
    "set-default-page": {
        method: "setDefaultPageClicked",
        url: "#",
        title: _t("Set as default page"),
        category: "dropdown",
        icon: "check-circle",
        css: "",
        modal: false,
    },
    "selectAll": {
        method: "selectAll",
        url: "#",
        title: _t("Select all contained items"),
        category: "dropdown",
        icon: "check-all",
        css: "",
        modal: false,
    },
};

const ActionMenu = function (menu) {
    // If an explicit menu was specified as an option to AppView, this
    // constructor will not override that.
    if (menu.menuOptions !== null) {
        return menu.menuOptions;
    }

    const model = menu.model.attributes;
    const app = menu.app;

    const result = _.cloneDeep(menuOptions);
    if (!(app.pasteAllowed() && model.is_folderish)) {
        delete result.pasteItem;
    }
    if (app.inQueryMode() || menu.options.canMove === false) {
        delete result["move-top"];
        delete result["move-bottom"];
    }
    if (
        app.defaultPageTypes.indexOf(model.portal_type) === -1 ||
        !app.setDefaultPageUrl
    ) {
        delete result["set-default-page"];
    }

    if (!model.is_folderish) {
        delete result.selectAll;
    }

    const typeToViewAction = app.options.typeToViewAction;
    const viewAction = (typeToViewAction && typeToViewAction[model.portal_type]) || "";
    result.openItem.url = model.getURL + viewAction;
    result.editItem.url = model.getURL + "/edit";

    return result;
};

export default ActionMenu;
<|endoftext|>


<commit_before>/**
  The MIT License (MIT)

  Copyright (c) 2014 Jeppe Rune Mortensen

  Permission is hereby granted, free of charge, to any person obtaining a copy of
  this software and associated documentation files (the "Software"), to deal in
  the Software without restriction, including without limitation the rights to
  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  the Software, and to permit persons to whom the Software is furnished to do so,
  subject to the following conditions:

  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
  FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
**/
// ==UserScript==
// @id           RedditRetractableSidebar
// @name         Reddit Retractable Sidebar
// @namespace    http://michaelcharl.es
// @version      0.4.2
// @description  Make Reddit's Sidebar Retractable
// @icon         https://mca62511.github.io/reddit-retractable-sidebar/favicon.png
// @icon64       https://mca62511.github.io/reddit-retractable-sidebar/favicon.png
// @author       Michael Aubrey
// @domain       reddit.com
// @domain       www.reddit.com
// @match        https://www.reddit.com/*
// @match        https://reddit.com/*
// @match        http://www.reddit.com/*
// @match        http://reddit.com/*
// @grant        none
// @license         MIT

// ==/UserScript==
/* global $ */

(function() {
    'use strict';
    var leftIcon = 'chevron_left';
    var rightIcon = 'chevron_right';
    $("head").append("<link href='https://fonts.googleapis.com/icon?family=Material+Icons' rel='stylesheet' type='text/css'>");
    var $content = $("div.content");
    var $toggleButton = $("

");
    $toggleButton.css({
        background: "#CEE3F8",
        color: "#212121",
        display: "flex",
        'justify-content': "center",
        'align-items': "center",
        height: "42px",
        width: "42px",
        "font-size": "32px",
        "border-radius": "100px",
        "box-shadow": "0px 3px 5px 0px rgba(33,33,33,0.3)",
        position: "fixed",
        top: "50%",
        right: "-7px",
        cursor: "pointer",
        "z-index": "9983"
    });
    var $sidebar = $("div.side");
    /**************************
    Add vote information below link so that when
    the sidebar is hidden you can still see what vote
    percentage and total votes you have.
    ***************************/
    var $score = $("");
    var votePercentage = $(".score").text().match(/\((.*?)\)/);
    var voteTotal = $(".linkinfo span.totalvotes").text();
    if (votePercentage) {
        $score.html(voteTotal.length > 0 ? votePercentage[1] + " out of " + voteTotal + " total, " : votePercentage[1] + ", ");
        $score.prependTo($("#siteTable .tagline"));
    }
    /**************************
    Some style changes to fix the way certain templates
    render with this script.
    ***************************/
    $("body").append("")
        /*****************************
        Functions for hiding and showing the sidebar
        ****************************/
    function hideSidebar() {
        $sidebar.hide();
        $content.css({
            "margin-right": "15px"
        });
        $toggleButton.html(leftIcon);
        setCookie("isRetracted", "true", 30);
    }

    function showSidebar() {
        $sidebar.show();
        $content.css({
            "margin-right": "335px"
        });
        $toggleButton.html(rightIcon);
        setCookie("isRetracted", "false", 30);
    }

    // These setCookie and getCookie functions were copied
    // from w3schools.
    function setCookie(cname, cvalue, exdays) {
        var d = new Date();
        d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
        var expires = "expires=" + d.toUTCString();
        document.cookie = cname + "=" + cvalue + "; " + expires + ";domain=.reddit.com;path=/";
    }

    function getCookie(cname) {
        var name = cname + "=";
        var ca = document.cookie.split(';');
        for (var i = 0; i < ca.length; i++) {
            var c = ca[i];
            while (c.charAt(0) == ' ') {
                c = c.substring(1);
            }
            if (c.indexOf(name) === 0) {
                return c.substring(name.length, c.length);
            }
        }
        return "";
    }
    /************************************************
    Unless it is a submit page, load up the fab and
    hide or show the sidebar according to the last
    state that it was in.
    *************************************************/
    if (window.location.href.indexOf("/submit") === -1 && window.location.href.indexOf("/submitted") > -1 ) {
        if (getCookie("isRetracted") == "true") {
            hideSidebar();
        }
        else {
            showSidebar();
        }
        $("body").append($toggleButton);
        $toggleButton.click(function() {
            var currentIcon = $toggleButton.html().trim();
            if (currentIcon === rightIcon) {
                hideSidebar();
            }
            else {
                showSidebar();
            }
        });
    }
    /*******************************
    Causes the sidebar to retract when you press "q"
    ********************************/
    $("body").on("keypress", function(e) {
        var tagName = e.target.tagName.toLowerCase();
        console.log(e.keyCode);
        if (tagName === "input" || tagName === "textarea") {
            return /*do nothing */;
        }
        else {
            if (e.keyCode === 113) {
                $toggleButton.click();
            }
        }
    });
})();<commit_msg>fixed if logic<commit_after>/**
  The MIT License (MIT)

  Copyright (c) 2014 Jeppe Rune Mortensen

  Permission is hereby granted, free of charge, to any person obtaining a copy of
  this software and associated documentation files (the "Software"), to deal in
  the Software without restriction, including without limitation the rights to
  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  the Software, and to permit persons to whom the Software is furnished to do so,
  subject to the following conditions:

  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
  FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
**/
// ==UserScript==
// @id           RedditRetractableSidebar
// @name         Reddit Retractable Sidebar
// @namespace    http://michaelcharl.es
// @version      0.4.3
// @description  Make Reddit's Sidebar Retractable
// @icon         https://mca62511.github.io/reddit-retractable-sidebar/favicon.png
// @icon64       https://mca62511.github.io/reddit-retractable-sidebar/favicon.png
// @author       Michael Aubrey
// @domain       reddit.com
// @domain       www.reddit.com
// @match        https://www.reddit.com/*
// @match        https://reddit.com/*
// @match        http://www.reddit.com/*
// @match        http://reddit.com/*
// @grant        none
// @license         MIT

// ==/UserScript==
/* global $ */

(function() {
    'use strict';
    var leftIcon = 'chevron_left';
    var rightIcon = 'chevron_right';
    $("head").append("<link href='https://fonts.googleapis.com/icon?family=Material+Icons' rel='stylesheet' type='text/css'>");
    var $content = $("div.content");
    var $toggleButton = $("

");
    $toggleButton.css({
        background: "#CEE3F8",
        color: "#212121",
        display: "flex",
        'justify-content': "center",
        'align-items': "center",
        height: "42px",
        width: "42px",
        "font-size": "32px",
        "border-radius": "100px",
        "box-shadow": "0px 3px 5px 0px rgba(33,33,33,0.3)",
        position: "fixed",
        top: "50%",
        right: "-7px",
        cursor: "pointer",
        "z-index": "9983"
    });
    var $sidebar = $("div.side");
    /**************************
    Add vote information below link so that when
    the sidebar is hidden you can still see what vote
    percentage and total votes you have.
    ***************************/
    var $score = $("");
    var votePercentage = $(".score").text().match(/\((.*?)\)/);
    var voteTotal = $(".linkinfo span.totalvotes").text();
    if (votePercentage) {
        $score.html(voteTotal.length > 0 ? votePercentage[1] + " out of " + voteTotal + " total, " : votePercentage[1] + ", ");
        $score.prependTo($("#siteTable .tagline"));
    }
    /**************************
    Some style changes to fix the way certain templates
    render with this script.
    ***************************/
    $("body").append("")
        /*****************************
        Functions for hiding and showing the sidebar
        ****************************/
    function hideSidebar() {
        $sidebar.hide();
        $content.css({
            "margin-right": "15px"
        });
        $toggleButton.html(leftIcon);
        setCookie("isRetracted", "true", 30);
    }

    function showSidebar() {
        $sidebar.show();
        $content.css({
            "margin-right": "335px"
        });
        $toggleButton.html(rightIcon);
        setCookie("isRetracted", "false", 30);
    }

    // These setCookie and getCookie functions were copied
    // from w3schools.
    function setCookie(cname, cvalue, exdays) {
        var d = new Date();
        d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
        var expires = "expires=" + d.toUTCString();
        document.cookie = cname + "=" + cvalue + "; " + expires + ";domain=.reddit.com;path=/";
    }

    function getCookie(cname) {
        var name = cname + "=";
        var ca = document.cookie.split(';');
        for (var i = 0; i < ca.length; i++) {
            var c = ca[i];
            while (c.charAt(0) == ' ') {
                c = c.substring(1);
            }
            if (c.indexOf(name) === 0) {
                return c.substring(name.length, c.length);
            }
        }
        return "";
    }
    /************************************************
    Unless it is a submit page, load up the fab and
    hide or show the sidebar according to the last
    state that it was in.
    *************************************************/
    if (window.location.href.indexOf("/submit") > -1 && window.location.href.indexOf("/submitted") === -1 ) {
            // do nothing
    } else {
        if (getCookie("isRetracted") == "true") {
            hideSidebar();
        }
        else {
            showSidebar();
        }
        $("body").append($toggleButton);
        $toggleButton.click(function() {
            var currentIcon = $toggleButton.html().trim();
            if (currentIcon === rightIcon) {
                hideSidebar();
            }
            else {
                showSidebar();
            }
        });
    }
    /*******************************
    Causes the sidebar to retract when you press "q"
    ********************************/
    $("body").on("keypress", function(e) {
        var tagName = e.target.tagName.toLowerCase();
        console.log(e.keyCode);
        if (tagName === "input" || tagName === "textarea") {
            return /*do nothing */;
        }
        else {
            if (e.keyCode === 113) {
                $toggleButton.click();
            }
        }
    });
})();<|endoftext|>


<commit_before><commit_msg>v2/handler: fix streamingRes leak in the error frame case<commit_after><|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import { createStyleSheet } from 'stylishly';
import { CircularProgress } from 'material-ui/Progress';
import { green } from 'material-ui/styles/colors';
import Button from 'material-ui/Button';

const styleSheet = createStyleSheet('CircularFab', (theme) => {
  return {
    wrapper: {
      position: 'relative',
      successButton: {
        backgroundColor: green[500],
        '&:hover': {
          backgroundColor: green[700],
        },
      },
    },
    progress: {
      position: 'absolute',
      top: -2,
      left: -2,
    },
  };
});

export default class CircularFab extends Component {
  static contextTypes = {
    styleManager: PropTypes.object.isRequired,
  };

  state = {
    loading: false,
    success: false,
  };

  handleButtonClick = () => {
    if (!this.state.loading) {
      this.setState({
        success: false,
        loading: true,
      }, () => {
        setTimeout(() => {
          this.setState({
            loading: false,
            success: true,
          });
        }, 2000);
      });
    }
  };

  render() {
    const { loading, success } = this.state;
    const classes = this.context.styleManager.render(styleSheet);
    let buttonClass = '';

    if (success) {
      buttonClass = classes.successButton;
    }

    return (

        <Button
          fab
          primary
          className={buttonClass}
          onClick={this.handleButtonClick}
        >
          {success ? 'check' : 'save'}
        </Button>
        {loading && <CircularProgress size={60} className={classes.progress} />}

    );
  }
}

<commit_msg>Fix lint error<commit_after>import React, { Component, PropTypes } from 'react';
import { createStyleSheet } from 'stylishly';
import { CircularProgress } from 'material-ui/Progress';
import { green } from 'material-ui/styles/colors';
import Button from 'material-ui/Button';

const styleSheet = createStyleSheet('CircularFab', () => {
  return {
    wrapper: {
      position: 'relative',
      successButton: {
        backgroundColor: green[500],
        '&:hover': {
          backgroundColor: green[700],
        },
      },
    },
    progress: {
      position: 'absolute',
      top: -2,
      left: -2,
    },
  };
});

export default class CircularFab extends Component {
  static contextTypes = {
    styleManager: PropTypes.object.isRequired,
  };

  state = {
    loading: false,
    success: false,
  };

  handleButtonClick = () => {
    if (!this.state.loading) {
      this.setState({
        success: false,
        loading: true,
      }, () => {
        setTimeout(() => {
          this.setState({
            loading: false,
            success: true,
          });
        }, 2000);
      });
    }
  };

  render() {
    const { loading, success } = this.state;
    const classes = this.context.styleManager.render(styleSheet);
    let buttonClass = '';

    if (success) {
      buttonClass = classes.successButton;
    }

    return (

        <Button
          fab
          primary
          className={buttonClass}
          onClick={this.handleButtonClick}
        >
          {success ? 'check' : 'save'}
        </Button>
        {loading && <CircularProgress size={60} className={classes.progress} />}

    );
  }
}

<|endoftext|>


<commit_before><commit_msg>implemented all the core api calls<commit_after><|endoftext|>


<commit_before>function TasksListEventRenderer() {
    var t = this;

    // exports
    t.renderEvents = renderEvents;
    t.clearEvents = clearEvents;
    t.bindDaySeg = bindDaySeg;
    t.getSelectedTasks = getSelectedTasks;
    t.applyToSelectedTasks = applyToSelectedTasks;
    t.unselectAllTasks = unselectAllTasks;

    // imports
    TasksListViewEventRenderer.call(t);
    var opt = t.opt;
    var trigger = t.trigger;
    var reportEvents = t.reportEvents;
    var reportEventClear = t.reportEventClear;
    var eventElementHandlers = t.eventElementHandlers;
    var getSegmentContainer = t.getSegmentContainer;
    var getActionContainer = t.getActionContainer;
    var renderTaskSegs = t.renderTaskSegs;

    // locals
    var selectedTasks = [];

    function renderEvents(events, modifiedEventId) {
        reportEvents(events);
        renderTaskSegs(compileSegs(events), selectedTasks, modifiedEventId);
    }

    function clearEvents() {
        reportEventClear();
        getSegmentContainer().empty();
    }

    function compileSegs(events) {
        var cnt = events.length,
            event, i,
            segs = [],
            optCnt = opt('tasksListLimit');
        if (optCnt > 0 && cnt > optCnt) {
            cnt = optCnt;
        }
        for (i = 0; i < cnt; i++) {
            event = events[i];
            if (event.type === 'task'
                && (opt('tasksShowDone') || !event.done)
                && (opt('tasksShowCanceled') || !event.canceled)
            ) {
                segs.push({ event: event });
            }
        }
        segs.sort(function (a, b) {
            return a.event.position - b.event.position;
        });
        return segs;
    }

    function bindDaySeg(event, eventElement, seg) {
        var $li = eventElement.closest('li');
        $li.find('.fc-task-checkbox input[type="checkbox"]').on('change', function(ev) {
            if ($(this).is(':checked')) {
                $li.addClass('fc-state-highlight');
                selectedTasks.push(event);
                getActionContainer().addClass('fc-tasks-select-actions-active');
                trigger('taskSelect', event, event, ev);
            } else {
                $li.removeClass('fc-state-highlight');
                var index = selectedTasks.indexOf(event);
                selectedTasks.splice(index, 1);
                if (!selectedTasks.length) {
                    getActionContainer().removeClass('fc-tasks-select-actions-active');
                }
                trigger('taskUnselect', event, event, ev);
            }
        });
        eventElementHandlers(event, eventElement);
    }

    function getSelectedTasks() {
        return selectedTasks;
    }

    function applyToSelectedTasks(func) {
        var i, cnt = selectedTasks.length;
        for (i = 0; i < cnt; i++) {
            func(selectedTasks[i]);
        }
    }

    function unselectAllTasks() {
        getSegmentContainer().find('.fc-task-checkbox input[type="checkbox"]:checked').trigger('click');
        selectedTasks = [];
        getActionContainer().removeClass('fc-tasks-select-actions-active');
    }
}

function TasksListViewEventRenderer() {
    var t = this;

    // exports
    t.renderTaskSegs = renderTaskSegs;

    // imports
    var opt = t.opt;
    var trigger = t.trigger;
    var reportEventElement = t.reportEventElement;
    var getSegmentContainer = t.getSegmentContainer;
    var bindDaySeg = t.bindDaySeg;
    var formatDates = t.calendar.formatDates;
    var getActionContainer = t.getActionContainer;

    function renderTaskSegs(segs, selectedTasks, modifiedEventId) {
        var segmentContainer = getSegmentContainer();

        segmentContainer[0].innerHTML = tasksSegHTML(segs, selectedTasks); // faster than .html()
        segElementResolve(segs, segmentContainer.find('.fc-task-title'));
        segElementReport(segs);
        segHandlers(segs, segmentContainer, modifiedEventId);

        triggerEventAfterRender(segs);
    }

    function tasksSegHTML(segs, selectedTasks) {
        var i, j;
        var seg, segCnt = segs.length;
        var newSelectedTasks = [];
        var event;
        var classes, taskClasses, timeClasses;

        var html = '
';

        for (i = 0; i < segCnt; i++) {
            seg = segs[i];

            event = seg.event;
            classes = [];
            taskClasses = ['fc-task', 'fc-event-skin'];
            timeClasses = ['fc-task-date'];

            var selected = (selectedTasks.indexOf(event) >= 0);
            if (!selected) {
                $.each(selectedTasks, function(index, ev) {
                    if (ev.id && ev.id == event.id) {
                        selected = true;
                    }
                });
            }
            if (selected) {
                newSelectedTasks.push(event);
                classes.push('fc-state-highlight')
            }
            taskClasses = taskClasses.concat(event.className);
            if (event.source) {
                taskClasses = taskClasses.concat(event.source.className || []);
            }
            if (event.canceled) {
                taskClasses.push('fc-task-canceled');
                timeClasses.push('fc-task-canceled');
            } else if (event.done) {
                taskClasses.push('fc-task-done');
                timeClasses.push('fc-task-done');
            }

            var today = clearTime(new Date());
            var eventDay = cloneDate(event.start, true);
            if (+today == +eventDay) {
                timeClasses.push('fc-state-highlight fc-today');
            }

            html += '
';
            if (event.indent) {
                for (j = 0; j < event.indent; j++) {
                    html += '';
                }
            }
            html += '<input type="checkbox"';
            if (selected) {
                html += ' checked="checked"';
            }
            html += '/>';

            var url = event.url;
            var skinCss = getSkinCss(event, opt);
            var skinCssAttr = (skinCss ? ' style="' + skinCss + '"' : "");

            html += ' ';
            if (event.canceled) {
                html += '' + opt("tasksCanceled") + ' ';
            }
            html +=     (url ? ('<a href="' + htmlEscape(url) + '"') : '' +
                        htmlEscape(event.title) +
                        '</' + (url ? 'a' : 'span' ) + '>' +
                    '';
            html += '' + htmlEscape(formatDates(event.start, event.end, opt('dateFormat')));
                html += ' ' + htmlEscape(formatDates(event.start, event.end, opt("timeFormat"))) + '';
            }
            else {
                html += '>';
            }
            html += '';
            html += '
';
        }

        html += '
';

        selectedTasks.length = 0;
        selectedTasks.push.apply(selectedTasks, newSelectedTasks);
        if (!selectedTasks.length) {
            getActionContainer().removeClass('fc-tasks-select-actions-active');
        }

        return html;
    }

    function segElementResolve(segs, elements) {
        var i;
        var segCnt = segs.length;
        var seg;
        var event;
        var element;
        var triggerRes;
        for (i = 0; i < segCnt; i++) {
            seg = segs[i];
            event = seg.event;
            element = $(elements[i]);
            triggerRes = trigger('eventRender', event, event, element);
            if (triggerRes === false) {
                element.remove();
            } else {
                if (triggerRes && triggerRes !== true) {
                    triggerRes = $(triggerRes)
                        .css({
                            position: 'absolute',
                            left: seg.left
                        });
                    element.replaceWith(triggerRes);
                    element = triggerRes;
                }
                seg.element = element;
                element.data('fcEvent', seg.event);
            }
        }
    }

    function segElementReport(segs) {
        var i;
        var segCnt = segs.length;
        var seg;
        var element;
        for (i = 0; i < segCnt; i++) {
            seg = segs[i];
            element = seg.element;
            if (element) {
                reportEventElement(seg.event, element);
            }
        }
    }

    function segHandlers(segs, segmentContainer, modifiedEventId) {
        var i;
        var segCnt = segs.length;
        var seg;
        var element;
        var event;
        for (i = 0; i < segCnt; i++) {
            seg = segs[i];
            if (seg.element) {
                bindDaySeg(seg.event, seg.element, seg);
            }
        }
        if ($.fn.sortable) {
            var $sortable = segmentContainer.find("ul"), oldIndex;
            $sortable.sortable({
                start: function(ev, ui) {
                    oldIndex = $(ui.item).index();
                },
                update: function(ev, ui) {
                    var $sortables = $sortable.children(),
                        i, newIndex = $(ui.item).index(),
                        evnt = $(ui.item).find('.fc-task-title').data('fcEvent');
                    if (newIndex < oldIndex) {
                        evnt.position = $sortables.eq(newIndex + 1).find('.fc-task-title').data('fcEvent').position;
                        for (i = newIndex + 1; i <= oldIndex; i++) {
                            $sortables.eq(i).find('.fc-task-title').data('fcEvent').position++;
                        }
                    } else {
                        evnt.position = $sortables.eq(newIndex - 1).find('.fc-task-title').data('fcEvent').position;
                        for (i = oldIndex; i < newIndex; i++) {
                            $sortables.eq(i).find('.fc-task-title').data('fcEvent').position--;
                        }
                    }
                    trigger('taskSortUpdate', evnt, evnt, ev, ui);
                }
            }).disableSelection();
        }
    }

    function triggerEventAfterRender(segs) {
        var i;
        var segCnt = segs.length;
        var seg;
        var element;
        for (i=0; i<segCnt; i++) {
            seg = segs[i];
            element = seg.element;
            if (element) {
                trigger('eventAfterRender', seg.event, seg.event, element);
            }
        }
    }

}<commit_msg>Added eventAfterAllRender trigger to Tasks<commit_after>function TasksListEventRenderer() {
    var t = this;

    // exports
    t.renderEvents = renderEvents;
    t.clearEvents = clearEvents;
    t.bindDaySeg = bindDaySeg;
    t.getSelectedTasks = getSelectedTasks;
    t.applyToSelectedTasks = applyToSelectedTasks;
    t.unselectAllTasks = unselectAllTasks;

    // imports
    TasksListViewEventRenderer.call(t);
    var opt = t.opt;
    var trigger = t.trigger;
    var reportEvents = t.reportEvents;
    var reportEventClear = t.reportEventClear;
    var eventElementHandlers = t.eventElementHandlers;
    var getSegmentContainer = t.getSegmentContainer;
    var getActionContainer = t.getActionContainer;
    var renderTaskSegs = t.renderTaskSegs;

    // locals
    var selectedTasks = [];

    function renderEvents(events, modifiedEventId) {
        reportEvents(events);
        renderTaskSegs(compileSegs(events), selectedTasks, modifiedEventId);

        trigger('eventAfterAllRender');
    }

    function clearEvents() {
        reportEventClear();
        getSegmentContainer().empty();
    }

    function compileSegs(events) {
        var cnt = events.length,
            event, i,
            segs = [],
            optCnt = opt('tasksListLimit');
        if (optCnt > 0 && cnt > optCnt) {
            cnt = optCnt;
        }
        for (i = 0; i < cnt; i++) {
            event = events[i];
            if (event.type === 'task'
                && (opt('tasksShowDone') || !event.done)
                && (opt('tasksShowCanceled') || !event.canceled)
            ) {
                segs.push({ event: event });
            }
        }
        segs.sort(function (a, b) {
            return a.event.position - b.event.position;
        });
        return segs;
    }

    function bindDaySeg(event, eventElement, seg) {
        var $li = eventElement.closest('li');
        $li.find('.fc-task-checkbox input[type="checkbox"]').on('change', function(ev) {
            if ($(this).is(':checked')) {
                $li.addClass('fc-state-highlight');
                selectedTasks.push(event);
                getActionContainer().addClass('fc-tasks-select-actions-active');
                trigger('taskSelect', event, event, ev);
            } else {
                $li.removeClass('fc-state-highlight');
                var index = selectedTasks.indexOf(event);
                selectedTasks.splice(index, 1);
                if (!selectedTasks.length) {
                    getActionContainer().removeClass('fc-tasks-select-actions-active');
                }
                trigger('taskUnselect', event, event, ev);
            }
        });
        eventElementHandlers(event, eventElement);
    }

    function getSelectedTasks() {
        return selectedTasks;
    }

    function applyToSelectedTasks(func) {
        var i, cnt = selectedTasks.length;
        for (i = 0; i < cnt; i++) {
            func(selectedTasks[i]);
        }
    }

    function unselectAllTasks() {
        getSegmentContainer().find('.fc-task-checkbox input[type="checkbox"]:checked').trigger('click');
        selectedTasks = [];
        getActionContainer().removeClass('fc-tasks-select-actions-active');
    }
}

function TasksListViewEventRenderer() {
    var t = this;

    // exports
    t.renderTaskSegs = renderTaskSegs;

    // imports
    var opt = t.opt;
    var trigger = t.trigger;
    var reportEventElement = t.reportEventElement;
    var getSegmentContainer = t.getSegmentContainer;
    var bindDaySeg = t.bindDaySeg;
    var formatDates = t.calendar.formatDates;
    var getActionContainer = t.getActionContainer;

    function renderTaskSegs(segs, selectedTasks, modifiedEventId) {
        var segmentContainer = getSegmentContainer();

        segmentContainer[0].innerHTML = tasksSegHTML(segs, selectedTasks); // faster than .html()
        segElementResolve(segs, segmentContainer.find('.fc-task-title'));
        segElementReport(segs);
        segHandlers(segs, segmentContainer, modifiedEventId);

        triggerEventAfterRender(segs);
    }

    function tasksSegHTML(segs, selectedTasks) {
        var i, j;
        var seg, segCnt = segs.length;
        var newSelectedTasks = [];
        var event;
        var classes, taskClasses, timeClasses;

        var html = '
';

        for (i = 0; i < segCnt; i++) {
            seg = segs[i];

            event = seg.event;
            classes = [];
            taskClasses = ['fc-task', 'fc-event-skin'];
            timeClasses = ['fc-task-date'];

            var selected = (selectedTasks.indexOf(event) >= 0);
            if (!selected) {
                $.each(selectedTasks, function(index, ev) {
                    if (ev.id && ev.id == event.id) {
                        selected = true;
                    }
                });
            }
            if (selected) {
                newSelectedTasks.push(event);
                classes.push('fc-state-highlight')
            }
            taskClasses = taskClasses.concat(event.className);
            if (event.source) {
                taskClasses = taskClasses.concat(event.source.className || []);
            }
            if (event.canceled) {
                taskClasses.push('fc-task-canceled');
                timeClasses.push('fc-task-canceled');
            } else if (event.done) {
                taskClasses.push('fc-task-done');
                timeClasses.push('fc-task-done');
            }

            var today = clearTime(new Date());
            var eventDay = cloneDate(event.start, true);
            if (+today == +eventDay) {
                timeClasses.push('fc-state-highlight fc-today');
            }

            html += '
';
            if (event.indent) {
                for (j = 0; j < event.indent; j++) {
                    html += '';
                }
            }
            html += '<input type="checkbox"';
            if (selected) {
                html += ' checked="checked"';
            }
            html += '/>';

            var url = event.url;
            var skinCss = getSkinCss(event, opt);
            var skinCssAttr = (skinCss ? ' style="' + skinCss + '"' : "");

            html += ' ';
            if (event.canceled) {
                html += '' + opt("tasksCanceled") + ' ';
            }
            html +=     (url ? ('<a href="' + htmlEscape(url) + '"') : '' +
                        htmlEscape(event.title) +
                        '</' + (url ? 'a' : 'span' ) + '>' +
                    '';
            html += '' + htmlEscape(formatDates(event.start, event.end, opt('dateFormat')));
                html += ' ' + htmlEscape(formatDates(event.start, event.end, opt("timeFormat"))) + '';
            }
            else {
                html += '>';
            }
            html += '';
            html += '
';
        }

        html += '
';

        selectedTasks.length = 0;
        selectedTasks.push.apply(selectedTasks, newSelectedTasks);
        if (!selectedTasks.length) {
            getActionContainer().removeClass('fc-tasks-select-actions-active');
        }

        return html;
    }

    function segElementResolve(segs, elements) {
        var i;
        var segCnt = segs.length;
        var seg;
        var event;
        var element;
        var triggerRes;
        for (i = 0; i < segCnt; i++) {
            seg = segs[i];
            event = seg.event;
            element = $(elements[i]);
            triggerRes = trigger('eventRender', event, event, element);
            if (triggerRes === false) {
                element.remove();
            } else {
                if (triggerRes && triggerRes !== true) {
                    triggerRes = $(triggerRes)
                        .css({
                            position: 'absolute',
                            left: seg.left
                        });
                    element.replaceWith(triggerRes);
                    element = triggerRes;
                }
                seg.element = element;
                element.data('fcEvent', seg.event);
            }
        }
    }

    function segElementReport(segs) {
        var i;
        var segCnt = segs.length;
        var seg;
        var element;
        for (i = 0; i < segCnt; i++) {
            seg = segs[i];
            element = seg.element;
            if (element) {
                reportEventElement(seg.event, element);
            }
        }
    }

    function segHandlers(segs, segmentContainer, modifiedEventId) {
        var i;
        var segCnt = segs.length;
        var seg;
        var element;
        var event;
        for (i = 0; i < segCnt; i++) {
            seg = segs[i];
            if (seg.element) {
                bindDaySeg(seg.event, seg.element, seg);
            }
        }
        if ($.fn.sortable) {
            var $sortable = segmentContainer.find("ul"), oldIndex;
            $sortable.sortable({
                start: function(ev, ui) {
                    oldIndex = $(ui.item).index();
                },
                update: function(ev, ui) {
                    var $sortables = $sortable.children(),
                        i, newIndex = $(ui.item).index(),
                        evnt = $(ui.item).find('.fc-task-title').data('fcEvent');
                    if (newIndex < oldIndex) {
                        evnt.position = $sortables.eq(newIndex + 1).find('.fc-task-title').data('fcEvent').position;
                        for (i = newIndex + 1; i <= oldIndex; i++) {
                            $sortables.eq(i).find('.fc-task-title').data('fcEvent').position++;
                        }
                    } else {
                        evnt.position = $sortables.eq(newIndex - 1).find('.fc-task-title').data('fcEvent').position;
                        for (i = oldIndex; i < newIndex; i++) {
                            $sortables.eq(i).find('.fc-task-title').data('fcEvent').position--;
                        }
                    }
                    trigger('taskSortUpdate', evnt, evnt, ev, ui);
                }
            }).disableSelection();
        }
    }

    function triggerEventAfterRender(segs) {
        var i;
        var segCnt = segs.length;
        var seg;
        var element;
        for (i=0; i<segCnt; i++) {
            seg = segs[i];
            element = seg.element;
            if (element) {
                trigger('eventAfterRender', seg.event, seg.event, element);
            }
        }
    }

}<|endoftext|>


<commit_before>'use babel';

export default class ToolBarSpacerView {
  constructor (options) {
    this.element = document.createElement('hr');
    this.priority = options && options.priority;
    const classNames = 'tool-bar-spacer';
    if (this.priority < 0) {
      classNames.push('tool-bar-item-align-end');
    }
    this.element.classList.add(...classNames);
  }

  destroy () {
    if (this.element.parentNode) {
      this.element.parentNode.removeChild(this.element);
    }
    this.element = null;
  }
}
<commit_msg>fix spacer view classnames<commit_after>'use babel';

export default class ToolBarSpacerView {
  constructor (options) {
    this.element = document.createElement('hr');
    this.priority = options && options.priority;
    const classNames = ['tool-bar-spacer'];
    if (this.priority < 0) {
      classNames.push('tool-bar-item-align-end');
    }
    this.element.classList.add(...classNames);
  }

  destroy () {
    if (this.element.parentNode) {
      this.element.parentNode.removeChild(this.element);
    }
    this.element = null;
  }
}
<|endoftext|>


<commit_before>$(document).ready(function(){

   // loading expand paragraphs
   if ( $('.expandmore').length  &&  $( ".to_expand" ).length ) { // if there are at least one :)
      $('.expandmore' ).each( function(index_to_expand) {
          var $this = $(this) ,
              index_lisible = index_to_expand+1,
              $to_expand = $this.next(".to_expand"),
              $expandmore_text = $this.html();

          $this.html( '<button class="expandmore__button">' + $expandmore_text + '</button>' );
          $button = $this.children('.expandmore__button');

          $button.attr({
                  'id' : 'label_expand_' + index_lisible,
                  'aria-controls': 'expand_' + index_lisible,
                  'aria-expanded': 'false'
                });
          $to_expand.attr({
                  'id' : 'expand_' + index_lisible,
                  'aria-hidden': 'true',
                  'aria-labelledby': 'label_expand_' + index_lisible
                });
          // quick tip to open
          if ($to_expand.hasClass('is-opened') ){
             $button.addClass('is-opened').attr('aria-expanded', 'true');
             $to_expand.removeClass('is-opened').removeAttr('aria-hidden');
          }

      });

      $( 'body' ).on( 'click', '.expandmore__button', function( event ) {
         var $this = $(this),
             $destination = $( '#' + $this.attr('aria-controls') );

         if ($this.attr('aria-expanded') === 'false') {
             $this.addClass('is-opened').attr('aria-expanded', 'true');
             $destination.removeAttr('aria-hidden');
         }
         else {
              $this.removeClass('is-opened').attr('aria-expanded', 'false');
              $destination.attr('aria-hidden', 'true');
              }

         event.preventDefault();

      })
      .on( "keydown", ".expandmore__button", function( event ) {
         if ( event.keyCode == 13 || event.keyCode == 32 ) { // enter or space
             var $this = $(this),
                 $destination = $( '#' + $this.attr('aria-controls') );

             if ($this.attr('aria-expanded') === 'false') {
                 $this.addClass('is-opened').attr('aria-expanded', 'true');
                 $destination.removeAttr('aria-hidden');
             }
             else {
                  $this.removeClass('is-opened').attr('aria-expanded', 'false');
                  $destination.attr('aria-hidden', 'true');
                  }

             event.preventDefault();
         }
      } );

   }

});
<commit_msg>Fix keyboard issue + update structure<commit_after>$(document).ready(function(){

   // loading expand paragraphs
   if ( $('.expandmore').length  &&  $( ".to_expand" ).length ) { // if there are at least one :)
      $('.expandmore' ).each( function(index_to_expand) {
          var $this = $(this) ,
              index_lisible = index_to_expand+1,
              $to_expand = $this.next(".to_expand"),
              $expandmore_text = $this.html();

          $this.html( '<button class="expandmore__button">' + $expandmore_text + '</button>' );
          $button = $this.children('.expandmore__button');

          $button.attr({
                  'id' : 'label_expand_' + index_lisible,
                  'aria-controls': 'expand_' + index_lisible,
                  'aria-expanded': 'false'
                });
          $to_expand.attr({
                  'id' : 'expand_' + index_lisible,
                  'aria-hidden': 'true',
                  'aria-labelledby': 'label_expand_' + index_lisible
                });
          // quick tip to open
          if ($to_expand.hasClass('is-opened') ){
             $button.addClass('is-opened').attr('aria-expanded', 'true');
             $to_expand.removeClass('is-opened').removeAttr('aria-hidden');
          }

      });

      $( 'body' ).on( 'click', '.expandmore__button', function( event ) {
         var $this = $(this),
             $destination = $( '#' + $this.attr('aria-controls') );

         if ($this.attr('aria-expanded') === 'false') {
             $this.addClass('is-opened').attr('aria-expanded', 'true');
             $destination.removeAttr('aria-hidden');
         }
         else {
              $this.removeClass('is-opened').attr('aria-expanded', 'false');
              $destination.attr('aria-hidden', 'true');
              }

         event.preventDefault();

      })
      .on( "keydown", ".expandmore__button", function( event ) {
         if ( event.keyCode == 13 ) { // space comes from button tag
             var $this = $(this),
                 $destination = $( '#' + $this.attr('aria-controls') );

             if ($this.attr('aria-expanded') === 'false') {
                 $this.addClass('is-opened').attr('aria-expanded', 'true');
                 $destination.removeAttr('aria-hidden');
             }
             else {
                  $this.removeClass('is-opened').attr('aria-expanded', 'false');
                  $destination.attr('aria-hidden', 'true');
                  }

             event.preventDefault();
         }
      } );

   }

});
<|endoftext|>


<commit_before>var m = require('mithril');
var makePool = require('./cevalPool');
var dict = require('./cevalDict');
var util = require('../util');
var stockfishProtocol = require('./stockfishProtocol');
var sunsetterProtocol = require('./sunsetterProtocol');

module.exports = function(possible, variant, emit) {

  var instanceId = Math.random().toString(36).substring(2).slice(0, 4);
  var nbWorkers = 3;
  var minDepth = 7;
  var maxDepth = util.storedProp('ceval.max-depth', 18);
  var curDepth = 0;
  var storageKey = 'client-eval-enabled';
  var allowed = m.prop(true);
  var enabled = m.prop(possible() && allowed() && lichess.storage.get(storageKey) === '1');
  var started = false;

  var pool;
  if (variant.key !== 'crazyhouse') {
    pool = makePool(stockfishProtocol, {
      asmjs: '/assets/vendor/stockfish.js/stockfish.js',
      pnacl: '/assets/vendor/stockfish.pexe/nacl/stockfish.nmf'
    }, {
      minDepth: minDepth,
      maxDepth: maxDepth,
      variant: variant,
    });
  } else {
    pool = makePool(sunsetterProtocol, {
      asmjs: '/assets/vendor/Sunsetter8/sunsetter.js'
    });
  }

  // adjusts maxDepth based on nodes per second
  var npsRecorder = (function() {
    var values = [];
    var applies = function(res) {
      return res.eval.nps && res.eval.depth >= 16 &&
        !res.eval.mate && Math.abs(res.eval.cp) < 500 &&
        (res.work.currentFen.split(/\s/)[0].split(/[nbrqkp]/i).length - 1) >= 10;
    }
    return function(res) {
      if (!applies(res)) return;
      values.push(res.eval.nps);
      if (values.length >= 5) {
        var depth = 18,
          knps = util.median(values) / 1000;
        if (knps > 100) depth = 19;
        if (knps > 150) depth = 20;
        if (knps > 200) depth = 21;
        if (knps > 250) depth = 22;
        maxDepth(depth);
        if (values.length > 20) values.shift();
      }
    };
  })();

  var onEmit = function(res) {
    res.instanceId = instanceId;
    res.eval.maxDepth = res.work.maxDepth;
    npsRecorder(res);
    curDepth = res.eval.depth;
    emit(res);
  }

  var start = function(path, steps, threatMode) {
    if (!enabled() || !possible()) return;
    var step = steps[steps.length - 1];

    var existing = step[threatMode ? 'threat' : 'ceval'];
    if (existing && existing.depth >= maxDepth()) return;

    var work = {
      initialFen: steps[0].fen,
      moves: [],
      currentFen: step.fen,
      path: path,
      ply: step.ply,
      maxDepth: maxDepth(),
      threatMode: threatMode,
      emit: function(res) {
        if (enabled()) onEmit(res);
      }
    };

    if (threatMode) {
      var c = step.ply % 2 === 1 ? 'w' : 'b';
      var fen = step.fen.replace(/ (w|b) /, ' ' + c + ' ');
      work.currentFen = fen;
      work.initialFen = fen;
    } else {
      // send fen after latest castling move and the following moves
      for (var i = 1; i < steps.length; i++) {
        var step = steps[i];
        if (step.san.indexOf('O-O') === 0) {
          work.moves = [];
          work.initialFen = step.fen;
        } else work.moves.push(step.uci);
      }
    }

    var dictRes = dict(work, variant);
    if (dictRes) {
      setTimeout(function() {
        // this has to be delayed, or it slows down analysis first render.
        work.emit({
          work: work,
          eval: {
            depth: maxDepth(),
            cp: dictRes.cp,
            best: dictRes.best,
            mate: 0,
            dict: true
          }
        });
      }, 500);
      pool.warmup();
    } else pool.start(work);

    started = true;
  };

  var stop = function() {
    if (!enabled() || !started) return;
    pool.stop();
    started = false;
  };

  return {
    instanceId: instanceId,
    start: start,
    stop: stop,
    allowed: allowed,
    possible: possible,
    enabled: enabled,
    toggle: function() {
      if (!possible() || !allowed()) return;
      stop();
      enabled(!enabled());
      lichess.storage.set(storageKey, enabled() ? '1' : '0');
    },
    curDepth: function() {
      return curDepth;
    },
    maxDepth: maxDepth
  };
};
<commit_msg>push local eval up to 24 plies if enough power<commit_after>var m = require('mithril');
var makePool = require('./cevalPool');
var dict = require('./cevalDict');
var util = require('../util');
var stockfishProtocol = require('./stockfishProtocol');
var sunsetterProtocol = require('./sunsetterProtocol');

module.exports = function(possible, variant, emit) {

  var instanceId = Math.random().toString(36).substring(2).slice(0, 4);
  var nbWorkers = 3;
  var minDepth = 7;
  var maxDepth = util.storedProp('ceval.max-depth', 18);
  var curDepth = 0;
  var storageKey = 'client-eval-enabled';
  var allowed = m.prop(true);
  var enabled = m.prop(possible() && allowed() && lichess.storage.get(storageKey) === '1');
  var started = false;

  var pool;
  if (variant.key !== 'crazyhouse') {
    pool = makePool(stockfishProtocol, {
      asmjs: '/assets/vendor/stockfish.js/stockfish.js',
      pnacl: '/assets/vendor/stockfish.pexe/nacl/stockfish.nmf'
    }, {
      minDepth: minDepth,
      maxDepth: maxDepth,
      variant: variant,
    });
  } else {
    pool = makePool(sunsetterProtocol, {
      asmjs: '/assets/vendor/Sunsetter8/sunsetter.js'
    });
  }

  // adjusts maxDepth based on nodes per second
  var npsRecorder = (function() {
    var values = [];
    var applies = function(res) {
      return res.eval.nps && res.eval.depth >= 16 &&
        !res.eval.mate && Math.abs(res.eval.cp) < 500 &&
        (res.work.currentFen.split(/\s/)[0].split(/[nbrqkp]/i).length - 1) >= 10;
    }
    return function(res) {
      if (!applies(res)) return;
      values.push(res.eval.nps);
      if (values.length >= 5) {
        var depth = 18,
          knps = util.median(values) / 1000;
        if (knps > 100) depth = 19;
        if (knps > 150) depth = 20;
        if (knps > 200) depth = 21;
        if (knps > 250) depth = 22;
        if (knps > 500) depth = 23;
        if (knps > 800) depth = 24;
        maxDepth(depth);
        if (values.length > 20) values.shift();
      }
    };
  })();

  var onEmit = function(res) {
    res.instanceId = instanceId;
    res.eval.maxDepth = res.work.maxDepth;
    npsRecorder(res);
    curDepth = res.eval.depth;
    emit(res);
  }

  var start = function(path, steps, threatMode) {
    if (!enabled() || !possible()) return;
    var step = steps[steps.length - 1];

    var existing = step[threatMode ? 'threat' : 'ceval'];
    if (existing && existing.depth >= maxDepth()) return;

    var work = {
      initialFen: steps[0].fen,
      moves: [],
      currentFen: step.fen,
      path: path,
      ply: step.ply,
      maxDepth: maxDepth(),
      threatMode: threatMode,
      emit: function(res) {
        if (enabled()) onEmit(res);
      }
    };

    if (threatMode) {
      var c = step.ply % 2 === 1 ? 'w' : 'b';
      var fen = step.fen.replace(/ (w|b) /, ' ' + c + ' ');
      work.currentFen = fen;
      work.initialFen = fen;
    } else {
      // send fen after latest castling move and the following moves
      for (var i = 1; i < steps.length; i++) {
        var step = steps[i];
        if (step.san.indexOf('O-O') === 0) {
          work.moves = [];
          work.initialFen = step.fen;
        } else work.moves.push(step.uci);
      }
    }

    var dictRes = dict(work, variant);
    if (dictRes) {
      setTimeout(function() {
        // this has to be delayed, or it slows down analysis first render.
        work.emit({
          work: work,
          eval: {
            depth: maxDepth(),
            cp: dictRes.cp,
            best: dictRes.best,
            mate: 0,
            dict: true
          }
        });
      }, 500);
      pool.warmup();
    } else pool.start(work);

    started = true;
  };

  var stop = function() {
    if (!enabled() || !started) return;
    pool.stop();
    started = false;
  };

  return {
    instanceId: instanceId,
    start: start,
    stop: stop,
    allowed: allowed,
    possible: possible,
    enabled: enabled,
    toggle: function() {
      if (!possible() || !allowed()) return;
      stop();
      enabled(!enabled());
      lichess.storage.set(storageKey, enabled() ? '1' : '0');
    },
    curDepth: function() {
      return curDepth;
    },
    maxDepth: maxDepth
  };
};
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withRouter } from 'react-router';
import { Route, Switch } from 'react-router-dom';
import { connect } from 'react-redux';
import { ViewerbaseDragDropContext } from 'react-viewerbase';
import ViewerRouting from "./ViewerRouting.js";
import StudyListRouting from './StudyListRouting.js';
import StandaloneRouting from './StandaloneRouting.js';
import IHEInvokeImageDisplay from './IHEInvokeImageDisplay.js';
import CallbackPage from './CallbackPage.js';
import userManager from "./userManager.js";
import './App.css';
import './variables.css';
import './theme-tide.css';

const reload = () => window.location.reload();

function setContext(context) {
    /*Rollbar.configure({
        payload: {
            context
        }
    });*/
}

function LoadingUser() {
    return (
Loading user...
);
}

class App extends Component {
    static propTypes = {
        history: PropTypes.object.isRequired,
        user: PropTypes.object
    }

    componentDidMount() {
        this.unlisten = this.props.history.listen((location, action) => {
            setContext(window.location.pathname);
        });
    }

    componentWillUnmount() {
        this.unlisten();
    }

    render() {
        const user = this.props.user;

        if (!user || user.expired) {
            // TODO: redirect to OAuth page if necessary
            return <Switch>
                <Route path="/callback" component={CallbackPage} />
                <Route exact path='/login' component={() => {
                    userManager.signinRedirect();
                }}/>
                <Route component={LoadingUser}/>
            </Switch>;
        }

        return (
            <Switch>
                <Route
                    exact
                    path="/studylist"
                    component={StudyListRouting}
                />
                <Route
                    exact
                    path="/"
                    component={StudyListRouting}
                />
                <Route
                    exact
                    path="/viewer"
                    component={StandaloneRouting}
                />
                <Route
                    path="/viewer/:studyInstanceUids"
                    component={ViewerRouting}
                />
                <Route
                    path="/study/:studyInstanceUid/series/:seriesInstanceUids"
                    component={ViewerRouting}
                />
                <Route
                    path="/IHEInvokeImageDisplay"
                    component={IHEInvokeImageDisplay}
                />

                <Route path="/silent-refresh.html" onEnter={reload} />
                <Route path="/logout-redirect.html" onEnter={reload} />
                <Route exact path='/login' component={() => {
                        userManager.signinRedirect();
                }}
                />
                <Route path="/callback" component={CallbackPage} />
                <Route render={() =>

 Sorry, this page does not exist.
}
                />
            </Switch>
        );
    }
}

const mapStateToProps = state => {
    return {
        user: state.oidc.user,
    };
};

const ConnectedApp = connect(
    mapStateToProps,
    null
)(App);

export default ViewerbaseDragDropContext(withRouter(ConnectedApp));
<commit_msg>Disable the user requirement for demo purposes<commit_after>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withRouter } from 'react-router';
import { Route, Switch } from 'react-router-dom';
import { connect } from 'react-redux';
import { ViewerbaseDragDropContext } from 'react-viewerbase';
import ViewerRouting from "./ViewerRouting.js";
import StudyListRouting from './StudyListRouting.js';
import StandaloneRouting from './StandaloneRouting.js';
import IHEInvokeImageDisplay from './IHEInvokeImageDisplay.js';
import CallbackPage from './CallbackPage.js';
import userManager from "./userManager.js";
import './App.css';
import './variables.css';
import './theme-tide.css';

const reload = () => window.location.reload();

function setContext(context) {
    /*Rollbar.configure({
        payload: {
            context
        }
    });*/
}

function LoadingUser() {
    return (
Loading user...
);
}

class App extends Component {
    static propTypes = {
        history: PropTypes.object.isRequired,
        user: PropTypes.object
    }

    componentDidMount() {
        this.unlisten = this.props.history.listen((location, action) => {
            setContext(window.location.pathname);
        });
    }

    componentWillUnmount() {
        this.unlisten();
    }

    render() {
        const user = this.props.user;

        /*if (!user || user.expired) {
            // TODO: redirect to OAuth page if necessary
            return <Switch>
                <Route path="/callback" component={CallbackPage} />
                <Route exact path='/login' component={() => {
                    userManager.signinRedirect();
                }}/>
                <Route component={LoadingUser}/>
            </Switch>;
        }*/

        return (
            <Switch>
                <Route
                    exact
                    path="/studylist"
                    component={StudyListRouting}
                />
                <Route
                    exact
                    path="/"
                    component={StudyListRouting}
                />
                <Route
                    exact
                    path="/viewer"
                    component={StandaloneRouting}
                />
                <Route
                    path="/viewer/:studyInstanceUids"
                    component={ViewerRouting}
                />
                <Route
                    path="/study/:studyInstanceUid/series/:seriesInstanceUids"
                    component={ViewerRouting}
                />
                <Route
                    path="/IHEInvokeImageDisplay"
                    component={IHEInvokeImageDisplay}
                />

                <Route path="/silent-refresh.html" onEnter={reload} />
                <Route path="/logout-redirect.html" onEnter={reload} />
                <Route exact path='/login' component={() => {
                        userManager.signinRedirect();
                }}
                />
                <Route path="/callback" component={CallbackPage} />
                <Route render={() =>

 Sorry, this page does not exist.
}
                />
            </Switch>
        );
    }
}

const mapStateToProps = state => {
    return {
        user: state.oidc.user,
    };
};

const ConnectedApp = connect(
    mapStateToProps,
    null
)(App);

export default ViewerbaseDragDropContext(withRouter(ConnectedApp));
<|endoftext|>


<commit_before>import { createBrowserHistory, createHashHistory } from 'history'

export default function configureHistory() {
  return window.matchMedia('(display-mode: standalone)').matches
    ? createHashHistory()
    : createBrowserHistory()
}
<commit_msg>Add home screen issue note<commit_after>import { createBrowserHistory, createHashHistory } from 'history'

/**
 * When app is launched from home screen on Android - we need to use hash history.
 * @see https://ayastreb.me/react-router-in-home-screen-pwa/
 */
export default function configureHistory() {
  return window.matchMedia('(display-mode: standalone)').matches
    ? createHashHistory()
    : createBrowserHistory()
}
<|endoftext|>