<commit_before>/*
Copyright 2017 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.
*/

import EventEmitter from 'events';
import { groupMemberFromApiObject, groupRoomFromApiObject } from '../groups';
import FlairStore from './FlairStore';
import MatrixClientPeg from '../MatrixClientPeg';

function parseMembersResponse(response) {
    return response.chunk.map((apiMember) => groupMemberFromApiObject(apiMember));
}

function parseRoomsResponse(response) {
    return response.chunk.map((apiRoom) => groupRoomFromApiObject(apiRoom));
}

// The number of ongoing group requests
let ongoingRequestCount = 0;

// This has arbitrarily been set to a small number to lower the priority
// of doing group-related requests because we care about other important
// requests like hitting /sync.
const LIMIT = 3; // Maximum number of ongoing group requests

// FIFO queue of functions to call in the backlog
const backlogQueue = [
    // () => {...}
];

// Pull from the FIFO queue
function checkBacklog() {
    const item = backlogQueue.shift();
    if (typeof item === 'function') item();
}

// Limit the maximum number of ongoing promises returned by fn to LIMIT and
// use a FIFO queue to handle the backlog.
function limitConcurrency(fn) {
    return new Promise((resolve, reject) => {
        const item = () => {
            ongoingRequestCount++;
            resolve();
        };
        if (ongoingRequestCount >= LIMIT) {
            // Enqueue this request for later execution
            backlogQueue.push(item);
        } else {
            item();
        }
    })
    .then(fn)
    .catch((err) => {
        ongoingRequestCount--;
        checkBacklog();
        throw err;
    })
    .then((result) => {
        ongoingRequestCount--;
        checkBacklog();
        return result;
    });
}

/**
 * Global store for tracking group summary, members, invited members and rooms.
 */
class GroupStore extends EventEmitter {
    STATE_KEY = {
        GroupMembers: 'GroupMembers',
        GroupInvitedMembers: 'GroupInvitedMembers',
        Summary: 'Summary',
        GroupRooms: 'GroupRooms',
    };

    constructor() {
        super();
        this._state = {};
        this._state[this.STATE_KEY.Summary] = {};
        this._state[this.STATE_KEY.GroupRooms] = {};
        this._state[this.STATE_KEY.GroupMembers] = {};
        this._state[this.STATE_KEY.GroupInvitedMembers] = {};

        this._ready = {};
        this._ready[this.STATE_KEY.Summary] = {};
        this._ready[this.STATE_KEY.GroupRooms] = {};
        this._ready[this.STATE_KEY.GroupMembers] = {};
        this._ready[this.STATE_KEY.GroupInvitedMembers] = {};

        this._fetchResourcePromise = {
            [this.STATE_KEY.Summary]: {},
            [this.STATE_KEY.GroupRooms]: {},
            [this.STATE_KEY.GroupMembers]: {},
            [this.STATE_KEY.GroupInvitedMembers]: {},
        };

        this._resourceFetcher = {
            [this.STATE_KEY.Summary]: (groupId) => {
                return limitConcurrency(
                    () => MatrixClientPeg.get().getGroupSummary(groupId),
                );
            },
            [this.STATE_KEY.GroupRooms]: (groupId) => {
                return limitConcurrency(
                    () => MatrixClientPeg.get().getGroupRooms(groupId).then(parseRoomsResponse),
                );
            },
            [this.STATE_KEY.GroupMembers]: (groupId) => {
                return limitConcurrency(
                    () => MatrixClientPeg.get().getGroupUsers(groupId).then(parseMembersResponse),
                );
            },
            [this.STATE_KEY.GroupInvitedMembers]: (groupId) => {
                return limitConcurrency(
                    () => MatrixClientPeg.get().getGroupInvitedUsers(groupId).then(parseMembersResponse),
                );
            },
        };

        this.on('error', (err, groupId) => {
            console.error(`GroupStore encountered error whilst fetching data for ${groupId}`, err);
        });
    }

    _fetchResource(stateKey, groupId) {
        // Ongoing request, ignore
        if (this._fetchResourcePromise[stateKey][groupId]) return;

        const clientPromise = this._resourceFetcher[stateKey](groupId);

        // Indicate ongoing request
        this._fetchResourcePromise[stateKey][groupId] = clientPromise;

        clientPromise.then((result) => {
            this._state[stateKey][groupId] = result;
            console.info(this._state);
            this._ready[stateKey][groupId] = true;
            this._notifyListeners();
        }).catch((err) => {
            // Invited users not visible to non-members
            if (stateKey === this.STATE_KEY.GroupInvitedMembers && err.httpStatus === 403) {
                return;
            }

            console.error(`Failed to get resource ${stateKey} for ${groupId}`, err);
            this.emit('error', err, groupId);
        }).finally(() => {
            // Indicate finished request, allow for future fetches
            delete this._fetchResourcePromise[stateKey][groupId];
        });

        return clientPromise;
    }

    _notifyListeners() {
        this.emit('update');
    }

    /**
     * Register a listener to recieve updates from the store. This also
     * immediately triggers an update to send the current state of the
     * store (which could be the initial state).
     *
     * This also causes a fetch of all data of the specified group,
     * which might cause 4 separate HTTP requests, but only if said
     * requests aren't already ongoing.
     *
     * @param {string} groupId the ID of the group to fetch data for.
     * @param {function} fn the function to call when the store updates.
     * @return {Object} tok a registration "token" with a single
     *                      property `unregister`, a function that can
     *                      be called to unregister the listener such
     *                      that it won't be called any more.
     */
    registerListener(groupId, fn) {
        this.on('update', fn);
        // Call to set initial state (before fetching starts)
        this.emit('update');

        this._fetchResource(this.STATE_KEY.Summary, groupId);
        this._fetchResource(this.STATE_KEY.GroupRooms, groupId);
        this._fetchResource(this.STATE_KEY.GroupMembers, groupId);
        this._fetchResource(this.STATE_KEY.GroupInvitedMembers, groupId);

        // Similar to the Store of flux/utils, we return a "token" that
        // can be used to unregister the listener.
        return {
            unregister: () => {
                this.unregisterListener(fn);
            },
        };
    }

    unregisterListener(fn) {
        this.removeListener('update', fn);
    }

    isStateReady(groupId, id) {
        return this._ready[id][groupId];
    }

    getSummary(groupId) {
        return this._state[this.STATE_KEY.Summary][groupId] || {};
    }

    getGroupRooms(groupId) {
        return this._state[this.STATE_KEY.GroupRooms][groupId] || [];
    }

    getGroupMembers(groupId) {
        return this._state[this.STATE_KEY.GroupMembers][groupId] || [];
    }

    getGroupInvitedMembers(groupId) {
        return this._state[this.STATE_KEY.GroupInvitedMembers][groupId] || [];
    }

    getGroupPublicity(groupId) {
        return (this._state[this.STATE_KEY.Summary][groupId] || {}).user ?
            (this._state[this.STATE_KEY.Summary][groupId] || {}).user.is_publicised : null;
    }

    isUserPrivileged(groupId) {
        return (this._state[this.STATE_KEY.Summary][groupId] || {}).user ?
            (this._state[this.STATE_KEY.Summary][groupId] || {}).user.is_privileged : null;
    }

    addRoomToGroup(groupId, roomId, isPublic) {
        return MatrixClientPeg.get()
            .addRoomToGroup(groupId, roomId, isPublic)
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupRooms, groupId));
    }

    updateGroupRoomVisibility(groupId, roomId, isPublic) {
        return MatrixClientPeg.get()
            .updateGroupRoomVisibility(groupId, roomId, isPublic)
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupRooms, groupId));
    }

    removeRoomFromGroup(groupId, roomId) {
        return MatrixClientPeg.get()
            .removeRoomFromGroup(groupId, roomId)
            // Room might be in the summary, refresh just in case
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId))
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupRooms, groupId));
    }

    inviteUserToGroup(groupId, userId) {
        return MatrixClientPeg.get().inviteUserToGroup(groupId, userId)
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupInvitedMembers, groupId));
    }

    acceptGroupInvite(groupId) {
        return MatrixClientPeg.get().acceptGroupInvite(groupId)
            // The user should now be able to access (personal) group settings
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId))
            // The user might be able to see more rooms now
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupRooms, groupId))
            // The user should now appear as a member
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupMembers, groupId))
            // The user should now not appear as an invited member
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupInvitedMembers, groupId));
    }

    joinGroup(groupId) {
        return MatrixClientPeg.get().joinGroup(groupId)
            // The user should now be able to access (personal) group settings
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId))
            // The user might be able to see more rooms now
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupRooms, groupId))
            // The user should now appear as a member
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupMembers, groupId))
            // The user should now not appear as an invited member
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupInvitedMembers, groupId));
    }

    leaveGroup(groupId) {
        return MatrixClientPeg.get().leaveGroup(groupId)
            // The user should now not be able to access group settings
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId))
            // The user might only be able to see a subset of rooms now
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupRooms, groupId))
            // The user should now not appear as a member
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupMembers, groupId));
    }

    addRoomToGroupSummary(groupId, roomId, categoryId) {
        return MatrixClientPeg.get()
            .addRoomToGroupSummary(groupId, roomId, categoryId)
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId));
    }

    addUserToGroupSummary(groupId, userId, roleId) {
        return MatrixClientPeg.get()
            .addUserToGroupSummary(groupId, userId, roleId)
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId));
    }

    removeRoomFromGroupSummary(groupId, roomId) {
        return MatrixClientPeg.get()
            .removeRoomFromGroupSummary(groupId, roomId)
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId));
    }

    removeUserFromGroupSummary(groupId, userId) {
        return MatrixClientPeg.get()
            .removeUserFromGroupSummary(groupId, userId)
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId));
    }

    setGroupPublicity(groupId, isPublished) {
        return MatrixClientPeg.get()
            .setGroupPublicity(groupId, isPublished)
            .then(() => { FlairStore.invalidatePublicisedGroups(MatrixClientPeg.get().credentials.userId); })
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId));
    }
}

let singletonGroupStore = null;
if (!singletonGroupStore) {
    singletonGroupStore = new GroupStore();
}
module.exports = singletonGroupStore;
<commit_msg>Rewrite limitConcurrency to fix error catching<commit_after>/*
Copyright 2017 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.
*/

import EventEmitter from 'events';
import { groupMemberFromApiObject, groupRoomFromApiObject } from '../groups';
import FlairStore from './FlairStore';
import MatrixClientPeg from '../MatrixClientPeg';

function parseMembersResponse(response) {
    return response.chunk.map((apiMember) => groupMemberFromApiObject(apiMember));
}

function parseRoomsResponse(response) {
    return response.chunk.map((apiRoom) => groupRoomFromApiObject(apiRoom));
}

// The number of ongoing group requests
let ongoingRequestCount = 0;

// This has arbitrarily been set to a small number to lower the priority
// of doing group-related requests because we care about other important
// requests like hitting /sync.
const LIMIT = 3; // Maximum number of ongoing group requests

// FIFO queue of functions to call in the backlog
const backlogQueue = [
    // () => {...}
];

// Pull from the FIFO queue
function checkBacklog() {
    const item = backlogQueue.shift();
    if (typeof item === 'function') item();
}

// Limit the maximum number of ongoing promises returned by fn to LIMIT and
// use a FIFO queue to handle the backlog.
async function limitConcurrency(fn) {
    if (ongoingRequestCount >= LIMIT) {
        // Enqueue this request for later execution
        await new Promise((resolve, reject) => {
            backlogQueue.push(resolve);
        });
    }

    let result;
    let error;

    ongoingRequestCount++;
    try {
        result = await fn();
    } catch (err) {
        error = err;
    } finally {
        ongoingRequestCount--;
        checkBacklog();
    }

    if (error) throw error;

    return result;
}

/**
 * Global store for tracking group summary, members, invited members and rooms.
 */
class GroupStore extends EventEmitter {
    STATE_KEY = {
        GroupMembers: 'GroupMembers',
        GroupInvitedMembers: 'GroupInvitedMembers',
        Summary: 'Summary',
        GroupRooms: 'GroupRooms',
    };

    constructor() {
        super();
        this._state = {};
        this._state[this.STATE_KEY.Summary] = {};
        this._state[this.STATE_KEY.GroupRooms] = {};
        this._state[this.STATE_KEY.GroupMembers] = {};
        this._state[this.STATE_KEY.GroupInvitedMembers] = {};

        this._ready = {};
        this._ready[this.STATE_KEY.Summary] = {};
        this._ready[this.STATE_KEY.GroupRooms] = {};
        this._ready[this.STATE_KEY.GroupMembers] = {};
        this._ready[this.STATE_KEY.GroupInvitedMembers] = {};

        this._fetchResourcePromise = {
            [this.STATE_KEY.Summary]: {},
            [this.STATE_KEY.GroupRooms]: {},
            [this.STATE_KEY.GroupMembers]: {},
            [this.STATE_KEY.GroupInvitedMembers]: {},
        };

        this._resourceFetcher = {
            [this.STATE_KEY.Summary]: (groupId) => {
                return limitConcurrency(
                    () => MatrixClientPeg.get().getGroupSummary(groupId),
                );
            },
            [this.STATE_KEY.GroupRooms]: (groupId) => {
                return limitConcurrency(
                    () => MatrixClientPeg.get().getGroupRooms(groupId).then(parseRoomsResponse),
                );
            },
            [this.STATE_KEY.GroupMembers]: (groupId) => {
                return limitConcurrency(
                    () => MatrixClientPeg.get().getGroupUsers(groupId).then(parseMembersResponse),
                );
            },
            [this.STATE_KEY.GroupInvitedMembers]: (groupId) => {
                return limitConcurrency(
                    () => MatrixClientPeg.get().getGroupInvitedUsers(groupId).then(parseMembersResponse),
                );
            },
        };

        this.on('error', (err, groupId) => {
            console.error(`GroupStore encountered error whilst fetching data for ${groupId}`, err);
        });
    }

    _fetchResource(stateKey, groupId) {
        // Ongoing request, ignore
        if (this._fetchResourcePromise[stateKey][groupId]) return;

        const clientPromise = this._resourceFetcher[stateKey](groupId);

        // Indicate ongoing request
        this._fetchResourcePromise[stateKey][groupId] = clientPromise;

        clientPromise.then((result) => {
            this._state[stateKey][groupId] = result;
            console.info(this._state);
            this._ready[stateKey][groupId] = true;
            this._notifyListeners();
        }).catch((err) => {
            // Invited users not visible to non-members
            if (stateKey === this.STATE_KEY.GroupInvitedMembers && err.httpStatus === 403) {
                return;
            }

            console.error(`Failed to get resource ${stateKey} for ${groupId}`, err);
            this.emit('error', err, groupId);
        }).finally(() => {
            // Indicate finished request, allow for future fetches
            delete this._fetchResourcePromise[stateKey][groupId];
        });

        return clientPromise;
    }

    _notifyListeners() {
        this.emit('update');
    }

    /**
     * Register a listener to recieve updates from the store. This also
     * immediately triggers an update to send the current state of the
     * store (which could be the initial state).
     *
     * This also causes a fetch of all data of the specified group,
     * which might cause 4 separate HTTP requests, but only if said
     * requests aren't already ongoing.
     *
     * @param {string} groupId the ID of the group to fetch data for.
     * @param {function} fn the function to call when the store updates.
     * @return {Object} tok a registration "token" with a single
     *                      property `unregister`, a function that can
     *                      be called to unregister the listener such
     *                      that it won't be called any more.
     */
    registerListener(groupId, fn) {
        this.on('update', fn);
        // Call to set initial state (before fetching starts)
        this.emit('update');

        this._fetchResource(this.STATE_KEY.Summary, groupId);
        this._fetchResource(this.STATE_KEY.GroupRooms, groupId);
        this._fetchResource(this.STATE_KEY.GroupMembers, groupId);
        this._fetchResource(this.STATE_KEY.GroupInvitedMembers, groupId);

        // Similar to the Store of flux/utils, we return a "token" that
        // can be used to unregister the listener.
        return {
            unregister: () => {
                this.unregisterListener(fn);
            },
        };
    }

    unregisterListener(fn) {
        this.removeListener('update', fn);
    }

    isStateReady(groupId, id) {
        return this._ready[id][groupId];
    }

    getSummary(groupId) {
        return this._state[this.STATE_KEY.Summary][groupId] || {};
    }

    getGroupRooms(groupId) {
        return this._state[this.STATE_KEY.GroupRooms][groupId] || [];
    }

    getGroupMembers(groupId) {
        return this._state[this.STATE_KEY.GroupMembers][groupId] || [];
    }

    getGroupInvitedMembers(groupId) {
        return this._state[this.STATE_KEY.GroupInvitedMembers][groupId] || [];
    }

    getGroupPublicity(groupId) {
        return (this._state[this.STATE_KEY.Summary][groupId] || {}).user ?
            (this._state[this.STATE_KEY.Summary][groupId] || {}).user.is_publicised : null;
    }

    isUserPrivileged(groupId) {
        return (this._state[this.STATE_KEY.Summary][groupId] || {}).user ?
            (this._state[this.STATE_KEY.Summary][groupId] || {}).user.is_privileged : null;
    }

    addRoomToGroup(groupId, roomId, isPublic) {
        return MatrixClientPeg.get()
            .addRoomToGroup(groupId, roomId, isPublic)
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupRooms, groupId));
    }

    updateGroupRoomVisibility(groupId, roomId, isPublic) {
        return MatrixClientPeg.get()
            .updateGroupRoomVisibility(groupId, roomId, isPublic)
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupRooms, groupId));
    }

    removeRoomFromGroup(groupId, roomId) {
        return MatrixClientPeg.get()
            .removeRoomFromGroup(groupId, roomId)
            // Room might be in the summary, refresh just in case
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId))
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupRooms, groupId));
    }

    inviteUserToGroup(groupId, userId) {
        return MatrixClientPeg.get().inviteUserToGroup(groupId, userId)
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupInvitedMembers, groupId));
    }

    acceptGroupInvite(groupId) {
        return MatrixClientPeg.get().acceptGroupInvite(groupId)
            // The user should now be able to access (personal) group settings
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId))
            // The user might be able to see more rooms now
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupRooms, groupId))
            // The user should now appear as a member
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupMembers, groupId))
            // The user should now not appear as an invited member
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupInvitedMembers, groupId));
    }

    joinGroup(groupId) {
        return MatrixClientPeg.get().joinGroup(groupId)
            // The user should now be able to access (personal) group settings
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId))
            // The user might be able to see more rooms now
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupRooms, groupId))
            // The user should now appear as a member
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupMembers, groupId))
            // The user should now not appear as an invited member
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupInvitedMembers, groupId));
    }

    leaveGroup(groupId) {
        return MatrixClientPeg.get().leaveGroup(groupId)
            // The user should now not be able to access group settings
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId))
            // The user might only be able to see a subset of rooms now
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupRooms, groupId))
            // The user should now not appear as a member
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupMembers, groupId));
    }

    addRoomToGroupSummary(groupId, roomId, categoryId) {
        return MatrixClientPeg.get()
            .addRoomToGroupSummary(groupId, roomId, categoryId)
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId));
    }

    addUserToGroupSummary(groupId, userId, roleId) {
        return MatrixClientPeg.get()
            .addUserToGroupSummary(groupId, userId, roleId)
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId));
    }

    removeRoomFromGroupSummary(groupId, roomId) {
        return MatrixClientPeg.get()
            .removeRoomFromGroupSummary(groupId, roomId)
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId));
    }

    removeUserFromGroupSummary(groupId, userId) {
        return MatrixClientPeg.get()
            .removeUserFromGroupSummary(groupId, userId)
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId));
    }

    setGroupPublicity(groupId, isPublished) {
        return MatrixClientPeg.get()
            .setGroupPublicity(groupId, isPublished)
            .then(() => { FlairStore.invalidatePublicisedGroups(MatrixClientPeg.get().credentials.userId); })
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId));
    }
}

let singletonGroupStore = null;
if (!singletonGroupStore) {
    singletonGroupStore = new GroupStore();
}
module.exports = singletonGroupStore;
<|endoftext|>


<commit_before>/**
 * @file Helper to capitalize strings
 * @since 0.1.5
 */
/*#ifndef(UMD)*/
"use strict";
/*exported _gpfStringCapitalize*/ // Capitalize the string
/*#endif*/

/**
 * Capitalize the string by applying toUpperCase on its first character
 *
 * @param {String} that String to capitalize
 * @return {String} Capitalized string
 * @since 0.1.5
 */
function _gpfStringCapitalize (that) {
    return that.charAt(0).toUpperCase() + that.substr(1);
}

/*#ifndef(UMD)*/

gpf.internals._gpfStringCapitalize = _gpfStringCapitalize;

/*#endif*/
<commit_msg>no-magic-numbers (#280)<commit_after>/**
 * @file Helper to capitalize strings
 * @since 0.1.5
 */
/*#ifndef(UMD)*/
"use strict";
/*global _GPF_START*/ // 0
/*exported _gpfStringCapitalize*/ // Capitalize the string
/*#endif*/

/**
 * Capitalize the string by applying toUpperCase on its first character
 *
 * @param {String} that String to capitalize
 * @return {String} Capitalized string
 * @since 0.1.5
 */
function _gpfStringCapitalize (that) {
    var REMAINDER = 1;
    return that.charAt(_GPF_START).toUpperCase() + that.substring(REMAINDER);
}

/*#ifndef(UMD)*/

gpf.internals._gpfStringCapitalize = _gpfStringCapitalize;

/*#endif*/
<|endoftext|>


<commit_before>// Base styles

const baseTextStyle = {
  fontStyle: 'normal',
  fontWeight: 400,
  textRendering: 'optimizeLegibility',
  WebkitFontSmoothing: 'antialiased',
  MozOsxFontSmoothing: 'grayscale'
};

const baseListStyle = {
  width: '100%',
  marginLeft: 0,
  paddingLeft: '2rem'
};

// Helpers

// Modular scale font size helper; level can be negative (for smaller font sizes) and positive (for larger font sizes) integers; level 0 === baseFontSize
export const getFontSize = ({baseFontSize, msRatio}, level = 0) => `${baseFontSize * Math.pow(msRatio, level)}px`;

const inlineElements = (theme, selector = '') => {
  return {
    [`${selector} i, em`]: {
      fontStyle: 'italic'
    },
    [`${selector} b, strong`]: {
      fontWeight: 700
    },
    [`${selector} a`]: {
      color: theme.linkColor,
      textDecoration: 'none'
    },
    [`${selector} a:hover`]: {
      textDecoration: 'underline'
    },
    [`${selector} code`]: {
      background: theme.bgLight,
      border: `1px solid #eee`,
      borderRadius: 1,
      display: 'inline-block',
      fontFamily: theme.fontMono,
      fontSize: `${Math.pow(theme.msRatio, -0.5)}em`,
      lineHeight: 1,
      padding: '0.12em 0.2em',
      textIndent: 0
    },
    [`${selector} img`]: {
      maxWidth: '100%'
    }
  };
};

const adjacent = (precedingSelectors = [], selector = '', style = {}) => ({
  [precedingSelectors.map((s) => `${s}+${selector}`).join(',')]: style
});

// Exports

// Text font style
export const text = (theme, level = 0) => ({
  ...baseTextStyle,
  color: theme.textColor,
  fontFamily: theme.fontFamily,
  fontSize: getFontSize(theme, level),
  lineHeight: theme.msRatio * theme.msRatio
});

// Heading font style
export const heading = (theme, level = 0) => ({
  ...baseTextStyle,
  color: theme.brandColor,
  fontFamily: theme.fontHeading,
  fontSize: getFontSize(theme, level),
  lineHeight: theme.msRatio,
  position: 'relative'
});

// Block element styles

export const textBlock = (theme, selector = 'p', level = 0) => ({
  [selector]: {
    ...text(theme, level),
    flexBasis: '100%',
    margin: `16px 0 0 0`
  },
  ...inlineElements(theme, `${selector} >`)
});

export const headingBlock = (theme, selector = 'h1', level = 0) => ({
  [selector]: {
    ...heading(theme, level),
    flexBasis: '100%',
    margin: `48px 0 0 0`
  },
  ...adjacent(['blockquote', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'], selector, {
    margin: `16px 0 0 0`
  }),
  ...inlineElements(theme, `${selector} >`)
});

export const unorderedList = (theme, selector = 'ul', level = 0, depth = 0) => {
  const nestedStyles = depth < 3 ? unorderedList(theme, `${selector} > li > ul`, level, depth + 1) : {};
  return {
    [selector]: {
      ...baseListStyle,
      ...text(theme, level),
      listStyle: 'disc',
      marginTop: depth > 0 ? 0 : '16px',
      marginBottom: 0
    },
    ...inlineElements(theme, selector),
    ...nestedStyles
  };
};

export function orderedList(theme, selector = 'ol', level = 0, depth = 0) {
  const nestedStyles = depth < 3 ? orderedList(theme, `${selector} > li > ol`, level, depth + 1) : {};
  return {
    [selector]: {
      ...baseListStyle,
      ...text(theme, level),
      listStyle: 'ordinal',
      marginTop: depth > 0 ? 0 : '16px',
      marginBottom: 0
    },
    ...inlineElements(theme, selector),
    ...nestedStyles
  };
}

export const blockquote = () => {
  return {
    blockquote: {
      quotes: 'none',
      margin: '48px 0 32px 0'
    },
    'blockquote > :first-child': {
      marginTop: 0
    },
    'blockquote > :last-child': {
      marginBottom: 0
    },
    'blockquote::before, blockquote::after': {
      content: 'none'
    }
  };
};
<commit_msg>Fix links visited color to prevent default style. (#355)<commit_after>// Base styles

const baseTextStyle = {
  fontStyle: 'normal',
  fontWeight: 400,
  textRendering: 'optimizeLegibility',
  WebkitFontSmoothing: 'antialiased',
  MozOsxFontSmoothing: 'grayscale'
};

const baseListStyle = {
  width: '100%',
  marginLeft: 0,
  paddingLeft: '2rem'
};

// Helpers

// Modular scale font size helper; level can be negative (for smaller font sizes) and positive (for larger font sizes) integers; level 0 === baseFontSize
export const getFontSize = ({baseFontSize, msRatio}, level = 0) => `${baseFontSize * Math.pow(msRatio, level)}px`;

const inlineElements = (theme, selector = '') => {
  return {
    [`${selector} i, em`]: {
      fontStyle: 'italic'
    },
    [`${selector} b, strong`]: {
      fontWeight: 700
    },
    [`${selector} a, a:visited`]: {
      color: theme.linkColor,
      textDecoration: 'none'
    },
    [`${selector} a:hover`]: {
      textDecoration: 'underline'
    },
    [`${selector} code`]: {
      background: theme.bgLight,
      border: `1px solid #eee`,
      borderRadius: 1,
      display: 'inline-block',
      fontFamily: theme.fontMono,
      fontSize: `${Math.pow(theme.msRatio, -0.5)}em`,
      lineHeight: 1,
      padding: '0.12em 0.2em',
      textIndent: 0
    },
    [`${selector} img`]: {
      maxWidth: '100%'
    }
  };
};

const adjacent = (precedingSelectors = [], selector = '', style = {}) => ({
  [precedingSelectors.map((s) => `${s}+${selector}`).join(',')]: style
});

// Exports

// Text font style
export const text = (theme, level = 0) => ({
  ...baseTextStyle,
  color: theme.textColor,
  fontFamily: theme.fontFamily,
  fontSize: getFontSize(theme, level),
  lineHeight: theme.msRatio * theme.msRatio
});

// Heading font style
export const heading = (theme, level = 0) => ({
  ...baseTextStyle,
  color: theme.brandColor,
  fontFamily: theme.fontHeading,
  fontSize: getFontSize(theme, level),
  lineHeight: theme.msRatio,
  position: 'relative'
});

// Block element styles

export const textBlock = (theme, selector = 'p', level = 0) => ({
  [selector]: {
    ...text(theme, level),
    flexBasis: '100%',
    margin: `16px 0 0 0`
  },
  ...inlineElements(theme, `${selector} >`)
});

export const headingBlock = (theme, selector = 'h1', level = 0) => ({
  [selector]: {
    ...heading(theme, level),
    flexBasis: '100%',
    margin: `48px 0 0 0`
  },
  ...adjacent(['blockquote', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'], selector, {
    margin: `16px 0 0 0`
  }),
  ...inlineElements(theme, `${selector} >`)
});

export const unorderedList = (theme, selector = 'ul', level = 0, depth = 0) => {
  const nestedStyles = depth < 3 ? unorderedList(theme, `${selector} > li > ul`, level, depth + 1) : {};
  return {
    [selector]: {
      ...baseListStyle,
      ...text(theme, level),
      listStyle: 'disc',
      marginTop: depth > 0 ? 0 : '16px',
      marginBottom: 0
    },
    ...inlineElements(theme, selector),
    ...nestedStyles
  };
};

export function orderedList(theme, selector = 'ol', level = 0, depth = 0) {
  const nestedStyles = depth < 3 ? orderedList(theme, `${selector} > li > ol`, level, depth + 1) : {};
  return {
    [selector]: {
      ...baseListStyle,
      ...text(theme, level),
      listStyle: 'ordinal',
      marginTop: depth > 0 ? 0 : '16px',
      marginBottom: 0
    },
    ...inlineElements(theme, selector),
    ...nestedStyles
  };
}

export const blockquote = () => {
  return {
    blockquote: {
      quotes: 'none',
      margin: '48px 0 32px 0'
    },
    'blockquote > :first-child': {
      marginTop: 0
    },
    'blockquote > :last-child': {
      marginBottom: 0
    },
    'blockquote::before, blockquote::after': {
      content: 'none'
    }
  };
};
<|endoftext|>


<commit_before>(function () {
    "use strict";

    var module = angular.module('password', []),
        checks = [
            /* alphaLower */ {
                re: /[a-z]/,
                score: 1
            },
            /* alphaUpper */ {
                re: /[A-Z]/,
                score: 5
            },
            /* mixture of upper and lowercase */ {
                re: /([a-z].*[A-Z])|([A-Z].*[a-z])/,
                score: 2
            },
            /* threeNumbers */ {
                re: /(.*[0-9].*[0-9].*[0-9])/,
                score: 7
            },
            /* special chars */ {
                re: /.[!@#$%^&*?_~]/,
                score: 5
            },
            /* multiple special chars */ {
                re: /(.*[!@#$%^&*?_~].*[!@#$%^&*?_~])/,
                score: 7
            },
            /* all together now, does it look nice? */ {
                re: /([a-zA-Z0-9].*[!@#$%^&*?_~])|([!@#$%^&*?_~].*[a-zA-Z0-9])/,
                score: 3
            },
            /* password of a single char sucks */ {
                re: /(.)\1+$/,
                score: 2
            }
        ],
        scores = [ 0, 10, 15, 25, 45 ],
        verdicts = [ 'Too Short', 'Very weak', 'Weak', 'Good', 'Strong', 'Very strong'],
        progress = [ '0%', '10%', '30%', '60%', '80%', '100%' ];

    module.factory('Password', function () {
        return {
            checkStrength: function (password) {
                var score = 0, minChars = 6, len = password.length, diff = len - minChars, idx = -1;

                // Calculate initial score based on password length
                (diff < 0 && (score -= 100)) || (diff >= 5 && (score += 18)) || (diff >= 3 && (score +=
                    12)) || (diff === 2 && (score += 6));

                angular.forEach(checks, function (check) {
                    password.match(check.re) && (score += check.score);
                });

                // Score bonus for length per character
                score && (score += len);

                angular.forEach(scores, function (value, index) {
                    (idx < 0) && (score < value) && (idx = index);
                });

                return {
                    score:score,
                    verdict: verdicts[idx],
                    progress: progress[idx]
                };
            }
        };
    });

}());<commit_msg>Password strength calculation fix<commit_after>(function () {
    "use strict";

    var module = angular.module('password', []),
        checks = [
            /* alphaLower */ {
                re: /[a-z]/,
                score: 1
            },
            /* alphaUpper */ {
                re: /[A-Z]/,
                score: 5
            },
            /* mixture of upper and lowercase */ {
                re: /([a-z].*[A-Z])|([A-Z].*[a-z])/,
                score: 2
            },
            /* threeNumbers */ {
                re: /(.*[0-9].*[0-9].*[0-9])/,
                score: 7
            },
            /* special chars */ {
                re: /.[!@#$%^&*?_~]/,
                score: 5
            },
            /* multiple special chars */ {
                re: /(.*[!@#$%^&*?_~].*[!@#$%^&*?_~])/,
                score: 7
            },
            /* all together now, does it look nice? */ {
                re: /([a-zA-Z0-9].*[!@#$%^&*?_~])|([!@#$%^&*?_~].*[a-zA-Z0-9])/,
                score: 3
            },
            /* password of a single char sucks */ {
                re: /(.)\1+$/,
                score: 2
            }
        ],
        scores = [ 0, 10, 15, 25, 45 ],
        verdicts = [ 'Very weak', 'Weak', 'Good', 'Strong', 'Very strong'],
        progress = [ 10, 30, 60, 80, 100 ];

    module.factory('Password', function () {
        return {
            checkStrength: function (password) {
                var score = 0, minChars = 6, len = password.length, diff = len - minChars, idx = -1;

                if (diff < 0) {
                    return {
                        score:-100,
                        verdict: 'Too Short',
                        progress: 0,
                        type: 'danger'
                    };
                }

                // Calculate initial score based on password length
                (diff >= 5 && (score += 18)) || (diff >= 3 && (score += 12)) || (diff === 2 && (score += 6));

                angular.forEach(checks, function (check) {
                    password.match(check.re) && (score += check.score);
                });

                // Score bonus for length per character
                score && (score += len);

                for(var i = scores.length - 1; i >= 0; i--) {
                    if (score >= scores[i]) {
                        idx = i;
                        break;
                    }
                }

                (idx == -1) && (idx == 0);

                return {
                    score:score,
                    verdict: verdicts[idx],
                    progress: progress[idx],
                    type: progress[idx] > 60 ? 'success' : progress[idx] > 30 ? 'warning' : 'danger'
                };
            }
        };
    });

}());<|endoftext|>


<commit_before>var http = require('http');

var singlePerfLimit = 16000;
var maxPerf = singlePerfLimit;
var perfReceived = -2;
var singlePerfStart = new Date();
var ended = false;

var checkEnd = function(){
    if(perfReceived >= maxPerf && !ended){
        var _now = new Date();
        console.log('end perf ' + _now);

        var t1 = _now;
        var t2 = singlePerfStart;
        var dif = t1.getTime() - t2.getTime()

        var Seconds_from_T1_to_T2 = dif / 1000;
        var Seconds_Between_Dates = Math.abs(Seconds_from_T1_to_T2);
        console.log('It took ' + Seconds_from_T1_to_T2 + ' seconds to process ' + singlePerfLimit + ' records');
        console.log('that is ' + (singlePerfLimit / Seconds_from_T1_to_T2) + ' records/sec');
        ended = true;
        socket.disconnect();
    }
};

var onSinglePerf = function(snapshot){
    perfReceived++;
//    console.log('perfReceieved = ' + perfReceived);
    if(perfReceived === 1){
        singlePerfStart = new Date();
        console.log('starting perf at ' + singlePerfStart);
    }

    if(perfReceived % 100 === 0){
        console.log(perfReceived);
    }

    checkEnd();
};

var io = require('./node_modules/socket.io/node_modules/socket.io-client');
var _base = 'http://10.0.0.42:3000'; //'http://192.168.56.100:3000';// '' for localhost

var onValue = function(data){};
var socket = null;
var getRef = function(path){
    var root = io(_base);

    root.on('subscribed',function(path){
        console.log('server ack ' + path);
        socket = io(_base + path);

        socket.on('value',function(data){
            onSinglePerf(null);
        });
    });

    root.on('connect',function(){
        console.log('subscribing to /TestOrg');
        root.emit('subscribe','/TestOrg/current');
    });
};

getRef();
<commit_msg>parameterize listener harness<commit_after>var http = require('http');
var config = require('./config.js');

var singlePerfLimit = 10000;
var maxPerf = singlePerfLimit;
var perfReceived = -2;
var singlePerfStart = new Date();
var ended = false;

var checkEnd = function(){
    if(perfReceived >= maxPerf && !ended){
        var _now = new Date();
        console.log('end perf ' + _now);

        var t1 = _now;
        var t2 = singlePerfStart;
        var dif = t1.getTime() - t2.getTime()

        var Seconds_from_T1_to_T2 = dif / 1000;
        var Seconds_Between_Dates = Math.abs(Seconds_from_T1_to_T2);
        console.log('It took ' + Seconds_from_T1_to_T2 + ' seconds to process ' + singlePerfLimit + ' records');
        console.log('that is ' + (singlePerfLimit / Seconds_from_T1_to_T2) + ' records/sec');
        ended = true;
        socket.disconnect();
    }
};

var onSinglePerf = function(snapshot){
    perfReceived++;
//    console.log('perfReceieved = ' + perfReceived);
    if(perfReceived === 1){
        singlePerfStart = new Date();
        console.log('starting perf at ' + singlePerfStart);
    }

    if(perfReceived % 100 === 0){
        console.log(perfReceived);
    }

    checkEnd();
};

var io = require('./node_modules/socket.io/node_modules/socket.io-client');
var _base = 'http://' + config.realTimeStoreHost + ':' + config.realTimeStorePort;

var onValue = function(data){};
var socket = null;
var getRef = function(path){
    var root = io(_base);

    root.on('subscribed',function(path){
        console.log('server ack ' + path);
        socket = io(_base + path);

        socket.on('value',function(data){
            onSinglePerf(null);
        });
    });

    root.on('connect',function(){
        console.log('subscribing to /TestOrg');
        root.emit('subscribe','/TestOrg/current');
    });
};

getRef();
<|endoftext|>


<commit_before>/**
 *  Ermittelt die Partei mit den meisten Stimmen im Wahlkreis
 * @param {Object} properties
 */
function getAnalyseForWechselwaehler(properties) {
    // FIXME Ergebnisse von 2017 einbauen
    if (properties.btw2013.zweitstimme !== 'undefined'
        && properties.btw2013.zweitstimme !== 'undefined') {
      var zweitstimmen2013 = properties.btw2013.zweitstimme;
      var zweitstimmen2017 = properties.btw2013.zweitstimme;
      var sieger2013 = getParteiMitMeistenStimmen(zweitstimmen2013);
      var sieger2017 = getParteiMitMeistenStimmen(zweitstimmen2017);

      var color = '#fff';
      var tooltip = "Kein Wechsel";
      if (sieger2013.partyName.toLowerCase() !== sieger2017.partyName.toLowerCase()) {
        color = getColorForParty(sieger2017.partyName);
        tooltip = "Wechsel von " + sieger2013.partyName + " (" + sieger2013.stimmen + ") zu " + sieger2017.partyName + " (" + sieger2013.stimmen + ")";
      }
      return {
        "color": color,
        "tooltipShowValue": tooltip,
      }
    } else {
        console.error("Error in analysis");
    }
}

function getParteiMitMeistenStimmen(zweitstimmen) {
  var max = 0;
  var partyName = null;
  zweitstimmen.forEach(function(party) {
     if (max < party.stimmen) {
         max = party.stimmen;
         partyName = party.partei;
     }
  });
  return {
    "partyName": partyName,
    "stimmen": max,
  };
}
<commit_msg>Optimierung Wechselwahl-Analyse<commit_after>/**
 *  Ermittelt die Partei mit den meisten Stimmen im Wahlkreis
 * @param {Object} properties
 */
function getAnalyseForWechselwaehler(properties) {
    // FIXME Ergebnisse von 2017 einbauen
    if (properties.btw2013.zweitstimme !== 'undefined'
        && properties.btw2013.zweitstimme !== 'undefined') {
      var zweitstimmen2013 = properties.btw2013.zweitstimme;
      var zweitstimmen2017 = properties.btw2013.zweitstimme;
      var sieger2013 = getParteiMitMeistenStimmen(zweitstimmen2013);
      var sieger2017 = getParteiMitMeistenStimmen(zweitstimmen2017);

      var color = '#fff';
      var tooltip = "Kein Wechsel";
      if (sieger2013.party.name.toLowerCase() !== sieger2017.party.name.toLowerCase()) {
        color = getColorForParty(sieger2017.party.name);
        tooltip = "Wechsel von " + sieger2013.party.name + " (" + sieger2013.stimmen + " Stimmen) zu " + sieger2017.party.name + " (" + sieger2013.stimmen + " Stimmen)";
      }
      return {
        "color": color,
        "tooltipShowValue": tooltip,
      }
    } else {
        console.error("Error in analysis");
    }
}

function getParteiMitMeistenStimmen(zweitstimmen) {
  var max = 0;
  var partyName = null;
  zweitstimmen.forEach(function(party) {
     if (max < party.stimmen) {
         max = party.stimmen;
         partyName = party.partei;
     }
  });
  var gewinner = findParty(partyName);
  return {
    "party": gewinner,
    "stimmen": max,
  };
}
<|endoftext|>


<commit_before>/**
 *  Ermittelt die Partei mit den meisten Stimmen im Wahlkreis
 * @param {Object} properties
 */
function getAnalyseForWechselwaehler(properties) {
    // FIXME Ergebnisse von 2017 einbauen
    if (properties.btw2013.zweitstimme !== 'undefined'
        && properties.btw2017_dummy.zweitstimme !== 'undefined') {
      var zweitstimmen2013 = properties.btw2013.zweitstimme;
      var zweitstimmen2017 = properties.btw2017_dummy.zweitstimme;
      var sieger2013 = getParteiMitMeistenStimmen(zweitstimmen2013);
      var sieger2017 = getParteiMitMeistenStimmen(zweitstimmen2017);

      var color = '#fff';
      var tooltip = "Kein Wechsel";
      if (sieger2013.party.name.toLowerCase() !== sieger2017.party.name.toLowerCase()) {
        color = getColorForParty(sieger2017.party.name);
        tooltip = "Wechsel von " + sieger2013.party.name + " (" + sieger2013.stimmen + " Stimmen) zu " + sieger2017.party.name + " (" + sieger2013.stimmen + " Stimmen)";
      }
      return {
        "color": color,
        "tooltipShowValue": tooltip,
      }
    } else {
        console.error("Error in analysis");
    }
}

function getParteiMitMeistenStimmen(zweitstimmen) {
  var max = 0;
  var partyName = null;
  zweitstimmen.forEach(function(party) {
     if (max < party.stimmen) {
         max = party.stimmen;
         partyName = party.partei;
     }
  });
  var gewinner = findParty(partyName);
  return {
    "party": gewinner,
    "stimmen": max,
  };
}

/**
 *  Ermittelt die Partei mit den meisten Stimmen im Wahlkreis
 * @param {Object} properties
 */
function getAnalyseForUngueltigeErststimmen(properties) {
    if (properties.btw2017_dummy.erststimme !== 'undefined') {
      var erststimmen = properties.btw2017_dummy.erststimme;
      var gesamtstimmen = properties.btw2017_dummy["wähler/-innen"];

      var tooltip = "Kein Wechsel";
      var gueltigeStimmen = 0;
      erststimmen.forEach(function(party) {
           gueltigeStimmen += party.stimmen;
      });
      var ungueltigeStimmen = gesamtstimmen - gueltigeStimmen;
      var opacity = ungueltigeStimmen / gesamtstimmen * 50;
      var color = 'rgba(26, 188, 156, ' + opacity + ')';
      return {
        "color": color,
        "tooltipShowValue": ungueltigeStimmen + " ungültige Zweitstimmen",
      }
    } else {
        console.error("Error in analysis");
    }
}

/**
 *  Ermittelt die Partei mit den meisten Stimmen im Wahlkreis
 * @param {Object} properties
 */
function getAnalyseForUngueltigeZweitstimmen(properties) {
    if (properties.btw2017_dummy.zweitstimme !== 'undefined') {
      var zweitstimmen = properties.btw2017_dummy.zweitstimme;
      var gesamtstimmen = properties.btw2017_dummy["wähler/-innen"];

      var tooltip = "Kein Wechsel";
      var gueltigeStimmen = 0;
      zweitstimmen.forEach(function(party) {
           gueltigeStimmen += party.stimmen;
      });
      var ungueltigeStimmen = gesamtstimmen - gueltigeStimmen;
      var opacity = ungueltigeStimmen / gesamtstimmen * 50;
      var color = 'rgba(26, 188, 156, ' + opacity + ')';
      return {
        "color": color,
        "tooltipShowValue": ungueltigeStimmen + " ungültige Zweitstimmen",
      }
    } else {
        console.error("Error in analysis");
    }
}
<commit_msg>Daten aus btw2017_dummy<commit_after>/**
 *  Ermittelt die Partei mit den meisten Stimmen im Wahlkreis
 * @param {Object} properties
 */
function getAnalyseForWechselwaehler(properties) {
    // FIXME Ergebnisse von 2017 einbauen
    if (properties.btw2013.zweitstimme !== 'undefined'
        && properties.btw2017_dummy.zweitstimme !== 'undefined') {
      var zweitstimmen2013 = properties.btw2013.zweitstimme;
      var zweitstimmen2017 = properties.btw2017_dummy.zweitstimme;
      var sieger2013 = getParteiMitMeistenStimmen(zweitstimmen2013);
      var sieger2017 = getParteiMitMeistenStimmen(zweitstimmen2017);

      var color = '#fff';
      var tooltip = "Kein Wechsel";
      if (sieger2013.party.name.toLowerCase() !== sieger2017.party.name.toLowerCase()) {
        color = getColorForParty(sieger2017.party.name);
        tooltip = "Wechsel von " + sieger2013.party.name + " (" + sieger2013.stimmen + " Stimmen) zu " + sieger2017.party.name + " (" + sieger2013.stimmen + " Stimmen)";
      }
      return {
        "color": color,
        "tooltipShowValue": tooltip,
      }
    } else {
        console.error("Error in analysis");
    }
}

function getParteiMitMeistenStimmen(zweitstimmen) {
  var max = 0;
  var partyName = null;
  zweitstimmen.forEach(function(party) {
     if (max < party.stimmen) {
         max = party.stimmen;
         partyName = party.partei;
     }
  });
  var gewinner = findParty(partyName);
  return {
    "party": gewinner,
    "stimmen": max,
  };
}

/**
 *  Ermittelt die Partei mit den meisten Stimmen im Wahlkreis
 * @param {Object} properties
 */
function getAnalyseForUngueltigeErststimmen(properties) {
    if (properties.btw2017_dummy.erststimme !== 'undefined') {
      var erststimmen = properties.btw2017_dummy.erststimme;
      var gesamtstimmen = properties.btw2017_dummy["waehler/-innen"];

      var tooltip = "Kein Wechsel";
      var gueltigeStimmen = 0;
      erststimmen.forEach(function(kandidat) {
           gueltigeStimmen += kandidat.stimmen;
      });
      var ungueltigeStimmen = gesamtstimmen - gueltigeStimmen;
      var opacity = ungueltigeStimmen / gesamtstimmen * 50;
      var color = 'rgba(26, 188, 156, ' + opacity + ')';
      return {
        "color": color,
        "tooltipShowValue": ungueltigeStimmen + " ungültige Zweitstimmen",
      }
    } else {
        console.error("Error in analysis");
    }
}

/**
 *  Ermittelt die Partei mit den meisten Stimmen im Wahlkreis
 * @param {Object} properties
 */
function getAnalyseForUngueltigeZweitstimmen(properties) {
    if (properties.btw2017_dummy.zweitstimme !== 'undefined') {
      var zweitstimmen = properties.btw2017_dummy.zweitstimme;
      var gesamtstimmen = properties.btw2017_dummy["waehler/-innen"];

      var tooltip = "Kein Wechsel";
      var gueltigeStimmen = 0;
      zweitstimmen.forEach(function(party) {
           gueltigeStimmen += party.stimmen;
      });
      var ungueltigeStimmen = gesamtstimmen - gueltigeStimmen;
      var opacity = ungueltigeStimmen / gesamtstimmen * 50;
      var color = 'rgba(26, 188, 156, ' + opacity + ')';
      return {
        "color": color,
        "tooltipShowValue": ungueltigeStimmen + " ungültige Zweitstimmen",
      }
    } else {
        console.error("Error in analysis");
    }
}
<|endoftext|>


<commit_before>'use strict';
var turf = require('turf');
var _ = require('underscore');
var rbush = require('rbush');
var flatten = require('geojson-flatten');

module.exports = function(tileLayers, tile, writeData, done) {
  var layer = tileLayers.osm.osm;
  var bboxLayer = turf.bboxPolygon(turf.extent(layer));
  bboxLayer.geometry.type = 'LineString';
  bboxLayer.geometry.coordinates = bboxLayer.geometry.coordinates[0];
  var bufferLayer = turf.buffer(bboxLayer, 0.01, 'miles').features[0];
  var preserveType = {
    //major
    'motorway': true,
    'trunk': true,
    'primary': true,
    'secondary': true,
    'tertiary': true,
    'unclassified': true,
    'residential': true,
    'service': true,
    'motorway_link': true,
    'trunk_link': true,
    'primary_link': true,
    'secondary_link': true,
    'tertiary_link': true,
    'living_street': true,
    'pedestrian': true,
    'road': true,
    //minor
    'track': true,
    'footway': true,
    'path': true,
    'cycleway': true
  };
  var unit = 'meters';
  var distance = 5;
  var highways = {};
  var bboxes = [];
  var output = {};
  var osmlint = 'nodeendingnearhighway';

  for (var i = 0; i < layer.features.length; i++) {
    var val = layer.features[i];
    // Linestring evaluation
    if (val.geometry.type === 'LineString' && preserveType[val.properties.highway]) {
      var bbox = turf.extent(val);
      bbox.push(val.properties._osm_way_id + 'L');
      bboxes.push(bbox);
      highways[val.properties._osm_way_id+ 'L'] = {
        highway: val,
        buffer: turf.buffer(val, distance, unit).features[0]
      };
    }
    // MultiLineString evaluation,
    else if (val.geometry.type === 'MultiLineString' && preserveType[val.properties.highway]) {
      var flat = flatten(val);
      var id = val.properties._osm_way_id + 'L';
      for (var f = 0; f < flat.length; f++) {
        if (flat[f].geometry.type === 'LineString') {
          var bbox = turf.extent(flat[f]);
          var idFlat = id + 'M' + f;
          bbox.push(idFlat);
          bboxes.push(bbox);
          highways[idFlat] = {
            highway: flat[f],
            buffer: turf.buffer(flat[f], distance, unit).features[0]
          };
        }
      }
    }
  }

  var highwaysTree = rbush(bboxes.length);
  highwaysTree.load(bboxes);

  for (var m = 0; m < bboxes.length; m++) {
    var valueBbox = bboxes[m];
    var valueHighway = highways[valueBbox[4]].highway;
    var firstCoord = valueHighway.geometry.coordinates[0];
    var firstPoint = turf.point(firstCoord);
    var endCoord = valueHighway.geometry.coordinates[valueHighway.geometry.coordinates.length - 1];
    var endPoint = turf.point(endCoord);
    var overlapsFirstPoint = [];
    if (!turf.inside(firstPoint, bufferLayer)) {
      overlapsFirstPoint = highwaysTree.search(turf.extent(turf.buffer(firstPoint, distance, unit)));
    }
    var overlapsEndPoint = [];
    if (!turf.inside(endPoint, bufferLayer)) {
      overlapsEndPoint = highwaysTree.search(turf.extent(turf.buffer(endPoint, distance, unit)));
    }
    var overlapBboxes = overlapsFirstPoint.concat(overlapsEndPoint);
    if (!_.isEqual(firstCoord, endCoord)) {
      var arrayCorrd = [];
      for (var j = 0; j < overlapBboxes.length; j++) {
        var overlapBbox = overlapBboxes[j];
        if (valueBbox[4] !== overlapBbox[4]) {
          arrayCorrd = arrayCorrd.concat(_.flatten(highways[overlapBbox[4]].highway.geometry.coordinates));
        }
      }
      var props = {
        wayA: valueBbox[4],
        _osmlint: osmlint
      };
      for (var k = 0; k < overlapsFirstPoint.length; k++) {
        var overlapPointFirst = overlapsFirstPoint[k];
        if (valueBbox[4] !== overlapPointFirst[4] && (arrayCorrd.indexOf(firstCoord[0]) === -1 || arrayCorrd.indexOf(firstCoord[1]) === -1)) {
          if (turf.inside(firstPoint, highways[overlapPointFirst[4]].buffer)) {
            props.wayB = overlapPointFirst[4];
            firstPoint.properties = props;
            output[valueBbox[4]] = highways[valueBbox[4]].highway;
            output[valueBbox[4]].properties._osmlint = osmlint;
            output[overlapPointFirst[4]] = highways[overlapPointFirst[4]].highway;
            output[overlapPointFirst[4]].properties._osmlint = osmlint;
            if (valueBbox[4] > overlapPointFirst[4]) {
              output[valueBbox[4].toString().concat(overlapPointFirst[4]).concat('first')] = firstPoint;
            } else {
              output[overlapPointFirst[4].toString().concat(valueBbox[4]).concat('first')] = firstPoint;
            }
          }
        }
      }
      for (var l = 0; l < overlapsEndPoint.length; l++) {
        var overlapPointEnd = overlapsEndPoint[l];
        if (valueBbox[4] !== overlapPointEnd[4] && (arrayCorrd.indexOf(endCoord[0]) === -1 || arrayCorrd.indexOf(endCoord[1]) === -1)) {
          if (turf.inside(endPoint, highways[overlapPointEnd[4]].buffer)) {
            props.wayB = overlapPointEnd[4];
            endPoint.properties = props;
            output[valueBbox[4]] = highways[valueBbox[4]].highway;
            output[valueBbox[4]].properties._osmlint = osmlint;
            output[overlapPointEnd[4]] = highways[overlapPointEnd[4]].highway;
            output[overlapPointEnd[4]].properties._osmlint = osmlint;
            if (valueBbox[4] > overlapPointEnd[4]) {
              output[valueBbox[4].toString().concat(overlapPointEnd[4]).concat('end')] = endPoint;
            } else {
              output[overlapPointEnd[4].toString().concat(valueBbox[4]).concat('end')] = endPoint;
            }
          }
        }
      }
    }
  }

  var result = _.values(output);

  if (result.length > 0) {
    var fc = turf.featurecollection(result);
    writeData(JSON.stringify(fc) + '\n');
  }

  done(null, null);

};<commit_msg>avoid crossing highways<commit_after>'use strict';
var turf = require('turf');
var _ = require('underscore');
var rbush = require('rbush');
var flatten = require('geojson-flatten');

module.exports = function(tileLayers, tile, writeData, done) {
  var layer = tileLayers.osm.osm;
  var bboxLayer = turf.bboxPolygon(turf.extent(layer));
  bboxLayer.geometry.type = 'LineString';
  bboxLayer.geometry.coordinates = bboxLayer.geometry.coordinates[0];
  var bufferLayer = turf.buffer(bboxLayer, 0.01, 'miles').features[0];
  var preserveType = {
    //major
    'motorway': true,
    'trunk': true,
    'primary': true,
    'secondary': true,
    'tertiary': true,
    'unclassified': true,
    'residential': true,
    'service': true,
    'motorway_link': true,
    'trunk_link': true,
    'primary_link': true,
    'secondary_link': true,
    'tertiary_link': true,
    'living_street': true,
    'pedestrian': true,
    'road': true,
    //minor
    'track': true,
    'footway': true,
    'path': true,
    'cycleway': true
  };
  var unit = 'meters';
  var distance = 5;
  var highways = {};
  var bboxes = [];
  var output = {};
  var osmlint = 'nodeendingnearhighway';

  for (var i = 0; i < layer.features.length; i++) {
    var val = layer.features[i];
    // Linestring evaluation
    if (val.geometry.type === 'LineString' && preserveType[val.properties.highway] && val.properties.layer === undefined) {
      var bbox = turf.extent(val);
      bbox.push(val.properties._osm_way_id + 'L');
      bboxes.push(bbox);
      highways[val.properties._osm_way_id + 'L'] = {
        highway: val,
        buffer: turf.buffer(val, distance, unit).features[0]
      };
    }
    // MultiLineString evaluation,
    else if (val.geometry.type === 'MultiLineString' && preserveType[val.properties.highway] && val.properties.layer === undefined) {
      var flat = flatten(val);
      var id = val.properties._osm_way_id + 'L';
      for (var f = 0; f < flat.length; f++) {
        if (flat[f].geometry.type === 'LineString') {
          var bbox = turf.extent(flat[f]);
          var idFlat = id + 'M' + f;
          bbox.push(idFlat);
          bboxes.push(bbox);
          highways[idFlat] = {
            highway: flat[f],
            buffer: turf.buffer(flat[f], distance, unit).features[0]
          };
        }
      }
    }
  }

  var highwaysTree = rbush(bboxes.length);
  highwaysTree.load(bboxes);

  for (var m = 0; m < bboxes.length; m++) {
    var valueBbox = bboxes[m];
    //obtaining first and last coordinates
    var valueHighway = highways[valueBbox[4]].highway;
    var firstCoord = valueHighway.geometry.coordinates[0];
    var firstPoint = turf.point(firstCoord);
    var endCoord = valueHighway.geometry.coordinates[valueHighway.geometry.coordinates.length - 1];
    var endPoint = turf.point(endCoord);
    var overlapsFirstPoint = [];
    if (!turf.inside(firstPoint, bufferLayer)) {
      overlapsFirstPoint = highwaysTree.search(turf.extent(turf.buffer(firstPoint, distance, unit)));
    }
    var overlapsEndPoint = [];
    if (!turf.inside(endPoint, bufferLayer)) {
      overlapsEndPoint = highwaysTree.search(turf.extent(turf.buffer(endPoint, distance, unit)));
    }
    var overlapBboxes = overlapsFirstPoint.concat(overlapsEndPoint);
    if (!_.isEqual(firstCoord, endCoord)) {
      var arrayCorrd = [];
      for (var j = 0; j < overlapBboxes.length; j++) {
        var overlapBbox = overlapBboxes[j];
        if (valueBbox[4] !== overlapBbox[4]) {
          arrayCorrd = arrayCorrd.concat(_.flatten(highways[overlapBbox[4]].highway.geometry.coordinates));
        }
      }
      var props = {
        wayA: valueHighway.properties._osm_way_id,
        _osmlint: osmlint
      };
      for (var k = 0; k < overlapsFirstPoint.length; k++) {
        var overlapPointFirst = overlapsFirstPoint[k];
        var overlapHighway = highways[overlapPointFirst[4]].highway;

        if (valueBbox[4] !== overlapPointFirst[4] && (arrayCorrd.indexOf(firstCoord[0]) === -1 || arrayCorrd.indexOf(firstCoord[1]) === -1)) {
          if (turf.inside(firstPoint, highways[overlapPointFirst[4]].buffer)) {
            props.wayB = overlapHighway.properties._osm_way_id;
            firstPoint.properties = props;
            //Check out whether the streets are connected at some point
            var coordinates = valueHighway.geometry.coordinates;
            var valueCoor = _.flatten([coordinates[1], coordinates[2]]);
            var overlapCoor = _.flatten(overlapHighway.geometry.coordinates);
            if (_.intersection(valueCoor, overlapCoor).length < 2) {
              output[valueBbox[4]] = valueHighway;
              output[valueBbox[4]].properties._osmlint = osmlint;
              output[overlapPointFirst[4]] = overlapHighway;
              output[overlapPointFirst[4]].properties._osmlint = osmlint;
              if (valueHighway.properties._osm_way_id > overlapHighway.properties._osm_way_id) {
                output[valueBbox[4].toString().concat(overlapPointFirst[4]).concat('first')] = firstPoint;
              } else {
                output[overlapPointFirst[4].toString().concat(valueBbox[4]).concat('first')] = firstPoint;
              }
            }
          }
        }
      }
      for (var l = 0; l < overlapsEndPoint.length; l++) {
        var overlapPointEnd = overlapsEndPoint[l];
        var overlapHighway = highways[overlapPointEnd[4]].highway;
        if (valueBbox[4] !== overlapPointEnd[4] && (arrayCorrd.indexOf(endCoord[0]) === -1 || arrayCorrd.indexOf(endCoord[1]) === -1)) {
          if (turf.inside(endPoint, highways[overlapPointEnd[4]].buffer)) {
            props.wayB = overlapHighway.properties._osm_way_id;
            endPoint.properties = props;
            //Check out whether the streets are connected at some point
            var coordinates = valueHighway.geometry.coordinates;
            var valueCoor = _.flatten([coordinates[coordinates.length - 1], coordinates[coordinates.length - 2]]);
            var overlapCoor = _.flatten(overlapHighway.geometry.coordinates);
            if (_.intersection(valueCoor, overlapCoor).length < 2) {
              output[valueBbox[4]] = valueHighway;
              output[valueBbox[4]].properties._osmlint = osmlint;
              output[overlapPointEnd[4]] = overlapHighway;
              output[overlapPointEnd[4]].properties._osmlint = osmlint;
              if (valueHighway.properties._osm_way_id > overlapHighway.properties._osm_way_id) {
                output[valueBbox[4].toString().concat(overlapPointEnd[4]).concat('end')] = endPoint;
              } else {
                output[overlapPointEnd[4].toString().concat(valueBbox[4]).concat('end')] = endPoint;
              }
            }
          }
        }
      }
    }
  }

  var result = _.values(output);

  if (result.length > 0) {
    var fc = turf.featurecollection(result);
    writeData(JSON.stringify(fc) + '\n');
  }

  done(null, null);

};<|endoftext|>


<commit_before>/**
 * [XMLvalidation evaluates an MODS XML file and ensures it meets standards set here: https://wiki.lib.utk.edu/display/DLP/UTK+Data+Dictionary]
 * @param {[String]} input [MODS XML filename]
 * @return {[Array]}       [Success] or [Filename, Error 1, Error 2, Error 3, ...]
 *
 */

var fs = require('fs');
var parser = require('xml2json');
var jp = require('jsonpath');
var status = [];
var filename = process.argv;
filename = String(filename[2]);

var modsIn = fs.readFileSync(filename, 'utf8');
var modsObj = parser.toJson(modsIn, options = {object: true});

var xmlValues = [];
// only testing for fields that are required and/or non-repeating
var xmlTargets = [0,   //collTitle (req if avail, non-repeating)
                  0,   //ms_arID (req if avail, non-repeating)
                  2,   //dateCreated*
                  1,   //dateIssued (optional, non-repeating)
                  1,   //digitalOrigin (req'd, non-repeating)
                  1,   //physicalDescription/extent (optional, non-repeating)
                  1,   //identifier[@type='filename'] (req'd, repeatable)
                  1,   //physicalDescription/form (req'd, repeatable)
                  1,   //physicalDescription/internetMediaType (req'd, non-repeating)
                  1,   //typeOfResource (req'd, non-repeating)
                  1,   //languageOfCataloging/languageTerm (req'd, non-repeating)
                  1,   //note[@type='ownership'] (optional, non-repeating)
                  1,   //recordInfo/recordOrigin (req'd, non-repeating)
                  1,   //recordInfo/recordContentSource (optional, non-repeating)
                  1,   //location/physicalLocation (req if avail, non-repeating)
                  1,   //rights! (required, non-repeating)
                  1,   //shelfLocator (optional, non-repeating)
                  1    //titleInfo/title (required, repeatable)

                 ];
var xmlErrors = ["XML: too many collection titles",
                 "XML: too many MS/AR numbers",
                 "XML: verify dateCreated values",
                 "XML: too many dateIssued elements",
                 "XML: problems with digitalOrigin",
                 "XML: too many extent elements",
                 "XML: please verify identifier[@type=\'filename\']",
                 "XML: too many form elements",
                 "XML: please verify internetMediaType",
                 "XML: please verify typeOfResource",
                 "XML: please verify languageOfCataloging/languageTerm",
                 "XML: please check the number of note[@type=\'ownership\']",
                 "XML: please check the number of recordOrigin elements",
                 "XML: please check the number of recordContentSource elements",
                 "XML: please check the number of location/physicalLocation elements",
                 "XML: please check the accessCondition element",
                 "XML: too many shelfLocator elements",
                 "XML: please add a titleInfo/title element"

                 ];

xmlValues[0] = jp.query(modsObj, '$.mods.relatedItem[?(@.displayLabel=="Collection")].titleInfo.title').length;
xmlValues[1] = jp.query(modsObj, '$.mods.relatedItem[?(@.displayLabel=="Collection")].identifier').length;
xmlValues[2] = jp.query(modsObj, '$.mods.originInfo.dateCreated.*').length;
xmlValues[3] = jp.query(modsObj, '$.mods.originInfo.dateIssued').length;
xmlValues[4] = jp.query(modsObj, '$.mods.physicalDescription.digitalOrigin').length;
xmlValues[5] = jp.query(modsObj, '$.mods.physicalDescription.extent').length;
xmlValues[6] = jp.query(modsObj, '$.mods.identifier[?(@.type=="filename")]').length;
xmlValues[7] = jp.query(modsObj, '$.mods.physicalDescription.form').length;
xmlValues[8] = jp.query(modsObj, '$.mods.physicalDescription.internetMediaType').length;
xmlValues[9] = jp.query(modsObj, '$.mods.typeOfResource').length;
xmlValues[10] = jp.query(modsObj, '$.mods..languageOfCataloging.languageTerm').length;
xmlValues[11] = jp.query(modsObj, '$.mods.note[?(@.type=="ownership")]').length;
xmlValues[12] = jp.query(modsObj, '$.mods.recordInfo.recordOrigin').length;
xmlValues[13] = jp.query(modsObj, '$.mods.recordInfo.recordContentSource').length;
xmlValues[14] = jp.query(modsObj, '$.mods.location.physicalLocation').length;
xmlValues[15] = jp.query(modsObj, '$.mods.accessCondition').length;
xmlValues[16] = jp.query(modsObj, '$..shelfLocator').length;
xmlValues[17] = jp.query(modsObj, '$.mods.titleInfo[*].title').length;

console.log('xmlValues:  ' + xmlValues + ' length: ' + xmlValues.length);
console.log('xmlTargets: ' + xmlTargets + ' length: ' + xmlTargets.length);

for(i=0; i < xmlValues.length; i++) {
  if((i==0) && ((xmlValues[i]>(xmlTargets[i])+1))) {
    console.log(`XML: Collection titles: ${xmlValues[i]} didn\'t match the expected value of ${xmlTargets[i]}`);
  }
  if((i==1) && (xmlValues[i]>1)) {
    console.log(`XML: MS/AR identifier(s): ${xmlValues[i]} didn\'t match the expected value of ${xmlTargets[i]}`);
  }
  if((i==2) && (xmlValues[i]>xmlTargets[i])) {
    console.log(`XML: dateCreated error: ${xmlValues[i]} didn\'t match the expected value of ${xmlTargets[i]}`);
  }
  // else {
  //  if(xmlValues[i]!=xmlTargets[i]) {
  //    console.log(`oops! Error Message: \"${xmlErrors[i]}\"\n ${xmlValues[i]} didn\'t match the expected value of ${xmlTargets[i]}`);
  //  }
  //}
}

startProcessing(filename, fileRead);

function startProcessing(file, callback) {
  fs.readFile(file, 'utf8', callback);
}

function fileRead(err, data) {
  var message;
  if (err) {
    message = 'Cannot read file';
  }
  else {
    message = 'Successfully read file';
  }
  //console.log('message :' + message);
  postResults(message, data);
}

function postResults(x, data) {
  switch(x) {
    case 'Cannot read file':
      console.log('CANNOT');
      break;
    case 'Successfully read file':
      //readMODS(data);
      return data;
      break;
  }
}

// fileKey for mods/identifier[@local] comparisons and interacting with db
// should accommodate multiple identifier[@type='filename']s now; this takes the first
// TODO this should incorporate a simple adminDB formatting validity check
var fileID = jp.query(modsObj, '$.mods.identifier[?(@.type=="filename")]["$t"]');
var fileKey = String(fileID[0]).slice(0, -4);
console.log('fileID: ' + fileID);
console.log('fileKey: ' + fileKey);

// originInfo/dateCreated* test (required and repeatable)
// TODO should be at least 1 dateCreated w/ no keyDate
// dateCreated[no attributes] = 1 (required, repeatable)
// dateCreated[@keyDate][@point='start'] = 1 (req'd, non-repeating)
// dateCreated[@keyDate][@qualifier] <= 1 (optional, non-repeating)

// TODO identifier testing
// identifier[@type='filename'], [@type='local'], [@type='opac']
// identifier[@type='filename'] is the only req'd, all repeatable

// genre (optional and repeatable)
// host title relatedItem[@type='host']/titleInfo/title (optional and repeatable)

// language/languageTerm (optional, repeatable)
// recordInfo/languageOfCataloging (required, non-repeating)

// name/namePart (optional, repeatable)
// name[@authority] (optional, repeatable)
// name/namePart/role/roleTerm (req'd if available, repeatable)
// name[@type] (optional, repeatable)

// subjects (all optional, all repeatable)
// thumbnail (req'd if applicable, non-repeating)
// title_initial_article, titleInfo/nonSort: articles in the title element (req'd if applicable, repeatable)
// title_of_part, titleInfo/partName (req'd if applicable, repeatable)
<commit_msg>start on lower level testing<commit_after>/**
 * [XMLvalidation evaluates an MODS XML file and ensures it meets standards set here: https://wiki.lib.utk.edu/display/DLP/UTK+Data+Dictionary]
 * @param {[String]} input [MODS XML filename]
 * @return {[Array]}       [Success] or [Filename, Error 1, Error 2, Error 3, ...]
 *
 */

var fs = require('fs');
var parser = require('xml2json');
var jp = require('jsonpath');
var status = [];
var filename = process.argv;
filename = String(filename[2]);

var modsIn = fs.readFileSync(filename, 'utf8');
var modsObj = parser.toJson(modsIn, options = {object: true});

var xmlValues = [];
// only testing for fields that are required and/or non-repeating
var xmlTargets = [0,   //collTitle (req if avail, non-repeating)
                  0,   //ms_arID (req if avail, non-repeating)
                  2,   //dateCreated*
                  1,   //dateIssued (optional, non-repeating)
                  1,   //digitalOrigin (req'd, non-repeating)
                  1,   //physicalDescription/extent (optional, non-repeating)
                  1,   //identifier[@type='filename'] (req'd, repeatable)
                  1,   //physicalDescription/form (req'd, repeatable)
                  1,   //physicalDescription/internetMediaType (req'd, non-repeating)
                  1,   //typeOfResource (req'd, non-repeating)
                  1,   //languageOfCataloging/languageTerm (req'd, non-repeating)
                  1,   //note[@type='ownership'] (optional, non-repeating)
                  1,   //recordInfo/recordOrigin (req'd, non-repeating)
                  1,   //recordInfo/recordContentSource (optional, non-repeating)
                  1,   //location/physicalLocation (req if avail, non-repeating)
                  1,   //rights! (required, non-repeating)
                  1,   //shelfLocator (optional, non-repeating)
                  1    //titleInfo/title (required, repeatable)

                 ];
var xmlErrors = ["XML: too many collection titles",
                 "XML: too many MS/AR numbers",
                 "XML: verify dateCreated values",
                 "XML: too many dateIssued elements",
                 "XML: problems with digitalOrigin",
                 "XML: too many extent elements",
                 "XML: please verify identifier[@type=\'filename\']",
                 "XML: too many form elements",
                 "XML: please verify internetMediaType",
                 "XML: please verify typeOfResource",
                 "XML: please verify languageOfCataloging/languageTerm",
                 "XML: please check the number of note[@type=\'ownership\']",
                 "XML: please check the number of recordOrigin elements",
                 "XML: please check the number of recordContentSource elements",
                 "XML: please check the number of location/physicalLocation elements",
                 "XML: please check the accessCondition element",
                 "XML: too many shelfLocator elements",
                 "XML: please add a titleInfo/title element"

                 ];

xmlValues[0] = jp.query(modsObj, '$.mods.relatedItem[?(@.displayLabel=="Collection")].titleInfo..title').length;
xmlValues[1] = jp.query(modsObj, '$.mods.relatedItem[?(@.displayLabel=="Collection")].identifier').length;
xmlValues[2] = jp.query(modsObj, '$.mods.originInfo.dateCreated.*').length;
xmlValues[3] = jp.query(modsObj, '$.mods.originInfo.dateIssued').length;
xmlValues[4] = jp.query(modsObj, '$.mods.physicalDescription.digitalOrigin').length;
xmlValues[5] = jp.query(modsObj, '$.mods.physicalDescription.extent').length;
xmlValues[6] = jp.query(modsObj, '$.mods.identifier[?(@.type=="filename")]').length;
xmlValues[7] = jp.query(modsObj, '$.mods.physicalDescription.form').length;
xmlValues[8] = jp.query(modsObj, '$.mods.physicalDescription.internetMediaType').length;
xmlValues[9] = jp.query(modsObj, '$.mods.typeOfResource').length;
xmlValues[10] = jp.query(modsObj, '$.mods..languageOfCataloging.languageTerm').length;
xmlValues[11] = jp.query(modsObj, '$.mods.note[?(@.type=="ownership")]').length;
xmlValues[12] = jp.query(modsObj, '$.mods.recordInfo.recordOrigin').length;
xmlValues[13] = jp.query(modsObj, '$.mods.recordInfo.recordContentSource').length;
xmlValues[14] = jp.query(modsObj, '$.mods.location.physicalLocation').length;
xmlValues[15] = jp.query(modsObj, '$.mods.accessCondition').length;
xmlValues[16] = jp.query(modsObj, '$..shelfLocator').length;
xmlValues[17] = jp.query(modsObj, '$.mods.titleInfo[*].title').length;

console.log('xmlValues:  ' + xmlValues + ' length: ' + xmlValues.length);
console.log('xmlTargets: ' + xmlTargets + ' length: ' + xmlTargets.length);

for(i=0; i < xmlValues.length; i++) {
  if((i==0) && (xmlValues[i]>1)) {
    console.log(`XML: Collection titles: ${xmlValues[i]} didn\'t match the expected value of ${xmlTargets[i]}`);
  }
  if((i==1) && (xmlValues[i]>1)) {
    console.log(`XML: MS/AR identifier(s): ${xmlValues[i]} didn\'t match the expected value of ${xmlTargets[i]}`);
  }
  if((i==2) && (xmlValues[i]>xmlTargets[i])) {
    console.log(`XML: dateCreated error: ${xmlValues[i]} didn\'t match the expected value of ${xmlTargets[i]}`);
    if(!jp.query(modsObj, '$.mods.originInfo.dateCreated[?(@.keyDate=="yes")]')) {
      console.log('dateCreated is here');
    } else {
      console.log('problems finding dateCreated');
    }
  }
  // else {
  //  if(xmlValues[i]!=xmlTargets[i]) {
  //    console.log(`oops! Error Message: \"${xmlErrors[i]}\"\n ${xmlValues[i]} didn\'t match the expected value of ${xmlTargets[i]}`);
  //  }
  //}
}

startProcessing(filename, fileRead);

function startProcessing(file, callback) {
  fs.readFile(file, 'utf8', callback);
}

function fileRead(err, data) {
  var message;
  if (err) {
    message = 'Cannot read file';
  }
  else {
    message = 'Successfully read file';
  }
  //console.log('message :' + message);
  postResults(message, data);
}

function postResults(x, data) {
  switch(x) {
    case 'Cannot read file':
      console.log('CANNOT');
      break;
    case 'Successfully read file':
      //readMODS(data);
      return data;
      break;
  }
}

// fileKey for mods/identifier[@local] comparisons and interacting with db
// should accommodate multiple identifier[@type='filename']s now; this takes the first
// TODO this should incorporate a simple adminDB formatting validity check
var fileID = jp.query(modsObj, '$.mods.identifier[?(@.type=="filename")]["$t"]');
var fileKey = String(fileID[0]).slice(0, -4);
console.log('fileID: ' + fileID);
console.log('fileKey: ' + fileKey);

// originInfo/dateCreated* test (required and repeatable)
// TODO should be at least 1 dateCreated w/ no keyDate
// dateCreated[no attributes] = 1 (required, repeatable)
// dateCreated[@keyDate][@point='start'] = 1 (req'd, non-repeating)
// dateCreated[@keyDate][@qualifier] <= 1 (optional, non-repeating)

// TODO identifier testing
// identifier[@type='filename'], [@type='local'], [@type='opac']
// identifier[@type='filename'] is the only req'd, all repeatable

// genre (optional and repeatable)
// host title relatedItem[@type='host']/titleInfo/title (optional and repeatable)

// language/languageTerm (optional, repeatable)
// recordInfo/languageOfCataloging (required, non-repeating)

// name/namePart (optional, repeatable)
// name[@authority] (optional, repeatable)
// name/namePart/role/roleTerm (req'd if available, repeatable)
// name[@type] (optional, repeatable)

// subjects (all optional, all repeatable)
// thumbnail (req'd if applicable, non-repeating)
// title_initial_article, titleInfo/nonSort: articles in the title element (req'd if applicable, repeatable)
// title_of_part, titleInfo/partName (req'd if applicable, repeatable)
<|endoftext|>


<commit_before>'use strict';

var util = require('util');

module.exports = function (options) {
  var opts = util._extend({
    indent: '  '
  }, options);

  // Set some shortcuts
  var req = {
    url: this.source.fullUrl,
    method: this.source.method,
    hostname: this.source.uriObj.hostname,
    port: this.source.uriObj.port,
    path: this.source.uriObj.path,
    headers: this.source.headersObj
  };

  var bodyPresent = this.source.postData && this.source.postData.text;

  // Let's Go!
  var code = [];

  // Create boilerplate
  code.push('package main\n');
  code.push('import (');
  code.push('\t"fmt"');
  if (bodyPresent) code.push('\t"strings"');
  code.push('\t"net/http"');
  code.push(')\n');

  code.push('func main() {')

  // Create client
  code.push('\tclient := &http.Client{}');
  code.push('\turl := "' + req.url + '"')

  // If we have body content or not create the var and reader or nil
  if (bodyPresent) {
    code.push('\tbody := ' + JSON.stringify(this.source.postData.text))
    req.body = 'strings.NewReader(body)'
  } else {
    req.body = 'nil'
  }

  code.push('\treq, _ := http.NewRequest("' + req.method + '", url, ' + req.body + ')');

  // Add headers
  var headersPresent = this.source.headers && this.source.headers.length;

  if (headersPresent) {
    for (var header in this.source.headers) {
      var key = this.source.headers[header].name
      var val = this.source.headers[header].value
      code.push('\treq.Header.Add("' + key + '", "' + val + '")');
    }
  }

  // Add Cookies
  var cookiesPresent = this.source.cookies && this.source.cookies.length;
  if (cookiesPresent) {
    var cookies = this.source.cookies.map(function (cookie) {
      return cookie.name + '=' + cookie.value;
    }).join('; ');
    code.push('\treq.Header.Add("Cookie", "' + cookies + '")');
  }

  // Make request
  code.push('\tres, _ := client.Do(req)');

  // Print it
  code.push('\tfmt.Printf("%+v", res)');

  // End main block
  code.push('}')

  return code.join('\n');
};

module.exports.info = {
  key: 'native',
  title: 'NewRequest',
  link: 'http://golang.org/pkg/net/http/#NewRequest',
  description: 'Golang HTTP client request'
};
<commit_msg>Pass jshint<commit_after>'use strict';

// var util = require('util');

module.exports = function (options) {

  // Might want to add error checking in as option

  // var opts = util._extend({
  //   errorChecking: false
  // }, options);

  // Set some shortcuts
  var req = {
    url: this.source.fullUrl,
    method: this.source.method,
    hostname: this.source.uriObj.hostname,
    port: this.source.uriObj.port,
    path: this.source.uriObj.path,
    headers: this.source.headersObj
  };

  var bodyPresent = this.source.postData && this.source.postData.text;

  // Let's Go!
  var code = [];

  // Create boilerplate
  code.push('package main\n');
  code.push('import (');
  code.push('\t"fmt"');
  if (bodyPresent) code.push('\t"strings"');
  code.push('\t"net/http"');
  code.push(')\n');

  code.push('func main() {');

  // Create client
  code.push('\tclient := &http.Client{}');
  code.push('\turl := "' + req.url + '"');

  // If we have body content or not create the var and reader or nil
  if (bodyPresent) {
    code.push('\tbody := ' + JSON.stringify(this.source.postData.text));
    req.body = 'strings.NewReader(body)';
  } else {
    req.body = 'nil';
  }

  code.push('\treq, _ := http.NewRequest("' + req.method + '", url, ' + req.body + ')');

  // Add headers
  var headersPresent = this.source.headers && this.source.headers.length;

  if (headersPresent) {
    for (var header in this.source.headers) {
      var key = this.source.headers[header].name;
      var val = this.source.headers[header].value;
      code.push('\treq.Header.Add("' + key + '", "' + val + '")');
    }
  }

  // Add Cookies
  var cookiesPresent = this.source.cookies && this.source.cookies.length;
  if (cookiesPresent) {
    var cookies = this.source.cookies.map(function (cookie) {
      return cookie.name + '=' + cookie.value;
    }).join('; ');
    code.push('\treq.Header.Add("Cookie", "' + cookies + '")');
  }

  // Make request
  code.push('\tres, _ := client.Do(req)');

  // Print it
  code.push('\tfmt.Printf("%+v", res)');

  // End main block
  code.push('}');

  return code.join('\n');
};

module.exports.info = {
  key: 'native',
  title: 'NewRequest',
  link: 'http://golang.org/pkg/net/http/#NewRequest',
  description: 'Golang HTTP client request'
};
<|endoftext|>


<commit_before>/*
 * Markdown Preview
 *
 * @requires jQuery v1.7 or later
 * @homepage http://github.com/mendicant-university/md_preview
 *
 */

var MdPreview = {};

(function($) {

  MdPreview.init = function(){
    $("textarea[data-preview=true]").each(function(index) {
  		  MdPreview.buildPreviewTab($(this));
  	});

  	// override tab clicks
  	$('div.markdown-preview ul.tabs li:not(.label) a').live('click', function(e) {
  		var tabs = $(this).parents('ul').find('li');
  		var tabContents = $(this).parents('ul').next('.tab-container').find('.tab-content');

  		tabs.removeClass("active");
  		$(this).parent().addClass("active");
  		tabContents.hide();

  		// show active tab contents
  		var activeTab = tabContents.filter("." + $(this).attr("href"));
  		$(activeTab).show();

  		// convert markdown for preview
  		if($(this).attr("href") == "preview"){
        MdPreview.convertMarkdown(tabContents);
      }
      else{
        activeTab.find('textarea').focus();
      }

  		e.preventDefault();
  	});

  	// override links in preview
  	$('tab-container .preview a').live('click', function(e){
      window.open(e.target.href);
      e.preventDefault();
  	});
  }

  MdPreview.buildPreviewTab = function(textarea){
    textarea.wrap('
');

    var labelText = "Comments are processed as " +
      "Markdown";
    var label = '
' + labelText + '
';

    // insert tabs
  	textarea.before(' \

 \

Edit
 \

Preview
' +
  		  label +
  		'
 \
  	');

  	// wrap textarea
  	textarea.wrap('
');

  	// tab-container wrapper
  	textarea.parent().wrap('
');

  	textarea.parent().after('
Preview
');

  	// activate tab links
  	MdPreview.enableTabs(textarea);
  }

  MdPreview.enableTabs = function(textarea) {
  	var tabs = textarea.parents('.tab-container').prev('ul.tabs').find('li');
  	var tabContents = textarea.parents('.tab-container').find('.tab-content');

  	tabContents.hide();

  	// show first tab
  	tabs.first().addClass("active").show();
  	tabContents.first().show();
  }

  MdPreview.convertMarkdown = function(tab) {
    var text = tab.find('textarea').val();
    var previewTab = tab.filter('.tab-content.preview');

    previewTab.html("
Loading ...
");

  	$.post("/markdown/parse.text", { text: text },
  	  function(data){
        previewTab.html(data);
  	  }
  	);
  }

  $(function(){
    MdPreview.init();

    try{
      $('textarea[data-preview=true]').elastic();
    }
    catch(error){
      // Include jquery.elastic to enable this feature
      //= require jquery.elastic
    }
  });

})(jQuery);<commit_msg>Update javascript  events to<commit_after>/*
 * Markdown Preview
 *
 * @requires jQuery v1.7 or later
 * @homepage http://github.com/mendicant-university/md_preview
 *
 */

var MdPreview = {};

(function($) {

  MdPreview.init = function(){
    $("textarea[data-preview=true]").each(function(index) {
  		  MdPreview.buildPreviewTab($(this));
  	});

  	// override tab clicks
  	$(document).on('click', 'div.markdown-preview ul.tabs li:not(.label) a', function(e) {
  		var tabs = $(this).parents('ul').find('li');
  		var tabContents = $(this).parents('ul').next('.tab-container').find('.tab-content');

  		tabs.removeClass("active");
  		$(this).parent().addClass("active");
  		tabContents.hide();

  		// show active tab contents
  		var activeTab = tabContents.filter("." + $(this).attr("href"));
  		$(activeTab).show();

  		// convert markdown for preview
  		if($(this).attr("href") == "preview"){
        MdPreview.convertMarkdown(tabContents);
      }
      else{
        activeTab.find('textarea').focus();
      }

  		e.preventDefault();
  	});

  	// override links in preview
  	$(document).on('click', 'tab-container .preview a', function(e){
      window.open(e.target.href);
      e.preventDefault();
  	});
  }

  MdPreview.buildPreviewTab = function(textarea){
    textarea.wrap('
');

    var labelText = "Comments are processed as " +
      "Markdown";
    var label = '
' + labelText + '
';

    // insert tabs
  	textarea.before(' \

 \

Edit
 \

Preview
' +
  		  label +
  		'
 \
  	');

  	// wrap textarea
  	textarea.wrap('
');

  	// tab-container wrapper
  	textarea.parent().wrap('
');

  	textarea.parent().after('
Preview
');

  	// activate tab links
  	MdPreview.enableTabs(textarea);
  }

  MdPreview.enableTabs = function(textarea) {
  	var tabs = textarea.parents('.tab-container').prev('ul.tabs').find('li');
  	var tabContents = textarea.parents('.tab-container').find('.tab-content');

  	tabContents.hide();

  	// show first tab
  	tabs.first().addClass("active").show();
  	tabContents.first().show();
  }

  MdPreview.convertMarkdown = function(tab) {
    var text = tab.find('textarea').val();
    var previewTab = tab.filter('.tab-content.preview');

    previewTab.html("
Loading ...
");

  	$.post("/markdown/parse.text", { text: text },
  	  function(data){
        previewTab.html(data);
  	  }
  	);
  }

  $(function(){
    MdPreview.init();

    try{
      $('textarea[data-preview=true]').elastic();
    }
    catch(error){
      // Include jquery.elastic to enable this feature
      //= require jquery.elastic
    }
  });

})(jQuery);
<|endoftext|>


<commit_before>import React from 'react'
import { graphql } from 'gatsby'

import GalleryGrid from './../components/gallery/galleryGrid'
import ContentHead from './../components/general/contentHead'
import SEO from './../components/general/SEO'

const GalleryTemplate = ({ data }) => {
  const gallery = data.contentfulExtendedGallery
  const subGalleries = data.contentfulExtendedGallery.galleries
  return (
    <>
      <SEO title={gallery.title} image={gallery.shareImage} />
      <ContentHead
        displayExcerpt
        title={gallery.title}
        body={gallery.body}
        tags={gallery.tags}
      />
      {subGalleries.map((subGallery, index) => (

          {subGallery.__typename === 'ContentfulSubGallery' && (
            <GalleryGrid
              key={subGallery.id}
              slug={subGallery.slug}
              images={subGallery.images}
              title={subGallery.title}
              itemsPerRow={[3, 5, 7]}
            />
          )}

      ))}
    </>
  )
}

export const query = graphql`
  query($slug: String!) {
    contentfulExtendedGallery(slug: { eq: $slug }) {
      title
      id
      slug
      metaDescription {
        internal {
          content
        }
      }
      publishDate(formatString: "MMMM DD, YYYY")
      publishDateISO: publishDate(formatString: "YYYY-MM-DD")
      tags {
        title
        id
        slug
      }
      shareImage {
        ogimg: resize(width: 1200) {
          src
          width
          height
        }
      }
      body {
        childMarkdownRemark {
          html
        }
      }
      galleries {
        __typename
        ... on ContentfulSubGallery {
          id
          slug
          title
          images {
            id
            title
            fluid(maxWidth: 1600, quality: 50) {
              ...GatsbyContentfulFluid_withWebp
              src
              aspectRatio
            }
            thumbnail: fluid(maxWidth: 300, quality: 20) {
              ...GatsbyContentfulFluid_withWebp
              src
              aspectRatio
            }
          }
        }
      }
    }
  }
`
export default GalleryTemplate
<commit_msg>Update gallery.js<commit_after>import React from 'react'
import { graphql } from 'gatsby'

import GalleryGrid from './../components/gallery/galleryGrid'
import ContentHead from './../components/general/contentHead'
import SEO from './../components/general/SEO'

const GalleryTemplate = ({ data }) => {
  const gallery = data.contentfulExtendedGallery
  const subGalleries = data.contentfulExtendedGallery.galleries
  return (
    <>
      <SEO title={gallery.title} image={gallery.shareImage} />
      <ContentHead
        displayExcerpt
        title={gallery.title}
        body={gallery.body}
        tags={gallery.tags}
      />
      {subGalleries.map((subGallery, index) => (

          {subGallery.__typename === 'ContentfulSubGallery' && (
            <GalleryGrid
              key={subGallery.id}
              slug={subGallery.slug}
              images={subGallery.images}
              title={subGallery.title}
              itemsPerRow={[3, 3, 5, 7]}
            />
          )}

      ))}
    </>
  )
}

export const query = graphql`
  query($slug: String!) {
    contentfulExtendedGallery(slug: { eq: $slug }) {
      title
      id
      slug
      metaDescription {
        internal {
          content
        }
      }
      publishDate(formatString: "MMMM DD, YYYY")
      publishDateISO: publishDate(formatString: "YYYY-MM-DD")
      tags {
        title
        id
        slug
      }
      shareImage {
        ogimg: resize(width: 1200) {
          src
          width
          height
        }
      }
      body {
        childMarkdownRemark {
          html
        }
      }
      galleries {
        __typename
        ... on ContentfulSubGallery {
          id
          slug
          title
          images {
            id
            title
            fluid(maxWidth: 1600, quality: 50) {
              ...GatsbyContentfulFluid_withWebp
              src
              aspectRatio
            }
            thumbnail: fluid(maxWidth: 300, quality: 20) {
              ...GatsbyContentfulFluid_withWebp
              src
              aspectRatio
            }
          }
        }
      }
    }
  }
`
export default GalleryTemplate
<|endoftext|>