<commit_before>import m from 'mithril';
import _ from 'underscore';
import $ from 'jquery';
import I18n from 'i18n-js';
import h from '../h';

const I18nScope = _.partial(h.i18nScope, 'projects.reward_fields');

const projectContributionReportContentCard = {
    controller(args) {
        const project = args.project(),
            showDetail = h.toggleProp(false, true),
            currentTab = m.prop('info'),
            checked = contribution => _.contains(args.selectedContributions(), contribution.id),
            selectContribution = (contribution) => {
                const anyChecked = $('input:checkbox').is(':checked');

                args.selectedAny(anyChecked);
                if (!checked(contribution)) {
                    args.selectedContributions().push(contribution.id);
                } else {
                    args.selectedContributions(_.without(args.selectedContributions(), contribution.id));
                }
                return true;
            },
            stateClass = (state) => {
                const classes = {
                    online: {
                        paid: 'text-success.fa-circle',
                        refunded: 'text-error.fa-circle',
                        pending_refund: 'text-error.fa-circle',
                        pending: 'text-waiting.fa-circle',
                        refused: 'text-error.fa-circle'
                    },
                    failed: {
                        paid: 'text-error.fa-circle-o',
                        refunded: 'text-refunded.fa-circle',
                        pending_refund: 'text-refunded.fa-circle-o',
                        pending: 'text-refunded',
                        refused: 'text-refunded'
                    },
                    waiting_funds: {
                        paid: 'text-success.fa-circle',
                        refunded: 'text-error.fa-circle',
                        pending_refund: 'text-error.fa-circle',
                        pending: 'text-waiting.fa-circle',
                        refused: 'text-error.fa-circle'
                    },
                    successful: {
                        paid: 'text-success.fa-circle',
                        refunded: 'text-error.fa-circle',
                        pending_refund: 'text-error.fa-circle',
                        pending: 'text-waiting.fa-circle',
                        refused: 'text-error.fa-circle'
                    }
                };

                return classes[project.state][state];
            };

        return {
            stateClass,
            checked,
            currentTab,
            showDetail,
            selectContribution
        };
    },
    view(ctrl, args) {
        const contribution = args.contribution(),
            project = args.project(),
            profileImg = (_.isEmpty(contribution.profile_img_thumbnail) ? '/assets/catarse_bootstrap/user.jpg' : contribution.profile_img_thumbnail),
            reward = contribution.reward || {
                minimum_value: 0,
                description: 'Nenhuma recompensa selecionada'
            },
            deliveryBadge = () => (contribution.delivery_status === 'error' ?
                                                m('span.badge.badge-attention.fontsize-smaller',
                                                    'Erro no envio'
                                                ) : contribution.delivery_status === 'delivered' ?
                                                m('span.badge.badge-success.fontsize-smaller',
                                                    'Enviada'
                                                ) : contribution.delivery_status === 'received' ?
                                                m('span.fontsize-smaller.badge.badge-success', [
                                                    m('span.fa.fa-check-circle',
                                                        ''
                                                    ),
                                                    ' Recebida'
                                                ]) : '');
        console.log(contribution);

        return m('div', [m(`.w-clearfix.card${ctrl.checked(contribution) ? '.card-alert' : ''}`, [
            m('.w-row', [
                m('.w-col.w-col-1.w-col-small-1.w-col-tiny-1',
                        m('.w-inline-block',
                            m('.w-checkbox.w-clearfix',
                                (contribution.delivery_status !== 'received' && project.state !== 'failed' ?
                                    m('input.w-checkbox-input[type=\'checkbox\']', {
                                        checked: ctrl.checked(contribution),
                                        value: contribution.id,
                                        onclick: () => ctrl.selectContribution(contribution)
                                    }) : '')
                            )
                        )
                    ),
                m('.w-col.w-col-11.w-col-small-11.w-col-tiny-11',
                        m('.w-row', [
                            m('.w-col.w-col-1.w-col-tiny-1', [
                                m(`img.user-avatar.u-marginbottom-10[src='${profileImg}']`)
                            ]),
                            m('.w-col.w-col-11.w-col-tiny-11', [
                                m('.w-row', [
                                    m('.w-col.w-col-3', [
                                        m('.fontcolor-secondary.fontsize-mini.fontweight-semibold', h.momentify(contribution.created_at, 'DD/MM/YYYY, HH:mm')),
                                        m('.fontweight-semibold.fontsize-smaller.lineheight-tighter', contribution.user_name),
                                        m('.fontsize-smallest.lineheight-looser', [
                                            (contribution.has_another ? [
                                                m('a.link-hidden-light.badge.badge-light', '+1 apoio '),
                                            ] : ''),
                                            (contribution.anonymous ? m('span.fa.fa-eye-slash.fontcolor-secondary', m('span.fontcolor-secondary[style="font-size:11px;"]', ' Apoio não-público')) : '')
                                        ]),
                                        m('.fontsize-smallest.lineheight-looser', (contribution.email))
                                    ]),
                                    m('.w-col.w-col-3', [
                                        m('.lineheight-tighter', [
                                            m(`span.fa.fontsize-smallest.${ctrl.stateClass(contribution.state)}`),
                                            '   ',
                                            m('span.fontsize-large', `R$ ${h.formatNumber(contribution.value, 2, 3)}`)
                                        ])
                                    ]),
                                    m('.w-col.w-col-3.w-hidden-small.w-hidden-tiny', [
                                        m('div',
                                            deliveryBadge()
                                        ),
                                        m('.fontsize-smallest.fontweight-semibold', `Recompensa: ${reward.minimum_value ? h.formatNumber(reward.minimum_value, 2, 3) : ''}`),
                                        m('.fontsize-smallest.fontweight-semibold',
                                            reward.title
                                        ),
                                        m('.fontsize-smallest.fontcolor-secondary', `${reward.description.substring(0, 80)}...`)
                                    ]),
                                    m('.w-col.w-col-3.w-col-push-1', [
                                        m('.fontsize-smallest', [
                                            m('a.link-hidden',
                                                'Questionário '
                                            ),
                                            m('span.fontweight-semibold.text-waiting',
                                                'enviado'
                                            )
                                        ]),
                                        m('.fontcolor-terciary.fontsize-smallest',
                                            'em 29/10/2015'
                                        )
                                    ])
                                ])
                            ])
                        ])
                    )
            ]),
            m('a.arrow-admin.fa.fa-chevron-down.fontcolor-secondary.w-inline-block', {
                onclick: ctrl.showDetail.toggle
            })
        ]),
            (ctrl.showDetail() ?
                m('.card.details-backed-project.w-tabs', [
                    m('.w-tab-menu', [
                        m(`a.dashboard-nav-link.w-inline-block.w-tab-link${ctrl.currentTab() === 'info' ? '.w--current' : ''}`, { onclick: () => ctrl.currentTab('info') },
                            m('div',
                                'Info'
                            )
                        ),
                        m(`a.dashboard-nav-link.w-inline-block.w-tab-link${ctrl.currentTab() === 'profile' ? '.w--current' : ''}`, { onclick: () => ctrl.currentTab('profile') },
                            m('div',
                                'Perfil'
                            )
                        )
                    ]),
                    m('.card.card-terciary.w-tab-content', [
                        (ctrl.currentTab() === 'info' ?
                        m('.w-tab-pane.w--tab-active',
                            m('.w-row', [
                                m('.right-divider.w-col.w-col-6', [
                                    m('.u-marginbottom-20', [
                                        m('.fontsize-base.fontweight-semibold.u-marginbottom-10',
                                            'Valor do apoio: R$50'
                                        ),
                                        m('div',
                                            m('.fontsize-smaller', [
                                                m('span.fa.fa-barcode',
                                                    ''
                                                ),
                                                m("a.link-hidden.fontweight-semibold[href='#']", [
                                                    'Boleto',
                                                    m.trust(' ')
                                                ])
                                            ])
                                        ),
                                        m('.fontsize-smaller.fontweight-semibold', [
                                            m('span.fa.fa-circle.text-error',
                                                ''
                                            ),
                                            m.trust(' '),
                                            'Não finalizado'
                                        ]),
                                        m('.fontcolor-secondary.fontsize-smallest',
                                            '19/05/2015, 01:20 h'
                                        )
                                    ]),
                                    m('.fontsize-base.fontweight-semibold',
                                        'Recompensa:'
                                    ),
                                    m('.fontsize-small.fontweight-semibold.u-marginbottom-10', [
                                        `R$${contribution.reward.minimum_value} ${contribution.reward.title ? `- ${contribution.reward.title}` : ''} `,
                                        deliveryBadge()
                                    ]),
                                    m('p.fontsize-smaller',
                                      contribution.reward.description
                                    ),
                                    m('.u-marginbottom-10', [
                                        m('.fontsize-smaller', [
                                            m('span.fontweight-semibold',
                                                'Entrega prevista: '
                                            ),
                                            h.momentify(reward.deliver_at, 'MMMM/YYYY')
                                        ]),
                                        m('.fontsize-smaller', [
                                            m('span.fontweight-semibold',
                                                'Envio: '
                                            ),
                                            I18n.t(`shipping_options.${reward.shipping_options}`, I18nScope())
                                        ])
                                    ])
                                ]),
                                m('.w-col.w-col-6', [
                                    m('.fontsize-base.fontweight-semibold',
                                        'Questionário'
                                    ),
                                    m('.fontsize-smaller.lineheight-tighter.u-marginbottom-20',
                                        'Respondido em 19/10/2015'
                                    ),
                                    m('.fontsize-small', [
                                        m('.fontweight-semibold.lineheight-looser',
                                            'Nome e endereço'
                                        ),
                                        m('p', [
                                            'Ciclana de Tals',
                                            m('br'),
                                            'Rua das Couves, 56',
                                            m('br'),
                                            'Bairro: cosme Velho',
                                            m('br'),
                                            '22241-090 Rio de janeiro-RJ',
                                            m('br'),
                                            'BRAZIL'
                                        ])
                                    ]),
                                    m('.fontsize-small', [
                                        m('.fontweight-semibold.lineheight-looser',
                                            'Tamanho da camisa'
                                        ),
                                        m('p',
                                            'P'
                                        )
                                    ])
                                ])
                            ])
                        ) :
                        m('.w-tab-pane',
                            m('.fontsize-small',
                                m('p', [
                                    contribution.user_name,
                                    m('br'),
                                    contribution.email,
                                    m('br'),
                                    'Conta no Catarse desde Julho 2011',
                                    m('br'),
                                    `Apoiou ${contribution.total_contributed_projects} projetos`,
                                    m('br'),
                                    'Criou 2 projetos'
                                ])
                            )
                        ))
                    ])
                ]) : '')
        ]);
    }
};

export default projectContributionReportContentCard;
<commit_msg>add survey answers to report card<commit_after>import m from 'mithril';
import _ from 'underscore';
import $ from 'jquery';
import I18n from 'i18n-js';
import postgrest from 'mithril-postgrest';
import models from '../models';
import paymentStatus from './payment-status';
import h from '../h';

const I18nScope = _.partial(h.i18nScope, 'projects.reward_fields');

const projectContributionReportContentCard = {
    controller(args) {
        const project = args.project(),
            showDetail = h.toggleProp(false, true),
            currentTab = m.prop('info'),
            checked = contribution => _.contains(args.selectedContributions(), contribution.id),
            selectContribution = (contribution) => {
                const anyChecked = $('input:checkbox').is(':checked');

                args.selectedAny(anyChecked);
                if (!checked(contribution)) {
                    args.selectedContributions().push(contribution.id);
                } else {
                    args.selectedContributions(_.without(args.selectedContributions(), contribution.id));
                }
                return true;
            },
            vm = postgrest.filtersVM({
                contribution_id: 'eq'
            }),
            surveyLoader = () => {
                vm.contribution_id(args.contribution().id);

                return postgrest.loaderWithToken(models.survey.getPageOptions(vm.parameters()));
            },
            survey = m.prop(),
            stateClass = (state) => {
                const classes = {
                    online: {
                        paid: 'text-success.fa-circle',
                        refunded: 'text-error.fa-circle',
                        pending_refund: 'text-error.fa-circle',
                        pending: 'text-waiting.fa-circle',
                        refused: 'text-error.fa-circle'
                    },
                    failed: {
                        paid: 'text-error.fa-circle-o',
                        refunded: 'text-refunded.fa-circle',
                        pending_refund: 'text-refunded.fa-circle-o',
                        pending: 'text-refunded',
                        refused: 'text-refunded'
                    },
                    waiting_funds: {
                        paid: 'text-success.fa-circle',
                        refunded: 'text-error.fa-circle',
                        pending_refund: 'text-error.fa-circle',
                        pending: 'text-waiting.fa-circle',
                        refused: 'text-error.fa-circle'
                    },
                    successful: {
                        paid: 'text-success.fa-circle',
                        refunded: 'text-error.fa-circle',
                        pending_refund: 'text-error.fa-circle',
                        pending: 'text-waiting.fa-circle',
                        refused: 'text-error.fa-circle'
                    }
                };

                return classes[project.state][state];
            };

        surveyLoader().load().then(survey);
        return {
            stateClass,
            survey,
            checked,
            currentTab,
            showDetail,
            selectContribution
        };
    },
    view(ctrl, args) {
        const contribution = args.contribution(),
            project = args.project(),
            survey = _.first(ctrl.survey()),
            profileImg = (_.isEmpty(contribution.profile_img_thumbnail) ? '/assets/catarse_bootstrap/user.jpg' : contribution.profile_img_thumbnail),
            reward = contribution.reward || {
                minimum_value: 0,
                description: 'Nenhuma recompensa selecionada'
            },
            deliveryBadge = () => (contribution.delivery_status === 'error' ?
                                                m('span.badge.badge-attention.fontsize-smaller',
                                                    'Erro no envio'
                                                ) : contribution.delivery_status === 'delivered' ?
                                                m('span.badge.badge-success.fontsize-smaller',
                                                    'Enviada'
                                                ) : contribution.delivery_status === 'received' ?
                                                m('span.fontsize-smaller.badge.badge-success', [
                                                    m('span.fa.fa-check-circle',
                                                        ''
                                                    ),
                                                    ' Recebida'
                                                ]) : '');

        return m('div', [m(`.w-clearfix.card${ctrl.checked(contribution) ? '.card-alert' : ''}`, [
            m('.w-row', [
                m('.w-col.w-col-1.w-col-small-1.w-col-tiny-1',
                        m('.w-inline-block',
                            m('.w-checkbox.w-clearfix',
                                (contribution.delivery_status !== 'received' && project.state !== 'failed' ?
                                    m('input.w-checkbox-input[type=\'checkbox\']', {
                                        checked: ctrl.checked(contribution),
                                        value: contribution.id,
                                        onclick: () => ctrl.selectContribution(contribution)
                                    }) : '')
                            )
                        )
                    ),
                m('.w-col.w-col-11.w-col-small-11.w-col-tiny-11',
                        m('.w-row', [
                            m('.w-col.w-col-1.w-col-tiny-1', [
                                m(`img.user-avatar.u-marginbottom-10[src='${profileImg}']`)
                            ]),
                            m('.w-col.w-col-11.w-col-tiny-11', [
                                m('.w-row', [
                                    m('.w-col.w-col-3', [
                                        m('.fontcolor-secondary.fontsize-mini.fontweight-semibold', h.momentify(contribution.created_at, 'DD/MM/YYYY, HH:mm')),
                                        m('.fontweight-semibold.fontsize-smaller.lineheight-tighter', contribution.user_name),
                                        m('.fontsize-smallest.lineheight-looser', [
                                            (contribution.has_another ? [
                                                m('a.link-hidden-light.badge.badge-light', '+1 apoio '),
                                            ] : ''),
                                            (contribution.anonymous ? m('span.fa.fa-eye-slash.fontcolor-secondary', m('span.fontcolor-secondary[style="font-size:11px;"]', ' Apoio não-público')) : '')
                                        ]),
                                        m('.fontsize-smallest.lineheight-looser', (contribution.email))
                                    ]),
                                    m('.w-col.w-col-3', [
                                        m('.lineheight-tighter', [
                                            m(`span.fa.fontsize-smallest.${ctrl.stateClass(contribution.state)}`),
                                            '   ',
                                            m('span.fontsize-large', `R$ ${h.formatNumber(contribution.value, 2, 3)}`)
                                        ])
                                    ]),
                                    m('.w-col.w-col-3.w-hidden-small.w-hidden-tiny', [
                                        m('div',
                                            deliveryBadge()
                                        ),
                                        m('.fontsize-smallest.fontweight-semibold', `Recompensa: ${reward.minimum_value ? h.formatNumber(reward.minimum_value, 2, 3) : ''}`),
                                        m('.fontsize-smallest.fontweight-semibold',
                                            reward.title
                                        ),
                                        m('.fontsize-smallest.fontcolor-secondary', `${reward.description.substring(0, 80)}...`)
                                    ]),
                                    m('.w-col.w-col-3.w-col-push-1', [
                                        m('.fontsize-smallest', [
                                            m('a.link-hidden',
                                                'Questionário '
                                            ),
                                            m('span.fontweight-semibold.text-waiting',
                                                'enviado'
                                            )
                                        ]),
                                        m('.fontcolor-terciary.fontsize-smallest',
                                            'em 29/10/2015'
                                        )
                                    ])
                                ])
                            ])
                        ])
                    )
            ]),
            m('a.arrow-admin.fa.fa-chevron-down.fontcolor-secondary.w-inline-block', {
                onclick: ctrl.showDetail.toggle
            })
        ]),
            (ctrl.showDetail() ?
                m('.card.details-backed-project.w-tabs', [
                    m('.w-tab-menu', [
                        m(`a.dashboard-nav-link.w-inline-block.w-tab-link${ctrl.currentTab() === 'info' ? '.w--current' : ''}`, { onclick: () => ctrl.currentTab('info') },
                            m('div',
                                'Info'
                            )
                        ),
                        m(`a.dashboard-nav-link.w-inline-block.w-tab-link${ctrl.currentTab() === 'profile' ? '.w--current' : ''}`, { onclick: () => ctrl.currentTab('profile') },
                            m('div',
                                'Perfil'
                            )
                        )
                    ]),
                    m('.card.card-terciary.w-tab-content', [
                        (ctrl.currentTab() === 'info' ?
                        m('.w-tab-pane.w--tab-active',
                            m('.w-row', [
                                m('.right-divider.w-col.w-col-6', [
                                    m('.u-marginbottom-20', [
                                        m('.fontsize-base.fontweight-semibold.u-marginbottom-10',
                                            `Valor do apoio: R$${contribution.value}`
                                        ),
                                        m(paymentStatus, { item: { payment_method: contribution.payment_method, state: contribution.state } }),
                                        m('.fontcolor-secondary.fontsize-smallest',
                                          h.momentify(contribution.created_at, 'DD/MM/YYYY hh:mm')
                                        )
                                    ]),
                                    m('.fontsize-base.fontweight-semibold',
                                        'Recompensa:'
                                    ),
                                    m('.fontsize-small.fontweight-semibold.u-marginbottom-10', [
                                        `R$${contribution.reward.minimum_value} ${contribution.reward.title ? `- ${contribution.reward.title}` : ''} `,
                                        deliveryBadge()
                                    ]),
                                    m('p.fontsize-smaller',
                                      contribution.reward.description
                                    ),
                                    m('.u-marginbottom-10', [
                                        m('.fontsize-smaller', [
                                            m('span.fontweight-semibold',
                                                'Entrega prevista: '
                                            ),
                                            h.momentify(reward.deliver_at, 'MMMM/YYYY')
                                        ]),
                                        m('.fontsize-smaller', [
                                            m('span.fontweight-semibold',
                                                'Envio: '
                                            ),
                                            I18n.t(`shipping_options.${reward.shipping_options}`, I18nScope())
                                        ])
                                    ])
                                ]),

                                m('.w-col.w-col-6', [
                                    m('.fontsize-base.fontweight-semibold',
                                        'Questionário'
                                    ),
                                    m('.fontsize-smaller.lineheight-tighter.u-marginbottom-20',
                                        'Respondido em 19/10/2015'
                                    ),
                                    m('.fontsize-small', [
                                        m('.fontweight-semibold.lineheight-looser',
                                            'Nome e endereço'
                                        ),
                                        m('p', [
                                            'Ciclana de Tals',
                                            m('br'),
                                            'Rua das Couves, 56',
                                            m('br'),
                                            'Bairro: cosme Velho',
                                            m('br'),
                                            '22241-090 Rio de janeiro-RJ',
                                            m('br'),
                                            'BRAZIL'
                                        ])
                                    ]),
                                    (survey ? [
                                        _.map(survey.multiple_choice_questions, (mc_question) => {
                                            const answer = _.find(mc_question.question_choices, choice => choice.id === mc_question.survey_question_choice_id);
                                            return m('.fontsize-small', [
                                                m('.fontweight-semibold.lineheight-looser',
                                              mc_question.question
                                          ),
                                                m('p',
                                                  answer.option
                                          )
                                            ]);
                                        }),
                                        _.map(survey.open_questions, open_question =>
                                      m('.fontsize-small', [
                                          m('.fontweight-semibold.lineheight-looser',
                                              open_question.question
                                          ),
                                          m('p',
                                              open_question.answer
                                          )
                                      ]))
                                    ] : '')
                                ])

                            ])
                        ) :
                        m('.w-tab-pane',
                            m('.fontsize-small',
                                m('p', [
                                    contribution.user_name,
                                    m('br'),
                                    contribution.email,
                                    m('br'),
                                    `Conta no Catarse desde ${h.momentify(contribution.user_created_at, 'MMMM YYYY')}`,
                                    m('br'),
                                    `Apoiou ${contribution.total_contributed_projects} projetos`,
                                    m('br'),
                                    `Criou ${contribution.total_published_projects} projetos`
                                ])
                            )
                        ))
                    ])
                ]) : '')
        ]);
    }
};

export default projectContributionReportContentCard;
<|endoftext|>


<commit_before>/**
 * Twitch follower alerts and actions
 */

import express from 'express';
let router = express.Router();
import {client} from 'backend/twitchapi';
import Follower from 'models/follower';
import credentials from 'credentials.json';
import {io} from 'backend/socket';

const headers = {
    'Client-ID': credentials.tmi.options.clientId,
};
const twBaseUrl = 'https://api.twitch.tv/kraken';

// Follower realtime checker
function getFollowers(cb) {
    client.api({
        'url': twBaseUrl + '/channels/orels1/follows',
        'headers': headers,
    }, (err, res, body) => {
        if (err) return console.error(err);
        // check if followers are already in DB
        for (let follower of body.follows) {
            Follower.findOne({
                'name': follower.user.display_name,
            }, (err, followerInDb) => {
                if (err) return console.error(err);
                // return null if already in there
                if (followerInDb) return null;
                // create new one
                return new Follower({
                    'name': follower.user.display_name,
                    'joindate': follower.created_at
                }).save((err, follower) => {
                    if (err) return console.error(err);
                    return cb(follower);
                });
            });
        }
        return null;
    });
}

setInterval(() => {
    // emit new follower alert to the client
    getFollowers((data) => {io.emit('follower', `New follower! ${data.name}`);});
}, 5000);

/**
 * @api {get} /followers/ List all followers currently in DB
 * @apiName getFollowersList
 * @apiGroup followers
 *
 * @apiUse DBError
 *
 * @apiSuccess (200) {Boolean} error Should always be false
 * @apiSuccess (200) {Object} results Contains the results of Request
 * @apiSuccess (200) {Array} results.list List of followers
 *
 * @apiSuccessExample {json} Success-Response:
 *      HTTP/1.1 200 OK
 *      {
 *          "error": false,
 *          "results": {
 *                  "list": [
 *                      {
 *                          "_id": "21fsdkg9342ijhgh9sf0234",
 *                          "name": "sonobotty",
 *                          "joindate": "ISODate('2016-11-20T21:39:21Z')"
 *                      }
 *                  ]
 *              }
 *      }
 */
router.get('/', (req, res) => {
    Follower.find({}, (err, followers) => {
        if (err) {
            console.log(err);
            return res.status(500).send({
                'error': 'DBError',
                'error_details': 'Could not list followers',
                'results': {},
            });
        }
        return res.status(200).send({
            'error': false,
            'results': {
                'list': followers,
            },
        });
    });
});

export {router};
<commit_msg>changed follower list output formatting + switched to yogscast for testing on a large channel<commit_after>/**
 * Twitch follower alerts and actions
 */

import express from 'express';
let router = express.Router();
import {client} from 'backend/twitchapi';
import Follower from 'models/follower';
import credentials from 'credentials.json';
import {io} from 'backend/socket';

const headers = {
    'Client-ID': credentials.tmi.options.clientId,
};
const twBaseUrl = 'https://api.twitch.tv/kraken';

// Follower realtime checker
function getFollowers(cb) {
    client.api({
        'url': twBaseUrl + '/channels/yogscast/follows',
        'headers': headers,
    }, (err, res, body) => {
        if (err) return console.error(err);
        // check if followers are already in DB
        for (let follower of body.follows) {
            Follower.findOne({
                'name': follower.user.display_name,
            }, (err, followerInDb) => {
                if (err) return console.error(err);
                // return null if already in there
                if (followerInDb) return null;
                // create new one
                return new Follower({
                    'name': follower.user.display_name,
                    'joindate': follower.created_at
                }).save((err, follower) => {
                    if (err) return console.error(err);
                    return cb(follower);
                });
            });
        }
        return null;
    });
}

setInterval(() => {
    // emit new follower alert to the client
    getFollowers((follower) => {io.emit('follower', {'name': follower.name, 'joindate': follower.joindate});});
}, 1000);

/**
 * @api {get} /followers/ List all followers currently in DB
 * @apiName getFollowersList
 * @apiGroup followers
 *
 * @apiUse DBError
 *
 * @apiSuccess (200) {Boolean} error Should always be false
 * @apiSuccess (200) {Object} results Contains the results of Request
 * @apiSuccess (200) {Array} results.list List of followers
 *
 * @apiSuccessExample {json} Success-Response:
 *      HTTP/1.1 200 OK
 *      {
 *          "error": false,
 *          "results": {
 *                  "list": [
 *                      {
 *                          "_id": "21fsdkg9342ijhgh9sf0234",
 *                          "name": "sonobotty",
 *                          "joindate": "ISODate('2016-11-20T21:39:21Z')"
 *                      }
 *                  ]
 *              }
 *      }
 */
router.get('/', (req, res) => {
    Follower.find({})
    .sort({'joindate': -1})
    .select({'_id': 0, 'name': 1, 'joindate': 1})
    .exec((err, followers) => {
        if (err) {
            console.log(err);
            return res.status(500).send({
                'error': 'DBError',
                'error_details': 'Could not list followers',
                'results': {},
            });
        }
        return res.status(200).send({
            'error': false,
            'results': {
                'list': followers,
            },
        });
    });
});

export {router};
<|endoftext|>


<commit_before>// tests/e2e/pattern-lab-compiling.js

const sauce = require('../../scripts/nightwatch-sauce');
const url = require('url');
const querystring = require('querystring');
const fetch = require('node-fetch');
const {
  spawnSync
} = require('child_process');

const { NOW_TOKEN } = process.env;

if (!NOW_TOKEN) {
  console.error('Need to have env var of NOW_TOKEN set');
  process.exit(1);
}

const getDeployUrl = async () => {
  // @todo determine if this is even needed since we have `deployedUrl` from deploy command
  const nowEndpoint = url.resolve('https://api.zeit.co/v2/now/deployments', `?${querystring.stringify({
    teamId: 'boltdesignsystem',
  })}`);

  const nowDeploys = await fetch(nowEndpoint, {
    headers: {
      'Authorization': `Bearer ${NOW_TOKEN}`,
      'Content-Type': 'application/json',
    },
  }).then(res => res.json());

  if (!nowDeploys) {
    console.error('Did not get any info on latest now deploys...');
    process.exit(1);
  }

  nowDeploys.deployments.sort((a, b) => {
    return a.created - b.created;
  }).reverse();

  const latestDeploy = nowDeploys.deployments[0];
  console.log('Latest now.sh Deploy:');
  console.log(latestDeploy);
  return 'https://' + latestDeploy.url;
}

let testingUrl = ''; // cached deploy url we grab from the now.sh API

module.exports = {
  async beforeEach(browser, done) {
    async function getUrl() {
      return await getDeployUrl();
    }

    // log the url being tested against to make it easier to debug failed deploys
    function logTestingUrl(){
      console.log(`Running tests against the ${testingUrl} deploy url.`);
    }

    // grab the latest testing URL from now.sh if we haven't grabbed it yet.
    if (testingUrl === '') {
      getUrl().then(deployUrl => {
        testingUrl = deployUrl; //
        logTestingUrl();
        done();
      });

    // otherwise let's be efficient and use the deploy url we already got
    } else {
      logTestingUrl();
      done();
    }
  },

  'Bolt Docs: Verify Docs Site Compiled + Deployed': function (browser) {
    browser
      .url(`${testingUrl}`)
      .waitForElementVisible('body.c-bolt-site', 1000)
      .assert.containsText('h1.c-bolt-headline', 'Bolt Design System')
      .end()
  },

  'Pattern Lab: Confirm Successful Now.sh Deploy + Pattern Lab Compiled': function (browser) {
    browser
      .url(`${testingUrl}/pattern-lab/patterns/02-components/index.html`)
      .waitForElementVisible('body .sg-main', 1000)
      .verify.title('Bolt Design System')
      .end()
  },
  tearDown: sauce,
};
<commit_msg>fix: fixing E2E integration test for PL<commit_after>// tests/e2e/pattern-lab-compiling.js

const sauce = require('../../scripts/nightwatch-sauce');
const url = require('url');
const querystring = require('querystring');
const fetch = require('node-fetch');
const {
  spawnSync
} = require('child_process');

const { NOW_TOKEN } = process.env;

if (!NOW_TOKEN) {
  console.error('Need to have env var of NOW_TOKEN set');
  process.exit(1);
}

const getDeployUrl = async () => {
  // @todo determine if this is even needed since we have `deployedUrl` from deploy command
  const nowEndpoint = url.resolve('https://api.zeit.co/v2/now/deployments', `?${querystring.stringify({
    teamId: 'boltdesignsystem',
  })}`);

  const nowDeploys = await fetch(nowEndpoint, {
    headers: {
      'Authorization': `Bearer ${NOW_TOKEN}`,
      'Content-Type': 'application/json',
    },
  }).then(res => res.json());

  if (!nowDeploys) {
    console.error('Did not get any info on latest now deploys...');
    process.exit(1);
  }

  nowDeploys.deployments.sort((a, b) => {
    return a.created - b.created;
  }).reverse();

  const latestDeploy = nowDeploys.deployments[0];
  console.log('Latest now.sh Deploy:');
  console.log(latestDeploy);
  return 'https://' + latestDeploy.url;
}

let testingUrl = ''; // cached deploy url we grab from the now.sh API

module.exports = {
  async beforeEach(browser, done) {
    async function getUrl() {
      return await getDeployUrl();
    }

    // log the url being tested against to make it easier to debug failed deploys
    function logTestingUrl(){
      console.log(`Running tests against the ${testingUrl} deploy url.`);
    }

    // grab the latest testing URL from now.sh if we haven't grabbed it yet.
    if (testingUrl === '') {
      getUrl().then(deployUrl => {
        testingUrl = deployUrl; //
        logTestingUrl();
        done();
      });

    // otherwise let's be efficient and use the deploy url we already got
    } else {
      logTestingUrl();
      done();
    }
  },

  'Bolt Docs: Verify Docs Site Compiled + Deployed': function (browser) {
    browser
      .url(`${testingUrl}`)
      .waitForElementVisible('body.c-bolt-site', 1000)
      .assert.containsText('h1.c-bolt-headline', 'Bolt Design System')
      .end()
  },

  'Pattern Lab: Confirm Successful Now.sh Deploy + Pattern Lab Compiled': function (browser) {
    browser
      .url(`${testingUrl}/pattern-lab/patterns/02-components/index.html`)
      .waitForElementVisible('body .pl-c-main', 1000)
      .verify.title('Bolt Design System')
      .end();
  },
  tearDown: sauce,
};
<|endoftext|>


<commit_before>"use strict";

module.exports.index = function(req, res, next) {
  next(new Error("TODO /"));
};

module.exports.identifier = function(req, res, next) {
  next(new Error("TODO /:identifier"));
};
<commit_msg>Mock data<commit_after>"use strict";

var async = require('async');

module.exports.index = function(req, res, next) {
  async.waterfall([
    function returnList() {
      res.send([
        {
          "id": "53cf88a5cf25841c6144ee46",
          "identifier": "https://docs.google.com/file/d/0Ao1Q9EZ6RihjdHBSSnlzWG5mRFMxTm5YRVMwZzd0b0E",
          "creation_date": "2013-06-26T09:13:46.552Z",
          "modification_date": "2013-06-26T09:13:46.552Z",
          "actions": {
              "show": "https://docs.google.com/file/d/0Ao1Q9EZ6RihjdHBSSnlzWG5mRFMxTm5YRVMwZzd0b0E"
          },
          "data": {
              "title": "Traductions.pdf",
              "path": "/Traductions.pdf",
              "snippet": "des entreprises\nIntégration du CV des participants\nApplication de recherche pour les recruteurs\nà partir de 50€/mois\nPapiel Process\nPour recrutement grande échelle\nTraductions\nIntégrez les CVs images"
          },
          "related_count": 0,
        }
      ]);
    }
  ], next);
};

module.exports.identifier = function(req, res, next) {
  async.waterfall([
    function returnItem() {
      res.send({
        "id": "53ce71b24882ec9d58f08235",
        "identifier": "https://mail.google.com/mail/b/matthieu.bacconnier@papiel.fr/#contact/79516afe8f2cdfba",
        "creation_date": "2014-07-22T14:14:10.150Z",
        "modification_date": "2014-02-07T12:45:56.254Z",
        "actions": {
            "show": "https://mail.google.com/mail/b/matthieu.bacconnier@papiel.fr/#contact/79516afe8f2cdfba"
        },
        "data": {
            "name": "Florian Rossiaud",
            "job": " ",
            "phone": [
                {
                    "phone": "+336 43 19 05 98",
                    "type": "mobile"
                }
            ],
            "email": [
                {
                    "email": "florianrossiaud@gmail.com",
                    "type": "work"
                }
            ],
            "image": "data:image/*;base64,/9j/4AAQSkZJRgqUa8G1Lfv6pUPUqv2Kwk5mJYj+df4oP//Z"
        },
        "related": []
      });
    }
  ], next);
};
<|endoftext|>


<commit_before>var canvas, ctx, flag = false;
var prevX, preyY, currX, currY;
prevX = prevY = currX = currY = 0;
var dot_flag = false;
var x = "black";
var y = 2;

function init() {
  canvas = document.getElementById("canvas");
  ctx = canvas.getContext("2d");
  w = canvas.width;
  h = canvas.height;

  canvas.addEventListener("mousemove", function(e) {
    findxy("move", e);
  }, false);
  canvas.addEventListener("mousedown", function(e) {
    findxy("down", e);
  }, false);
  canvas.addEventListener("mouseup", function(e) {
    findxy("up", e);
  }, false);
  canvas.addEventListener("mouseout", function(e) {
    findxy("out", e);
  }, false);
}

function draw() {
  ctx.beginPath();
  ctx.moveTo(prevX, prevY);
  ctx.lineTo(currX, currY);
  ctx.strokeStyle = x;
  ctx.lineWidth = y;
  ctx.stroke();
  ctx.closePath();
}

function findxy(res, e) {
  var div = document.getElementById("pos")
  console.log(res);
  if (res == "down") {
    prevX = currX;
    prevY = currY;
    currX = e.clientX - canvas.offsetLeft;
    currY = e.clientY - canvas.offsetTop;

    flag = true;
    dot_flag = true;
    if (dot_flag) {
      ctx.beginPath();
      ctx.fillStyle = x;
      ctx.fillRect(currX, currY, 2, 2);
      ctx.closePath();
      dot_flag = false;
    }
  }
  if (res == "up" || res == "out") {
    flag = false;
  }
  if (res == "move") {
    if (flag) {
      prevX = currX;
      prevY = currY;
      currX = e.clientX - canvas.offsetLeft;
      currY = e.clientY - canvas.offsetTop;
      draw();
    }
  }
  pos.innerHTML = "prevX: " + prevX + "
prevY: " + prevY + "
currX: " + currX + "
currY: " + currY
}
<commit_msg>began serialising<commit_after>var canvas, ctx, isDrawing = false;
var prevX, preyY, currX, currY;
prevX = prevY = currX = currY = 0;
var dot_flag = false;
var colour = "black";
var strokeWidth = 2;

function Point(prevX, prevY, currX, currY) {
  this.prevX = prevX;
  this.prevY = prevY;
  this.currX = currX;
  this.currY = currY;
}

function Drawing() {
  this.points = [];
}

Drawing.prototype.addPoint = function(point) {
  this.points.push(point);
}

var drawings = [];
var currentDrawing;

function init() {
  canvas = document.getElementById("canvas");
  ctx = canvas.getContext("2d");
  w = canvas.width;
  h = canvas.height;

  canvas.addEventListener("mousemove", function(e) {
    findxy("move", e);
  }, false);
  canvas.addEventListener("mousedown", function(e) {
    findxy("down", e);
  }, false);
  canvas.addEventListener("mouseup", function(e) {
    findxy("up", e);
  }, false);
  canvas.addEventListener("mouseout", function(e) {
    findxy("out", e);
  }, false);
}

function draw() {
  ctx.beginPath();
  ctx.moveTo(prevX, prevY);
  ctx.lineTo(currX, currY);
  ctx.strokeStyle = colour;
  ctx.lineWidth = strokeWidth;
  ctx.stroke();
  ctx.closePath();
}

function findxy(res, e) {
  var div = document.getElementById("pos")
  console.log(res);
  if (res == "down") {
    // new drawing object
    currentDrawing = new Drawing();
    prevX = currX;
    prevY = currY;
    currX = e.clientX - canvas.offsetLeft;
    currY = e.clientY - canvas.offsetTop;

    isDrawing = true;
    dot_flag = true;
    if (dot_flag) {
      ctx.beginPath();
      ctx.fillStyle = colour;
      ctx.fillRect(currX, currY, 2, 2);
      ctx.closePath();
      dot_flag = false;
    }
  }
  if (res == "up" || res == "out") {
    isDrawing = false;
    drawings.push(currentDrawing);
    console.log("added drawing " + JSON.stringify(currentDrawing));
    // finalise drawing object
  }
  if (res == "move") {
    if (isDrawing) {
      prevX = currX;
      prevY = currY;
      currX = e.clientX - canvas.offsetLeft;
      currY = e.clientY - canvas.offsetTop;
      p = new Point(prevX, prevY, currX, currY);
      console.log("added point " + JSON.stringify(p));
      currentDrawing.addPoint(p);
      draw();
    }
  }
  pos.innerHTML = "prevX: " + prevX + "
prevY: " + prevY + "
currX: " + currX + "
currY: " + currY
}

function replay_draw() {

}

function replay(drawing) {

}
<|endoftext|>


<commit_before>import assert from 'assert';
import sinon from 'sinon';
import {
  promiseThat,
  assertThat,
  fulfilled,
  isFulfilledWith,
  allOf,
  equalTo,
  is,
  truthy,
  falsy
} from 'hamjest';

assert.called = sinon.assert.called;
assert.calledWith = sinon.assert.calledWith;

describe('', function() {

  class File {}

  class JsonFile {
    constructor(readFile, writeFile) {
      this.readFile = readFile;
      this.writeFile = writeFile;
      this.fileContent = null;
    }
    readFrom(file) {
      this.fileContent = this.readFile(file);
      return this;
    }
    minify() {
      const minifyJson = content => JSON.stringify(JSON.parse(content));
      this.fileContent = this.fileContent.then(minifyJson);
      return this;
    }
    writeTo(file) {
      return this.fileContent.then(content => this.writeFile(file, content));
    }
  }

  describe('reads the file', function() {

    const payload = {a:1};
    const beautifiedJson = JSON.stringify(payload, null, 4);
    const minifiedJson = JSON.stringify(payload);
    const file = new File();

    function readJsonFile(readFile, file) {
      return new JsonFile(readFile).readFrom(file).minify().fileContent;
    }

    it('and minifies the JSON', function() {
      let readFile = () => Promise.resolve(beautifiedJson);

      const fileContent = readJsonFile(readFile);

      return promiseThat(fileContent, isFulfilledWith(minifiedJson));
    });

    it('from the given path', function() {
      let readFile = sinon.stub();
      readFile.returns(Promise.resolve(beautifiedJson));

      readJsonFile(readFile, file);

      assert.calledWith(readFile, file);
    });

  });

  describe('writes file content', function() {

    const fileContent = 'file content';
    const readFile = () => Promise.resolve(fileContent);
    const writeFile = sinon.stub();
    const file = new File();

    it('and returns a promise when done', function() {
      let jsonFile = new JsonFile(readFile, writeFile);

      return promiseThat(jsonFile.readFrom().writeTo(file), fulfilled());
    });

    it('to given path', function(done) {
      let jsonFile = new JsonFile(readFile, writeFile);

      // actually i think i want this!
      //return promiseThat(jsonFile.readFrom().writeTo(file), allOf(fulfilled(), is(truthy(writeFile.calledWith(file, fileContent)))));

      jsonFile.readFrom().writeTo(file).then(() => {
        assert.calledWith(writeFile, file, fileContent);
        done();
      }).catch(() => {
        done(new Error('promise was rejected'));
      });
    });

  });

});
<commit_msg>More logical order.<commit_after>import assert from 'assert';
import sinon from 'sinon';
import {
  promiseThat,
  assertThat,
  fulfilled,
  isFulfilledWith,
  allOf,
  equalTo,
  is,
  truthy,
  falsy
} from 'hamjest';

assert.called = sinon.assert.called;
assert.calledWith = sinon.assert.calledWith;

describe('read, minify and write a JSON file', function() {

  class File {}

  class JsonFile {
    constructor(readFile, writeFile) {
      this.readFile = readFile;
      this.writeFile = writeFile;
      this.fileContent = null;
    }
    readFrom(file) {
      this.fileContent = this.readFile(file);
      return this;
    }
    minify() {
      const minifyJson = content => JSON.stringify(JSON.parse(content));
      this.fileContent = this.fileContent.then(minifyJson);
      return this;
    }
    writeTo(file) {
      return this.fileContent.then(content => this.writeFile(file, content));
    }
  }

  describe('reads the file', function() {

    const payload = {a:1};
    const beautifiedJson = JSON.stringify(payload, null, 4);
    const minifiedJson = JSON.stringify(payload);
    const file = new File();

    function readJsonFile(readFile, file) {
      return new JsonFile(readFile).readFrom(file).minify().fileContent;
    }

    it('from the given path', function() {
      let readFile = sinon.stub();
      readFile.returns(Promise.resolve(beautifiedJson));

      readJsonFile(readFile, file);

      assert.calledWith(readFile, file);
    });

    it('and minifies the JSON', function() {
      let readFile = () => Promise.resolve(beautifiedJson);

      const fileContent = readJsonFile(readFile);

      return promiseThat(fileContent, isFulfilledWith(minifiedJson));
    });

  });

  describe('writes file content', function() {

    const fileContent = 'file content';
    const readFile = () => Promise.resolve(fileContent);
    const writeFile = sinon.stub();
    const file = new File();

    it('and returns a promise when done', function() {
      let jsonFile = new JsonFile(readFile, writeFile);

      return promiseThat(jsonFile.readFrom().writeTo(file), fulfilled());
    });

    it('to given path', function(done) {
      let jsonFile = new JsonFile(readFile, writeFile);

      // actually i think i want this!
      //return promiseThat(jsonFile.readFrom().writeTo(file), allOf(fulfilled(), is(truthy(writeFile.calledWith(file, fileContent)))));

      jsonFile.readFrom().writeTo(file).then(() => {
        assert.calledWith(writeFile, file, fileContent);
        done();
      }).catch(() => {
        done(new Error('promise was rejected'));
      });
    });

  });

});
<|endoftext|>


<commit_before>const React = require('react');
const h = require('react-hyperscript');
const { Tab, Tabs, TabList, TabPanel } = require('react-tabs');

class EnrichmentMenu extends React.Component {
  /**
   *  @description Hides nodes based on adjusted p_value, determined by on-screen slider
   */
  sliderUpdate(){
    //get value from slider
    let sliderVal = document.getElementById('enrichment-p_value-slider').value;
    this.props.controller.updateSlider(sliderVal);
    this.filterNodes(sliderVal);
  }

  filterNodes(sliderVal){
    //compare p_values and hide if outside of chosen threshold
    const cy = this.props.cySrv.get();
    cy.nodes().forEach(node => {
      if(node.data('p_value') > sliderVal)
        node.addClass('hidden');
      else
        node.removeClass('hidden');
    });
  }

  render(){

    let { invalidTokens, sliderVal } = this.props;

    const slider = [
      h("input",{type:"range",id:'enrichment-p_value-slider',min:0,max:0.05,step:0.0001,defaultValue:sliderVal,
      onInput:() => this.sliderUpdate() })
    ];

    this.filterNodes(sliderVal);

    const unrecognizedTokens = invalidTokens.length === 0 ? '' : [
        h('h3', 'Unrecognized Genes (' + invalidTokens.length + ')'),
        h('div', invalidTokens.join(", "))
      ];

    return h(Tabs, [
      h('div.enrichment-drawer-header', [
        h('h2', 'Enrichment App'), //******************* CHANGE TO NEW NAME ONCE CHOSEN
        h(TabList, [
          h(Tab, {
            className: 'enrichment-drawer-tab',
            selectedClassName: 'enrichment-drawer-tab-selected'
            }, 'Data'),
          h(Tab, {
            className: 'enrichment-drawer-tab',
            selectedClassName: 'enrichment-drawer-tab-selected'
          }, 'FAQ')
        ])
      ]),
      h(TabPanel, [
        h('h3', 'P-Value Cutoff'),
        h('div.enrichment-legend-container', [
          h('div.enrichment-legend', [
            h('p', '0'),
            h('p', '.025'),
            h('p', '.05')
          ])
        ]),
        h('div.enrichment-slider-wrapper', slider),
        h('div.unrecognized-token-container', unrecognizedTokens),
      ]),
      h(TabPanel, [
        h('h4', `What does it do?`),
        h('p', `This app identifies biological pathways that contain your genes of interest and displays them as an interactive network,
          allowing you to explore individual pathways.
          Pathways are drawn from Reactome (v56) and Gene Ontology, Biological Process (Ensembl v90 / Ensembl Genomes v37).`),

        h('h4', `What kinds of inputs are accepted?`),
        h('p', `This app will recognize:`),
        h('p', [
          h('li.gene-identifier-list', `HUGO Gene Nomenclature (HGNC) symbols (e.g. 'TP53') and IDs (e.g. 'HGNC:11998')`),
          h('li.gene-identifier-list', `UniProt protein accessions (e.g. 'P04637')`),
          h('li.gene-identifier-list', `NCBI Gene gene IDs (e.g. '7157)`),
        ]),

        h('h4', `What do the elements of the network represent?`),
        h('p', `Each pathway is represented by a node (circle) whose size corresponds to the number of genes in that pathway.
          Pathways that share genes are connected by edges (lines) whose width corresponds to the number of shared genes.
          Click on a node to see more information about the pathway.`),

        h('h4', `How does the app identify pathways?`),
        h('p',[
          h('span',`The input gene list is compared to genes in each candidate pathway and a statistical score is calculated (adjusted p-value).
            Pathways with an adjusted p-value less than a threshold (0.05) are deemed 'enriched' for genes in the input list and are displayed in the network.
            This analysis is performed by gProfiler's g:GOst service (rev 1741 2017-10-19). Please refer to their `),
          h('a', {href: 'https://biit.cs.ut.ee/gprofiler/help.cgi?help_id=55', target:"_blank"}, 'documentation'),
          h('span', ` for further details.`)
        ])
      ])
    ]);
  }
}

module.exports = EnrichmentMenu;<commit_msg>delete unused enrichment menu component<commit_after><|endoftext|>


<commit_before>var addClass = require('../addClass')
var jsdom = require('mocha-jsdom')
var assert = require('chai').assert

describe('addClass', function () {
  jsdom()
  it('should add a class to the current class', function () {
    Object.defineProperty(window.Element.prototype, 'classList', {
      get: function () {
        return null
      }
    })
    var div = document.createElement('div')
    div.className = 'foo bar'
    addClass(div, 'baz')
    assert.equal(div.className, 'foo bar baz')
  })
  it('should not add a class that already exists', function () {
    var div = document.createElement('div')
    div.className = 'foo bar'
    addClass(div, 'bar')
    assert.equal(div.className, 'foo bar')
  })
})
<commit_msg>Added timeout to first test<commit_after>var addClass = require('../addClass')
var jsdom = require('mocha-jsdom')
var assert = require('chai').assert

describe('addClass', function () {
  jsdom()
  it('should add a class to the current class', function () {
    this.timeout(5000)
    Object.defineProperty(window.Element.prototype, 'classList', {
      get: function () {
        return null
      }
    })
    var div = document.createElement('div')
    div.className = 'foo bar'
    addClass(div, 'baz')
    assert.equal(div.className, 'foo bar baz')
  })
  it('should not add a class that already exists', function () {
    var div = document.createElement('div')
    div.className = 'foo bar'
    addClass(div, 'bar')
    assert.equal(div.className, 'foo bar')
  })
})
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015-2016 Actor LLC. <https://actor.im>
 */

import { forEach, map, throttle } from 'lodash';

import React, { Component, PropTypes } from 'react';
import { findDOMNode } from 'react-dom';
import { Container } from 'flux/utils';
import Scrollbar from '../common/Scrollbar.react';

import CreateGroupActionCreators from '../../actions/CreateGroupActionCreators';
import ContactActionCreators from '../../actions/ContactActionCreators';
import GroupListActionCreators from '../../actions/GroupListActionCreators';
import AddContactActionCreators from '../../actions/AddContactActionCreators';

import AllDialogsStore from '../../stores/AllDialogsStore';

import RecentItem from './RecentItem.react';

class Recent extends Component {
  constructor(props) {
    super(props);
  }

  static contextTypes = {
    intl: PropTypes.object
  };

  static getStores = () => [AllDialogsStore];

  static calculateState() {
    return {
      allDialogs: AllDialogsStore.getAllDialogs()
    };
  }

  componentDidUpdate() {
    setTimeout(() => this.checkInvisibleCounters(), 500)
  }

  handleCreateGroup = () => CreateGroupActionCreators.open();

  handleCreatePrivate = () => AddContactActionCreators.open();

  handleGroupListClick = () => GroupListActionCreators.open();

  handlePrivateListClick = () => ContactActionCreators.open();

  handleRecentScroll = throttle((event) => {
    this.checkInvisibleCounters();
  }, 100, {trailing: true});

  checkInvisibleCounters = () => {
    const unreadNodes = document.getElementsByClassName('sidebar__list__item--unread');
    const scrollNode = findDOMNode(this.refs.container);
    const scrollNodeRect = scrollNode.getBoundingClientRect();

    let haveUnreadAbove = false,
        haveUnreadBelow = false,
        lastUnreadBelow,
        firstUnreadAbove;

    forEach(unreadNodes, (node) => {
      const rect = node.getBoundingClientRect();
      if ((scrollNodeRect.top + scrollNodeRect.height) < rect.top) {
        haveUnreadBelow = true;
        lastUnreadBelow = node;
      }
      if (scrollNodeRect.top > (rect.top + rect.height)) {
        haveUnreadAbove = true;
        if (!firstUnreadAbove) {
          firstUnreadAbove = node;
        }
      }
    });

    this.setState({haveUnreadAbove, haveUnreadBelow, firstUnreadAbove, lastUnreadBelow})
  };

  scrollToFirstHiddenAbove = () => {
    const { firstUnreadAbove } = this.state;
    const rect = firstUnreadAbove.getBoundingClientRect();
    const scrollNode = findDOMNode(this.refs.container).getElementsByClassName('ss-content')[0];
    const scrollNodeRect = scrollNode.getBoundingClientRect();

    this.refs.container.scrollTo(scrollNode.scrollTop + rect.top - scrollNodeRect.top)
  };

  scrollToLastHiddenBelow = () => {
    const { lastUnreadBelow } = this.state;
    const rect = lastUnreadBelow.getBoundingClientRect();
    const scrollNode = findDOMNode(this.refs.container).getElementsByClassName('ss-content')[0];
    const scrollNodeRect = scrollNode.getBoundingClientRect();

    this.refs.container.scrollTo(scrollNode.scrollTop + rect.top - (scrollNodeRect.top + scrollNodeRect.height - rect.height));
  };

  render() {
    const { allDialogs, haveUnreadAbove, haveUnreadBelow } = this.state;
    const { intl } = this.context;

    const recentGroups = map(allDialogs, (dialogGroup, index) => {
      let groupTitle;
      switch (dialogGroup.key) {
        case 'groups':
          groupTitle = (

              {intl.messages[`sidebar.recents.${dialogGroup.key}`]}
              add_circle_outline

          );
          break;
        case 'privates':
          groupTitle = (

              {intl.messages[`sidebar.recents.${dialogGroup.key}`]}
              add_circle_outline

          );
          break;
        default:
          groupTitle =
{intl.messages[`sidebar.recents.${dialogGroup.key}`]}
;
      }

      const groupList = map(dialogGroup.shorts, (dialog, index) => <RecentItem dialog={dialog}
                                                                               key={index}
                                                                               type={dialogGroup.key}/>);

      return (
        <ul className={`sidebar__list sidebar__list--${dialogGroup.key}`} key={index}>
          {groupTitle}
          {groupList}

      )
    });

    return (

        {
          haveUnreadAbove
            ?

            keyboard_arrow_up

            : null
        }
        <Scrollbar ref="container" onScroll={this.handleRecentScroll}>

{recentGroups}

        </Scrollbar>
        {
          haveUnreadBelow
            ?

            keyboard_arrow_down

            : null
        }

    );
  }
}

export default Container.create(Recent, {pure: false});
<commit_msg>fix(web): reduce lag on sidebar scrolling<commit_after>/*
 * Copyright (C) 2015-2016 Actor LLC. <https://actor.im>
 */

import { forEach, map, debounce } from 'lodash';

import React, { Component, PropTypes } from 'react';
import { findDOMNode } from 'react-dom';
import { Container } from 'flux/utils';
import Scrollbar from '../common/Scrollbar.react';

import CreateGroupActionCreators from '../../actions/CreateGroupActionCreators';
import ContactActionCreators from '../../actions/ContactActionCreators';
import GroupListActionCreators from '../../actions/GroupListActionCreators';
import AddContactActionCreators from '../../actions/AddContactActionCreators';

import AllDialogsStore from '../../stores/AllDialogsStore';

import RecentItem from './RecentItem.react';

class Recent extends Component {
  constructor(props) {
    super(props);
    this.checkInvisibleCounters = debounce(this.checkInvisibleCounters, 50, {
      maxWait: 150,
      leading: true
    });
  }

  static contextTypes = {
    intl: PropTypes.object
  };

  static getStores = () => [AllDialogsStore];

  static calculateState() {
    return {
      allDialogs: AllDialogsStore.getAllDialogs()
    };
  }

  componentDidUpdate() {
    setTimeout(() => this.checkInvisibleCounters(), 500)
  }

  handleCreateGroup = () => CreateGroupActionCreators.open();

  handleCreatePrivate = () => AddContactActionCreators.open();

  handleGroupListClick = () => GroupListActionCreators.open();

  handlePrivateListClick = () => ContactActionCreators.open();

  handleRecentScroll = () => this.checkInvisibleCounters();

  checkInvisibleCounters = () => {
    const unreadNodes = document.getElementsByClassName('sidebar__list__item--unread');
    const scrollNode = findDOMNode(this.refs.container);
    const scrollNodeRect = scrollNode.getBoundingClientRect();

    let haveUnreadAbove = false,
        haveUnreadBelow = false,
        lastUnreadBelow,
        firstUnreadAbove;

    forEach(unreadNodes, (node) => {
      const rect = node.getBoundingClientRect();
      if ((scrollNodeRect.top + scrollNodeRect.height) < rect.top) {
        haveUnreadBelow = true;
        lastUnreadBelow = node;
      }
      if (scrollNodeRect.top > (rect.top + rect.height)) {
        haveUnreadAbove = true;
        if (!firstUnreadAbove) {
          firstUnreadAbove = node;
        }
      }
    });

    this.setState({haveUnreadAbove, haveUnreadBelow, firstUnreadAbove, lastUnreadBelow});
  };

  scrollToFirstHiddenAbove = () => {
    const { firstUnreadAbove } = this.state;
    const rect = firstUnreadAbove.getBoundingClientRect();
    const scrollNode = findDOMNode(this.refs.container).getElementsByClassName('ss-content')[0];
    const scrollNodeRect = scrollNode.getBoundingClientRect();

    this.refs.container.scrollTo(scrollNode.scrollTop + rect.top - scrollNodeRect.top)
  };

  scrollToLastHiddenBelow = () => {
    const { lastUnreadBelow } = this.state;
    const rect = lastUnreadBelow.getBoundingClientRect();
    const scrollNode = findDOMNode(this.refs.container).getElementsByClassName('ss-content')[0];
    const scrollNodeRect = scrollNode.getBoundingClientRect();

    this.refs.container.scrollTo(scrollNode.scrollTop + rect.top - (scrollNodeRect.top + scrollNodeRect.height - rect.height));
  };

  render() {
    const { allDialogs, haveUnreadAbove, haveUnreadBelow } = this.state;
    const { intl } = this.context;

    const recentGroups = map(allDialogs, (dialogGroup, index) => {
      let groupTitle;
      switch (dialogGroup.key) {
        case 'groups':
          groupTitle = (

              {intl.messages[`sidebar.recents.${dialogGroup.key}`]}
              add_circle_outline

          );
          break;
        case 'privates':
          groupTitle = (

              {intl.messages[`sidebar.recents.${dialogGroup.key}`]}
              add_circle_outline

          );
          break;
        default:
          groupTitle =
{intl.messages[`sidebar.recents.${dialogGroup.key}`]}
;
      }

      const groupList = map(dialogGroup.shorts, (dialog, index) => <RecentItem dialog={dialog}
                                                                               key={index}
                                                                               type={dialogGroup.key}/>);

      return (
        <ul className={`sidebar__list sidebar__list--${dialogGroup.key}`} key={index}>
          {groupTitle}
          {groupList}

      )
    });

    return (

        {
          haveUnreadAbove
            ?

            keyboard_arrow_up

            : null
        }
        <Scrollbar ref="container" onScroll={this.handleRecentScroll}>

{recentGroups}

        </Scrollbar>
        {
          haveUnreadBelow
            ?

            keyboard_arrow_down

            : null
        }

    );
  }
}

export default Container.create(Recent, {pure: false});
<|endoftext|>


<commit_before>// Karma configuration
// Generated on Sat Jun 13 2015 13:23:14 GMT-0600 (MDT)

var customLaunchers = {
    "sl_chrome_43": {
        base: "SauceLabs",
        browserName: "chrome",
        version: "43"
    },
    "sl_chrome_42": {
        base: "SauceLabs",
        browserName: "chrome",
        version: "42"
    },
    "sl_chrome_36": {
        base: "SauceLabs",
        browserName: "chrome",
        version: "36"
    },
    "sl_chrome_31": {
        base: "SauceLabs",
        browserName: "chrome",
        version: "31"
    },
    "sl_firefox_37": {
        base: "SauceLabs",
        browserName: "firefox",
        version: "37"
    },
    "sl_firefox_38": {
        base: "SauceLabs",
        browserName: "firefox",
        version: "38"
    },
    "sl_ie_9": {
        base: "SauceLabs",
        browserName: "internet explorer",
        version: "9"
    },
    "sl_ie_10": {
        base: "SauceLabs",
        browserName: "internet explorer",
        version: "10"
    },
    "sl_ie_11": {
        base: "SauceLabs",
        browserName: "internet explorer",
        version: "11"
    }
};

module.exports = function(config) {

  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: "",

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ["browserify", "mocha", "chai"], // "detectBrowsers"],

    // list of files / patterns to load in the browser
    files: [
      "node_modules/babel/polyfill.js",
      "es5/**/*.js"
    ],

    // list of files to exclude
    exclude: [
    ],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
        "node_modules/babel/polyfill.js": ["browserify"],
        "es5/**/*.js": ["browserify"]
    },

    browserify: {
      debug: true
    },

    sauceLabs: {
        testName: "Jargon.js Unit Tests"
    },

    customLaunchers: customLaunchers,

    // test results reporter to use
    // possible values: "dots", "progress"
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ["progress", "saucelabs"],

    // web server port
    port: 9876,

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: false,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: Object.keys(customLaunchers), // "Chrome", "Firefox", "Safari", "Opera", "IE"],

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false
  });
};
<commit_msg>Adding tunnel identifier to saucelabs travis integration<commit_after>// Karma configuration
// Generated on Sat Jun 13 2015 13:23:14 GMT-0600 (MDT)

var customLaunchers = {
    "sl_chrome_43": {
        base: "SauceLabs",
        browserName: "chrome",
        version: "43"
    },
    "sl_chrome_42": {
        base: "SauceLabs",
        browserName: "chrome",
        version: "42"
    },
    "sl_chrome_36": {
        base: "SauceLabs",
        browserName: "chrome",
        version: "36"
    },
    "sl_chrome_31": {
        base: "SauceLabs",
        browserName: "chrome",
        version: "31"
    },
    "sl_firefox_37": {
        base: "SauceLabs",
        browserName: "firefox",
        version: "37"
    },
    "sl_firefox_38": {
        base: "SauceLabs",
        browserName: "firefox",
        version: "38"
    },
    "sl_ie_9": {
        base: "SauceLabs",
        browserName: "internet explorer",
        version: "9"
    },
    "sl_ie_10": {
        base: "SauceLabs",
        browserName: "internet explorer",
        version: "10"
    },
    "sl_ie_11": {
        base: "SauceLabs",
        browserName: "internet explorer",
        version: "11"
    }
};

module.exports = function(config) {

  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: "",

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ["browserify", "mocha", "chai"], // "detectBrowsers"],

    // list of files / patterns to load in the browser
    files: [
      "node_modules/babel/polyfill.js",
      "es5/**/*.js"
    ],

    // list of files to exclude
    exclude: [
    ],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
        "node_modules/babel/polyfill.js": ["browserify"],
        "es5/**/*.js": ["browserify"]
    },

    browserify: {
      debug: true
    },

    sauceLabs: {
        testName: "Jargon.js Unit Tests",
        tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER
    },

    customLaunchers: customLaunchers,

    // test results reporter to use
    // possible values: "dots", "progress"
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ["progress", "saucelabs"],

    // web server port
    port: 9876,

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: false,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: Object.keys(customLaunchers), // "Chrome", "Firefox", "Safari", "Opera", "IE"],

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false
  });
};
<|endoftext|>


<commit_before>var defaultLanguage = undefined;
function getDefaultLanguage() {
    if (!defaultLanguage && app)
        defaultLanguage = app.getConfig().getDefaultLanguage();
    return defaultLanguage;
}

function convertBasicHtml(content) {

    var options = Opal.hash2(['backend', 'safe', 'attributes'], {
        backend: 'html5',
        safe: 'safeMode',
        attributes: 'showtitle icons=font@ source-highlighter=highlight.js platform=opal platform-opal env=browser env-browser '
    });

    var doc = Opal.Asciidoctor.$load(content,options);

    doc.attributes.keys["lang"] = doc.attributes.keys["lang"] || getDefaultLanguage();

    return doc.$convert();
}

function convertHtmlBook(content) {

    var options = Opal.hash2(['backend', 'safe', 'attributes', "header_footer"], {
        backend: 'html5',
        safe: 'safeMode',
        attributes: 'showtitle icons=font@ source-highlighter=highlight.js platform=opal platform-opal env=browser env-browser '
        , 'header_footer': true
    });

    var doc = Opal.Asciidoctor.$load(content,options);

    doc.attributes.keys["lang"] = doc.attributes.keys["lang"] || getDefaultLanguage();

    return doc.$render();
}

function convertHtmlArticle(content) {

    var options = Opal.hash2(['attributes', 'header_footer'],
        {
            'attributes': ['backend=html5', 'doctype=article'],
            'header_footer': true
        });

    var doc = Opal.Asciidoctor.$load(content,options);

    doc.attributes.keys["lang"] = doc.attributes.keys["lang"] || getDefaultLanguage();

    return doc.$render();
}

function convertDocbook(content, includeHeader) {

    var options = Opal.hash2(['attributes', 'header_footer'],
        {
            'attributes': ['backend=docbook5', 'doctype=book'],
            'header_footer': includeHeader
        });

    var doc = Opal.Asciidoctor.$load(content, options);

    doc.attributes.keys["lang"] = doc.attributes.keys["lang"] || getDefaultLanguage();

    return doc.$render();
}

function convertDocbookArticle(content) {

    var options = Opal.hash2(["lang", 'attributes', 'header_footer'],
        {
            'attributes': ['backend=docbook5', 'doctype=article'],
            'header_footer': true
        });

    var doc = Opal.Asciidoctor.$load(content,options);

    doc.attributes.keys["lang"] = doc.attributes.keys["lang"] || getDefaultLanguage();

    return doc.$render();
}

function scrollTo60(position) {
    $(window).scrollTop(position - 60);
}

function runScroller(content) {

    var renderedSelection = Opal.Asciidoctor.$render(content);

    if (renderedSelection.trim() == "") {
        return;
    }
    else if ($(renderedSelection).css("page-break-after")) {
        //page break element
        return;
    }
    else if ($(renderedSelection).is("hr")) {
        // horizontal rules
        return;
    }
    else if ($(renderedSelection).is(".imageblock")) {
        var src = $(renderedSelection).find("img").attr("src");
        scrollTo60($("img[src='" + src + "']").offset().top);
        return;
    }

    if ($(renderedSelection).is(".admonitionblock")) {
        scrollToElement(".admonitionblock", $(renderedSelection).find(".content").text());
    }
    else if ($(renderedSelection).is(".exampleblock")) {
        scrollToUniqueElement(".exampleblock", $(renderedSelection).text(), /^Example \d+\./, "Example 1.");
    }
    else if ($(renderedSelection).is(".listingblock")) {
        scrollToElement(".listingblock", $(renderedSelection).text());
    }
    else if ($(renderedSelection).is(".literalblock")) {
        scrollToElement(".literalblock", $(renderedSelection).text());
    }
    else if ($(renderedSelection).is(".openblock")) {
        scrollToElement(".openblock", $(renderedSelection).text());
    }
    else if ($(renderedSelection).is(".sidebarblock")) {
        scrollToElement(".sidebarblock", $(renderedSelection).text());
    }
    else if ($(renderedSelection).is(".quoteblock")) {
        scrollToElement(".quoteblock > blockquote", $(renderedSelection).find(".paragraph").text());
    }
    else if ($(renderedSelection).is(".paragraph")) {
        scrollToElement(".paragraph, .quoteblock > blockquote", $(renderedSelection).text());
    }
    else if ($(renderedSelection).is(".ulist")) {
        scrollToElement(".ulist:not(.checklist) > ul > li", $(renderedSelection).text());
    }
    else if ($(renderedSelection).is(".olist")) {
        scrollToElement(".olist > ol > li", $(renderedSelection).text());
    }
    else if ($(renderedSelection).is(".colist")) {
        scrollToUniqueElement(".colist > table > tbody > tr", $(renderedSelection).text(), /^\d+ */, "");
    }
    else if ($(renderedSelection).is(".tableblock")) {
        scrollToUniqueElement("table.tableblock, table.frame-all", $(renderedSelection).text(), /^Table \d+\./, "Table 1.");
    }
    else {
        var header = false;

        $("*:header").each(function () {
            if ($(this).text().trim() == $(renderedSelection).text().trim()) {
                header = true;
                scrollTo60($(this).offset().top);
                return false;
            }
        });

        if (!header) {
            var search = $("*:contains(" + $(renderedSelection).text().trim() + "):last");
            scrollTo60(search.offset().top);
        }
    }
}

function scrollToElement(elements, content) {
    $(elements).each(function () {
        if (simplify($(this).text()) == simplify(content)) {
            scrollTo60($(this).offset().top);
            return false;
        }
    });
}

function scrollToUniqueElement(elements, content, pattern, expected) {
    $(elements).each(function () {
        var element = $(this).text().trim().replace(pattern, expected);
        if (simplify(element) == simplify(content)) {
            scrollTo60($(this).offset().top);
            return false;
        }
    });
}

function simplify(text) {
    return text.replace(/( |\n|\r|\t|\")/g, "");
}
<commit_msg>include also verseblocks<commit_after>var defaultLanguage = undefined;
function getDefaultLanguage() {
    if (!defaultLanguage && app)
        defaultLanguage = app.getConfig().getDefaultLanguage();
    return defaultLanguage;
}

function convertBasicHtml(content) {

    var options = Opal.hash2(['backend', 'safe', 'attributes'], {
        backend: 'html5',
        safe: 'safeMode',
        attributes: 'showtitle icons=font@ source-highlighter=highlight.js platform=opal platform-opal env=browser env-browser '
    });

    var doc = Opal.Asciidoctor.$load(content,options);

    doc.attributes.keys["lang"] = doc.attributes.keys["lang"] || getDefaultLanguage();

    return doc.$convert();
}

function convertHtmlBook(content) {

    var options = Opal.hash2(['backend', 'safe', 'attributes', "header_footer"], {
        backend: 'html5',
        safe: 'safeMode',
        attributes: 'showtitle icons=font@ source-highlighter=highlight.js platform=opal platform-opal env=browser env-browser '
        , 'header_footer': true
    });

    var doc = Opal.Asciidoctor.$load(content,options);

    doc.attributes.keys["lang"] = doc.attributes.keys["lang"] || getDefaultLanguage();

    return doc.$render();
}

function convertHtmlArticle(content) {

    var options = Opal.hash2(['attributes', 'header_footer'],
        {
            'attributes': ['backend=html5', 'doctype=article'],
            'header_footer': true
        });

    var doc = Opal.Asciidoctor.$load(content,options);

    doc.attributes.keys["lang"] = doc.attributes.keys["lang"] || getDefaultLanguage();

    return doc.$render();
}

function convertDocbook(content, includeHeader) {

    var options = Opal.hash2(['attributes', 'header_footer'],
        {
            'attributes': ['backend=docbook5', 'doctype=book'],
            'header_footer': includeHeader
        });

    var doc = Opal.Asciidoctor.$load(content, options);

    doc.attributes.keys["lang"] = doc.attributes.keys["lang"] || getDefaultLanguage();

    return doc.$render();
}

function convertDocbookArticle(content) {

    var options = Opal.hash2(["lang", 'attributes', 'header_footer'],
        {
            'attributes': ['backend=docbook5', 'doctype=article'],
            'header_footer': true
        });

    var doc = Opal.Asciidoctor.$load(content,options);

    doc.attributes.keys["lang"] = doc.attributes.keys["lang"] || getDefaultLanguage();

    return doc.$render();
}

function scrollTo60(position) {
    $(window).scrollTop(position - 60);
}

function runScroller(content) {

    var renderedSelection = Opal.Asciidoctor.$render(content);

    if (renderedSelection.trim() == "") {
        return;
    }
    else if ($(renderedSelection).css("page-break-after")) {
        //page break element
        return;
    }
    else if ($(renderedSelection).is("hr")) {
        // horizontal rules
        return;
    }
    else if ($(renderedSelection).is(".imageblock")) {
        var src = $(renderedSelection).find("img").attr("src");
        scrollTo60($("img[src='" + src + "']").offset().top);
        return;
    }

    if ($(renderedSelection).is(".admonitionblock")) {
        scrollToElement(".admonitionblock", $(renderedSelection).find(".content").text());
    }
    else if ($(renderedSelection).is(".exampleblock")) {
        scrollToUniqueElement(".exampleblock", $(renderedSelection).text(), /^Example \d+\./, "Example 1.");
    }
    else if ($(renderedSelection).is(".listingblock")) {
        scrollToElement(".listingblock", $(renderedSelection).text());
    }
    else if ($(renderedSelection).is(".literalblock")) {
        scrollToElement(".literalblock", $(renderedSelection).text());
    }
    else if ($(renderedSelection).is(".openblock")) {
        scrollToElement(".openblock", $(renderedSelection).text());
    }
    else if ($(renderedSelection).is(".sidebarblock")) {
        scrollToElement(".sidebarblock", $(renderedSelection).text());
    }
    else if ($(renderedSelection).is(".quoteblock")) {
        scrollToElement(".quoteblock > blockquote, .verseblock > .content", $(renderedSelection).find(".paragraph").text());
    }
    else if ($(renderedSelection).is(".paragraph")) {
        scrollToElement(".paragraph, .quoteblock > blockquote", $(renderedSelection).text());
    }
    else if ($(renderedSelection).is(".ulist")) {
        scrollToElement(".ulist:not(.checklist) > ul > li", $(renderedSelection).text());
    }
    else if ($(renderedSelection).is(".olist")) {
        scrollToElement(".olist > ol > li", $(renderedSelection).text());
    }
    else if ($(renderedSelection).is(".colist")) {
        scrollToUniqueElement(".colist > table > tbody > tr", $(renderedSelection).text(), /^\d+ */, "");
    }
    else if ($(renderedSelection).is(".tableblock")) {
        scrollToUniqueElement("table.tableblock, table.frame-all", $(renderedSelection).text(), /^Table \d+\./, "Table 1.");
    }
    else {
        var header = false;

        $("*:header").each(function () {
            if ($(this).text().trim() == $(renderedSelection).text().trim()) {
                header = true;
                scrollTo60($(this).offset().top);
                return false;
            }
        });

        if (!header) {
            var search = $("*:contains(" + $(renderedSelection).text().trim() + "):last");
            scrollTo60(search.offset().top);
        }
    }
}

function scrollToElement(elements, content) {
    $(elements).each(function () {
        if (simplify($(this).text()) == simplify(content)) {
            scrollTo60($(this).offset().top);
            return false;
        }
    });
}

function scrollToUniqueElement(elements, content, pattern, expected) {
    $(elements).each(function () {
        var element = $(this).text().trim().replace(pattern, expected);
        if (simplify(element) == simplify(content)) {
            scrollTo60($(this).offset().top);
            return false;
        }
    });
}

function simplify(text) {
    return text.replace(/( |\n|\r|\t|\")/g, "");
}
<|endoftext|>


<commit_before>var canvas;

function Point(x, y) {
  this.x = x;
  this.y = y;
}

function Drawing() {
  this.points = [];
}

Drawing.prototype.addPoint = function(point) {
  this.points.push(point);
}

Drawing.prototype.lastPoint = function(point) {
  return this.points[this.points.length - 1]
}

function init() {
  canvas = new Canvas();
}

function Canvas() {
  this.canvas = document.getElementById("canvas");
  this.ctx = this.canvas.getContext("2d");
  this.w = this.canvas.width;
  this.h = this.canvas.height;

  this.p = new Point(0, 0);

  this.isMouseDown = false;

  this.drawings = [];
  this.currentDrawing;

  var me = this;

  this.canvas.addEventListener("mousedown", function(e) {
    setMouseDown(e, me);
  }, false);

  this.canvas.addEventListener("mousemove", function(e) {
    onMouseMove(e, me);
  }, false);

  this.canvas.addEventListener("mouseup", function(e) {
    setMouseUp(e, me);
  }, false);

  this.canvas.addEventListener("mouseout", function(e) {
    onMouseOutBounds(e, me);
  }, false);
}

function draw(p, p0, obj) {
  var colour = "black";
  var strokeWidth = 2;
  obj.ctx.beginPath();
  obj.ctx.moveTo(p0.x, p0.y);
  obj.ctx.lineTo(p.x, p.y);
  obj.ctx.strokeStyle = colour;
  obj.ctx.lineWidth = strokeWidth;
  obj.ctx.stroke();
  obj.ctx.closePath();
}

function setMouseDown(event, obj) {
  obj.currentDrawing = new Drawing();
  obj.currentDrawing.addPoint(event.clientX - obj.canvas.offsetLeft, event.clientY - obj.canvas.offsetTop);
  obj.p = new Point(event.clientX - obj.canvas.offsetLeft, event.clientY - obj.canvas.offsetTop);

  obj.isMouseDown = true;
  obj.ctx.beginPath();
  obj.ctx.fillStyle = "black";
  obj.ctx.fillRect(obj.p.x, obj.p.y, 2, 2);
  obj.ctx.closePath();
}

function setMouseUp(event, obj) {
  if (obj.isMouseDown) {
    obj.isMouseDown = false;
    obj.drawings.push(obj.currentDrawing);
  }
}

function onMouseOutBounds(event, obj) {
  setMouseUp(event, obj);
}

function onMouseMove(event, obj) {
  if (obj.isMouseDown) {
    obj.p = new Point(event.clientX - obj.canvas.offsetLeft, event.clientY - obj.canvas.offsetTop);
    var prevPoint = obj.currentDrawing.lastPoint();
    obj.currentDrawing.addPoint(obj.p);
    draw(obj.p, prevPoint, obj);
  }
}
<commit_msg>drawing from series of points<commit_after>var canvas;

var drawthis = JSON.parse('{"points":[{"x":140,"y":116},{"x":148,"y":116},{"x":164,"y":116},{"x":220,"y":140},{"x":296,"y":168},{"x":400,"y":192},{"x":484,"y":220},{"x":572,"y":236},{"x":600,"y":252},{"x":624,"y":256},{"x":640,"y":260},{"x":660,"y":268},{"x":684,"y":280},{"x":724,"y":300},{"x":756,"y":320},{"x":788,"y":336},{"x":808,"y":352},{"x":824,"y":360},{"x":832,"y":364},{"x":836,"y":368},{"x":840,"y":376},{"x":840,"y":380}]}');

function Point(x, y) {
  this.x = x;
  this.y = y;
}

function Drawing() {
  this.points = [];
}

Drawing.prototype.addPoint = function(point) {
  this.points.push(point);
}

Drawing.prototype.lastPoint = function(point) {
  return this.points[this.points.length - 1]
}

function init() {
  canvas = new Canvas();
  drawFromPoints(drawthis, canvas);
}

function Canvas() {
  this.canvas = document.getElementById("canvas");
  this.ctx = this.canvas.getContext("2d");
  this.w = this.canvas.width;
  this.h = this.canvas.height;

  this.p = new Point(0, 0);

  this.isMouseDown = false;

  this.drawings = [];
  this.currentDrawing;

  var me = this;

  this.canvas.addEventListener("mousedown", function(e) {
    setMouseDown(e, me);
  }, false);

  this.canvas.addEventListener("mousemove", function(e) {
    onMouseMove(e, me);
  }, false);

  this.canvas.addEventListener("mouseup", function(e) {
    setMouseUp(e, me);
  }, false);

  this.canvas.addEventListener("mouseout", function(e) {
    onMouseOutBounds(e, me);
  }, false);
}

function draw(p, p0, obj) {
  var colour = "black";
  var strokeWidth = 2;
  obj.ctx.beginPath();
  obj.ctx.moveTo(p0.x, p0.y);
  obj.ctx.lineTo(p.x, p.y);
  obj.ctx.strokeStyle = colour;
  obj.ctx.lineWidth = strokeWidth;
  obj.ctx.stroke();
  obj.ctx.closePath();
}

function setMouseDown(event, obj) {
  obj.currentDrawing = new Drawing();
  obj.p = new Point(event.clientX - obj.canvas.offsetLeft, event.clientY - obj.canvas.offsetTop);
  obj.currentDrawing.addPoint(obj.p);

  obj.isMouseDown = true;
  obj.ctx.beginPath();
  obj.ctx.fillStyle = "black";
  obj.ctx.fillRect(obj.p.x, obj.p.y, 2, 2);
  obj.ctx.closePath();
}

function setMouseUp(event, obj) {
  if (obj.isMouseDown) {
    obj.isMouseDown = false;
    obj.drawings.push(obj.currentDrawing);
    console.log(JSON.stringify(obj.currentDrawing));
  }
}

function onMouseOutBounds(event, obj) {
  setMouseUp(event, obj);
}

function onMouseMove(event, obj) {
  if (obj.isMouseDown) {
    obj.p = new Point(event.clientX - obj.canvas.offsetLeft, event.clientY - obj.canvas.offsetTop);
    var prevPoint = obj.currentDrawing.lastPoint();
    obj.currentDrawing.addPoint(obj.p);
    draw(obj.p, prevPoint, obj);
  }
}

function drawFromPoints(drawing, obj) {
  for (var i = 0; i < drawing.points.length - 2; i++) {
    draw(drawing.points[i], drawing.points[i + 1], obj);
  }
}
<|endoftext|>