<commit_before>import { GraphQLString, GraphQLNonNull, GraphQLID } from 'graphql';
import {Meteor} from 'meteor/meteor';
// types
import CommentType, {CommentInputType} from '/imports/graphql/types/models/comment';
import { RemoveType } from '/imports/graphql/types/index';
import { RevisionInputType } from '/imports/graphql/types/models/revision';

// models
import Comments from '/imports/models/comments';
import Books from '/imports/models/books';

// errors
import { AuthenticationError } from '/imports/errors';

//bll
import AnnotationService from '../bll/annotations';

function hasAnnotationPermission(token, chapterUrl) {
	const user = Meteor.users.findOne({
		'services.resume.loginTokens.hashedToken': Accounts._hashLoginToken(token),
	});

	const book = Books.findOne({ 'chapters.url': chapterUrl });
	const authorizedBooks = user.canAnnotateBooks || [];
	return user && (book || ~authorizedBooks.indexOf(book._id));
}

function hasAnnotationRevisionPermission(token, annotationId) {
	const user = Meteor.users.findOne({
		'services.resume.loginTokens.hashedToken': Accounts._hashLoginToken(token),
	});
	const comment = Comments.findOne({_id: annotationId, users: user._id});
	return !!comment;
}

const annotationMutationFields = {
	annotationCreate: {
		type: CommentType,
		description: 'Create new annotation',
		args: {
			comment: {
				type: CommentInputType
			}
		},
		async resolve(parent, { comment }, {token}) {
			const annotationService = new AnnotationService({token});
			return await annotationService.createAnnotation(comment);
		}
	},
	annotationDelete: {
		type: RemoveType,
		description: 'Remove annotation',
		args: {
			annotationId: {
				type: new GraphQLNonNull(GraphQLID)
			}
		},
		async resolve(parent, { annotationId }, {token}) {

			const annotationService = new AnnotationService({token});
			return await annotationService.deleteAnnotation(annotationId);
		}
	},
	annotationAddRevision: {
		type: RemoveType,
		description: 'Remove annotation',
		args: {
			annotationId: {
				type: new GraphQLNonNull(GraphQLID)
			},
			revision: {
				type: new GraphQLNonNull(RevisionInputType)
			}
		},
		async resolve(parent, {annotationId, revision}, {token}) {
			const annotationService = new AnnotationService({token});
			return await annotationService.addRevision(annotationId, revision);
		}
	}
};

export default annotationMutationFields;
<commit_msg>remove unused functions<commit_after>import { GraphQLString, GraphQLNonNull, GraphQLID } from 'graphql';
import {Meteor} from 'meteor/meteor';
// types
import CommentType, {CommentInputType} from '/imports/graphql/types/models/comment';
import { RemoveType } from '/imports/graphql/types/index';
import { RevisionInputType } from '/imports/graphql/types/models/revision';

// models
import Comments from '/imports/models/comments';
import Books from '/imports/models/books';

// errors
import { AuthenticationError } from '/imports/errors';

//bll
import AnnotationService from '../bll/annotations';

const annotationMutationFields = {
	annotationCreate: {
		type: CommentType,
		description: 'Create new annotation',
		args: {
			comment: {
				type: CommentInputType
			}
		},
		async resolve(parent, { comment }, {token}) {
			const annotationService = new AnnotationService({token});
			return await annotationService.createAnnotation(comment);
		}
	},
	annotationDelete: {
		type: RemoveType,
		description: 'Remove annotation',
		args: {
			annotationId: {
				type: new GraphQLNonNull(GraphQLID)
			}
		},
		async resolve(parent, { annotationId }, {token}) {

			const annotationService = new AnnotationService({token});
			return await annotationService.deleteAnnotation(annotationId);
		}
	},
	annotationAddRevision: {
		type: RemoveType,
		description: 'Remove annotation',
		args: {
			annotationId: {
				type: new GraphQLNonNull(GraphQLID)
			},
			revision: {
				type: new GraphQLNonNull(RevisionInputType)
			}
		},
		async resolve(parent, {annotationId, revision}, {token}) {
			const annotationService = new AnnotationService({token});
			return await annotationService.addRevision(annotationId, revision);
		}
	}
};

export default annotationMutationFields;
<|endoftext|>


<commit_before>import { Component, PropTypes } from "react";

import RelatedContent from "../../blocks/content/RelatedContent";
import SingleVideoPlayer from "../../components/players/video/Player";
import backgrounds from "../../util/backgrounds";
import react from "../../util/react";

const ratio = window.isTablet ? "2:1" : "1:1";
const StoryImage = ({ story }) => (

);

export default class StoriesContent extends Component {

  static propTypes = {
    story: PropTypes.object.isRequired,
  }

  render() {
    const story = this.props.story;

    return (

          {(() => {
            if (story.content.ooyalaId.length === 0) return <StoryImage story={story} />;
            return <SingleVideoPlayer ooyalaId={story.content.ooyalaId} />;
          })()}

{story.title}

{story.subtitle}

        <RelatedContent excludedIds={[story.id]} tags={story.content.tags} />

    );
  }

}
<commit_msg>lint pages stories content<commit_after>import { PropTypes } from "react";

import RelatedContent from "../../blocks/content/RelatedContent";
import SingleVideoPlayer from "../../components/players/video/Player";
import backgrounds from "../../util/backgrounds";
import react from "../../util/react";

const ratio = window.isTablet ? "2:1" : "1:1";
const StoryImage = ({ story }) => (

);

StoryImage.propTypes = {
  story: PropTypes.object.isRequired,
};

const StoriesContent = () => {
  const story = this.props.story;

  return (

        {(() => {
          if (story.content.ooyalaId.length === 0) return <StoryImage story={story} />;
          return <SingleVideoPlayer ooyalaId={story.content.ooyalaId} />;
        })()}

{story.title}

{story.subtitle}

      <RelatedContent excludedIds={[story.id]} tags={story.content.tags} />

  );
};

StoriesContent.propTypes = {
  story: PropTypes.object.isRequired,
};

export default StoriesContent;
<|endoftext|>


<commit_before>$(function() {
    var $body = $("body");
    var $container = $("body > .container");
    var pageCache = {};
    var postCollect = [];

    // bindWindowScrollHandler();
    bindChangePage();
    initPage();
    bindChangeApp();

    function bindChangeApp() {
        $(".navbar.navbar-fixed-top").on("click", "#bs-example-navbar-collapse-1 .apps a", changeAppHandler);

        function changeAppHandler() {
            var $this = $(this);
            var url = $this.attr("href");

            // load
            $.get(url, function(resp) {})
                .done(function(resp) {
                    $container.html(resp);
                    $(".nav.apps li").each(function(i, ele) {
                        $(ele).removeClass('active');
                    });
                    console.log($this.parent());
                    $this.parent().addClass('active');
                    history.pushState({}, '', url);
                    initPage();
                })
            return false;
        }
    }

    function prefetchPage() {
        // load pages
        $(".archives.page-nav .page").each(function(idx, ele) {
            var $ele = $(ele);
            var page = $ele.html();
            var url = $ele.attr("href");

            if (!pageCache.hasOwnProperty(page)) {
                $.get(url, function(data) {})
                    .done(function(data) {
                        pageCache[page] = data;
                    });
            }
        });
    }

    function initPage() {
        prefetchPage();
        bindKeyboardMove();
        $.globalEval($(".comment-count-js").html());
    }

    function bindChangePage() {
        function updateContainerByPage(page) {
            var url = '/api/posts/get-post-by-page/';
            var data = {
                page: page
            };
            $.get(url, data, function(data) {})
                .done(function(data) {
                    $container.html(data);
                    loadersCss.initLoaderCss();
                    history.pushState({}, '', '/archives/?page=' + page);
                    initPage();
                });
        }

        function updateContainerByCache(page) {
            $container.html(pageCache[page]);
            loadersCss.initLoaderCss();
            history.pushState({}, '', '/archives/?page=' + page);
            initPage();
        }

        $(document).on("click", "li a.page", function() {
            var page = $(this).html();

            if (page in pageCache) {
                updateContainerByCache(page);
            } else {
                updateContainerByPage(page);
            }
            window.scrollTo(0, document.body.scrollHeight);
            return false;
        });

        $(document).on("click", "li a.page-previous, li a.page-next", function() {
            var page = $(this).data("page");

            if (page in pageCache) {
                updateContainerByCache(page);
            } else {
                updateContainerByPage(page);
            }
            window.scrollTo(0, document.body.scrollHeight);
            return false;
        });
    }

    function bindWindowScrollHandler() {
        $(window).on("scroll", function() {
            if ($(".post").length > 1) {
                if ($(window).scrollTop() + $(window).height() > $(document).height() - 400) {
                    $(window).unbind("scroll");
                    loadMorePosts();
                }
            }
        });
    }

    function getLastPostName() {
        var lastPost = $("#archives").children().last();
        var href = lastPost.children(".post-title").children().prop("href");
        var postName = href.split("/").pop();
        // postName = postName.slice(0, postName.indexOf("#"));

        return postName;
    }

    function loadMorePosts() {
        var url = "/api/posts/get-lastest-posts-by-name/";
        var data = {
            "since_name": getLastPostName()
        };

        $.getJSON(url, data, function(data) {
                // console.log(data['status']);
            })
            .done(function(data) {
                $("#archives").append(data['data']);
            })
            .always(function() {
                $(window).bind('scroll', windowScrollHandler);
            });
    }

    // 键盘方向键的交互
    function bindKeyboardMove() {
        var pathname = window.location.pathname;
        if (pathname == "/" || pathname.indexOf("/archives/") == 0) {
            // 只在文章页绑定这一事件
            updatePostCollect();
            bindKeyboardHandler();

            function updatePostCollect() {
                postCollect = [];
                $("#archives > div").each(function(idx, ele) {
                    postCollect.push($(ele).position()["top"]);
                });
            }
        }
    }

    function getCurrentPostIdx() {
        var current = getCurrentPostion();

        for (var i = postCollect.length; i >= 0; i--) {
            if (postCollect[i] <= current || i == 0) {
                return i;
            }
        }
    }

    function getCurrentPostion() {
        return $body.scrollTop();
    }

    function bindKeyboardHandler() {
        $body.off("keydown", keybordHandler);
        $body.on("keydown", keybordHandler);

    }

    function keybordHandler(e) {
        if (e.keyCode == 38) {
            postMoveHandler("up");
            return false;
        } else if (e.keyCode == 40) {
            postMoveHandler("down");
            return false;
        }
    }

    function postMoveHandler(direction) {
        // console.log("postMoveHandler for direction " + direction);

        var currentPosition = getCurrentPostion();
        var currentPostIdx = getCurrentPostIdx();
        var curentPostPostion = postCollect[currentPostIdx];

        if (direction == "up") {
            if (Math.abs(curentPostPostion - currentPosition) > 10) {
                $body.animate({
                    scrollTop: curentPostPostion
                });
                return false;
            }

            if (currentPostIdx == 0) {
                return false;
            }

            // move to forward post
            $body.animate({
                scrollTop: postCollect[currentPostIdx - 1]
            }, 200);

        } else if (direction == "down") {
            if (currentPostIdx == postCollect.length - 1) {
                return false;
            }
            // move to next post
            $body.animate({
                scrollTop: postCollect[currentPostIdx + 1]
            }, 200);
        }
    }

});
<commit_msg>#49   - 加上了 fade   - 加上了 缓存<commit_after>$(function() {
    var $body = $("body");
    var $container = $("body > .container");
    var pageCache = {};
    var appCache = {};
    var postCollect = [];

    // bindWindowScrollHandler();
    bindChangePage();
    initPage();
    bindChangeApp();

    function bindChangeApp() {
        $(".navbar.navbar-fixed-top").off("click", "#bs-example-navbar-collapse-1 .apps a", changeAppHandler);
        $(".navbar.navbar-fixed-top").on("click", "#bs-example-navbar-collapse-1 .apps a", changeAppHandler);

        function changeAppHandler() {
            var $this = $(this);
            var url = $this.attr("href");

            globalFadeLayer.fadeIn();

            if (url in appCache) {
                // console.log("app cache");
                changeApp($this, appCache[url]);
                return false;
            }

            // load
            $.get(url, function(resp) {})
                .done(function(resp) {
                    changeApp($this, resp);
                    appCache[url] = resp;
                })
            return false;

            function changeApp($app, container) {
                $container.html(container);
                $(".nav.apps li").each(function(i, ele) {
                    $(ele).removeClass('active');
                });
                // console.log($app.parent());
                $app.parent().addClass('active');
                history.pushState({}, '', url);
                globalFadeLayer.fadeOut();
                initPage();
            }
        }
    }

    function prefetchPage() {
        // load pages
        $(".archives.page-nav .page").each(function(idx, ele) {
            var $ele = $(ele);
            var page = $ele.html();
            var url = $ele.attr("href");

            if (!pageCache.hasOwnProperty(page)) {
                $.get(url, function(data) {})
                    .done(function(data) {
                        pageCache[page] = data;
                    });
            }
        });
    }

    function initPage() {
        prefetchPage();
        bindKeyboardMove();
        $.globalEval($(".comment-count-js").html());
    }

    function bindChangePage() {
        function updateContainerByPage(page) {
            var url = '/api/posts/get-post-by-page/';
            var data = {
                page: page
            };
            $.get(url, data, function(data) {})
                .done(function(data) {
                    $container.html(data);
                    loadersCss.initLoaderCss();
                    history.pushState({}, '', '/archives/?page=' + page);
                    initPage();
                });
        }

        function updateContainerByCache(page) {
            $container.html(pageCache[page]);
            loadersCss.initLoaderCss();
            history.pushState({}, '', '/archives/?page=' + page);
            initPage();
        }

        $(document).on("click", "li a.page", function() {
            var page = $(this).html();

            if (page in pageCache) {
                updateContainerByCache(page);
            } else {
                updateContainerByPage(page);
            }
            window.scrollTo(0, document.body.scrollHeight);
            return false;
        });

        $(document).on("click", "li a.page-previous, li a.page-next", function() {
            var page = $(this).data("page");

            if (page in pageCache) {
                updateContainerByCache(page);
            } else {
                updateContainerByPage(page);
            }
            window.scrollTo(0, document.body.scrollHeight);
            return false;
        });
    }

    function bindWindowScrollHandler() {
        $(window).on("scroll", function() {
            if ($(".post").length > 1) {
                if ($(window).scrollTop() + $(window).height() > $(document).height() - 400) {
                    $(window).unbind("scroll");
                    loadMorePosts();
                }
            }
        });
    }

    function getLastPostName() {
        var lastPost = $("#archives").children().last();
        var href = lastPost.children(".post-title").children().prop("href");
        var postName = href.split("/").pop();
        // postName = postName.slice(0, postName.indexOf("#"));

        return postName;
    }

    function loadMorePosts() {
        var url = "/api/posts/get-lastest-posts-by-name/";
        var data = {
            "since_name": getLastPostName()
        };

        $.getJSON(url, data, function(data) {
                // console.log(data['status']);
            })
            .done(function(data) {
                $("#archives").append(data['data']);
            })
            .always(function() {
                $(window).bind('scroll', windowScrollHandler);
            });
    }

    // 键盘方向键的交互
    function bindKeyboardMove() {
        var pathname = window.location.pathname;
        if (pathname == "/" || pathname.indexOf("/archives/") == 0) {
            // 只在文章页绑定这一事件
            updatePostCollect();
            bindKeyboardHandler();

            function updatePostCollect() {
                postCollect = [];
                $("#archives > div").each(function(idx, ele) {
                    postCollect.push($(ele).position()["top"]);
                });
            }
        }
    }

    function getCurrentPostIdx() {
        var current = getCurrentPostion();

        for (var i = postCollect.length; i >= 0; i--) {
            if (postCollect[i] <= current || i == 0) {
                return i;
            }
        }
    }

    function getCurrentPostion() {
        return $body.scrollTop();
    }

    function bindKeyboardHandler() {
        $body.off("keydown", keybordHandler);
        $body.on("keydown", keybordHandler);

    }

    function keybordHandler(e) {
        if (e.keyCode == 38) {
            postMoveHandler("up");
            return false;
        } else if (e.keyCode == 40) {
            postMoveHandler("down");
            return false;
        }
    }

    function postMoveHandler(direction) {
        // console.log("postMoveHandler for direction " + direction);

        var currentPosition = getCurrentPostion();
        var currentPostIdx = getCurrentPostIdx();
        var curentPostPostion = postCollect[currentPostIdx];

        if (direction == "up") {
            if (Math.abs(curentPostPostion - currentPosition) > 10) {
                $body.animate({
                    scrollTop: curentPostPostion
                });
                return false;
            }

            if (currentPostIdx == 0) {
                return false;
            }

            // move to forward post
            $body.animate({
                scrollTop: postCollect[currentPostIdx - 1]
            }, 200);

        } else if (direction == "down") {
            if (currentPostIdx == postCollect.length - 1) {
                return false;
            }
            // move to next post
            $body.animate({
                scrollTop: postCollect[currentPostIdx + 1]
            }, 200);
        }
    }

});
<|endoftext|>


<commit_before>import GitHub from './github';
import GITHUB_MESSAGE_TYPES from './github-message-types';
import { log, saveState, emptyState }from './log';

export default class MessagesHandler {
    constructor (bot, state, collaborator) {
        this.bot    = bot;
        this.github = new GitHub(bot.name, bot.token);

        if (collaborator)
            this.collaboratorGithub = new GitHub(collaborator.name, collaborator.token);

        this.state = state || emptyState();

        this.SYNCHRONIZE_TIMEOUT = 5 * 60 * 1000;
    }

    _saveState () {
        var prs   = Object.keys(this.state.openedPullRequests);
        var state = {
            openedPullRequests: {}
        };

        for (var i = 0; i < prs.length; i++) {
            var pr    = this.state.openedPullRequests[prs[i]];
            var clone = {};

            for (var field in pr) {
                if (pr.hasOwnProperty(field) && field !== 'syncTimeout' && field !== 'waitForTestsTimeout')
                    clone[field] = pr[field];
            }

            state.openedPullRequests[prs[i]] = clone;
        }

        saveState(state);
    }

    static _getPRName (repo, id) {
        return `${repo}/${id}`;
    }

    static _getTestBranchName (issueId) {
        return 'rp-' + issueId;
    }

    static _getTemporaryBranchName (branchName) {
        return 'build-bot-temp-' + branchName;
    }

    static _getMark (state) {
        return state === 'success' ? ':white_check_mark:' : ':x:';
    }

    static _getCILink (status) {
        return `[${MessagesHandler._getMark(status.state) + status.context}](${status.target_url})`;
    }

    static _getDetails (statuses) {
        if (statuses.length === 1)
            return `[details](${statuses[0].target_url})`;

        return 'details:\n\n' +
            statuses
                .map(status => `*  ${MessagesHandler._getCILink(status)}`)
                .join('\n\n');
    }

    _getPRBySha (repo, sha) {
        var prNumbers = Object.keys(this.state.openedPullRequests);

        var prId = prNumbers.filter((id) => {
            var pr = this.state.openedPullRequests[id];

            return (pr.sha === sha || pr.travisConfSha === sha) && pr.repo === repo;
        })[0];

        return prId ? this.state.openedPullRequests[prId] : null;
    }

    _createBranch (repo, owner, prSha, branchName, travisConf, prNumber) {
        if (!travisConf)
            return this.github.createBranch(repo, prSha, branchName);

        var temporaryBranchName = MessagesHandler._getTemporaryBranchName(branchName);

        this.github.createBranch(repo, prSha, temporaryBranchName)
            .then(() => this.github.getCommitMessage(repo, owner, prSha))
            .then(commitMessage => {
                return this.github.replaceFile(repo, '.travis.yml', `.travis-${travisConf}.yml`,
                    temporaryBranchName, commitMessage);
            })
            .then(commitSha => {
                this.github.createBranch(repo, commitSha, branchName);

                var currentPr = this.state.openedPullRequests[MessagesHandler._getPRName(repo, prNumber)];

                currentPr.travisConfSha = commitSha;

                this._saveState();
            });
    }

    _syncBranchWithCommit (repo, owner, branchName, prSha, travisConf, prNumber) {
        if (!travisConf)
            return this.github.syncBranchWithCommit(repo, branchName, prSha);

        var temporaryBranchName = MessagesHandler._getTemporaryBranchName(branchName);

        this.github.syncBranchWithCommit(repo, temporaryBranchName, prSha)
            .then(() => this.github.getCommitMessage(repo, owner, prSha))
            .then(commitMessage => {
                return this.github.replaceFile(repo, '.travis.yml', `.travis-${travisConf}.yml`,
                    temporaryBranchName, commitMessage);
            })
            .then(commitSha => {
                this.github.syncBranchWithCommit(repo, branchName, commitSha);

                var currentPr = this.state.openedPullRequests[MessagesHandler._getPRName(repo, prNumber)];

                currentPr.travisConfSha = commitSha;

                this._saveState();
            });
    }

    _getTravisConf (prTitle) {
        if (prTitle.indexOf('[docs]') > -1)
            return 'docs';

        return null;
    }

    _onPROpened (repo, prNumber, prSha, branchName, owner, title) {
        var existedPr = this.state.openedPullRequests[MessagesHandler._getPRName(repo, prNumber)];
        var pr        = existedPr || {};

        pr.number        = prNumber;
        pr.sha           = prSha;
        pr.repo          = repo;
        pr.owner         = owner;
        pr.branchName    = branchName;
        pr.travisConfSha = null;

        this.state.openedPullRequests[MessagesHandler._getPRName(repo, prNumber)] = pr;

        this._saveState();

        if (existedPr)
            this._syncBranchWithCommit(repo, owner, branchName, prSha, this._getTravisConf(title), prNumber);
        else
            this._createBranch(repo, owner, prSha, branchName, this._getTravisConf(title), prNumber);
    }

    _onPRClosed (repo, prNumber, branchName, title) {
        var prName = MessagesHandler._getPRName(repo, prNumber);
        var pr     = this.state.openedPullRequests[prName];

        clearTimeout(pr.waitForTestsTimeout);
        clearTimeout(pr.syncTimeout);

        delete this.state.openedPullRequests[prName];

        this._saveState();

        var travisConf = this._getTravisConf(title);

        this.github.deleteBranch(repo, branchName);

        if (travisConf)
            this.github.deleteBranch(repo, MessagesHandler._getTemporaryBranchName(branchName));
    }

    _waitForTestsStart (pr, repo, owner, sha, statuses) {
        var handler = this;

        if (pr.waitForTestsTimeout) {
            clearTimeout(pr.waitForTestsTimeout);
            pr.waitForTestsTimeout = null;
        }

        pr.timeToTests = Math.round(this.SYNCHRONIZE_TIMEOUT / 60000);

        function setStatus (time) {
            var message = `Tests will start in ${time} minute(s).`;

            statuses.forEach(status => (handler.collaboratorGithub ||
                handler.github).createStatus(repo, owner, sha, 'pending', '', message, status.context));

            if (time) {
                pr.waitForTestsTimeout = setTimeout(() => {
                    pr.waitForTestsTimeout = null;
                    pr.timeToTests--;

                    if (pr.timeToTests)
                        setStatus(pr.timeToTests);
                }, 60 * 1000);
            }
        }

        setStatus(pr.timeToTests);
    }

    async _onPRSynchronized (repo, prNumber, branchName, sha, owner, title) {
        var pr = this.state.openedPullRequests[MessagesHandler._getPRName(repo, prNumber)];

        if (!pr)
            return;

        var statusInfo = await this.github.getCombinedStatus(repo, this.bot.name, branchName);

        pr.sha = sha;

        if (pr.syncTimeout) {
            clearTimeout(pr.syncTimeout);
            delete pr.syncTimeout;
        }

        pr.syncTimeout = setTimeout(() => {
            delete pr.syncTimeout;
            this._saveState();

            this._syncBranchWithCommit(repo, owner, branchName, sha, this._getTravisConf(title), prNumber);
        }, this.SYNCHRONIZE_TIMEOUT);

        this._saveState();

        this._waitForTestsStart(pr, repo, owner, sha, statusInfo.statuses);
    }

    _onPRMessage (body) {
        if (/temp-pr/.test(body.pull_request.base.ref))
            return;

        var owner          = body.repository.owner.login;
        var repo           = body.repository.name;
        var prSha          = body.pull_request.head.sha;
        var prId           = body.pull_request.id;
        var title          = body.pull_request.title;
        var prNumber       = body.number;
        var testBranchName = MessagesHandler._getTestBranchName(prId);

        if (/opened/.test(body.action))
            this._onPROpened(repo, prNumber, prSha, testBranchName, owner, title);

        if (body.action === 'closed')
            this._onPRClosed(repo, prNumber, testBranchName, title);

        if (body.action === 'synchronize')
            this._onPRSynchronized(repo, prNumber, testBranchName, prSha, owner, title);
    }

    async _onStatusMessage (body) {
        log('Status message: ' + JSON.stringify(body, null, 4));

        if (body.repository.owner.login !== this.bot.name)
            return;

        var repo = body.repository.name;

        var pr = this._getPRBySha(repo, body.sha);

        if (!pr)
            return;

        var statusInfo = await this.github.getCombinedStatus(repo, this.bot.name, pr.branchName);

        if (statusInfo.sha !== body.sha)
            return;

        var owner = pr.owner;

        (this.collaboratorGithub || this.github).createStatus(repo, owner, pr.sha, body.state, body.target_url,
            body.description, body.context);

        if (statusInfo.state === 'pending' || statusInfo.statuses.some(status => status.state === 'pending'))
            return;

        var status  = statusInfo.state === 'success' ? 'passed' : 'failed';
        var mark    = MessagesHandler._getMark(statusInfo.state);
        var details = MessagesHandler._getDetails(statusInfo.statuses);

        this.github.createPullRequestComment(repo, pr.number,
            `${mark} Tests for the commit ${pr.sha} have ${status}. See ${details}.`, owner, repo);
    }

    _onIssueCommentMessage (body) {
        if (body.action !== 'created')
            return;

        var owner = body.repository.owner.login;
        var repo  = body.repository.name;
        var pr    = this.state.openedPullRequests[MessagesHandler._getPRName(repo, body.issue.number)];

        if (!pr)
            return;

        var commandHandler = this._getCommandHandler(body.comment.body, body.issue.title);

        if (!commandHandler)
            return;

        this.github.isUserCollaborator(repo, owner, body.comment.user.login)
            .then(isCollaborator => {
                if (isCollaborator)
                    commandHandler(pr);
            });
    }

    _getCommandHandler (message, title) {
        var name = this.bot.name;

        if (message.indexOf(`@${name}`) < 0)
            return null;

        message = message.replace(`@${name}`, '').replace(/\s/g, '');

        var handlers = {
            '\\retest': async pr => {
                var statusInfo = await this.github.getCombinedStatus(pr.repo, name, pr.branchName);

                if (statusInfo.statuses.some(status => status.state === 'pending') || pr.syncTimeout)
                    return;

                this._syncBranchWithCommit(pr.repo, pr.owner, pr.branchName, pr.sha, this._getTravisConf(title), pr.number);
            }
        };

        return handlers[message] || null;
    }

    handle (message) {
        if (message.type === GITHUB_MESSAGE_TYPES.pullRequest)
            this._onPRMessage(message.body);

        if (message.type === GITHUB_MESSAGE_TYPES.status)
            this._onStatusMessage(message.body);

        if (message.type === GITHUB_MESSAGE_TYPES.issueComment)
            this._onIssueCommentMessage(message.body);
    }
}
<commit_msg>Improve result comment formatting<commit_after>import GitHub from './github';
import GITHUB_MESSAGE_TYPES from './github-message-types';
import { log, saveState, emptyState }from './log';

export default class MessagesHandler {
    constructor (bot, state, collaborator) {
        this.bot    = bot;
        this.github = new GitHub(bot.name, bot.token);

        if (collaborator)
            this.collaboratorGithub = new GitHub(collaborator.name, collaborator.token);

        this.state = state || emptyState();

        this.SYNCHRONIZE_TIMEOUT = 5 * 60 * 1000;
    }

    _saveState () {
        var prs   = Object.keys(this.state.openedPullRequests);
        var state = {
            openedPullRequests: {}
        };

        for (var i = 0; i < prs.length; i++) {
            var pr    = this.state.openedPullRequests[prs[i]];
            var clone = {};

            for (var field in pr) {
                if (pr.hasOwnProperty(field) && field !== 'syncTimeout' && field !== 'waitForTestsTimeout')
                    clone[field] = pr[field];
            }

            state.openedPullRequests[prs[i]] = clone;
        }

        saveState(state);
    }

    static _getPRName (repo, id) {
        return `${repo}/${id}`;
    }

    static _getTestBranchName (issueId) {
        return 'rp-' + issueId;
    }

    static _getTemporaryBranchName (branchName) {
        return 'build-bot-temp-' + branchName;
    }

    static _getMark (state) {
        return state === 'success' ? ':white_check_mark:' : ':x:';
    }

    static _getCILink (status) {
        return `[${MessagesHandler._getMark(status.state)} ${status.context}](${status.target_url})`;
    }

    static _getDetails (statuses) {
        if (statuses.length === 1)
            return `[details](${statuses[0].target_url})`;

        return 'details:\n\n' +
            statuses
                .map(status => `* ${MessagesHandler._getCILink(status)}`)
                .join('\n\n');
    }

    _getPRBySha (repo, sha) {
        var prNumbers = Object.keys(this.state.openedPullRequests);

        var prId = prNumbers.filter((id) => {
            var pr = this.state.openedPullRequests[id];

            return (pr.sha === sha || pr.travisConfSha === sha) && pr.repo === repo;
        })[0];

        return prId ? this.state.openedPullRequests[prId] : null;
    }

    _createBranch (repo, owner, prSha, branchName, travisConf, prNumber) {
        if (!travisConf)
            return this.github.createBranch(repo, prSha, branchName);

        var temporaryBranchName = MessagesHandler._getTemporaryBranchName(branchName);

        this.github.createBranch(repo, prSha, temporaryBranchName)
            .then(() => this.github.getCommitMessage(repo, owner, prSha))
            .then(commitMessage => {
                return this.github.replaceFile(repo, '.travis.yml', `.travis-${travisConf}.yml`,
                    temporaryBranchName, commitMessage);
            })
            .then(commitSha => {
                this.github.createBranch(repo, commitSha, branchName);

                var currentPr = this.state.openedPullRequests[MessagesHandler._getPRName(repo, prNumber)];

                currentPr.travisConfSha = commitSha;

                this._saveState();
            });
    }

    _syncBranchWithCommit (repo, owner, branchName, prSha, travisConf, prNumber) {
        if (!travisConf)
            return this.github.syncBranchWithCommit(repo, branchName, prSha);

        var temporaryBranchName = MessagesHandler._getTemporaryBranchName(branchName);

        this.github.syncBranchWithCommit(repo, temporaryBranchName, prSha)
            .then(() => this.github.getCommitMessage(repo, owner, prSha))
            .then(commitMessage => {
                return this.github.replaceFile(repo, '.travis.yml', `.travis-${travisConf}.yml`,
                    temporaryBranchName, commitMessage);
            })
            .then(commitSha => {
                this.github.syncBranchWithCommit(repo, branchName, commitSha);

                var currentPr = this.state.openedPullRequests[MessagesHandler._getPRName(repo, prNumber)];

                currentPr.travisConfSha = commitSha;

                this._saveState();
            });
    }

    _getTravisConf (prTitle) {
        if (prTitle.indexOf('[docs]') > -1)
            return 'docs';

        return null;
    }

    _onPROpened (repo, prNumber, prSha, branchName, owner, title) {
        var existedPr = this.state.openedPullRequests[MessagesHandler._getPRName(repo, prNumber)];
        var pr        = existedPr || {};

        pr.number        = prNumber;
        pr.sha           = prSha;
        pr.repo          = repo;
        pr.owner         = owner;
        pr.branchName    = branchName;
        pr.travisConfSha = null;

        this.state.openedPullRequests[MessagesHandler._getPRName(repo, prNumber)] = pr;

        this._saveState();

        if (existedPr)
            this._syncBranchWithCommit(repo, owner, branchName, prSha, this._getTravisConf(title), prNumber);
        else
            this._createBranch(repo, owner, prSha, branchName, this._getTravisConf(title), prNumber);
    }

    _onPRClosed (repo, prNumber, branchName, title) {
        var prName = MessagesHandler._getPRName(repo, prNumber);
        var pr     = this.state.openedPullRequests[prName];

        clearTimeout(pr.waitForTestsTimeout);
        clearTimeout(pr.syncTimeout);

        delete this.state.openedPullRequests[prName];

        this._saveState();

        var travisConf = this._getTravisConf(title);

        this.github.deleteBranch(repo, branchName);

        if (travisConf)
            this.github.deleteBranch(repo, MessagesHandler._getTemporaryBranchName(branchName));
    }

    _waitForTestsStart (pr, repo, owner, sha, statuses) {
        var handler = this;

        if (pr.waitForTestsTimeout) {
            clearTimeout(pr.waitForTestsTimeout);
            pr.waitForTestsTimeout = null;
        }

        pr.timeToTests = Math.round(this.SYNCHRONIZE_TIMEOUT / 60000);

        function setStatus (time) {
            var message = `Tests will start in ${time} minute(s).`;

            statuses.forEach(status => (handler.collaboratorGithub ||
                handler.github).createStatus(repo, owner, sha, 'pending', '', message, status.context));

            if (time) {
                pr.waitForTestsTimeout = setTimeout(() => {
                    pr.waitForTestsTimeout = null;
                    pr.timeToTests--;

                    if (pr.timeToTests)
                        setStatus(pr.timeToTests);
                }, 60 * 1000);
            }
        }

        setStatus(pr.timeToTests);
    }

    async _onPRSynchronized (repo, prNumber, branchName, sha, owner, title) {
        var pr = this.state.openedPullRequests[MessagesHandler._getPRName(repo, prNumber)];

        if (!pr)
            return;

        var statusInfo = await this.github.getCombinedStatus(repo, this.bot.name, branchName);

        pr.sha = sha;

        if (pr.syncTimeout) {
            clearTimeout(pr.syncTimeout);
            delete pr.syncTimeout;
        }

        pr.syncTimeout = setTimeout(() => {
            delete pr.syncTimeout;
            this._saveState();

            this._syncBranchWithCommit(repo, owner, branchName, sha, this._getTravisConf(title), prNumber);
        }, this.SYNCHRONIZE_TIMEOUT);

        this._saveState();

        this._waitForTestsStart(pr, repo, owner, sha, statusInfo.statuses);
    }

    _onPRMessage (body) {
        if (/temp-pr/.test(body.pull_request.base.ref))
            return;

        var owner          = body.repository.owner.login;
        var repo           = body.repository.name;
        var prSha          = body.pull_request.head.sha;
        var prId           = body.pull_request.id;
        var title          = body.pull_request.title;
        var prNumber       = body.number;
        var testBranchName = MessagesHandler._getTestBranchName(prId);

        if (/opened/.test(body.action))
            this._onPROpened(repo, prNumber, prSha, testBranchName, owner, title);

        if (body.action === 'closed')
            this._onPRClosed(repo, prNumber, testBranchName, title);

        if (body.action === 'synchronize')
            this._onPRSynchronized(repo, prNumber, testBranchName, prSha, owner, title);
    }

    async _onStatusMessage (body) {
        log('Status message: ' + JSON.stringify(body, null, 4));

        if (body.repository.owner.login !== this.bot.name)
            return;

        var repo = body.repository.name;

        var pr = this._getPRBySha(repo, body.sha);

        if (!pr)
            return;

        var statusInfo = await this.github.getCombinedStatus(repo, this.bot.name, pr.branchName);

        if (statusInfo.sha !== body.sha)
            return;

        var owner = pr.owner;

        (this.collaboratorGithub || this.github).createStatus(repo, owner, pr.sha, body.state, body.target_url,
            body.description, body.context);

        if (statusInfo.state === 'pending' || statusInfo.statuses.some(status => status.state === 'pending'))
            return;

        var status  = statusInfo.state === 'success' ? 'passed' : 'failed';
        var mark    = MessagesHandler._getMark(statusInfo.state);
        var details = MessagesHandler._getDetails(statusInfo.statuses);

        this.github.createPullRequestComment(repo, pr.number,
            `${mark} Tests for the commit ${pr.sha} have ${status}. See ${details}.`, owner, repo);
    }

    _onIssueCommentMessage (body) {
        if (body.action !== 'created')
            return;

        var owner = body.repository.owner.login;
        var repo  = body.repository.name;
        var pr    = this.state.openedPullRequests[MessagesHandler._getPRName(repo, body.issue.number)];

        if (!pr)
            return;

        var commandHandler = this._getCommandHandler(body.comment.body, body.issue.title);

        if (!commandHandler)
            return;

        this.github.isUserCollaborator(repo, owner, body.comment.user.login)
            .then(isCollaborator => {
                if (isCollaborator)
                    commandHandler(pr);
            });
    }

    _getCommandHandler (message, title) {
        var name = this.bot.name;

        if (message.indexOf(`@${name}`) < 0)
            return null;

        message = message.replace(`@${name}`, '').replace(/\s/g, '');

        var handlers = {
            '\\retest': async pr => {
                var statusInfo = await this.github.getCombinedStatus(pr.repo, name, pr.branchName);

                if (statusInfo.statuses.some(status => status.state === 'pending') || pr.syncTimeout)
                    return;

                this._syncBranchWithCommit(pr.repo, pr.owner, pr.branchName, pr.sha, this._getTravisConf(title), pr.number);
            }
        };

        return handlers[message] || null;
    }

    handle (message) {
        if (message.type === GITHUB_MESSAGE_TYPES.pullRequest)
            this._onPRMessage(message.body);

        if (message.type === GITHUB_MESSAGE_TYPES.status)
            this._onStatusMessage(message.body);

        if (message.type === GITHUB_MESSAGE_TYPES.issueComment)
            this._onIssueCommentMessage(message.body);
    }
}
<|endoftext|>


<commit_before>import BytesAwareModel from "./abstract_bytes_aware_model";

export default class GameModel extends BytesAwareModel {
    constructor() {
        super();

        this.size = 0;
        this.battlefields = [];
    }

    getSize() {
        return this.size;
    }

    setSize(size) {
        this.size = size;
    }

    addBattlefield(battlefield) {
        this.battlefields.push(battlefield);
    }

    getBattlefields() {
        return this.battlefields;
    }

    setBattlefields(battlefields) {
        return this.battlefields = battlefields;
    }
}
<commit_msg>cleanup game model<commit_after>import BytesAwareModel from "./abstract_bytes_aware_model";

export default class GameModel extends BytesAwareModel {
    constructor() {
        super();

        this.battlefields = [];
    }

    addBattlefield(battlefield) {
        this.battlefields.push(battlefield);
    }

    getBattlefields() {
        return this.battlefields;
    }
}
<|endoftext|>


<commit_before>import mongoose from 'mongoose';
const Schema = mongoose.Schema;

const screeningSchema = new Schema({
  movie: {
    type: Schema.Types.ObjectId,
    ref: 'Movie',
    required: true,
  },
  theater: {
    type: Schema.Types.ObjectId,
    ref: 'Theater',
    required: true,
  },
  attendenceTotal: {
    type: Number,
    required: true,
  },
  dateTime: {
    type: Date,
    required: true,
  },
  admissionsTotal: {
    type: Number,
    required: true,
  },
  concessionsTotal: {
    type: Number,
  },
  format: {
    type: String,
  },
  seats: {
    type: Number,
    required: true,
  },
  // toObject: {
  //   virtuals: true,
  // },
  // toJSON: {
  //   virtuals: true,
  // },
});

// screeningSchema.virtual('ratioTicketsSale')
//   .get(() => this.attendenceTotal / this.admissionsTotal);
//
// screeningSchema.virtual('ratioAttendenceAdmissions')
//   .get(() => this.attendenceTotal / this.seats);

const screening = mongoose.model('Screening', screeningSchema);

export default screening;
<commit_msg>ommented out stuff in screening<commit_after>import mongoose from 'mongoose';
const Schema = mongoose.Schema;

const screeningSchema = new Schema({
  movie: {
    type: Schema.Types.ObjectId,
    ref: 'Movie',
    required: true,
  },
  theater: {
    type: Schema.Types.ObjectId,
    ref: 'Theater',
    required: true,
  },
  attendenceTotal: {
    type: Number,
    required: true,
  },
  dateTime: {
    type: Date,
    required: true,
  },
  admissionsTotal: {
    type: Number,
    required: true,
  },
  concessionsTotal: {
    type: Number,
  },
  format: {
    type: String,
  },
  seats: {
    type: Number,
    required: true,
  },
  // Breaks.  Will check later to confirm if these are needed for virtual properties.
  // toObject: {
  //   virtuals: true,
  // },
  // toJSON: {
  //   virtuals: true,
  // },
});

screeningSchema.virtual('ratioTicketsSale')
  .get(() => this.attendenceTotal / this.admissionsTotal);

screeningSchema.virtual('ratioAttendenceAdmissions')
  .get(() => this.attendenceTotal / this.seats);

const screening = mongoose.model('Screening', screeningSchema);

export default screening;
<|endoftext|>


<commit_before>/* This file is part of Indico.
 * Copyright (C) 2002 - 2016 European Organization for Nuclear Research (CERN).
 *
 * Indico is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 3 of the
 * License, or (at your option) any later version.
 *
 * Indico is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Indico; if not, see <http://www.gnu.org/licenses/>.
 */

// Global scripts that should be executed on all pages

$(document).ready(function() {
    // Create static tabs. They just load the target URL and use no ajax whatsoever
    $('.static-tabs').each(function() {
        var tabCtrl = $(this);
        tabCtrl.tabs({
            active: tabCtrl.data('active')
        });
        // Turn tabs into plain links and fix urls (needed for the active tab)
        $('> .ui-tabs-nav a', this).each(function() {
            var $this = $(this);
            tabCtrl.data('ui-tabs')._off($this, 'click');
            $this.attr('href', $this.data('href'));
        });
    });

    // Remove ui-widget-content style from static tabs as it messes up e.g. link colors
    $('.static-tabs').find('.ui-widget-content').addBack().removeClass('ui-widget-content');

    $('.main-breadcrumb a[href="#"]').css({cursor: 'default', outline: 'none'}).on('click', function(e) {
        e.preventDefault();
    });

    // Use qtip for context help

    $.fn.qtip.defaults = $.extend(true, {}, $.fn.qtip.defaults, {
        position: {
            my: 'top center',
            at: 'bottom center'
        },
        hide: {
            event: 'click mouseout'
        }
    });

    $('.contextHelp[title]').qtip();

    /* Add qTips to elements with a title attribute. The position of the qTip
     * can be specified in the data-qtip-position attribute and is one of
     * "top", "left", "bottom" or "right". It defaults to "bottom".
     *
     * It is also possible to have HTML inside the qTip by using the
     * data-qtip-html attribute instead of the title attribute. */
    $(document).on("mouseenter",
                   '[title]:not([title=""]):not(iframe), [data-qtip-html], [data-qtip-oldtitle]:not(iframe)',
                   function(event) {
        var $target = $(this),
            title = ($target.attr('title') || $target.data('qtip-oldtitle') || '').trim(),
            extraOpts = $(this).data('qtip-opts') || {},
            qtipClass = $(this).data('qtip-style'),
            qtipHTMLContainer = $(this).data('qtip-html'),
            qtipHTML = (qtipHTMLContainer && qtipHTMLContainer.length) ? $(this).next(qtipHTMLContainer) : null;

        if (!qtipHTML && !title) {
            return;
        }

        $target.attr('data-qtip-oldtitle', title);
        $target.removeAttr('title');

        var position = $(this).data('qtip-position');
        var positionOptions;
        if (position == "left") {
            positionOptions = {
                my: 'right center',
                at: 'left center'
            };
        } else if (position == "right") {
            positionOptions = {
                my: 'left center',
                at: 'right center'
            };
        } else if (position == "top") {
            positionOptions = {
                my: 'bottom center',
                at: 'top center'
            };
        }

        /* Attach the qTip to a new element to avoid side-effects on all elements with "title" attributes. */
        var container = $('<span>').qtip($.extend(true, {}, {
            overwrite: false,
            position: $.extend({
                target: $target
            }, positionOptions),

            show: {
                event: event.type,
                ready: true
            },

            content: {
                text: function() {
                    if (qtipHTML) {
                        return qtipHTML.html();
                    } else {
                        return title.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
                    }
                }
            },

            events: {
                show: function(event) {
                    if ($(event.originalEvent.target).hasClass('open')) {
                        event.preventDefault();
                    }
                },

                hide: function(event) {
                    $(this).qtip('destroy');
                },

                render: function(event, api) {
                    $target.on('DOMNodeRemovedFromDocument remove', function(evt) {
                        api.destroy();
                    });
                }
            },

            hide: {
                event: 'mouseleave',
                target: $target
            },

            style: {
                classes: qtipClass ? 'qtip-' + qtipClass : null
            }
        }, extraOpts), event);
    });

    // Enable colorbox for links with .js-lightbox
    $('body').on('click', 'a.js-lightbox', function() {
        $(this).colorbox({
            maxHeight: '90%',
            maxWidth: '90%',
            loop: false,
            photo: true,
            returnFocus: false
        });
    });

    $('.contextHelp[data-src]').qtip({
        content: {
            text: function() {
                return $($(this).data('src')).removeClass('tip');
            }
        }
    });

    $('.js-dropdown').each(function() {
        this.id = this.id || uniqueId();
        $(this).parent().dropdown({selector: '#' + this.id});
    });

    if (navigator.userAgent.match(/Trident\/7\./)) {
        // Silly IE11 will clear the second password field if
        // password autocompletion is enabled!
        // https://social.msdn.microsoft.com/Forums/en-US/7d02173f-8f45-4a74-90bf-5dfbd8f9c1de/ie-11-issue-with-two-password-input-fields
        $('input:password').each(function(){
            if (!this.value && this.getAttribute('value')) {
                this.value = this.getAttribute('value');
            }
        });
    }

    // jQuery UI prevents anything outside modal dialogs from gaining focus.
    // This breaks e.g. the session color date selector. To fix this we prevent
    // the focus trap (focusin event bound on document) from ever receiving the
    // event in case the z-index of the focused event is higher than the dialog's.
    function getMaxZ(elem) {
        var maxZ = 0;
        elem.parents().addBack().each(function() {
            var z = +$(this).css('zIndex');
            if (!isNaN(z)) {
                maxZ = Math.max(z, maxZ);
            }
        });
        return maxZ;
    }

    $('body').on('focusin', function(e) {
        if(!$.ui.dialog.overlayInstances) {
            return;
        }

        if(getMaxZ($(e.target)) > getMaxZ($('.ui-dialog:visible:last'))) {
            e.stopPropagation();
        }
    });

    // Prevent BACK in browser with backspace when focused on a readonly field
    $('input, textarea').on('keydown', function(e) {
        if (this.readOnly && e.which == K.BACKSPACE) {
            e.preventDefault();
        }
    });

    $('input.permalink').on('focus', function() {
        this.select();
    });

    showFormErrors();
});
<commit_msg>Fix ".js-dropdown" class<commit_after>/* This file is part of Indico.
 * Copyright (C) 2002 - 2016 European Organization for Nuclear Research (CERN).
 *
 * Indico is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 3 of the
 * License, or (at your option) any later version.
 *
 * Indico is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Indico; if not, see <http://www.gnu.org/licenses/>.
 */

// Global scripts that should be executed on all pages

$(document).ready(function() {
    // Create static tabs. They just load the target URL and use no ajax whatsoever
    $('.static-tabs').each(function() {
        var tabCtrl = $(this);
        tabCtrl.tabs({
            active: tabCtrl.data('active')
        });
        // Turn tabs into plain links and fix urls (needed for the active tab)
        $('> .ui-tabs-nav a', this).each(function() {
            var $this = $(this);
            tabCtrl.data('ui-tabs')._off($this, 'click');
            $this.attr('href', $this.data('href'));
        });
    });

    // Remove ui-widget-content style from static tabs as it messes up e.g. link colors
    $('.static-tabs').find('.ui-widget-content').addBack().removeClass('ui-widget-content');

    $('.main-breadcrumb a[href="#"]').css({cursor: 'default', outline: 'none'}).on('click', function(e) {
        e.preventDefault();
    });

    // Use qtip for context help

    $.fn.qtip.defaults = $.extend(true, {}, $.fn.qtip.defaults, {
        position: {
            my: 'top center',
            at: 'bottom center'
        },
        hide: {
            event: 'click mouseout'
        }
    });

    $('.contextHelp[title]').qtip();

    /* Add qTips to elements with a title attribute. The position of the qTip
     * can be specified in the data-qtip-position attribute and is one of
     * "top", "left", "bottom" or "right". It defaults to "bottom".
     *
     * It is also possible to have HTML inside the qTip by using the
     * data-qtip-html attribute instead of the title attribute. */
    $(document).on("mouseenter",
                   '[title]:not([title=""]):not(iframe), [data-qtip-html], [data-qtip-oldtitle]:not(iframe)',
                   function(event) {
        var $target = $(this),
            title = ($target.attr('title') || $target.data('qtip-oldtitle') || '').trim(),
            extraOpts = $(this).data('qtip-opts') || {},
            qtipClass = $(this).data('qtip-style'),
            qtipHTMLContainer = $(this).data('qtip-html'),
            qtipHTML = (qtipHTMLContainer && qtipHTMLContainer.length) ? $(this).next(qtipHTMLContainer) : null;

        if (!qtipHTML && !title) {
            return;
        }

        $target.attr('data-qtip-oldtitle', title);
        $target.removeAttr('title');

        var position = $(this).data('qtip-position');
        var positionOptions;
        if (position == "left") {
            positionOptions = {
                my: 'right center',
                at: 'left center'
            };
        } else if (position == "right") {
            positionOptions = {
                my: 'left center',
                at: 'right center'
            };
        } else if (position == "top") {
            positionOptions = {
                my: 'bottom center',
                at: 'top center'
            };
        }

        /* Attach the qTip to a new element to avoid side-effects on all elements with "title" attributes. */
        var container = $('<span>').qtip($.extend(true, {}, {
            overwrite: false,
            position: $.extend({
                target: $target
            }, positionOptions),

            show: {
                event: event.type,
                ready: true
            },

            content: {
                text: function() {
                    if (qtipHTML) {
                        return qtipHTML.html();
                    } else {
                        return title.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
                    }
                }
            },

            events: {
                show: function(event) {
                    if ($(event.originalEvent.target).hasClass('open')) {
                        event.preventDefault();
                    }
                },

                hide: function(event) {
                    $(this).qtip('destroy');
                },

                render: function(event, api) {
                    $target.on('DOMNodeRemovedFromDocument remove', function(evt) {
                        api.destroy();
                    });
                }
            },

            hide: {
                event: 'mouseleave',
                target: $target
            },

            style: {
                classes: qtipClass ? 'qtip-' + qtipClass : null
            }
        }, extraOpts), event);
    });

    // Enable colorbox for links with .js-lightbox
    $('body').on('click', 'a.js-lightbox', function() {
        $(this).colorbox({
            maxHeight: '90%',
            maxWidth: '90%',
            loop: false,
            photo: true,
            returnFocus: false
        });
    });

    $('.contextHelp[data-src]').qtip({
        content: {
            text: function() {
                return $($(this).data('src')).removeClass('tip');
            }
        }
    });

    $('.js-dropdown').each(function() {
        $(this).parent().dropdown({selector: '.js-dropdown'});
    });

    if (navigator.userAgent.match(/Trident\/7\./)) {
        // Silly IE11 will clear the second password field if
        // password autocompletion is enabled!
        // https://social.msdn.microsoft.com/Forums/en-US/7d02173f-8f45-4a74-90bf-5dfbd8f9c1de/ie-11-issue-with-two-password-input-fields
        $('input:password').each(function(){
            if (!this.value && this.getAttribute('value')) {
                this.value = this.getAttribute('value');
            }
        });
    }

    // jQuery UI prevents anything outside modal dialogs from gaining focus.
    // This breaks e.g. the session color date selector. To fix this we prevent
    // the focus trap (focusin event bound on document) from ever receiving the
    // event in case the z-index of the focused event is higher than the dialog's.
    function getMaxZ(elem) {
        var maxZ = 0;
        elem.parents().addBack().each(function() {
            var z = +$(this).css('zIndex');
            if (!isNaN(z)) {
                maxZ = Math.max(z, maxZ);
            }
        });
        return maxZ;
    }

    $('body').on('focusin', function(e) {
        if(!$.ui.dialog.overlayInstances) {
            return;
        }

        if(getMaxZ($(e.target)) > getMaxZ($('.ui-dialog:visible:last'))) {
            e.stopPropagation();
        }
    });

    // Prevent BACK in browser with backspace when focused on a readonly field
    $('input, textarea').on('keydown', function(e) {
        if (this.readOnly && e.which == K.BACKSPACE) {
            e.preventDefault();
        }
    });

    $('input.permalink').on('focus', function() {
        this.select();
    });

    showFormErrors();
});
<|endoftext|>


<commit_before>const getLanguage        = require('./language').get;
const State              = require('./storage').State;
const Url                = require('./url');
const createElement      = require('./utility').createElement;
const applyToAllElements = require('./utility').applyToAllElements;

const BinaryPjax = (() => {
    let previous_url;

    const params   = {};
    const cache    = {};

    const init = (container, content_selector) => {
        if (!(window.history && window.history.pushState && window.history.replaceState &&
            // pushState isn't reliable on iOS until 5.
            !navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/))) {
            return;
        }

        if (!container || !content_selector) {
            return;
        }

        params.container        = container;
        params.content_selector = content_selector;

        const url     = window.location.href;
        const title   = document.title;
        const content = container.querySelector(content_selector);

        // put current content to cache, so we won't need to load it again
        if (content) {
            window.history.replaceState({ url }, title, url);
            setDataPage(content, url);
            params.container.dispatchEvent(new CustomEvent('binarypjax:after', { detail: content }));
        }

        applyToAllElements('a', (el) => { el.addEventListener('click', handleClick); }, '', document.getElementById('all-accounts'));
        document.addEventListener('click', (event) => {
            if (event.target.nodeName === 'A') {
                handleClick(event);
            }
        });
        window.addEventListener('popstate', handlePopstate);
    };

    const setDataPage = (content, url) => {
        content.setAttribute('data-page', url.match(/.+\/(.+)\.html.*/)[1]);
    };

    const handleClick = (event) => {
        let link;
        if (event.target.nodeName === 'A') {
            link = event.target;
        } else if (event.target.parentNode.nodeName === 'A') {
            link = event.target.parentNode;
        } else {
            return;
        }

        const url  = link.href;

        if (!url) {
            return;
        }

        // Exclude links having 'no-ajax' class or target="_blank" or not html
        if (link.classList.contains('no-ajax') || link.target === '_blank' || !/\.html/i.test(url)) {
            return;
        }

        // Middle click, cmd click, and ctrl click should open links in a new tab as normal
        if (event.which > 1 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
            return;
        }

        // Ignore cross origin links
        if (location.protocol !== link.protocol || location.hostname !== link.hostname) {
            return;
        }

        // Ignore event with default prevented
        if (event.defaultPrevented) {
            return;
        }

        event.preventDefault();
        // check if url is not same as current
        if (location.href !== url) {
            processUrl(url);
        }
    };

    const processUrl = (url, replace) => {
        State.set('is_loaded_by_pjax', true);

        const complete_url = /^http/i.test(url) ? url : Url.urlFor(url);

        const cached_content = cacheGet(complete_url);
        if (cached_content) {
            replaceContent(complete_url, cached_content, replace);
        } else {
            load(complete_url, replace);
        }
    };

    /**
     * Load url from server
     */
    const load = (url, replace) => {
        const lang    = getLanguage();
        const xhttp   = new XMLHttpRequest();

        xhttp.onreadystatechange = function() {
            if (this.readyState !== 4 || this.status !== 200) {
                return;
            }
            const div = createElement('div', { html: this.responseText });

            const result = {
                title  : div.getElementsByTagName('title')[0].textContent.trim(),
                content: div.querySelector(params.content_selector),
            };

            // If failed to find title or content, load the page in traditional way
            if (!result.title || !result.content) {
                locationReplace(url);
                return;
            }

            setDataPage(result.content, url);
            cachePut(url, result);
            replaceContent(url, result, replace);
        };

        xhttp.open('GET', url.replace(new RegExp(`/${lang}/`, 'i'), `/${lang.toLowerCase()}/pjax/`), true);
        xhttp.send();
    };

    const handlePopstate = (e) => {
        const url = e.originalEvent && e.originalEvent.state ? e.originalEvent.state.url : window.location.href;
        if (url) {
            processUrl(url, true);
        }
        return false;
    };

    const replaceContent = (url, content, replace) => {
        previous_url = window.location.href;
        window.history[replace ? 'replaceState' : 'pushState']({ url }, content.title, url);

        params.container.dispatchEvent(new Event('binarypjax:before'));

        document.title = content.title;
        params.container.querySelector(params.content_selector).remove();
        $(params.container).append($(content.content).clone());

        params.container.dispatchEvent(new CustomEvent('binarypjax:after', { detail: content.content }));
        $.scrollTo('body', 500);
    };

    const cachePut = (url, content) => {
        cache[cleanUrl(url)] = content;
    };

    const cacheGet = url => cache[cleanUrl(url)];

    const cleanUrl = url => url.replace(/(\?|#).*$/, '');

    const locationReplace = (url) => {
        window.history.replaceState(null, '', url);
        window.location.replace(url);
    };

    const loadPreviousUrl = () => {
        if (window.location.href === previous_url) {
            previous_url = '';
        }
        processUrl(previous_url || Url.defaultRedirectUrl());
    };

    return {
        init,
        loadPreviousUrl,

        load          : processUrl,
        getPreviousUrl: () => previous_url,
    };
})();

module.exports = BinaryPjax;
<commit_msg>remove originalEvent from javascript event<commit_after>const getLanguage        = require('./language').get;
const State              = require('./storage').State;
const Url                = require('./url');
const createElement      = require('./utility').createElement;
const applyToAllElements = require('./utility').applyToAllElements;

const BinaryPjax = (() => {
    let previous_url;

    const params   = {};
    const cache    = {};

    const init = (container, content_selector) => {
        if (!(window.history && window.history.pushState && window.history.replaceState &&
            // pushState isn't reliable on iOS until 5.
            !navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/))) {
            return;
        }

        if (!container || !content_selector) {
            return;
        }

        params.container        = container;
        params.content_selector = content_selector;

        const url     = window.location.href;
        const title   = document.title;
        const content = container.querySelector(content_selector);

        // put current content to cache, so we won't need to load it again
        if (content) {
            window.history.replaceState({ url }, title, url);
            setDataPage(content, url);
            params.container.dispatchEvent(new CustomEvent('binarypjax:after', { detail: content }));
        }

        applyToAllElements('a', (el) => { el.addEventListener('click', handleClick); }, '', document.getElementById('all-accounts'));
        document.addEventListener('click', (event) => {
            if (event.target.nodeName === 'A') {
                handleClick(event);
            }
        });
        window.addEventListener('popstate', handlePopstate);
    };

    const setDataPage = (content, url) => {
        content.setAttribute('data-page', url.match(/.+\/(.+)\.html.*/)[1]);
    };

    const handleClick = (event) => {
        let link;
        if (event.target.nodeName === 'A') {
            link = event.target;
        } else if (event.target.parentNode.nodeName === 'A') {
            link = event.target.parentNode;
        } else {
            return;
        }

        const url  = link.href;

        if (!url) {
            return;
        }

        // Exclude links having 'no-ajax' class or target="_blank" or not html
        if (link.classList.contains('no-ajax') || link.target === '_blank' || !/\.html/i.test(url)) {
            return;
        }

        // Middle click, cmd click, and ctrl click should open links in a new tab as normal
        if (event.which > 1 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
            return;
        }

        // Ignore cross origin links
        if (location.protocol !== link.protocol || location.hostname !== link.hostname) {
            return;
        }

        // Ignore event with default prevented
        if (event.defaultPrevented) {
            return;
        }

        event.preventDefault();
        // check if url is not same as current
        if (location.href !== url) {
            processUrl(url);
        }
    };

    const processUrl = (url, replace) => {
        State.set('is_loaded_by_pjax', true);

        const complete_url = /^http/i.test(url) ? url : Url.urlFor(url);

        const cached_content = cacheGet(complete_url);
        if (cached_content) {
            replaceContent(complete_url, cached_content, replace);
        } else {
            load(complete_url, replace);
        }
    };

    /**
     * Load url from server
     */
    const load = (url, replace) => {
        const lang    = getLanguage();
        const xhttp   = new XMLHttpRequest();

        xhttp.onreadystatechange = function() {
            if (this.readyState !== 4 || this.status !== 200) {
                return;
            }
            const div = createElement('div', { html: this.responseText });

            const result = {
                title  : div.getElementsByTagName('title')[0].textContent.trim(),
                content: div.querySelector(params.content_selector),
            };

            // If failed to find title or content, load the page in traditional way
            if (!result.title || !result.content) {
                locationReplace(url);
                return;
            }

            setDataPage(result.content, url);
            cachePut(url, result);
            replaceContent(url, result, replace);
        };

        xhttp.open('GET', url.replace(new RegExp(`/${lang}/`, 'i'), `/${lang.toLowerCase()}/pjax/`), true);
        xhttp.send();
    };

    const handlePopstate = (e) => {
        const url = e.state ? e.state.url : window.location.href;
        if (url) {
            processUrl(url, true);
        }
        return false;
    };

    const replaceContent = (url, content, replace) => {
        previous_url = window.location.href;
        window.history[replace ? 'replaceState' : 'pushState']({ url }, content.title, url);

        params.container.dispatchEvent(new Event('binarypjax:before'));

        document.title = content.title;
        params.container.querySelector(params.content_selector).remove();
        $(params.container).append($(content.content).clone());

        params.container.dispatchEvent(new CustomEvent('binarypjax:after', { detail: content.content }));
        $.scrollTo('body', 500);
    };

    const cachePut = (url, content) => {
        cache[cleanUrl(url)] = content;
    };

    const cacheGet = url => cache[cleanUrl(url)];

    const cleanUrl = url => url.replace(/(\?|#).*$/, '');

    const locationReplace = (url) => {
        window.history.replaceState(null, '', url);
        window.location.replace(url);
    };

    const loadPreviousUrl = () => {
        if (window.location.href === previous_url) {
            previous_url = '';
        }
        processUrl(previous_url || Url.defaultRedirectUrl());
    };

    return {
        init,
        loadPreviousUrl,

        load          : processUrl,
        getPreviousUrl: () => previous_url,
    };
})();

module.exports = BinaryPjax;
<|endoftext|>


<commit_before>import { Link } from "react-router";
import PropTypes from "prop-types";
import React from "react";
import { Table } from "reactjs-components";

import ResourceTableUtil from "#SRC/js/utils/ResourceTableUtil";
import StringUtil from "../utils/StringUtil";
import TableUtil from "../utils/TableUtil";
import UnitHealthUtil from "../utils/UnitHealthUtil";

const METHODS_TO_BIND = ["renderHealth", "renderNode", "renderNodeRole"];

class UnitHealthNodesTable extends React.Component {
  constructor() {
    super();

    METHODS_TO_BIND.forEach(method => {
      this[method] = this[method].bind(this);
    }, this);
  }

  getColGroup() {
    return (
      <colgroup>
        <col style={{ width: "25%" }} />
        <col />
        <col style={{ width: "25%" }} />
      </colgroup>
    );
  }

  getColumns() {
    const classNameFn = ResourceTableUtil.getClassName;
    const headings = ResourceTableUtil.renderHeading({
      health: "Health",
      host_ip: "Node",
      role: "Role"
    });

    const sortFunction = TableUtil.getSortFunction("host_ip", function(
      node,
      prop
    ) {
      if (prop === "health") {
        return UnitHealthUtil.getHealthSorting(node);
      }

      return node.get(prop);
    });

    return [
      {
        className: classNameFn,
        headerClassName: classNameFn,
        heading: headings,
        prop: "health",
        render: this.renderHealth,
        sortable: true,
        sortFunction
      },
      {
        className: classNameFn,
        headerClassName: classNameFn,
        heading: headings,
        prop: "host_ip",
        render: this.renderNode,
        sortable: true,
        sortFunction
      },
      {
        className: classNameFn,
        headerClassName: classNameFn,
        heading: headings,
        prop: "role",
        render: this.renderNodeRole,
        sortable: true,
        sortFunction
      }
    ];
  }

  getNodeLink(node, linkText) {
    const { unitID } = this.props.params;
    const unitNodeID = node.get("host_ip");

    return (
      <Link
        className="table-cell-link-primary text-overflow"
        to={`/components/${unitID}/nodes/${unitNodeID}`}
      >
        {linkText}
      </Link>
    );
  }

  renderHealth(prop, node) {
    const health = node.getHealth();

    return (

        {StringUtil.capitalize(health.title)}

    );
  }

  renderNode(prop, node) {
    return this.getNodeLink(node, node.get(prop));
  }

  renderNodeRole(prop, node) {
    return StringUtil.capitalize(node.get(prop));
  }

  render() {
    return (

    );
  }
}

UnitHealthNodesTable.propTypes = {
  nodes: PropTypes.array.isRequired,
  params: PropTypes.object
};

module.exports = UnitHealthNodesTable;
<commit_msg>feat(UnitHealthNodesTable): Mark resource table headings<commit_after>import { i18nMark } from "@lingui/react";
import { Link } from "react-router";
import PropTypes from "prop-types";
import React from "react";
import { Table } from "reactjs-components";

import ResourceTableUtil from "#SRC/js/utils/ResourceTableUtil";
import StringUtil from "../utils/StringUtil";
import TableUtil from "../utils/TableUtil";
import UnitHealthUtil from "../utils/UnitHealthUtil";

const METHODS_TO_BIND = ["renderHealth", "renderNode", "renderNodeRole"];

class UnitHealthNodesTable extends React.Component {
  constructor() {
    super();

    METHODS_TO_BIND.forEach(method => {
      this[method] = this[method].bind(this);
    }, this);
  }

  getColGroup() {
    return (
      <colgroup>
        <col style={{ width: "25%" }} />
        <col />
        <col style={{ width: "25%" }} />
      </colgroup>
    );
  }

  getColumns() {
    const classNameFn = ResourceTableUtil.getClassName;
    const headings = ResourceTableUtil.renderHeading({
      health: i18nMark("Health"),
      host_ip: i18nMark("Node"),
      role: i18nMark("Role")
    });

    const sortFunction = TableUtil.getSortFunction("host_ip", function(
      node,
      prop
    ) {
      if (prop === "health") {
        return UnitHealthUtil.getHealthSorting(node);
      }

      return node.get(prop);
    });

    return [
      {
        className: classNameFn,
        headerClassName: classNameFn,
        heading: headings,
        prop: "health",
        render: this.renderHealth,
        sortable: true,
        sortFunction
      },
      {
        className: classNameFn,
        headerClassName: classNameFn,
        heading: headings,
        prop: "host_ip",
        render: this.renderNode,
        sortable: true,
        sortFunction
      },
      {
        className: classNameFn,
        headerClassName: classNameFn,
        heading: headings,
        prop: "role",
        render: this.renderNodeRole,
        sortable: true,
        sortFunction
      }
    ];
  }

  getNodeLink(node, linkText) {
    const { unitID } = this.props.params;
    const unitNodeID = node.get("host_ip");

    return (
      <Link
        className="table-cell-link-primary text-overflow"
        to={`/components/${unitID}/nodes/${unitNodeID}`}
      >
        {linkText}
      </Link>
    );
  }

  renderHealth(prop, node) {
    const health = node.getHealth();

    return (

        {StringUtil.capitalize(health.title)}

    );
  }

  renderNode(prop, node) {
    return this.getNodeLink(node, node.get(prop));
  }

  renderNodeRole(prop, node) {
    return StringUtil.capitalize(node.get(prop));
  }

  render() {
    return (

    );
  }
}

UnitHealthNodesTable.propTypes = {
  nodes: PropTypes.array.isRequired,
  params: PropTypes.object
};

module.exports = UnitHealthNodesTable;
<|endoftext|>


<commit_before>'use strict';

(function(){

  angular
    .module('bitcoincom.services')
    .factory('walletHistoryService', walletHistoryService);

    function walletHistoryService(configService, storageService, lodash, $log, txFormatService) {
      //var PAGE_SIZE = 50;
      var PAGE_SIZE = 20; // For dev only
      // How much to overlap on each end of the page, for mitigating inconsistent sort order.
      var PAGE_OVERLAP_FRACTION = 0.2;
      var PAGE_OVERLAP = Math.floor(PAGE_SIZE * PAGE_OVERLAP_FRACTION);
      // The fraction of transactions in the new overlapping resultset that we already know about.
      // If we know about at least this many, then there are probably no gaps.
      var MIN_KNOWN_TX_OVERLAP_FRACTION = 0.5;

      var SAFE_CONFIRMATIONS = 6;

      var allTransactionsFetched = false;
      var service = {
        getCachedTxHistory: getCachedTxHistory,
        updateLocalTxHistoryByPage: updateLocalTxHistoryByPage,
      };
      return service;

      function addEarlyTransactions(walletId, cachedTxs, newTxs) {

        var cachedTxIndexFromId = {};
        cachedTxs.forEach(function forCachedTx(tx, txIndex){
          cachedTxIndexFromId[tx.txid] = txIndex;
        });

        var confirmationsUpdated = false;
        var someTransactionsWereNew = false;
        var overlappingTxsCount = 0;

        newTxs.forEach(function forNewTx(tx){
          if (typeof cachedTxIndexFromId[tx.txid] === "undefined") {
            someTransactionsWereNew = true;
            cachedTxs.push(tx);
          } else {
            var txUpdated = updateCachedTx(cachedTxs, cachedTxIndexFromId, tx);
            confirmationsUpdated = confirmationsUpdated || txUpdated;
            overlappingTxsCount++;
          }
        });

        var overlappingTxFraction = overlappingTxsCount / Math.min(cachedTxs.length, PAGE_OVERLAP);
        console.log('overlappingTxFraction:', overlappingTxFraction);

        if (overlappingTxFraction >= MIN_KNOWN_TX_OVERLAP_FRACTION) { // We are good
          if (someTransactionsWereNew) {
            saveTxHistory(walletId, cachedTxs);
          } else if (confirmationsUpdated) {
            saveTxHistory(walletId, cachedTxs);
          } else if (overlappingTxsCount === newTxs.length) {
            allTransactionsFetched = true;
          }
          return cachedTxs;
        } else {
          // We might be missing some txs.
          console.error('We might be missing some txs in the history.');
          // Our history is wrong, so remove it - we could instead, try to fetch data that was not so early.
          storageService.removeTxHistory(walletId, function onRemoveTxHistory(){});
          return [];
        }

      }

      function addLatestTransactions(walletId, cachedTxs, newTxs) {
        var cachedTxIndexFromId = {};
        cachedTxs.forEach(function forCachedTx(tx, txIndex){
          cachedTxIndexFromId[tx.txid] = txIndex;
        });

        var someTransactionsWereNew = false;
        var confirmationsUpdated = false;
        var overlappingTxsCount = 0;
        var uniqueNewTxs = [];

        newTxs.forEach(function forNewTx(tx){
          if (typeof cachedTxIndexFromId[tx.txid] === "undefined") {
            someTransactionsWereNew = true;
            uniqueNewTxs.push(tx);
          } else {
            var txUpdated = updateCachedTx(cachedTxs, cachedTxIndexFromId, tx);
            confirmationsUpdated = confirmationsUpdated || txUpdated;
            overlappingTxsCount++;
          }
        });

        var overlappingTxFraction = overlappingTxsCount / Math.min(cachedTxs.length, PAGE_OVERLAP);

        if (overlappingTxFraction >= MIN_KNOWN_TX_OVERLAP_FRACTION) { // We are good
          if (someTransactionsWereNew) {
            var allTxs = uniqueNewTxs.concat(cachedTxs);
            saveTxHistory(walletId, allTxs);
            return allTxs;
          } else {
            if (confirmationsUpdated) {
              saveTxHistory(walletId, cachedTxs);
            }
            return cachedTxs;
          }
        } else {
          // We might be missing some txs.
          // Our history is wrong, so just include the latest ones
          saveTxHistory(walletId, newTxs);
          return newTxs;
        }

      }

      // Only clear the cache once we have received new transactions from the server.
      /**
       * @param wallet
       * @param start
       * @param {function(err, txs)} cb - transactions is always an array, may be empty
       */
      function fetchTxHistoryByPage(wallet, start, cb) {
        var skip = Math.max(0, start - PAGE_OVERLAP);
        var limit = PAGE_SIZE;

        var opts = {
          skip: skip,
          limit: limit
        };
        wallet.getTxHistory(opts, function onTxHistory(err, txsFromServer) {
          if (err) {
            return cb(err, []);
          }

          if (txsFromServer.length === 0) {
            return cb(null, []);
          }

          var processedTxs = processNewTxs(wallet, txsFromServer);

          return cb(null, processedTxs);
        });
      }

      /**
       * @param {string} walletId
       * @param {function(error, txs)} cb - txs is always an array, may be empty
       */
      function getCachedTxHistory(walletId, cb) {
        storageService.getTxHistory(walletId, function onGetTxHistory(err, txHistoryString){
          if (err) {
            return cb(err, []);
          }

          if (!txHistoryString) {
            return cb(null, []);
          }

          try {
            var txHistory = JSON.parse(txHistoryString);
            return cb(null, txHistory);
          } catch (e) {
            $log.error('Failed to parse tx history.', e);
            return cb(e, []);
          }
        });
      }

      function processNewTxs(wallet, txs) {
        var now = Math.floor(Date.now() / 1000);
        var txHistoryUnique = {};
        var processedTxs = [];
        wallet.hasUnsafeConfirmed = false;

        lodash.each(txs, function(tx) {
          tx = txFormatService.processTx(wallet.coin, tx);

          // no future transactions...
          if (tx.time > now)
            tx.time = now;

          if (tx.confirmations >= SAFE_CONFIRMATIONS) {
            tx.safeConfirmed = SAFE_CONFIRMATIONS + '+';
          } else {
            tx.safeConfirmed = false;
            wallet.hasUnsafeConfirmed = true;
          }

          if (tx.note) {
            delete tx.note.encryptedEditedByName;
            delete tx.note.encryptedBody;
          }

          if (!txHistoryUnique[tx.txid]) {
            processedTxs.push(tx);
            txHistoryUnique[tx.txid] = true;
          } else {
            $log.debug('Ignoring duplicate TX in history: ' + tx.txid)
          }
        });

        return processedTxs;
      }

      function saveTxHistory(walletId, processedTxs) {
        storageService.setTxHistory(processedTxs, walletId, function onSetTxHistory(error){
          if (error) {
            $log.error('pagination Failed to save tx history.', error);
          }
        });
      }

      /**
       * Returns true if the cached tx was updated
       * @param {*} cachedTxs
       * @param {*} cachedTxIndexFromId - Indices for cachedTxs, based on txid
       * @param {*} tx - The most recent tx info
       */
      function updateCachedTx(cachedTxs, cachedTxIndexFromId, tx) {
        var updated = false;
        var txIndex = cachedTxIndexFromId[tx.txid];
        var cachedTx = cachedTxs[txIndex];

        if (cachedTx.confirmations < SAFE_CONFIRMATIONS && tx.confirmations > cachedTx.confirmations) {
          cachedTxs[txIndex].confirmations = tx.confirmations;
          updated = true;
        }
        return updated;
      }

      function updateLocalTxHistoryByPage(wallet, getLatest, flushCacheOnNew, cb) {
        if (flushCacheOnNew) {
          fetchTxHistoryByPage(wallet, 0, function onFetchTxHistory(err, txs){
            if (err) {
              return cb(err, txs);
            }
            saveTxHistory(wallet.id, txs);
            return cb(null, txs);
          });
        } else {
          getCachedTxHistory(wallet.id, function onCachedHistory(err, cachedTxs){
            if (err) {
              $log.error('Failed to get cached tx history.', err);
              return cb(err, []);
            }

            var start = getLatest ? 0 : cachedTxs.length;
            fetchTxHistoryByPage(wallet, start, function onFetchHistory(err, fetchedTxs){
                if (err) {
                  return cb(err);
                }

                if (fetchedTxs.length === 0) {
                  return cb(null, cachedTxs, true /*fetchedAllTransactions*/);
                }

                var txs = [];
                if (getLatest) {
                  txs = addLatestTransactions(wallet.id, cachedTxs, fetchedTxs);
                } else {
                  allTransactionsFetched = false;
                  txs = addEarlyTransactions(wallet.id, cachedTxs, fetchedTxs);
                  return cb(null, txs, allTransactionsFetched);
                }
                return cb(null, txs);
            });

          });
        }
      }
    }
})();<commit_msg>Fix the case where the cache is empty<commit_after>'use strict';

(function(){

  angular
    .module('bitcoincom.services')
    .factory('walletHistoryService', walletHistoryService);

    function walletHistoryService(configService, storageService, lodash, $log, txFormatService) {
      //var PAGE_SIZE = 50;
      var PAGE_SIZE = 20; // For dev only
      // How much to overlap on each end of the page, for mitigating inconsistent sort order.
      var PAGE_OVERLAP_FRACTION = 0.2;
      var PAGE_OVERLAP = Math.floor(PAGE_SIZE * PAGE_OVERLAP_FRACTION);
      // The fraction of transactions in the new overlapping resultset that we already know about.
      // If we know about at least this many, then there are probably no gaps.
      var MIN_KNOWN_TX_OVERLAP_FRACTION = 0.5;

      var SAFE_CONFIRMATIONS = 6;

      var allTransactionsFetched = false;
      var service = {
        getCachedTxHistory: getCachedTxHistory,
        updateLocalTxHistoryByPage: updateLocalTxHistoryByPage,
      };
      return service;

      function addEarlyTransactions(walletId, cachedTxs, newTxs) {

        var cachedTxIndexFromId = {};
        cachedTxs.forEach(function forCachedTx(tx, txIndex){
          cachedTxIndexFromId[tx.txid] = txIndex;
        });

        var confirmationsUpdated = false;
        var someTransactionsWereNew = false;
        var overlappingTxsCount = 0;

        newTxs.forEach(function forNewTx(tx){
          if (typeof cachedTxIndexFromId[tx.txid] === "undefined") {
            someTransactionsWereNew = true;
            cachedTxs.push(tx);
          } else {
            var txUpdated = updateCachedTx(cachedTxs, cachedTxIndexFromId, tx);
            confirmationsUpdated = confirmationsUpdated || txUpdated;
            overlappingTxsCount++;
          }
        });

        var overlappingTxFraction = overlappingTxsCount / Math.min(cachedTxs.length, PAGE_OVERLAP);
        console.log('overlappingTxFraction:', overlappingTxFraction);
        console.log('overlappingTxsCount:', overlappingTxsCount);

        if (overlappingTxFraction >= MIN_KNOWN_TX_OVERLAP_FRACTION || (someTransactionsWereNew && overlappingTxsCount === 0)) { // We are good
          if (someTransactionsWereNew) {
            saveTxHistory(walletId, cachedTxs);
          } else if (confirmationsUpdated) {
            saveTxHistory(walletId, cachedTxs);
          } else if (overlappingTxsCount === newTxs.length) {
            allTransactionsFetched = true;
          }
          return cachedTxs;
        } else {
          // We might be missing some txs.
          console.error('We might be missing some txs in the history.');
          // Our history is wrong, so remove it - we could instead, try to fetch data that was not so early.
          storageService.removeTxHistory(walletId, function onRemoveTxHistory(){});
          return [];
        }

      }

      function addLatestTransactions(walletId, cachedTxs, newTxs) {
        var cachedTxIndexFromId = {};
        cachedTxs.forEach(function forCachedTx(tx, txIndex){
          cachedTxIndexFromId[tx.txid] = txIndex;
        });

        var someTransactionsWereNew = false;
        var confirmationsUpdated = false;
        var overlappingTxsCount = 0;
        var uniqueNewTxs = [];

        newTxs.forEach(function forNewTx(tx){
          if (typeof cachedTxIndexFromId[tx.txid] === "undefined") {
            someTransactionsWereNew = true;
            uniqueNewTxs.push(tx);
          } else {
            var txUpdated = updateCachedTx(cachedTxs, cachedTxIndexFromId, tx);
            confirmationsUpdated = confirmationsUpdated || txUpdated;
            overlappingTxsCount++;
          }
        });

        var overlappingTxFraction = overlappingTxsCount / Math.min(cachedTxs.length, PAGE_OVERLAP);

        if (overlappingTxFraction >= MIN_KNOWN_TX_OVERLAP_FRACTION) { // We are good
          if (someTransactionsWereNew) {
            var allTxs = uniqueNewTxs.concat(cachedTxs);
            saveTxHistory(walletId, allTxs);
            return allTxs;
          } else {
            if (confirmationsUpdated) {
              saveTxHistory(walletId, cachedTxs);
            }
            return cachedTxs;
          }
        } else {
          // We might be missing some txs.
          // Our history is wrong, so just include the latest ones
          saveTxHistory(walletId, newTxs);
          return newTxs;
        }

      }

      // Only clear the cache once we have received new transactions from the server.
      /**
       * @param wallet
       * @param start
       * @param {function(err, txs)} cb - transactions is always an array, may be empty
       */
      function fetchTxHistoryByPage(wallet, start, cb) {
        var skip = Math.max(0, start - PAGE_OVERLAP);
        var limit = PAGE_SIZE;

        var opts = {
          skip: skip,
          limit: limit
        };
        wallet.getTxHistory(opts, function onTxHistory(err, txsFromServer) {
          if (err) {
            return cb(err, []);
          }

          if (txsFromServer.length === 0) {
            return cb(null, []);
          }

          var processedTxs = processNewTxs(wallet, txsFromServer);

          return cb(null, processedTxs);
        });
      }

      /**
       * @param {string} walletId
       * @param {function(error, txs)} cb - txs is always an array, may be empty
       */
      function getCachedTxHistory(walletId, cb) {
        storageService.getTxHistory(walletId, function onGetTxHistory(err, txHistoryString){
          if (err) {
            return cb(err, []);
          }

          if (!txHistoryString) {
            return cb(null, []);
          }

          try {
            var txHistory = JSON.parse(txHistoryString);
            return cb(null, txHistory);
          } catch (e) {
            $log.error('Failed to parse tx history.', e);
            return cb(e, []);
          }
        });
      }

      function processNewTxs(wallet, txs) {
        var now = Math.floor(Date.now() / 1000);
        var txHistoryUnique = {};
        var processedTxs = [];
        wallet.hasUnsafeConfirmed = false;

        lodash.each(txs, function(tx) {
          tx = txFormatService.processTx(wallet.coin, tx);

          // no future transactions...
          if (tx.time > now)
            tx.time = now;

          if (tx.confirmations >= SAFE_CONFIRMATIONS) {
            tx.safeConfirmed = SAFE_CONFIRMATIONS + '+';
          } else {
            tx.safeConfirmed = false;
            wallet.hasUnsafeConfirmed = true;
          }

          if (tx.note) {
            delete tx.note.encryptedEditedByName;
            delete tx.note.encryptedBody;
          }

          if (!txHistoryUnique[tx.txid]) {
            processedTxs.push(tx);
            txHistoryUnique[tx.txid] = true;
          } else {
            $log.debug('Ignoring duplicate TX in history: ' + tx.txid)
          }
        });

        return processedTxs;
      }

      function saveTxHistory(walletId, processedTxs) {
        storageService.setTxHistory(processedTxs, walletId, function onSetTxHistory(error){
          if (error) {
            $log.error('pagination Failed to save tx history.', error);
          }
        });
      }

      /**
       * Returns true if the cached tx was updated
       * @param {*} cachedTxs
       * @param {*} cachedTxIndexFromId - Indices for cachedTxs, based on txid
       * @param {*} tx - The most recent tx info
       */
      function updateCachedTx(cachedTxs, cachedTxIndexFromId, tx) {
        var updated = false;
        var txIndex = cachedTxIndexFromId[tx.txid];
        var cachedTx = cachedTxs[txIndex];

        if (cachedTx.confirmations < SAFE_CONFIRMATIONS && tx.confirmations > cachedTx.confirmations) {
          cachedTxs[txIndex].confirmations = tx.confirmations;
          updated = true;
        }
        return updated;
      }

      function updateLocalTxHistoryByPage(wallet, getLatest, flushCacheOnNew, cb) {
        if (flushCacheOnNew) {
          fetchTxHistoryByPage(wallet, 0, function onFetchTxHistory(err, txs){
            if (err) {
              return cb(err, txs);
            }
            saveTxHistory(wallet.id, txs);
            return cb(null, txs);
          });
        } else {
          getCachedTxHistory(wallet.id, function onCachedHistory(err, cachedTxs){
            if (err) {
              $log.error('Failed to get cached tx history.', err);
              return cb(err, []);
            }

            var start = getLatest ? 0 : cachedTxs.length;
            fetchTxHistoryByPage(wallet, start, function onFetchHistory(err, fetchedTxs){
                if (err) {
                  return cb(err);
                }

                if (fetchedTxs.length === 0) {
                  return cb(null, cachedTxs, true /*fetchedAllTransactions*/);
                }

                var txs = [];
                if (getLatest) {
                  txs = addLatestTransactions(wallet.id, cachedTxs, fetchedTxs);
                } else {
                  allTransactionsFetched = false;
                  txs = addEarlyTransactions(wallet.id, cachedTxs, fetchedTxs);
                  return cb(null, txs, allTransactionsFetched);
                }
                return cb(null, txs);
            });

          });
        }
      }
    }
})();<|endoftext|>


<commit_before>// Color bar with the choropleth

export default class ColorBar {
  constructor(d3, cmap, elementId) {
    let svg = d3.select('#' + elementId).append('svg')

    let chartDiv = document.getElementById(elementId),
        divWidth = chartDiv.offsetWidth,
        divHeight = chartDiv.offsetHeight

    let group = svg.append('g')
        .attr('class', 'colorbar-group')

    let bar = {
      height: 0.7 * divHeight,
      width: divWidth / 3,
      x: divWidth * 3 / 5,
      y: 0.15 * divHeight
    }

    let eachHeight = bar.height / cmap.length

    // Add rectangles
    for (let i = 0; i < cmap.length; i++) {
      group.append('rect')
        .attr('x', bar.x)
        .attr('y', bar.y + i * eachHeight)
        .attr('height', eachHeight)
        .attr('width', bar.width)
        .style('fill', cmap[i])
    }

    // Add axis
    let scale = d3.scaleLinear()
        .range([bar.height, 0])

    group.append('g')
      .attr('class', 'axis axis-color')
      .attr('transform', 'translate(' + divWidth / 2 + ',' + bar.y + ')')

    this.d3 = d3
    this.svg = svg
    this.scale = scale
  }

  // Update scale of colorbar
  calibrate(range) {
    this.scale.domain(range)

    let axis = this.d3.axisLeft(this.scale)
        .ticks(5)

    this.svg.select('.axis-color')
      .transition()
      .duration(200)
      .call(axis)
  }
}
<commit_msg>Remove extra colorbar module<commit_after><|endoftext|>