<commit_before>'use strict';

const RigidDB = require('rigiddb');
const User = require('../models/user');
const Conversation = require('../models/conversation');
const ConversationMember = require('../models/conversationMember');
const ConversationMessage = require('../models/conversationMessage');
const Window = require('../models/window');
const Friend = require('../models/friend');
const Settings = require('../models/settings');
const UserGId = require('../models/userGId');
const NetworkInfo = require('../models/networkInfo');

const REVISION = 1;

const store = new RigidDB('mas', REVISION, { db: 10 });
const allUserIds = [];
const activeUserIds = [];
const conversations = [];

main();

process.on('unhandledRejection', (reason, p) => {
    log(`Unhandled Rejection at: Promise ${p}, reason: ${reason}`);
    throw reason;
});

async function main() {
    const { val: userIds } = await store.list('users');

    log(`${userIds.length} users found.`);

    for (const userId of userIds) {
        const user = await User.fetch(userId);
        allUserIds.push(userId);

        if (!user.get('deleted')) {
            activeUserIds.push(userId);
        }
    }

    log(`${activeUserIds.length} active users found.`);

    await checkConversations();
    await checkWindows();
    await checkConversationMembers();
    await checkSettings();
    await checkFriends();
    await checkConversationMessages();
    await checkNetworkInfos();

    log('Done.');
    await store.quit();
}

async function checkConversations() {
    const { val: conversationIds } = await store.list('conversations');

    log('Checking that conversations have active user as an owner...');
    log(`${conversationIds.length} conversations found.`);

    for (const conversationId of conversationIds) {
        conversations.push(conversationId);

        const conversation = await Conversation.fetch(conversationId);
        const ownerUserId = conversation.get('owner');

        if (ownerUserId && !activeUserIds.find(activeUserId => activeUserId === ownerUserId)) {
            log(`Conversation has deleted user as an owner: ${JSON.stringify(conversation._props)}`);
        }
    }
}

async function checkWindows() {
    const { val: windowIds } = await store.list('windows');

    log('Checking windows...');
    log(`${windowIds.length} windows found.`);

    for (const windowId of windowIds) {
        const window = await Window.fetch(windowId);
        const userId = window.get('userId');
        const conversationId = window.get('conversationId');

        if (!activeUserIds.find(activeUserId => activeUserId === userId)) {
            log('Found window that belongs to deleted user');
        }

        if (!conversations.find(
            existingConversationId => existingConversationId === conversationId)) {
            log('Found window without conversation');
        }
    }
}

async function checkConversationMembers() {
    const { val: conversationMemberIds } = await store.list('conversationMembers');

    log('Checking conversationMembers...');
    log(`${conversationMemberIds.length} conversationMembers found.`);

    for (const conversationMemberId of conversationMemberIds) {
        const conversationMember = await ConversationMember.fetch(conversationMemberId);
        const userGId = UserGId.create(conversationMember.get('userGId'));
        const conversationId = conversationMember.get('conversationId');

        if (userGId.type === 'mas') {
            if (!activeUserIds.find(activeUserId => activeUserId === userGId.id)) {
                log('Found conversationMember that is a deleted user. Fixing');

                await conversationMember.delete();
            }
        }

        if (!conversations.find(
            existingConversationId => existingConversationId === conversationId)) {
            log('Found conversationMember that points to non-existent conversation');
        }
    }
}

async function checkSettings() {
    log('Checking that all users have settings...');

    for (const userId of activeUserIds) {
        const settings = await Settings.findFirst({ userId });

        if (!settings) {
            log(`User ${userId} doesn't have settings. Fixing`);
            await Settings.create({ userId });
        }
    }

    log('Checking that settings belong to active users...');

    const { val: settingIds } = await store.list('settings');

    log(`${settingIds.length} settings found.`);

    for (const settingId of settingIds) {
        const settings = await Settings.fetch(settingId);

        const userId = settings.get('userId');

        if (!activeUserIds.find(activeUserId => activeUserId === userId)) {
            log('Found settings that belongs to deleted user');
        }
    }
}

async function checkFriends() {
    log('Checking friend objects are valid...');

    const { val: friendIds } = await store.list('friends');

    log(`${friendIds.length} friend objects found.`);

    const srcFriends = [];
    const dstFriends = [];

    for (const friendId of friendIds) {
        const friend = await Friend.fetch(friendId);

        const srcUserId = friend.get('srcUserId');
        srcFriends.push(srcUserId);

        const dstUserId = friend.get('dstUserId');
        dstFriends.push(dstUserId);

        if (!activeUserIds.find(activeUserId => activeUserId === srcUserId)) {
            log('Found friendUserId that belongs to deleted user');
        }

        if (!activeUserIds.find(activeUserId => activeUserId === dstUserId)) {
            log('Found friendUserId that belongs to deleted user');
        }

        const counterFried = await Friend.findFirst({ srcUserId: dstUserId, dstUserId: srcUserId });

        if (!counterFried) {
            log(`Friendship ${JSON.stringify(friend._props)} doesn't have counter friend object. Fixing by deleting.`);
            await friend.delete();
        }
    }
}

async function checkConversationMessages() {
    log('Checking conversationMessage objects are valid...');

    const { val: conversationMessageIds } = await store.list('conversationMessages');

    log(`${conversationMessageIds.length} conversationMessage objects found.`);

    for (const conversationMessageId of conversationMessageIds) {
        const conversationMessage = await ConversationMessage.fetch(conversationMessageId);
        const conversationId = conversationMessage.get('conversationId');
        const userGIdString = conversationMessage.get('userGId');

        if (!conversations.find(
            existingConversationId => existingConversationId === conversationId)) {
            log(`Found conversationMessage ${JSON.stringify(conversationMessage._props)} that points to non-existent conversation. Fixing by deleting.`);
            await conversationMessage.delete();
            continue;
        }

        if (userGIdString) {
            const userGId = UserGId.create(userGIdString);

            if (userGId.type === 'mas') {
                if (!allUserIds.find(userId => userId === userGId.id)) {
                    log(`Found message ${JSON.stringify(conversationMessage._props)} that belongs to non-existent user. Fixing by deleting.`);
                    await conversationMessage.delete();
                }
            } else {
                const decoded = new Buffer(userGId.id, 'base64').toString('ascii');

                if (decoded.indexOf('!') === -1) {
                    log(`Found corrupted IRC userGId: ${userGId.id}, decoded: ${decoded}`);
                }
            }
        }
    }
}

async function checkNetworkInfos() {
    log('Checking networkInfo objects are valid...');

    const { val: networkInfoIds } = await store.list('networkInfos');

    for (const networkInfoId of networkInfoIds) {
        const networkInfo = await NetworkInfo.fetch(networkInfoId);

        if (!allUserIds.find(userId => userId === networkInfo.get('userId'))) {
            log('Found networkInfo that belongs to non-existent user.');
        }
    }
}

function log(msg) {
    console.log(msg); // eslint-disable-line no-console
}
<commit_msg>Improve rigiddb-integrity-check script<commit_after>'use strict';

const readlineSync = require('readline-sync');
const RigidDB = require('rigiddb');
const User = require('../models/user');
const Conversation = require('../models/conversation');
const ConversationMember = require('../models/conversationMember');
const ConversationMessage = require('../models/conversationMessage');
const Window = require('../models/window');
const Friend = require('../models/friend');
const Settings = require('../models/settings');
const UserGId = require('../models/userGId');
const NetworkInfo = require('../models/networkInfo');

const REVISION = 1;

const store = new RigidDB('mas', REVISION, { db: 10 });
const allUserIds = [];
const activeUserIds = [];
const conversations = [];

main();

process.on('unhandledRejection', (reason, p) => {
    log(`Unhandled Rejection at: Promise ${p}, reason: ${reason}`);
    throw reason;
});

log('');
log('*** Last line of defence, only needed in special cases ***');
log('');

const fix = readlineSync.question('Fix issues in addition to reporting them [yes/NO]? ') === 'yes';

async function main() {
    const { val: userIds } = await store.list('users');

    log(`${userIds.length} users found.`);

    for (const userId of userIds) {
        const user = await User.fetch(userId);
        allUserIds.push(userId);

        if (!user.get('deleted')) {
            activeUserIds.push(userId);
        }
    }

    log(`${activeUserIds.length} active users found.`);

    await checkConversations();
    await checkWindows();
    await checkConversationMembers();
    await checkSettings();
    await checkFriends();
    await checkConversationMessages();
    await checkNetworkInfos();

    log('Done.');
    await store.quit();
}

async function checkConversations() {
    const { val: conversationIds } = await store.list('conversations');

    log('Checking that conversations have active user as an owner...');
    log(`${conversationIds.length} conversations found.`);

    for (const conversationId of conversationIds) {
        conversations.push(conversationId);

        const conversation = await Conversation.fetch(conversationId);
        const ownerUserId = conversation.get('owner');

        if (ownerUserId && !activeUserIds.find(activeUserId => activeUserId === ownerUserId)) {
            log(`!!! Conversation has deleted user as an owner: ${JSON.stringify(conversation._props)}`);
        }
    }
}

async function checkWindows() {
    const { val: windowIds } = await store.list('windows');

    log('Checking windows...');
    log(`${windowIds.length} windows found.`);

    for (const windowId of windowIds) {
        const window = await Window.fetch(windowId);
        const userId = window.get('userId');
        const conversationId = window.get('conversationId');

        if (!activeUserIds.find(activeUserId => activeUserId === userId)) {
            log('!!! Found window that belongs to deleted user');

            if (fix) {
                log('Fixing by deleting');
                await window.delete();
            }
        }

        if (!conversations.find(
            existingConversationId => existingConversationId === conversationId)) {
            log('!!! Found window without conversation');

            if (fix) {
                log('Fixing by deleting');
                await window.delete();
            }
        }
    }
}

async function checkConversationMembers() {
    const { val: conversationMemberIds } = await store.list('conversationMembers');

    log('Checking conversationMembers...');
    log(`${conversationMemberIds.length} conversationMembers found.`);

    for (const conversationMemberId of conversationMemberIds) {
        const conversationMember = await ConversationMember.fetch(conversationMemberId);

        if (!conversationMember) {
            // Active user, has parted already
            continue;
        }

        const userGId = UserGId.create(conversationMember.get('userGId'));
        const conversationId = conversationMember.get('conversationId');

        if (userGId.type === 'mas') {
            if (!activeUserIds.find(activeUserId => activeUserId === userGId.id)) {
                log('!!! Found conversationMember that is a deleted user.');

                if (fix) {
                    log('Fixing');
                    await conversationMember.delete();
                }
            }
        }

        if (!conversations.find(
            existingConversationId => existingConversationId === conversationId)) {
            log('!!! Found conversationMember that points to non-existent conversation');

            if (fix) {
                log('Fixing by deleting');
                await conversationMember.delete();
            }
        }
    }
}

async function checkSettings() {
    log('Checking that all users have settings...');

    for (const userId of activeUserIds) {
        const settings = await Settings.findFirst({ userId });

        if (!settings) {
            log(`!!! User ${userId} doesn't have settings.`);

            if (fix) {
                log('Fixing by creating');
                await Settings.create({ userId });
            }
        }
    }

    log('Checking that settings belong to active users...');

    const { val: settingIds } = await store.list('settings');

    log(`${settingIds.length} settings found.`);

    for (const settingId of settingIds) {
        const settings = await Settings.fetch(settingId);

        const userId = settings.get('userId');

        if (!activeUserIds.find(activeUserId => activeUserId === userId)) {
            log('!!! Found settings that belongs to deleted user');

            if (fix) {
                log('Fixing by deleting');
                await settings.delete();
            }
        }
    }
}

async function checkFriends() {
    log('Checking friend objects are valid...');

    const { val: friendIds } = await store.list('friends');

    log(`${friendIds.length} friend objects found.`);

    const srcFriends = [];
    const dstFriends = [];

    for (const friendId of friendIds) {
        const friend = await Friend.fetch(friendId);

        const srcUserId = friend.get('srcUserId');
        srcFriends.push(srcUserId);

        const dstUserId = friend.get('dstUserId');
        dstFriends.push(dstUserId);

        if (!activeUserIds.find(activeUserId => activeUserId === srcUserId)) {
            log('!!! Found friendUserId that belongs to deleted user');
        }

        if (!activeUserIds.find(activeUserId => activeUserId === dstUserId)) {
            log('!!! Found friendUserId that belongs to deleted user');
        }

        const counterFried = await Friend.findFirst({ srcUserId: dstUserId, dstUserId: srcUserId });

        if (!counterFried) {
            log(`!!! Friendship ${JSON.stringify(friend._props)} doesn't have counter friend object.`);

            if (fix) {
                log('Fixing by deleting.');
                await friend.delete();
            }
        }
    }
}

async function checkConversationMessages() {
    log('Checking conversationMessage objects are valid...');

    const { val: conversationMessageIds } = await store.list('conversationMessages');

    log(`${conversationMessageIds.length} conversationMessage objects found.`);

    for (const conversationMessageId of conversationMessageIds) {
        const conversationMessage = await ConversationMessage.fetch(conversationMessageId);

        if (!conversationMessage) {
            // Active group, old message was just deleted
            continue;
        }

        const conversationId = conversationMessage.get('conversationId');
        const userGIdString = conversationMessage.get('userGId');

        if (!conversations.find(
            existingConversationId => existingConversationId === conversationId)) {
            log(`!!! Found conversationMessage ${JSON.stringify(conversationMessage._props)} that points to non-existent conversation.`);

            if (fix) {
                log('Fixing by deleting.');
                await conversationMessage.delete();
            }

            continue;
        }

        if (userGIdString) {
            const userGId = UserGId.create(userGIdString);

            if (userGId.type === 'mas') {
                if (!allUserIds.find(userId => userId === userGId.id)) {
                    log(`!!! Found message ${JSON.stringify(conversationMessage._props)} that belongs to non-existent user.`);

                    if (fix) {
                        log('Fixing by deleting.');
                        await conversationMessage.delete();
                    }
                }
            }
        }
    }
}

async function checkNetworkInfos() {
    log('Checking networkInfo objects are valid...');

    const { val: networkInfoIds } = await store.list('networkInfos');

    for (const networkInfoId of networkInfoIds) {
        const networkInfo = await NetworkInfo.fetch(networkInfoId);

        if (!allUserIds.find(userId => userId === networkInfo.get('userId'))) {
            log('!!! Found networkInfo that belongs to non-existent user.');
        }
    }
}

function log(msg) {
    console.log(msg); // eslint-disable-line no-console
}
<|endoftext|>


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

    angular
        .module('GVA.Voting')
        .directive('gvaResultsChart', resultsChart);

    resultsChart.$inject = ['$window'];

    function resultsChart($window) {

        function link($scope) {

            // Keep track of mouse position
            var mouseXPos = null;
            var mouseYPos = null;

            document.addEventListener('mousemove', onMouseUpdate, false);
            document.addEventListener('mouseenter', onMouseUpdate, false);

            function onMouseUpdate(e) {
                mouseXPos = e.pageX;
                mouseYPos = e.pageY;
            }

            var canvas = document.createElement('canvas');

            $scope.$watch('data', function (newVal, oldVal) {
                if ((JSON.stringify(newVal) !== JSON.stringify(oldVal))) {
                    drawChart();
                }
            });

            $window.addEventListener('resize', drawChart);

            function textWidth(text) {
                var context = canvas.getContext('2d');
                context.font = '16px Open Sans';
                var metrics = context.measureText(text);
                return metrics.width;
            }

            function tickFrequencyForRange(range) {
                var tickFrequency = 1;
                var step = 0;

                while (range / tickFrequency >= 10) {
                    // Multiply by: 2x, 5x, 10x, 20x, 50x, 100x, etc.
                    var multiplier = (step % 3 === 1) ? 2.5 : 2;
                    tickFrequency = tickFrequency * multiplier;

                    step++;
                }

                return tickFrequency;
            }

            function drawChart() {
                // Hack to fix lack of data reloading
                var tooltips = document.getElementsByClassName('d3-tip');
                for (var i = 0; i < tooltips.length; i++) {
                    var element = tooltips[i];
                    element.parentElement.removeChild(element);
                }

                var chartElement = document.getElementById('inner-chart');
                chartElement.innerHTML = '';

                var data = $scope.data.sort(function (a, b) { return b.Sum - a.Sum; });
                // Trim long names
                data.map(function (result) {
                    if (result.Name.length > 20) {
                        result.Name = result.Name.substring(0, 20) + '...';
                    }
                    return result;
                });

                var chartHeight = Math.min(data.length * 60 + 30, 600);
                var chartWidth = chartElement.parentElement.parentElement.offsetWidth;

                var longestTextWidth = d3.max(data, function (d) { return textWidth(d.Name); });

                var margin = { top: 30, right: 10, bottom: 10, left: longestTextWidth + 10 };
                var width = chartWidth - margin.left - margin.right;
                var height = chartHeight - margin.top - margin.bottom;

                var minX = d3.min(data, function (d) { return d.Sum; });
                var maxX = d3.max(data, function (d) { return d.Sum; });

                var x = d3.scale.linear()
                    .range([0, width])
                    .domain([Math.min(0, minX), Math.max(0, maxX)])
                    .nice();

                var dataRange = x.domain()[1] - x.domain()[0];
                var tickFrequency = tickFrequencyForRange(dataRange);

                var y = d3.scale.ordinal()
                    .rangeRoundBands([0, height], 0.1)
                    .domain(data.map(function (d) { return d.Name; }));

                var xAxis = d3.svg.axis()
                    .scale(x)
                    .orient('top')
                    .ticks(dataRange / tickFrequency); // Ticks is *how many* ticks we display, not the frequency...

                var yAxis = d3.svg.axis()
                    .scale(y)
                    .orient('left');

                var tooltip = d3.tip()
                    .attr('class', 'd3-tip')
                    .offset([-10, 0])
                    .html(createToolTipText);

                var chart = d3.select(chartElement)
                    .append('svg')
                        .attr('width', chartWidth)
                        .attr('height', chartHeight)
                    .append('g')
                        .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

                chart.append('g')
                    .attr('class', 'x axis')
                    .call(xAxis);

                chart.append('g')
                    .attr('class', 'y axis')
                    .call(yAxis)
                    .append('line')
                        .attr('x1', x(0))
                        .attr('x2', x(0))
                        .attr('y2', height);

                chart.selectAll('.bar')
                    .data(data)
                    .enter()
                    .append('rect')
                        .attr('class', function (d) { return d.Sum < 0 ? 'bar negative' : 'bar positive'; })
                        .attr('x', function (d) {
                            return x(Math.min(0, d.Sum));
                        })
                        .attr('y', function (d) {
                            return y(d.Name);
                        })
                        .attr('width', function (d) {
                            return Math.abs(x(-d.Sum) - x(0));
                        })
                        .attr('height', y.rangeBand())
                        .on('mouseover', tooltip.show)
                        .on('mouseout', tooltip.hide);

                chart.call(tooltip);

                // Hack to simiulate a mouseover
                var mouseElement = document.elementFromPoint(mouseXPos, mouseYPos);
                if (mouseElement) {
                    var eventObj = document.createEvent('Events');
                    eventObj.initEvent('mouseover', true, false);
                    mouseElement.dispatchEvent(eventObj);
                }
            }

            function createToolTipText(result) {

                var tooltipText;

                if (result.Voters.every(function (item) { return item.Name === null; })) {
                    tooltipText = createToolTipHeader(result);
                }
                else {
                    var voterNames = result.Voters
                        .slice(0, 5)
                        .map(function (d) {
                            return d.Name + ' (' + d.Value + ')';
                        });

                    tooltipText = createToolTipHeader(result) + '
' + voterNames.join('
');

                    // Clip for more than 5 voters
                    if (result.Voters.length > 5) {
                        tooltipText += '
+ ' + (result.Voters.length - 5) + ' other';

                        if ((result.Voters.length - 5) > 1) {
                            tooltipText += 's';
                        }
                    }
                }

                return tooltipText;
            }

            function createToolTipHeader(result) {
                var header = '' + result.Name + ': ' + result.Sum + ' vote';
                if (result.Sum > 1) {
                    header += 's';
                }

                return header;
            }
        }

        return {
            restrict: 'E',
            scope: {
                data: '='
            },
            link: link,
            templateUrl: '/Scripts/Directives/ResultsChart.html'
        };
    }
})();<commit_msg>Spelling<commit_after>(function () {
    'use strict';

    angular
        .module('GVA.Voting')
        .directive('gvaResultsChart', resultsChart);

    resultsChart.$inject = ['$window'];

    function resultsChart($window) {

        function link($scope) {

            // Keep track of mouse position
            var mouseXPos = null;
            var mouseYPos = null;

            document.addEventListener('mousemove', onMouseUpdate, false);
            document.addEventListener('mouseenter', onMouseUpdate, false);

            function onMouseUpdate(e) {
                mouseXPos = e.pageX;
                mouseYPos = e.pageY;
            }

            var canvas = document.createElement('canvas');

            $scope.$watch('data', function (newVal, oldVal) {
                if ((JSON.stringify(newVal) !== JSON.stringify(oldVal))) {
                    drawChart();
                }
            });

            $window.addEventListener('resize', drawChart);

            function textWidth(text) {
                var context = canvas.getContext('2d');
                context.font = '16px Open Sans';
                var metrics = context.measureText(text);
                return metrics.width;
            }

            function tickFrequencyForRange(range) {
                var tickFrequency = 1;
                var step = 0;

                while (range / tickFrequency >= 10) {
                    // Multiply by: 2x, 5x, 10x, 20x, 50x, 100x, etc.
                    var multiplier = (step % 3 === 1) ? 2.5 : 2;
                    tickFrequency = tickFrequency * multiplier;

                    step++;
                }

                return tickFrequency;
            }

            function drawChart() {
                // Hack to fix lack of data reloading
                var tooltips = document.getElementsByClassName('d3-tip');
                for (var i = 0; i < tooltips.length; i++) {
                    var element = tooltips[i];
                    element.parentElement.removeChild(element);
                }

                var chartElement = document.getElementById('inner-chart');
                chartElement.innerHTML = '';

                var data = $scope.data.sort(function (a, b) { return b.Sum - a.Sum; });
                // Trim long names
                data.map(function (result) {
                    if (result.Name.length > 20) {
                        result.Name = result.Name.substring(0, 20) + '...';
                    }
                    return result;
                });

                var chartHeight = Math.min(data.length * 60 + 30, 600);
                var chartWidth = chartElement.parentElement.parentElement.offsetWidth;

                var longestTextWidth = d3.max(data, function (d) { return textWidth(d.Name); });

                var margin = { top: 30, right: 10, bottom: 10, left: longestTextWidth + 10 };
                var width = chartWidth - margin.left - margin.right;
                var height = chartHeight - margin.top - margin.bottom;

                var minX = d3.min(data, function (d) { return d.Sum; });
                var maxX = d3.max(data, function (d) { return d.Sum; });

                var x = d3.scale.linear()
                    .range([0, width])
                    .domain([Math.min(0, minX), Math.max(0, maxX)])
                    .nice();

                var dataRange = x.domain()[1] - x.domain()[0];
                var tickFrequency = tickFrequencyForRange(dataRange);

                var y = d3.scale.ordinal()
                    .rangeRoundBands([0, height], 0.1)
                    .domain(data.map(function (d) { return d.Name; }));

                var xAxis = d3.svg.axis()
                    .scale(x)
                    .orient('top')
                    .ticks(dataRange / tickFrequency); // Ticks is *how many* ticks we display, not the frequency...

                var yAxis = d3.svg.axis()
                    .scale(y)
                    .orient('left');

                var tooltip = d3.tip()
                    .attr('class', 'd3-tip')
                    .offset([-10, 0])
                    .html(createToolTipText);

                var chart = d3.select(chartElement)
                    .append('svg')
                        .attr('width', chartWidth)
                        .attr('height', chartHeight)
                    .append('g')
                        .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

                chart.append('g')
                    .attr('class', 'x axis')
                    .call(xAxis);

                chart.append('g')
                    .attr('class', 'y axis')
                    .call(yAxis)
                    .append('line')
                        .attr('x1', x(0))
                        .attr('x2', x(0))
                        .attr('y2', height);

                chart.selectAll('.bar')
                    .data(data)
                    .enter()
                    .append('rect')
                        .attr('class', function (d) { return d.Sum < 0 ? 'bar negative' : 'bar positive'; })
                        .attr('x', function (d) {
                            return x(Math.min(0, d.Sum));
                        })
                        .attr('y', function (d) {
                            return y(d.Name);
                        })
                        .attr('width', function (d) {
                            return Math.abs(x(-d.Sum) - x(0));
                        })
                        .attr('height', y.rangeBand())
                        .on('mouseover', tooltip.show)
                        .on('mouseout', tooltip.hide);

                chart.call(tooltip);

                // Hack to simulate a mouseover
                var mouseElement = document.elementFromPoint(mouseXPos, mouseYPos);
                if (mouseElement) {
                    var eventObj = document.createEvent('Events');
                    eventObj.initEvent('mouseover', true, false);
                    mouseElement.dispatchEvent(eventObj);
                }
            }

            function createToolTipText(result) {

                var tooltipText;

                if (result.Voters.every(function (item) { return item.Name === null; })) {
                    tooltipText = createToolTipHeader(result);
                }
                else {
                    var voterNames = result.Voters
                        .slice(0, 5)
                        .map(function (d) {
                            return d.Name + ' (' + d.Value + ')';
                        });

                    tooltipText = createToolTipHeader(result) + '
' + voterNames.join('
');

                    // Clip for more than 5 voters
                    if (result.Voters.length > 5) {
                        tooltipText += '
+ ' + (result.Voters.length - 5) + ' other';

                        if ((result.Voters.length - 5) > 1) {
                            tooltipText += 's';
                        }
                    }
                }

                return tooltipText;
            }

            function createToolTipHeader(result) {
                var header = '' + result.Name + ': ' + result.Sum + ' vote';
                if (result.Sum > 1) {
                    header += 's';
                }

                return header;
            }
        }

        return {
            restrict: 'E',
            scope: {
                data: '='
            },
            link: link,
            templateUrl: '/Scripts/Directives/ResultsChart.html'
        };
    }
})();<|endoftext|>


<commit_before>'use strict';

var srlog = SIREPO.srlog;
var srdbg = SIREPO.srdbg;

SIREPO.SINGLE_FRAME_ANIMATION = ['beamEvolutionAnimation'];
SIREPO.appFieldEditors = [
    '
',
      '

',
    '
',
    '
',
      '

',
    '
',
].join('');
SIREPO.appImportText = 'Import a MAD-X or elegant Lattice';
SIREPO.FILE_UPLOAD_TYPE = {
    'bunch-particleFile': '.h5,.hdf5',
};
SIREPO.lattice = {
    elementColor: {
        QUADRUPOLE: 'red',
        SEXTUPOLE: 'lightgreen',
        VKICKER: 'blue',
        NLINSERT: 'green',
    },
    elementPic: {
        aperture: ['ECOLLIMATOR', 'RCOLLIMATOR'],
        bend: ['HKICKER', 'KICKER', 'RBEND', 'SBEND'],
        drift: ['DRIFT'],
        magnet: ['NLINSERT', 'QUADRUPOLE', 'SEXTUPOLE', 'VKICKER'],
        rf: ['RFCAVITY'],
        solenoid: ['SOLENOID'],
        watch: ['HMONITOR', 'MARKER', 'MONITOR', 'VMONITOR'],
        zeroLength: ['DIPEDGE', 'MULTIPOLE', 'NLLENS', 'SROTATION'],
    },
};

SIREPO.app.factory('synergiaService', function(appState) {
    var self = {};

    self.computeModel = function(analysisModel) {
        return 'animation';
    };

    appState.setAppService(self);

    return self;
});

SIREPO.app.controller('LatticeController', function(latticeService) {
    var self = this;
    self.latticeService = latticeService;

    self.advancedNames = ['DIPEDGE', 'ECOLLIMATOR', 'HKICKER', 'HMONITOR', 'MARKER', 'MULTIPOLE', 'NLINSERT', 'NLLENS', 'RCOLLIMATOR', 'SEXTUPOLE', 'SOLENOID', 'SROTATION', 'VKICKER', 'VMONITOR'];

    self.basicNames = ['DRIFT', 'MONITOR', 'KICKER', 'QUADRUPOLE', 'RFCAVITY', 'SBEND'];

    self.titleForName = function(name) {
        return SIREPO.APP_SCHEMA.view[name].description;
    };
});

SIREPO.app.controller('SynergiaSourceController', function (appState, latticeService, panelState, requestSender, validationService, $scope) {
    var self = this;

    function calculateBunchParameters() {
        requestSender.getApplicationData(
            {
                method: 'calculate_bunch_parameters',
                bunch: appState.clone(appState.models.bunch),
            },
            function(data) {
                if (data.bunch && appState.isLoaded()) {
                    appState.models.bunch = data.bunch;
                }
            });
    }

    function processBeamDefinition() {
        processBunchFields();
        calculateBunchParameters();
    }

    function processBunchFields() {
        var bunch = appState.models.bunch;
        panelState.enableField('bunch', 'beta', false);
        var def = bunch.beam_definition;
        var bdv = validationService.getEnumValidator('BeamDefinition');
        ['energy', 'momentum', 'gamma'].forEach(function (f) {
            panelState.enableField('bunch', f, def === bdv.find(f));
        });
        var pv = validationService.getEnumValidator('Particle');
        ['mass', 'charge'].forEach(function(f) {
            panelState.enableField('bunch', f, bunch.particle === pv.find('other'));
        });
        var isFile = bunch.distribution == 'file';
        panelState.showRow('bunch', 'emit_x', ! isFile);
        ['rms_z', 'dpop', 'num_macro_particles', 'seed'].forEach(function(f) {
            panelState.showField('bunch', f, ! isFile);
        });
        panelState.showField('bunch', 'particleFile', isFile);
        var isLattice = bunch.distribution == 'lattice';
        ['beta_x', 'beta_y', 'alpha_x', 'alpha_y'].forEach(function(f) {
            panelState.enableField('bunch', f, ! isLattice);
            if (isLattice) {
                bunch[f] = appState.models.bunchTwiss[f];
            }
        });
        ['nonlinear_t', 'nonlinear_c', 'nonlinear_cutoff'].forEach(function(f) {
            panelState.showField('bunch', f, bunch.distribution.indexOf('nonlinear') >= 0);
        });
    }

    self.handleModalShown = function(name) {
        if (name == 'bunch') {
            processBunchFields();
        }
    };

    appState.whenModelsLoaded($scope, function() {
        processBeamDefinition();
        appState.watchModelFields($scope, ['bunch.distribution'], processBunchFields);
        appState.watchModelFields($scope, ['bunch.beam_definition', 'bunch.particle'], processBeamDefinition);
        appState.watchModelFields($scope, ['bunch.mass', 'bunch.energy', 'bunch.momentum', 'bunch.gamma'], calculateBunchParameters);
    });

    $scope.$on('bunchReport1.summaryData', function(e, info) {
        if (appState.isLoaded() && info.bunchTwiss) {
            appState.models.bunchTwiss = info.bunchTwiss;
            appState.saveChanges('bunchTwiss');
            processBunchFields();
        }
    });

    latticeService.initSourceController(self);
});

SIREPO.app.controller('VisualizationController', function (appState, frameCache, panelState, persistentSimulation, plotRangeService, synergiaService, $scope) {
    var self = this;
    var turnCount = 0;
    self.panelState = panelState;
    self.errorMessage = '';

    function handleStatus(data) {
        frameCache.setFrameCount(0, 'turnComparisonAnimation');
        turnCount = 0;
        self.errorMessage = data.error;
        if ('percentComplete' in data && ! data.error) {
            plotRangeService.computeFieldRanges(self, 'bunchAnimation', data.percentComplete);
            turnCount = data.turnCount;
            ['beamEvolutionAnimation', 'bunchAnimation', 'turnComparisonAnimation'].forEach(function(m) {
                appState.saveQuietly(m);
                var key = m + '.frameCount';
                if (!(key in data)) {
                    key = 'frameCount';
                }
                if (m != 'turnComparisonAnimation') {
                    frameCache.setFrameCount(data[key], m);
                }
            });
            if (data.percentComplete == 100) {
                frameCache.setFrameCount(1, 'turnComparisonAnimation');
            }
        }
        frameCache.setFrameCount(data.frameCount || 0);
    }

    self.handleModalShown = function(name) {
        if (name == 'bunchAnimation') {
            plotRangeService.processPlotRange(self, name);
        }
    };

    self.hasTurnComparisonResults = function() {
        return frameCache.getFrameCount('turnComparisonAnimation') > 0;
    };

    appState.whenModelsLoaded($scope, function() {
        appState.watchModelFields($scope, ['bunchAnimation.plotRangeType'], function() {
            plotRangeService.processPlotRange(self, 'bunchAnimation');
        });
    });

    self.simState = persistentSimulation.initSimulationState(
        $scope,
        synergiaService.computeModel(),
        handleStatus
    );

    self.simState.errorMessage = function() {
        return self.errorMessage;
    };

    self.simState.notRunningMessage = function() {
        return 'Simulation ' + self.simState.stateAsText();
    };

    self.simState.runningMessage = function() {
        if (appState.isLoaded() && turnCount) {
            return 'Simulating turn: ' + turnCount + ' / ' + appState.models.simulationSettings.turn_count;
        }
        return 'Simulation running';
    };
});

SIREPO.app.directive('appFooter', function() {
    return {
        restrict: 'A',
        scope: {
            nav: '=appFooter',
        },
        template: [
            '

',
            '

',
        ].join(''),
    };
});

SIREPO.app.directive('appHeader', function(latticeService) {
    return {
        restrict: 'A',
        scope: {
            nav: '=appHeader',
        },
        template: [
            '

',
            '

',
            '
',
              '<app-header-right-sim-loaded>',
		'
',
                  '
 Lattice
',
                  '
 Bunch
',
                  '
 Visualization
',
                '
',
              '</app-header-right-sim-loaded>',
              '<app-settings>',
		//  '
App-specific setting item
',
	      '</app-settings>',
              '<app-header-right-sim-list>',
                '
',
                  '
 Import
',
                '
',
              '</app-header-right-sim-list>',
            '
',
	].join(''),
        controller: function($scope) {
            $scope.latticeService = latticeService;
        },
    };
});

SIREPO.app.directive('turnCountField', function(appState) {
    return {
        restrict: 'A',
        scope: {
            model: '=',
            field: '=',
        },
        template: [
            '<select class="form-control" data-ng-model="model[field]" data-ng-options="name as name for name in turnCountList()"></select>',
        ].join(''),
        controller: function($scope) {
            var turnCountList = [];
            $scope.turnCountList = function() {
                if (! appState.isLoaded() || ! $scope.model) {
                    return null;
                }
                var turnCount = appState.applicationState().simulationSettings.turn_count;
                if (turnCount == turnCount.length - 1) {
                    return turnCountList;
                }
                turnCountList.length = 0;
                for (var i = 1; i <= turnCount; i++) {
                    turnCountList.push('' + i);
                }
                return turnCountList;
            };
        },
    };
});
<commit_msg>synergia: save bunch twiss info quietly to avoid cancelling running bunch reports<commit_after>'use strict';

var srlog = SIREPO.srlog;
var srdbg = SIREPO.srdbg;

SIREPO.SINGLE_FRAME_ANIMATION = ['beamEvolutionAnimation'];
SIREPO.appFieldEditors = [
    '
',
      '

',
    '
',
    '
',
      '

',
    '
',
].join('');
SIREPO.appImportText = 'Import a MAD-X or elegant Lattice';
SIREPO.FILE_UPLOAD_TYPE = {
    'bunch-particleFile': '.h5,.hdf5',
};
SIREPO.lattice = {
    elementColor: {
        QUADRUPOLE: 'red',
        SEXTUPOLE: 'lightgreen',
        VKICKER: 'blue',
        NLINSERT: 'green',
    },
    elementPic: {
        aperture: ['ECOLLIMATOR', 'RCOLLIMATOR'],
        bend: ['HKICKER', 'KICKER', 'RBEND', 'SBEND'],
        drift: ['DRIFT'],
        magnet: ['NLINSERT', 'QUADRUPOLE', 'SEXTUPOLE', 'VKICKER'],
        rf: ['RFCAVITY'],
        solenoid: ['SOLENOID'],
        watch: ['HMONITOR', 'MARKER', 'MONITOR', 'VMONITOR'],
        zeroLength: ['DIPEDGE', 'MULTIPOLE', 'NLLENS', 'SROTATION'],
    },
};

SIREPO.app.factory('synergiaService', function(appState) {
    var self = {};

    self.computeModel = function(analysisModel) {
        return 'animation';
    };

    appState.setAppService(self);

    return self;
});

SIREPO.app.controller('LatticeController', function(latticeService) {
    var self = this;
    self.latticeService = latticeService;

    self.advancedNames = ['DIPEDGE', 'ECOLLIMATOR', 'HKICKER', 'HMONITOR', 'MARKER', 'MULTIPOLE', 'NLINSERT', 'NLLENS', 'RCOLLIMATOR', 'SEXTUPOLE', 'SOLENOID', 'SROTATION', 'VKICKER', 'VMONITOR'];

    self.basicNames = ['DRIFT', 'MONITOR', 'KICKER', 'QUADRUPOLE', 'RFCAVITY', 'SBEND'];

    self.titleForName = function(name) {
        return SIREPO.APP_SCHEMA.view[name].description;
    };
});

SIREPO.app.controller('SynergiaSourceController', function (appState, latticeService, panelState, requestSender, validationService, $scope) {
    var self = this;

    function calculateBunchParameters() {
        requestSender.getApplicationData(
            {
                method: 'calculate_bunch_parameters',
                bunch: appState.clone(appState.models.bunch),
            },
            function(data) {
                if (data.bunch && appState.isLoaded()) {
                    appState.models.bunch = data.bunch;
                }
            });
    }

    function processBeamDefinition() {
        processBunchFields();
        calculateBunchParameters();
    }

    function processBunchFields() {
        var bunch = appState.models.bunch;
        panelState.enableField('bunch', 'beta', false);
        var def = bunch.beam_definition;
        var bdv = validationService.getEnumValidator('BeamDefinition');
        ['energy', 'momentum', 'gamma'].forEach(function (f) {
            panelState.enableField('bunch', f, def === bdv.find(f));
        });
        var pv = validationService.getEnumValidator('Particle');
        ['mass', 'charge'].forEach(function(f) {
            panelState.enableField('bunch', f, bunch.particle === pv.find('other'));
        });
        var isFile = bunch.distribution == 'file';
        panelState.showRow('bunch', 'emit_x', ! isFile);
        ['rms_z', 'dpop', 'num_macro_particles', 'seed'].forEach(function(f) {
            panelState.showField('bunch', f, ! isFile);
        });
        panelState.showField('bunch', 'particleFile', isFile);
        var isLattice = bunch.distribution == 'lattice';
        ['beta_x', 'beta_y', 'alpha_x', 'alpha_y'].forEach(function(f) {
            panelState.enableField('bunch', f, ! isLattice);
            if (isLattice) {
                bunch[f] = appState.models.bunchTwiss[f];
            }
        });
        ['nonlinear_t', 'nonlinear_c', 'nonlinear_cutoff'].forEach(function(f) {
            panelState.showField('bunch', f, bunch.distribution.indexOf('nonlinear') >= 0);
        });
    }

    self.handleModalShown = function(name) {
        if (name == 'bunch') {
            processBunchFields();
        }
    };

    appState.whenModelsLoaded($scope, function() {
        processBeamDefinition();
        appState.watchModelFields($scope, ['bunch.distribution'], processBunchFields);
        appState.watchModelFields($scope, ['bunch.beam_definition', 'bunch.particle'], processBeamDefinition);
        appState.watchModelFields($scope, ['bunch.mass', 'bunch.energy', 'bunch.momentum', 'bunch.gamma'], calculateBunchParameters);
    });

    $scope.$on('bunchReport1.summaryData', function(e, info) {
        if (appState.isLoaded() && info.bunchTwiss) {
            appState.models.bunchTwiss = info.bunchTwiss;
            //TODO(pjm): can't save normally or bunch reports will call runCancel on refresh
            appState.saveQuietly('bunchTwiss');
            processBunchFields();
        }
    });

    latticeService.initSourceController(self);
});

SIREPO.app.controller('VisualizationController', function (appState, frameCache, panelState, persistentSimulation, plotRangeService, synergiaService, $scope) {
    var self = this;
    var turnCount = 0;
    self.panelState = panelState;
    self.errorMessage = '';

    function handleStatus(data) {
        frameCache.setFrameCount(0, 'turnComparisonAnimation');
        turnCount = 0;
        self.errorMessage = data.error;
        if ('percentComplete' in data && ! data.error) {
            plotRangeService.computeFieldRanges(self, 'bunchAnimation', data.percentComplete);
            turnCount = data.turnCount;
            ['beamEvolutionAnimation', 'bunchAnimation', 'turnComparisonAnimation'].forEach(function(m) {
                appState.saveQuietly(m);
                var key = m + '.frameCount';
                if (!(key in data)) {
                    key = 'frameCount';
                }
                if (m != 'turnComparisonAnimation') {
                    frameCache.setFrameCount(data[key], m);
                }
            });
            if (data.percentComplete == 100) {
                frameCache.setFrameCount(1, 'turnComparisonAnimation');
            }
        }
        frameCache.setFrameCount(data.frameCount || 0);
    }

    self.handleModalShown = function(name) {
        if (name == 'bunchAnimation') {
            plotRangeService.processPlotRange(self, name);
        }
    };

    self.hasTurnComparisonResults = function() {
        return frameCache.getFrameCount('turnComparisonAnimation') > 0;
    };

    appState.whenModelsLoaded($scope, function() {
        appState.watchModelFields($scope, ['bunchAnimation.plotRangeType'], function() {
            plotRangeService.processPlotRange(self, 'bunchAnimation');
        });
    });

    self.simState = persistentSimulation.initSimulationState(
        $scope,
        synergiaService.computeModel(),
        handleStatus
    );

    self.simState.errorMessage = function() {
        return self.errorMessage;
    };

    self.simState.notRunningMessage = function() {
        return 'Simulation ' + self.simState.stateAsText();
    };

    self.simState.runningMessage = function() {
        if (appState.isLoaded() && turnCount) {
            return 'Simulating turn: ' + turnCount + ' / ' + appState.models.simulationSettings.turn_count;
        }
        return 'Simulation running';
    };
});

SIREPO.app.directive('appFooter', function() {
    return {
        restrict: 'A',
        scope: {
            nav: '=appFooter',
        },
        template: [
            '

',
            '

',
        ].join(''),
    };
});

SIREPO.app.directive('appHeader', function(latticeService) {
    return {
        restrict: 'A',
        scope: {
            nav: '=appHeader',
        },
        template: [
            '

',
            '

',
            '
',
              '<app-header-right-sim-loaded>',
		'
',
                  '
 Lattice
',
                  '
 Bunch
',
                  '
 Visualization
',
                '
',
              '</app-header-right-sim-loaded>',
              '<app-settings>',
		//  '
App-specific setting item
',
	      '</app-settings>',
              '<app-header-right-sim-list>',
                '
',
                  '
 Import
',
                '
',
              '</app-header-right-sim-list>',
            '
',
	].join(''),
        controller: function($scope) {
            $scope.latticeService = latticeService;
        },
    };
});

SIREPO.app.directive('turnCountField', function(appState) {
    return {
        restrict: 'A',
        scope: {
            model: '=',
            field: '=',
        },
        template: [
            '<select class="form-control" data-ng-model="model[field]" data-ng-options="name as name for name in turnCountList()"></select>',
        ].join(''),
        controller: function($scope) {
            var turnCountList = [];
            $scope.turnCountList = function() {
                if (! appState.isLoaded() || ! $scope.model) {
                    return null;
                }
                var turnCount = appState.applicationState().simulationSettings.turn_count;
                if (turnCount == turnCount.length - 1) {
                    return turnCountList;
                }
                turnCountList.length = 0;
                for (var i = 1; i <= turnCount; i++) {
                    turnCountList.push('' + i);
                }
                return turnCountList;
            };
        },
    };
});
<|endoftext|>


<commit_before>#!/usr/bin/env node
var child_process = require('child_process');

module.exports = function(context) {
	var fs = context.requireCordovaModule('fs');
	var path = context.requireCordovaModule('path');
	var deferral = context.requireCordovaModule('q').defer();

	var make_platform_dir = function(base) {
		return path.join(base, 'platforms', 'ios');
	}
	var pluginDir = path.join(context.opts.projectRoot, 'plugins', context.opts.plugin.id);
	var hooksDir = path.join(make_platform_dir(pluginDir), 'hooks');

	var addHook = function() {
		var XmlHelpers = context.requireCordovaModule("cordova-lib/src/util/xml-helpers");
		var et = context.requireCordovaModule('elementtree');

		var configFile = path.join(context.opts.projectRoot, 'config.xml');
		var xml = XmlHelpers.parseElementtreeSync(configFile);

		var getParent = function(tag, name) {
			var list = xml.findall('/' + tag);
			var ios = list.find(function(e) {
				return e.get('name') === name;
			});
			process.stdout.write('Found platform(name=ios): ' + ios);
			if (!ios) {
				process.stdout.write('Creating tag: ' + tag + '(name=' + name + ')');
				ios = el.makeelement(tag, {name: name});
				xml.append(ios);
			}
			return ios;
		}
		var child = et.XML('<hook type="after_plugin_add" src="' + path.join(hooksDir, 'add-bridging_header.sh') + '" />');
		getParent('platform', 'ios').append(child);

		fs.writeFileSync(configFile, xml.write({indent: 4}), 'utf-8');
	}

	var main = function() {
		process.stdout.write("################################ Start preparing\n");
		addHook();

		var script = path.join(hooksDir, 'after_plugin_install.sh');
		var child = child_process.execFile(script, [ context.opts.plugin.id ], {
			cwd : make_platform_dir(context.opts.projectRoot)
		}, function(error) {
			if (error) {
				deferral.reject(error);
			} else {
				process.stdout.write("################################ Finish preparing\n\n");
				deferral.resolve();
			}
		});
		child.stdout.on('data', function(data) {
			process.stdout.write(data);
		});
		child.stderr.on('data', function(data) {
			process.stderr.write(data);
		});
	}
	main();
	return deferral.promise;
};
<commit_msg>log メソッドを追加<commit_after>#!/usr/bin/env node
var child_process = require('child_process');

function log(msg) {
	process.stdout.write(msg);
}

module.exports = function(context) {
	var fs = context.requireCordovaModule('fs');
	var path = context.requireCordovaModule('path');
	var deferral = context.requireCordovaModule('q').defer();

	var make_platform_dir = function(base) {
		return path.join(base, 'platforms', 'ios');
	}
	var pluginDir = path.join(context.opts.projectRoot, 'plugins', context.opts.plugin.id);
	var hooksDir = path.join(make_platform_dir(pluginDir), 'hooks');

	var addHook = function() {
		var XmlHelpers = context.requireCordovaModule("cordova-lib/src/util/xml-helpers");
		var et = context.requireCordovaModule('elementtree');

		var configFile = path.join(context.opts.projectRoot, 'config.xml');
		var xml = XmlHelpers.parseElementtreeSync(configFile);

		var getParent = function(tag, name) {
			var list = xml.findall('/' + tag);
			log('Finding ios in: ' + list);
			var ios = list.find(function(e) {
				return e.get('name') === name;
			});
			log('Found platform(name=ios): ' + ios);
			if (!ios) {
				log('Creating tag: ' + tag + '(name=' + name + ')');
				ios = el.makeelement(tag, {name: name});
				xml.append(ios);
			}
			return ios;
		}
		var child = et.XML('<hook type="after_plugin_add" src="' + path.join(hooksDir, 'add-bridging_header.sh') + '" />');
		getParent('platform', 'ios').append(child);

		fs.writeFileSync(configFile, xml.write({indent: 4}), 'utf-8');
	}

	var main = function() {
		log("################################ Start preparing\n");
		addHook();

		var script = path.join(hooksDir, 'after_plugin_install.sh');
		var child = child_process.execFile(script, [ context.opts.plugin.id ], {
			cwd : make_platform_dir(context.opts.projectRoot)
		}, function(error) {
			if (error) {
				deferral.reject(error);
			} else {
				log("################################ Finish preparing\n\n");
				deferral.resolve();
			}
		});
		child.stdout.on('data', function(data) {
			log(data);
		});
		child.stderr.on('data', function(data) {
			process.stderr.write(data);
		});
	}
	main();
	return deferral.promise;
};
<|endoftext|>


<commit_before>Ext.namespace('Gemma');

Gemma.DIFF_THRESHOLD = 0.01;
Gemma.MAX_DIFF_RESULTS = 75;
/**
 *
 * Top level container for all sections of gene info
 *
 * To open the page at a specific tab, include ?tab=[tabName] suffix in the URL. Tab names are each tab's itemId.
 *
 * @class Gemma.GenePage
 * @extends Ext.TabPanel
 *
 */

Gemma.GenePage =  Ext.extend(Ext.TabPanel, {

	autoScroll:true,
	defaults: {
		autoScroll: true,
		width: 850
	},
	deferredRender: true,
	listeners: {
		'tabchange': function(tabPanel, newTab){
			newTab.fireEvent('tabChanged');
		},
		'beforetabchange': function(tabPanel, newTab, currTab){
			// if false is returned, tab isn't changed
			if (currTab) {
				return currTab.fireEvent('leavingTab');
			}
			return true;
		}
	},
	initComponent: function(){

		var geneId = this.geneId;

		var isAdmin = (Ext.get("hasAdmin"))?(Ext.get("hasAdmin").getValue() === 'true')?true:false:false;

		Gemma.GenePage.superclass.initComponent.call(this);

		//DETAILS TAB
		var details = new Gemma.GeneDetails({
			title: 'Details',
			itemId: 'details',
			geneId: geneId
		});
		details.on('changeTab', function(tabName){
			this.setActiveTab(tabName);
		},this);
		this.add(details);

		//ALLEN BRAIN ATLAS IMAGES
		this.add({
			xtype:'geneallenbrainatlasimages',
			geneId: geneId,
			title:'Expression Images',
			itemId:'expression'
		});

		// diff expression grid

		var diffExGrid = new Gemma.ProbeLevelDiffExGrid({
			title: 'Differential Expression',
			itemId: 'diffEx'
		});
		diffExGrid.on('render', function(){
			var visColumnIndex = diffExGrid.getColumnModel().getIndexById('visualize');
			diffExGrid.getColumnModel().setHidden(visColumnIndex, false);
			// this should go in grid itself, but it wasn't working properly (or at all)
			if (!this.loadMask && this.getEl()) {
				this.loadMask = new Ext.LoadMask(this.getEl(), {
					msg : Gemma.StatusText.Loading.generic,
					msgCls: 'absolute-position-loading-mask ext-el-mask-msg x-mask-loading'
				});
			}
			this.loadMask.show();
			diffExGrid.getStore().load({
				params: [geneId, Gemma.DIFF_THRESHOLD, Gemma.MAX_DIFF_RESULTS]
			});
		});
		this.add(diffExGrid);

		// Coexpression grid.
		var coexpressedGeneGrid = new Gemma.CoexpressionGrid({
			title: 'Coexpression',
			colspan: 2,
			lite: true,
			noSmallGemma: true,
			itemId: 'coex'
		});
		coexpressedGeneGrid.on('render', function(){
			coexpressedGeneGrid.doSearch({
				geneIds: [geneId],
				quick: true,
				stringency: 2,
				forceProbeLevelSearch: false
			});
		});
		this.add(coexpressedGeneGrid);

		this.add({
			xtype: 'geneproductgrid',
			geneid: geneId,
			title: 'Gene Products',
			deferLoadToRender: true,
			itemId: 'products'
		});

		this.add({
			title:'Gene Ontology Terms',
			xtype: 'genegogrid',
			border: true,
			geneid: this.geneId,
			minHeight: 150,
			deferLoadToRender: true,
			itemId: 'goGrid'
		});

		var phenotypeEvidenceGridPanel = new Gemma.PhenotypeEvidenceGridPanel({
			title: 'Phenotypes',
			itemId: 'phenotypes',
			hasRelevanceColumn: false,
			deferLoadToRender: true,
			currentGene: {
	    		id: this.geneId,
	    		ncbiId: this.geneNcbiId,
	    		officialSymbol: this.geneSymbol,
	    		officialName: this.geneName,
	    		taxonCommonName: this.geneTaxonName,
	    		taxonId: this.geneTaxonId
    		},
			evidencePhenotypeColumnRenderer: function(value, metadata, record, row, col, ds) {
				var phenotypesHtml = '';
				for (var i = 0; i < record.data.phenotypes.length; i++) {
					phenotypesHtml += String.format('{1}
',
						record.data.phenotypes[i].urlId, record.data.phenotypes[i].value, geneId);
				}
				return phenotypesHtml;
			}
		});
		// In PhenotypePanel, when a user logs in, PhenotypeGridPanel will be reloaded first, followed by
		// PhenotypeGeneGridPanel and then PhenotypeEvidenceGridPanel. So, the following code should not
		// be done in PhenotypeEvidenceGridPanel. Otherwise, PhenotypeEvidenceGridPanel would be reloaded twice.
		Gemma.Application.currentUser.on("logIn",
			function(userName, isAdmin) {
				var phenotypeEvidenceGridStore = phenotypeEvidenceGridPanel.getStore();
				phenotypeEvidenceGridStore.reload(phenotypeEvidenceGridStore.lastOptions);
			},
			this);

		this.add(phenotypeEvidenceGridPanel);

		var initialTab = 'details';
		this.loadSpecificTab = (document.URL.indexOf("?") > -1 && (document.URL.indexOf("tab=") > -1));
        if ( this.loadSpecificTab ) {
            var param = Ext.urlDecode(document.URL.substr(document.URL.indexOf("?") + 1));
            if (param.tab) {
            	if(this.getComponent( param.tab ) != undefined){
            		initialTab =  param.tab;
            	}
            }
        }

		this.on('render', function(){
			this.setActiveTab( initialTab );
		});
	}
});
<commit_msg>gene page: remove 'products' tab and reorder other tabs (bug 2972)<commit_after>Ext.namespace('Gemma');

Gemma.DIFF_THRESHOLD = 0.01;
Gemma.MAX_DIFF_RESULTS = 75;
/**
 *
 * Top level container for all sections of gene info
 *
 * To open the page at a specific tab, include ?tab=[tabName] suffix in the URL. Tab names are each tab's itemId.
 *
 * @class Gemma.GenePage
 * @extends Ext.TabPanel
 *
 */

Gemma.GenePage =  Ext.extend(Ext.TabPanel, {

	autoScroll:true,
	defaults: {
		autoScroll: true,
		width: 850
	},
	deferredRender: true,
	listeners: {
		'tabchange': function(tabPanel, newTab){
			newTab.fireEvent('tabChanged');
		},
		'beforetabchange': function(tabPanel, newTab, currTab){
			// if false is returned, tab isn't changed
			if (currTab) {
				return currTab.fireEvent('leavingTab');
			}
			return true;
		}
	},
	initComponent: function(){

		var geneId = this.geneId;

		var isAdmin = (Ext.get("hasAdmin"))?(Ext.get("hasAdmin").getValue() === 'true')?true:false:false;

		Gemma.GenePage.superclass.initComponent.call(this);

		//DETAILS TAB
		var details = new Gemma.GeneDetails({
			title: 'Details',
			itemId: 'details',
			geneId: geneId
		});
		details.on('changeTab', function(tabName){
			this.setActiveTab(tabName);
		},this);
		this.add(details);

		this.add(this.initDiffExTab( geneId ));

		this.add(this.initCoexTab( geneId ));

		this.add(this.initPhenotypeTab());

		this.add({
			title:'Gene Ontology Terms',
			xtype: 'genegogrid',
			border: true,
			geneid: this.geneId,
			minHeight: 150,
			deferLoadToRender: true,
			itemId: 'goGrid'
		});

		//ALLEN BRAIN ATLAS IMAGES
		this.add({
			xtype:'geneallenbrainatlasimages',
			geneId: geneId,
			title:'Expression Images',
			itemId:'expression'
		});

		/* see bug 2972
		 * this.add({
			xtype: 'geneproductgrid',
			geneid: geneId,
			title: 'Gene Products',
			deferLoadToRender: true,
			itemId: 'products'
		});*/

		var initialTab = 'details';
		this.loadSpecificTab = (document.URL.indexOf("?") > -1 && (document.URL.indexOf("tab=") > -1));
        if ( this.loadSpecificTab ) {
            var param = Ext.urlDecode(document.URL.substr(document.URL.indexOf("?") + 1));
            if (param.tab) {
            	if(this.getComponent( param.tab ) != undefined){
            		initialTab =  param.tab;
            	}
            }
        }

		this.on('render', function(){
			this.setActiveTab( initialTab );
		});
	},
	initCoexTab: function( geneId ){
		var coexpressedGeneGrid = new Gemma.CoexpressionGrid({
			title: 'Coexpression',
			colspan: 2,
			lite: true,
			noSmallGemma: true,
			itemId: 'coex'
		});
		coexpressedGeneGrid.on('render', function(){
			coexpressedGeneGrid.doSearch({
				geneIds: [geneId],
				quick: true,
				stringency: 2,
				forceProbeLevelSearch: false
			});
		});
		return coexpressedGeneGrid;

	},
	initDiffExTab: function( geneId ){
		var diffExGrid = new Gemma.ProbeLevelDiffExGrid({
			title: 'Differential Expression',
			itemId: 'diffEx'
		});
		diffExGrid.on('render', function(){
			var visColumnIndex = diffExGrid.getColumnModel().getIndexById('visualize');
			diffExGrid.getColumnModel().setHidden(visColumnIndex, false);
			// this should go in grid itself, but it wasn't working properly (or at all)
			if (!this.loadMask && this.getEl()) {
				this.loadMask = new Ext.LoadMask(this.getEl(), {
					msg : Gemma.StatusText.Loading.generic,
					msgCls: 'absolute-position-loading-mask ext-el-mask-msg x-mask-loading'
				});
			}
			this.loadMask.show();
			diffExGrid.getStore().load({
				params: [geneId, Gemma.DIFF_THRESHOLD, Gemma.MAX_DIFF_RESULTS]
			});
		});
		return diffExGrid;
	},
	initPhenotypeTab : function(){

		var phenotypeEvidenceGridPanel = new Gemma.PhenotypeEvidenceGridPanel({
			title: 'Phenotypes',
			itemId: 'phenotypes',
			hasRelevanceColumn: false,
			deferLoadToRender: true,
			currentGene: {
	    		id: this.geneId,
	    		ncbiId: this.geneNcbiId,
	    		officialSymbol: this.geneSymbol,
	    		officialName: this.geneName,
	    		taxonCommonName: this.geneTaxonName,
	    		taxonId: this.geneTaxonId
    		},
			evidencePhenotypeColumnRenderer: function(value, metadata, record, row, col, ds) {
				var phenotypesHtml = '';
				for (var i = 0; i < record.data.phenotypes.length; i++) {
					phenotypesHtml += String.format('{1}
',
						record.data.phenotypes[i].urlId, record.data.phenotypes[i].value, geneId);
				}
				return phenotypesHtml;
			}
		});
		// In PhenotypePanel, when a user logs in, PhenotypeGridPanel will be reloaded first, followed by
		// PhenotypeGeneGridPanel and then PhenotypeEvidenceGridPanel. So, the following code should not
		// be done in PhenotypeEvidenceGridPanel. Otherwise, PhenotypeEvidenceGridPanel would be reloaded twice.
		Gemma.Application.currentUser.on("logIn",
			function(userName, isAdmin) {
				var phenotypeEvidenceGridStore = phenotypeEvidenceGridPanel.getStore();
				phenotypeEvidenceGridStore.reload(phenotypeEvidenceGridStore.lastOptions);
			},
			this);

		return phenotypeEvidenceGridPanel;
	}
});
<|endoftext|>


<commit_before>Ensembl.Panel.ToolsSpeciesList = Ensembl.Panel.extend({

  init: function () {
    this.base();
    this.elLk.checkboxes  = $('.checkboxes', this.el);
    this.elLk.speciesList = $('.checkboxes input[name="species"]', this.el);
    this.elLk.multiselect = parseInt($('input[name="multiselect"]', this.el).val() || 1);
    this.elLk.vep_assembly= $('._vep_assembly span', this.el);
    this.elLk.list        = $('.list', this.el);
    this.elLk.modalLink   = $('.modal_link', this.el);
    if ($('input[name=species_form_data]', this.el).length > 0) {
      this.elLk.species_form_data = $.parseJSON($('input[name=species_form_data]', this.el).val());
    }
    Ensembl.species && Ensembl.species !== 'Multi' && this.updateTaxonSelection([{key: Ensembl.species, title: Ensembl.species}]);
    Ensembl.EventManager.register('updateTaxonSelection', this, this.updateTaxonSelection);
  },

  updateTaxonSelection: function(items) {
    var panel = this;
    var key;
    var new_list = [];

    // empty and re-populate the species list
    panel.elLk.list.empty();
    panel.elLk.checkboxes.empty();
    $.each(items, function(index, item){
      key = item.key.charAt(0).toUpperCase() + item.key.substr(1); // ucfirst
      var _delete = $('', {
        text: 'x',
        'class': 'ss-selection-delete',
        click: function() {
          // Update taxon selection
          var clicked_item_title = $(this).parent('li').find('span.ss-selected').html();
          var updated_items = [];

          //removing human and hence hide grch37 message
          if(clicked_item_title === "Homo_sapiens" || clicked_item_title === "Human") { panel.el.find('div.assembly_msg').hide(); }

          $.each(items, function(i, item) {
            if(clicked_item_title !== item.title) {
              updated_items.push(item);
            }
          });
          Ensembl.EventManager.trigger('updateTaxonSelection', updated_items);
          // Remove item from the Tools form list
          $(this).parent('li').remove();
        }
      });

      //adding human and hence show grch37 message
      if(item.title === "Homo_sapiens" || item.title === "Human") { panel.el.find('div.assembly_msg').show(); }

      var _selected_img = $('', {
        src: item.img_url || (panel.elLk.species_form_data && panel.elLk.species_form_data[item.key].img_url) || Ensembl.speciesImage,
        'class': 'nosprite'
      });

      var _selected_item = $('', {
        text: item.title,
        'data-title': item.title,
        'data-key': item.key,
        'class': 'ss-selected',
        title: item.title
      });

      var li = $('
', {
      }).append(_selected_img, _selected_item, _delete).appendTo(panel.elLk.list);
      var _class = (panel.elLk.species_form_data && panel.elLk.species_form_data[item.key]) ? panel.elLk.species_form_data[item.key].class : ''
      $(panel.elLk.checkboxes).append('<input type="checkbox" name="species" class="' + _class + '" value="' + key + '" checked>' + item.title + '
');
      new_list.push(key);
    });

    // Check blat availability and reset
    Ensembl.EventManager.trigger('resetSearchTools', null, new_list);
    // Update sourceType on species selection change
    Ensembl.EventManager.trigger('resetSourceTypes', new_list);

    var toggleMap = {};
    if (panel.elLk.species_form_data && Object.keys(panel.elLk.species_form_data).length > 0) {
      $.each(Object.keys(panel.elLk.species_form_data), function(i, sp_key) {
        if (panel.elLk.species_form_data[sp_key] && panel.elLk.species_form_data[sp_key].class) {
          var _class = panel.elLk.species_form_data[sp_key].class;
          var filters = $.map(_class.match(/(\s+|^)_stt__([^\s]+)/g) || [], function(str) { return str.replace('_stt__', '._stt_') });
              filters.push('._stt_' + sp_key);
          toggleMap[sp_key] = _class.match(/(\s+|^)_sttmulti($|\s+)/) ? filters.join(',') : filters[0];
        }
      });
      Ensembl.EventManager.trigger('resetSelectToToggle', toggleMap);
      panel.elLk.vep_assembly.html(panel.elLk.species_form_data[items[0].key].vep_assembly);
    }

    // update the modal link href in the form
    if (panel.elLk.modalLink.length) {
      var modalBaseUrl = panel.elLk.modalLink.attr('href').split('?')[0];
      var keys = $.map(items, function(item){ return item.key; });
      var queryString = $.param({s: keys, multiselect: this.elLk.multiselect, referer_type: 'Tools'}, true);
      panel.elLk.modalLink.attr('href', modalBaseUrl + '?' + queryString);
    }
  }
});
<commit_msg>Merging postreleasefix/101 to release/101<commit_after>Ensembl.Panel.ToolsSpeciesList = Ensembl.Panel.extend({

  init: function () {
    this.base();
    this.elLk.checkboxes  = $('.checkboxes', this.el);
    this.elLk.speciesList = $('.checkboxes input[name="species"]', this.el);
    this.elLk.multiselect = parseInt($('input[name="multiselect"]', this.el).val() || 1);
    this.elLk.vep_assembly= $('._vep_assembly span', this.el);
    this.elLk.list        = $('.list', this.el);
    this.elLk.modalLink   = $('.modal_link', this.el);
    if ($('input[name=species_form_data]', this.el).length > 0) {
      this.elLk.species_form_data = $.parseJSON($('input[name=species_form_data]', this.el).val());
    }
    Ensembl.species && Ensembl.species !== 'Multi' && this.updateTaxonSelection([{key: Ensembl.species, title: Ensembl.species}]);
    Ensembl.EventManager.register('updateTaxonSelection', this, this.updateTaxonSelection);
  },

  updateTaxonSelection: function(items) {
    var panel = this;
    var key;
    var new_list = [];

    // empty and re-populate the species list
    panel.elLk.list.empty();
    panel.elLk.checkboxes.empty();
    $.each(items, function(index, item){
      key = item.key.charAt(0).toUpperCase() + item.key.substr(1); // ucfirst
      var _delete = $('', {
        text: 'x',
        'class': 'ss-selection-delete',
        click: function() {
          // Update taxon selection
          var clicked_item_title = $(this).parent('li').find('span.ss-selected').html();
          var updated_items = [];

          //removing human and hence hide grch37 message
          if(clicked_item_title === "Homo_sapiens" || clicked_item_title === "Human") { panel.el.find('div.assembly_msg').hide(); }

          $.each(items, function(i, item) {
            if(clicked_item_title !== item.title) {
              updated_items.push(item);
            }
          });
          Ensembl.EventManager.trigger('updateTaxonSelection', updated_items);
          // Remove item from the Tools form list
          $(this).parent('li').remove();
        }
      });

      //adding human and hence show grch37 message
      if(item.title === "Homo_sapiens" || item.title === "Human") { panel.el.find('div.assembly_msg').show(); }

      var _selected_img = $('', {
        src: item.img_url || (panel.elLk.species_form_data && panel.elLk.species_form_data[item.key].img_url) || Ensembl.speciesImage,
        'class': 'nosprite'
      });

      var _selected_item = $('', {
        text: item.title,
        'data-title': item.title,
        'data-key': item.key,
        'class': 'ss-selected',
        title: item.title
      });

      var li = $('
', {
      }).append(_selected_img, _selected_item, _delete).appendTo(panel.elLk.list);
      var _class = '';
      if(panel.elLk.species_form_data && panel.elLk.species_form_data[item.key]) {
        _class = panel.elLk.species_form_data[item.key]['class'];
      }
      $(panel.elLk.checkboxes).append('<input type="checkbox" name="species" class="' + _class + '" value="' + key + '" checked>' + item.title + '
');
      new_list.push(key);
    });

    // Check blat availability and reset
    Ensembl.EventManager.trigger('resetSearchTools', null, new_list);
    // Update sourceType on species selection change
    Ensembl.EventManager.trigger('resetSourceTypes', new_list);

    var toggleMap = {};
    if (panel.elLk.species_form_data && Object.keys(panel.elLk.species_form_data).length > 0) {
      $.each(Object.keys(panel.elLk.species_form_data), function(i, sp_key) {
        if (panel.elLk.species_form_data[sp_key] && panel.elLk.species_form_data[sp_key]['class']) {
          var _class = panel.elLk.species_form_data[sp_key]['class'];
          var filters = $.map(_class.match(/(\s+|^)_stt__([^\s]+)/g) || [], function(str) { return str.replace('_stt__', '._stt_') });
              filters.push('._stt_' + sp_key);
          toggleMap[sp_key] = _class.match(/(\s+|^)_sttmulti($|\s+)/) ? filters.join(',') : filters[0];
        }
      });
      Ensembl.EventManager.trigger('resetSelectToToggle', toggleMap);
      panel.elLk.vep_assembly.html(panel.elLk.species_form_data[items[0].key].vep_assembly);
    }

    // update the modal link href in the form
    if (panel.elLk.modalLink.length) {
      var modalBaseUrl = panel.elLk.modalLink.attr('href').split('?')[0];
      var keys = $.map(items, function(item){ return item.key; });
      var queryString = $.param({s: keys, multiselect: this.elLk.multiselect, referer_type: 'Tools'}, true);
      panel.elLk.modalLink.attr('href', modalBaseUrl + '?' + queryString);
    }
  }
});
<|endoftext|>


<commit_before>import React, { useState, useEffect } from 'react';
import styled from 'styled-components';
import { useHistory } from 'react-router-dom';
import f from 'lodash/fp';
import _ from 'lodash';

import { Col, Button, Box, Row, Icon, Text } from '@tlon/indigo-react';

import './css/custom.css';

import Tiles from './components/tiles';
import Tile from './components/tiles/tile';
import Welcome from './components/welcome';
import Groups from './components/Groups';
import ModalButton from './components/ModalButton';
import { StatelessAsyncButton } from '~/views/components/StatelessAsyncButton';
import { writeText } from '~/logic/lib/util';
import { useModal } from "~/logic/lib/useModal";
import { NewGroup } from "~/views/landscape/components/NewGroup";
import { JoinGroup } from "~/views/landscape/components/JoinGroup";
import { Helmet } from 'react-helmet';
import useLocalState from "~/logic/state/local";
import { useWaitForProps } from '~/logic/lib/useWaitForProps';
import {
  hasTutorialGroup,
  TUTORIAL_GROUP,
  TUTORIAL_HOST
} from '~/logic/lib/tutorialModal';

const ScrollbarLessBox = styled(Box)`
  scrollbar-width: none !important;

  ::-webkit-scrollbar {
    display: none;
  }
`;

const tutSelector = f.pick(['tutorialProgress', 'nextTutStep']);

export default function LaunchApp(props) {
  const history = useHistory();
  const [hashText, setHashText] = useState(props.baseHash);
  const hashBox = (
    <Box
      position={["relative", "absolute"]}
      fontFamily="mono"
      left="0"
      bottom="0"
      color="washedGray"
      bg="white"
      ml={3}
      mb={3}
      borderRadius={2}
      fontSize={0}
      p={2}
      boxShadow="0 0 0px 1px inset"
      cursor="pointer"
      onClick={() => {
        writeText(props.baseHash);
        setHashText('copied');
        setTimeout(() => {
          setHashText(props.baseHash);
        }, 2000);
      }}
    >
      <Text color="gray">{hashText || props.baseHash}</Text>
    </Box>
  );

  const { tutorialProgress, nextTutStep } = useLocalState(tutSelector);

  const waiter = useWaitForProps(props);

  const { modal, showModal } = useModal({
    modal: (dismiss) => {
      const onDismiss = (e) => {
        e.stopPropagation();
        props.api.settings.putEntry('tutorial', 'seen', true);
        dismiss();
      };
      const onContinue = async (e) => {
        e.stopPropagation();
        if(!hasTutorialGroup(props)) {
          await props.api.groups.join(TUTORIAL_HOST, TUTORIAL_GROUP);
          await waiter(hasTutorialGroup);
        }
        nextTutStep();
        dismiss();
      }
      return (
      <Col gapY="2" p="3">
        <Text lineHeight="tall" fontWeight="medium">Welcome</Text>
        <Text lineHeight="tall">
          You have been invited to use Landscape, an interface to chat
          and interact with communities

          Would you like a tour of Landscape?
        </Text>
        <Row gapX="2" justifyContent="flex-end">
          <Button onClick={onDismiss}>Skip</Button>
          <StatelessAsyncButton primary onClick={onContinue}>
            Yes
          </StatelessAsyncButton>
        </Row>
      </Col>
    )}
  });
  useEffect(() => {
    const seenTutorial = _.get(props.settings, ['tutorial', 'seen'], true);
    if(!seenTutorial && tutorialProgress === 'hidden') {
      showModal();
    }
  }, [props.settings]);

  return (
    <>
      <Helmet defer={false}>
        <title>{ props.notificationsCount ? `(${String(props.notificationsCount) }) `: '' }Landscape</title>
      </Helmet>
      <ScrollbarLessBox height='100%' overflowY='scroll' display="flex" flexDirection="column">
        {modal}
        <Welcome firstTime={props.launch.firstTime} api={props.api} />
        <Box
          mx='2'
          display='grid'
          gridTemplateColumns='repeat(auto-fill, minmax(128px, 1fr))'
          gridGap={3}
          p={2}
          pt={0}
        >
          <Tile
            bg="white"
            color="scales.black20"
            to="/~landscape/home"
            p={0}
          >
            <Box p={2} height='100%' width='100%' bg='scales.black20'>
              <Row alignItems='center'>
                <Icon
                  color="black"
                  icon="Home"
                />
                <Text ml="2" mt='1px' color="black">My Channels</Text>
              </Row>
            </Box>
          </Tile>
          <Tiles
            tiles={props.launch.tiles}
            tileOrdering={props.launch.tileOrdering}
            api={props.api}
            location={props.userLocation}
            weather={props.weather}
          />
          <ModalButton
            icon="Plus"
            bg="blue"
            color="#fff"
            text="Join a Group"
            style={{ gridColumnStart: 1 }}
          >
            <JoinGroup {...props} />
          </ModalButton>
          <ModalButton
            icon="CreateGroup"
            bg="green"
            color="#fff"
            text="Create Group"
          >
            <NewGroup {...props} />
          </ModalButton>
          <Groups unreads={props.unreads} groups={props.groups} associations={props.associations} />
        </Box>
        <Box alignSelf="flex-start" display={["block", "none"]}>{hashBox}</Box>
      </ScrollbarLessBox>
      <Box display={["none", "block"]}>{hashBox}</Box>
    </>
  );
}
<commit_msg>tutorial: automatically join graphs before start<commit_after>import React, { useState, useEffect } from 'react';
import styled from 'styled-components';
import { useHistory } from 'react-router-dom';
import f from 'lodash/fp';
import _ from 'lodash';

import { Col, Button, Box, Row, Icon, Text } from '@tlon/indigo-react';

import './css/custom.css';

import Tiles from './components/tiles';
import Tile from './components/tiles/tile';
import Welcome from './components/welcome';
import Groups from './components/Groups';
import ModalButton from './components/ModalButton';
import { StatelessAsyncButton } from '~/views/components/StatelessAsyncButton';
import { writeText } from '~/logic/lib/util';
import { useModal } from "~/logic/lib/useModal";
import { NewGroup } from "~/views/landscape/components/NewGroup";
import { JoinGroup } from "~/views/landscape/components/JoinGroup";
import { Helmet } from 'react-helmet';
import useLocalState from "~/logic/state/local";
import { useWaitForProps } from '~/logic/lib/useWaitForProps';
import {
  hasTutorialGroup,
  TUTORIAL_GROUP,
  TUTORIAL_HOST,
  TUTORIAL_BOOK,
  TUTORIAL_CHAT,
  TUTORIAL_LINKS
} from '~/logic/lib/tutorialModal';

const ScrollbarLessBox = styled(Box)`
  scrollbar-width: none !important;

  ::-webkit-scrollbar {
    display: none;
  }
`;

const tutSelector = f.pick(['tutorialProgress', 'nextTutStep']);

export default function LaunchApp(props) {
  const history = useHistory();
  const [hashText, setHashText] = useState(props.baseHash);
  const hashBox = (
    <Box
      position={["relative", "absolute"]}
      fontFamily="mono"
      left="0"
      bottom="0"
      color="washedGray"
      bg="white"
      ml={3}
      mb={3}
      borderRadius={2}
      fontSize={0}
      p={2}
      boxShadow="0 0 0px 1px inset"
      cursor="pointer"
      onClick={() => {
        writeText(props.baseHash);
        setHashText('copied');
        setTimeout(() => {
          setHashText(props.baseHash);
        }, 2000);
      }}
    >
      <Text color="gray">{hashText || props.baseHash}</Text>
    </Box>
  );

  const { tutorialProgress, nextTutStep } = useLocalState(tutSelector);

  const waiter = useWaitForProps(props);

  const { modal, showModal } = useModal({
    modal: (dismiss) => {
      const onDismiss = (e) => {
        e.stopPropagation();
        props.api.settings.putEntry('tutorial', 'seen', true);
        dismiss();
      };
      const onContinue = async (e) => {
        e.stopPropagation();
        if(!hasTutorialGroup(props)) {
          await props.api.groups.join(TUTORIAL_HOST, TUTORIAL_GROUP);
          await waiter(hasTutorialGroup);
          await Promise.all(
            [TUTORIAL_BOOK, TUTORIAL_CHAT, TUTORIAL_LINKS].map(graph =>
              api.graph.join(TUTORIAL_HOST, graph)));

          await waiter(p => {
            return `/ship/${TUTORIAL_HOST}/${TUTORIAL_CHAT}` in p.associations.graph &&
                  `/ship/${TUTORIAL_HOST}/${TUTORIAL_BOOK}` in p.associations.graph &&
                  `/ship/${TUTORIAL_HOST}/${TUTORIAL_LINKS}` in p.associations.graph;
          });
        }
        nextTutStep();
        dismiss();
      }
      return (
      <Col gapY="2" p="3">
        <Text lineHeight="tall" fontWeight="medium">Welcome</Text>
        <Text lineHeight="tall">
          You have been invited to use Landscape, an interface to chat
          and interact with communities

          Would you like a tour of Landscape?
        </Text>
        <Row gapX="2" justifyContent="flex-end">
          <Button onClick={onDismiss}>Skip</Button>
          <StatelessAsyncButton primary onClick={onContinue}>
            Yes
          </StatelessAsyncButton>
        </Row>
      </Col>
    )}
  });
  useEffect(() => {
    const seenTutorial = _.get(props.settings, ['tutorial', 'seen'], true);
    if(!seenTutorial && tutorialProgress === 'hidden') {
      showModal();
    }
  }, [props.settings]);

  return (
    <>
      <Helmet defer={false}>
        <title>{ props.notificationsCount ? `(${String(props.notificationsCount) }) `: '' }Landscape</title>
      </Helmet>
      <ScrollbarLessBox height='100%' overflowY='scroll' display="flex" flexDirection="column">
        {modal}
        <Welcome firstTime={props.launch.firstTime} api={props.api} />
        <Box
          mx='2'
          display='grid'
          gridTemplateColumns='repeat(auto-fill, minmax(128px, 1fr))'
          gridGap={3}
          p={2}
          pt={0}
        >
          <Tile
            bg="white"
            color="scales.black20"
            to="/~landscape/home"
            p={0}
          >
            <Box p={2} height='100%' width='100%' bg='scales.black20'>
              <Row alignItems='center'>
                <Icon
                  color="black"
                  icon="Home"
                />
                <Text ml="2" mt='1px' color="black">My Channels</Text>
              </Row>
            </Box>
          </Tile>
          <Tiles
            tiles={props.launch.tiles}
            tileOrdering={props.launch.tileOrdering}
            api={props.api}
            location={props.userLocation}
            weather={props.weather}
          />
          <ModalButton
            icon="Plus"
            bg="blue"
            color="#fff"
            text="Join a Group"
            style={{ gridColumnStart: 1 }}
          >
            <JoinGroup {...props} />
          </ModalButton>
          <ModalButton
            icon="CreateGroup"
            bg="green"
            color="#fff"
            text="Create Group"
          >
            <NewGroup {...props} />
          </ModalButton>
          <Groups unreads={props.unreads} groups={props.groups} associations={props.associations} />
        </Box>
        <Box alignSelf="flex-start" display={["block", "none"]}>{hashBox}</Box>
      </ScrollbarLessBox>
      <Box display={["none", "block"]}>{hashBox}</Box>
    </>
  );
}
<|endoftext|>


<commit_before>/**
 * Helpers
 *
 * @author Andrey Antukh <niwi@niwi.nz>, 2016-2017
 * @license MIT License <https://opensource.org/licenses/MIT>
 */

goog.provide("bide.impl.helpers");

goog.scope(function() {
  var self = bide.impl.helpers;

  if (!Object.keys) Object.keys = function(o) {
    if (o !== Object(o))
      throw new TypeError('Object.keys called on a non-object');
    var k=[],p;
    for (p in o) if (Object.prototype.hasOwnProperty.call(o,p)) k.push(p);
    return k;
  };

  self.keys = function(o) {
    if (o == null) {
      return [];
    }
    return Object.keys(o);
  };

  self.isArray = Array.isArray || function (val) {
    return !! val && '[object Array]' == toString.call(val);
  };
});

<commit_msg>Fix shadow-cljs warning<commit_after>/**
 * Helpers
 *
 * @author Andrey Antukh <niwi@niwi.nz>, 2016-2017
 * @license MIT License <https://opensource.org/licenses/MIT>
 */

goog.provide("bide.impl.helpers");

goog.scope(function() {
  var self = bide.impl.helpers;

  if (!Object.keys) Object.keys = function(o) {
    if (o !== Object(o))
      throw new TypeError('Object.keys called on a non-object');
    var k=[],p;
    for (p in o) if (Object.prototype.hasOwnProperty.call(o,p)) k.push(p);
    return k;
  };

  self.keys = function(o) {
    if (o == null) {
      return [];
    }
    return Object.keys(o);
  };

  self.isArray = Array.isArray || function (val) {
    return !! val && '[object Array]' == Object.prototype.toString.call(val);
  };
});

<|endoftext|>


<commit_before>var traits = [

	// thanks to Stephen Dobson
	"frustrated poet",
	"tries to hide circular scar around neck",
	"one blue eye, one brown",
	"poor personal hygiene",
	"constantly scratching",
	"missing top two front teeth",
	'tattoo on inner forearm in an unusual foreign language. Thinks it says "warrior spirit" but actually means "import duty paid"',
	"thinks they can sing. They're wrong.",
	"silver necklace with four different faiths' holy symbols",
	"dagger with a (costume jewel) jewelled pommel tucked into their belt",
	"yellow sash stretching from left shoulder to right waist",
	"missing right eye, wears a patch",
	"left little finger missing last joint, scarring evidently old",
	"greying at the temples. tries to hide with hat.",
	"dirty fingernails",
	"halitosis",
	"heavy drinker (red nose and cheeks)",
	"dried rations make them nauseous",

	// thanks to John Wilson
	"wide mouth",
	"beady eyes",
	"long face",
	"big hands",
	"long legs",
	"bristly hair",
	"limps (but no less agile for it)",
	"lisps",
	"always smiles",
	"greasy",
	"hirsute",
	"slurred speech",
	"stutters",
	"never first in line",
	"lucky",
	"weak ankle",
	"never gets tired",
	"interrupts conversations",
	"always says they've done what you did, but better",
	"drop things. constantly",
	"neat freak",
	"collector of souvenirs (monster claws, etc.)",
	"will try to sneak an extra coin from every treasure",

	// thanks to me
	"missing left eye, leaves scar uncovered",
	"has one gold tooth and no others",
	"missing both little fingers",
	'has a pet rat named "Mr Squeek" who lives in their pocket',
	"small hands",
	"dislikes loud noises",
	"scared of dogs",
	"afraid of heights",
	"claustrophobic",
	"tends to whisper",
	"arachnophobic",
	"distrusts men with beards",
	"village wrestling champion",
	"always wanted to be a magic user",
	"un-sure if their chosen deity exists",
	"considers themselves a master of strategy games",
	"inveterate gambler",
	"skilled at sleight of hand tricks",
	"scars run from corners of mouth to below ears",
	"tattoos on fingers of both hands spell words",
	"shameless womanizer",
	"has an invisible friend",
	"keen botanist",
	"ex bookkeeper",
	"ex beekeeper",
	"ex cowheard",
	"ex goatherd",
	"ex dung carter",
	"ex tanner",
	"ex woodcutter",
	"ex stone mason",
	"ex rat catcher, hates rats",
	"ex gamekeeper",
	"ex fisherman but now scared of the sea",
	"starving artist",
	"failed musician",
	"terrible artist",
	"skilled artist",
	"shoemaker's apprentice",
	"baker",
	"believes Frogs are Gods in mortal form",
	"believes Finch is greatest of all birds",
	"ex bricklayer",
	"ex brick maker",
	"ex grave digger",
	"turned to crime to surive as a child",
	"sheltered upbringing",
	"lute-maker",
	"snake oil salesman",
	"wants to be an actor",
	"kicked out of town guard for corruption",
	"fallen, level drained former Paladin. Protects this secret.",
	"hedge herbalist",
	"former mercenary",
	"former body guard",
	"beggar",
	"cut-purse",
	"scion of destitute noble family",
	"transient",
	"writes terrible plays. Wants you to act in them.",
	"worries the Sun may not rise",
	"believes the Moon laughs at them",
	"cannot distinguish left and right",
	"village sporting hero. Misses glory days.",
	"poor judge of character",
	"terrible choice in romantic partners",
	"prone to panic attacks",
	"passion for embroidery",
	"former bailiff. Relished the job.",
	"owned a tavern, it burnt down.",
	"arsonist",
	"completely untrustworthy",
	"sells information without morals",
    "thinks the party is conspiring against them",
    "pure evil"
];

function get_trait(character_class) {
	var choice = randomRange(0, traits.length-1);
	return(traits[choice]);
}
<commit_msg>Improve a couple of traits' formatting<commit_after>var traits = [

	// thanks to Stephen Dobson
	"frustrated poet",
	"tries to hide circular scar around neck",
	"one blue eye, one brown",
	"poor personal hygiene",
	"constantly scratching",
	"missing top two front teeth",
	'tattoo on inner forearm in an unusual foreign language. Thinks it says "warrior spirit" but actually means "import duty paid"',
	"thinks they can sing. They're wrong.",
	"silver necklace with four different faiths' holy symbols",
	"dagger with a (costume jewel) jewelled pommel tucked into their belt",
	"yellow sash stretching from left shoulder to right waist",
	"missing right eye, wears a patch",
	"left little finger missing last joint, scarring evidently old",
	"greying at the temples. tries to hide with hat.",
	"dirty fingernails",
	"halitosis",
	"heavy drinker (red nose and cheeks)",
	"dried rations make them nauseous",

	// thanks to John Wilson
	"wide mouth",
	"beady eyes",
	"long face",
	"big hands",
	"long legs",
	"bristly hair",
	"limps (but no less agile for it)",
	"lisps",
	"always smiles",
	"greasy",
	"hirsute",
	"slurred speech",
	"stutters",
	"never first in line",
	"lucky",
	"weak ankle",
	"never gets tired",
	"interrupts conversations",
	"always says they've done what you did, but better",
	"drop things. constantly",
	"neat freak",
	"collector of souvenirs (monster claws, etc.)",
	"will try to sneak an extra coin from every treasure",

	// thanks to me
	"missing left eye, leaves scar uncovered",
	"has one gold tooth and no others",
	"missing both little fingers",
	'has a pet rat named "Mr Squeek" who lives in their pocket',
	"small hands",
	"dislikes loud noises",
	"scared of dogs",
	"afraid of heights",
	"claustrophobic",
	"tends to whisper",
	"arachnophobic",
	"distrusts men with beards",
	"village wrestling champion",
	"always wanted to be a magic user",
	"un-sure if their chosen deity exists",
	"considers themselves a master of strategy games",
	"inveterate gambler",
	"skilled at sleight of hand tricks",
	"scars run from corners of mouth to below ears",
	"tattoos on fingers of both hands spell words",
	"shameless womanizer",
	"has an invisible friend",
	"keen botanist",
	"ex bookkeeper",
	"ex beekeeper",
	"ex cowheard",
	"ex goatherd",
	"ex dung carter",
	"ex tanner",
	"ex woodcutter",
	"ex stone mason",
	"ex rat catcher, hates rats",
	"ex gamekeeper",
	"ex fisherman but now scared of the sea",
	"starving artist",
	"failed musician",
	"terrible artist",
	"skilled artist",
	"shoemaker's apprentice",
	"baker",
	"believes frogs are Gods in mortal form",
	"believes finch is greatest of all birds",
	"ex bricklayer",
	"ex brick maker",
	"ex grave digger",
	"turned to crime to surive as a child",
	"sheltered upbringing",
	"lute-maker",
	"snake oil salesman",
	"wants to be an actor",
	"kicked out of town guard for corruption",
	"fallen, level drained former Paladin. Protects this secret.",
	"hedge herbalist",
	"former mercenary",
	"former body guard",
	"beggar",
	"cut-purse",
	"scion of destitute noble family",
	"transient",
	"writes terrible plays. Wants you to act in them.",
	"worries the Sun may not rise",
	"believes the Moon laughs at them",
	"cannot distinguish left and right",
	"village sporting hero. Misses glory days.",
	"poor judge of character",
	"terrible choice in romantic partners",
	"prone to panic attacks",
	"passion for embroidery",
	"former bailiff. Relished the job.",
	"owned a tavern, it burnt down.",
	"arsonist",
	"completely untrustworthy",
	"sells information without morals",
    "thinks the party is conspiring against them",
    "pure evil"
];

function get_trait(character_class) {
	var choice = randomRange(0, traits.length-1);
	return(traits[choice]);
}
<|endoftext|>


<commit_before>exports.defaultContentType = function (req, res, next) {
    req.headers['content-type'] = req.headers['content-type'] || 'application/json';
    if (req.headers['content-type'] !== 'application/json') {
        res.send(
            400,
            'content type "application/json" expected but found "'+ req.headers['content-type'] + '" instead');
    } else {
        next();
    }
};<commit_msg>add comments<commit_after>// Only allows Content-Type application/json
exports.defaultContentType = function (req, res, next) {
    req.headers['content-type'] = req.headers['content-type'] || 'application/json';
    if (req.headers['content-type'] !== 'application/json') {
        res.send(
            400,
            'content type "application/json" expected but found "'+ req.headers['content-type'] + '" instead');
    } else {
        next();
    }
};<|endoftext|>