<commit_before>/*
 * This file ties together the IRC and Matrix interfaces into a bridge between
 * the two.
 */
"use strict";
var q = require("q");
var matrixLib = require("./mxlib/matrix");
var ircLib = require("./irclib/irc");
var identifiers = require("./identifiers");
var models = require("./models");
var store = new models.RoomStore();
module.exports.store = store;

var msgTypes = {
    message: "m.text",
    privmsg: "m.emote",
    notice: "m.notice",
    "m.text": "message",
    "m.emote": "privmsg",
    "m.notice": "notice"
};

var createMatrixUserWithIrcUserId = function(userId) {
    var defer = q.defer();

    var srvNick = identifiers.userIdToServerNick(userId, ircLib.getServers());
    if (!srvNick.server) {
        return q.reject("Bad user ID");
    }

    ircLib.checkNickExists(srvNick.server, srvNick.nick).then(function(info) {
        // make the user
        var server = info.server;
        var nick = info.nick;
        console.log("Creating virtual user for %s on %s",
            nick, server.domain);
        var localpart = identifiers.createUserLocalpartForServerNick(
            server, nick
        );
        return matrixLib.getMatrixUser(localpart);
    }).then(function(user) {
        console.log("Created virtual user %s", user.userId);
        defer.resolve(user);
    }, function(err) {
        console.error("Virtual user creation for %s failed: %s",
            userId, err);
        defer.reject({});
    }).done();

    return defer.promise;
}

module.exports.hooks = {
    matrix: {
        onInvite: function(event) {
            console.log("onInvite: %s", JSON.stringify(event));
            var errFn = function(err) {
                console.error(
                    "onInvite: Failed to handle invite from %s to room %s : %s",
                    userId, event.room_id, err);
            };
            var userId = event.state_key;
            createMatrixUserWithIrcUserId(userId).then(function(user) {
                return matrixLib.joinRoom(event.room_id, user);
            }).then(function() {
                console.log("Joined %s to room %s", userId, event.room_id);
                return matrixLib.isPmRoom(userId, event.room_id, event.user_id);
            }).then(function(isPmRoom) {
                if (isPmRoom) {
                    // make the room. We need two rooms here, mapped to the same
                    // room ID. Best illustrated with an example.
                    // Alice(real IRC) and Bob(real Matrix):
                    // - Bob (event.user_id) initiated this invite to a virtual
                    //   user ID (event.state_key) which maps to a real IRC user
                    //   (Alice).
                    // - When Bob sends a message in the room (event.room_id),
                    //   we need to map to Alice's PM channel (the nick for the
                    //   Alice's user ID (event.state_key)).
                    // - When Alice replies, it goes to a virtual IRC user which
                    //   represents Bob (the nick for Bob's user ID
                    //   (event.user_id), which needs to be mapped back to Bob's
                    //   real user ID.
                    var virtualIrcUserTarget = identifiers.userIdToServerNick(
                        userId, ircLib.getServers()
                    );
                    var room = models.createMatrixRoom(event.room_id);
                    room.server = virtualIrcUserTarget.server;
                    room.channel = virtualIrcUserTarget.nick;
                    store.storeRoom(room);

                    var virtualIrcUser = identifiers.userIdToServerNick(
                        userId, ircLib.getServers()
                    );
                    room = models.createMatrixRoom(event.room_id);
                    // the real matrix user will have a virtual IRC user on the
                    // same server as the target.
                    room.server = virtualIrcUserTarget.server;
                    // FIXME: We cannot guarantee this nick will be free!
                    room.channel = identifiers.createIrcNickForUserId(
                        event.user_id
                    );
                    store.storeRoom(room);
                }
                else {
                    // whine that you don't do group chats and leave.
                    matrixLib.sendNoticeRaw(
                        event.room_id, userId, "Group chat not supported."
                    ).finally(function() {
                        matrixLib.leaveRoom(userId, event.room_id).done(
                            undefined, errFn
                        );
                    });
                }
            }).done(undefined, function(err) {
                console.error("onInvite: Failed to handle invite: %s", err);
            });
        },
        onJoin: function(event) {
            // if this is another Matrix user joining a PM room:
            //  - Whine that you don't do group chats and leave (virtual user)
        },
        onMessage: function(event) {
            console.log("[M->I]onMessage usr=%s rm=%s", event.user_id,
                event.room_id);

            var ircRooms = store.getRoomsForRoomId(event.room_id);

            if (ircRooms.length == 0) {
                console.log("No mapped channels.");
                return;
            }

            ircRooms.forEach(function(ircRoom) {
                console.log("Relaying message in %s on %s",
                    ircRoom.channel, ircRoom.server.domain);
                ircLib.getVirtualIrcUser(ircRoom.server, event.user_id).then(
                    function(ircUser) {
                        if (!ircUser) {
                            console.error("Unknown IRC user for user ID %s",
                                event.user_id);
                            return;
                        }
                        console.log("Obtained virtual IRC user: %s",
                            ircUser.nick);
                        var msgtype = msgTypes[event.content.msgtype];
                        var msg = event.content.body;
                        return ircUser.sendMessage(ircRoom, msgtype, msg);
                }).done(function() {
                    console.log("[M->I] Sent message.");
                }, function(err) {
                    console.error("[M->I]Failed to relay Matrix message: %s",
                        JSON.stringify(err));
                });
            });
        },
        onAliasQuery: function(roomAlias) {
            console.log("onAliasQuery %s", roomAlias);
            var defer = q.defer();
            // check if alias maps to a valid IRC server and channel
            var channelInfo = identifiers.aliasToServerChannel(
                roomAlias, ircLib.getServers()
            );
            if (!channelInfo.channel) {
                console.log("Unknown alias: %s", roomAlias);
                return q.reject();  // bad alias
            }

            // join the irc server + channel
            ircLib.trackChannel(channelInfo.server, channelInfo.channel).then(
                function(ircRoom) {
                    return matrixLib.createRoomWithAlias(
                        roomAlias, channelInfo.channel
                    );
                }
            ).then(function(matrixRoom) {
                // TODO set topic, add matrix members f.e. irc user(?) given
                // they are cheap to do.

                // store the mapping and return OK
                matrixRoom.channel = channelInfo.channel;
                matrixRoom.server = channelInfo.server;
                store.storeRoom(matrixRoom);
                defer.resolve({});
            }).done(undefined, function(err) {
                console.error(
                    "onAliasQuery: Failed to create a room for alias %s : %s",
                    roomAlias, err
                );
                defer.reject(err);
            });

            return defer.promise;
        },
        onUserQuery: function(userId) {
            console.log("onUserQuery: %s", userId);
            return createMatrixUserWithIrcUserId(userId);
        }
    },
    irc: {
        onMessage: function(server, from, to, kind, msg) {
            console.log("[I->M]onMessage: from=%s to=%s kind=%s msg=%s",
                from, to, kind, msg);

            // TODO if message is a PM to a Matrix user, send message in PM
            // room, creating one if need be.

            var matrixRooms = store.getRoomsForChannel(server, to);

            if (ircLib.isNickVirtualUser(server, from)) {
                console.log("Virtual user: bailing.");
                return;
            }

            var errFn = function(err) {
                console.error("[I->M]Failed to relay IRC message: %s",
                    JSON.stringify(err));
            };

            if (matrixRooms.length == 0) {
                if (to.indexOf("#") !== 0) {  // target is a PM
                    var virtualIrcUser = ircLib.getVirtualUserByNick(server, to);
                    if (!virtualIrcUser) {
                        console.error("Received a PM but we don't have a"+
                            " virtual user for nick %s", to);
                        return;
                    }
                    // make a PM room with this person. First make a user for
                    // this IRC person
                    var fromUser = undefined;
                    matrixLib.getMatrixUser(server.userPrefix+from).then(
                        function(user) {
                            // create the PM room as this user and invite the
                            // real matrix user
                            fromUser = user;
                            return matrixLib.createRoomWithUser(
                                user.userId, virtualIrcUser.userId,
                                (from + " (PM on " + server.domain + ")")
                            )
                        }
                    ).then(function(room) {
                        // persist mapping. We need two rooms here, as with the
                        // other PM room creation logic, due to having 2
                        // different PM "channels" depending on who sent the
                        // message.
                        var roomForInitiator = models.createMatrixRoom(
                            room.roomId
                        );
                        roomForInitiator.server = server;
                        roomForInitiator.channel = from;
                        store.storeRoom(roomForInitiator);
                        var roomForTarget = models.createMatrixRoom(
                            room.roomId
                        );
                        roomForTarget.server = server;
                        roomForTarget.channel = to;
                        store.storeRoom(roomForTarget);
                        // send the message
                        return matrixLib.sendMessage(
                            roomForInitiator, fromUser, msgTypes[kind], msg
                        );
                    }).done(undefined, errFn);
                }
                else {
                    console.log("No mapped rooms.");
                }
                return;
            }

            matrixLib.getMatrixUser(server.userPrefix+from).done(function(user) {
                matrixRooms.forEach(function(room) {
                    console.log("[I->M]Relaying in room %s", room.roomId);
                    matrixLib.sendMessage(
                        room, user, msgTypes[kind], msg
                    ).done(undefined, errFn);
                });
            }, errFn);

        }
    }
};
<commit_msg>Implement expose.pm/channel support.<commit_after>/*
 * This file ties together the IRC and Matrix interfaces into a bridge between
 * the two.
 */
"use strict";
var q = require("q");
var matrixLib = require("./mxlib/matrix");
var ircLib = require("./irclib/irc");
var identifiers = require("./identifiers");
var models = require("./models");
var store = new models.RoomStore();
module.exports.store = store;

var msgTypes = {
    message: "m.text",
    privmsg: "m.emote",
    notice: "m.notice",
    "m.text": "message",
    "m.emote": "privmsg",
    "m.notice": "notice"
};

var createMatrixUserWithIrcUserId = function(userId) {
    var defer = q.defer();

    var srvNick = identifiers.userIdToServerNick(userId, ircLib.getServers());
    if (!srvNick.server) {
        return q.reject("Bad user ID");
    }

    ircLib.checkNickExists(srvNick.server, srvNick.nick).then(function(info) {
        // make the user
        var server = info.server;
        var nick = info.nick;
        console.log("Creating virtual user for %s on %s",
            nick, server.domain);
        var localpart = identifiers.createUserLocalpartForServerNick(
            server, nick
        );
        return matrixLib.getMatrixUser(localpart);
    }).then(function(user) {
        console.log("Created virtual user %s", user.userId);
        defer.resolve(user);
    }, function(err) {
        console.error("Virtual user creation for %s failed: %s",
            userId, err);
        defer.reject({});
    }).done();

    return defer.promise;
}

module.exports.hooks = {
    matrix: {
        onInvite: function(event) {
            console.log("onInvite: %s", JSON.stringify(event));
            var errFn = function(err) {
                console.error(
                    "onInvite: Failed to handle invite from %s to room %s : %s",
                    userId, event.room_id, err);
            };
            var userId = event.state_key;
            var virtualIrcUserTarget = identifiers.userIdToServerNick(
                userId, ircLib.getServers()
            );
            if (!virtualIrcUserTarget.nick) {
                console.error("Invite isn't for an IRC user.");
                return;
            }
            if (!virtualIrcUserTarget.server.allowsPms()) {
                console.error(
                    "Rejecting invite: This server does not allow PMs."
                );
                return;
            }

            createMatrixUserWithIrcUserId(userId).then(function(user) {
                return matrixLib.joinRoom(event.room_id, user);
            }).then(function() {
                console.log("Joined %s to room %s", userId, event.room_id);
                return matrixLib.isPmRoom(userId, event.room_id, event.user_id);
            }).then(function(isPmRoom) {
                if (isPmRoom) {
                    // make the room. We need two rooms here, mapped to the same
                    // room ID. Best illustrated with an example.
                    // Alice(real IRC) and Bob(real Matrix):
                    // - Bob (event.user_id) initiated this invite to a virtual
                    //   user ID (event.state_key) which maps to a real IRC user
                    //   (Alice).
                    // - When Bob sends a message in the room (event.room_id),
                    //   we need to map to Alice's PM channel (the nick for the
                    //   Alice's user ID (event.state_key)).
                    // - When Alice replies, it goes to a virtual IRC user which
                    //   represents Bob (the nick for Bob's user ID
                    //   (event.user_id), which needs to be mapped back to Bob's
                    //   real user ID.
                    var room = models.createMatrixRoom(event.room_id);
                    room.server = virtualIrcUserTarget.server;
                    room.channel = virtualIrcUserTarget.nick;
                    store.storeRoom(room);

                    var virtualIrcUser = identifiers.userIdToServerNick(
                        userId, ircLib.getServers()
                    );
                    room = models.createMatrixRoom(event.room_id);
                    // the real matrix user will have a virtual IRC user on the
                    // same server as the target.
                    room.server = virtualIrcUserTarget.server;
                    // FIXME: We cannot guarantee this nick will be free!
                    room.channel = identifiers.createIrcNickForUserId(
                        event.user_id
                    );
                    store.storeRoom(room);
                }
                else {
                    // whine that you don't do group chats and leave.
                    matrixLib.sendNoticeRaw(
                        event.room_id, userId, "Group chat not supported."
                    ).finally(function() {
                        matrixLib.leaveRoom(userId, event.room_id).done(
                            undefined, errFn
                        );
                    });
                }
            }).done(undefined, function(err) {
                console.error("onInvite: Failed to handle invite: %s", err);
            });
        },
        onJoin: function(event) {
            // if this is another Matrix user joining a PM room:
            //  - Whine that you don't do group chats and leave (virtual user)
        },
        onMessage: function(event) {
            console.log("[M->I]onMessage usr=%s rm=%s", event.user_id,
                event.room_id);

            var ircRooms = store.getRoomsForRoomId(event.room_id);

            if (ircRooms.length == 0) {
                console.log("No mapped channels.");
                return;
            }

            ircRooms.forEach(function(ircRoom) {
                console.log("Relaying message in %s on %s",
                    ircRoom.channel, ircRoom.server.domain);
                ircLib.getVirtualIrcUser(ircRoom.server, event.user_id).then(
                    function(ircUser) {
                        if (!ircUser) {
                            console.error("Unknown IRC user for user ID %s",
                                event.user_id);
                            return;
                        }
                        console.log("Obtained virtual IRC user: %s",
                            ircUser.nick);
                        var msgtype = msgTypes[event.content.msgtype];
                        var msg = event.content.body;
                        return ircUser.sendMessage(ircRoom, msgtype, msg);
                }).done(function() {
                    console.log("[M->I] Sent message.");
                }, function(err) {
                    console.error("[M->I]Failed to relay Matrix message: %s",
                        JSON.stringify(err));
                });
            });
        },
        onAliasQuery: function(roomAlias) {
            console.log("onAliasQuery %s", roomAlias);
            var defer = q.defer();
            // check if alias maps to a valid IRC server and channel
            var channelInfo = identifiers.aliasToServerChannel(
                roomAlias, ircLib.getServers()
            );
            if (!channelInfo.channel) {
                console.log("Unknown alias: %s", roomAlias);
                return q.reject();  // bad alias
            }
            if (!channelInfo.server.shouldMapAllRooms()) {
                console.error("This server does not allow alias mappings.");
                return q.reject();
            }

            // join the irc server + channel
            ircLib.trackChannel(channelInfo.server, channelInfo.channel).then(
                function(ircRoom) {
                    return matrixLib.createRoomWithAlias(
                        roomAlias, channelInfo.channel
                    );
                }
            ).then(function(matrixRoom) {
                // TODO set topic, add matrix members f.e. irc user(?) given
                // they are cheap to do.

                // store the mapping and return OK
                matrixRoom.channel = channelInfo.channel;
                matrixRoom.server = channelInfo.server;
                store.storeRoom(matrixRoom);
                defer.resolve({});
            }).done(undefined, function(err) {
                console.error(
                    "onAliasQuery: Failed to create a room for alias %s : %s",
                    roomAlias, err
                );
                defer.reject(err);
            });

            return defer.promise;
        },
        onUserQuery: function(userId) {
            console.log("onUserQuery: %s", userId);
            return createMatrixUserWithIrcUserId(userId);
        }
    },
    irc: {
        onMessage: function(server, from, to, kind, msg) {
            console.log("[I->M]onMessage: from=%s to=%s kind=%s msg=%s",
                from, to, kind, msg);

            // TODO if message is a PM to a Matrix user, send message in PM
            // room, creating one if need be.

            var matrixRooms = store.getRoomsForChannel(server, to);

            if (ircLib.isNickVirtualUser(server, from)) {
                console.log("Virtual user: bailing.");
                return;
            }

            var errFn = function(err) {
                console.error("[I->M]Failed to relay IRC message: %s",
                    JSON.stringify(err));
            };

            if (matrixRooms.length == 0) {
                // target is a PM and we allow them to be passed to Matrix
                if (to.indexOf("#") !== 0 && server.allowsPms()) {
                    var virtualIrcUser = ircLib.getVirtualUserByNick(server, to);
                    if (!virtualIrcUser) {
                        console.error("Received a PM but we don't have a"+
                            " virtual user for nick %s", to);
                        return;
                    }
                    // make a PM room with this person. First make a user for
                    // this IRC person
                    var fromUser = undefined;
                    matrixLib.getMatrixUser(server.userPrefix+from).then(
                        function(user) {
                            // create the PM room as this user and invite the
                            // real matrix user
                            fromUser = user;
                            return matrixLib.createRoomWithUser(
                                user.userId, virtualIrcUser.userId,
                                (from + " (PM on " + server.domain + ")")
                            )
                        }
                    ).then(function(room) {
                        // persist mapping. We need two rooms here, as with the
                        // other PM room creation logic, due to having 2
                        // different PM "channels" depending on who sent the
                        // message.
                        var roomForInitiator = models.createMatrixRoom(
                            room.roomId
                        );
                        roomForInitiator.server = server;
                        roomForInitiator.channel = from;
                        store.storeRoom(roomForInitiator);
                        var roomForTarget = models.createMatrixRoom(
                            room.roomId
                        );
                        roomForTarget.server = server;
                        roomForTarget.channel = to;
                        store.storeRoom(roomForTarget);
                        // send the message
                        return matrixLib.sendMessage(
                            roomForInitiator, fromUser, msgTypes[kind], msg
                        );
                    }).done(undefined, errFn);
                }
                else {
                    console.log("No mapped rooms.");
                }
                return;
            }

            matrixLib.getMatrixUser(server.userPrefix+from).done(function(user) {
                matrixRooms.forEach(function(room) {
                    console.log("[I->M]Relaying in room %s", room.roomId);
                    matrixLib.sendMessage(
                        room, user, msgTypes[kind], msg
                    ).done(undefined, errFn);
                });
            }, errFn);

        }
    }
};
<|endoftext|>


<commit_before>// @ts-check

/** @typedef {'dark' | 'light'} ColorScheme */

document.addEventListener('DOMContentLoaded', init)

function init() {
  initColorSchemeToggle()
  setCurrentEmploymentTime()
}

function initColorSchemeToggle() {
  const storedColorScheme = window.localStorage.getItem('active-color-scheme')
  if (typeof storedColorScheme === 'string') {
    document.body.setAttribute('data-color-scheme', storedColorScheme)
  }

  const toggle = document.querySelector('[data-color-scheme-toggle]')
  toggle.addEventListener('click', toggleColorScheme)
}

function toggleColorScheme() {
  const activeColorScheme = getActiveColorScheme()
  const newActiveColorScheme = activeColorScheme === 'light' ? 'dark' : 'light'
  document.body.setAttribute('data-color-scheme', newActiveColorScheme)
  window.localStorage.setItem('active-color-scheme', newActiveColorScheme)
}

/**
 * @returns {ColorScheme} the active color scheme.
 */
function getActiveColorScheme() {
  const activeColorScheme = /** @type {ColorScheme | null} */ (document.body.getAttribute('data-color-scheme'))

  if (typeof activeColorScheme === 'string') {
    return activeColorScheme
  }

  const colorSchemePreference = getColorSchemePreference()
  return colorSchemePreference !== 'no-preference' ? colorSchemePreference : 'light'
}

/**
 * @returns {ColorScheme | 'no-preference'} a user’s color scheme preference.
 */
function getColorSchemePreference() {
  if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
    return 'dark'
  } else if (window.matchMedia('(prefers-color-scheme: light)').matches) {
    return 'light'
  } else {
    return 'no-preference'
  }
}

function setCurrentEmploymentTime() {
  const elements = Array.from(document.querySelectorAll('[data-start-date]'))
  for (const element of elements) {
    const startDateString = element.getAttribute('data-start-date')
    const startDate = new Date(startDateString)
    element.textContent = ' · ' + getFormattedTimeDifference(startDate)
  }
}

/**
 * @param {Date} startDate
 * @param {Date} [endDate]
 * @returns {string} a formatted time difference (e.g. `"1 year, 3 months"`).
 */
function getFormattedTimeDifference(startDate, endDate = new Date()) {
  const epochDifference = endDate.getTime() - startDate.getTime()

  if (epochDifference < 0) {
    return ''
  }

  const differenceDate = new Date(epochDifference)
  const years = differenceDate.getFullYear() - 1970
  const months = differenceDate.getMonth()

  const pieces = []

  if (years > 0) {
    pieces.push(`${years} year${years > 1 ? 's' : ''}`)
  }

  if (months > 0) {
    pieces.push(`${months} month${months > 1 ? 's' : ''}`)
  }

  return pieces.join(', ')
}
<commit_msg>Remove dom ready from main script<commit_after>// @ts-check

/** @typedef {'dark' | 'light'} ColorScheme */

init()

function init() {
  initColorSchemeToggle()
  setCurrentEmploymentTime()
}

function initColorSchemeToggle() {
  const storedColorScheme = window.localStorage.getItem('active-color-scheme')
  if (typeof storedColorScheme === 'string') {
    document.body.setAttribute('data-color-scheme', storedColorScheme)
  }

  const toggle = /** @type {HTMLButtonElement} */(document.querySelector('[data-color-scheme-toggle]'))
  toggle.addEventListener('click', toggleColorScheme)
}

function toggleColorScheme() {
  const activeColorScheme = getActiveColorScheme()
  const newActiveColorScheme = activeColorScheme === 'light' ? 'dark' : 'light'
  document.body.setAttribute('data-color-scheme', newActiveColorScheme)
  window.localStorage.setItem('active-color-scheme', newActiveColorScheme)
}

/**
 * @returns {ColorScheme} the active color scheme.
 */
function getActiveColorScheme() {
  const activeColorScheme = /** @type {ColorScheme | null} */ (document.body.getAttribute('data-color-scheme'))

  if (typeof activeColorScheme === 'string') {
    return activeColorScheme
  }

  const colorSchemePreference = getColorSchemePreference()
  return colorSchemePreference !== 'no-preference' ? colorSchemePreference : 'light'
}

/**
 * @returns {ColorScheme | 'no-preference'} a user’s color scheme preference.
 */
function getColorSchemePreference() {
  if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
    return 'dark'
  } else if (window.matchMedia('(prefers-color-scheme: light)').matches) {
    return 'light'
  } else {
    return 'no-preference'
  }
}

function setCurrentEmploymentTime() {
  const elements = Array.from(document.querySelectorAll('[data-start-date]'))
  for (const element of elements) {
    const startDateString = /** @type {string} */ (element.getAttribute('data-start-date'))
    const startDate = new Date(startDateString)
    element.textContent = ' · ' + getFormattedTimeDifference(startDate)
  }
}

/**
 * @param {Date} startDate
 * @param {Date} [endDate]
 * @returns {string} a formatted time difference (e.g. `"1 year, 3 months"`).
 */
function getFormattedTimeDifference(startDate, endDate = new Date()) {
  const epochDifference = endDate.getTime() - startDate.getTime()

  if (epochDifference < 0) {
    return ''
  }

  const differenceDate = new Date(epochDifference)
  const years = differenceDate.getFullYear() - 1970
  const months = differenceDate.getMonth()

  const pieces = []

  if (years > 0) {
    pieces.push(`${years} year${years > 1 ? 's' : ''}`)
  }

  if (months > 0) {
    pieces.push(`${months} month${months > 1 ? 's' : ''}`)
  }

  return pieces.join(', ')
}
<|endoftext|>


<commit_before>const parts = {
    allboomsCommentsWidget: () => document.body.classList.contains('single-ait-item'),
    topMenuNormalize: () => true,
};

function loader(){
    // loads only needed modules. Every module must have side effect
    // spawns only after dom is ready
    for(var part in parts){
        if(parts[part]()) import(`./parts/${part}.js`)
    }
}

if (document.readyState !== "loading") loader(); else document.addEventListener('DOMContentLoaded', loader);
<commit_msg>logging errors at the module's main thread<commit_after>import console from './components/console.js'

const parts = {
    allboomsCommentsWidget: () => document.body.classList.contains('single-ait-item'),
    topMenuNormalize: () => true,
};

function loader(){
    // loads only needed modules. Every module must have side effect
    // spawns only after dom is ready
    for(var part in parts){
        if(parts[part]()) import(`./parts/${part}.js`).catch(e => { console.error(e) })
    }
}

if (document.readyState !== "loading") loader(); else document.addEventListener('DOMContentLoaded', loader);
<|endoftext|>


<commit_before>// is the DOM ready for manipulation?
document.addEventListener('DOMContentLoaded', function() {

		// ---- variables ----
		var themeLight = 'light';
		var themeDark = 'dark';
		var elementToToggleOnLoad = 'application-loading';
		var effectClassApplyTo = document.getElementsByTagName('body')[0];
		var feedItem = '#feed-items li';

		// helper: change classes
		function addClass(element,className) {
			element.classList.add(className);
		}
		function removeClass(element,className) {
			element.classList.remove(className);
		}

		// helper: scroll to desired position
		function scrollToTarget(x,y) {
			window.scrollTo(x,y);
		}

		// helper: get Attribute from Element
		function getAtrributeFromElement(selector,attribute) {
			var attributeValue = document.querySelector(selector).getAttribute(attribute);
			return attributeValue;
		}

		// add JS to body-tag to allow CSS-Manipulation if JS is available
		function setJs() {
			var body = document.getElementsByTagName("body")[0];
			addClass(body,'js');
		}

		// ---- global functions ----

		// Overlay-Handling
		function handleOverlayTriggers(elementClassName) {
			var elements = document.getElementsByClassName(elementClassName);
			for(i=0; i < elements.length; i++) {
				elements[i].onclick = function(event) {
					var target = this.getAttribute('data-target');
					toggleOverlay(target,event);
				}
			}
		}
		function toggleOverlay(elementId,event) {
			var targetElement = document.getElementById(elementId);
			var fixElementId = 'content';
			if(targetElement.classList.contains('js-visible')) {
				addClass(targetElement,'js-hidden');
				removeClass(targetElement,'js-visible');
				removeClass(effectClassApplyTo,'js-fx');
			} else {
				removeClass(targetElement,'js-hidden');
				addClass(targetElement,'js-visible');
				addClass(effectClassApplyTo,'js-fx');
			}
			fixElement('content');
			event.preventDefault();
		}

		// make element sticky (via position in css)
		function stickyElement(stickyId,compensateId,compensateProperty) {
			stickyElement = document.getElementById(stickyId);
			stickyElement.classList.add('sticky');
			stickyHeight = stickyElement.clientHeight + 'px';

			//add Element-Height as defined property to desired element
			document.getElementById(compensateId).style.setProperty(compensateProperty,stickyHeight);
		}

		// place Element in relation to sticky element
		function placeOverlay(elementId) {
			overlayElement = document.getElementById(elementId);
			overlayElement.style.top = stickyHeight;
		}

		// ---- theme-switching ----
		// check if localStorage is filled and set body.class with it. This is useful, if the site runs as app
		var savedLocalStorageTheme = localStorage.getItem('theme');
		var applyThemeClassTo = document.getElementsByTagName('html')[0];
		if(savedLocalStorageTheme !== null) {
			removeClass(applyThemeClassTo, themeLight);
			removeClass(applyThemeClassTo, themeDark);
			addClass(applyThemeClassTo,savedLocalStorageTheme);
			if(savedLocalStorageTheme === themeLight) {
				document.getElementById('theme-switcher').checked = false;
			}
			if(savedLocalStorageTheme === themeDark) {
				document.getElementById('theme-switcher').checked = true;
			}
		}

		// switch theme by adding and removing classes to body
		function themeSwitch(elementId) {
			var renderFile = 'themeswitch.php?theme=';
			var applyThemeClassTo = document.getElementsByTagName('html')[0];

			switchingElement = document.getElementById(elementId);
			switchingElement.onclick = function() {
				xmlhttp = new XMLHttpRequest();
				if (this.checked) {
					addClass(applyThemeClassTo, themeDark);
					removeClass(applyThemeClassTo, themeLight);
					xmlhttp.open('GET',renderFile+themeDark,true);
					xmlhttp.send();
					localStorage.setItem('theme', themeDark);
					console.log('localStorage Theme is: ' + themeDark);
				} else {
					addClass(applyThemeClassTo, themeLight);
					removeClass(applyThemeClassTo, themeDark);
					xmlhttp.open('GET','themeswitch.php?theme='+themeLight,true);
					xmlhttp.send();
					localStorage.setItem('theme', themeLight);
					console.log('localStorage Theme is: ' + themeLight);
				}
			}
		}

		// ---- Loading Feeds (via Ajax) ----
		// add listener
		function channelSwitch(elementId) {
			elementContainer = document.getElementById(elementId);
			elementContainer.addEventListener('click', switchChannel, false);
		}

		// prepare loading after click
		function switchChannel(e) {
			if (e.target !== e.currentTarget) {
				channelLink = e.target.getAttribute('href');
				removeClass(effectClassApplyTo,'js-fx');
				ajaxRequest(channelLink);
				e.preventDefault();
			}
			e.stopPropagation();
		}

		// loading the content
		function ajaxRequest(channelLink) {
			// if this function is called with no parameter, we're chencking the localStorag, if one is present and use this (useful for initial load)
			if(channelLink === '') {
				var savedLocalStorageChannel = localStorage.getItem('channel');
				if(savedLocalStorageChannel !== null) {
					var channelLink = savedLocalStorageChannel;
				}
			}

			// requesting the content
			document.getElementById(elementToToggleOnLoad).classList.remove('js-hidden');
			renderFile = 'render-feeds.php';
			xmlhttp = new XMLHttpRequest();
			xmlhttp.open('GET',renderFile+channelLink,true);
			xmlhttp.send();

			overlayContainer = document.getElementById('application-overlay');
			addClass(overlayContainer,'js-hidden');
			fixElement('content');

			// output if call is succesful
			xmlhttp.onreadystatechange = function() {
				if (xmlhttp.readyState === 4 && xmlhttp.readyState) {
					outputContainer = document.getElementById('content');
					outputContainer.innerHTML = xmlhttp.response;
					document.getElementById(elementToToggleOnLoad).classList.add('js-hidden');
					removeClass(elementToFix,'js-fixed');
					document.getElementById('application-overlay').classList.remove('js-visible');
					scrollToTarget(0,0);
					localStorage.setItem('channel', channelLink);

					lastSavedItemTs = localStorage.getItem('latestItemTs');
					latestItemTs = getLatestItemTs();
					compareAndSaveLatestItemTs(latestItemTs,lastSavedItemTs);
					listenerClickFeedItem(feedItem);
				}
			}
		}

		// ---- handle Timeline-Update
		function getLatestItemTs() {
			latestItemTs = getAtrributeFromElement(feedItem,'data-ts');
			return latestItemTs;
		}

		function compareAndSaveLatestItemTs(latestItemTs,lastSavedItemTs) {
			lastSavedItemTsId = '#ts-' + lastSavedItemTs;
			if(lastSavedItemTs && latestItemTs > lastSavedItemTs) {
				updatedCount = getAtrributeFromElement(lastSavedItemTsId,'data-count');
				setUnreadItemCount(updatedCount);
				scrollIntoView(lastSavedItemTsId);
			} else {
				var stickyOffset = document.getElementById('application-header').clientHeight;
				scrollToTarget(0,localStorage.getItem('offsetTop'));
				localStorage.setItem('offsetTop',0);
			}
			localStorage.setItem('latestItemTs',latestItemTs);
		}

		function scrollIntoView(target) {
			var scrollPositionY = document.querySelector(target).offsetTop;
			var stickyOffset = document.getElementById('application-header').clientHeight;
			scrollToTarget(0,scrollPositionY - stickyOffset);
		}

		// ---- handle scrolldepth
		function listenerClickFeedItem(selector) {
			var elements = document.querySelectorAll(selector);
			for(i=0; i < elements.length; i++) {
				elements[i].onclick = function(event) {
					localStorage.setItem('offsetTop',window.pageYOffset);
				}
			}

		}

		// ---- handle unreadItem badge
		//https://stackoverflow.com/questions/487073/check-if-element-is-visible-after-scrolling
		function setUnreadItemCount(value) {
			badge = '#unread-items';
			badgeValue = '#unread-items__count';
			if(value > 0) {
				document.querySelector(badgeValue).innerText = value;
				document.querySelector(badge).classList.remove('js-hidden');
				document.querySelector(badge).classList.add('js-show');
			} else {
				document.querySelector(badge).classList.add('js-hide');
			}
		}

		// ---- fix element to current position
		function fixElement(elementId) {
			elementToFix = document.getElementById(elementId);
			scrollY = window.pageYOffset;

			if(elementToFix.classList.contains('js-fixed')) {
				removeClass(elementToFix,'js-fixed');
				elementToFix.style.top = '';
				scrollToTarget(0,scrollYMem);
			} else {
				addClass(elementToFix,'js-fixed');
				elementToFix.style.top = '-' + scrollY + 'px';
				scrollYMem = scrollY;
			}
		}

		// ---- initialize ----
		// set Js on body if JS is available
		setJs();

		// initial load of content
		ajaxRequest('');

		//sticky header (item(id) to fix, item(id) with property to compensate fix)
		stickyElement('application-header','content','margin-top');

		// theme switcher
		themeSwitch('theme-switcher');

		// Overlays
		handleOverlayTriggers('js-overlay-toggle');

		// place overlay
		placeOverlay('application-overlay');

		// switch channels
		channelSwitch('channels');

	});
<commit_msg>Update script.js<commit_after>// is the DOM ready for manipulation?
document.addEventListener('DOMContentLoaded', function() {

		// ---- variables ----
		var themeLight = 'light';
		var themeDark = 'dark';
		var elementToToggleOnLoad = 'application-loading';
		var effectClassApplyTo = document.getElementsByTagName('body')[0];
		var feedItem = '#feed-items li';

		// helper: change classes
		function addClass(element,className) {
			element.classList.add(className);
		}
		function removeClass(element,className) {
			element.classList.remove(className);
		}

		// helper: scroll to desired position
		function scrollToTarget(x,y) {
			window.scrollTo(x,y);
		}

		// helper: get Attribute from Element
		function getAtrributeFromElement(selector,attribute) {
			var attributeValue = document.querySelector(selector).getAttribute(attribute);
			return attributeValue;
		}

		// add JS to body-tag to allow CSS-Manipulation if JS is available
		function setJs() {
			var body = document.getElementsByTagName("body")[0];
			addClass(body,'js');
		}

		// ---- global functions ----

		// Overlay-Handling
		function handleOverlayTriggers(elementClassName) {
			var elements = document.getElementsByClassName(elementClassName);
			for(i=0; i < elements.length; i++) {
				elements[i].onclick = function(event) {
					var target = this.getAttribute('data-target');
					toggleOverlay(target,event);
				}
			}
		}
		function toggleOverlay(elementId,event) {
			var targetElement = document.getElementById(elementId);
			var fixElementId = 'content';
			if(targetElement.classList.contains('js-visible')) {
				addClass(targetElement,'js-hidden');
				removeClass(targetElement,'js-visible');
				removeClass(effectClassApplyTo,'js-fx');
			} else {
				removeClass(targetElement,'js-hidden');
				addClass(targetElement,'js-visible');
				addClass(effectClassApplyTo,'js-fx');
			}
			fixElement('content');
			event.preventDefault();
		}

		// make element sticky (via position in css)
		function stickyElement(stickyId,compensateId,compensateProperty) {
			stickyElement = document.getElementById(stickyId);
			stickyElement.classList.add('sticky');
			stickyHeight = stickyElement.clientHeight + 'px';

			//add Element-Height as defined property to desired element
			document.getElementById(compensateId).style.setProperty(compensateProperty,stickyHeight);
		}

		// place Element in relation to sticky element
		function placeOverlay(elementId) {
			overlayElement = document.getElementById(elementId);
			overlayElement.style.top = stickyHeight;
		}

		// ---- theme-switching ----
		// check if localStorage is filled and set body.class with it. This is useful, if the site runs as app
		var savedLocalStorageTheme = localStorage.getItem('theme');
		var applyThemeClassTo = document.getElementsByTagName('html')[0];
		if(savedLocalStorageTheme !== null) {
			removeClass(applyThemeClassTo, themeLight);
			removeClass(applyThemeClassTo, themeDark);
			addClass(applyThemeClassTo,savedLocalStorageTheme);
			if(savedLocalStorageTheme === themeLight) {
				document.getElementById('theme-switcher').checked = false;
			}
			if(savedLocalStorageTheme === themeDark) {
				document.getElementById('theme-switcher').checked = true;
			}
		}

		// switch theme by adding and removing classes to body
		function themeSwitch(elementId) {
			var renderFile = 'themeswitch.php?theme=';
			var applyThemeClassTo = document.getElementsByTagName('html')[0];

			switchingElement = document.getElementById(elementId);
			switchingElement.onclick = function() {
				xmlhttp = new XMLHttpRequest();
				if (this.checked) {
					addClass(applyThemeClassTo, themeDark);
					removeClass(applyThemeClassTo, themeLight);
					xmlhttp.open('GET',renderFile+themeDark,true);
					xmlhttp.send();
					localStorage.setItem('theme', themeDark);
					console.log('localStorage Theme is: ' + themeDark);
				} else {
					addClass(applyThemeClassTo, themeLight);
					removeClass(applyThemeClassTo, themeDark);
					xmlhttp.open('GET','themeswitch.php?theme='+themeLight,true);
					xmlhttp.send();
					localStorage.setItem('theme', themeLight);
					console.log('localStorage Theme is: ' + themeLight);
				}
			}
		}

		// ---- Loading Feeds (via Ajax) ----
		// add listener
		function channelSwitch(elementId) {
			elementContainer = document.getElementById(elementId);
			elementContainer.addEventListener('click', switchChannel, false);
		}

		// prepare loading after click
		function switchChannel(e) {
			if (e.target !== e.currentTarget) {
				channelLink = e.target.getAttribute('href');
				removeClass(effectClassApplyTo,'js-fx');
				ajaxRequest(channelLink);
				e.preventDefault();
			}
			e.stopPropagation();
		}

		// loading the content
		function ajaxRequest(channelLink) {
			// if this function is called with no parameter, we're chencking the localStorag, if one is present and use this (useful for initial load)
			if(channelLink === '') {
				var savedLocalStorageChannel = localStorage.getItem('channel');
				if(savedLocalStorageChannel !== null) {
					var channelLink = savedLocalStorageChannel;
				}
			}

			// requesting the content
			document.getElementById(elementToToggleOnLoad).classList.remove('js-hidden');
			renderFile = 'render-feeds.php';
			xmlhttp = new XMLHttpRequest();
			xmlhttp.open('GET',renderFile+channelLink,true);
			xmlhttp.send();

			overlayContainer = document.getElementById('application-overlay');
			addClass(overlayContainer,'js-hidden');
			fixElement('content');

			// output if call is succesful
			xmlhttp.onreadystatechange = function() {
				if (xmlhttp.readyState === 4 && xmlhttp.readyState) {
					outputContainer = document.getElementById('content');
					outputContainer.innerHTML = xmlhttp.response;
					document.getElementById(elementToToggleOnLoad).classList.add('js-hidden');
					removeClass(elementToFix,'js-fixed');
					document.getElementById('application-overlay').classList.remove('js-visible');
					scrollToTarget(0,0);
					localStorage.setItem('channel', channelLink);

					lastSavedItemTs = localStorage.getItem('latestItemTs');
					latestItemTs = getLatestItemTs();
					compareAndSaveLatestItemTs(latestItemTs,lastSavedItemTs);
					listenerClickFeedItem(feedItem);
				}
			}
		}

		// ---- handle Timeline-Update
		function getLatestItemTs() {
			latestItemTs = getAtrributeFromElement(feedItem,'data-ts');
			return latestItemTs;
		}

		function compareAndSaveLatestItemTs(latestItemTs,lastSavedItemTs) {
			lastSavedItemTsId = '#ts-' + lastSavedItemTs;
			if(lastSavedItemTs && latestItemTs > lastSavedItemTs) {
				updatedCount = getAtrributeFromElement(lastSavedItemTsId,'data-count');
				setUnreadItemCount(updatedCount);
				scrollIntoView(lastSavedItemTsId);
			} else {
				var stickyOffset = document.getElementById('application-header').clientHeight;
				scrollToTarget(0,localStorage.getItem('offsetTop'));
				localStorage.setItem('offsetTop',0);
			}
			localStorage.setItem('latestItemTs',latestItemTs);
		}

		function scrollIntoView(target) {
			var scrollPositionY = document.querySelector(target).offsetTop;
			var stickyOffset = document.getElementById('application-header').clientHeight;
			scrollToTarget(0,scrollPositionY - stickyOffset);
		}

		// ---- handle scrolldepth
		function listenerClickFeedItem(selector) {
			var elements = document.querySelectorAll(selector);
			for(i=0; i < elements.length; i++) {
				elements[i].onclick = function(event) {
					localStorage.setItem('offsetTop',window.pageYOffset);
				}
			}

		}

		// ---- handle unreadItem badge
		function setUnreadItemCount(value) {
			badge = '#unread-items';
			badgeValue = '#unread-items__count';
			if(value > 0) {
				document.querySelector(badgeValue).innerText = value;
				document.querySelector(badge).classList.remove('js-hidden');
				document.querySelector(badge).classList.add('js-show');
			} else {
				document.querySelector(badge).classList.add('js-hide');
			}
		}

		// ---- fix element to current position
		function fixElement(elementId) {
			elementToFix = document.getElementById(elementId);
			scrollY = window.pageYOffset;

			if(elementToFix.classList.contains('js-fixed')) {
				removeClass(elementToFix,'js-fixed');
				elementToFix.style.top = '';
				scrollToTarget(0,scrollYMem);
			} else {
				addClass(elementToFix,'js-fixed');
				elementToFix.style.top = '-' + scrollY + 'px';
				scrollYMem = scrollY;
			}
		}

		// ---- initialize ----
		// set Js on body if JS is available
		setJs();

		// initial load of content
		ajaxRequest('');

		//sticky header (item(id) to fix, item(id) with property to compensate fix)
		stickyElement('application-header','content','margin-top');

		// theme switcher
		themeSwitch('theme-switcher');

		// Overlays
		handleOverlayTriggers('js-overlay-toggle');

		// place overlay
		placeOverlay('application-overlay');

		// switch channels
		channelSwitch('channels');

	});
<|endoftext|>


<commit_before>/*
 * TODO:
 * - Fetch choices from Salesforce directly (or server's database), and
 *   provide an admin tool to let them update these easily.
 * - Convert these into objects with IDs so Salesforce can pick up changes
 *   without doing some string matching bullshit
 */

export const ETHNICITY_CHOICES = [
  'African American',
  'Asian / Pacific Islander',
  'Caucasian',
  'Latino',
  'Native American',
]

export const LANGUAGE_CHOICES = [
  'English',
  'Cantonese / Mandarin',
  'Russian',
  'Spanish',
  'Vietnamese',
]

export const PRIMARY_HEALTHCARE_CHOICES = [
  { value: 'Hospital/Emergency Room', label: 'Hospital/Emergency Room' },
  { value: 'Urgent Care', label: 'Urgent Care' },
  { value: 'Clinic / Primary Care Doctor', label: 'Clinic / Primary Care Doctor' },
  { value: 'VA', label: 'VA' },
  { value: 'Does not go for care', label: 'Does not go for care' },
  { value: 'Other', label: 'Other' },
]

export const LENGTH_OF_HOMELESSNESS_CHOICES = [
  { value: 'Less than 3', label: 'Less than 3' },
  { value: '3-6 months', label: '3-6 months' },
  { value: '6-9 months', label: '6-9 months' },
  { value: '9-12 months', label: '9-12 months' },
  { value: '1-3 years', label: '1-3 years' },
  { value: '3-5 years', label: '3-5 years' },
  { value: '5-7 years', label: '5-7 years' },
  { value: '7-10 years', label: '7-10 years' },
  { value: '10-15 years', label: '10-15 years' },
  { value: '15 years and more', label: '15 years and more' }
]

// TODO: Fetch dynamically.
export const MEDICAL_CHOICES = [
  'Acupuncture',
  'Addiction Services',
  'Dental',
  'Hearing Tests',
  'HIV, STI, and Hepatitis C Testing',
  'Flu Shots',
  // // 'Mammograms',
  'Massage',
  'Medical',
  'Mental Health Services',
  'Needle Exchange',
  'Podiatry',
  'Reading Glasses',
  'Vision Screenings',
  'Prescription Glasses',
]

// TODO: Fetch dynamically.
export const SUPPORT_CHOICES = [
  'Adult Probation',
  'Banking Services',
  'Books and Library Cards',
  'CAAP',
  'CalFresh',
  'Child Care',
  'Criminal Record Help',
  'Disability Services',
  'DMV ID',
  'Employment Services',
  'Family Services',
  'Foot Care',
  'Groceries',
  'Haircuts',
  'Homeward Bound',
  'Housing and Shelter Info',
  'Legal',
  'Lunch',
  'Lifeline Cell Phones',
  'Pet Sitting and VetSOS',
  'Phone Calls and Voicemail',
  'Photo Portraits',
  'Senior Services',
  'Showers',
  'SSI/SSDI and Medi-Cal',
  'Veteran Services',
  'Wheelchair/Walker Repairs',
  'Women\'s and Domestic Violence Services',
  'Youth Services (up to age 24)',
]
<commit_msg>Updated check-in.js constants for PHC69<commit_after>/*
 * TODO:
 * - Fetch choices from Salesforce directly (or server's database), and
 *   provide an admin tool to let them update these easily.
 * - Convert these into objects with IDs so Salesforce can pick up changes
 *   without doing some string matching bullshit
 */

export const ETHNICITY_CHOICES = [
  'African American',
  'Asian / Pacific Islander',
  'Caucasian',
  'Latino',
  'Native American',
]

export const LANGUAGE_CHOICES = [
  'English',
  'Cantonese / Mandarin',
  'Russian',
  'Spanish',
  'Vietnamese',
]

export const PRIMARY_HEALTHCARE_CHOICES = [
  { value: 'Hospital/Emergency Room', label: 'Hospital/Emergency Room' },
  { value: 'Urgent Care', label: 'Urgent Care' },
  { value: 'Clinic / Primary Care Doctor', label: 'Clinic / Primary Care Doctor' },
  { value: 'VA', label: 'VA' },
  { value: 'Does not go for care', label: 'Does not go for care' },
  { value: 'Other', label: 'Other' },
]

export const LENGTH_OF_HOMELESSNESS_CHOICES = [
  { value: 'Less than 3', label: 'Less than 3' },
  { value: '3-6 months', label: '3-6 months' },
  { value: '6-9 months', label: '6-9 months' },
  { value: '9-12 months', label: '9-12 months' },
  { value: '1-3 years', label: '1-3 years' },
  { value: '3-5 years', label: '3-5 years' },
  { value: '5-7 years', label: '5-7 years' },
  { value: '7-10 years', label: '7-10 years' },
  { value: '10-15 years', label: '10-15 years' },
  { value: '15 years and more', label: '15 years and more' }
]

// TODO: Fetch dynamically.
export const MEDICAL_CHOICES = [
  'Acupuncture',
  'Addiction Services',
  'Bugs and Lice Exams', //Needs salesforce
  'Dental',
  'Healthy SF',
  'Hepatitis A Vaccine', //Needs salesforce
//  'Hearing Tests',
  'HIV, STI, and Hepatitis C Testing',
  'Flu Shots',
  // // 'Mammograms',
  'Massage',
  'Medical',
  'Medical Clinics & Support', //Needs salesforce
  'Mental Health Services',
  //'Needle Exchange',
  'Podiatry',
  'Reading Glasses',
  'TB Testing',
  'Vision Screenings',
  //'Prescription Glasses',
]

// TODO: Fetch dynamically.
export const SUPPORT_CHOICES = [
  'Adult Probation',
  'Banking Services',
  //'Books and Library Cards',
  'CAAP',
  //'CalFresh',
  //'Child Care',
  //'Criminal Record Help',
  'Disability Services',
  'DMV ID',
  'Employment',
  'Food Bank',
  //'Family Services',
  //'Foot Care',
  //'Groceries',
  'Haircuts',
  'Homeward Bound',
  'Housing Info',
  'Legal',
  //'Lunch',
  //'Lifeline Cell Phones',
  'Pet Care',
  'Phone Calls and Voicemail',
  //'Photo Portraits',
  'Senior Services',
  'Showers',
  'SSI/SSDI and Medi-Cal',
  'Veteran Services',
  'Wheelchair/Walker Repairs',
  'Women\'s and Domestic Violence Services',
  'Youth Services (up to age 24)',
]
<|endoftext|>


<commit_before>
var Craft=function(options) {
  this.pos=[0,0];
  this.angle=0;

  this.scenario="f9r-dev1";

  this.crash_velocity=2;
  this.crash_angle=radians(5);

  this.scenarios={
    "f9r-dev1": {
      position: [0,0],
      velocity: [0,0],
      engine_number:3,
      max_engines: 3,
      angle: 0,
      angular_velocity: 0,
      fuel: 90000,
      gear_down:false,
      clamp:true
    },
    "f9r-dev2": {
      position: [0,0],
      velocity: [0,0],
      engine_number:9,
      max_engines: 9,
      angle: 0,
      angular_velocity: 0,
      fuel: 350000,
      gear_down:false,
      clamp:true
    },
    "f9-reentry": {
      position: [0,7000],
      velocity: [0,-300],
      engine_number:9,
      max_engines: 9,
      angle: 0,
      angular_velocity: 0,
      fuel: 30000,
      gear_down:false,
      clamp:false
    }
  };

  this.g_force=0;

  this.autopilot={
    enabled: false
  };

  this.clamped=false;

  this.reset=function(scenario) {
    if(!scenario) scenario=this.scenario;
    this.scenario=scenario;
    var s=this.scenarios[scenario];
    this.start=time()

    this.clamped=s.clamp;

    this.crashed=false;
    this.crash_time=null;

    this.mass=18000;
    this.mass=20000;
    this.fuel=s.fuel;

    if(s.gear_down) {
      this.gear_down=true;
      this.gear_animation=new Animation({
        value:1,
        start_value:1,
        end_value:0,
        duration:4
      });
    } else {
      this.gear_down=false;
      this.gear_animation=new Animation({
        value:0,
        start_value:0,
        end_value:1,
        duration:4
      });
    }
//    this.fuel=350000;

    this.throttle=0;

    this.engine_number=s.engine_number;
    this.max_engines=s.max_engines;

    this.rocket_body.position=[s.position[0],s.position[1]];
    this.rocket_body.velocity=[s.velocity[0],s.velocity[1]];
    this.rocket_body.angle=s.angle;
    this.rocket_body.angularVelocity=s.angular_velocity;

    this.rocket_body.position[1]=25;//-this.offset;
    this.updateOffset();

    if(s.clamp) this.clamp();

    this.update();

  };

  this.full_fuel=350000;
  // kg of thrust
  this.thrust_peak=[6540,7160];

  // kg of fuel per second of a single engine at sea level and in a vacuum
  this.fuel_flow=[600,450];

  // max engine vector
  this.vector_max=radians(5);

  this.min_throttle=0.7;
  this.max_throttle=1.03;

  this.thrust=0;
  this.thrust_vector=0; // angle in radians

  this.engine_number=9;

  this.rocket_body=new p2.Body({
    position: [0,30],
    angle: 0.01,
    mass: 1
  });

  this.rocket_shape=new p2.Rectangle(3.66, 42);
  this.rocket_body.addShape(this.rocket_shape);

  this.mass_distribution=[2,20];

  this.leg_offset=-23;

  this.right_leg_shape=new p2.Rectangle(2, 0.5);
  this.left_leg_shape=new p2.Rectangle(2, 0.5);
  this.rocket_body.addShape(this.right_leg_shape,[-6.5,this.leg_offset]);
  this.rocket_body.addShape(this.left_leg_shape,[6.5,this.leg_offset]);

  this.rocket_body.updateBoundingRadius();

  this.gear_down=true;
  this.gear_animation=new Animation({
    value:1,
    start_value:1,
    end_value:0,
    duration:4
  });

  prop.physics.world.addBody(this.rocket_body);

  this.setGear=function(down) {
    this.gear_down=down;
    if(down) {
      this.left_leg_shape.sensor=false;
      this.right_leg_shape.sensor=false;
      this.gear_animation.set(1);
    } else {
      this.left_leg_shape.sensor=true;
      this.right_leg_shape.sensor=true;
      this.gear_animation.set(0);
    }
  };

  this.clamp=function() {
    this.rocket_body.position=[-prop.ground.clamp[0],prop.ground.clamp[1]+26-this.offset];
    this.rocket_body.mass=0;
  };

  this.unclamp=function() {
    if(!this.clamped) return;
    this.clamped=false;
    this.rocket_body.position=[-prop.ground.clamp[0],prop.ground.clamp[1]+26-this.offset];
  };

  this.toggleGear=function() {
    this.setGear(!this.gear_down);
  };

  this.raiseGear=function() {
    this.setGear(false);
  };

  this.lowerGear=function() {
    this.setGear(true);
  };

  this.updateOffset=function() {
    this.offset=trange(0,this.fuel,this.full_fuel,this.mass_distribution[0],this.mass_distribution[1]);
    this.rocket_body.shapeOffsets[0][0]=0;
    this.rocket_body.shapeOffsets[0][1]=this.offset;
    this.rocket_body.shapeOffsets[1][0]=-6.5;
    this.rocket_body.shapeOffsets[2][0]=6.5;
    this.rocket_body.shapeOffsets[1][1]=this.offset+this.leg_offset;
    this.rocket_body.shapeOffsets[2][1]=this.offset+this.leg_offset;
  };

  this.updateMass=function() {
    if(this.clamped) {
      return;
    }
    this.offset=trange(0,this.fuel,this.full_fuel,this.mass_distribution[0],this.mass_distribution[1]);
    this.offset=0;
    this.rocket_body.mass=(this.mass+this.fuel)*0.01;
    this.updateOffset();
    if(this.gear_down) {
      this.left_leg_shape.sensor=false;
      this.right_leg_shape.sensor=false;
    } else {
      this.rocket_body.shapeOffsets[1][0]=0;
      this.rocket_body.shapeOffsets[1][1]=0;
      this.rocket_body.shapeOffsets[2][0]=0;
      this.rocket_body.shapeOffsets[2][1]=0;
      this.left_leg_shape.sensor=true;
      this.right_leg_shape.sensor=true;
    }
    this.rocket_body.updateMassProperties();
    var d=0.15;
//    if(this.gear_down) d=0.06;
    this.rocket_body.damping=crange(0,this.getAltitude(),100000,d,0.0);
    this.rocket_body.angular_damping=crange(0,this.getAltitude(),100000,d,0.0);
  };

  this.getAltitude=function() {
    return this.pos[1]-this.offset+13.4;
  };

  this.updateAutopilot=function() {
    if(!this.autopilot.enabled) return;
    this.engine_number=3;

    this.autopilot.target_altitude=100;
    this.autopilot.target_vspeed=crange(10,this.getAltitude()-this.autopilot.target_altitude,-10,-0.1,0.1);

    this.throttle+=crange(-1,this.autopilot.target_vspeed,1,-10*delta(),10*delta());
    this.thrust_vector=this.angle*100;
  };

  this.updateFuel=function() {
    if(this.throttle < 0.01) return;
    var single_engine_fuel_flow=trange(0,this.getAltitude(),100000,this.fuel_flow[0],this.fuel_flow[1]);
    var fuel_flow=single_engine_fuel_flow*crange(0,this.throttle,1,this.min_throttle,this.max_throttle)*this.engine_number*delta();
    this.fuel-=fuel_flow;
    this.fuel=Math.max(0,this.fuel);
  };

  this.updateThrust=function() {
    this.engine_number=clamp(1,this.engine_number,this.max_engines);
    this.thrust_vector=clamp(-1,this.thrust_vector,1);
    if(this.crashed) this.throttle=0;
    var throttle=trange(0,this.throttle,1,this.min_throttle,this.max_throttle);
    if(this.throttle <= 0.01) throttle=0;
    var thrust=trange(0,this.getAltitude(),100000,this.thrust_peak[0],this.thrust_peak[1])*this.engine_number*throttle;
    if(this.fuel <= 0) thrust=0;
    var v=(this.thrust_vector*this.vector_max)+this.angle;
    var force=[-sin(v)*this.thrust,cos(v)*this.thrust];
    var point=[0,0];
    if(!this.clamped) {
      this.rocket_body.toWorldFrame(point,[0,-22]);
      this.rocket_body.applyForce(force,point);
    }
    var mix=0.7;
    this.thrust=thrust*(1-mix)+this.thrust*mix;
  };

  this.updateLocal=function() {
    this.pos=this.rocket_body.position;
    this.angle=normalizeAngle(this.rocket_body.angle);
    this.g_force=distance([0,0],this.rocket_body.force)*0.001;
  };

  this.updateCrash=function() {
    if(time()-this.start < 1) return; // do not crash in the first few seconds
    if(!this.crashed) {
      if(this.rocket_body.overlaps(prop.physics.ground_body)) { // touching ground
        if(!this.gear_down) this.crashed=true; // crash if gear up
        if(distance([0,0],this.rocket_body.velocity) > this.crash_velocity) {
          this.crashed=true;
        }
        var angle=normalizeAngle(this.rocket_body.angle+Math.PI);
        if(Math.abs(angle-Math.PI) > this.crash_angle) this.crashed=true;
      }
      if(this.crashed) {
        this.throttle=0;
        this.crash_time=time();
      }
    }
  }

  this.update=function() {
    this.throttle=clamp(0,this.throttle,1);
    this.updateCrash();
    this.updateAutopilot();
    this.updateFuel();
    this.updateMass();
    this.updateThrust();
    this.updateLocal();
  };

  this.reset();

};

function craft_init_pre() {
  prop.craft=new Craft();
}

function craft_update() {
  prop.craft.update();
  $("#touch-buttons div").removeClass("active");
  if(prop.craft.engine_number == 1) $("#1-engine").addClass("active");
  if(prop.craft.engine_number == 3) $("#3-engine").addClass("active");
  if(prop.craft.engine_number == 9) $("#9-engine").addClass("active");
}

function craft_reset(scenario) {
  prop.craft.reset(scenario);
}
<commit_msg>fixed altitude<commit_after>
var Craft=function(options) {
  this.pos=[0,0];
  this.angle=0;

  this.scenario="f9r-dev1";

  this.crash_velocity=2;
  this.crash_angle=radians(5);

  this.scenarios={
    "f9r-dev1": {
      position: [0,0],
      velocity: [0,0],
      engine_number:3,
      max_engines: 3,
      angle: 0,
      angular_velocity: 0,
      fuel: 90000,
      gear_down:false,
      clamp:true
    },
    "f9r-dev2": {
      position: [0,0],
      velocity: [0,0],
      engine_number:9,
      max_engines: 9,
      angle: 0,
      angular_velocity: 0,
      fuel: 350000,
      gear_down:false,
      clamp:true
    },
    "f9-reentry": {
      position: [0,7000],
      velocity: [0,-300],
      engine_number:9,
      max_engines: 9,
      angle: 0,
      angular_velocity: 0,
      fuel: 30000,
      gear_down:false,
      clamp:false
    }
  };

  this.g_force=0;

  this.autopilot={
    enabled: false
  };

  this.clamped=false;

  this.reset=function(scenario) {
    if(!scenario) scenario=this.scenario;
    this.scenario=scenario;
    var s=this.scenarios[scenario];
    this.start=time()

    this.clamped=s.clamp;

    this.crashed=false;
    this.crash_time=null;

    this.mass=18000;
    this.mass=20000;
    this.fuel=s.fuel;

    if(s.gear_down) {
      this.gear_down=true;
      this.gear_animation=new Animation({
        value:1,
        start_value:1,
        end_value:0,
        duration:4
      });
    } else {
      this.gear_down=false;
      this.gear_animation=new Animation({
        value:0,
        start_value:0,
        end_value:1,
        duration:4
      });
    }
//    this.fuel=350000;

    this.throttle=0;

    this.engine_number=s.engine_number;
    this.max_engines=s.max_engines;

    this.rocket_body.position=[s.position[0],s.position[1]];
    this.rocket_body.velocity=[s.velocity[0],s.velocity[1]];
    this.rocket_body.angle=s.angle;
    this.rocket_body.angularVelocity=s.angular_velocity;

    this.rocket_body.position[1]=25;//-this.offset;
    this.updateOffset();

    if(s.clamp) this.clamp();

    this.update();

  };

  this.full_fuel=350000;
  // kg of thrust
  this.thrust_peak=[6540,7160];

  // kg of fuel per second of a single engine at sea level and in a vacuum
  this.fuel_flow=[600,450];

  // max engine vector
  this.vector_max=radians(5);

  this.min_throttle=0.7;
  this.max_throttle=1.03;

  this.thrust=0;
  this.thrust_vector=0; // angle in radians

  this.engine_number=9;

  this.rocket_body=new p2.Body({
    position: [0,30],
    angle: 0.01,
    mass: 1
  });

  this.rocket_shape=new p2.Rectangle(3.66, 42);
  this.rocket_body.addShape(this.rocket_shape);

  this.mass_distribution=[2,20];

  this.leg_offset=-23;

  this.right_leg_shape=new p2.Rectangle(2, 0.5);
  this.left_leg_shape=new p2.Rectangle(2, 0.5);
  this.rocket_body.addShape(this.right_leg_shape,[-6.5,this.leg_offset]);
  this.rocket_body.addShape(this.left_leg_shape,[6.5,this.leg_offset]);

  this.rocket_body.updateBoundingRadius();

  this.gear_down=true;
  this.gear_animation=new Animation({
    value:1,
    start_value:1,
    end_value:0,
    duration:4
  });

  prop.physics.world.addBody(this.rocket_body);

  this.setGear=function(down) {
    this.gear_down=down;
    if(down) {
      this.left_leg_shape.sensor=false;
      this.right_leg_shape.sensor=false;
      this.gear_animation.set(1);
    } else {
      this.left_leg_shape.sensor=true;
      this.right_leg_shape.sensor=true;
      this.gear_animation.set(0);
    }
  };

  this.clamp=function() {
    this.rocket_body.position=[-prop.ground.clamp[0],prop.ground.clamp[1]+26-this.offset];
    this.rocket_body.mass=0;
  };

  this.unclamp=function() {
    if(!this.clamped) return;
    this.clamped=false;
    this.rocket_body.position=[-prop.ground.clamp[0],prop.ground.clamp[1]+26-this.offset];
  };

  this.toggleGear=function() {
    this.setGear(!this.gear_down);
  };

  this.raiseGear=function() {
    this.setGear(false);
  };

  this.lowerGear=function() {
    this.setGear(true);
  };

  this.updateOffset=function() {
    this.offset=trange(0,this.fuel,this.full_fuel,this.mass_distribution[0],this.mass_distribution[1]);
    this.rocket_body.shapeOffsets[0][0]=0;
    this.rocket_body.shapeOffsets[0][1]=this.offset;
    this.rocket_body.shapeOffsets[1][0]=-6.5;
    this.rocket_body.shapeOffsets[2][0]=6.5;
    this.rocket_body.shapeOffsets[1][1]=this.offset+this.leg_offset;
    this.rocket_body.shapeOffsets[2][1]=this.offset+this.leg_offset;
  };

  this.updateMass=function() {
    if(this.clamped) {
      return;
    }
    this.offset=trange(0,this.fuel,this.full_fuel,this.mass_distribution[0],this.mass_distribution[1]);
    this.offset=0;
    this.rocket_body.mass=(this.mass+this.fuel)*0.01;
    this.updateOffset();
    if(this.gear_down) {
      this.left_leg_shape.sensor=false;
      this.right_leg_shape.sensor=false;
    } else {
      this.rocket_body.shapeOffsets[1][0]=0;
      this.rocket_body.shapeOffsets[1][1]=0;
      this.rocket_body.shapeOffsets[2][0]=0;
      this.rocket_body.shapeOffsets[2][1]=0;
      this.left_leg_shape.sensor=true;
      this.right_leg_shape.sensor=true;
    }
    this.rocket_body.updateMassProperties();
    var d=0.15;
//    if(this.gear_down) d=0.06;
    this.rocket_body.damping=crange(0,this.getAltitude(),100000,d,0.0);
    this.rocket_body.angular_damping=crange(0,this.getAltitude(),100000,d,0.0);
  };

  this.getAltitude=function() {
    return this.pos[1]+this.offset-26;
  };

  this.getVspeed=function() {
    return this.rocket_body.velocity[1];
  };

  this.updateAutopilot=function() {
    if(!this.autopilot.enabled) return;
    this.engine_number=3;

    this.autopilot.target_altitude=100;
    this.autopilot.target_vspeed=crange(100,this.getAltitude()-this.autopilot.target_altitude,-100,-10,10);

    this.throttle=crange(0,this.autopilot.target_vspeed-this.getVspeed(),1,0,1);
    this.throttle=clamp(0.05,this.throttle,1);
    this.thrust_vector=(mod((this.angle-Math.PI),Math.PI*2)-Math.PI)*1000;
  };

  this.updateFuel=function() {
    if(this.throttle < 0.01) return;
    var single_engine_fuel_flow=trange(0,this.getAltitude(),100000,this.fuel_flow[0],this.fuel_flow[1]);
    var fuel_flow=single_engine_fuel_flow*crange(0,this.throttle,1,this.min_throttle,this.max_throttle)*this.engine_number*delta();
    this.fuel-=fuel_flow;
    this.fuel=Math.max(0,this.fuel);
  };

  this.updateThrust=function() {
    this.engine_number=clamp(1,this.engine_number,this.max_engines);
    this.thrust_vector=clamp(-1,this.thrust_vector,1);
    if(this.crashed) this.throttle=0;
    var throttle=trange(0,this.throttle,1,this.min_throttle,this.max_throttle);
    if(this.throttle <= 0.01) throttle=0;
    var thrust=trange(0,this.getAltitude(),100000,this.thrust_peak[0],this.thrust_peak[1])*this.engine_number*throttle;
    if(this.fuel <= 0) thrust=0;
    var v=(this.thrust_vector*this.vector_max)+this.angle;
    var force=[-sin(v)*this.thrust,cos(v)*this.thrust];
    var point=[0,0];
    if(!this.clamped) {
      this.rocket_body.toWorldFrame(point,[0,-22]);
      this.rocket_body.applyForce(force,point);
    }
    var mix=0.7;
    this.thrust=thrust*(1-mix)+this.thrust*mix;
  };

  this.updateLocal=function() {
    this.pos=this.rocket_body.position;
    this.angle=normalizeAngle(this.rocket_body.angle);
    this.g_force=distance([0,0],this.rocket_body.force)*0.001;
  };

  this.updateCrash=function() {
    if(time()-this.start < 1) return; // do not crash in the first few seconds
    if(!this.crashed) {
      if(this.rocket_body.overlaps(prop.physics.ground_body)) { // touching ground
        if(!this.gear_down) this.crashed=true; // crash if gear up
        if(distance([0,0],this.rocket_body.velocity) > this.crash_velocity) {
          this.crashed=true;
        }
        var angle=normalizeAngle(this.rocket_body.angle+Math.PI);
        if(Math.abs(angle-Math.PI) > this.crash_angle) this.crashed=true;
      }
      if(this.crashed) {
        this.throttle=0;
        this.crash_time=time();
      }
    }
  }

  this.update=function() {
    this.throttle=clamp(0,this.throttle,1);
    this.updateCrash();
    this.updateAutopilot();
    this.updateFuel();
    this.updateMass();
    this.updateThrust();
    this.updateLocal();
  };

  this.reset();

};

function craft_init_pre() {
  prop.craft=new Craft();
}

function craft_update() {
  prop.craft.update();
  $("#touch-buttons div").removeClass("active");
  if(prop.craft.engine_number == 1) $("#1-engine").addClass("active");
  if(prop.craft.engine_number == 3) $("#3-engine").addClass("active");
  if(prop.craft.engine_number == 9) $("#9-engine").addClass("active");
}

function craft_reset(scenario) {
  prop.craft.reset(scenario);
}
<|endoftext|>


<commit_before>import React from 'react';
import classNames from 'classnames/bind';
import {connectToStores} from 'fluxible-addons-react';
import DeckListStore from '../../stores/DeckListStore';
import DeckViewPanel from '../Deck/ContentPanel/DeckModes/DeckViewPanel/DeckViewPanel';
import CustomDate from '../Deck/util/CustomDate';
import ISO6391 from 'iso-639-1';
import cheerio from 'cheerio';
import lodash from 'lodash';
import { Microservices } from '../../configs/microservices';

class DeckList extends React.Component {

    render() {

        let decks_to_show = this.props.scope === 'featured' ? this.props.DeckListStore.featured : this.props.DeckListStore.recent;

        return (

                {/* Read https://slidewiki.atlassian.net/wiki/display/SWIK/How+To+Use+Slide+Thumbnail to know the details */}
                {decks_to_show.map((deck, index) => {
                    let deckCreatorid = deck.user;
                    let deckCreator = deck.username;
                    let deckIdAndrevision = deck._id; //not used for now
                    if (deck.revision_to_show) deckIdAndrevision+= '-' + deck.revision_to_show; //not used for now
                    let deckDate = CustomDate.format(deck.timestamp, 'Do MMMM YYYY');
                    let deckLanguageCode = deck.language === undefined ? 'gb' : deck.language;
                    let deckLanguage = deckLanguageCode === undefined ? '' : ISO6391.getName(deckLanguageCode);
                    // default English
                    deckLanguage = (deckLanguage === '' ? 'English' : deckLanguage);
                    let countryFlag = deckLanguageCode;
                    //let totalSlides = '1000'; //TODO
                    let deckTheme = deck.theme === undefined ? 'Simple' : deck.theme;

                    return (

{deck.title}

Creator: {deckCreator}

Date: {deckDate}

                                            {deckLanguage}

                                        {/*

                                            {totalSlides}
*/}

                                            {deck.countRevisions}

                    );
                })}

        );

    }
}

DeckList = connectToStores(DeckList, [DeckListStore], (context, props) => {
    return {
        DeckListStore: context.getStore(DeckListStore).getState()
    };
});
export default DeckList;
<commit_msg>bug fixed<commit_after>import React from 'react';
import classNames from 'classnames/bind';
import {connectToStores} from 'fluxible-addons-react';
import DeckListStore from '../../stores/DeckListStore';
import DeckViewPanel from '../Deck/ContentPanel/DeckModes/DeckViewPanel/DeckViewPanel';
import CustomDate from '../Deck/util/CustomDate';
import ISO6391 from 'iso-639-1';
import cheerio from 'cheerio';
import lodash from 'lodash';
import { Microservices } from '../../configs/microservices';

class DeckList extends React.Component {

    render() {
        let result = '';
        let decks_to_show = this.props.scope === 'featured' ? this.props.DeckListStore.featured : this.props.DeckListStore.recent;
        if (decks_to_show){
            result =
                decks_to_show.map((deck, index) => {
                    let deckCreatorid = deck.user;
                    let deckCreator = deck.username;
                    let deckIdAndrevision = deck._id; //not used for now
                    if (deck.revision_to_show) deckIdAndrevision+= '-' + deck.revision_to_show; //not used for now
                    let deckDate = CustomDate.format(deck.timestamp, 'Do MMMM YYYY');
                    let deckLanguageCode = deck.language === undefined ? 'gb' : deck.language;
                    let deckLanguage = deckLanguageCode === undefined ? '' : ISO6391.getName(deckLanguageCode);
                    // default English
                    deckLanguage = (deckLanguage === '' ? 'English' : deckLanguage);
                    let countryFlag = deckLanguageCode;
                    //let totalSlides = '1000'; //TODO
                    let deckTheme = deck.theme === undefined ? 'Simple' : deck.theme;

                    return (

{deck.title}

Creator: {deckCreator}

Date: {deckDate}

                                            {deckLanguage}

                                        {/*

                                            {totalSlides}
*/}

                                            {deck.countRevisions}

                    );
                });
        }

        return (

                {result}

        );
    }
}

DeckList = connectToStores(DeckList, [DeckListStore], (context, props) => {
    return {
        DeckListStore: context.getStore(DeckListStore).getState()
    };
});
export default DeckList;
<|endoftext|>


<commit_before>((window, document) => {
    'use strict';

    const round = (number, decimals) => {
        return Math.round(number * Math.pow(10, decimals)) / Math.pow(10, decimals);
    };

    const convert = (options) => {
        const units = `${ options.from }-${ options.to }`;
        const formulas = {
            'ch-cm': options.value * 0.21087588,
            'ch-em': options.value * 0.5,
            'ch-ex': options.value / 0.9,
            'ch-in': options.value * 0.083022,
            'ch-mm': options.value * 2.1087588,
            'ch-pc': options.value * 0.5,
            'ch-pt': options.value * 5.977584,
            'ch-%': options.value * 50,
            'ch-px': options.value * options.base * 0.5,

            'cm-ch': options.value / 0.21087588,
            'cm-em': options.value / 0.42175176,
            'cm-ex': options.value / 0.189788292,
            'cm-in': options.value * 0.39,
            'cm-mm': options.value * 10,
            'cm-pc': options.value / 0.42175176,
            'cm-pt': options.value * 28.3464566929,
            'cm-%': options.value / options.base * 100 / 2.54 * options.dpi,
            'cm-px': options.value / 2.54 * options.dpi,

            'em-ch': options.value / 0.5,
            'em-cm': options.value * 0.42175176,
            'em-ex': options.value / 0.45,
            'em-in': options.value * 0.166044,
            'em-mm': options.value / 0.237106301584,
            'em-pc': options.value,
            'em-pt': options.value * 11.955168,
            'em-%': options.value * 100,
            'em-px': options.value * options.base,

            'ex-ch': options.value * 0.9,
            'ex-cm': options.value * 0.189788292,
            'ex-em': options.value * 0.45,
            'ex-in': options.value * 0.0747198,
            'ex-mm': options.value * 1.89788292,
            'ex-pc': options.value * 0.45,
            'ex-pt': options.value * 5.3798256,
            'ex-%': options.value * 45,
            'ex-px': options.value * options.base * 0.45,

            'in-ch': options.value / 0.083022,
            'in-cm': options.value * 2.54,
            'in-em': options.value / 0.166044,
            'in-ex': options.value / 0.0747198,
            'in-mm': options.value * 2.54 * 10,
            'in-pc': options.value / 0.166044,
            'in-pt': options.value / 0.014842519685,
            'in-%': options.value / options.base * 100 * options.dpi,
            'in-px': options.value * options.dpi,

            'mm-ch': options.value / 2.1087588,
            'mm-cm': options.value / 10,
            'mm-em': options.value * 0.237106301584,
            'mm-ex': options.value / 1.89788292,
            'mm-in': options.value * 0.39 / 10,
            'mm-pc': options.value / 4.42175176,
            'mm-pt': options.value / 0.352777777778,
            'mm-%': options.value / options.base * 100 / 2.54 * options.dpi / 10,
            'mm-px': options.value / 2.54 * options.dpi / 10,

            'pc-ch': options.value / 0.5,
            'pc-cm': options.value * 0.42175176,
            'pc-em': options.value,
            'pc-ex': options.value / 0.45,
            'pc-in': options.value * 0.166044,
            'pc-mm': options.value * 4.42175176,
            'pc-pt': options.value / 0.0836458341698,
            'pc-%': options.value * 100,
            'pc-px': options.value * options.base,

            'pt-ch': options.value / 5.977584,
            'pt-cm': options.value / 28.3464566929,
            'pt-em': options.value / 11.955168,
            'pt-ex': options.value / 5.3798256,
            'pt-in': options.value * 0.014842519685,
            'pt-mm': options.value * 0.352777777778,
            'pt-pc': options.value * 0.0836458341698,
            'pt-%': options.value / (options.base - 4) * 100,
            'pt-px': options.value * 96 / 72,

            '%-ch': options.value / 50,
            '%-cm': options.value * options.base / 100 * 2.54 / options.dpi,
            '%-em': options.value / 100,
            '%-ex': options.value / 45,
            '%-in': options.value * options.base / 100 / options.dpi,
            '%-mm': options.value * options.base / 100 * 2.54 / options.dpi * 10,
            '%-pc': options.value / 100,
            '%-pt': options.value * (options.base - 4) / 100,
            '%-px': options.value * options.base / 100,

            'px-ch': options.value / options.base / 0.5,
            'px-cm': options.value * 2.54 / options.dpi,
            'px-em': options.value / options.base,
            'px-ex': options.value / options.base / 0.45,
            'px-in': options.value / options.dpi,
            'px-mm': options.value * 2.54 / options.dpi * 10,
            'px-pc': options.value / options.base,
            'px-pt': options.value * 72 / 96,
            'px-%': options.value / options.base * 100,
        };

        const result = formulas[units];

        return (isNaN(result) ? 'N/A' : round(result, options.decimals) + options.to);
    };

    const setupForm = () => {
        const settings = document.querySelectorAll('.settings input');
        const fragment = document.createDocumentFragment();
        const selects = document.querySelectorAll('select');
        const from = document.querySelector('.from');
        const units = ['ch', 'cm', 'em', 'ex', 'in', 'mm', 'pc', 'pt', '%', 'px'];

        // Set from value
        from.value = localStorage.getItem(from.name) || '';

        // Add the units
        units.forEach((unit) => {
            const option = document.createElement('option');

            option.value = unit;
            option.textContent = unit;

            fragment.appendChild(option);
        });

        // Select correct value
        Array.from(selects).forEach((select) => {
            const unit = localStorage.getItem(select.name);
            let selected;

            if (unit) {
                selected = fragment.querySelector(`[value="${ unit }"]`);
                selected.defaultSelected = true;
            }

            select.appendChild(fragment.cloneNode(true));

            if (selected) {
                selected.defaultSelected = false;
            }
        });

        // Add settings
        Array.from(settings).forEach((setting) => {
            setting.value = localStorage.getItem(setting.name) || setting.value;
        });
    };

    const run = () => {
        document.querySelector('.result').textContent = convert({
            base: document.querySelector('.base-size').value,
            decimals: document.querySelector('.decimals').value,
            dpi: document.querySelector('.dpi').value,
            from: document.querySelector('.from-unit').value,
            to: document.querySelector('.to-unit').value,
            value: document.querySelector('.from').value,
        });
    };

    window.addEventListener('DOMContentLoaded', () => {
        const elements = document.querySelectorAll('input, select');

        setupForm();

        Array.from(elements).forEach((element) => {
            element.addEventListener('change', run);
            element.addEventListener('keyup', run);
        });

        // Toogle display of setting forms
        document.querySelector('.toggle').addEventListener('click', () => {
            document.querySelector('.settings').classList.toggle('show');
        });

        // Save all values once every 30 seconds
        setInterval(() => {
            Array.from(elements).forEach((element) => {
                localStorage.setItem(element.name, element.value);
            });
        }, 30000);
    });
})(window, document);
<commit_msg>Disable ESLint sort-keys for unit definitions<commit_after>((window, document) => {
    'use strict';

    const round = (number, decimals) => {
        return Math.round(number * Math.pow(10, decimals)) / Math.pow(10, decimals);
    };

    const convert = (options) => {
        /* eslint-disable sort-keys */
        const units = `${ options.from }-${ options.to }`;
        const formulas = {
            'ch-cm': options.value * 0.21087588,
            'ch-em': options.value * 0.5,
            'ch-ex': options.value / 0.9,
            'ch-in': options.value * 0.083022,
            'ch-mm': options.value * 2.1087588,
            'ch-pc': options.value * 0.5,
            'ch-pt': options.value * 5.977584,
            'ch-%': options.value * 50,
            'ch-px': options.value * options.base * 0.5,

            'cm-ch': options.value / 0.21087588,
            'cm-em': options.value / 0.42175176,
            'cm-ex': options.value / 0.189788292,
            'cm-in': options.value * 0.39,
            'cm-mm': options.value * 10,
            'cm-pc': options.value / 0.42175176,
            'cm-pt': options.value * 28.3464566929,
            'cm-%': options.value / options.base * 100 / 2.54 * options.dpi,
            'cm-px': options.value / 2.54 * options.dpi,

            'em-ch': options.value / 0.5,
            'em-cm': options.value * 0.42175176,
            'em-ex': options.value / 0.45,
            'em-in': options.value * 0.166044,
            'em-mm': options.value / 0.237106301584,
            'em-pc': options.value,
            'em-pt': options.value * 11.955168,
            'em-%': options.value * 100,
            'em-px': options.value * options.base,

            'ex-ch': options.value * 0.9,
            'ex-cm': options.value * 0.189788292,
            'ex-em': options.value * 0.45,
            'ex-in': options.value * 0.0747198,
            'ex-mm': options.value * 1.89788292,
            'ex-pc': options.value * 0.45,
            'ex-pt': options.value * 5.3798256,
            'ex-%': options.value * 45,
            'ex-px': options.value * options.base * 0.45,

            'in-ch': options.value / 0.083022,
            'in-cm': options.value * 2.54,
            'in-em': options.value / 0.166044,
            'in-ex': options.value / 0.0747198,
            'in-mm': options.value * 2.54 * 10,
            'in-pc': options.value / 0.166044,
            'in-pt': options.value / 0.014842519685,
            'in-%': options.value / options.base * 100 * options.dpi,
            'in-px': options.value * options.dpi,

            'mm-ch': options.value / 2.1087588,
            'mm-cm': options.value / 10,
            'mm-em': options.value * 0.237106301584,
            'mm-ex': options.value / 1.89788292,
            'mm-in': options.value * 0.39 / 10,
            'mm-pc': options.value / 4.42175176,
            'mm-pt': options.value / 0.352777777778,
            'mm-%': options.value / options.base * 100 / 2.54 * options.dpi / 10,
            'mm-px': options.value / 2.54 * options.dpi / 10,

            'pc-ch': options.value / 0.5,
            'pc-cm': options.value * 0.42175176,
            'pc-em': options.value,
            'pc-ex': options.value / 0.45,
            'pc-in': options.value * 0.166044,
            'pc-mm': options.value * 4.42175176,
            'pc-pt': options.value / 0.0836458341698,
            'pc-%': options.value * 100,
            'pc-px': options.value * options.base,

            'pt-ch': options.value / 5.977584,
            'pt-cm': options.value / 28.3464566929,
            'pt-em': options.value / 11.955168,
            'pt-ex': options.value / 5.3798256,
            'pt-in': options.value * 0.014842519685,
            'pt-mm': options.value * 0.352777777778,
            'pt-pc': options.value * 0.0836458341698,
            'pt-%': options.value / (options.base - 4) * 100,
            'pt-px': options.value * 96 / 72,

            '%-ch': options.value / 50,
            '%-cm': options.value * options.base / 100 * 2.54 / options.dpi,
            '%-em': options.value / 100,
            '%-ex': options.value / 45,
            '%-in': options.value * options.base / 100 / options.dpi,
            '%-mm': options.value * options.base / 100 * 2.54 / options.dpi * 10,
            '%-pc': options.value / 100,
            '%-pt': options.value * (options.base - 4) / 100,
            '%-px': options.value * options.base / 100,

            'px-ch': options.value / options.base / 0.5,
            'px-cm': options.value * 2.54 / options.dpi,
            'px-em': options.value / options.base,
            'px-ex': options.value / options.base / 0.45,
            'px-in': options.value / options.dpi,
            'px-mm': options.value * 2.54 / options.dpi * 10,
            'px-pc': options.value / options.base,
            'px-pt': options.value * 72 / 96,
            'px-%': options.value / options.base * 100,
        };

        const result = formulas[units];

        return (isNaN(result) ? 'N/A' : round(result, options.decimals) + options.to);
    };

    const setupForm = () => {
        const settings = document.querySelectorAll('.settings input');
        const fragment = document.createDocumentFragment();
        const selects = document.querySelectorAll('select');
        const from = document.querySelector('.from');
        const units = ['ch', 'cm', 'em', 'ex', 'in', 'mm', 'pc', 'pt', '%', 'px'];

        // Set from value
        from.value = localStorage.getItem(from.name) || '';

        // Add the units
        units.forEach((unit) => {
            const option = document.createElement('option');

            option.value = unit;
            option.textContent = unit;

            fragment.appendChild(option);
        });

        // Select correct value
        Array.from(selects).forEach((select) => {
            const unit = localStorage.getItem(select.name);
            let selected;

            if (unit) {
                selected = fragment.querySelector(`[value="${ unit }"]`);
                selected.defaultSelected = true;
            }

            select.appendChild(fragment.cloneNode(true));

            if (selected) {
                selected.defaultSelected = false;
            }
        });

        // Add settings
        Array.from(settings).forEach((setting) => {
            setting.value = localStorage.getItem(setting.name) || setting.value;
        });
    };

    const run = () => {
        document.querySelector('.result').textContent = convert({
            base: document.querySelector('.base-size').value,
            decimals: document.querySelector('.decimals').value,
            dpi: document.querySelector('.dpi').value,
            from: document.querySelector('.from-unit').value,
            to: document.querySelector('.to-unit').value,
            value: document.querySelector('.from').value,
        });
    };

    window.addEventListener('DOMContentLoaded', () => {
        const elements = document.querySelectorAll('input, select');

        setupForm();

        Array.from(elements).forEach((element) => {
            element.addEventListener('change', run);
            element.addEventListener('keyup', run);
        });

        // Toogle display of setting forms
        document.querySelector('.toggle').addEventListener('click', () => {
            document.querySelector('.settings').classList.toggle('show');
        });

        // Save all values once every 30 seconds
        setInterval(() => {
            Array.from(elements).forEach((element) => {
                localStorage.setItem(element.name, element.value);
            });
        }, 30000);
    });
})(window, document);
<|endoftext|>


<commit_before>import React, { PropTypes } from 'react'
import { Header, HeaderTitle } from 'components/Header'
import Navigation from 'components/Navigation'
import QueueItem from './QueueItem'

class QueueView extends React.Component {
  static propTypes = {
    result: PropTypes.array.isRequired,
    entities: PropTypes.object.isRequired,
    errors: PropTypes.object,
    curId: PropTypes.number,
    curPos: PropTypes.number,
    isFinished: PropTypes.bool.isRequired,
    // library
    artistIds: PropTypes.array.isRequired,
    artists: PropTypes.object.isRequired,
    songIds: PropTypes.array.isRequired,
    songs: PropTypes.object.isRequired,
    // user
    user: PropTypes.object.isRequired,
    // actions
    requestPlay: PropTypes.func.isRequired,
    requestPlayNext: PropTypes.func.isRequired,
    requestPause: PropTypes.func.isRequired,
  }

  render() {
    let songs = []
    let wait = 0
    let nextWait = 0

    songs = this.props.result.map(function(queueId, i) {
      const { curId, curPos } = this.props

      const item = this.props.entities[queueId]
      const song = this.props.songs[item.songId]
      const isActive = (item.queueId === curId) && !this.props.isFinished
      const isUpcoming = queueId > curId
      const isOwner = item.userId === this.props.user.userId
      const isAdmin = this.props.user.isAdmin === 1

      // hand time-to-play to the next queue item
      if (isActive) {
        nextWait = Math.round(song.duration - curPos)
      } else if (isUpcoming) {
        wait += nextWait
        nextWait = song.duration
      }

      return (
        <QueueItem
          key={queueId}
          artist={this.props.artists[song.artistId].name}
          title={song.title}
          userName={item.userName}
          isActive={isActive}
          isUpcoming={isUpcoming}
          wait={isUpcoming && wait ? secToTime(wait) : ''}
          canSkip={isActive && (isOwner || isAdmin)}
          canRemove={isUpcoming && (isOwner || isAdmin)}
          hasErrors={typeof this.props[queueId] !== 'undefined'}
          pctPlayed={isActive ? curPos / song.duration * 100 : 0}
          onRemoveClick={this.handleRemoveClick.bind(this, queueId)}
          onSkipClick={this.props.requestPlayNext}
          onErrorInfoClick={this.handleErrorInfoClick.bind(this, queueId)}
        />
      )
    }, this)

    return (

        {songs}

    )
  }

  handleRemoveClick(queueId) {
    this.props.removeItem(queueId)
  }

  handleErrorInfoClick(queueId) {
    alert(this.props.errors[queueId].join("\n\n"))
  }
}

export default QueueView

function secToTime(sec) {
  if (sec >= 60) {
    return Math.round(sec/60) + ' min'
  } else {
    return (Math.floor(sec/5)+1)*5 + ' sec'
  }
}

// function getTime(sec) {
//   const date = new Date()
//   if (sec) date.setSeconds(date.getSeconds() + sec)
//   return date.toTimeString().split(' ')[0]
// }

// function toMinSec(sec) {
//   const m = Math.floor(sec / 60)
//   const s = sec - (m * 60)
//   return (m + ':' + (s < 10 ? '0' + s : s))
// }
<commit_msg>Estimate to nearest second<commit_after>import React, { PropTypes } from 'react'
import { Header, HeaderTitle } from 'components/Header'
import Navigation from 'components/Navigation'
import QueueItem from './QueueItem'

class QueueView extends React.Component {
  static propTypes = {
    result: PropTypes.array.isRequired,
    entities: PropTypes.object.isRequired,
    errors: PropTypes.object,
    curId: PropTypes.number,
    curPos: PropTypes.number,
    isFinished: PropTypes.bool.isRequired,
    // library
    artistIds: PropTypes.array.isRequired,
    artists: PropTypes.object.isRequired,
    songIds: PropTypes.array.isRequired,
    songs: PropTypes.object.isRequired,
    // user
    user: PropTypes.object.isRequired,
    // actions
    requestPlay: PropTypes.func.isRequired,
    requestPlayNext: PropTypes.func.isRequired,
    requestPause: PropTypes.func.isRequired,
  }

  render() {
    let songs = []
    let wait = 0
    let nextWait = 0

    songs = this.props.result.map(function(queueId, i) {
      const { curId, curPos } = this.props

      const item = this.props.entities[queueId]
      const song = this.props.songs[item.songId]
      const isActive = (item.queueId === curId) && !this.props.isFinished
      const isUpcoming = queueId > curId
      const isOwner = item.userId === this.props.user.userId
      const isAdmin = this.props.user.isAdmin === 1

      // hand time-to-play to the next queue item
      if (isActive) {
        nextWait = Math.round(song.duration - curPos)
      } else if (isUpcoming) {
        wait += nextWait
        nextWait = song.duration
      }

      return (
        <QueueItem
          key={queueId}
          artist={this.props.artists[song.artistId].name}
          title={song.title}
          userName={item.userName}
          isActive={isActive}
          isUpcoming={isUpcoming}
          wait={isUpcoming && wait ? secToTime(wait) : ''}
          canSkip={isActive && (isOwner || isAdmin)}
          canRemove={isUpcoming && (isOwner || isAdmin)}
          hasErrors={typeof this.props[queueId] !== 'undefined'}
          pctPlayed={isActive ? curPos / song.duration * 100 : 0}
          onRemoveClick={this.handleRemoveClick.bind(this, queueId)}
          onSkipClick={this.props.requestPlayNext}
          onErrorInfoClick={this.handleErrorInfoClick.bind(this, queueId)}
        />
      )
    }, this)

    return (

        {songs}

    )
  }

  handleRemoveClick(queueId) {
    this.props.removeItem(queueId)
  }

  handleErrorInfoClick(queueId) {
    alert(this.props.errors[queueId].join("\n\n"))
  }
}

export default QueueView

function secToTime(sec) {
  if (sec >= 60) {
    return Math.round(sec/60) + ' min'
  } else {
    return Math.floor(sec) + ' sec'
  }
}

// function getTime(sec) {
//   const date = new Date()
//   if (sec) date.setSeconds(date.getSeconds() + sec)
//   return date.toTimeString().split(' ')[0]
// }

// function toMinSec(sec) {
//   const m = Math.floor(sec / 60)
//   const s = sec - (m * 60)
//   return (m + ':' + (s < 10 ? '0' + s : s))
// }
<|endoftext|>


<commit_before>var $ = require('jquery');
require('mapbox.js'); // attaches "automatically" to window.L

$(function(){
	L.mapbox.map( 'map', 'fauntleroy.hfnj60nk' );
});<commit_msg>temporarily center map immediately on SF<commit_after>var $ = require('jquery');
require('mapbox.js'); // attaches "automatically" to window.L

$(function(){
	var map = L.mapbox.map( 'map', 'fauntleroy.hfnj60nk' );
	map.setView( [ 37.78, -122.419 ], 13 ); // temporary. This should eventually default
});<|endoftext|>