<commit_before>$.fn.paginate = function(pagesize, page) {

    var $this = this;
    if (pagesize == 'clear') {
        $this.removeAttr('data-paginate');
        $this.find('[data-paginate-visible]').removeAttr('data-paginate-visible');
        $this.find('tbody tr').show();
        return;
    }

    var pagesize = pagesize || 50;
    var page = page || 1;

    var rows = $this.find('tbody tr');
    if (!$this.attr('data-paginate')) {
        $this.attr('data-paginate', pagesize);
        rows.filter(':visible').attr('data-paginate-visible', 1);
    } else {
        pagesize = parseInt($this.attr('data-paginate'));
    }

    var visible = rows.filter('[data-paginate-visible=1]');
    visible.each(function(index, row) {
        row = $(row);
        if (index >= (page-1)*pagesize
            && index < page*pagesize) {
            row.show();
        } else { // rollin', rollin', rollin'...
            row.hide();
        }
    });

    return visible.size();
};

var ranking = {

    playerClick : function(ev) {
        if (!$(ev.target).closest('a').size()) {
            var row = $(this);
            var pid = row.find('td.pid').text();
            if (pid) {
                var link = 'players/' + pid.trim() + '.html';
                location.href = link;
            }
        }
    },

    parseHash : function(hash) {
        hash = hash.replace(/^#/, '');
        var hashParams = hash.split(';');
        var params = new Map();
        hashParams.forEach(function(hashParam) {
            var param = hashParam.split(':');
            if (param.length > 1) {
                params.set(param[0], param[1].split(','));
            }
        });
        return params;
    },

    constructHash : function(params) {
        var paramArray = [];
        params.forEach(function(value, key) {
            paramArray.push(key + ':' + value.join(','));
        });
        return paramArray.join(';');
    },

    savedParams : new Map(),

    readHash : function() {
        var params = ranking.parseHash(location.hash);
        var allParams = ['age', 'gender', 'region', 'name'];
        var paramsChanged = false;
        allParams.forEach(function(param) {
            var newParam = params.get(param) || [];
            var oldParam = ranking.savedParams.get(param) || [];
            if (newParam.length != oldParam.length || newParam.join(',') != oldParam.join(',')) {
                paramsChanged = true;
            }
        });
        $('button.btn-primary').removeClass('btn-primary');
        params.forEach(function(values, param) {
            values.forEach(function(value) {
                $('button[data-filter="' + param + '"][data-value="' + value + '"]').addClass('btn-primary');
            });
        });
        var filtersPresent = false;
        allParams.forEach(function(param) {
            if ($('button[data-filter="' + param + '"]').size() > 0
                && $('button[data-filter="' + param + '"].btn-primary').size() == 0) {
                $('button[data-clear="' + param + '"]').addClass('btn-primary');
            } else {
                filtersPresent |= ($('button[data-filter="' + param + '"]').size() > 0);
            }
            var field = $('input[data-filter-field="' + param + '"]');
            if (field.size() > 0) {
                field.val(params.get(param));
                if (field.val().length > 0) {
                    filtersPresent = true;
                }
            }
        });
        if (filtersPresent) {
            $('#filters').collapse();
        }
        if (paramsChanged) {
            $('table.table-paginate').paginate('clear');
            ranking.filterRows(params);
            ranking.savedParams = params;
        }
        $('table.data-table, .filters .panel-body').css('opacity', 1);
        ranking.filtersDisabled = false;
        var pagesize = 40;
        var page = params.get('page') || [0];
        var count = $('table.table-paginate').paginate(pagesize, parseInt(page[0]));
        $('table.data-table tbody tr[data-paginate-visible=1]').eq(0).addClass('gold');
        $('table.data-table tbody tr[data-paginate-visible=1]').eq(1).addClass('silver');
        $('table.data-table tbody tr[data-paginate-visible=1]').eq(2).addClass('bronze');
        ranking.buildPaginator('#top-paginator', count, pagesize, page[0] || 1);
        ranking.buildPaginator('#bottom-paginator', count, pagesize, page[0] || 1);
    },

    filtersDisabled: false,

    filterRows : function(params) {
        $('table.data-table tbody tr').show().removeClass('gold silver bronze').each(function() {
            var row = $(this);
            var hidden = false;
            params.forEach(function(value, param) {
                if (param == 'name') {
                    if (row.find('td.' + param).text().trim().toLowerCase().search(value.join('')) == -1) {
                        row.hide();
                        hidden = true;
                    }
                } else if (param != 'page') {
                    if (value.indexOf(row.find('td.' + param).text().trim()) == -1) {
                        row.hide();
                        hidden = true;
                    }
                }
            });
            if (!hidden) {
                row.show();
            }
        });
    },

    buildPaginator: function(selector, count, pagesize, page) {
        var box = $(selector);
        box.html('');
        var buttonGroup = $('<div>').addClass('btn-group');
        var pages = parseInt(Math.ceil(count / pagesize));
        buttonGroup.attr('data-pages', pages);
        buttonGroup.append($('<button>').addClass('btn btn-default paginator-prev').attr('type', 'button').text('<<'));
        for (var i = 0; i < pages; i++) {
            var pageBtn = $('<button>').addClass('btn btn-default paginator-page').attr('type', 'button').attr('data-page', i+1).text(i+1);
            if (i+1 == page) {
                pageBtn.addClass('btn-primary');
            }
            buttonGroup.append(pageBtn);
        }
        buttonGroup.append($('<button>').addClass('btn btn-default paginator-next').attr('type', 'button').text('>>'));
        box.append(buttonGroup);
    },

    changePage: function() {
        $('table.data-table').css('opacity', 0.1);
        var btn = $(this);
        var page = parseInt(btn.attr('data-page'));
        var params = ranking.parseHash(location.hash);
        var currentPage = params.get('page');
        currentPage = currentPage ? parseInt(currentPage[0]) : 1;
        if (btn.hasClass('paginator-prev')) {
            page = currentPage - 1;
        }
        if (btn.hasClass('paginator-next')) {
            page = currentPage + 1;
        }
        var count = parseInt(btn.closest('[data-pages]').attr('data-pages'));
        if (page > 0 && page <= count) {
            params.set('page', [page]);
            location.hash = ranking.constructHash(params);
        } else {
            $('table.data-table').css('opacity', 1);
        }
    },

    init : function() {

        $('.container .table tbody tr').click(ranking.playerClick);

        $(document).ready(function() {
            $(window).on('hashchange', ranking.readHash).trigger('hashchange');
        });

        $('button[data-filter-action]').click(function(ev) {
            if (!ranking.filtersDisabled) {
                ranking.filtersDisabled = true;
                $('table.data-table, .filters .panel-body').css('opacity', 0.1);
                var button = $(this);
                var params = ranking.parseHash(location.hash);
                var param = params.get(button.attr('data-filter-action'));
                var value = $('input[data-filter-field="' + button.attr('data-filter-action') + '"]').val().trim().toLowerCase();
                params.set(button.attr('data-filter-action'), [value]);
                params.delete('page');
                location.hash = ranking.constructHash(params);
            }
        });

        $('button[data-filter]').click(function(ev) {
            if (!ranking.filtersDisabled) {
                ranking.filtersDisabled = true;
                $('table.data-table, .filters .panel-body').css('opacity', 0.1);
                var button = $(this);
                var params = ranking.parseHash(location.hash);
                var param = params.get(button.attr('data-filter'));
                var value = button.attr('data-value');
                var index = param ? param.indexOf(value) : -1;
                if (index > -1) {
                    param = param.filter(function(v) { return v != value; });
                } else {
                    if (!param || !ev.ctrlKey) {
                        param = [];
                    }
                    param.push(value);
                }
                if (!param.length) {
                    params.delete(button.attr('data-filter'));
                } else {
                    params.set(button.attr('data-filter'), param);
                }
                params.delete('page');
                location.hash = ranking.constructHash(params);
            }
        });

        $('button[data-clear]').click(function() {
            if (!ranking.filtersDisabled) {
                ranking.filtersDisabled = true;
                $('table.data-table, .filters .panel-body').css('opacity', 0.1);
                var button = $(this);
                var params = ranking.parseHash(location.hash);
                params.delete(button.attr('data-clear'));
                params.delete('page');
                location.hash = ranking.constructHash(params);
            }
        });

        $(document).on('click', 'button.paginator-prev, button.paginator-next, button.paginator-page', ranking.changePage);
    }

};

ranking.init();
<commit_msg>Inified way to handle parameter changes<commit_after>$.fn.paginate = function(pagesize, page) {

    var $this = this;
    if (pagesize == 'clear') {
        $this.removeAttr('data-paginate');
        $this.find('[data-paginate-visible]').removeAttr('data-paginate-visible');
        $this.find('tbody tr').show();
        return;
    }

    var pagesize = pagesize || 50;
    var page = page || 1;

    var rows = $this.find('tbody tr');
    if (!$this.attr('data-paginate')) {
        $this.attr('data-paginate', pagesize);
        rows.filter(':visible').attr('data-paginate-visible', 1);
    } else {
        pagesize = parseInt($this.attr('data-paginate'));
    }

    var visible = rows.filter('[data-paginate-visible=1]');
    visible.each(function(index, row) {
        row = $(row);
        if (index >= (page-1)*pagesize
            && index < page*pagesize) {
            row.show();
        } else { // rollin', rollin', rollin'...
            row.hide();
        }
    });

    return visible.size();
};

var ranking = {

    playerClick : function(ev) {
        if (!$(ev.target).closest('a').size()) {
            var row = $(this);
            var pid = row.find('td.pid').text();
            if (pid) {
                var link = 'players/' + pid.trim() + '.html';
                location.href = link;
            }
        }
    },

    parseHash : function(hash) {
        hash = hash.replace(/^#/, '');
        var hashParams = hash.split(';');
        var params = new Map();
        hashParams.forEach(function(hashParam) {
            var param = hashParam.split(':');
            if (param.length > 1) {
                params.set(param[0], param[1].split(','));
            }
        });
        return params;
    },

    constructHash : function(params) {
        var paramArray = [];
        params.forEach(function(value, key) {
            paramArray.push(key + ':' + value.join(','));
        });
        return paramArray.join(';');
    },

    savedParams : new Map(),

    readHash : function() {
        var params = ranking.parseHash(location.hash);
        var allParams = ['age', 'gender', 'region', 'name'];
        var paramsChanged = false;
        allParams.forEach(function(param) {
            var newParam = params.get(param) || [];
            var oldParam = ranking.savedParams.get(param) || [];
            if (newParam.length != oldParam.length || newParam.join(',') != oldParam.join(',')) {
                paramsChanged = true;
            }
        });
        $('button.btn-primary').removeClass('btn-primary');
        params.forEach(function(values, param) {
            values.forEach(function(value) {
                $('button[data-filter="' + param + '"][data-value="' + value + '"]').addClass('btn-primary');
            });
        });
        var filtersPresent = false;
        allParams.forEach(function(param) {
            if ($('button[data-filter="' + param + '"]').size() > 0
                && $('button[data-filter="' + param + '"].btn-primary').size() == 0) {
                $('button[data-clear="' + param + '"]').addClass('btn-primary');
            } else {
                filtersPresent |= ($('button[data-filter="' + param + '"]').size() > 0);
            }
            var field = $('input[data-filter-field="' + param + '"]');
            if (field.size() > 0) {
                field.val(params.get(param));
                if (field.val().length > 0) {
                    filtersPresent = true;
                }
            }
        });
        if (filtersPresent) {
            $('#filters').collapse();
        }
        if (paramsChanged) {
            $('table.table-paginate').paginate('clear');
            ranking.filterRows(params);
            ranking.savedParams = params;
        }
        $('table.data-table, .filters .panel-body').css('opacity', 1);
        ranking.filtersDisabled = false;
        var pagesize = 40;
        var page = params.get('page') || [0];
        var count = $('table.table-paginate').paginate(pagesize, parseInt(page[0]));
        $('table.data-table tbody tr[data-paginate-visible=1]').eq(0).addClass('gold');
        $('table.data-table tbody tr[data-paginate-visible=1]').eq(1).addClass('silver');
        $('table.data-table tbody tr[data-paginate-visible=1]').eq(2).addClass('bronze');
        ranking.buildPaginator('#top-paginator', count, pagesize, page[0] || 1);
        ranking.buildPaginator('#bottom-paginator', count, pagesize, page[0] || 1);
    },

    filtersDisabled: false,

    filterRows : function(params) {
        $('table.data-table tbody tr').show().removeClass('gold silver bronze').each(function() {
            var row = $(this);
            var hidden = false;
            params.forEach(function(value, param) {
                if (param == 'name') {
                    if (row.find('td.' + param).text().trim().toLowerCase().search(value.join('')) == -1) {
                        row.hide();
                        hidden = true;
                    }
                } else if (param != 'page') {
                    if (value.indexOf(row.find('td.' + param).text().trim()) == -1) {
                        row.hide();
                        hidden = true;
                    }
                }
            });
            if (!hidden) {
                row.show();
            }
        });
    },

    buildPaginator: function(selector, count, pagesize, page) {
        var box = $(selector);
        box.html('');
        var buttonGroup = $('<div>').addClass('btn-group');
        var pages = parseInt(Math.ceil(count / pagesize));
        buttonGroup.attr('data-pages', pages);
        buttonGroup.append($('<button>').addClass('btn btn-default paginator-prev').attr('type', 'button').text('<<'));
        for (var i = 0; i < pages; i++) {
            var pageBtn = $('<button>').addClass('btn btn-default paginator-page').attr('type', 'button').attr('data-page', i+1).text(i+1);
            if (i+1 == page) {
                pageBtn.addClass('btn-primary');
            }
            buttonGroup.append(pageBtn);
        }
        buttonGroup.append($('<button>').addClass('btn btn-default paginator-next').attr('type', 'button').text('>>'));
        box.append(buttonGroup);
    },

    changePage: function() {
        $('table.data-table').css('opacity', 0.1);
        var btn = $(this);
        var page = parseInt(btn.attr('data-page'));
        var params = ranking.parseHash(location.hash);
        var currentPage = params.get('page');
        currentPage = currentPage ? parseInt(currentPage[0]) : 1;
        if (btn.hasClass('paginator-prev')) {
            page = currentPage - 1;
        }
        if (btn.hasClass('paginator-next')) {
            page = currentPage + 1;
        }
        var count = parseInt(btn.closest('[data-pages]').attr('data-pages'));
        if (page > 0 && page <= count) {
            params.set('page', [page]);
            location.hash = ranking.constructHash(params);
        } else {
            $('table.data-table').css('opacity', 1);
        }
    },

    init : function() {

        $('.container .table tbody tr').click(ranking.playerClick);

        $(document).ready(function() {
            $(window).on('hashchange', ranking.readHash).trigger('hashchange');
        });

        var handleParams = function(callback, target, ev) {
            if (!ranking.filtersDisabled) {
                ranking.filtersDisabled = true;
                $('table.data-table, .filters .panel-body').css('opacity', 0.1);
                var oldHash = location.hash.replace(/^#/, '');
                var newParams = callback(target, ev);
                var newHash = ranking.constructHash(newParams);
                if (oldHash != newHash) {
                    location.hash = newHash;
                } else {
                    $('table.data-table, .filters .panel-body').css('opacity', 1);
                    ranking.filtersDisabled = false;
                }
            }
        };

        $('button[data-filter-action]').click(function(ev) {
            handleParams(function(target, ev) {
                var button = target;
                var params = ranking.parseHash(location.hash);
                var param = params.get(button.attr('data-filter-action'));
                var value = $('input[data-filter-field="' + button.attr('data-filter-action') + '"]').val().trim().toLowerCase();
                if (value.length > 0) {
                    params.set(button.attr('data-filter-action'), [value]);
                    params.delete('page');
                }
                return params;
            }, $(this), ev);
        });

        $('button[data-filter]').click(function(ev) {
            handleParams(function(target, ev) {
                var button = target;
                var params = ranking.parseHash(location.hash);
                var param = params.get(button.attr('data-filter'));
                var value = button.attr('data-value');
                var index = param ? param.indexOf(value) : -1;
                if (index > -1) {
                    param = param.filter(function(v) { return v != value; });
                } else {
                    if (!param || !ev.ctrlKey) {
                        param = [];
                    }
                    param.push(value);
                }
                if (!param.length) {
                    params.delete(button.attr('data-filter'));
                } else {
                    params.set(button.attr('data-filter'), param);
                }
                params.delete('page');
                return params;
            }, $(this), ev);
        });

        $('button[data-clear]').click(function() {
            handleParams(function(target, ev) {
                var button = target;
                var params = ranking.parseHash(location.hash);
                params.delete(button.attr('data-clear'));
                params.delete('page');
                return params;
            }, $(this));
        });

        $(document).on('click', 'button.paginator-prev, button.paginator-next, button.paginator-page', ranking.changePage);
    }

};

ranking.init();
<|endoftext|>


<commit_before>/* do not change this file, it is auto generated by storybook. */

import {
  configure,
  addDecorator,
  addParameters,
  addArgsEnhancer,
} from "@storybook/react-native";

import "@storybook/addon-ondevice-actions/register";
import "@storybook/addon-ondevice-controls/register";

import { argsEnhancers } from "@storybook/addon-actions/dist/modern/preset/addArgs";

import { decorators, parameters } from "./preview";

if (decorators) {
  decorators.forEach((decorator) => addDecorator(decorator));
}

if (parameters) {
  addParameters(parameters);
}

argsEnhancers.forEach((enhancer) => addArgsEnhancer(enhancer));

const getStories = () => {
  return [
    require("../app/Components/ArtsyKeyboardAvoidingView.stories.tsx"),
    require("../app/Components/ArtworkGrids/ArtworksFilterHeader.stories.tsx"),
    require("../app/Components/PopoverMessage/PopoverMessage.stories.tsx"),
    require("../app/Components/ReadMore.stories.tsx"),
    require("../palette/atoms/BackButton/BackButton.stories.tsx"),
    require("../palette/atoms/colors.stories.tsx"),
    require("../palette/atoms/space.stories.tsx"),
    require("../palette/atoms/Spacer/Spacer.stories.tsx"),
    require("../palette/elements/Avatar/Avatar.stories.tsx"),
    require("../palette/elements/Button/Button.stories.tsx"),
    require("../palette/elements/Checkbox/Checkbox.stories.tsx"),
    require("../palette/elements/CollapsibleMenuItem/CollapsibleMenuItem.stories.tsx"),
    require("../palette/elements/Dialog/Dialog.stories.tsx"),
    require("../palette/elements/Header/Header.stories.tsx"),
    require("../palette/elements/Histogram/Histogram.stories.tsx"),
    require("../palette/elements/Input/Input.stories.tsx"),
    require("../palette/elements/List/List.stories.tsx"),
    require("../palette/elements/Message/Message.stories.tsx"),
    require("../palette/elements/Pill/Pill.stories.tsx"),
    require("../palette/elements/ProgressBar/ProgressBar.stories.tsx"),
    require("../palette/elements/Radio/RadioButton.stories.tsx"),
    require("../palette/elements/Select/Select.stories.tsx"),
    require("../palette/elements/SimpleMessage/SimpleMessage.stories.tsx"),
    require("../palette/elements/Tabs/Tabs.stories.tsx"),
    require("../palette/elements/Text/Text.stories.tsx"),
    require("../palette/elements/VisualClue/VisualClue.stories.tsx"),
    require("../palette/icons.stories.tsx"),
    require("../palette/organisms/screenStructure/Screen.stories.tsx"),
  ];
};

configure(getStories, module, false);
<commit_msg>yarn prettier<commit_after>/* do not change this file, it is auto generated by storybook. */

import { configure, addDecorator, addParameters, addArgsEnhancer } from "@storybook/react-native"

import "@storybook/addon-ondevice-actions/register"
import "@storybook/addon-ondevice-controls/register"

import { argsEnhancers } from "@storybook/addon-actions/dist/modern/preset/addArgs"

import { decorators, parameters } from "./preview"

if (decorators) {
  decorators.forEach((decorator) => addDecorator(decorator))
}

if (parameters) {
  addParameters(parameters)
}

argsEnhancers.forEach((enhancer) => addArgsEnhancer(enhancer))

const getStories = () => {
  return [
    require("../app/Components/ArtsyKeyboardAvoidingView.stories.tsx"),
    require("../app/Components/ArtworkGrids/ArtworksFilterHeader.stories.tsx"),
    require("../app/Components/PopoverMessage/PopoverMessage.stories.tsx"),
    require("../app/Components/ReadMore.stories.tsx"),
    require("../palette/atoms/BackButton/BackButton.stories.tsx"),
    require("../palette/atoms/colors.stories.tsx"),
    require("../palette/atoms/space.stories.tsx"),
    require("../palette/atoms/Spacer/Spacer.stories.tsx"),
    require("../palette/elements/Avatar/Avatar.stories.tsx"),
    require("../palette/elements/Button/Button.stories.tsx"),
    require("../palette/elements/Checkbox/Checkbox.stories.tsx"),
    require("../palette/elements/CollapsibleMenuItem/CollapsibleMenuItem.stories.tsx"),
    require("../palette/elements/Dialog/Dialog.stories.tsx"),
    require("../palette/elements/Header/Header.stories.tsx"),
    require("../palette/elements/Histogram/Histogram.stories.tsx"),
    require("../palette/elements/Input/Input.stories.tsx"),
    require("../palette/elements/List/List.stories.tsx"),
    require("../palette/elements/Message/Message.stories.tsx"),
    require("../palette/elements/Pill/Pill.stories.tsx"),
    require("../palette/elements/ProgressBar/ProgressBar.stories.tsx"),
    require("../palette/elements/Radio/RadioButton.stories.tsx"),
    require("../palette/elements/Select/Select.stories.tsx"),
    require("../palette/elements/SimpleMessage/SimpleMessage.stories.tsx"),
    require("../palette/elements/Tabs/Tabs.stories.tsx"),
    require("../palette/elements/Text/Text.stories.tsx"),
    require("../palette/elements/VisualClue/VisualClue.stories.tsx"),
    require("../palette/icons.stories.tsx"),
    require("../palette/organisms/screenStructure/Screen.stories.tsx"),
  ]
}

configure(getStories, module, false)
<|endoftext|>


<commit_before>// Load in our dependencies
var fs = require('fs');
var $ = require('jquery');
var _ = require('underscore');
var async = require('async');
var Application = require('../../../browser/js/application');
var GlobalState = require('../../../browser/js/global-state');
var diagonalBase64 = fs.readFileSync(__dirname + '/../../test-files/diagonal.png', 'base64');
var diagonalDotDiffBase64 = fs.readFileSync(__dirname + '/../../test-files/diagonal-dot-diff.png', 'base64');
var dotBase64 = fs.readFileSync(__dirname + '/../../test-files/dot.png', 'base64');
var largeDiagonalBase64DataUri = require('./image').getLargeDiagonalBase64DataUri();
var largeDotBase64DataUri = require('./image').getLargeDotBase64DataUri();

// Disable transitions for Bootstrap
// https://github.com/twbs/bootstrap/blob/v3.3.7/js/transition.js#L45-L46
// DEV: This is used to speed up tests and make screenshots consistent
$(function handleReady () {
  $.support.transition = false;
});

// Run our DOM bindings once
Application.bindOnce();

// Define various image set configurations
var base64Prefix = 'data:image/png;base64,';
exports.IMAGE_SET_EQUAL = {
  id: 'mock-img-equal',
  currentImageUrl: base64Prefix + diagonalBase64,
  diffImageUrl: base64Prefix + diagonalBase64,
  refImageUrl: base64Prefix + diagonalBase64,
  imagesEqual: true
};
exports.IMAGE_SET_NOT_EQUAL = {
  id: 'mock-img-not-equal',
  currentImageUrl: base64Prefix + diagonalBase64,
  diffImageUrl: base64Prefix + diagonalDotDiffBase64,
  refImageUrl: base64Prefix + dotBase64,
  imagesEqual: false
};
exports.IMAGE_SET_NOT_EQUAL2 = _.defaults({
  id: 'mock-img-not-equal2'
}, exports.IMAGE_SET_NOT_EQUAL);
exports.IMAGE_SET_LARGE_EQUAL = {
  id: 'mock-img-equal-large',
  currentImageUrl: largeDiagonalBase64DataUri,
  diffImageUrl: largeDiagonalBase64DataUri,
  refImageUrl: largeDiagonalBase64DataUri,
  imagesEqual: true
};
exports.IMAGE_SET_LARGE_NOT_EQUAL = {
  id: 'mock-img-not-equal-large',
  currentImageUrl: largeDiagonalBase64DataUri,
  // DEV: Technically we should be using a large diff image but that's generated on disk
  //   which we avoid doing for large images to prevent bloating our repo
  diffImageUrl: largeDotBase64DataUri,
  refImageUrl: largeDotBase64DataUri,
  imagesEqual: false
};
exports.IMAGE_SETS = {
  DEFAULT: [exports.IMAGE_SET_EQUAL, exports.IMAGE_SET_NOT_EQUAL],
  MULTIPLE_NOT_EQUAL: [exports.IMAGE_SET_EQUAL, exports.IMAGE_SET_NOT_EQUAL, exports.IMAGE_SET_NOT_EQUAL2]
};
exports.IMAGE_SETS.PERFORMANCE = [];
(function generatePerformanceImageSet () {
  var i = 0;
  for (; i < 10; i += 2) {
    exports.IMAGE_SETS.PERFORMANCE.push(_.defaults({
      id: 'mock-img-equal-' + i
    }, exports.IMAGE_SET_LARGE_EQUAL));
    exports.IMAGE_SETS.PERFORMANCE.push(_.defaults({
      id: 'mock-img-not-equal-' + (i + 1)
    }, exports.IMAGE_SET_LARGE_NOT_EQUAL));
  }
}());

// Define our application utils
// DEV: If `applicationUtils` needs to be reused, place it into a `utils` folder,
//   add `(imageSets)` as an option, and define constants of `imageSets` for us to use in utils
var loggedDebugNotice = false;
exports.init = function (imageSetInfoArr) {
  before(function resetGlobalState () {
    GlobalState.reset();
  });
  before(function createApplication () {
    // DEV: We add `className` for nicer looking screenshots
    this.containerEl = document.createElement('div');
    this.containerEl.className = 'container-fluid';
    document.body.appendChild(this.containerEl);
    this.app = new Application(this.containerEl, imageSetInfoArr || exports.IMAGE_SETS.DEFAULT);
  });
  before(function waitForImagesToLoad (done) {
    // Wait for images to load to prevent canvas and screenshot issues
    var imgElArr = this.containerEl.querySelectorAll('img');
    async.forEach(imgElArr, function waitForImageToLoad (imgEl, cb) {
      // If the image is already loaded, then callback in a second (prevent zalgo)
      // https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement
      if (imgEl.complete) {
        return process.nextTick(cb);
      }

      // Set up onload/onerror bindings
      imgEl.onload = _.once(function () {
        cb(null);
      });
      imgEl.onerror = cb;
    }, done);
  });
  after(function cleanup () {
    // If we are on the debug page, expose everything
    if (window.location.pathname === '/debug.html')  {
      // If only 1 test is running, expose everything and stop
      if (window.mocha.options.hasOnly) {
        console.info('/debug.html and `hasOnly` detected, ' +
          'exposing `window.app` and `window.containerEl`');
        window.app = this.app;
        window.containerEl = this.containerEl;
        return;
      }

      // Notify user about debugging
      if (!loggedDebugNotice) {
        console.info('/debug.html detected but no `.only`. ' +
          'To visually debug tests/stop cleanup, add a `.only` to a test suite');
        loggedDebugNotice = true;
      }
    }

    // Perform our cleanup
    document.body.removeChild(this.containerEl);
    delete this.app;
    delete this.containerEl;
  });
};

exports._screenshot = function (filename) {
  // Call our to `onCallback` handler
  // https://github.com/karma-runner/karma/blob/v1.3.0/context/main.js#L5
  // DEV: We use `window.opener` as we're in a new window
  if (window.callPhantom) {
    window.callPhantom({type: 'render', filename: filename});
  }
};
exports.screenshot = function (filename) {
  before(function screenshotFn () {
    exports._screenshot.call(this, filename);
  });
};

// DEV: This uses the same technique as our application so we can do direct comparisons
// DEV: We could do pixel based comparisons but that's overkill
exports.getBase64Content = function (imgEl) {
  // Create our elements
  var base64CanvasEl = document.createElement('canvas');
  var base64Context = base64CanvasEl.getContext('2d');

  // Resize our canvas to target size
  base64CanvasEl.width = imgEl.naturalWidth;
  base64CanvasEl.height = imgEl.naturalHeight;

  // Draw our image and return its data URL
  // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage
  // https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL
  // DEV: We use `image/png` for lossless encoding which is necessary for visual comparison
  base64Context.drawImage(imgEl, 0, 0);
  return base64CanvasEl.toDataURL('image/png');
};
<commit_msg>Bumped up test suite but disliking experience, going to do simple benchmark<commit_after>// Load in our dependencies
var fs = require('fs');
var $ = require('jquery');
var _ = require('underscore');
var async = require('async');
var Application = require('../../../browser/js/application');
var GlobalState = require('../../../browser/js/global-state');
var diagonalBase64 = fs.readFileSync(__dirname + '/../../test-files/diagonal.png', 'base64');
var diagonalDotDiffBase64 = fs.readFileSync(__dirname + '/../../test-files/diagonal-dot-diff.png', 'base64');
var dotBase64 = fs.readFileSync(__dirname + '/../../test-files/dot.png', 'base64');
var largeDiagonalBase64DataUri = require('./image').getLargeDiagonalBase64DataUri();
var largeDotBase64DataUri = require('./image').getLargeDotBase64DataUri();

// Disable transitions for Bootstrap
// https://github.com/twbs/bootstrap/blob/v3.3.7/js/transition.js#L45-L46
// DEV: This is used to speed up tests and make screenshots consistent
$(function handleReady () {
  $.support.transition = false;
});

// Run our DOM bindings once
Application.bindOnce();

// Define various image set configurations
var base64Prefix = 'data:image/png;base64,';
exports.IMAGE_SET_EQUAL = {
  id: 'mock-img-equal',
  currentImageUrl: base64Prefix + diagonalBase64,
  diffImageUrl: base64Prefix + diagonalBase64,
  refImageUrl: base64Prefix + diagonalBase64,
  imagesEqual: true
};
exports.IMAGE_SET_NOT_EQUAL = {
  id: 'mock-img-not-equal',
  currentImageUrl: base64Prefix + diagonalBase64,
  diffImageUrl: base64Prefix + diagonalDotDiffBase64,
  refImageUrl: base64Prefix + dotBase64,
  imagesEqual: false
};
exports.IMAGE_SET_NOT_EQUAL2 = _.defaults({
  id: 'mock-img-not-equal2'
}, exports.IMAGE_SET_NOT_EQUAL);
exports.IMAGE_SET_LARGE_EQUAL = {
  id: 'mock-img-equal-large',
  currentImageUrl: largeDiagonalBase64DataUri,
  diffImageUrl: largeDiagonalBase64DataUri,
  refImageUrl: largeDiagonalBase64DataUri,
  imagesEqual: true
};
exports.IMAGE_SET_LARGE_NOT_EQUAL = {
  id: 'mock-img-not-equal-large',
  currentImageUrl: largeDiagonalBase64DataUri,
  // DEV: Technically we should be using a large diff image but that's generated on disk
  //   which we avoid doing for large images to prevent bloating our repo
  diffImageUrl: largeDotBase64DataUri,
  refImageUrl: largeDotBase64DataUri,
  imagesEqual: false
};
exports.IMAGE_SETS = {
  DEFAULT: [exports.IMAGE_SET_EQUAL, exports.IMAGE_SET_NOT_EQUAL],
  MULTIPLE_NOT_EQUAL: [exports.IMAGE_SET_EQUAL, exports.IMAGE_SET_NOT_EQUAL, exports.IMAGE_SET_NOT_EQUAL2]
};
exports.IMAGE_SETS.PERFORMANCE = [];
(function generatePerformanceImageSet () {
  var i = 0;
  for (; i < 100; i += 2) {
    exports.IMAGE_SETS.PERFORMANCE.push(_.defaults({
      id: 'mock-img-equal-' + i
    }, exports.IMAGE_SET_LARGE_EQUAL));
    exports.IMAGE_SETS.PERFORMANCE.push(_.defaults({
      id: 'mock-img-not-equal-' + (i + 1)
    }, exports.IMAGE_SET_LARGE_NOT_EQUAL));
  }
}());

// Define our application utils
// DEV: If `applicationUtils` needs to be reused, place it into a `utils` folder,
//   add `(imageSets)` as an option, and define constants of `imageSets` for us to use in utils
var loggedDebugNotice = false;
exports.init = function (imageSetInfoArr) {
  before(function resetGlobalState () {
    GlobalState.reset();
  });
  before(function createApplication () {
    // DEV: We add `className` for nicer looking screenshots
    this.containerEl = document.createElement('div');
    this.containerEl.className = 'container-fluid';
    document.body.appendChild(this.containerEl);
    this.app = new Application(this.containerEl, imageSetInfoArr || exports.IMAGE_SETS.DEFAULT);
  });
  before(function waitForImagesToLoad (done) {
    // Wait for images to load to prevent canvas and screenshot issues
    var imgElArr = this.containerEl.querySelectorAll('img');
    async.forEach(imgElArr, function waitForImageToLoad (imgEl, cb) {
      // If the image is already loaded, then callback in a second (prevent zalgo)
      // https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement
      if (imgEl.complete) {
        return process.nextTick(cb);
      }

      // Set up onload/onerror bindings
      imgEl.onload = _.once(function () {
        cb(null);
      });
      imgEl.onerror = cb;
    }, done);
  });
  after(function cleanup () {
    // If we are on the debug page, expose everything
    if (window.location.pathname === '/debug.html')  {
      // If only 1 test is running, expose everything and stop
      if (window.mocha.options.hasOnly) {
        console.info('/debug.html and `hasOnly` detected, ' +
          'exposing `window.app` and `window.containerEl`');
        window.app = this.app;
        window.containerEl = this.containerEl;
        return;
      }

      // Notify user about debugging
      if (!loggedDebugNotice) {
        console.info('/debug.html detected but no `.only`. ' +
          'To visually debug tests/stop cleanup, add a `.only` to a test suite');
        loggedDebugNotice = true;
      }
    }

    // Perform our cleanup
    document.body.removeChild(this.containerEl);
    delete this.app;
    delete this.containerEl;
  });
};

exports._screenshot = function (filename) {
  // Call our to `onCallback` handler
  // https://github.com/karma-runner/karma/blob/v1.3.0/context/main.js#L5
  // DEV: We use `window.opener` as we're in a new window
  if (window.callPhantom) {
    window.callPhantom({type: 'render', filename: filename});
  }
};
exports.screenshot = function (filename) {
  before(function screenshotFn () {
    exports._screenshot.call(this, filename);
  });
};

// DEV: This uses the same technique as our application so we can do direct comparisons
// DEV: We could do pixel based comparisons but that's overkill
exports.getBase64Content = function (imgEl) {
  // Create our elements
  var base64CanvasEl = document.createElement('canvas');
  var base64Context = base64CanvasEl.getContext('2d');

  // Resize our canvas to target size
  base64CanvasEl.width = imgEl.naturalWidth;
  base64CanvasEl.height = imgEl.naturalHeight;

  // Draw our image and return its data URL
  // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage
  // https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL
  // DEV: We use `image/png` for lossless encoding which is necessary for visual comparison
  base64Context.drawImage(imgEl, 0, 0);
  return base64CanvasEl.toDataURL('image/png');
};
<|endoftext|>


<commit_before>import test from 'tape'
import React from 'react'
import { shallow } from 'enzyme'
import { Success } from '../../../src/client/containers/success.js'
import Header from '../../../src/client/components/header.js'
import BerkeleyLogo from '../../../src/client/components/berkeley_logo.js'
import ThanksMessage from '../../../src/client/components/thanks_message.js'
import SuccessIllustration from '../../../src/client/components/success_illustration.js'
import HomeBtn from '../../../src/client/components/home_button.js'

test('<Success />', t => {
  const wrapper = shallow(<Success />)
  t.plan(5)
  t.equal(wrapper.find(Header).length, 1, 'has Header')
  t.equal(wrapper.find(BerkeleyLogo).length, 1, 'has Berkeley logo')
  t.equal(wrapper.find(ThanksMessage).length, 1, 'has thanks message')
  t.equal(
    wrapper.find(SuccessIllustration).length,
    1,
    'has success illustration'
  )
  t.equal(wrapper.find(HomeBtn).length, 1, 'has home button')
  t.end()
})
<commit_msg>adds extra tests for success container related #117<commit_after>import test from "tape";
import React from "react";
import { shallow } from "enzyme";
import { Success } from "../../../src/client/containers/success.js";
import Header from "../../../src/client/components/header.js";
import BerkeleyLogo from "../../../src/client/components/berkeley_logo.js";
import ThanksMessage from "../../../src/client/components/thanks_message.js";
import SuccessIllustration from "../../../src/client/components/success_illustration.js";
import HomeBtn from "../../../src/client/components/home_button.js";

test("<Success />", t => {
  const wrapper = shallow(<Success />);
  t.plan(5);
  t.equal(wrapper.find(Header).length, 1, "has Header");
  t.equal(wrapper.find(BerkeleyLogo).length, 1, "has Berkeley logo");
  t.equal(wrapper.find(ThanksMessage).length, 1, "has thanks message");
  t.equal(
    wrapper.find(SuccessIllustration).length,
    1,
    "has success illustration"
  );
  t.equal(wrapper.find(HomeBtn).length, 1, "has home button");
  t.end();
});

test("<Success /> with error", t => {
  const wrapper = shallow(<Success payload={{ response: 500 }} />);
  t.equal(wrapper.find(ThanksMessage).length, 0, "No thanks message shown");
  t.equal(wrapper.find("p").length, 1, "Shows error message instead");
  t.end();
});
<|endoftext|>


<commit_before>require('buster').spec.expose();
var expect = require('buster').expect;
var assertSame = require('../helper/stream-helper').assertSame;

var concatMap = require('../../lib/combinator/concatMap');
var delay = require('../../lib/combinator/delay').delay;
var concat = require('../../lib/combinator/build').concat;
var take = require('../../lib/combinator/slice').take;
var drain = require('../../lib/combinator/observe').drain;
var core = require('../../lib/source/core');
var fromArray = require('../../lib/source/fromArray').fromArray;
var Stream = require('../../lib/Stream');

var te = require('../helper/testEnv');
var FakeDisposeSource = require('../helper/FakeDisposeSource');

var streamOf = core.of;
var never = core.never;

var sentinel = { value: 'sentinel' };

function identity(x) {
	return x;
}

describe('concatMap', function() {

	it('should satisfy associativity', function() {
		// m.concatMap(f).concatMap(g) ~= m.concatMap(function(x) { return f(x).concatMap(g); })
		function f(x) { return streamOf(x + 'f'); }
		function g(x) { return streamOf(x + 'g'); }

		var m = streamOf('m');

		return assertSame(
			concatMap.concatMap(function(x) { return concatMap.concatMap(g, f(x)); }, m),
			concatMap.concatMap(g, concatMap.concatMap(f, m))
		);
	});

	it('should concatenate', function() {
		var s1 = [{ time: 2, value: 2 }, { time: 3, value: 3 }];
		var s2 = [{ time: 1, value: 1 }];
		var s3 = [{ time: 0, value: 0 }];
		var s = concatMap.concatMap(te.atTimes, fromArray([s1, s2, s3]));

		return te.collectEvents(s, te.ticks(5))
			.then(function(events) {
				expect(events).toEqual([
					{ time: 2, value: 2 },
					{ time: 3, value: 3 },
					{ time: 4, value: 1 },
					{ time: 4, value: 0 }
				]);
			});
	});

	it('should dispose outer stream', function() {
		var dispose = this.spy();
		var inner = streamOf(sentinel);
		var outer = streamOf(inner);

		var s = concatMap.concatMap(identity, new Stream(new FakeDisposeSource(dispose, outer.source)));

		return drain(s).then(function() {
			expect(dispose).toHaveBeenCalled();
		});
	});

	it('should dispose inner stream', function() {
		var dispose = this.spy();
		var inner = new Stream(new FakeDisposeSource(dispose, streamOf(sentinel).source));

		var s = concatMap.concatMap(identity, streamOf(inner));

		return drain(s).then(function() {
			expect(dispose).toHaveBeenCalled();
		});
	});

	it('should dispose inner stream immediately', function() {
		var s = streamOf(concat(streamOf(1), never()));

		return drain(take(1, concatMap.concatMap(identity, s))).then(function() {
			expect(true).toBe(true);
		});
	});

	it('should dispose all inner streams', function() {

		var values = [1,2,3];
		var spies = values.map(function() {
			return this.spy();
		}, this);

		var inners = values.map(function(x, i) {
			return new Stream(new FakeDisposeSource(spies[i], streamOf(x).source));
		});

		var s = concatMap.concatMap(identity, fromArray(inners));

		return drain(s).then(function() {
			spies.forEach(function(spy) {
				expect(spy).toHaveBeenCalledOnce();
			});
		});
	});
});
<commit_msg>Add test to verify concatMap maps lazily. Fails on master<commit_after>require('buster').spec.expose();
var expect = require('buster').expect;
var assertSame = require('../helper/stream-helper').assertSame;

var concatMap = require('../../lib/combinator/concatMap');
var delay = require('../../lib/combinator/delay').delay;
var concat = require('../../lib/combinator/build').concat;
var take = require('../../lib/combinator/slice').take;
var drain = require('../../lib/combinator/observe').drain;
var core = require('../../lib/source/core');
var fromArray = require('../../lib/source/fromArray').fromArray;
var Stream = require('../../lib/Stream');

var te = require('../helper/testEnv');
var FakeDisposeSource = require('../helper/FakeDisposeSource');

var streamOf = core.of;
var never = core.never;

var sentinel = { value: 'sentinel' };

function identity(x) {
	return x;
}

describe('concatMap', function() {

	it('should satisfy associativity', function() {
		// m.concatMap(f).concatMap(g) ~= m.concatMap(function(x) { return f(x).concatMap(g); })
		function f(x) { return streamOf(x + 'f'); }
		function g(x) { return streamOf(x + 'g'); }

		var m = streamOf('m');

		return assertSame(
			concatMap.concatMap(function(x) { return concatMap.concatMap(g, f(x)); }, m),
			concatMap.concatMap(g, concatMap.concatMap(f, m))
		);
	});

	it('should concatenate', function() {
		var s1 = [{ time: 2, value: 2 }, { time: 3, value: 3 }];
		var s2 = [{ time: 1, value: 1 }];
		var s3 = [{ time: 0, value: 0 }];
		var s = concatMap.concatMap(te.atTimes, fromArray([s1, s2, s3]));

		return te.collectEvents(s, te.ticks(5))
			.then(function(events) {
				expect(events).toEqual([
					{ time: 2, value: 2 },
					{ time: 3, value: 3 },
					{ time: 4, value: 1 },
					{ time: 4, value: 0 }
				]);
			});
	});

	it('should map lazily', function() {
		var s1 = te.atTimes([{ time: 0, value: 0 }, { time: 1, value: 1 }]);

		var env = te.ticks(4);
		var s = concatMap.concatMap(function(x) {
			return te.atTimes([{ time: 2, value: env.scheduler.now() }]);
		}, s1)

		return te.collectEvents(s, env)
			.then(function(events) {
				expect(events).toEqual([
					{ time: 2, value: 0 },
					{ time: 4, value: 2 }
				]);
			});
	})

	it('should dispose outer stream', function() {
		var dispose = this.spy();
		var inner = streamOf(sentinel);
		var outer = streamOf(inner);

		var s = concatMap.concatMap(identity, new Stream(new FakeDisposeSource(dispose, outer.source)));

		return drain(s).then(function() {
			expect(dispose).toHaveBeenCalled();
		});
	});

	it('should dispose inner stream', function() {
		var dispose = this.spy();
		var inner = new Stream(new FakeDisposeSource(dispose, streamOf(sentinel).source));

		var s = concatMap.concatMap(identity, streamOf(inner));

		return drain(s).then(function() {
			expect(dispose).toHaveBeenCalled();
		});
	});

	it('should dispose inner stream immediately', function() {
		var s = streamOf(concat(streamOf(1), never()));

		return drain(take(1, concatMap.concatMap(identity, s))).then(function() {
			expect(true).toBe(true);
		});
	});

	it('should dispose all inner streams', function() {

		var values = [1,2,3];
		var spies = values.map(function() {
			return this.spy();
		}, this);

		var inners = values.map(function(x, i) {
			return new Stream(new FakeDisposeSource(spies[i], streamOf(x).source));
		});

		var s = concatMap.concatMap(identity, fromArray(inners));

		return drain(s).then(function() {
			spies.forEach(function(spy) {
				expect(spy).toHaveBeenCalledOnce();
			});
		});
	});
});
<|endoftext|>


<commit_before>/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var gadgets = gadgets || {};

/**
 * @fileoverview Provides remote content retrieval facilities.
 *     Available to every gadget.
 */

/**
 * @static
 * @class Provides remote content retrieval functions.
 * @name gadgets.io
 */

gadgets.io = function() {
  /**
   * Holds configuration-related data such as proxy urls.
   */
  var config = {};

  /**
   * Internal facility to create an xhr request.
   */
  function makeXhr() {
    if (window.XMLHttpRequest) {
      return new XMLHttpRequest();
    } else if (window.ActiveXObject) {
      var x = new ActiveXObject("Msxml2.XMLHTTP");
      if (!x) {
        x = new ActiveXObject("Microsoft.XMLHTTP");
      }
      return x;
    }
  }

  var UNPARSEABLE_CRUFT = "throw 1; < don't be evil' >";

  /**
   * Handles XHR callback processing.
   *
   * @param {String} url
   * @param {Function} callback
   * @param {Object} params
   * @param {Object} xobj
   */
  function processResponse(url, callback, params, xobj) {
    if (xobj.readyState !== 4) {
      return;
    }
    if (xobj.status !== 200) {
      // TODO Need to work on standardizing errors
      callback({errors : ["Error " + xobj.status] });
      return;
    }
    var txt = xobj.responseText;
    // remove unparseable cruft.
    // TODO: really remove this by eliminating it. It's not any real security
    //    to begin with, and we can solve this problem by using post requests
    //    and / or passing the url in the http headers.
    txt = txt.substr(UNPARSEABLE_CRUFT.length);
    var data = gadgets.json.parse(txt);
    data = data[url];
    var resp = {
     text: data.body,
     errors: []
    };
    switch (params.CONTENT_TYPE) {
      case "JSON":
      case "FEED":
        resp.data = gadgets.json.parse(resp.text);
        if (!resp.data) {
          resp.errors = ["failed to parse JSON"];
          resp.data = null;
        }
        break;
     case "DOM":
      var dom;
      if (window.ActiveXObject) {
        dom = new ActiveXObject("Microsoft.XMLDOM");
        dom.async = false;
        dom.validateOnParse = false;
        dom.resolveExternals = false;
        if (!dom.loadXML(resp.text)) {
          resp.errors = ["failed to parse XML"];
        } else {
          resp.data = dom;
        }
      } else {
        var parser = new DOMParser();
        dom = parser.parseFromString(resp.text, "text/xml");
        if ("parsererror" == dom.documentElement.nodeName) {
          resp.errors = ["failed to parse XML"];
        } else {
          resp.data = dom;
        }
      }
      break;
    default:
      resp.data = resp.text;
      break;
   }
   callback(resp);
  }

  return /** @scope gadgets.io */ {
    /**
     * Fetches content from the provided URL and feeds that content into the
     * callback function.
     *
     * Example:
     *

     * gadgets.io.makeRequest(url, fn,
     *    {contentType: gadgets.io.ContentType.FEED});
     *

     *
     * @param {String} url The URL where the content is located
     * @param {Function} callback The function to call with the data from the
     *     URL once it is fetched
     * @param {Map.<gadgets.io.RequestParameters, Object>} opt_params
     *     Additional
     *     parameters
     *     to pass to the request
     *
     * @member gadgets.io
     */
    makeRequest : function (url, callback, opt_params) {
      // TODO: This method also needs to respect all members of
      // gadgets.io.RequestParameters, and validate them.
      var xhr = makeXhr();
      var params = opt_params || {};
      var newUrl = config.jsonProxyUrl.replace("%url%",
          encodeURIComponent(url));

      // Check if authorization is requested
      if (opt_params && opt_params.AUTHORIZATION &&
          gadgets.io.AuthorizationType[opt_params.AUTHORIZATION.toUpperCase()]
              != gadgets.io.AuthorizationType.NONE) {
        newUrl += "&authz=" + opt_params.AUTHORIZATION.toLowerCase();
        // Add the security-token if available
        if (gadgets.util.getUrlParameters()["st"]) {
          newUrl += "&st=" + gadgets.util.getUrlParameters()["st"];
        }
      }
      // TODO: Fetcher cannot distinguish between GET & POST yet.
      xhr.open(params.METHOD || "GET", newUrl, true);
      if (callback) {
        xhr.onreadystatechange = gadgets.util.makeClosure(null,
            processResponse, url, callback, params, xhr);
      }
      if (params.METHOD == "POST") {
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        if (params.postData) {
          xhr.send("postData=" + encodeURIComponent(params.postData));
        } else {
          xhr.send("postData=");
        }
      } else {
        xhr.send();
      }
    },

    /**
     * Converts an input object into a URL-encoded data string.
     * (key=value&...)
     *
     * @param {Object} fields The post fields you wish to encode
     * @return {String} The processed post data; this will include a trailing
     *    ampersand (&)
     *
     * @member gadgets.io
     */
    encodeValues : function (fields) {
      var buf = [];
      for (var i in fields) {
        buf.push(encodeURIComponent(i));
        buf.push("=");
        buf.push(encodeURIComponent(fields[i]));
        buf.push("&");
      }
      return buf.join("");
    },

    /**
     * Gets the proxy version of the passed-in URL.
     *
     * @param {String} url The URL to get the proxy URL for
     * @return {String} The proxied version of the URL
     *
     * @member gadgets.io
     */
    getProxyUrl : function (url) {
      return config.proxyUrl.replace("%url%", encodeURIComponent(url));
    },

    /**
     * Initializes fetchers
     *
     * @param {Object} configuration Configuration settings
     *     Required:
     *       - proxyUrl: The url for content proxy requests. Include %url%
     *           as a placeholder for the actual url.
     *       - jsonProxyUrl: The url for dynamic proxy requests. Include %url%
     *           as a placeholder for the actual url.
     * @private
     */
    init : function (configuration) {
      config = configuration;
      if (!config.proxyUrl || !config.jsonProxyUrl) {
        throw new Error("proxyUrl and jsonProxyUrl are required.");
      }
    }
  };
}();

// TODO: This can all be removed after the spec is published. This is only
// here to satisfy documentation requirements.

/**
 * @static
 * @class
 * Used by the
 *
 * gadgets.io.makeRequest() method.
 * @name gadgets.io.RequestParameters
 */
gadgets.io.RequestParameters = {
  /**
   * The method to use when fetching content from the URL;
   * defaults to MethodType.GET.
   * Specified as a
   * MethodType.
   *
   * @member gadgets.io.RequestParameters
   */
   METHOD : 'METHOD',

  /**
   * The type of content that lives at the URL;
   * defaults to ContentType.HTML.
   * Specified as a
   *
   * ContentType.
   *
   * @member gadgets.io.RequestParameters
   */
  CONTENT_TYPE : "CONTENT_TYPE",

  /**
   * The data to send to the URL using the POST method;
   * defaults to null.
   * Specified as a String.
   *
   * @member gadgets.io.RequestParameters
   */
  POST_DATA : "POST_DATA",

  /**
   * The HTTP headers to send to the URL;
   * defaults to null.
   * Specified as a Map.<String,String>.
   *
   * @member gadgets.io.RequestParameters
   */
  HEADERS : "HEADERS",

  /**
   * The type of authentication to use when fetching the content;
   * defaults to AuthorizationType.NONE.
   * Specified as an
   *
   * AuthorizationType.
   *
   * @member gadgets.io.RequestParameters
   */
  AUTHORIZATION : 'AUTHORIZATION',

  /**
   * If the content is a feed, the number of entries to fetch;
   * defaults to 3.
   * Specified as a Number.
   *
   * @member gadgets.io.RequestParameters
   */
  NUM_ENTRIES : 'NUM_ENTRIES',

  /**
   * If the content is a feed, whether to fetch summaries for that feed;
   * defaults to false.
   * Specified as a Boolean.
   *
   * @member gadgets.io.RequestParameters
   */
  GET_SUMMARIES : 'GET_SUMMARIES'
};

/**
 * @static
 * @class
 * Used by
 *
 * RequestParameters.
 * @name gadgets.io.MethodType
 */
gadgets.io.MethodType = {
  /**
   * The default type.
   * @member gadgets.io.MethodType
   */
  GET : 'GET',

  /**
   * Not supported by all containers.
   * @member gadgets.io.MethodType
   */
  POST : 'POST',

  /**
   * Not supported by all containers.
   * @member gadgets.io.MethodType
   */
  PUT : 'PUT',

  /**
   * Not supported by all containers.
   * @member gadgets.io.MethodType
   */
  DELETE : 'DELETE',

  /**
   * Not supported by all containers.
   * @member gadgets.io.MethodType
   */
  HEAD : 'HEAD'
};

/**
 * @static
 * @class
 * Used by
 *
 * RequestParameters.
 * @name gadgets.io.ContentType
 */
gadgets.io.ContentType = {
  /**
   * Returns text; used for fetching HTML.
   * @member gadgets.io.ContentType
   */
  TEXT : 'TEXT',

  /**
   * Returns a DOM object; used for fetching XML.
   * @member gadgets.io.ContentType
   */
  DOM : 'DOM',

  /**
   * Returns a JSON object.
   * @member gadgets.io.ContentType
   */
  JSON : 'JSON',

  /**
   * Returns a JSON representation of a feed.
   * @member gadgets.io.ContentType
   */
  FEED : 'FEED'
};

/**
 * @static
 * @class
 * Used by
 *
 * RequestParameters.
 * @name gadgets.io.AuthorizationType
 */
gadgets.io.AuthorizationType = {
  /**
   * No authorization.
   * @member gadgets.io.AuthorizationType
   */
  NONE : 'NONE',

  /**
   * The request will be signed by the container.
   * @member gadgets.io.AuthorizationType
   */
  SIGNED : 'SIGNED',

  /**
   * The container will use full authentication.
   * @member gadgets.io.AuthorizationType
   */
  AUTHENTICATED : 'AUTHENTICATED'
};
<commit_msg>Firefox requires a null argument for xhr.send()<commit_after>/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var gadgets = gadgets || {};

/**
 * @fileoverview Provides remote content retrieval facilities.
 *     Available to every gadget.
 */

/**
 * @static
 * @class Provides remote content retrieval functions.
 * @name gadgets.io
 */

gadgets.io = function() {
  /**
   * Holds configuration-related data such as proxy urls.
   */
  var config = {};

  /**
   * Internal facility to create an xhr request.
   */
  function makeXhr() {
    if (window.XMLHttpRequest) {
      return new XMLHttpRequest();
    } else if (window.ActiveXObject) {
      var x = new ActiveXObject("Msxml2.XMLHTTP");
      if (!x) {
        x = new ActiveXObject("Microsoft.XMLHTTP");
      }
      return x;
    }
  }

  var UNPARSEABLE_CRUFT = "throw 1; < don't be evil' >";

  /**
   * Handles XHR callback processing.
   *
   * @param {String} url
   * @param {Function} callback
   * @param {Object} params
   * @param {Object} xobj
   */
  function processResponse(url, callback, params, xobj) {
    if (xobj.readyState !== 4) {
      return;
    }
    if (xobj.status !== 200) {
      // TODO Need to work on standardizing errors
      callback({errors : ["Error " + xobj.status] });
      return;
    }
    var txt = xobj.responseText;
    // remove unparseable cruft.
    // TODO: really remove this by eliminating it. It's not any real security
    //    to begin with, and we can solve this problem by using post requests
    //    and / or passing the url in the http headers.
    txt = txt.substr(UNPARSEABLE_CRUFT.length);
    var data = gadgets.json.parse(txt);
    data = data[url];
    var resp = {
     text: data.body,
     errors: []
    };
    switch (params.CONTENT_TYPE) {
      case "JSON":
      case "FEED":
        resp.data = gadgets.json.parse(resp.text);
        if (!resp.data) {
          resp.errors = ["failed to parse JSON"];
          resp.data = null;
        }
        break;
     case "DOM":
      var dom;
      if (window.ActiveXObject) {
        dom = new ActiveXObject("Microsoft.XMLDOM");
        dom.async = false;
        dom.validateOnParse = false;
        dom.resolveExternals = false;
        if (!dom.loadXML(resp.text)) {
          resp.errors = ["failed to parse XML"];
        } else {
          resp.data = dom;
        }
      } else {
        var parser = new DOMParser();
        dom = parser.parseFromString(resp.text, "text/xml");
        if ("parsererror" == dom.documentElement.nodeName) {
          resp.errors = ["failed to parse XML"];
        } else {
          resp.data = dom;
        }
      }
      break;
    default:
      resp.data = resp.text;
      break;
   }
   callback(resp);
  }

  return /** @scope gadgets.io */ {
    /**
     * Fetches content from the provided URL and feeds that content into the
     * callback function.
     *
     * Example:
     *

     * gadgets.io.makeRequest(url, fn,
     *    {contentType: gadgets.io.ContentType.FEED});
     *

     *
     * @param {String} url The URL where the content is located
     * @param {Function} callback The function to call with the data from the
     *     URL once it is fetched
     * @param {Map.<gadgets.io.RequestParameters, Object>} opt_params
     *     Additional
     *     parameters
     *     to pass to the request
     *
     * @member gadgets.io
     */
    makeRequest : function (url, callback, opt_params) {
      // TODO: This method also needs to respect all members of
      // gadgets.io.RequestParameters, and validate them.
      var xhr = makeXhr();
      var params = opt_params || {};
      var newUrl = config.jsonProxyUrl.replace("%url%",
          encodeURIComponent(url));

      // Check if authorization is requested
      if (opt_params && opt_params.AUTHORIZATION &&
          gadgets.io.AuthorizationType[opt_params.AUTHORIZATION.toUpperCase()]
              != gadgets.io.AuthorizationType.NONE) {
        newUrl += "&authz=" + opt_params.AUTHORIZATION.toLowerCase();
        // Add the security-token if available
        if (gadgets.util.getUrlParameters()["st"]) {
          newUrl += "&st=" + gadgets.util.getUrlParameters()["st"];
        }
      }
      // TODO: Fetcher cannot distinguish between GET & POST yet.
      xhr.open(params.METHOD || "GET", newUrl, true);
      if (callback) {
        xhr.onreadystatechange = gadgets.util.makeClosure(null,
            processResponse, url, callback, params, xhr);
      }
      if (params.METHOD == "POST") {
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        if (params.postData) {
          xhr.send("postData=" + encodeURIComponent(params.postData));
        } else {
          xhr.send("postData=");
        }
      } else {
        xhr.send(null);
      }
    },

    /**
     * Converts an input object into a URL-encoded data string.
     * (key=value&...)
     *
     * @param {Object} fields The post fields you wish to encode
     * @return {String} The processed post data; this will include a trailing
     *    ampersand (&)
     *
     * @member gadgets.io
     */
    encodeValues : function (fields) {
      var buf = [];
      for (var i in fields) {
        buf.push(encodeURIComponent(i));
        buf.push("=");
        buf.push(encodeURIComponent(fields[i]));
        buf.push("&");
      }
      return buf.join("");
    },

    /**
     * Gets the proxy version of the passed-in URL.
     *
     * @param {String} url The URL to get the proxy URL for
     * @return {String} The proxied version of the URL
     *
     * @member gadgets.io
     */
    getProxyUrl : function (url) {
      return config.proxyUrl.replace("%url%", encodeURIComponent(url));
    },

    /**
     * Initializes fetchers
     *
     * @param {Object} configuration Configuration settings
     *     Required:
     *       - proxyUrl: The url for content proxy requests. Include %url%
     *           as a placeholder for the actual url.
     *       - jsonProxyUrl: The url for dynamic proxy requests. Include %url%
     *           as a placeholder for the actual url.
     * @private
     */
    init : function (configuration) {
      config = configuration;
      if (!config.proxyUrl || !config.jsonProxyUrl) {
        throw new Error("proxyUrl and jsonProxyUrl are required.");
      }
    }
  };
}();

// TODO: This can all be removed after the spec is published. This is only
// here to satisfy documentation requirements.

/**
 * @static
 * @class
 * Used by the
 *
 * gadgets.io.makeRequest() method.
 * @name gadgets.io.RequestParameters
 */
gadgets.io.RequestParameters = {
  /**
   * The method to use when fetching content from the URL;
   * defaults to MethodType.GET.
   * Specified as a
   * MethodType.
   *
   * @member gadgets.io.RequestParameters
   */
   METHOD : 'METHOD',

  /**
   * The type of content that lives at the URL;
   * defaults to ContentType.HTML.
   * Specified as a
   *
   * ContentType.
   *
   * @member gadgets.io.RequestParameters
   */
  CONTENT_TYPE : "CONTENT_TYPE",

  /**
   * The data to send to the URL using the POST method;
   * defaults to null.
   * Specified as a String.
   *
   * @member gadgets.io.RequestParameters
   */
  POST_DATA : "POST_DATA",

  /**
   * The HTTP headers to send to the URL;
   * defaults to null.
   * Specified as a Map.<String,String>.
   *
   * @member gadgets.io.RequestParameters
   */
  HEADERS : "HEADERS",

  /**
   * The type of authentication to use when fetching the content;
   * defaults to AuthorizationType.NONE.
   * Specified as an
   *
   * AuthorizationType.
   *
   * @member gadgets.io.RequestParameters
   */
  AUTHORIZATION : 'AUTHORIZATION',

  /**
   * If the content is a feed, the number of entries to fetch;
   * defaults to 3.
   * Specified as a Number.
   *
   * @member gadgets.io.RequestParameters
   */
  NUM_ENTRIES : 'NUM_ENTRIES',

  /**
   * If the content is a feed, whether to fetch summaries for that feed;
   * defaults to false.
   * Specified as a Boolean.
   *
   * @member gadgets.io.RequestParameters
   */
  GET_SUMMARIES : 'GET_SUMMARIES'
};

/**
 * @static
 * @class
 * Used by
 *
 * RequestParameters.
 * @name gadgets.io.MethodType
 */
gadgets.io.MethodType = {
  /**
   * The default type.
   * @member gadgets.io.MethodType
   */
  GET : 'GET',

  /**
   * Not supported by all containers.
   * @member gadgets.io.MethodType
   */
  POST : 'POST',

  /**
   * Not supported by all containers.
   * @member gadgets.io.MethodType
   */
  PUT : 'PUT',

  /**
   * Not supported by all containers.
   * @member gadgets.io.MethodType
   */
  DELETE : 'DELETE',

  /**
   * Not supported by all containers.
   * @member gadgets.io.MethodType
   */
  HEAD : 'HEAD'
};

/**
 * @static
 * @class
 * Used by
 *
 * RequestParameters.
 * @name gadgets.io.ContentType
 */
gadgets.io.ContentType = {
  /**
   * Returns text; used for fetching HTML.
   * @member gadgets.io.ContentType
   */
  TEXT : 'TEXT',

  /**
   * Returns a DOM object; used for fetching XML.
   * @member gadgets.io.ContentType
   */
  DOM : 'DOM',

  /**
   * Returns a JSON object.
   * @member gadgets.io.ContentType
   */
  JSON : 'JSON',

  /**
   * Returns a JSON representation of a feed.
   * @member gadgets.io.ContentType
   */
  FEED : 'FEED'
};

/**
 * @static
 * @class
 * Used by
 *
 * RequestParameters.
 * @name gadgets.io.AuthorizationType
 */
gadgets.io.AuthorizationType = {
  /**
   * No authorization.
   * @member gadgets.io.AuthorizationType
   */
  NONE : 'NONE',

  /**
   * The request will be signed by the container.
   * @member gadgets.io.AuthorizationType
   */
  SIGNED : 'SIGNED',

  /**
   * The container will use full authentication.
   * @member gadgets.io.AuthorizationType
   */
  AUTHENTICATED : 'AUTHENTICATED'
};
<|endoftext|>


<commit_before>'use strict';

require('../test-support/crypto-mock-node');

var expect = require('expect.js'),
    KeyEncryptor = require('../../lib/crypto/key-encryptor'),
    asmCrypto = require('asmcrypto.js');

describe('KeyEncryptor', function() {
    var subtle = global.crypto.subtle || global.crypto.webkitSubtle;

    /* jshint camelcase:false */
    /* globals Promise */

    var data = asmCrypto.hex_to_bytes('5d18f8a5ae0e7ea86f0ad817f0c0d40656ef1da6367d8a88508b3c13cec0d7af');
    var key = asmCrypto.hex_to_bytes('ee66af917de0b0336e659fe6bd40a337d04e3c2b3635210fa16f28fb24d563ac');

    it('decrypts one round with fallback algorithm', function(done) {
        KeyEncryptor.fallbackEncrypt(data, key, 1, function(res) {
            expect(asmCrypto.bytes_to_hex(res)).to.be('46e891c182a31d005a8990ac5d61bb2124ffe5927fa008a739a9b0d217c79717');
            done();
        });
    });

    it('decrypts one round with default algorithm', function(done) {
        KeyEncryptor.encrypt(data, key, 1, function(res) {
            expect(asmCrypto.bytes_to_hex(res)).to.be('46e891c182a31d005a8990ac5d61bb2124ffe5927fa008a739a9b0d217c79717');
            done();
        });
    });

    if (typeof Promise !== 'undefined') {
        it('uses fallback encryption if webcrypto.importKey generates promise error', function (done) {
            var oldImportKey = subtle.importKey;
            subtle.importKey = function () { return new Promise(function (resolve, reject) { reject('fail'); }); };
            KeyEncryptor.encrypt(data, key, 1, function (res) {
                subtle.importKey = oldImportKey;
                expect(asmCrypto.bytes_to_hex(res)).to.be('46e891c182a31d005a8990ac5d61bb2124ffe5927fa008a739a9b0d217c79717');
                done();
            });
        });

        it('uses fallback encryption if webcrypto.encrypt generates promise error', function (done) {
            var oldEncrypt = subtle.encrypt;
            subtle.encrypt = function () { return new Promise(function (resolve, reject) { reject('fail'); }); };
            KeyEncryptor.encrypt(data, key, 1, function (res) {
                subtle.encrypt = oldEncrypt;
                expect(asmCrypto.bytes_to_hex(res)).to.be('46e891c182a31d005a8990ac5d61bb2124ffe5927fa008a739a9b0d217c79717');
                done();
            });
        });
    }

    it('uses fallback encryption if webcrypto.importKey throws an error', function (done) {
        var oldImportKey = subtle.importKey;
        subtle.importKey = function () { throw 'err'; };
        KeyEncryptor.encrypt(data, key, 1, function (res) {
            subtle.importKey = oldImportKey;
            expect(asmCrypto.bytes_to_hex(res)).to.be('46e891c182a31d005a8990ac5d61bb2124ffe5927fa008a739a9b0d217c79717');
            done();
        });
    });

    it('uses fallback encryption if webcrypto.encrypt throws an error', function (done) {
        var oldImportKey = subtle.encrypt;
        subtle.encrypt = function () { throw 'err'; };
        KeyEncryptor.encrypt(data, key, 1, function (res) {
            subtle.encrypt = oldImportKey;
            expect(asmCrypto.bytes_to_hex(res)).to.be('46e891c182a31d005a8990ac5d61bb2124ffe5927fa008a739a9b0d217c79717');
            done();
        });
    });

    it('decrypts two rounds with fallback algorithm', function(done) {
        KeyEncryptor.fallbackEncrypt(data, key, 2, function(res) {
            expect(asmCrypto.bytes_to_hex(res)).to.be('1818f732cb1a933911ec90baed252d388980cd3665e1009705e5007aa48ad916');
            done();
        });
    });

    it('decrypts two rounds with default algorithm', function(done) {
        KeyEncryptor.encrypt(data, key, 2, function(res) {
            expect(asmCrypto.bytes_to_hex(res)).to.be('1818f732cb1a933911ec90baed252d388980cd3665e1009705e5007aa48ad916');
            done();
        });
    });

    it('decrypts many rounds with fallback algorithm', function(done) {
        KeyEncryptor.fallbackEncrypt(data, key, 10021, function(res) {
            expect(asmCrypto.bytes_to_hex(res)).to.be('64d62f7ec4a363ff0fbb4520163b478ef4d0d631b690a2e7daa6bc09bca092df');
            done();
        });
    });

    it('decrypts many rounds with default algorithm', function(done) {
        KeyEncryptor.encrypt(data, key, 10021, function(res) {
            expect(asmCrypto.bytes_to_hex(res)).to.be('64d62f7ec4a363ff0fbb4520163b478ef4d0d631b690a2e7daa6bc09bca092df');
            done();
        });
    });
});
<commit_msg>Ignore subtle tests if webcrypto not available<commit_after>'use strict';

require('../test-support/crypto-mock-node');

var expect = require('expect.js'),
    KeyEncryptor = require('../../lib/crypto/key-encryptor'),
    asmCrypto = require('asmcrypto.js');

describe('KeyEncryptor', function() {
    var subtle = global.crypto ? global.crypto.subtle || global.crypto.webkitSubtle : undefined;

    /* jshint camelcase:false */
    /* globals Promise */

    var data = asmCrypto.hex_to_bytes('5d18f8a5ae0e7ea86f0ad817f0c0d40656ef1da6367d8a88508b3c13cec0d7af');
    var key = asmCrypto.hex_to_bytes('ee66af917de0b0336e659fe6bd40a337d04e3c2b3635210fa16f28fb24d563ac');

    it('decrypts one round with fallback algorithm', function(done) {
        KeyEncryptor.fallbackEncrypt(data, key, 1, function(res) {
            expect(asmCrypto.bytes_to_hex(res)).to.be('46e891c182a31d005a8990ac5d61bb2124ffe5927fa008a739a9b0d217c79717');
            done();
        });
    });

    it('decrypts one round with default algorithm', function(done) {
        KeyEncryptor.encrypt(data, key, 1, function(res) {
            expect(asmCrypto.bytes_to_hex(res)).to.be('46e891c182a31d005a8990ac5d61bb2124ffe5927fa008a739a9b0d217c79717');
            done();
        });
    });

    if (subtle !== undefined) {
        it('uses fallback encryption if webcrypto.importKey generates promise error', function (done) {
            var oldImportKey = subtle.importKey;
            subtle.importKey = function () { return new Promise(function (resolve, reject) { reject('fail'); }); };
            KeyEncryptor.encrypt(data, key, 1, function (res) {
                subtle.importKey = oldImportKey;
                expect(asmCrypto.bytes_to_hex(res)).to.be('46e891c182a31d005a8990ac5d61bb2124ffe5927fa008a739a9b0d217c79717');
                done();
            });
        });

        it('uses fallback encryption if webcrypto.encrypt generates promise error', function (done) {
            var oldEncrypt = subtle.encrypt;
            subtle.encrypt = function () { return new Promise(function (resolve, reject) { reject('fail'); }); };
            KeyEncryptor.encrypt(data, key, 1, function (res) {
                subtle.encrypt = oldEncrypt;
                expect(asmCrypto.bytes_to_hex(res)).to.be('46e891c182a31d005a8990ac5d61bb2124ffe5927fa008a739a9b0d217c79717');
                done();
            });
        });

        it('uses fallback encryption if webcrypto.importKey throws an error', function (done) {
            var oldImportKey = subtle.importKey;
            subtle.importKey = function () { throw 'err'; };
            KeyEncryptor.encrypt(data, key, 1, function (res) {
                subtle.importKey = oldImportKey;
                expect(asmCrypto.bytes_to_hex(res)).to.be('46e891c182a31d005a8990ac5d61bb2124ffe5927fa008a739a9b0d217c79717');
                done();
            });
        });

        it('uses fallback encryption if webcrypto.encrypt throws an error', function (done) {
            var oldImportKey = subtle.encrypt;
            subtle.encrypt = function () { throw 'err'; };
            KeyEncryptor.encrypt(data, key, 1, function (res) {
                subtle.encrypt = oldImportKey;
                expect(asmCrypto.bytes_to_hex(res)).to.be('46e891c182a31d005a8990ac5d61bb2124ffe5927fa008a739a9b0d217c79717');
                done();
            });
        });

    }

    it('decrypts two rounds with fallback algorithm', function(done) {
        KeyEncryptor.fallbackEncrypt(data, key, 2, function(res) {
            expect(asmCrypto.bytes_to_hex(res)).to.be('1818f732cb1a933911ec90baed252d388980cd3665e1009705e5007aa48ad916');
            done();
        });
    });

    it('decrypts two rounds with default algorithm', function(done) {
        KeyEncryptor.encrypt(data, key, 2, function(res) {
            expect(asmCrypto.bytes_to_hex(res)).to.be('1818f732cb1a933911ec90baed252d388980cd3665e1009705e5007aa48ad916');
            done();
        });
    });

    it('decrypts many rounds with fallback algorithm', function(done) {
        KeyEncryptor.fallbackEncrypt(data, key, 10021, function(res) {
            expect(asmCrypto.bytes_to_hex(res)).to.be('64d62f7ec4a363ff0fbb4520163b478ef4d0d631b690a2e7daa6bc09bca092df');
            done();
        });
    });

    it('decrypts many rounds with default algorithm', function(done) {
        KeyEncryptor.encrypt(data, key, 10021, function(res) {
            expect(asmCrypto.bytes_to_hex(res)).to.be('64d62f7ec4a363ff0fbb4520163b478ef4d0d631b690a2e7daa6bc09bca092df');
            done();
        });
    });
});
<|endoftext|>


<commit_before>import injectTapEventPlugin from 'react-tap-event-plugin';
injectTapEventPlugin();

import Calendar from 'date-picker/calendar';
import injectTheme from '../fixtures/inject-theme';
import React from 'react';
import CalendarToolbar from 'date-picker/calendar-toolbar';
import IconButton from 'icon-button';
import DateDisplay  from 'date-picker/date-display';
import DateTime from 'utils/date-time';

const TestUtils = React.addons.TestUtils;

describe(`Calendar`, () => {
    let ThemedCalendar;

    beforeEach(() => {
        ThemedCalendar = injectTheme(Calendar);
    });

    it(`should set the DateDisplay height to 64 if the mode prop is not provided (default mode is potrait)`, () => {
        let render = TestUtils.renderIntoDocument(<ThemedCalendar />);
        let dateDisplay = React.findDOMNode(
            TestUtils.findRenderedComponentWithType(render, DateDisplay));

        expect(dateDisplay.style.height).to.equal('64px');
    });

    it(`should set the DateDisplay height to 64 if the mode prop is set to potrait`, function() {
        let render = TestUtils.renderIntoDocument(<ThemedCalendar mode='potrait'/>);
        let dateDisplay = React.findDOMNode(
            TestUtils.findRenderedComponentWithType(render, DateDisplay));

        expect(dateDisplay.style.height).to.equal('64px');
    });

    it(`should set the DateDisplay height to 198 if the weekCount is not equal to 5 or 6 and the mode prop is landscape`, () => {
        let initialDate = new Date('February 01, 2015');
        let weekCount = DateTime.getWeekArray(initialDate);

        expect(weekCount).to.not.equal(5);
        expect(weekCount).to.not.equal(6);

        let render = TestUtils.renderIntoDocument(
            <ThemedCalendar mode='landscape' initialDate={initialDate}/>);
        let dateDisplay = React.findDOMNode(
            TestUtils.findRenderedComponentWithType(render, DateDisplay));

        expect(dateDisplay.style.height).to.equal('198px');
    });

    it(`should set the DateDisplay height to 238 if the weekCount is equal to 5 and the mode prop is landscape`, () => {
        let initialDate = new Date('March 01, 2015');
        let weekCount = DateTime.getWeekArray(initialDate).length;

        expect(weekCount).to.equal(5);

        let render = TestUtils.renderIntoDocument(
            <ThemedCalendar mode='landscape' initialDate={initialDate} />);
        let dateDisplay = React.findDOMNode(
            TestUtils.findRenderedComponentWithType(render, DateDisplay));

        expect(dateDisplay.style.height).to.equal('238px');
    });

    it(`should set the DateDisplay height to 278 if the weekCount is equal to 6 and the mode prop is landscape`, () => {
        let initialDate = new Date('May 01, 2015');
        let weekCount = DateTime.getWeekArray(initialDate).length;

        expect(weekCount).to.equal(6);

        let render = TestUtils.renderIntoDocument(
            <ThemedCalendar mode='landscape' initialDate={initialDate} />);
        let dateDisplay = React.findDOMNode(
            TestUtils.findRenderedComponentWithType(render, DateDisplay));

        expect(dateDisplay.style.height).to.equal('278px');
    });

    describe(`Next Month Button`, () => {
        it(`should initially be disabled if the current month is the same as the month in the maxDate prop`, () => {
            let initialDate = new Date();
            let maxDate = new Date(initialDate.toDateString());

            let render = TestUtils.renderIntoDocument(
                <ThemedCalendar
                    initialDate={initialDate}
                    maxDate={maxDate}
                />
            );
            let renderedCalendarToolbar =
                TestUtils.findRenderedComponentWithType(render, CalendarToolbar);

            expect(renderedCalendarToolbar.props.nextMonth).to.be.false;
        });

        it(`should initially be disabled if the current month is after the month in the maxDate prop`, () => {
            let initialDate = new Date();
            let maxDate = new Date(initialDate.toDateString());
            maxDate.setMonth(maxDate.getMonth() - 1);

            let render = TestUtils.renderIntoDocument(
                <ThemedCalendar
                    initialDate={initialDate}
                    maxDate={maxDate} />
            );
            let renderedCalendarToolbar =
                TestUtils.findRenderedComponentWithType(render, CalendarToolbar);

            expect(renderedCalendarToolbar.props.nextMonth).to.be.false;
        });

        it(`should initially enable the next month button if the current month is before the maxDate prop`, () => {
            let initialDate = new Date();
            let maxDate = new Date(initialDate.toDateString());
            maxDate.setMonth(maxDate.getMonth() + 1);

            let render = TestUtils.renderIntoDocument(
                <ThemedCalendar
                    initialDate={initialDate}
                    maxDate={maxDate} />
            );

            let renderedCalendarToolbar =
                TestUtils.findRenderedComponentWithType(render, CalendarToolbar);

            expect(renderedCalendarToolbar.props.nextMonth).to.be.true;
        });

        it(`should reenable the next month button when the current month is before the maxDate prop`, () => {
            let initialDate = new Date();
            let maxDate = new Date(initialDate.toDateString());

            let render = TestUtils.renderIntoDocument(
                <ThemedCalendar
                    initialDate={initialDate}
                    maxDate={maxDate} />
            );
            let prevMonthButton = React.findDOMNode(
                TestUtils.scryRenderedComponentsWithType(render, IconButton)[0]);
            TestUtils.Simulate.touchTap(prevMonthButton);

            let renderedCalendarToolbar =
                TestUtils.findRenderedComponentWithType(render, CalendarToolbar);

            expect(renderedCalendarToolbar.props.nextMonth).to.be.true;
        });

        it(`should redisable the next month button when the current month is the same as the maxDate prop`, () => {
            let initialDate = new Date();
            let maxDate = new Date(initialDate.toDateString());
            maxDate.setMonth(maxDate.getMonth() + 1);

            let render = TestUtils.renderIntoDocument(
                <ThemedCalendar
                    initialDate={initialDate}
                    maxDate={maxDate} />
            );
            let nextMonthButton = React.findDOMNode(
                TestUtils.scryRenderedComponentsWithType(render, IconButton)[1]);
            TestUtils.Simulate.touchTap(nextMonthButton);

            let renderedCalendarToolbar =
                TestUtils.findRenderedComponentWithType(render, CalendarToolbar);

            expect(renderedCalendarToolbar.props.nextMonth).to.be.false;
        });
    });
});<commit_msg>Added test to check if the previous month is disabled if the minDate month and initialDate month are the same<commit_after>import injectTapEventPlugin from 'react-tap-event-plugin';
injectTapEventPlugin();

import Calendar from 'date-picker/calendar';
import injectTheme from '../fixtures/inject-theme';
import React from 'react';
import CalendarToolbar from 'date-picker/calendar-toolbar';
import IconButton from 'icon-button';
import DateDisplay  from 'date-picker/date-display';
import DateTime from 'utils/date-time';

const TestUtils = React.addons.TestUtils;

describe(`Calendar`, () => {
    let ThemedCalendar;

    beforeEach(() => {
        ThemedCalendar = injectTheme(Calendar);
    });

    it(`should set the DateDisplay height to 64 if the mode prop is not provided (default mode is potrait)`, () => {
        let render = TestUtils.renderIntoDocument(<ThemedCalendar />);
        let dateDisplay = React.findDOMNode(
            TestUtils.findRenderedComponentWithType(render, DateDisplay));

        expect(dateDisplay.style.height).to.equal('64px');
    });

    it(`should set the DateDisplay height to 64 if the mode prop is set to potrait`, function() {
        let render = TestUtils.renderIntoDocument(<ThemedCalendar mode='potrait'/>);
        let dateDisplay = React.findDOMNode(
            TestUtils.findRenderedComponentWithType(render, DateDisplay));

        expect(dateDisplay.style.height).to.equal('64px');
    });

    it(`should set the DateDisplay height to 198 if the weekCount is not equal to 5 or 6 and the mode prop is landscape`, () => {
        let initialDate = new Date('February 01, 2015');
        let weekCount = DateTime.getWeekArray(initialDate);

        expect(weekCount).to.not.equal(5);
        expect(weekCount).to.not.equal(6);

        let render = TestUtils.renderIntoDocument(
            <ThemedCalendar mode='landscape' initialDate={initialDate}/>);
        let dateDisplay = React.findDOMNode(
            TestUtils.findRenderedComponentWithType(render, DateDisplay));

        expect(dateDisplay.style.height).to.equal('198px');
    });

    it(`should set the DateDisplay height to 238 if the weekCount is equal to 5 and the mode prop is landscape`, () => {
        let initialDate = new Date('March 01, 2015');
        let weekCount = DateTime.getWeekArray(initialDate).length;

        expect(weekCount).to.equal(5);

        let render = TestUtils.renderIntoDocument(
            <ThemedCalendar mode='landscape' initialDate={initialDate} />);
        let dateDisplay = React.findDOMNode(
            TestUtils.findRenderedComponentWithType(render, DateDisplay));

        expect(dateDisplay.style.height).to.equal('238px');
    });

    it(`should set the DateDisplay height to 278 if the weekCount is equal to 6 and the mode prop is landscape`, () => {
        let initialDate = new Date('May 01, 2015');
        let weekCount = DateTime.getWeekArray(initialDate).length;

        expect(weekCount).to.equal(6);

        let render = TestUtils.renderIntoDocument(
            <ThemedCalendar mode='landscape' initialDate={initialDate} />);
        let dateDisplay = React.findDOMNode(
            TestUtils.findRenderedComponentWithType(render, DateDisplay));

        expect(dateDisplay.style.height).to.equal('278px');
    });

    describe(`Next Month Button`, () => {
        it(`should initially be disabled if the current month is the same as the month in the maxDate prop`, () => {
            let initialDate = new Date();
            let maxDate = new Date(initialDate.toDateString());

            let render = TestUtils.renderIntoDocument(
                <ThemedCalendar
                    initialDate={initialDate}
                    maxDate={maxDate}
                />
            );
            let renderedCalendarToolbar =
                TestUtils.findRenderedComponentWithType(render, CalendarToolbar);

            expect(renderedCalendarToolbar.props.nextMonth).to.be.false;
        });

        it(`should initially be disabled if the current month is after the month in the maxDate prop`, () => {
            let initialDate = new Date();
            let maxDate = new Date(initialDate.toDateString());
            maxDate.setMonth(maxDate.getMonth() - 1);

            let render = TestUtils.renderIntoDocument(
                <ThemedCalendar
                    initialDate={initialDate}
                    maxDate={maxDate} />
            );
            let renderedCalendarToolbar =
                TestUtils.findRenderedComponentWithType(render, CalendarToolbar);

            expect(renderedCalendarToolbar.props.nextMonth).to.be.false;
        });

        it(`should initially enable the next month button if the current month is before the maxDate prop`, () => {
            let initialDate = new Date();
            let maxDate = new Date(initialDate.toDateString());
            maxDate.setMonth(maxDate.getMonth() + 1);

            let render = TestUtils.renderIntoDocument(
                <ThemedCalendar
                    initialDate={initialDate}
                    maxDate={maxDate} />
            );

            let renderedCalendarToolbar =
                TestUtils.findRenderedComponentWithType(render, CalendarToolbar);

            expect(renderedCalendarToolbar.props.nextMonth).to.be.true;
        });

        it(`should reenable the next month button when the current month is before the maxDate prop`, () => {
            let initialDate = new Date();
            let maxDate = new Date(initialDate.toDateString());

            let render = TestUtils.renderIntoDocument(
                <ThemedCalendar
                    initialDate={initialDate}
                    maxDate={maxDate} />
            );
            let prevMonthButton = React.findDOMNode(
                TestUtils.scryRenderedComponentsWithType(render, IconButton)[0]);
            TestUtils.Simulate.touchTap(prevMonthButton);

            let renderedCalendarToolbar =
                TestUtils.findRenderedComponentWithType(render, CalendarToolbar);

            expect(renderedCalendarToolbar.props.nextMonth).to.be.true;
        });

        it(`should redisable the next month button when the current month is the same as the maxDate prop`, () => {
            let initialDate = new Date();
            let maxDate = new Date(initialDate.toDateString());
            maxDate.setMonth(maxDate.getMonth() + 1);

            let render = TestUtils.renderIntoDocument(
                <ThemedCalendar
                    initialDate={initialDate}
                    maxDate={maxDate} />
            );
            let nextMonthButton = React.findDOMNode(
                TestUtils.scryRenderedComponentsWithType(render, IconButton)[1]);
            TestUtils.Simulate.touchTap(nextMonthButton);

            let renderedCalendarToolbar =
                TestUtils.findRenderedComponentWithType(render, CalendarToolbar);

            expect(renderedCalendarToolbar.props.nextMonth).to.be.false;
        });
    });

    it(`should disable the previous month button if the current month is the same as the minDate month prop`, () => {
        let initialDate = new Date();
        let minDate = new Date(initialDate.toDateString());

        let render = TestUtils.renderIntoDocument(
            <ThemedCalendar
                initialDate={initialDate}
                minDate={minDate}
            />
        );
        let calendarToolbar = TestUtils.findRenderedComponentWithType(render, CalendarToolbar);

        expect(calendarToolbar.props.prevMonth).to.be.false;
    });
});<|endoftext|>


<commit_before>"use strict";

const BlueBird = require('bluebird');
const {generateStringHash} = require('../utils/hash');

class File {
  constructor(path, fileSystem) {
    this.path = path;
    this.fileSystem = fileSystem;
  }
  get stat() {
    if (!this._stat) {
      this._stat = this.fileSystem.stat(this.path);
    }
    return this._stat;
  }
  set stat(stat) {
    this._stat = BlueBird.resolve(stat);
    // Force re-evaluation of the modified time
    this._modifiedTime = null;
    return stat;
  }
  get modifiedTime() {
    if (!this._modifiedTime) {
      this._modifiedTime = this.stat
        .then(stat => stat.mtime.getTime());
    }
    return this._modifiedTime;
  }
  set modifiedTime(modifiedTime) {
    this._modifiedTime = BlueBird.resolve(modifiedTime);
    return modifiedTime;
  }
  get isFile() {
    if (!this._isFile) {
      this._isFile = this.stat
        .then(stat => stat.isFile())
        .catch(err => {
          if (err.code === 'ENOENT') {
            return false;
          }
          return BlueBird.reject(err);
        });
    }
    return this._isFile;
  }
  set isFile(isFile) {
    this._isFile = BlueBird.resolve(isFile);
    return isFile;
  }
  get buffer() {
    if (!this._buffer) {
      this._buffer = this.fileSystem.readFile(this.path);
    }
    return this._buffer;
  }
  set buffer(buffer) {
    this._buffer = BlueBird.resolve(buffer);
    return _buffer;
  }
  get text() {
    if (!this._text) {
      this._text = this.fileSystem.readFile(this.path, 'utf8');
    }
    return this._text;
  }
  set text(text) {
    this._text = BlueBird.resolve(text);
    // Force re-evaluation of the text's hash
    this._textHash = null;
    return text;
  }
  get textHash() {
    if (!this._textHash) {
      this._textHash = this.text.then(generateStringHash);
    }
    return this._textHash;
  }
  set textHash(textHash) {
    this._textHash = BlueBird.resolve(textHash);
    return textHash;
  }
}

module.exports = {
  File
};<commit_msg>Cleanup<commit_after>"use strict";

const BlueBird = require('bluebird');
const {generateStringHash} = require('../utils/hash');

class File {
  constructor(path, fileSystem) {
    this.path = path;
    this.fileSystem = fileSystem;
  }
  get stat() {
    if (!this._stat) {
      this._stat = this.fileSystem.stat(this.path);
    }
    return this._stat;
  }
  set stat(stat) {
    this._stat = BlueBird.resolve(stat);
    // Force re-evaluation of the modified time
    this._modifiedTime = null;
    return stat;
  }
  get modifiedTime() {
    if (!this._modifiedTime) {
      this._modifiedTime = this.stat
        .then(stat => stat.mtime.getTime());
    }
    return this._modifiedTime;
  }
  set modifiedTime(modifiedTime) {
    this._modifiedTime = BlueBird.resolve(modifiedTime);
    return modifiedTime;
  }
  get isFile() {
    if (!this._isFile) {
      this._isFile = this.stat
        .then(stat => stat.isFile())
        .catch(err => {
          if (err.code === 'ENOENT') {
            return false;
          }
          return BlueBird.reject(err);
        });
    }
    return this._isFile;
  }
  set isFile(isFile) {
    this._isFile = BlueBird.resolve(isFile);
    return isFile;
  }
  get buffer() {
    if (!this._buffer) {
      this._buffer = this.fileSystem.readFile(this.path);
    }
    return this._buffer;
  }
  set buffer(buffer) {
    this._buffer = BlueBird.resolve(buffer);
    return buffer;
  }
  get text() {
    if (!this._text) {
      this._text = this.fileSystem.readFile(this.path, 'utf8');
    }
    return this._text;
  }
  set text(text) {
    this._text = BlueBird.resolve(text);
    // Force re-evaluation of the text's hash
    this._textHash = null;
    return text;
  }
  get textHash() {
    if (!this._textHash) {
      this._textHash = this.text.then(generateStringHash);
    }
    return this._textHash;
  }
  set textHash(textHash) {
    this._textHash = BlueBird.resolve(textHash);
    return textHash;
  }
}

module.exports = {
  File
};<|endoftext|>


<commit_before>PinPoint.ItemsfromDomExtractor = function(targetName) {
  this.targetName = targetName;
}

// We'll need logic stating what className to use depending on sit:
// YouTube: "ytp-time-current"
// Vimeo: "box"
PinPoint.ItemsfromDomExtractor.prototype = {
  getTime : function(className) {
    var timeDivArray = document.getElementsbyClassName("ytp-time-current")
    var time = timeDivArray[0].innerHTML

  }
}
<commit_msg>Add return value to ItemsFromDomExtractor and remove hard-coded className.<commit_after>PinPoint.ItemsfromDomExtractor = function(targetName) {
  this.targetName = targetName;
}

// We'll need logic stating what className to use depending on sit:
// YouTube: "ytp-time-current"
// Vimeo: "box"

PinPoint.ItemsFromDomExtractor.prototype = {
  getTime : function(className) {
    var timeDivArray = document.getElementsbyClassName(className);
    var time = timeDivArray[0].innerHTML;
    return time;
  }
}
<|endoftext|>