<commit_before>/*
Copyright 2019 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 MatrixClientPeg from "./MatrixClientPeg";
import isIp from "is-ip";
import utils from 'matrix-js-sdk/lib/utils';

export const host = "matrix.to";
export const baseUrl = `https://${host}`;

// The maximum number of servers to pick when working out which servers
// to add to permalinks. The servers are appended as ?via=example.org
const MAX_SERVER_CANDIDATES = 3;

// Permalinks can have servers appended to them so that the user
// receiving them can have a fighting chance at joining the room.
// These servers are called "candidates" at this point because
// it is unclear whether they are going to be useful to actually
// join in the future.
//
// We pick 3 servers based on the following criteria:
//
//   Server 1: The highest power level user in the room, provided
//   they are at least PL 50. We don't calculate "what is a moderator"
//   here because it is less relevant for the vast majority of rooms.
//   We also want to ensure that we get an admin or high-ranking mod
//   as they are less likely to leave the room. If no user happens
//   to meet this criteria, we'll pick the most popular server in the
//   room.
//
//   Server 2: The next most popular server in the room (in user
//   distribution). This cannot be the same as Server 1. If no other
//   servers are available then we'll only return Server 1.
//
//   Server 3: The next most popular server by user distribution. This
//   has the same rules as Server 2, with the added exception that it
//   must be unique from Server 1 and 2.

// Rationale for popular servers: It's hard to get rid of people when
// they keep flocking in from a particular server. Sure, the server could
// be ACL'd in the future or for some reason be evicted from the room
// however an event like that is unlikely the larger the room gets. If
// the server is ACL'd at the time of generating the link however, we
// shouldn't pick them. We also don't pick IP addresses.

// Note: we don't pick the server the room was created on because the
// homeserver should already be using that server as a last ditch attempt
// and there's less of a guarantee that the server is a resident server.
// Instead, we actively figure out which servers are likely to be residents
// in the future and try to use those.

// Note: Users receiving permalinks that happen to have all 3 potential
// servers fail them (in terms of joining) are somewhat expected to hunt
// down the person who gave them the link to ask for a participating server.
// The receiving user can then manually append the known-good server to
// the list and magically have the link work.

export class RoomPermalinkCreator {
    constructor(room) {
        this._room = room;
        this._highestPlUserId = null;
        this._populationMap = null;
        this._bannedHostsRegexps = null;
        this._allowedHostsRegexps = null;
        this._serverCandidates = null;

        this.onMembership = this.onMembership.bind(this);
        this.onRoomState = this.onRoomState.bind(this);
    }

    load() {
        this._updateAllowedServers();
        this._updateHighestPlUser();
        this._updatePopulationMap();
        this._updateServerCandidates();
    }

    start() {
        this.load();
        this._room.on("RoomMember.membership", this.onMembership);
        this._room.on("RoomState.events", this.onRoomState);
    }

    stop() {
        this._room.removeListener("RoomMember.membership", this.onMembership);
        this._room.removeListener("RoomState.events", this.onRoomState);
    }

    forEvent(eventId) {
        const roomId = this._room.roomId;
        const permalinkBase = `${baseUrl}/#/${roomId}/${eventId}`;
        return `${permalinkBase}${encodeServerCandidates(this._serverCandidates)}`;
    }

    forRoom() {
        const roomId = this._room.roomId;
        const permalinkBase = `${baseUrl}/#/${roomId}`;
        return `${permalinkBase}${encodeServerCandidates(this._serverCandidates)}`;
    }

    onRoomState(event) {
        switch (event.getType()) {
            case "m.room.server_acl":
                this._updateAllowedServers();
                this._updateHighestPlUser();
                this._updatePopulationMap();
                this._updateServerCandidates();
                return;
            case "m.room.power_levels":
                this._updateHighestPlUser();
                this._updateServerCandidates();
                return;
        }
    }

    onMembership(evt, member, oldMembership) {
        const userId = member.userId;
        const membership = member.membership;
        const serverName = getServerName(userId);
        const hasJoined = oldMembership !== "join" && membership === "join";
        const hasLeft = oldMembership === "join" && membership !== "join";

        if (hasLeft) {
            this._populationMap[serverName]--;
        } else if (hasJoined) {
            this._populationMap[serverName]++;
        }

        this._updateHighestPlUser();
        this._updateServerCandidates();
    }

    _updateHighestPlUser() {
        const plEvent = this._room.currentState.getStateEvents("m.room.power_levels", "");
        const content = plEvent && plEvent.getContent();
        if (content) {
            const users = content.users;
            if (users) {
                const entries = Object.entries(users);
                const allowedEntries = entries.filter(([userId]) => {
                    const member = this._room.getMember(userId);
                    if (!member || member.membership !== "join") {
                        return false;
                    }
                    const serverName = getServerName(userId);
                    return !isHostnameIpAddress(serverName) &&
                           !isHostInRegex(serverName, this._bannedHostsRegexps) &&
                           isHostInRegex(serverName, this._allowedHostsRegexps);
                });
                const maxEntry = allowedEntries.reduce((max, entry) => {
                    return (entry[1] > max[1]) ? entry : max;
                }, [null, 0]);
                const [userId, powerLevel] = maxEntry;
                // object wasn't empty, and max entry wasn't a demotion from the default
                if (userId !== null && powerLevel >= 50) {
                    this._highestPlUserId = userId;
                    return;
                }
            }
        }
        this._highestPlUserId = null;
    }

    _updateAllowedServers() {
        const bannedHostsRegexps = [];
        let allowedHostsRegexps = [new RegExp(".*")]; // default allow everyone
        if (this._room.currentState) {
            const aclEvent = this._room.currentState.getStateEvents("m.room.server_acl", "");
            if (aclEvent && aclEvent.getContent()) {
                const getRegex = (hostname) => new RegExp("^" + utils.globToRegexp(hostname, false) + "$");

                const denied = aclEvent.getContent().deny || [];
                denied.forEach(h => bannedHostsRegexps.push(getRegex(h)));

                const allowed = aclEvent.getContent().allow || [];
                allowedHostsRegexps = []; // we don't want to use the default rule here
                allowed.forEach(h => allowedHostsRegexps.push(getRegex(h)));
            }
        }
        this._bannedHostsRegexps = bannedHostsRegexps;
        this._allowedHostsRegexps = allowedHostsRegexps;
    }

    _updatePopulationMap() {
        const populationMap: {[server:string]:number} = {};
        for (const member of this._room.getJoinedMembers()) {
            const serverName = getServerName(member.userId);
            if (!populationMap[serverName]) {
                populationMap[serverName] = 0;
            }
            populationMap[serverName]++;
        }
        this._populationMap = populationMap;
    }

    _updateServerCandidates() {
        let candidates = [];
        if (this._highestPlUserId) {
            candidates.push(getServerName(this._highestPlUserId));
        }

        const serversByPopulation = Object.keys(this._populationMap)
            .sort((a, b) => this._populationMap[b] - this._populationMap[a])
            .filter(a => {
                return !candidates.includes(a) &&
                       !isHostnameIpAddress(a) &&
                       !isHostInRegex(a, this._bannedHostsRegexps) &&
                       isHostInRegex(a, this._allowedHostsRegexps);
            });

        const remainingServers = serversByPopulation.slice(0, MAX_SERVER_CANDIDATES - candidates.length);
        candidates = candidates.concat(remainingServers);

        this._serverCandidates = candidates;
    }
}

export function makeUserPermalink(userId) {
    return `${baseUrl}/#/${userId}`;
}

export function makeRoomPermalink(roomId) {
    const permalinkBase = `${baseUrl}/#/${roomId}`;

    // If the roomId isn't actually a room ID, don't try to list the servers.
    // Aliases are already routable, and don't need extra information.
    if (roomId[0] !== '!') return permalinkBase;

    const client = MatrixClientPeg.get();
    const room = client.getRoom(roomId);
    const permalinkCreator = new RoomPermalinkCreator(room);
    permalinkCreator.load();
    return permalinkCreator.forRoom();
}

export function makeGroupPermalink(groupId) {
    return `${baseUrl}/#/${groupId}`;
}

export function encodeServerCandidates(candidates) {
    if (!candidates || candidates.length === 0) return '';
    return `?via=${candidates.map(c => encodeURIComponent(c)).join("&via=")}`;
}

function getServerName(userId) {
    return userId.split(":").splice(1).join(":");
}

function getHostnameFromMatrixDomain(domain) {
    if (!domain) return null;
    return new URL(`https://${domain}`).hostname;
}

function isHostInRegex(hostname, regexps) {
    hostname = getHostnameFromMatrixDomain(hostname);
    if (!hostname) return true; // assumed
    if (regexps.length > 0 && !regexps[0].test) throw new Error(regexps[0]);

    return regexps.filter(h => h.test(hostname)).length > 0;
}

function isHostnameIpAddress(hostname) {
    hostname = getHostnameFromMatrixDomain(hostname);
    if (!hostname) return false;

    // is-ip doesn't want IPv6 addresses surrounded by brackets, so
    // take them off.
    if (hostname.startsWith("[") && hostname.endsWith("]")) {
        hostname = hostname.substring(1, hostname.length - 1);
    }

    return isIp(hostname);
}
<commit_msg>check room exists<commit_after>/*
Copyright 2019 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 MatrixClientPeg from "./MatrixClientPeg";
import isIp from "is-ip";
import utils from 'matrix-js-sdk/lib/utils';

export const host = "matrix.to";
export const baseUrl = `https://${host}`;

// The maximum number of servers to pick when working out which servers
// to add to permalinks. The servers are appended as ?via=example.org
const MAX_SERVER_CANDIDATES = 3;

// Permalinks can have servers appended to them so that the user
// receiving them can have a fighting chance at joining the room.
// These servers are called "candidates" at this point because
// it is unclear whether they are going to be useful to actually
// join in the future.
//
// We pick 3 servers based on the following criteria:
//
//   Server 1: The highest power level user in the room, provided
//   they are at least PL 50. We don't calculate "what is a moderator"
//   here because it is less relevant for the vast majority of rooms.
//   We also want to ensure that we get an admin or high-ranking mod
//   as they are less likely to leave the room. If no user happens
//   to meet this criteria, we'll pick the most popular server in the
//   room.
//
//   Server 2: The next most popular server in the room (in user
//   distribution). This cannot be the same as Server 1. If no other
//   servers are available then we'll only return Server 1.
//
//   Server 3: The next most popular server by user distribution. This
//   has the same rules as Server 2, with the added exception that it
//   must be unique from Server 1 and 2.

// Rationale for popular servers: It's hard to get rid of people when
// they keep flocking in from a particular server. Sure, the server could
// be ACL'd in the future or for some reason be evicted from the room
// however an event like that is unlikely the larger the room gets. If
// the server is ACL'd at the time of generating the link however, we
// shouldn't pick them. We also don't pick IP addresses.

// Note: we don't pick the server the room was created on because the
// homeserver should already be using that server as a last ditch attempt
// and there's less of a guarantee that the server is a resident server.
// Instead, we actively figure out which servers are likely to be residents
// in the future and try to use those.

// Note: Users receiving permalinks that happen to have all 3 potential
// servers fail them (in terms of joining) are somewhat expected to hunt
// down the person who gave them the link to ask for a participating server.
// The receiving user can then manually append the known-good server to
// the list and magically have the link work.

export class RoomPermalinkCreator {
    constructor(room) {
        this._room = room;
        this._highestPlUserId = null;
        this._populationMap = null;
        this._bannedHostsRegexps = null;
        this._allowedHostsRegexps = null;
        this._serverCandidates = null;

        this.onMembership = this.onMembership.bind(this);
        this.onRoomState = this.onRoomState.bind(this);
    }

    load() {
        this._updateAllowedServers();
        this._updateHighestPlUser();
        this._updatePopulationMap();
        this._updateServerCandidates();
    }

    start() {
        this.load();
        this._room.on("RoomMember.membership", this.onMembership);
        this._room.on("RoomState.events", this.onRoomState);
    }

    stop() {
        this._room.removeListener("RoomMember.membership", this.onMembership);
        this._room.removeListener("RoomState.events", this.onRoomState);
    }

    forEvent(eventId) {
        const roomId = this._room.roomId;
        const permalinkBase = `${baseUrl}/#/${roomId}/${eventId}`;
        return `${permalinkBase}${encodeServerCandidates(this._serverCandidates)}`;
    }

    forRoom() {
        const roomId = this._room.roomId;
        const permalinkBase = `${baseUrl}/#/${roomId}`;
        return `${permalinkBase}${encodeServerCandidates(this._serverCandidates)}`;
    }

    onRoomState(event) {
        switch (event.getType()) {
            case "m.room.server_acl":
                this._updateAllowedServers();
                this._updateHighestPlUser();
                this._updatePopulationMap();
                this._updateServerCandidates();
                return;
            case "m.room.power_levels":
                this._updateHighestPlUser();
                this._updateServerCandidates();
                return;
        }
    }

    onMembership(evt, member, oldMembership) {
        const userId = member.userId;
        const membership = member.membership;
        const serverName = getServerName(userId);
        const hasJoined = oldMembership !== "join" && membership === "join";
        const hasLeft = oldMembership === "join" && membership !== "join";

        if (hasLeft) {
            this._populationMap[serverName]--;
        } else if (hasJoined) {
            this._populationMap[serverName]++;
        }

        this._updateHighestPlUser();
        this._updateServerCandidates();
    }

    _updateHighestPlUser() {
        const plEvent = this._room.currentState.getStateEvents("m.room.power_levels", "");
        const content = plEvent && plEvent.getContent();
        if (content) {
            const users = content.users;
            if (users) {
                const entries = Object.entries(users);
                const allowedEntries = entries.filter(([userId]) => {
                    const member = this._room.getMember(userId);
                    if (!member || member.membership !== "join") {
                        return false;
                    }
                    const serverName = getServerName(userId);
                    return !isHostnameIpAddress(serverName) &&
                           !isHostInRegex(serverName, this._bannedHostsRegexps) &&
                           isHostInRegex(serverName, this._allowedHostsRegexps);
                });
                const maxEntry = allowedEntries.reduce((max, entry) => {
                    return (entry[1] > max[1]) ? entry : max;
                }, [null, 0]);
                const [userId, powerLevel] = maxEntry;
                // object wasn't empty, and max entry wasn't a demotion from the default
                if (userId !== null && powerLevel >= 50) {
                    this._highestPlUserId = userId;
                    return;
                }
            }
        }
        this._highestPlUserId = null;
    }

    _updateAllowedServers() {
        const bannedHostsRegexps = [];
        let allowedHostsRegexps = [new RegExp(".*")]; // default allow everyone
        if (this._room.currentState) {
            const aclEvent = this._room.currentState.getStateEvents("m.room.server_acl", "");
            if (aclEvent && aclEvent.getContent()) {
                const getRegex = (hostname) => new RegExp("^" + utils.globToRegexp(hostname, false) + "$");

                const denied = aclEvent.getContent().deny || [];
                denied.forEach(h => bannedHostsRegexps.push(getRegex(h)));

                const allowed = aclEvent.getContent().allow || [];
                allowedHostsRegexps = []; // we don't want to use the default rule here
                allowed.forEach(h => allowedHostsRegexps.push(getRegex(h)));
            }
        }
        this._bannedHostsRegexps = bannedHostsRegexps;
        this._allowedHostsRegexps = allowedHostsRegexps;
    }

    _updatePopulationMap() {
        const populationMap: {[server:string]:number} = {};
        for (const member of this._room.getJoinedMembers()) {
            const serverName = getServerName(member.userId);
            if (!populationMap[serverName]) {
                populationMap[serverName] = 0;
            }
            populationMap[serverName]++;
        }
        this._populationMap = populationMap;
    }

    _updateServerCandidates() {
        let candidates = [];
        if (this._highestPlUserId) {
            candidates.push(getServerName(this._highestPlUserId));
        }

        const serversByPopulation = Object.keys(this._populationMap)
            .sort((a, b) => this._populationMap[b] - this._populationMap[a])
            .filter(a => {
                return !candidates.includes(a) &&
                       !isHostnameIpAddress(a) &&
                       !isHostInRegex(a, this._bannedHostsRegexps) &&
                       isHostInRegex(a, this._allowedHostsRegexps);
            });

        const remainingServers = serversByPopulation.slice(0, MAX_SERVER_CANDIDATES - candidates.length);
        candidates = candidates.concat(remainingServers);

        this._serverCandidates = candidates;
    }
}

export function makeUserPermalink(userId) {
    return `${baseUrl}/#/${userId}`;
}

export function makeRoomPermalink(roomId) {
    const permalinkBase = `${baseUrl}/#/${roomId}`;

    // If the roomId isn't actually a room ID, don't try to list the servers.
    // Aliases are already routable, and don't need extra information.
    if (roomId[0] !== '!') return permalinkBase;

    const client = MatrixClientPeg.get();
    const room = client.getRoom(roomId);
    if (!room) {
        return permalinkBase;
    }
    const permalinkCreator = new RoomPermalinkCreator(room);
    permalinkCreator.load();
    return permalinkCreator.forRoom();
}

export function makeGroupPermalink(groupId) {
    return `${baseUrl}/#/${groupId}`;
}

export function encodeServerCandidates(candidates) {
    if (!candidates || candidates.length === 0) return '';
    return `?via=${candidates.map(c => encodeURIComponent(c)).join("&via=")}`;
}

function getServerName(userId) {
    return userId.split(":").splice(1).join(":");
}

function getHostnameFromMatrixDomain(domain) {
    if (!domain) return null;
    return new URL(`https://${domain}`).hostname;
}

function isHostInRegex(hostname, regexps) {
    hostname = getHostnameFromMatrixDomain(hostname);
    if (!hostname) return true; // assumed
    if (regexps.length > 0 && !regexps[0].test) throw new Error(regexps[0]);

    return regexps.filter(h => h.test(hostname)).length > 0;
}

function isHostnameIpAddress(hostname) {
    hostname = getHostnameFromMatrixDomain(hostname);
    if (!hostname) return false;

    // is-ip doesn't want IPv6 addresses surrounded by brackets, so
    // take them off.
    if (hostname.startsWith("[") && hostname.endsWith("]")) {
        hostname = hostname.substring(1, hostname.length - 1);
    }

    return isIp(hostname);
}
<|endoftext|>


<commit_before>class Mavigator {
    constructor(selector = 'html', options) {
        this.selector = selector;
        this.options = this.mergeOptions(Mavigator.defaults(), options);
    }

    static mark(selector, options) {
        (new Mavigator(selector, options)).init();
    }

    static defaults() {
        return {
            className: 'active',
            classToParent: false,
            uri: window.location.pathname,
            warn: false
        };
    }

    mergeOptions(source, override) {
        for (var key in override) {
            if (source.hasOwnProperty(key)) {
                source[key] = override[key];
            }
        }

        return source;
    }

    init() {
        let nodes = this.getNodesToMark();

        if ( ! nodes || ! nodes.length) {
            if (this.options.warn) {
                console.warn(`No link to mark was found for the given URI [${this.options.uri}]`);
            }
            return;
        }

        for (let i = 0; i < nodes.length; i++) {
            let realNode = this.options.classToParent ? nodes[i].parentNode : nodes[i];

            this.addClassTo(realNode);
        }
    }

    getNodesToMark() {
        let nodes = [];
        let sets = this.sets();

        if ( ! sets.length) return;

        for (let i = 0; i < sets.length; i++) {
            nodes = nodes.concat(this.getMarkableNodesFrom(sets[i]));
        }

        return nodes;
    }

    sets() {
        this.validateSelector();

        return document.querySelectorAll(this.selector);
    }

    validateSelector() {
        if ( ! this.selector.length) {
            throw new TypeError('The provided selector is empty.');
        }

        if (typeof this.selector === 'object') {
            throw new TypeError('A selector must be a string.');
        }
    }

    getMarkableNodesFrom(set) {
        let selector = 'a';
        let links = set.querySelectorAll(selector);
        links = [].slice.call(links);

        return links.filter(link => link.pathname === this.options.uri);
    }

    addClassTo(node) {
        if (node.classList) {
            node.classList.add(this.options.className);
        } else {
            node.className += ' ' + className;
        }
    }
}

export default Mavigator;<commit_msg>fix an unresolved variable reference<commit_after>class Mavigator {
    constructor(selector = 'html', options) {
        this.selector = selector;
        this.options = this.mergeOptions(Mavigator.defaults(), options);
    }

    static mark(selector, options) {
        (new Mavigator(selector, options)).init();
    }

    static defaults() {
        return {
            className: 'active',
            classToParent: false,
            uri: window.location.pathname,
            warn: false
        };
    }

    mergeOptions(source, override) {
        for (var key in override) {
            if (source.hasOwnProperty(key)) {
                source[key] = override[key];
            }
        }

        return source;
    }

    init() {
        let nodes = this.getNodesToMark();

        if ( ! nodes || ! nodes.length) {
            if (this.options.warn) {
                console.warn(`No link to mark was found for the given URI [${this.options.uri}]`);
            }
            return;
        }

        for (let i = 0; i < nodes.length; i++) {
            let realNode = this.options.classToParent ? nodes[i].parentNode : nodes[i];

            this.addClassTo(realNode);
        }
    }

    getNodesToMark() {
        let nodes = [];
        let sets = this.sets();

        if ( ! sets.length) return;

        for (let i = 0; i < sets.length; i++) {
            nodes = nodes.concat(this.getMarkableNodesFrom(sets[i]));
        }

        return nodes;
    }

    sets() {
        this.validateSelector();

        return document.querySelectorAll(this.selector);
    }

    validateSelector() {
        if ( ! this.selector.length) {
            throw new TypeError('The provided selector is empty.');
        }

        if (typeof this.selector === 'object') {
            throw new TypeError('A selector must be a string.');
        }
    }

    getMarkableNodesFrom(set) {
        let selector = 'a';
        let links = set.querySelectorAll(selector);
        links = [].slice.call(links);

        return links.filter(link => link.pathname === this.options.uri);
    }

    addClassTo(node) {
        if (node.classList) {
            node.classList.add(this.options.className);
        } else {
            node.className += ' ' + this.options.className;
        }
    }
}

export default Mavigator;<|endoftext|>


<commit_before>/// FIXME replace while(hasnext) with do-while(hasnext) ?

(function($) {

var lib = $.mediawiki = $.mediawiki || {};

lib.tokenize = function tokenize(str, callback) {
	var g_curr_pos = 0; // current possition in stream
	var g_last_pos = 0; // last position were token was found

	function peek() {
		return str.charAt(g_curr_pos);
	}

	function next() {
		g_curr_pos++;
	}

	function hasnext() {
		return g_curr_pos < str.length;
	}

	function add_token(token, s_curr_pos) {
		callback(token);
		g_last_pos = s_curr_pos;
	}

	function handle_newline() {
		var newline = 0;

		var was_r = false;
		while(hasnext()) {
			var ch = peek();
			if (ch === "\n") {
				next();
				newline++;
				was_r = false;
			} else if (ch === "\r") {
				next(); //always go to the next char to avoid second call of handle_p()
				if (!was_r) {
					was_r = true;
				} else {
					break;
				}
			} else {
				break;
			}
		}

		return newline;
	}

	function handle_text(s_curr_pos) {
		if (g_last_pos !== s_curr_pos) {
			add_token(["t", str.substring(g_last_pos, s_curr_pos)], s_curr_pos);
		}
	}

	function handle_heading(s_curr_pos, newline) {
		var open_tag = 0;
		var close_tag = 0;

		// calculate tag level, always eat control symbols to avoid second call of handler
		while(hasnext()) {
			if (peek() === "=") {
				open_tag++;
				next();
			} else {
				break;
			}
		}

		// invalid tag length or not at start of the line
		if (!newline || open_tag < 1 || open_tag > 6) {
			return 0;
		}

		// header body and ending tag
		while(hasnext()) {
			var ch = peek();
			if (ch === "=") {
				close_tag++;
				next();
			} else if (ch === "\r" || ch === "\n") {
				break;
			} else {
				close_tag = 0;
				next();
			}
		}

		// no close tag
		if (!close_tag) {
			return 0;
		}

		var e_curr_pos = g_curr_pos; // end position of the text

		newline = handle_newline(); // skip empty lines
		if (newline) {
			var level = Math.min(open_tag, close_tag);
			handle_text(s_curr_pos);
			add_token(["h", level, str.substring(g_last_pos + level, e_curr_pos - level) ], g_curr_pos);
		}

		return newline;
	}

	function handle_paragraph(s_curr_pos, newline) {
		// calculate newlines only if it was called after non-newline character
		newline = newline || handle_newline();

		if (newline > 1) {
			handle_text(s_curr_pos);
			add_token(["p"], g_curr_pos);
			if (newline > 2) {
				add_token(["b", newline - 2], g_curr_pos);
			}
		}
		return newline;
	}

	function handle_list(s_curr_pos, newline) {
		var ch;
		// always eat control symbols to avoid second call of handler
		while(hasnext()) {
			ch = peek();
			if (ch === '#' || ch === '*') {
				next();
			} else {
				break;
			}
		}
		var l_curr_pos = g_curr_pos; // end position of the markers

		// no at start of the line
		if (!newline) {
			return 0;
		}

		while(hasnext()) {
			ch = peek();
			if (ch === "\n" || ch === "\r") {
				break;
			} else {
				next();
			}
		}
		var e_curr_pos = g_curr_pos; // end position of the text

		newline = handle_newline(); //skip empty lines
		if (newline) {
			handle_text(s_curr_pos);
			add_token([str.substring(g_last_pos, l_curr_pos), str.substring(l_curr_pos, e_curr_pos) ], g_curr_pos);
			handle_paragraph(g_curr_pos, newline);
		}

		return newline;
	}

	function handle_emphasize(s_curr_pos, newline) {
		var e_level = 0;
		while(hasnext()) {
			if (peek() === "'") {
				e_level++;
				next();
			} else {
				break;
			}
		}

		switch(e_level) {
			case 1:
				break;
			case 2:
			case 3:
			case 5:
				handle_text(s_curr_pos);
				add_token(["'", e_level], g_curr_pos);
				break;
			case 4:
				handle_text(s_curr_pos);
				add_token(["'", 3], g_curr_pos-1);
				break;
			default: // > 5
				handle_text(s_curr_pos);
				add_token(["'", 5], g_curr_pos - e_level + 5);
				break;
		}
		return 0;
	}

	function handle_default(s_curr_pos, newline) {
		next();
		return 0;
	}

	var newline = 1;
	while(hasnext()) {
		switch(peek()) {
			case "=":
				newline = handle_heading(g_curr_pos, newline);
				break;
			case "\r":
			case "\n":
				newline = handle_paragraph(g_curr_pos, 0);
				break;
			case "#":
			case "*":
				newline = handle_list(g_curr_pos, newline);
				break;
			case "'":
				newline = handle_emphasize(g_curr_pos, newline);
				break;
			default:
				newline = handle_default(g_curr_pos, newline);
				break;
		}
	}
	handle_text(g_curr_pos);
};

lib.autocorrect = function(callback) {
	var g_stack = new lib.utils.DoubleLinkedList(); // stack of disbalanced items
	var g_tokens = new lib.utils.DoubleLinkedList(); // head of list of unprocessing items
	var g_context = null; // current global context

	function dump_tokens() {
		// automatically drop last unclosed tokens
		// automatically close other unclosed tokens
		while(g_stack.length) {
			var last = g_stack.pop();
			if (!last.next) {
				g_tokens.pop();
			} else {
				var l_tag = last.data[1];
				if (l_tag === 5) {
					last.data = ["'", 3];
					g_tokens.push(["'", 2], last);
					g_tokens.push(["'", 2]);
					g_tokens.push(["'", 3]);
				} else {
					g_tokens.push(["'", l_tag]);
				}
			}
		}

		g_tokens.foreach(callback);
		g_tokens = new lib.utils.DoubleLinkedList();
	}

	function handle_emphasize(token) {
		if (!g_stack.length) { // no unbalanced items
			dump_tokens();
			g_stack.push(g_tokens.push(token));
			return;
		}

		var curr_tag = token[1];
		var prev = g_stack.peek(); // last unbalanced item
		var prev_tag = prev.data[1];

		if (prev_tag === curr_tag) {
			if (prev_tag === 5) {
				prev.data = ["'", 3];
				g_tokens.push(["'", 2], prev);
				g_tokens.push(["'", 2]);
				g_tokens.push(["'", 3]);
			} else {
				g_tokens.push(token);
			}
			g_stack.pop();
		} else {
			if (prev_tag === 5) {
				prev.data = ["'", 5 - curr_tag];
				g_tokens.push(["'", curr_tag], prev);
				g_tokens.push(["'", curr_tag]);
			} else if (curr_tag === 5) {
				g_tokens.push(["'", prev_tag]);
				g_stack.pop();
				var new_tag = 5 - prev_tag;
				var new_curr = g_tokens.push(["'", new_tag]);
				if (!g_stack.length || g_stack.peek().data[1] !== new_tag) {
					g_stack.push(new_curr);
				} else {
					g_stack.pop();
				}
			} else if (g_stack.length === 1) {
				g_stack.push(g_tokens.push(token));
			} else {
				g_tokens.push(["'", prev_tag]);
				g_stack.pop();
				g_tokens.push(token);
				g_stack.pop();
				g_stack.push(g_tokens.push(["'", prev_tag]));
			}
		}
	}

	return function(token) {
		if (token == null) {
			dump_tokens();
			return;
		}
		var token_type = token[0][0];
		switch(token_type) {
			// block tokens
			case "h":
			case "p":
			case "*":
			case "#":
				dump_tokens();
				if (token_type === "p") {
					g_context = null;
				} else {
					g_context = token;
					callback(g_context);
				}
				break;
			// inline tokens
			case "b":
			case "t":
			case "'":
				if (!g_context || g_context[0] !== "p") {
					g_context = ["p"];
					callback(g_context);
				}
				if (token_type === "'") {
					handle_emphasize(token);
				} else if (g_tokens.length) {
					g_tokens.push(token);
				} else {
					callback(token);
				}
				break;
			default:
				throw new Error("unsupported token");
		}
	};
};

lib.format = function (text) {
	var g_context = []; // current global context, paragraph, ordered or unordered list,
	var g_result = []; // output

	function close_ctx(depth) {
		while(g_context.length && (depth == null || depth--)) {
			var ctx = g_context.pop();
			g_result.push("</" + ctx[0] + ">");
		}
	}

	function open_ctx(ctx) {
		g_context.push(ctx);
		g_result.push("<" + ctx[0] + ">");
	}

	function curr_ctx() {
		return g_context.length?g_context[g_context.length - 1]:[null];
	}

	function handle_paragraph(s_token) {
		close_ctx(null);
		open_ctx(["p"]);
	}

	function handle_text(s_token) {
		g_result.push(s_token[1]);
	}

	function handle_br(s_token) {
		g_result.push(new Array(s_token[1] + 1).join("
"));
	}

	function handle_heading(s_token) {
		close_ctx(null);
		g_result.push(["<h",s_token[1],">",$.trim(s_token[2]),"</h",s_token[1],">"].join(""));
	}

	function handle_list(s_token) {
		var l_type = s_token[0];
		var l_text = s_token[1];
		var level;

		var ctx = curr_ctx();
		if (ctx[0] === "li") {
			var c_type = ctx[1];
			var common_prefix = 0;
			var common_length = Math.min(c_type.length, l_type.length);

			for(common_prefix = 0; common_prefix < common_length; ++common_prefix) {
				if (c_type[common_prefix] !== l_type[common_prefix]) {
					break;
				}
			}
			for(level=c_type.length; level > common_prefix; --level) {
				close_ctx(2);
			}
			if (l_type.length <= common_prefix) {
				close_ctx(1);
			}
			for(level=common_prefix; level < l_type.length; ++level) {
				open_ctx([(l_type[level] === "*")?"ul":"ol"]);
				open_ctx(["li", l_type]);
			}
			if (l_type.length === common_prefix) {
				open_ctx(["li", l_type]);
			}
		} else {
			close_ctx(null);
			for(level=0; level < l_type.length; ++level) {
				open_ctx([(l_type[level] === "*")?"ul":"ol"]);
				open_ctx(["li", l_type]);
			}
		}

		g_result.push($.trim(l_text));
	}

	function handle_emphasize(s_token) {
		var e_tag;
		switch(s_token[1]) {
			case 2:
				e_tag = "em";
				break;
			case 3:
				e_tag = "strong";
				break;
			default:
				return; // ignore invalid emphasize tags
		}

		if (e_tag === curr_ctx()[0]) {
			close_ctx(1);
		} else {
			open_ctx([e_tag]);
		}
	}

	var corrector = lib.autocorrect(function(token) {
		switch (token[0][0]) {
			case "p":
				handle_paragraph(token);
				break;
			case "t":
				handle_text(token);
				break;
			case "h":
				handle_heading(token);
				break;
			case "b":
				handle_br(token);
				break;
			case "*":
			case "#":
				handle_list(token);
				break;
			case "'":
				handle_emphasize(token);
				break;
			default:
				throw new Error("invalid token");
		}
	});
	lib.tokenize(text, corrector);
	corrector(null); // flush buffers
	close_ctx(null); // finally close all active contexts

	return g_result.join("");
};

}(jQuery));
<commit_msg>enable strict mode, use new linked lists<commit_after>/// FIXME replace while(hasnext) with do-while(hasnext) ?

(function($) {
"use strict";

var lib = $.mediawiki = $.mediawiki || {};

lib.tokenize = function tokenize(str, callback) {
	var g_curr_pos = 0; // current possition in stream
	var g_last_pos = 0; // last position were token was found

	function peek() {
		return str.charAt(g_curr_pos);
	}

	function next() {
		g_curr_pos++;
	}

	function hasnext() {
		return g_curr_pos < str.length;
	}

	function add_token(token, s_curr_pos) {
		callback(token);
		g_last_pos = s_curr_pos;
	}

	function handle_newline() {
		var newline = 0;

		var was_r = false;
		while(hasnext()) {
			var ch = peek();
			if (ch === "\n") {
				next();
				newline++;
				was_r = false;
			} else if (ch === "\r") {
				next(); //always go to the next char to avoid second call of handle_p()
				if (!was_r) {
					was_r = true;
				} else {
					break;
				}
			} else {
				break;
			}
		}

		return newline;
	}

	function handle_text(s_curr_pos) {
		if (g_last_pos !== s_curr_pos) {
			add_token(["t", str.substring(g_last_pos, s_curr_pos)], s_curr_pos);
		}
	}

	function handle_heading(s_curr_pos, newline) {
		var open_tag = 0;
		var close_tag = 0;

		// calculate tag level, always eat control symbols to avoid second call of handler
		while(hasnext()) {
			if (peek() === "=") {
				open_tag++;
				next();
			} else {
				break;
			}
		}

		// invalid tag length or not at start of the line
		if (!newline || open_tag < 1 || open_tag > 6) {
			return 0;
		}

		// header body and ending tag
		while(hasnext()) {
			var ch = peek();
			if (ch === "=") {
				close_tag++;
				next();
			} else if (ch === "\r" || ch === "\n") {
				break;
			} else {
				close_tag = 0;
				next();
			}
		}

		// no close tag
		if (!close_tag) {
			return 0;
		}

		var e_curr_pos = g_curr_pos; // end position of the text

		newline = handle_newline(); // skip empty lines
		if (newline) {
			var level = Math.min(open_tag, close_tag);
			handle_text(s_curr_pos);
			add_token(["h", level, str.substring(g_last_pos + level, e_curr_pos - level) ], g_curr_pos);
		}

		return newline;
	}

	function handle_paragraph(s_curr_pos, newline) {
		// calculate newlines only if it was called after non-newline character
		newline = newline || handle_newline();

		if (newline > 1) {
			handle_text(s_curr_pos);
			add_token(["p"], g_curr_pos);
			if (newline > 2) {
				add_token(["b", newline - 2], g_curr_pos);
			}
		}
		return newline;
	}

	function handle_list(s_curr_pos, newline) {
		var ch;
		// always eat control symbols to avoid second call of handler
		while(hasnext()) {
			ch = peek();
			if (ch === '#' || ch === '*') {
				next();
			} else {
				break;
			}
		}
		var l_curr_pos = g_curr_pos; // end position of the markers

		// no at start of the line
		if (!newline) {
			return 0;
		}

		while(hasnext()) {
			ch = peek();
			if (ch === "\n" || ch === "\r") {
				break;
			} else {
				next();
			}
		}
		var e_curr_pos = g_curr_pos; // end position of the text

		newline = handle_newline(); //skip empty lines
		if (newline) {
			handle_text(s_curr_pos);
			add_token([str.substring(g_last_pos, l_curr_pos), str.substring(l_curr_pos, e_curr_pos) ], g_curr_pos);
			handle_paragraph(g_curr_pos, newline);
		}

		return newline;
	}

	function handle_emphasize(s_curr_pos, newline) {
		var e_level = 0;
		while(hasnext()) {
			if (peek() === "'") {
				e_level++;
				next();
			} else {
				break;
			}
		}

		switch(e_level) {
			case 1:
				break;
			case 2:
			case 3:
			case 5:
				handle_text(s_curr_pos);
				add_token(["'", e_level], g_curr_pos);
				break;
			case 4:
				handle_text(s_curr_pos);
				add_token(["'", 3], g_curr_pos-1);
				break;
			default: // > 5
				handle_text(s_curr_pos);
				add_token(["'", 5], g_curr_pos - e_level + 5);
				break;
		}
		return 0;
	}

	function handle_default(s_curr_pos, newline) {
		next();
		return 0;
	}

	var newline = 1;
	while(hasnext()) {
		switch(peek()) {
			case "=":
				newline = handle_heading(g_curr_pos, newline);
				break;
			case "\r":
			case "\n":
				newline = handle_paragraph(g_curr_pos, 0);
				break;
			case "#":
			case "*":
				newline = handle_list(g_curr_pos, newline);
				break;
			case "'":
				newline = handle_emphasize(g_curr_pos, newline);
				break;
			default:
				newline = handle_default(g_curr_pos, newline);
				break;
		}
	}
	handle_text(g_curr_pos);
};

lib.autocorrect = function(callback) {
	var g_stack = lib.utils.linkedlist(); // stack of disbalanced items
	var g_tokens = lib.utils.linkedlist(); // head of list of unprocessing items
	var g_context = null; // current global context

	function dump_tokens() {
		// automatically drop last unclosed tokens
		// automatically close other unclosed tokens
		while(g_stack.length) {
			var last = g_stack.pop();
			if (g_tokens.is_last(last)) {
				g_tokens.pop();
			} else {
				var l_tag = last.data[1];
				if (l_tag === 5) {
					last.data = ["'", 3];
					g_tokens.push(["'", 2], last);
					g_tokens.push(["'", 2]);
					g_tokens.push(["'", 3]);
				} else {
					g_tokens.push(["'", l_tag]);
				}
			}
		}

		g_tokens.foreach(callback);
		g_tokens = lib.utils.linkedlist();
	}

	function handle_emphasize(token) {
		if (!g_stack.length) { // no unbalanced items
			dump_tokens();
			g_stack.push(g_tokens.push(token));
			return;
		}

		var curr_tag = token[1];
		var prev = g_stack.peek(); // last unbalanced item
		var prev_tag = prev.data[1];

		if (prev_tag === curr_tag) {
			if (prev_tag === 5) {
				prev.data = ["'", 3];
				g_tokens.push(["'", 2], prev);
				g_tokens.push(["'", 2]);
				g_tokens.push(["'", 3]);
			} else {
				g_tokens.push(token);
			}
			g_stack.pop();
		} else {
			if (prev_tag === 5) {
				prev.data = ["'", 5 - curr_tag];
				g_tokens.push(["'", curr_tag], prev);
				g_tokens.push(["'", curr_tag]);
			} else if (curr_tag === 5) {
				g_tokens.push(["'", prev_tag]);
				g_stack.pop();
				var new_tag = 5 - prev_tag;
				var new_curr = g_tokens.push(["'", new_tag]);
				if (!g_stack.length || g_stack.peek().data[1] !== new_tag) {
					g_stack.push(new_curr);
				} else {
					g_stack.pop();
				}
			} else if (g_stack.length === 1) {
				g_stack.push(g_tokens.push(token));
			} else {
				g_tokens.push(["'", prev_tag]);
				g_stack.pop();
				g_tokens.push(token);
				g_stack.pop();
				g_stack.push(g_tokens.push(["'", prev_tag]));
			}
		}
	}

	return function(token) {
		if (token == null) {
			dump_tokens();
			return;
		}
		var token_type = token[0][0];
		switch(token_type) {
			// block tokens
			case "h":
			case "p":
			case "*":
			case "#":
				dump_tokens();
				if (token_type === "p") {
					g_context = null;
				} else {
					g_context = token;
					callback(g_context);
				}
				break;
			// inline tokens
			case "b":
			case "t":
			case "'":
				if (!g_context || g_context[0] !== "p") {
					g_context = ["p"];
					callback(g_context);
				}
				if (token_type === "'") {
					handle_emphasize(token);
				} else if (g_tokens.length) {
					g_tokens.push(token);
				} else {
					callback(token);
				}
				break;
			default:
				throw new Error("unsupported token");
		}
	};
};

lib.format = function (text) {
	var g_context = []; // current global context, paragraph, ordered or unordered list,
	var g_result = []; // output

	function close_ctx(depth) {
		while(g_context.length && (depth == null || depth--)) {
			var ctx = g_context.pop();
			g_result.push("</" + ctx[0] + ">");
		}
	}

	function open_ctx(ctx) {
		g_context.push(ctx);
		g_result.push("<" + ctx[0] + ">");
	}

	function curr_ctx() {
		return g_context.length?g_context[g_context.length - 1]:[null];
	}

	function handle_paragraph(s_token) {
		close_ctx(null);
		open_ctx(["p"]);
	}

	function handle_text(s_token) {
		g_result.push(s_token[1]);
	}

	function handle_br(s_token) {
		g_result.push(new Array(s_token[1] + 1).join("
"));
	}

	function handle_heading(s_token) {
		close_ctx(null);
		g_result.push(["<h",s_token[1],">",$.trim(s_token[2]),"</h",s_token[1],">"].join(""));
	}

	function handle_list(s_token) {
		var l_type = s_token[0];
		var l_text = s_token[1];
		var level;

		var ctx = curr_ctx();
		if (ctx[0] === "li") {
			var c_type = ctx[1];
			var common_prefix = 0;
			var common_length = Math.min(c_type.length, l_type.length);

			for(common_prefix = 0; common_prefix < common_length; ++common_prefix) {
				if (c_type[common_prefix] !== l_type[common_prefix]) {
					break;
				}
			}
			for(level=c_type.length; level > common_prefix; --level) {
				close_ctx(2);
			}
			if (l_type.length <= common_prefix) {
				close_ctx(1);
			}
			for(level=common_prefix; level < l_type.length; ++level) {
				open_ctx([(l_type[level] === "*")?"ul":"ol"]);
				open_ctx(["li", l_type]);
			}
			if (l_type.length === common_prefix) {
				open_ctx(["li", l_type]);
			}
		} else {
			close_ctx(null);
			for(level=0; level < l_type.length; ++level) {
				open_ctx([(l_type[level] === "*")?"ul":"ol"]);
				open_ctx(["li", l_type]);
			}
		}

		g_result.push($.trim(l_text));
	}

	function handle_emphasize(s_token) {
		var e_tag;
		switch(s_token[1]) {
			case 2:
				e_tag = "em";
				break;
			case 3:
				e_tag = "strong";
				break;
			default:
				return; // ignore invalid emphasize tags
		}

		if (e_tag === curr_ctx()[0]) {
			close_ctx(1);
		} else {
			open_ctx([e_tag]);
		}
	}

	var corrector = lib.autocorrect(function(token) {
		switch (token[0][0]) {
			case "p":
				handle_paragraph(token);
				break;
			case "t":
				handle_text(token);
				break;
			case "h":
				handle_heading(token);
				break;
			case "b":
				handle_br(token);
				break;
			case "*":
			case "#":
				handle_list(token);
				break;
			case "'":
				handle_emphasize(token);
				break;
			default:
				throw new Error("invalid token");
		}
	});
	lib.tokenize(text, corrector);
	corrector(null); // flush buffers
	close_ctx(null); // finally close all active contexts

	return g_result.join("");
};

}(jQuery));
<|endoftext|>


<commit_before>'use strict'
const Sequelize = require('sequelize')
const db = require('../index.js')

module.exports = db.define('payment', {
  vendor: {
    type: Sequelize.ENUM('MasterCard', 'Visa', 'American Express', 'Discover'),
    allowNull: false
  },
  ExpirationDate: {
    type: Sequelize.DATEONLY,
    allowNull: false
  },
  CCV: {
    type: Sequelize.INTEGER,
    allowNull: false
  },
  Number: {
    type: Sequelize.INTEGER,
    allowNull: false,
    validate: {
      min: 13
    }
  },
  Name: {
    type: Sequelize.STRING,
    allowNull: false
  },
  Street: {
    type: Sequelize.STRING,
    allowNull: false
  },
  Apartment: {
    type: Sequelize.STRING,
    allowNull: false
  },
  State: {
    type: Sequelize.ENUM('Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorado', 'Connecticut', 'Delaware', 'District of Columbia', 'Florida', 'Georgia', 'Hawaii', 'Idaho', 'Illinois', 'Indiana', 'Iowa', 'Kansas', 'Kentucky', 'Louisiana', 'Maine', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota', 'Mississippi', 'Missouri', 'Montana', 'Nebraska', 'Nevada', 'New Hampshire', 'New Jersey', 'New Mexico', 'New York', 'North Carolina', 'North Dakota', 'Ohio', 'Oklahoma', 'Oregon', 'Pennsylvania', 'Rhode')
  },
  City: {
    type: Sequelize.STRING,
    allowNull: false
  },
  zipCode: {
    type: Sequelize.INTEGER
  }
})
<commit_msg>trailing commas for payment<commit_after>// 'use strict'
const Sequelize = require('sequelize');
const db = require('../index.js');

module.exports = db.define('payment', {
  vendor: {
    type: Sequelize.ENUM('MasterCard', 'Visa', 'American Express', 'Discover'),
    allowNull: false
  },
  ExpirationDate: {
    type: Sequelize.DATEONLY,
    allowNull: false
  },
  CCV: {
    type: Sequelize.INTEGER,
    allowNull: false
  },
  Number: {
    type: Sequelize.INTEGER,
    allowNull: false,
    validate: {
      min: 13
    }
  },
  Name: {
    type: Sequelize.STRING,
    allowNull: false
  },
  Street: {
    type: Sequelize.STRING,
    allowNull: false
  },
  Apartment: {
    type: Sequelize.STRING,
    allowNull: false
  },
  State: {
    type: Sequelize.ENUM('Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorado', 'Connecticut', 'Delaware', 'District of Columbia', 'Florida', 'Georgia', 'Hawaii', 'Idaho', 'Illinois', 'Indiana', 'Iowa', 'Kansas', 'Kentucky', 'Louisiana', 'Maine', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota', 'Mississippi', 'Missouri', 'Montana', 'Nebraska', 'Nevada', 'New Hampshire', 'New Jersey', 'New Mexico', 'New York', 'North Carolina', 'North Dakota', 'Ohio', 'Oklahoma', 'Oregon', 'Pennsylvania', 'Rhode')
  },
  City: {
    type: Sequelize.STRING,
    allowNull: false
  },
  zipCode: {
    type: Sequelize.INTEGER
  }
});
<|endoftext|>


<commit_before>'use strict'

// dependencies
var Element = require('vigour-element')
var Observable = require('vigour-js/lib/observable')
var Prop = require('vigour-element/lib/property')

// styles
require('vigour-scratch')
require('./style.less')

// components
var Icon = require('../../component/icon')

// properties
Element.prototype.inject(
  require('vigour-element/lib/property/text'),
  require('vigour-element/lib/property/css'),
  require('vigour-element/lib/property/attributes'),
  require('../../property')
)

/**
 * Select element
 * @memberOf UIKit
 */
module.exports = new Element({
  node: 'label',

  // css defaults
  ui: {
    name: 'select',
    scheme: 'graylight',
    size: 'medium',
    state: 'clean',
    atomic: 'atom',
    childOf: 'form-element',
    interactive: false
  },
  css: { addClass: 'ui-dirty' },
  options: {
    node: 'select',

    ChildConstructor: new Element({
      node: 'option'
    }),

    placeholder: {
      attributes: {
        disabled: 'disabled',
        selected: 'selected',
        value: ''
      },
      text: 'Select your option'
    },

    // on: {
    //   change: {
    //     dirty () {
    //       this.parent.setKey('css', {
    //         addClass: 'ui-dirty'
    //       })
    //     }
    //   }
    // }
  },

  down: new Icon('bottom ui-additional')
}).Constructor
<commit_msg>update<commit_after>'use strict'

// dependencies
var Element = require('vigour-element')
var Observable = require('vigour-js/lib/observable')
var Prop = require('vigour-element/lib/property')

// styles
require('vigour-scratch')
require('./style.less')

// components
var Icon = require('../../component/icon')

// properties
Element.prototype.inject(
  require('vigour-element/lib/property/text'),
  require('vigour-element/lib/property/css'),
  require('vigour-element/lib/property/attributes'),
  require('../../property')
)

/**
 * Select element
 * @memberOf UIKit
 */
module.exports = new Element({
  node: 'label',

  // css defaults
  ui: {
    name: 'select',
    scheme: 'graylight',
    size: 'medium',
    state: 'clean',
    atomic: 'atom',
    childOf: 'form-element',
    interactive: false
  },
  css: { addClass: 'ui-dirty' },
  options: {
    node: 'select',

    ChildConstructor: new Element({
      node: 'option'
    }),

    placeholder: {
      attributes: {
        disabled: 'disabled',
        selected: 'selected',
        value: ''
      },
      // text: 'Select your option'
    },

    // on: {
    //   change: {
    //     dirty () {
    //       this.parent.setKey('css', {
    //         addClass: 'ui-dirty'
    //       })
    //     }
    //   }
    // }
  },

  down: new Icon('bottom ui-additional')
}).Constructor
<|endoftext|>


<commit_before>/**
 * Map spatial electorate dataset to legislator records and generate combined geoJSON file
 *
 * This will probably only ever be done once, but to execute you will need to do the following:
 *
 * Edit the first path below to point to the raw electorate boundary dataset downloaded from
 * http://www.aec.gov.au/Electorates/gis/gis_datadownload.htm
 * Actually, I say raw, but you can and should pass this through mapshaper first, a GUI should be
 * online at http://mapshaper.org. This reduces filesize astronomically, I reduced quality
 * down to 0.5% whilst still maintaining fairly accurate boundary polygons.. this means the
 * divisions might be slightly misaligned in some cases; we circumvent this by querying intersection
 * against a bounding box and showing stats for all nearby electorates when in doubt - the end result
 * is faster computation of the results in the frontend and completely clientside geocoding.
 *
 * You could totally use the original file and import that dataset into mongo if you wanted to
 * put the servers behind it to run really exact queries, though. We would have to check that
 * there are no intersecting polygon arcs however as these do not pass index checks.
 *
 * Second path is a CSV of the data from the members / member IDs spreadsheet @rolandnsharp has.
 * You can edit column indexes and state name / ID mappings easily at the top if the format changes.
 * :TODO: link this data when final and publicly available somewhere
 *
 * @package StopTheSpies API
 * @author  Sam Pospischil <pospi@spadgos.com>
 * @since   2014-08-18
 */

var geoJSON = require(__dirname + '/../../website/map/COM20111216_ELB_region.json');
var csvFile = __dirname + '/reps.csv';
var destFile = __dirname + '/member_electorates.json';

var csvIdxs = {
	0 : 'type',
	2 : 'name',
	3 : 'electorate',
	13 : 'member_id',
};

var states = {
	'NT' : [ "northern territory" ],
	'QLD' : [ "queensland" ],
	'NSW' : [ "new south wales" ],
	'VIC' : [ "victoria" ],
	'TAS' : [ "tasmania" ],
	'SA' : [ "south australia" ],
	'WA' : [ "western australia" ],
	'ACT' : [ "australian capital territory" ],
};

//------------------------------------------------------------------------------

var fs = require('fs');
var csv = require('csv');

var countFails = 0, countSuccesses = 0, countDupes = 0,
	membersCount = 0, senatorsCount = 0, repsCount = 0;

// read CSV. Keep this simple it's a small file

var parser = csv.parse();
var csvStream = fs.createReadStream(csvFile), csvOpened = false;

var outputFeatures = [];

csvStream.on('data', function readCSV(data) {
	parser.write(data);
});
parser.on('readable', function parseCSV() {
	while (data = parser.read()) {
		matchGeoJSON(data);
	}
});
csvStream.on('end', outputResult);

// match records against reference place names

function matchGeoJSON(theHonorableMember)	// ლ(ಠ益ಠლ)
{
	var member = {}, i;

	for (i in csvIdxs) {
		member[csvIdxs[i]] = theHonorableMember[i] || null;
	}

	++membersCount;

	if (member.type == 'senate') {
		++senatorsCount;
		console.log('Ignore senator: ' + member.name);
		return;
	}
	++repsCount;

	function trim(str) {
		return str.trim();
	}

	var memberBits = member.electorate.split(',').map(trim);
	var ward;
	var state;
	var memberState;

	// if (memberBits.length == 1) {	// senators
	// 	state = memberBits[0].toLowerCase();
	// 	ward = null;
	// } else {							// representatives
		ward = memberBits[0];
		state = memberBits[1].toLowerCase();
	// }

	for (i in states) {
		if (states[i].indexOf(state) != -1) {
			memberState = i;
			break;
		}
	}

	if (!memberState) {
		++countFails;
		console.error('Member ' + member.name + ' [' + member.member_id + '] has unknown state: ' + member.electorate);
		return;
	}

	var dupeCheck = false;

	geoJSON.features.forEach(function(feat) {
		if (memberState == feat.properties.STATE) {
			if (feat.properties.ELECT_DIV.match(new RegExp('^' + ward + '$', 'i'))) {
				if (dupeCheck) {
					console.error('Member ' + member.name + ' [' + member.member_id + '] matches multiple areas: ' + member.electorate);
					++countDupes;
					--countSuccesses;
					return;
				}

				outputFeatures.push(mergeFeature(feat, member));
				++countSuccesses;
				dupeCheck = true;
			}
		}
	});
}

// write matched features to geoJSON output as we go

function mergeFeature(feat, member)
{
	return {
		type : feat.type,
		properties : {
			electorate : feat.ELECT_DIV,
			state : feat.STATE,
			member_id : member.member_id,
		},
		geometry : feat.geometry,
	};
}

function outputResult()
{
	fs.writeFileSync(destFile, JSON.stringify({
		type : 'FeatureCollection',
		features : outputFeatures,
	}));

	console.log('');
	console.log('Completed. Processed:');
	console.log(geoJSON.features.length, 'areas', '(', countSuccesses, 'ok ,', countFails, 'bad;', countDupes, 'dupes', ')');
	console.log(membersCount, 'members', '(', senatorsCount, 'senators ignored ,', repsCount, 'reps processed', ')');
}
<commit_msg>fix indexing in building output area data<commit_after>/**
 * Map spatial electorate dataset to legislator records and generate combined geoJSON file
 *
 * This will probably only ever be done once, but to execute you will need to do the following:
 *
 * Edit the first path below to point to the raw electorate boundary dataset downloaded from
 * http://www.aec.gov.au/Electorates/gis/gis_datadownload.htm
 * Actually, I say raw, but you can and should pass this through mapshaper first, a GUI should be
 * online at http://mapshaper.org. This reduces filesize astronomically, I reduced quality
 * down to 0.5% whilst still maintaining fairly accurate boundary polygons.. this means the
 * divisions might be slightly misaligned in some cases; we circumvent this by querying intersection
 * against a bounding box and showing stats for all nearby electorates when in doubt - the end result
 * is faster computation of the results in the frontend and completely clientside geocoding.
 *
 * You could totally use the original file and import that dataset into mongo if you wanted to
 * put the servers behind it to run really exact queries, though. We would have to check that
 * there are no intersecting polygon arcs however as these do not pass index checks.
 *
 * Second path is a CSV of the data from the members / member IDs spreadsheet @rolandnsharp has.
 * You can edit column indexes and state name / ID mappings easily at the top if the format changes.
 * :TODO: link this data when final and publicly available somewhere
 *
 * @package StopTheSpies API
 * @author  Sam Pospischil <pospi@spadgos.com>
 * @since   2014-08-18
 */

var geoJSON = require(__dirname + '/../../website/map/COM20111216_ELB_region.json');
var csvFile = __dirname + '/reps.csv';
var destFile = __dirname + '/member_electorates.json';

var csvIdxs = {
	0 : 'type',
	2 : 'name',
	3 : 'electorate',
	12 : 'member_id',
};

var states = {
	'NT' : [ "northern territory" ],
	'QLD' : [ "queensland" ],
	'NSW' : [ "new south wales" ],
	'VIC' : [ "victoria" ],
	'TAS' : [ "tasmania" ],
	'SA' : [ "south australia" ],
	'WA' : [ "western australia" ],
	'ACT' : [ "australian capital territory" ],
};

//------------------------------------------------------------------------------

var fs = require('fs');
var csv = require('csv');

var countFails = 0, countSuccesses = 0, countDupes = 0,
	membersCount = 0, senatorsCount = 0, repsCount = 0;

// read CSV. Keep this simple it's a small file

var parser = csv.parse();
var csvStream = fs.createReadStream(csvFile), csvOpened = false;

var outputFeatures = [];

csvStream.on('data', function readCSV(data) {
	parser.write(data);
});
parser.on('readable', function parseCSV() {
	while (data = parser.read()) {
		matchGeoJSON(data);
	}
});
csvStream.on('end', outputResult);

// match records against reference place names

function matchGeoJSON(theHonorableMember)	// ლ(ಠ益ಠლ)
{
	var member = {}, i;

	for (i in csvIdxs) {
		member[csvIdxs[i]] = theHonorableMember[i] || null;
	}

	++membersCount;

	if (member.type == 'senate') {
		++senatorsCount;
		console.log('Ignore senator: ' + member.name);
		return;
	}
	++repsCount;

	function trim(str) {
		return str.trim();
	}

	var memberBits = member.electorate.split(',').map(trim);
	var ward;
	var state;
	var memberState;

	// if (memberBits.length == 1) {	// senators
	// 	state = memberBits[0].toLowerCase();
	// 	ward = null;
	// } else {							// representatives
		ward = memberBits[0];
		state = memberBits[1].toLowerCase();
	// }

	for (i in states) {
		if (states[i].indexOf(state) != -1) {
			memberState = i;
			break;
		}
	}

	if (!memberState) {
		++countFails;
		console.error('Member ' + member.name + ' [' + member.member_id + '] has unknown state: ' + member.electorate);
		return;
	}

	var dupeCheck = false;

	geoJSON.features.forEach(function(feat) {
		if (memberState == feat.properties.STATE) {
			if (feat.properties.ELECT_DIV.match(new RegExp('^' + ward + '$', 'i'))) {
				if (dupeCheck) {
					console.error('Member ' + member.name + ' [' + member.member_id + '] matches multiple areas: ' + member.electorate);
					++countDupes;
					--countSuccesses;
					return;
				}

				outputFeatures.push(mergeFeature(feat, member));
				++countSuccesses;
				dupeCheck = true;
			}
		}
	});
}

// write matched features to geoJSON output as we go

function mergeFeature(feat, member)
{
	return {
		type : feat.type,
		properties : {
			electorate : feat.properties.ELECT_DIV,
			state : feat.properties.STATE,
			member_id : member.member_id,
		},
		geometry : feat.geometry,
	};
}

function outputResult()
{
	fs.writeFileSync(destFile, JSON.stringify({
		type : 'FeatureCollection',
		features : outputFeatures,
	}));

	console.log('');
	console.log('Completed. Processed:');
	console.log(geoJSON.features.length, 'areas', '(', countSuccesses, 'ok ,', countFails, 'bad;', countDupes, 'dupes', ')');
	console.log(membersCount, 'members', '(', senatorsCount, 'senators ignored ,', repsCount, 'reps processed', ')');
}
<|endoftext|>


<commit_before>var assert       = require('assert');
var _            = require('lodash');
var ThreadNeedle = require('../');

describe('Global settings', function () {

  describe('#global', function () {

    it('should set the global settings', function () {
      var threadneedle = new ThreadNeedle();
      threadneedle.global({
        chris: 'test'
      });
      assert.deepEqual(threadneedle._globalOptions, {
        chris: 'test'
      });
    });

  });

});<commit_msg>Global tests updated<commit_after>const assert = require('assert');
const _ = require('lodash');
const ThreadNeedle = require('../');

describe('Global settings', function () {

	describe.only('#global', function () {

		it('type should be `rest` by default', function () {
			const threadneedle = new ThreadNeedle();
			assert.deepEqual(threadneedle._globalOptions, {
				type: 'rest'
			});
		});

		it('should set the global settings', function () {
			const threadneedle = new ThreadNeedle();
			threadneedle.global({
				chris: 'test'
			});
			assert.deepEqual(threadneedle._globalOptions, {
				type: 'rest',
				chris: 'test'
			});
		});

		it('should override default if `type` is defined and valid', function () {
			const threadneedle = new ThreadNeedle();
			threadneedle.global({
				type: 'SOAP'
			});
			assert.deepEqual(threadneedle._globalOptions, {
				type: 'soap'
			});
		});

	});

});
<|endoftext|>


<commit_before>// JavaScript code for the Arduino BLE example app.

/**
 * The BLE plugin is loaded asynchronously so the ble
 * variable is set in the onDeviceReady handler.
 */
var ble = null;

/**
 * Called when HTML page has been loaded.
 */
$(document).ready( function()
{
	// Adjust canvas size when browser resizes
	$(window).resize( respondCanvas );

	// Adjust the canvas size when the document has loaded.
	respondCanvas();
});

/**
 * Adjust the canvas dimensions based on its container's dimensions.
 */
function respondCanvas()
{
	var canvas = $('#canvas');
	var container = $(canvas).parent();
	canvas.attr('width', $(container).width() ); // Max width
	canvas.attr('height', $(container).height() ); // Max height
}

/**
 * Application object that holds data and functions used by the app.
 */
var app =
{
	// Discovered devices.
	knownDevices: {},

	// Reference to the device we are connecting to.
	connectee: null,

	// Handle to the connected device.
	deviceHandle: null,

	// Handles to characteristics and descriptor for reading and
	// writing data from/to the Arduino using the BLE shield.
	characteristicRead: null,
	characteristicWrite: null,
	descriptorNotification: null,

	// Data that is plotted on the canvas.
	dataPoints: [],

	initialize: function()
	{
		document.addEventListener('deviceready', app.onDeviceReady, false);

		// Important reset BLE when page reloads/closes!
		window.hyper && window.hyper.onReload(function()
		{
			evothings.ble.stopScan();
			if (app.deviceHandle)
			{
				evothings.ble.close(app.deviceHandle);
			}
		});
	},

	// Called when device plugin functions are ready for use.
	onDeviceReady: function()
	{
		ble = evothings.ble; // Evothings BLE plugin

		app.startScan();
	},

	startScan: function()
	{
		console.log('Scanning...');
		evothings.ble.startScan(
			function(deviceInfo)
			{
				if (app.knownDevices[deviceInfo.address])
				{
					return;
				}
				console.log('found device: ' + deviceInfo.name);
				app.knownDevices[deviceInfo.address] = deviceInfo;
				if (deviceInfo.name == 'BLE Shield' && !app.connectee)
				{
					console.log('Found BLE shield');
					connectee = deviceInfo;
					app.connect(deviceInfo.address);
				}
			},
			function(errorCode)
			{
				console.log('startScan error: ' + errorCode);
			});
	},

	connect: function(address)
	{
		evothings.ble.stopScan();
		console.log('Connecting...');
		evothings.ble.connect(
			address,
			function(connectInfo)
			{
				if (connectInfo.state == 2) // Connected
				{
					console.log('Connected');
					app.deviceHandle = connectInfo.deviceHandle;
					app.getServices(connectInfo.deviceHandle);
				}
			},
			function(errorCode)
			{
				console.log('connect error: ' + errorCode);
			});
	},

	on: function()
	{
		app.write(
			'writeCharacteristic',
			app.deviceHandle,
			app.characteristicWrite,
			new Uint8Array([1])); // 1 = on
	},

	off: function()
	{
		app.write(
			'writeCharacteristic',
			app.deviceHandle,
			app.characteristicWrite,
			new Uint8Array([0])); // 0 = off
	},

	write: function(writeFunc, deviceHandle, handle, value)
	{
		if (handle)
		{
			ble[writeFunc](
				deviceHandle,
				handle,
				value,
				function()
				{
					console.log(writeFunc + ': ' + handle + ' success.');
				},
				function(errorCode)
				{
					console.log(writeFunc + ': ' + handle + ' error: ' + errorCode);
				});
		}
	},

	startReading: function(deviceHandle)
	{
		console.log('Enabling notifications');

		// Turn notifications on.
		app.write(
			'writeDescriptor',
			deviceHandle,
			app.descriptorNotification,
			new Uint8Array([1,0]));

		// Start reading notifications.
		evothings.ble.enableNotification(
			deviceHandle,
			app.characteristicRead,
			function(data)
			{
				app.drawLines([new DataView(data).getUint16(0, true)]);
			},
			function(errorCode)
			{
				console.log('enableNotification error: ' + errorCode);
			});
	},

	drawLines: function(dataArray)
	{
		var canvas = document.getElementById('canvas');
		var context = canvas.getContext('2d');
		var dataPoints = app.dataPoints;

		dataPoints.push(dataArray);
		if (dataPoints.length > canvas.width)
		{
			dataPoints.splice(0, (dataPoints.length - canvas.width));
		}

		var magnitude = 1024;

		function calcY(i)
		{
			return ((i * canvas.height) / (magnitude * 2)) + (canvas.height / 2);
		}

		function drawLine(offset, color)
		{
			context.strokeStyle = color;
			context.beginPath();
			context.moveTo(0, calcY(dataPoints[dataPoints.length-1][offset]));
			var x = 1;
			for (var i = dataPoints.length - 2; i >= 0; i--)
			{
				var y = calcY(dataPoints[i][offset]);
				context.lineTo(x, y);
				x++;
			}
			context.stroke();
		}

		context.clearRect(0, 0, canvas.width, canvas.height);
		drawLine(0, '#f00');
	},

	getServices: function(deviceHandle)
	{
		console.log('Reading services...');

		evothings.ble.readAllServiceData(deviceHandle, function(services)
		{
			// Find handles for characteristics and descriptor needed.
			for (var si in services)
			{
				var service = services[si];

				for (var ci in service.characteristics)
				{
					var characteristic = service.characteristics[ci];

					if (characteristic.uuid == '713d0002-503e-4c75-ba94-3148f18d941e')
					{
						app.characteristicRead = characteristic.handle;
					}
					else if (characteristic.uuid == '713d0003-503e-4c75-ba94-3148f18d941e')
					{
						app.characteristicWrite = characteristic.handle;
					}

					for (var di in characteristic.descriptors)
					{
						var descriptor = characteristic.descriptors[di];

						if (characteristic.uuid == '713d0002-503e-4c75-ba94-3148f18d941e' &&
							descriptor.uuid == '00002902-0000-1000-8000-00805f9b34fb')
						{
							app.descriptorNotification = descriptor.handle;
						}
					}
				}
			}

			if (app.characteristicRead && app.characteristicWrite && app.descriptorNotification)
			{
				console.log('RX/TX services found.');
				app.startReading(deviceHandle);
			}
			else
			{
				console.log('ERROR: RX/TX services not found!');
			}
		},
		function(errorCode)
		{
			console.log('readAllServiceData error: ' + errorCode);
		});
	},
};
// End of app object.
<commit_msg>Updated Arduino BLE to display graph within the entire diagram (thanks to Aaron).<commit_after>// JavaScript code for the Arduino BLE example app.

/**
 * The BLE plugin is loaded asynchronously so the ble
 * variable is set in the onDeviceReady handler.
 */
var ble = null;

/**
 * Called when HTML page has been loaded.
 */
$(document).ready( function()
{
	// Adjust canvas size when browser resizes
	$(window).resize( respondCanvas );

	// Adjust the canvas size when the document has loaded.
	respondCanvas();
});

/**
 * Adjust the canvas dimensions based on its container's dimensions.
 */
function respondCanvas()
{
	var canvas = $('#canvas');
	var container = $(canvas).parent();
	canvas.attr('width', $(container).width() ); // Max width
	canvas.attr('height', $(container).height() ); // Max height
}

/**
 * Application object that holds data and functions used by the app.
 */
var app =
{
	// Discovered devices.
	knownDevices: {},

	// Reference to the device we are connecting to.
	connectee: null,

	// Handle to the connected device.
	deviceHandle: null,

	// Handles to characteristics and descriptor for reading and
	// writing data from/to the Arduino using the BLE shield.
	characteristicRead: null,
	characteristicWrite: null,
	descriptorNotification: null,

	// Data that is plotted on the canvas.
	dataPoints: [],

	initialize: function()
	{
		document.addEventListener('deviceready', app.onDeviceReady, false);

		// Important reset BLE when page reloads/closes!
		window.hyper && window.hyper.onReload(function()
		{
			evothings.ble.stopScan();
			if (app.deviceHandle)
			{
				evothings.ble.close(app.deviceHandle);
			}
		});
	},

	// Called when device plugin functions are ready for use.
	onDeviceReady: function()
	{
		ble = evothings.ble; // Evothings BLE plugin

		app.startScan();
	},

	startScan: function()
	{
		console.log('Scanning...');
		evothings.ble.startScan(
			function(deviceInfo)
			{
				if (app.knownDevices[deviceInfo.address])
				{
					return;
				}
				console.log('found device: ' + deviceInfo.name);
				app.knownDevices[deviceInfo.address] = deviceInfo;
				if (deviceInfo.name == 'BLE Shield' && !app.connectee)
				{
					console.log('Found BLE shield');
					connectee = deviceInfo;
					app.connect(deviceInfo.address);
				}
			},
			function(errorCode)
			{
				console.log('startScan error: ' + errorCode);
			});
	},

	connect: function(address)
	{
		evothings.ble.stopScan();
		console.log('Connecting...');
		evothings.ble.connect(
			address,
			function(connectInfo)
			{
				if (connectInfo.state == 2) // Connected
				{
					console.log('Connected');
					app.deviceHandle = connectInfo.deviceHandle;
					app.getServices(connectInfo.deviceHandle);
				}
			},
			function(errorCode)
			{
				console.log('connect error: ' + errorCode);
			});
	},

	on: function()
	{
		app.write(
			'writeCharacteristic',
			app.deviceHandle,
			app.characteristicWrite,
			new Uint8Array([1])); // 1 = on
	},

	off: function()
	{
		app.write(
			'writeCharacteristic',
			app.deviceHandle,
			app.characteristicWrite,
			new Uint8Array([0])); // 0 = off
	},

	write: function(writeFunc, deviceHandle, handle, value)
	{
		if (handle)
		{
			ble[writeFunc](
				deviceHandle,
				handle,
				value,
				function()
				{
					console.log(writeFunc + ': ' + handle + ' success.');
				},
				function(errorCode)
				{
					console.log(writeFunc + ': ' + handle + ' error: ' + errorCode);
				});
		}
	},

	startReading: function(deviceHandle)
	{
		console.log('Enabling notifications');

		// Turn notifications on.
		app.write(
			'writeDescriptor',
			deviceHandle,
			app.descriptorNotification,
			new Uint8Array([1,0]));

		// Start reading notifications.
		evothings.ble.enableNotification(
			deviceHandle,
			app.characteristicRead,
			function(data)
			{
				app.drawLines([new DataView(data).getUint16(0, true)]);
			},
			function(errorCode)
			{
				console.log('enableNotification error: ' + errorCode);
			});
	},

	drawLines: function(dataArray)
	{
		var canvas = document.getElementById('canvas');
		var context = canvas.getContext('2d');
		var dataPoints = app.dataPoints;

		dataPoints.push(dataArray);
		if (dataPoints.length > canvas.width)
		{
			dataPoints.splice(0, (dataPoints.length - canvas.width));
		}

		var magnitude = 1024;

		function calcY(i)
		{
			return (i * canvas.height) / magnitude;
		}

		function drawLine(offset, color)
		{
			context.strokeStyle = color;
			context.beginPath();
			context.moveTo(0, calcY(dataPoints[dataPoints.length-1][offset]));
			var x = 1;
			for (var i = dataPoints.length - 2; i >= 0; i--)
			{
				var y = calcY(dataPoints[i][offset]);
				context.lineTo(x, y);
				x++;
			}
			context.stroke();
		}

		context.clearRect(0, 0, canvas.width, canvas.height);
		drawLine(0, '#f00');
	},

	getServices: function(deviceHandle)
	{
		console.log('Reading services...');

		evothings.ble.readAllServiceData(deviceHandle, function(services)
		{
			// Find handles for characteristics and descriptor needed.
			for (var si in services)
			{
				var service = services[si];

				for (var ci in service.characteristics)
				{
					var characteristic = service.characteristics[ci];

					if (characteristic.uuid == '713d0002-503e-4c75-ba94-3148f18d941e')
					{
						app.characteristicRead = characteristic.handle;
					}
					else if (characteristic.uuid == '713d0003-503e-4c75-ba94-3148f18d941e')
					{
						app.characteristicWrite = characteristic.handle;
					}

					for (var di in characteristic.descriptors)
					{
						var descriptor = characteristic.descriptors[di];

						if (characteristic.uuid == '713d0002-503e-4c75-ba94-3148f18d941e' &&
							descriptor.uuid == '00002902-0000-1000-8000-00805f9b34fb')
						{
							app.descriptorNotification = descriptor.handle;
						}
					}
				}
			}

			if (app.characteristicRead && app.characteristicWrite && app.descriptorNotification)
			{
				console.log('RX/TX services found.');
				app.startReading(deviceHandle);
			}
			else
			{
				console.log('ERROR: RX/TX services not found!');
			}
		},
		function(errorCode)
		{
			console.log('readAllServiceData error: ' + errorCode);
		});
	},
};
// End of app object.
<|endoftext|>


<commit_before>index_module.controller('ClusterCtl', ['$rootScope', '$scope', '$stateParams', '$window', '$location', 'toastr', 'AppUtil', 'ClusterService', 'ShardService',
    function ($rootScope, $scope, $stateParams, $window, $location, toastr, AppUtil, ClusterService, ShardService) {

        $scope.dcs, $scope.dcActiveTab, $scope.shards;
        $scope.clusterName = $stateParams.clusterName;

        $scope.switchDc = switchDc;
        $scope.loadCluster = loadCluster;
        $scope.loadShards = loadShards;

        if ($scope.clusterName) {
            loadCluster();
        }

        function switchDc(dc) {
            $scope.currentDcName = dc.dcName;
            loadShards($scope.clusterName, dc.dcName);
        }

        function loadCluster() {
            ClusterService.findClusterDCs($scope.clusterName)
                .then(function (result) {
                    if (!result || result.length == 0) {
                        $scope.dcs = [];
                        $scope.shards = [];
                        return;
                    }
                    $scope.dcs = result;

                    // TODO [marsqing] do not re-get dc data when switch dc
                    if($scope.dcs && $scope.dcs.length > 0) {
	                    $scope.dcs.forEach(function(dc){
	                    	if(dc.dcName === $stateParams.currentDcName) {
			                    $scope.currentDcName = dc.dcName;
	                    	}
	                    });

	                    if(!$scope.currentDcName) {
	                    	$scope.currentDcName = $scope.dcs[0].dcName;
	                    }
                    }

                    loadShards($scope.clusterName, $scope.dcs[0].dcName);

                }, function (result) {
                    toastr.error(AppUtil.errorMsg(result));
                });

        }

        function loadShards(clusterName, dcName) {
            ShardService.findClusterDcShards(clusterName, dcName)
                .then(function (result) {
                    $scope.shards = result;
                }, function (result) {
                    toastr.error(AppUtil.errorMsg(result));
                });
        }

    }]);<commit_msg>fix shard data mismatch bug<commit_after>index_module.controller('ClusterCtl', ['$rootScope', '$scope', '$stateParams', '$window', '$location', 'toastr', 'AppUtil', 'ClusterService', 'ShardService',
    function ($rootScope, $scope, $stateParams, $window, $location, toastr, AppUtil, ClusterService, ShardService) {

        $scope.dcs, $scope.dcActiveTab, $scope.shards;
        $scope.clusterName = $stateParams.clusterName;

        $scope.switchDc = switchDc;
        $scope.loadCluster = loadCluster;
        $scope.loadShards = loadShards;

        if ($scope.clusterName) {
            loadCluster();
        }

        function switchDc(dc) {
            $scope.currentDcName = dc.dcName;
            loadShards($scope.clusterName, dc.dcName);
        }

        function loadCluster() {
            ClusterService.findClusterDCs($scope.clusterName)
                .then(function (result) {
                    if (!result || result.length == 0) {
                        $scope.dcs = [];
                        $scope.shards = [];
                        return;
                    }
                    $scope.dcs = result;

                    // TODO [marsqing] do not re-get dc data when switch dc
                    if($scope.dcs && $scope.dcs.length > 0) {
	                    $scope.dcs.forEach(function(dc){
	                    	if(dc.dcName === $stateParams.currentDcName) {
			                    $scope.currentDcName = dc.dcName;
	                    	}
	                    });

	                    if(!$scope.currentDcName) {
	                    	$scope.currentDcName = $scope.dcs[0].dcName;
	                    }
                    }

                    loadShards($scope.clusterName, $scope.currentDcName);

                }, function (result) {
                    toastr.error(AppUtil.errorMsg(result));
                });

        }

        function loadShards(clusterName, dcName) {
            ShardService.findClusterDcShards(clusterName, dcName)
                .then(function (result) {
                    $scope.shards = result;
                }, function (result) {
                    toastr.error(AppUtil.errorMsg(result));
                });
        }

    }]);<|endoftext|>


<commit_before>//Because Browserify encapsulates every module, use strict won't apply to the global scope and break everything
'use strict';

//Require necessary modules
var Entity = require('../gameobjects/entity.js');

/**
 * Control constructor
 *
 * @class Group
 * @classdesc The object that holds multiple entities and is able to search them
 *
 * @param {Game} game - A reference to the current game object
 */
var Group = function(game) {

	/**
	 * @property {Game} game - Reference to the current game object
	 */
	this.game = game;

	/**
	 * @property {Array} entities - Collection of all the entities in this group
	 */
	this.entities = [];

};

Group.prototype = {

	/**
	 * Function to add a new entity to this group
	 * @protected
	 *
	 * @param {Entity} entity - A reference to entity being added
	 */
	addEntity: function(entity) {

		//Check if the entity is the correct object
		if(!entity instanceof Entity) {

			return;

		}

		//Add the current entity to the list
		this.entities.push(entity);

	},

	/**
	 * Function to remove an entity from this group
	 * @protected
	 *
	 * @param {Entity} entity - A reference to entity being removed
	 */
	removeEntity: function(entity) {

		//Check if the entity exists, if not, we don't have to delete it
		var index = this.entities.indexOf(entity);

		//The element doesn't exist in the list
		if(index === -1) {

			return;

		}

		//Remove the current entity from the group
		this.entities.splice(index, 1);

	},

	/**
	 * Function to return all entities with certain components
	 * @protected
	 *
	 * @return {Array} The array with all matching entities
	 */
	getEntities: function() {

		//Initialize the array that is going to be returned
		var entitiesMatch = [];

		//Loop through each entity in this group
		for(var i = 0; i < this.entities.length; i++) {

			//Initialize an empty array
			var isThere = [];

			//Loop through the arguments
			for(var a = 0; a < arguments.length; a++) {

				//If the current entity has the specified component. Push a random
				//value into the isThere array for later checks
				if(this.entities[i].components[arguments[a]]) {

					isThere.push(1);

				}

			}

			//If there are as many matches as supplied arguments, every component
			//is available within this entity
			if(isThere.length === arguments.length) {

				//Push the current entity into the array that is returned
				entitiesMatch.push(this.entities[i]);

			}

		}

		//Return all matching entities
		return entitiesMatch;

	}

};

//Export the Browserify module
module.exports = Group;
<commit_msg>The Group object now inherits from his PIXI class.<commit_after>//Because Browserify encapsulates every module, use strict won't apply to the global scope and break everything
'use strict';

//Require necessary modules
var Entity = require('../gameobjects/entity.js');

/**
 * Control constructor
 *
 * @class Group
 * @classdesc The object that holds multiple entities and is able to search them
 * Inherits from PIXI.DisplayObjectContainer
 *
 * @param {Game} game - A reference to the current game object
 */
var Group = function(game) {

	/**
	 * Inherit the constructor from the PIXI.DisplayObjectContainer object
	 */
	PIXI.DisplayObjectContainer.call(this);

	/**
	 * @property {Game} game - Reference to the current game object
	 */
	this.game = game;

	/**
	 * @property {Array} entities - Collection of all the entities in this group
	 */
	this.entities = [];

};

//Inherit the prototype from the PIXI.DisplayObjectContainer
Group.prototype = Object.create(PIXI.DisplayObjectContainer.prototype);
Group.prototype.constructor = Group;

/**
 * Function to add a new entity to this group
 * @protected
 *
 * @param {Entity} entity - A reference to entity being added
 */
Group.prototype.addEntity = function(entity) {

	//Check if the entity is the correct object
	if(!entity instanceof Entity) {

		return;

	}

	//Add the sprite component to the DisplayObjectContainer
	this.addChild(entity.sprite);

	//Add the current entity to the list
	this.entities.push(entity);

};

/**
 * Function to remove an entity from this group
 * @protected
 *
 * @param {Entity} entity - A reference to entity being removed
 */
Group.prototype.removeEntity = function(entity) {

	//Check if the entity exists, if not, we don't have to delete it
	var index = this.entities.indexOf(entity);

	//The element doesn't exist in the list
	if(index === -1) {

		return;

	}

	//Remove the sprite component from the the DisplayObjectContainer
	this.removeChild(entity.sprite);

	//Remove the current entity from the group
	this.entities.splice(index, 1);

};

/**
 * Function to return all entities with certain components
 * @protected
 *
 * @return {Array} The array with all matching entities
 */
Group.prototype.getEntities = function() {

	//Initialize the array that is going to be returned
	var entitiesMatch = [];

	//Loop through each entity in this group
	for(var i = 0; i < this.entities.length; i++) {

		//Initialize an empty array
		var isThere = [];

		//Loop through the arguments
		for(var a = 0; a < arguments.length; a++) {

			//If the current entity has the specified component. Push a random
			//value into the isThere array for later checks
			if(this.entities[i].components[arguments[a]]) {

				isThere.push(1);

			}

		}

		//If there are as many matches as supplied arguments, every component
		//is available within this entity
		if(isThere.length === arguments.length) {

			//Push the current entity into the array that is returned
			entitiesMatch.push(this.entities[i]);

		}

	}

	//Return all matching entities
	return entitiesMatch;

};

//Export the Browserify module
module.exports = Group;
<|endoftext|>


<commit_before>/**
 * Customer listing controller.
 * Handles logic related to customer listing / querying.
 *
 * @param {[type]} $scope       [description]
 * @param {[type]} Customers    [description]
 * @param {[type]} $window      [description]
 * @param {[type]} $routeParams [description]
 */
function ListingCtrl($scope, Customers, $window, $routeParams, $location){

    /**
     * Max items per page.
     *
     * @type {Number}
     */
    $scope.itemsPerPage = 10;

    /**
     * Available options for items per page.
     *
     * @type {Array}
     */
    $scope.itemsPerPageOptions = [10, 25, 50, 100];

    /**
     * Current page number
     */
    $scope.currentPage;

    /**
     * Total count of customer
     */
    $scope.totalItems;

    /**
     * response object for the current page number
     */
    $scope.response;

    /**
    * Filter enabled
    */

    $scope.filteractive = false;

    $scope.filterterms;

    $scope.filtervalue;

    $scope.filterlabel;

    /**
     * Watching currentPage number which triggers
     * listing call in return.
     *
     * @param  {[type]} newValue [description]
     * @param  {[type]} oldValue [description]
     *
     * @return {[type]}          [description]
     */
    $scope.$watch('currentPage',function(newValue, oldValue){
        if(!newValue){
            return;
        }

        var skip = (newValue - 1) * $scope.itemsPerPage;
        $scope.list(skip, $scope.itemsPerPage);
    });

    /**
     * Watching itemsPerPage number which triggers
     * listing call in return.
     *
     * @param  {[type]} newValue [description]
     * @param  {[type]} oldValue [description]
     *
     * @return {[type]}          [description]
     */
    $scope.$watch('itemsPerPage',function(){
        var skip = $scope.currentPage * $scope.itemsPerPage;
        $scope.list(skip, $scope.itemsPerPage);
    });
    /**
     * Get Customer Listing
     *
     * @param  {[type]} skip   [description]
     * @param  {[type]} limit  [description]
     *
     * @return {[type]}        [description]
     */
    $scope.list = function(skip, limit){
        if($scope.filteractive==true){

            $scope.filterterms = '{"'+$scope.filterlabel+'": {"$contains":"'+$scope.filtervalue+'"}}';

            Customers.query({skip: skip, limit: limit, where: $scope.filterterms}).$promise.
                then(function(response){
                    $scope.response = response;
                    $scope.totalItems = response.totalItemsCount;
                });

        }else{
            Customers.query({skip: skip, limit: limit}).$promise.
                then(function(response){
                    $scope.response = response;
                    $scope.totalItems = response.totalItemsCount;
                });
        }
    }

    /**
     * trigger for customer view page
     *
     * @param  {[type]} index [description]
     *
     * @return {[type]}       [description]
     */
    $scope.view = function(index){
        var customer = $scope.response.items[index];
        $location.path('/customers/view/' + customer.id)
    }

    $scope.filter = function(){
        $scope.filteractive = true;

        if($scope.currentPage != 1) {
            $scope.currentPage = 1;
        } else {
            $scope.list(0, $scope.itemsPerPage);
        }

    }

    $scope.clearfilter = function(){
        $scope.filteractive = false;
        $scope.filterlabel = "";
        $scope.filtervalue = "";
        if($scope.currentPage != 1) {
            $scope.currentPage = 1;
        } else {
            $scope.list(0, $scope.itemsPerPage);
        }
    }

    /**
     * Finds duplicates for a given customer
     *
     * @param  {[type]} customer [description]
     *
     * @return {[type]}          [description]
     */
    $scope.findDuplicates = function(index){
        var customer = $scope.response.items[index];
        var duplicates = [];

        console.log(customer);

        var where = '{' +
                '"firstName" : {"$contains" : "' + customer.firstName + '"},' +
                '"lastName" : {"$contains" : "' + customer.lastName + '"},' +
                '"id" : {"$ne" : ' + customer.id+ '}' +
                '}';

        Customers.query({skip: 0, limit: 100, where: where}).$promise.
            then(function(response){

            });
    }

    /**
     * [mergeContact description]
     *
     * @param  {[type]} from [description]
     * @param  {[type]} to   [description]
     *
     * @return {[type]}      [description]
     */
    $scope.mergeContact = function(from, to){

    }

    /**
     * Initialize current page. This will trigger the scope watch
     * and hence listing
     *
     * @type {Number}
     */
    $scope.currentPage = 1;
};<commit_msg>duplicates output<commit_after>/**
 * Customer listing controller.
 * Handles logic related to customer listing / querying.
 *
 * @param {[type]} $scope       [description]
 * @param {[type]} Customers    [description]
 * @param {[type]} $window      [description]
 * @param {[type]} $routeParams [description]
 */
function ListingCtrl($scope, Customers, $window, $routeParams, $location){

    /**
     * Max items per page.
     *
     * @type {Number}
     */
    $scope.itemsPerPage = 10;

    /**
     * Available options for items per page.
     *
     * @type {Array}
     */
    $scope.itemsPerPageOptions = [10, 25, 50, 100];

    /**
     * Current page number
     */
    $scope.currentPage;

    /**
     * Total count of customer
     */
    $scope.totalItems;

    /**
     * response object for the current page number
     */
    $scope.response;

    /**
     * Currently selected index.
     */
    $scope.selectedIndex;

    /**
    * Filter enabled
    */

    $scope.filteractive = false;

    $scope.filterterms;

    $scope.filtervalue;

    $scope.filterlabel;

    /**
     * Watching currentPage number which triggers
     * listing call in return.
     *
     * @param  {[type]} newValue [description]
     * @param  {[type]} oldValue [description]
     *
     * @return {[type]}          [description]
     */
    $scope.$watch('currentPage',function(newValue, oldValue){
        if(!newValue){
            return;
        }

        var skip = (newValue - 1) * $scope.itemsPerPage;
        $scope.list(skip, $scope.itemsPerPage);
    });

    /**
     * Watching itemsPerPage number which triggers
     * listing call in return.
     *
     * @param  {[type]} newValue [description]
     * @param  {[type]} oldValue [description]
     *
     * @return {[type]}          [description]
     */
    $scope.$watch('itemsPerPage',function(){
        var skip = $scope.currentPage * $scope.itemsPerPage;
        $scope.list(skip, $scope.itemsPerPage);
    });
    /**
     * Get Customer Listing
     *
     * @param  {[type]} skip   [description]
     * @param  {[type]} limit  [description]
     *
     * @return {[type]}        [description]
     */
    $scope.list = function(skip, limit){
        if($scope.filteractive==true){

            $scope.filterterms = '{"'+$scope.filterlabel+'": {"$contains":"'+$scope.filtervalue+'"}}';

            Customers.query({skip: skip, limit: limit, where: $scope.filterterms}).$promise.
                then(function(response){
                    $scope.selectedIndex = null;
                    $scope.response = response;
                    $scope.totalItems = response.totalItemsCount;
                });

        }else{
            Customers.query({skip: skip, limit: limit}).$promise.
                then(function(response){
                    $scope.selectedIndex = null;
                    $scope.response = response;
                    $scope.totalItems = response.totalItemsCount;
                });
        }
    }

    /**
     * trigger for customer view page
     *
     * @param  {[type]} index [description]
     *
     * @return {[type]}       [description]
     */
    $scope.view = function(index){
        var customer = $scope.response.items[index];
        $location.path('/customers/view/' + customer.id)
    }

    $scope.filter = function(){
        $scope.filteractive = true;

        if($scope.currentPage != 1) {
            $scope.currentPage = 1;
        } else {
            $scope.list(0, $scope.itemsPerPage);
        }

    }

    $scope.clearfilter = function(){
        $scope.filteractive = false;
        $scope.filterlabel = "";
        $scope.filtervalue = "";
        if($scope.currentPage != 1) {
            $scope.currentPage = 1;
        } else {
            $scope.list(0, $scope.itemsPerPage);
        }
    }

    /**
     * Finds duplicates for a given customer
     *
     * @param  {[type]} customer [description]
     *
     * @return {[type]}          [description]
     */
    $scope.findDuplicates = function(index){
        $scope.selectedIndex = index;
        var customer = $scope.response.items[index];
        var duplicates = [];

        var where = '{' +
                '"firstName" : {"$contains" : "' + customer.firstName + '"},' +
                '"lastName" : {"$contains" : "' + customer.lastName + '"},' +
                '"id" : {"$ne" : ' + customer.id+ '}' +
                '}';

        Customers.query({skip: 0, limit: 100, where: where}).$promise.
            then(function(response){
                if(response.items.length){
                    var ids = [];
                    angular.forEach(response.items, function(customer){
                        ids.push(customer.id);
                    });

                    alert('Duplicates found with customer ID - ' + ids.join(', ') );

                } else {
                    alert('No duplicates found');
                }
            });
    }

    /**
     * [mergeContact description]
     *
     * @param  {[type]} from [description]
     * @param  {[type]} to   [description]
     *
     * @return {[type]}      [description]
     */
    $scope.mergeContact = function(from, to){

    }

    /**
     * Initialize current page. This will trigger the scope watch
     * and hence listing
     *
     * @type {Number}
     */
    $scope.currentPage = 1;
};<|endoftext|>