<commit_before>/* global d3:false, fc:false, requestAnimationFrame:false */
(function(d3, fc) {
    'use strict';

    // Assigning to fc is nasty but there's not a lot of choice I don't think...
    fc.tooltip = function() {

        var formatters = {
            date: d3.time.format('%A, %b %e, %Y'),
            price: d3.format('.2f'),
            volume: d3.format('0,5p')
        };

        function format(type, value) {
            return formatters[type](value);
        }

        var items = [
            function(d) { return format('date', d.date); },
            function(d) { return 'Open: ' + format('price', d.open); },
            function(d) { return 'High: ' + format('price', d.high); },
            function(d) { return 'Low: ' + format('price', d.low); },
            function(d) { return 'Close: ' + format('price', d.close); },
            function(d) { return 'Volume: ' + format('volume', d.volume); }
        ];

        var tooltip = function(selection) {

            var container = selection.enter()
                .append('g')
                .attr({
                    'class': 'info',
                    'transform': 'translate(5, 5)'
                });

            container.append('rect')
                .attr({
                    width: 130,
                    height: 76
                });

            container.append('text');

            container = selection.select('g.info');

            var tspan = container.select('text')
                .selectAll('tspan')
                .data(items);

            tspan.enter()
                .append('tspan')
                .attr('x', 4)
                .attr('dy', 12);

            tspan.text(function(d) {
                return d(container.datum().datum);
            });
        };

        return tooltip;
    };

}(d3, fc));

(function(d3, fc) {
    'use strict';

    var dataGenerator = fc.data.random.financial()
        .startDate(new Date(2014, 1, 1));

    var container = d3.select('#low-barrel')
        .datum(dataGenerator(250))
        .layout();

    function mainChart(selection) {

        var data = selection.datum();

        var gridlines = fc.annotation.gridline()
            .yTicks(3);

        var candlestick = fc.series.candlestick();

        var tooltip = fc.tooltip();

        var crosshairs = fc.tool.crosshair()
            .decorate(tooltip)
            .snap(fc.util.seriesPointSnap(candlestick, data))
            .on('trackingstart.link', render)
            .on('trackingmove.link', render)
            .on('trackingend.link', render)
            .xLabel('')
            .yLabel('');

        var multi = fc.series.multi()
            .series([gridlines, candlestick, crosshairs])
            .mapping(function(series) {
                switch (series) {
                case crosshairs:
                    return data.crosshairs;
                default:
                    return data;
                }
            });

        var xScale = fc.scale.dateTime();

        var chart = fc.chart.cartesianChart(xScale)
            .margin({
                bottom: 20,
                right: 60
            })
            .xDomain(data.dateDomain)
            .xTicks(0)
            .yDomain(fc.util.extent(data, ['high', 'low']))
            .yNice()
            .yTicks(3)
            .plotArea(multi);

        selection.call(chart);

        var zoom = d3.behavior.zoom()
            .x(xScale)
            .on('zoom', function() {
                data.dateDomain[0] = chart.xDomain()[0];
                data.dateDomain[1] = chart.xDomain()[1];
                render();
            });

        selection.call(zoom);
    }

    function volumeChart(selection) {

        var data = selection.datum();

        var chart = fc.chart.cartesianChart(fc.scale.dateTime())
            .margin({
                bottom: 20,
                right: 60
            })
            .xDomain(data.dateDomain)
            .yDomain(fc.util.extent(data, 'volume'))
            .yNice()
            .yTicks(2);

        var gridlines = fc.annotation.gridline()
            .yTicks(2);

        var bar = fc.series.bar()
            .yValue(function(d) { return d.volume; })
            .y0Value(chart.yDomain()[0]);

        var crosshairs = fc.tool.crosshair()
            .snap(fc.util.seriesPointSnap(bar, data))
            .xLabel('')
            .yLabel('')
            .on('trackingstart.link', render)
            .on('trackingmove.link', render)
            .on('trackingend.link', render);

        var multi = fc.series.multi()
            .series([gridlines, bar, crosshairs])
            .mapping(function(series) {
                switch (series) {
                case crosshairs:
                    return data.crosshairs;
                default:
                    return data;
                }
            });

        chart.plotArea(multi);

        selection.call(chart);
    }

    function navigatorChart(selection) {

        var data = selection.datum();

        var chart = fc.chart.cartesianChart(fc.scale.dateTime())
            .margin({
                top: 10,
                bottom: 20,
                right: 60
            })
            .xDomain(fc.util.extent(data, 'date'))
            .yDomain(fc.util.extent(data, 'close'))
            .yNice()
            .xTicks(3)
            .yTicks(0);

        var gridlines = fc.annotation.gridline()
            .xTicks(3)
            .yTicks(0);

        var line = fc.series.line();

        var area = fc.series.area()
            .y0Value(chart.yDomain()[0]);

        var brush = d3.svg.brush()
            .on('brush', function() {
                var domain = [brush.extent()[0][0], brush.extent()[1][0]];
                // Scales with a domain delta of 0 === NaN
                if (domain[0] - domain[1] !== 0) {
                    data.dateDomain = domain;
                    render();
                }
            });

        var multi = fc.series.multi()
            .series([gridlines, line, area, brush])
            .mapping(function(series) {
                // Need to set the extent AFTER the scales
                // are set AND their ranges defined
                if (series === brush) {
                    brush.extent([
                        [data.dateDomain[0], chart.yDomain()[0]],
                        [data.dateDomain[1], chart.yDomain()[1]]
                    ]);
                }
                return data;
            })
            .decorate(function(sel) {
                var height = d3.select(sel.node().parentNode).layout('height');
                sel.enter()
                    .selectAll('.resize.e>rect, .resize.w>rect')
                    .style('visibility', 'visible')
                    .attr('y', height / 4)
                    .attr('rx', 4)
                    .attr('ry', 4);

                // As a y scale is set on the brush (multi does this),
                // the brush component resets the height of the rect on every redraw,
                // as such it has to be overridden within the update selection,
                // rather than the enter selection
                sel.selectAll('.resize.e>rect, .resize.w>rect')
                    .attr('height', height / 2);
            });

        chart.plotArea(multi);

        selection.call(chart);
    }

    function renderInternal() {
        var data = container.datum();

        // Enhance data with interactive state
        if (data.crosshairs == null) {
            data.crosshairs = [];
        }
        if (data.dateDomain == null) {
            var maxDate = fc.util.extent(container.datum(), 'date')[1];
            var dateScale = d3.time.scale()
                .domain([maxDate - 50 * 24 * 60 * 60 * 1000, maxDate]);
            data.dateDomain = dateScale.domain();
        }

        // Calculate visible data for main/volume charts
        var bisector = d3.bisector(function(d) { return d.date; });
        var visibleData = data.slice(
            // Pad and clamp the bisector values to ensure extents can be calculated
            Math.max(0, bisector.left(data, data.dateDomain[0]) - 1),
            Math.min(bisector.right(data, data.dateDomain[1]) + 1, data.length)
        );
        visibleData.dateDomain = data.dateDomain;
        visibleData.crosshairs = data.crosshairs;

        container.select('svg.main')
            .datum(visibleData)
            .call(mainChart);

        container.select('svg.volume')
            .datum(visibleData)
            .call(volumeChart);

        container.select('svg.navigator')
            .call(navigatorChart);

        container.layoutSuspended(true);
    }

    var rafId = null;
    function render() {
        if (rafId == null) {
            rafId = requestAnimationFrame(function() {
                rafId = null;
                renderInternal();
            });
        }
    }

    renderInternal();

}(d3, fc));
<commit_msg>Cache navigator domain extents<commit_after>/* global d3:false, fc:false, requestAnimationFrame:false */
(function(d3, fc) {
    'use strict';

    // Assigning to fc is nasty but there's not a lot of choice I don't think...
    fc.tooltip = function() {

        var formatters = {
            date: d3.time.format('%A, %b %e, %Y'),
            price: d3.format('.2f'),
            volume: d3.format('0,5p')
        };

        function format(type, value) {
            return formatters[type](value);
        }

        var items = [
            function(d) { return format('date', d.date); },
            function(d) { return 'Open: ' + format('price', d.open); },
            function(d) { return 'High: ' + format('price', d.high); },
            function(d) { return 'Low: ' + format('price', d.low); },
            function(d) { return 'Close: ' + format('price', d.close); },
            function(d) { return 'Volume: ' + format('volume', d.volume); }
        ];

        var tooltip = function(selection) {

            var container = selection.enter()
                .append('g')
                .attr({
                    'class': 'info',
                    'transform': 'translate(5, 5)'
                });

            container.append('rect')
                .attr({
                    width: 130,
                    height: 76
                });

            container.append('text');

            container = selection.select('g.info');

            var tspan = container.select('text')
                .selectAll('tspan')
                .data(items);

            tspan.enter()
                .append('tspan')
                .attr('x', 4)
                .attr('dy', 12);

            tspan.text(function(d) {
                return d(container.datum().datum);
            });
        };

        return tooltip;
    };

}(d3, fc));

(function(d3, fc) {
    'use strict';

    var dataGenerator = fc.data.random.financial()
        .startDate(new Date(2014, 1, 1));

    var container = d3.select('#low-barrel')
        .datum(dataGenerator(250))
        .layout();

    function mainChart(selection) {

        var data = selection.datum();

        var gridlines = fc.annotation.gridline()
            .yTicks(3);

        var candlestick = fc.series.candlestick();

        var tooltip = fc.tooltip();

        var crosshairs = fc.tool.crosshair()
            .decorate(tooltip)
            .snap(fc.util.seriesPointSnap(candlestick, data))
            .on('trackingstart.link', render)
            .on('trackingmove.link', render)
            .on('trackingend.link', render)
            .xLabel('')
            .yLabel('');

        var multi = fc.series.multi()
            .series([gridlines, candlestick, crosshairs])
            .mapping(function(series) {
                switch (series) {
                case crosshairs:
                    return data.crosshairs;
                default:
                    return data;
                }
            });

        var xScale = fc.scale.dateTime();

        var chart = fc.chart.cartesianChart(xScale)
            .margin({
                bottom: 20,
                right: 60
            })
            .xDomain(data.dateDomain)
            .xTicks(0)
            .yDomain(fc.util.extent(data, ['high', 'low']))
            .yNice()
            .yTicks(3)
            .plotArea(multi);

        selection.call(chart);

        var zoom = d3.behavior.zoom()
            .x(xScale)
            .on('zoom', function() {
                data.dateDomain[0] = chart.xDomain()[0];
                data.dateDomain[1] = chart.xDomain()[1];
                render();
            });

        selection.call(zoom);
    }

    function volumeChart(selection) {

        var data = selection.datum();

        var chart = fc.chart.cartesianChart(fc.scale.dateTime())
            .margin({
                bottom: 20,
                right: 60
            })
            .xDomain(data.dateDomain)
            .yDomain(fc.util.extent(data, 'volume'))
            .yNice()
            .yTicks(2);

        var gridlines = fc.annotation.gridline()
            .yTicks(2);

        var bar = fc.series.bar()
            .yValue(function(d) { return d.volume; })
            .y0Value(chart.yDomain()[0]);

        var crosshairs = fc.tool.crosshair()
            .snap(fc.util.seriesPointSnap(bar, data))
            .xLabel('')
            .yLabel('')
            .on('trackingstart.link', render)
            .on('trackingmove.link', render)
            .on('trackingend.link', render);

        var multi = fc.series.multi()
            .series([gridlines, bar, crosshairs])
            .mapping(function(series) {
                switch (series) {
                case crosshairs:
                    return data.crosshairs;
                default:
                    return data;
                }
            });

        chart.plotArea(multi);

        selection.call(chart);
    }

    function navigatorChart(selection) {

        var data = selection.datum();

        var chart = fc.chart.cartesianChart(fc.scale.dateTime())
            .margin({
                top: 10,
                bottom: 20,
                right: 60
            })
            .xDomain(data.navigatorDateDomain)
            .yDomain(data.navigatorYDomain)
            .yNice()
            .xTicks(3)
            .yTicks(0);

        var gridlines = fc.annotation.gridline()
            .xTicks(3)
            .yTicks(0);

        var line = fc.series.line();

        var area = fc.series.area()
            .y0Value(chart.yDomain()[0]);

        var brush = d3.svg.brush()
            .on('brush', function() {
                var domain = [brush.extent()[0][0], brush.extent()[1][0]];
                // Scales with a domain delta of 0 === NaN
                if (domain[0] - domain[1] !== 0) {
                    data.dateDomain = domain;
                    render();
                }
            });

        var multi = fc.series.multi()
            .series([gridlines, line, area, brush])
            .mapping(function(series) {
                // Need to set the extent AFTER the scales
                // are set AND their ranges defined
                if (series === brush) {
                    brush.extent([
                        [data.dateDomain[0], chart.yDomain()[0]],
                        [data.dateDomain[1], chart.yDomain()[1]]
                    ]);
                }
                return data;
            })
            .decorate(function(sel) {
                var height = d3.select(sel.node().parentNode).layout('height');
                sel.enter()
                    .selectAll('.resize.e>rect, .resize.w>rect')
                    .style('visibility', 'visible')
                    .attr('y', height / 4)
                    .attr('rx', 4)
                    .attr('ry', 4);

                // As a y scale is set on the brush (multi does this),
                // the brush component resets the height of the rect on every redraw,
                // as such it has to be overridden within the update selection,
                // rather than the enter selection
                sel.selectAll('.resize.e>rect, .resize.w>rect')
                    .attr('height', height / 2);
            });

        chart.plotArea(multi);

        selection.call(chart);
    }

    function renderInternal() {
        var data = container.datum();

        // Enhance data with interactive state
        if (data.crosshairs == null) {
            data.crosshairs = [];
        }
        if (data.dateDomain == null) {
            var maxDate = fc.util.extent(container.datum(), 'date')[1];
            var dateScale = d3.time.scale()
                .domain([maxDate - 50 * 24 * 60 * 60 * 1000, maxDate]);
            data.dateDomain = dateScale.domain();

             data.navigatorDateDomain = fc.util.extent(data, 'date');
             data.navigatorYDomain = fc.util.extent(data, 'close');
        }

        // Calculate visible data for main/volume charts
        var bisector = d3.bisector(function(d) { return d.date; });
        var visibleData = data.slice(
            // Pad and clamp the bisector values to ensure extents can be calculated
            Math.max(0, bisector.left(data, data.dateDomain[0]) - 1),
            Math.min(bisector.right(data, data.dateDomain[1]) + 1, data.length)
        );
        visibleData.dateDomain = data.dateDomain;
        visibleData.crosshairs = data.crosshairs;

        container.select('svg.main')
            .datum(visibleData)
            .call(mainChart);

        container.select('svg.volume')
            .datum(visibleData)
            .call(volumeChart);

        container.select('svg.navigator')
            .call(navigatorChart);

        container.layoutSuspended(true);
    }

    var rafId = null;
    function render() {
        if (rafId == null) {
            rafId = requestAnimationFrame(function() {
                rafId = null;
                renderInternal();
            });
        }
    }

    renderInternal();

}(d3, fc));
<|endoftext|>


<commit_before>/*
* Copyright (c) 2013 BlackBerry Limited.
*
* 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 _self = {},
    _ID = "com.blackberry.community.curl",
    exec = cordova.require("cordova/exec");

    // These methods are called by your App's JavaScript
    // They make WebWorks function calls to the methods
    // in the index.js of the Extension

    _self.get = function (input, callback) {
        var success = function (data, response) {
                if (typeof successCallback === 'function') {
                    successCallback(data, response, input);
                } else {
                    // print data by default
                    console.log(input);
                    console.log(data);
                }
            },
            fail = function (data, response) {
                if (typeof failCallback === 'function') {
                    failCallback(data, response, input);
                } else {
                    // print error by default
                    console.log(input);
                    console.log("Error: " + data);
                }
            };

        input = input || "";

        exec(success, fail, _ID, "get", {input: input}, true);
    };

module.exports = _self;
<commit_msg>Replaced success and fail callbakcs to onDone<commit_after>/*
* Copyright (c) 2013 BlackBerry Limited.
*
* 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 _self = {},
    _ID = "com.blackberry.community.curl",
    exec = cordova.require("cordova/exec");

    // These methods are called by your App's JavaScript
    // They make WebWorks function calls to the methods
    // in the index.js of the Extension

    _self.get = function (input, onDone) {
        var callback = function (data, response) {
            if (typeof onDone === 'function') {
                onDone(data, response, input);
            } else {
                // print data by default
                console.log(input);
                console.log(data);
            }
        };

        input = input || "";

        exec(callback, callback, _ID, "get", {input: input}, true);
    };

module.exports = _self;
<|endoftext|>


<commit_before>// ==UserScript==
// @name         Banner Carousel
// @namespace    http://terrasoft.gr/userscripts/
// @version      0.2
// @description  Banner Carousel
// @author       George Schizas
// @match        https://www.reddit.com/r/greece/comments/2zg5ou/*
// @downloadUrl  https://raw.githubusercontent.com/gschizas/RedditContentScripts/master/BannerCarousel/banner_carousel.user.js
// @grant        unsafeWindow
// ==/UserScript==

var bannerImages = [
    'https://i.imgur.com/ptC8pyl.jpg',
    'https://i.imgur.com/Ece3hYj.jpg',
    'https://i.imgur.com/T22nJc6.png',
    'https://i.imgur.com/EzEYiEC.png',
    'https://i.imgur.com/RVV7fVL.png',
    'https://i.imgur.com/oM4QXGP.png',
    'https://dl.dropboxusercontent.com/u/6166376/banner.png',
    'https://i.imgur.com/z6oul4p.jpg',
    'https://i.imgur.com/JzMaSvM.jpg',
    'https://i.imgur.com/DpLvIm8.jpg',
    'https://i.imgur.com/GwVgxvD.jpg',
    'https://i.imgur.com/oV3xsfC.jpg',
    'https://i.imgur.com/V2kYfgv.jpg',
    'https://i.imgur.com/7gRJxLy.png',
    'https://i.imgur.com/eFlc02U.jpg'
];

var $ = unsafeWindow.jQuery;

var bannerControl = $("<select id='bannerSelect'><option value='0'>--Default Banner--</option></select><button id='bannerPrev'>« Prev</button><button id='bannerNext'>Next »</button>");

$('#siteTable').prepend(bannerControl);

console.log(bannerControl);

bannerImages.forEach(function (el, i) {
    // console.log(el, i);
    $('#bannerSelect').append("<option value='" + (i+1) + "'>" + el + "</option>");
});

// console.log(combo);
// console.log($('.commentarea'));

$('#bannerSelect').change(function() {
    selectedImage = parseInt($(this).val());
    if (selectedImage==0) {
        imageUrl = '//c.thumbs.redditmedia.com/-oIzLQ0UPDfe1Swn.png'
    } else {
        imageUrl = bannerImages[selectedImage-1];
    }
    $('#header').css('background-image', 'url(' + imageUrl + ')')
})

$('#bannerPrev').click(function() {
    console.log("« Prev");
    var currentBanner = $('#bannerSelect').val();
    currentBanner--;
    if (currentBanner < 0) currentBanner = bannerImages.length;
    $('#bannerSelect').val(currentBanner);
    $('#bannerSelect').change();
})
$('#bannerNext').click(function() {
    console.log("Next »");
    var currentBanner = $('#bannerSelect').val();
    currentBanner++;
    if (currentBanner>bannerImages.length) currentBanner = 0;
    $('#bannerSelect').val(currentBanner);
    $('#bannerSelect').change();
})
<commit_msg>version bump<commit_after>// ==UserScript==
// @name         Banner Carousel
// @namespace    http://terrasoft.gr/userscripts/
// @version      0.2.1
// @description  Banner Carousel
// @author       George Schizas
// @match        https://www.reddit.com/r/greece/comments/2zg5ou/*
// @downloadUrl  https://raw.githubusercontent.com/gschizas/RedditContentScripts/master/BannerCarousel/banner_carousel.user.js
// @grant        unsafeWindow
// ==/UserScript==

var bannerImages = [
    'https://i.imgur.com/ptC8pyl.jpg',
    'https://i.imgur.com/Ece3hYj.jpg',
    'https://i.imgur.com/T22nJc6.png',
    'https://i.imgur.com/EzEYiEC.png',
    'https://i.imgur.com/RVV7fVL.png',
    'https://i.imgur.com/oM4QXGP.png',
    'https://dl.dropboxusercontent.com/u/6166376/banner.png',
    'https://i.imgur.com/z6oul4p.jpg',
    'https://i.imgur.com/JzMaSvM.jpg',
    'https://i.imgur.com/DpLvIm8.jpg',
    'https://i.imgur.com/GwVgxvD.jpg',
    'https://i.imgur.com/oV3xsfC.jpg',
    'https://i.imgur.com/V2kYfgv.jpg',
    'https://i.imgur.com/7gRJxLy.png',
    'https://i.imgur.com/eFlc02U.jpg'
];

var $ = unsafeWindow.jQuery;

var bannerControl = $("<select id='bannerSelect'><option value='0'>--Default Banner--</option></select><button id='bannerPrev'>« Prev</button><button id='bannerNext'>Next »</button>");

$('#siteTable').prepend(bannerControl);

console.log(bannerControl);

bannerImages.forEach(function (el, i) {
    // console.log(el, i);
    $('#bannerSelect').append("<option value='" + (i+1) + "'>" + el + "</option>");
});

// console.log(combo);
// console.log($('.commentarea'));

$('#bannerSelect').change(function() {
    selectedImage = parseInt($(this).val());
    if (selectedImage==0) {
        imageUrl = '//c.thumbs.redditmedia.com/-oIzLQ0UPDfe1Swn.png'
    } else {
        imageUrl = bannerImages[selectedImage-1];
    }
    $('#header').css('background-image', 'url(' + imageUrl + ')')
})

$('#bannerPrev').click(function() {
    console.log("« Prev");
    var currentBanner = $('#bannerSelect').val();
    currentBanner--;
    if (currentBanner < 0) currentBanner = bannerImages.length;
    $('#bannerSelect').val(currentBanner);
    $('#bannerSelect').change();
})
$('#bannerNext').click(function() {
    console.log("Next »");
    var currentBanner = $('#bannerSelect').val();
    currentBanner++;
    if (currentBanner>bannerImages.length) currentBanner = 0;
    $('#bannerSelect').val(currentBanner);
    $('#bannerSelect').change();
})
<|endoftext|>


<commit_before>/* global YT */
/**
 * UTube
 * @module
 * @author Nic Bell
 */

require('custom-event-polyfill');
var objectAssign = require('object-assign');

/**
 * @constructor
 * @param {Element} element - Element where player is created.
 * @param {Object} options - UTube options
 */
function UTube(element, options) {
    this.element = element;

    // Options parameter
    if (options) {
        this.options = objectAssign({}, this.options, options);
    }

    // Options from data attribute
    this.options = objectAssign({}, this.options, JSON.parse(this.element.getAttribute('data-utube-options') || {}));

    this.addEvents();
}

UTube.prototype.options = {
    id: null,
    autohide: 1,
    autoplay: 1,
    color: 'white',
    controls: 1,
    cc_load_policy: 0,
    disablekb: 0,
    enablejsapi: 1,
    end: null,
    fs: 1,
    iv_load_policy: 3,
    loop: 0,
    modestbranding: 1,
    origin: document.domain, // Should be set to domain
    playsinline: 0,
    rel: 0,
    showinfo: 0,
    start: null,
    theme: 'dark',
    wmode: 'opaque',
    dispatch: [25, 50, 75, 100]
};

UTube.prototype.addEvents = function () {
    this.element.addEventListener('click', this.onPlay.bind(this));
};

UTube.prototype.onPlay = function () {
    if (typeof YT == 'undefined' || typeof YT.Player == 'undefined') {
        this.waitingForApi = true;

        this.loadAPI(function () {
            if (this.waitingForApi) {
                this.createPlayer();
                this.waitingForApi = false;
            }
        });
    }
    else {
        this.createPlayer();
    }
};

UTube.prototype.loadAPI = function (callback) {
    var tag = document.createElement('script');
    tag.src = "https://www.youtube.com/iframe_api";

    document.head.appendChild(tag);

    window.onYouTubeIframeAPIReadyCallbacks = window.onYouTubeIframeAPIReadyCallbacks || [];
    window.onYouTubeIframeAPIReadyCallbacks.push(callback.bind(this));

    window.onYouTubeIframeAPIReady = function () {
        for (var i = 0; i < window.onYouTubeIframeAPIReadyCallbacks.length; i++) {
            window.onYouTubeIframeAPIReadyCallbacks[i].call();
        }
    }
};

UTube.prototype.createPlayer = function () {
    this.player = new YT.Player(this.element.querySelector('.movie-player'), {
        height: '100%',
        width: '100%',
        videoId: this.options.id,
        playerVars: {
            'autohide': this.options.autohide,
            'autoplay': this.options.autoplay,
            'color': this.options.color,
            'controls': this.options.controls,
            'disablekb': this.options.disablekb,
            'enablejsapi': this.options.enablejsapi,
            'end': this.options.end,
            'fs': this.options.fs,
            'iv_load_policy': this.options.iv_load_policy,
            'loop': this.options.loop,
            'modestbranding': this.options.modestbranding,
            'playsinline': this.options.playsinline,
            'rel': this.options.rel,
            'showinfo': this.options.showinfo,
            'start': this.options.start,
            'theme': this.options.theme,
            'wmode': 'opaque'
        }
    });

    this.videoPercentage = 0;
    this.player.addEventListener('onStateChange', this.onStateChange.bind(this));
};

UTube.prototype.onStateChange = function (e) {
    var state = e.data,
        trackVideoPercentage = function () {
            var currentTime = this.player.getCurrentTime(),
                duration = this.player.getDuration();

            for (var index = 0; index < this.options.dispatch.length; index++) {
                var percentage = this.options.dispatch[index];

                if (currentTime >= (duration * percentage / 100) && this.videoPercentage < percentage) {
                    this.videoPercentage = percentage;
                    this.dispatchEvent(percentage + '%');
                }
            }
        };

    if (state == YT.PlayerState.PLAYING) {
        this.dispatchEvent('PLAYING');
        this.playerInterval = setInterval(trackVideoPercentage.bind(this), 200);
    }
    else if (state == YT.PlayerState.PAUSED) {
        this.dispatchEvent('PAUSED');
        clearInterval(this.playerInterval);
    }
    else if (state == YT.PlayerState.ENDED) {
        this.dispatchEvent('ENDED');
        clearInterval(this.playerInterval);
        this.close();
    }
    else {
        clearInterval(this.playerInterval);
    }
};

/**
 * Dispatch an event
 * @fires utube
 */
UTube.prototype.dispatchEvent = function (status) {
    var event = new CustomEvent('utube', {
        detail: {
            title: this.player.getVideoData().title,
            status: status
        }
    });

	/**
	 * @event utube
	 * @type {object}
	 * @property {string} title - Video title
	 * @property {string} status - Video status
	 */
    window.dispatchEvent(event);
};

UTube.prototype.pause = function () {
    if (this.player) {
        this.player.pauseVideo();
    }
};

UTube.prototype.close = function () {
    if (this.player) {
        this.player.destroy();
    }
};

module.exports = UTube;<commit_msg>Add `hl` language option<commit_after>/* global YT */
/**
 * UTube
 * @module
 * @author Nic Bell
 */

require('custom-event-polyfill');
var objectAssign = require('object-assign');

/**
 * @constructor
 * @param {Element} element - Element where player is created.
 * @param {Object} options - UTube options
 */
function UTube(element, options) {
    this.element = element;

    // Options parameter
    if (options) {
        this.options = objectAssign({}, this.options, options);
    }

    // Options from data attribute
    this.options = objectAssign({}, this.options, JSON.parse(this.element.getAttribute('data-utube-options') || {}));

    this.addEvents();
}

UTube.prototype.options = {
    id: null,
    autohide: 1,
    autoplay: 1,
    color: 'white',
    controls: 1,
    cc_load_policy: 0,
    disablekb: 0,
    enablejsapi: 1,
    end: null,
    fs: 1,
    iv_load_policy: 3,
    loop: 0,
    modestbranding: 1,
    origin: document.domain, // Should be set to domain
    playsinline: 0,
    rel: 0,
    showinfo: 0,
    start: null,
    theme: 'dark',
    wmode: 'opaque',
    hl: 'en',
    dispatch: [25, 50, 75, 100]
};

UTube.prototype.addEvents = function () {
    this.element.addEventListener('click', this.onPlay.bind(this));
};

UTube.prototype.onPlay = function () {
    if (typeof YT == 'undefined' || typeof YT.Player == 'undefined') {
        this.waitingForApi = true;

        this.loadAPI(function () {
            if (this.waitingForApi) {
                this.createPlayer();
                this.waitingForApi = false;
            }
        });
    }
    else {
        this.createPlayer();
    }
};

UTube.prototype.loadAPI = function (callback) {
    var tag = document.createElement('script');
    tag.src = "https://www.youtube.com/iframe_api";

    document.head.appendChild(tag);

    window.onYouTubeIframeAPIReadyCallbacks = window.onYouTubeIframeAPIReadyCallbacks || [];
    window.onYouTubeIframeAPIReadyCallbacks.push(callback.bind(this));

    window.onYouTubeIframeAPIReady = function () {
        for (var i = 0; i < window.onYouTubeIframeAPIReadyCallbacks.length; i++) {
            window.onYouTubeIframeAPIReadyCallbacks[i].call();
        }
    }
};

UTube.prototype.createPlayer = function () {
    this.player = new YT.Player(this.element.querySelector('.movie-player'), {
        height: '100%',
        width: '100%',
        videoId: this.options.id,
        playerVars: {
            'autohide': this.options.autohide,
            'autoplay': this.options.autoplay,
            'color': this.options.color,
            'controls': this.options.controls,
            'disablekb': this.options.disablekb,
            'enablejsapi': this.options.enablejsapi,
            'end': this.options.end,
            'fs': this.options.fs,
            'iv_load_policy': this.options.iv_load_policy,
            'loop': this.options.loop,
            'modestbranding': this.options.modestbranding,
            'playsinline': this.options.playsinline,
            'rel': this.options.rel,
            'showinfo': this.options.showinfo,
            'start': this.options.start,
            'theme': this.options.theme,
            'wmode': 'opaque',
            'hl': this.options.hl
        }
    });

    this.videoPercentage = 0;
    this.player.addEventListener('onStateChange', this.onStateChange.bind(this));
};

UTube.prototype.onStateChange = function (e) {
    var state = e.data,
        trackVideoPercentage = function () {
            var currentTime = this.player.getCurrentTime(),
                duration = this.player.getDuration();

            for (var index = 0; index < this.options.dispatch.length; index++) {
                var percentage = this.options.dispatch[index];

                if (currentTime >= (duration * percentage / 100) && this.videoPercentage < percentage) {
                    this.videoPercentage = percentage;
                    this.dispatchEvent(percentage + '%');
                }
            }
        };

    if (state == YT.PlayerState.PLAYING) {
        this.dispatchEvent('PLAYING');
        this.playerInterval = setInterval(trackVideoPercentage.bind(this), 200);
    }
    else if (state == YT.PlayerState.PAUSED) {
        this.dispatchEvent('PAUSED');
        clearInterval(this.playerInterval);
    }
    else if (state == YT.PlayerState.ENDED) {
        this.dispatchEvent('ENDED');
        clearInterval(this.playerInterval);
        this.close();
    }
    else {
        clearInterval(this.playerInterval);
    }
};

/**
 * Dispatch an event
 * @fires utube
 */
UTube.prototype.dispatchEvent = function (status) {
    var event = new CustomEvent('utube', {
        detail: {
            title: this.player.getVideoData().title,
            status: status
        }
    });

	/**
	 * @event utube
	 * @type {object}
	 * @property {string} title - Video title
	 * @property {string} status - Video status
	 */
    window.dispatchEvent(event);
};

UTube.prototype.pause = function () {
    if (this.player) {
        this.player.pauseVideo();
    }
};

UTube.prototype.close = function () {
    if (this.player) {
        this.player.destroy();
    }
};

module.exports = UTube;
<|endoftext|>


<commit_before><commit_msg>Added random color on hover<commit_after><|endoftext|>


<commit_before>/** @flow */
import shouldPureComponentUpdate from 'react-pure-render/function'
import React, { Component, PropTypes } from 'react'
import cn from 'classnames'
import raf from 'raf'
import {
  getUpdatedOffsetForIndex,
  getVisibleCellIndices,
  initCellMetadata,
  initOnRowsRenderedHelper
} from '../utils'
import styles from './VirtualScroll.css'

const IS_SCROLLING_TIMEOUT = 150

/**
 * It is inefficient to create and manage a large list of DOM elements within a scrolling container
 * if only a few of those elements are visible. The primary purpose of this component is to improve
 * performance by only rendering the DOM nodes that a user is able to see based on their current
 * scroll position.
 *
 * This component renders a simple list of elements with a fixed height. It is similar to the
 * 'react-infinite' library's `Infinite` component but offers some additional functionality such as
 * the ability to programmatically scroll to ensure that a specific row/item is visible within the
 * container.
 */
export default class VirtualScroll extends Component {
  static shouldComponentUpdate = shouldPureComponentUpdate

  static propTypes = {
    /** Optional CSS class name */
    className: PropTypes.string,
    /** Height constraint for list (determines how many actual rows are rendered) */
    height: PropTypes.number.isRequired,
    /** Optional renderer to be used in place of rows when rowsCount is 0 */
    noRowsRenderer: PropTypes.func,
    /**
     * Callback invoked with information about the slice of rows that were just rendered.
     * ({ startIndex, stopIndex }): void
     */
    onRowsRendered: PropTypes.func,
    /** Either a fixed row height (number) or a function that returns the height of a row given its index. */
    rowHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.func]).isRequired,
    /** Responsbile for rendering a row given an index */
    rowRenderer: PropTypes.func.isRequired,
    /** Number of rows in list. */
    rowsCount: PropTypes.number.isRequired,
    /** Row index to ensure visible (by forcefully scrolling if necessary) */
    scrollToIndex: PropTypes.number
  }

  static defaultProps = {
    noRowsRenderer: () => null,
    onRowsRendered: () => null
  }

  constructor (props, context) {
    super(props, context)

    this.state = {
      computeCellMetadataOnNextUpdate: false,
      isScrolling: false,
      scrollTop: 0
    }

    // Invokes onRowsRendered callback only when start/stop row indices change
    this._OnRowsRenderedHelper = initOnRowsRenderedHelper()

    this._onKeyPress = this._onKeyPress.bind(this)
    this._onScroll = this._onScroll.bind(this)
    this._onWheel = this._onWheel.bind(this)
  }

  /**
   * Forced recompute of row heights.
   * This function should be called if dynamic row heights have changed but nothing else has.
   * Since VirtualScroll receives a :rowsCount it has no way of knowing if the underlying list data has changed.
   */
  recomputeRowHeights () {
    this.setState({
      computeCellMetadataOnNextUpdate: true
    })
  }

  /**
   * Scroll the list to ensure the row at the specified index is visible.
   * This method exists so that a user can forcefully scroll to the same row twice.
   * (The :scrollToIndex property would not change in that case, so it would not be picked up by the component.)
   */
  scrollToRow (scrollToIndex) {
    this._updateScrollTopForScrollToIndex(scrollToIndex)
  }

  componentDidMount () {
    const { onRowsRendered, scrollToIndex } = this.props

    if (scrollToIndex >= 0) {
      // Without setImmediate() the initial scrollingContainer.scrollTop assignment doesn't work
      this._scrollTopId = setImmediate(() => {
        this._scrollTopId = null
        this._updateScrollTopForScrollToIndex()
      })
    }

    // Update onRowsRendered callback
    this._OnRowsRenderedHelper({
      onRowsRendered,
      startIndex: this._renderedStartIndex,
      stopIndex: this._renderedStopIndex
    })
  }

  componentDidUpdate (prevProps, prevState) {
    const { height, onRowsRendered, rowsCount, rowHeight, scrollToIndex } = this.props
    const { scrollTop } = this.state

    // Make sure any changes to :scrollTop (from :scrollToIndex) get applied
    if (scrollTop >= 0 && scrollTop !== prevState.scrollTop) {
      this.refs.scrollingContainer.scrollTop = scrollTop
    }

    const hasScrollToIndex = scrollToIndex >= 0 && scrollToIndex < rowsCount
    const sizeHasChanged = (
      height !== prevProps.height ||
      !prevProps.rowHeight ||
      (
        rowHeight instanceof Number &&
        rowHeight !== prevProps.rowHeight
      )
    )

    // If we have a new scroll target OR if height/row-height has changed,
    // We should ensure that the scroll target is visible.
    if (hasScrollToIndex && (sizeHasChanged || scrollToIndex !== prevProps.scrollToIndex)) {
      this._updateScrollTopForScrollToIndex()

    // If we don't have a selected item but list size or number of children have decreased,
    // Make sure we aren't scrolled too far past the current content.
    } else if (!hasScrollToIndex && (height < prevProps.height || rowsCount < prevProps.rowsCount)) {
      const calculatedScrollTop = getUpdatedOffsetForIndex({
        cellMetadata: this._cellMetadata,
        containerSize: height,
        currentOffset: scrollTop,
        targetIndex: rowsCount - 1
      })

      // Only adjust the scroll position if we've scrolled below the last set of rows.
      if (calculatedScrollTop < scrollTop) {
        this._updateScrollTopForScrollToIndex(rowsCount - 1)
      }
    }

    // Update onRowsRendered callback if start/stop indices have changed
    this._OnRowsRenderedHelper({
      onRowsRendered,
      startIndex: this._renderedStartIndex,
      stopIndex: this._renderedStopIndex
    })
  }

  componentWillMount () {
    this._computeCellMetadata(this.props)
  }

  componentWillUnmount () {
    if (this._disablePointerEventsTimeoutId) {
      clearTimeout(this._disablePointerEventsTimeoutId)
    }
    if (this._scrollTopId) {
      clearImmediate(this._scrollTopId)
    }
    if (this._setNextStateAnimationFrameId) {
      raf.cancel(this._setNextStateAnimationFrameId)
    }
  }

  componentWillUpdate (nextProps, nextState) {
    if (
      nextProps.rowsCount === 0 &&
      nextState.scrollTop !== 0
    ) {
      this.setState({ scrollTop: 0 })
    }

    // Don't compare rowHeight if it's a function because inline functions would cause infinite loops.
    // In that event users should use recomputeRowHeights() to inform of changes.
    if (
      nextState.computeCellMetadataOnNextUpdate ||
      this.props.rowsCount !== nextProps.rowsCount ||
      (
        (
          typeof this.props.rowHeight === 'number' ||
          typeof nextProps.rowHeight === 'number'
        ) &&
        this.props.rowHeight !== nextProps.rowHeight
      )
    ) {
      this._computeCellMetadata(nextProps)

      this.setState({
        computeCellMetadataOnNextUpdate: false
      })

      // Updated cell metadata may have hidden the previous scrolled-to item.
      // In this case we should also update the scrollTop to ensure it stays visible.
      if (this.props.scrollToIndex === nextProps.scrollToIndex) {
        this._updateScrollTopForScrollToIndex()
      }
    }
  }

  render () {
    const {
      className,
      height,
      noRowsRenderer,
      rowsCount,
      rowRenderer
    } = this.props

    const {
      isScrolling,
      scrollTop
    } = this.state

    let childrenToDisplay = []

    // Render only enough rows to cover the visible (vertical) area of the table.
    if (height > 0) {
      const {
        start,
        stop
      } = getVisibleCellIndices({
        cellCount: rowsCount,
        cellMetadata: this._cellMetadata,
        containerSize: height,
        currentOffset: scrollTop
      })

      // Store for onRowsRendered callback in componentDidUpdate
      this._renderedStartIndex = start
      this._renderedStopIndex = stop

      for (let i = start; i <= stop; i++) {
        let datum = this._cellMetadata[i]
        let child = React.cloneElement(
          rowRenderer(i), {
            style: {
              position: 'absolute',
              top: datum.offset,
              width: '100%',
              height: this._getRowHeight(i)
            }
          }
        )

        childrenToDisplay.push(child)
      }
    }

    return (
      <div
        ref='scrollingContainer'
        className={cn(styles.VirtualScroll, className)}
        onKeyDown={this._onKeyPress}
        onScroll={this._onScroll}
        onWheel={this._onWheel}
        tabIndex={0}
        style={{
          height: height
        }}
      >
        {rowsCount > 0 &&
          <div
            className={styles.InnerScrollContainer}
            style={{
              height: this._getTotalRowsHeight(),
              maxHeight: this._getTotalRowsHeight(),
              pointerEvents: isScrolling ? 'none' : 'auto'
            }}
          >
            {childrenToDisplay}
          </div>
        }
        {rowsCount === 0 &&
          noRowsRenderer()
        }
      </div>
    )
  }

  _computeCellMetadata (props) {
    const { rowHeight, rowsCount } = props

    this._cellMetadata = initCellMetadata({
      cellCount: rowsCount,
      size: rowHeight
    })
  }

  _getRowHeight (index) {
    const { rowHeight } = this.props

    return rowHeight instanceof Function
      ? rowHeight(index)
      : rowHeight
  }

  _getTotalRowsHeight () {
    if (this._cellMetadata.length === 0) {
      return 0
    }

    const datum = this._cellMetadata[this._cellMetadata.length - 1]

    return datum.offset + datum.size
  }

  /**
   * Updates the state during the next animation frame.
   * Use this method to avoid multiple renders in a small span of time.
   * This helps performance for bursty events (like onWheel).
   */
  _setNextState (state) {
    if (this._setNextStateAnimationFrameId) {
      raf.cancel(this._setNextStateAnimationFrameId)
    }

    this._setNextStateAnimationFrameId = raf(() => {
      this._setNextStateAnimationFrameId = null
      this.setState(state)
    })
  }

  _stopEvent (event) {
    event.preventDefault()
    event.stopPropagation()
  }

  /**
   * Sets an :isScrolling flag for a small window of time.
   * This flag is used to disable pointer events on the scrollable portion of the table (the rows).
   * This prevents jerky/stuttery mouse-wheel scrolling.
   */
  _temporarilyDisablePointerEvents () {
    if (this._disablePointerEventsTimeoutId) {
      clearTimeout(this._disablePointerEventsTimeoutId)
    }

    this._disablePointerEventsTimeoutId = setTimeout(() => {
      this._disablePointerEventsTimeoutId = null
      this.setState({
        isScrolling: false
      })
    }, IS_SCROLLING_TIMEOUT)
  }

  /**
   * Calculates and adjusts scrollTop if necessary to ensure that the row at the specified index is visible.
   */
  _updateScrollTopForScrollToIndex (scrollToIndexOverride) {
    const scrollToIndex = scrollToIndexOverride !== undefined
      ? scrollToIndexOverride
      : this.props.scrollToIndex

    const { height } = this.props
    const { scrollTop } = this.state

    if (scrollToIndex >= 0) {
      const calculatedScrollTop = getUpdatedOffsetForIndex({
        cellMetadata: this._cellMetadata,
        containerSize: height,
        currentOffset: scrollTop,
        targetIndex: scrollToIndex
      })

      if (scrollTop !== calculatedScrollTop) {
        this.setState({ scrollTop: calculatedScrollTop })
      }
    }
  }

  _onKeyPress (event) {
    const { height, rowsCount } = this.props
    const { scrollTop } = this.state

    let start, datum, newScrollTop

    switch (event.key) {
      case 'ArrowDown':
        this._stopEvent(event) // Prevent key from also scrolling surrounding window

        start = getVisibleCellIndices({
          cellCount: rowsCount,
          cellMetadata: this._cellMetadata,
          containerSize: height,
          currentOffset: scrollTop
        }).start
        datum = this._cellMetadata[start]
        newScrollTop = Math.min(
          this._getTotalRowsHeight() - height,
          scrollTop + datum.size
        )

        this.setState({
          scrollTop: newScrollTop
        })
        break
      case 'ArrowUp':
        this._stopEvent(event) // Prevent key from also scrolling surrounding window

        start = getVisibleCellIndices({
          cellCount: rowsCount,
          cellMetadata: this._cellMetadata,
          containerSize: height,
          currentOffset: scrollTop
        }).start

        this.scrollToRow(Math.max(0, start - 1))
        break
    }
  }

  _onScroll (event) {
    // In certain edge-cases React dispatches an onScroll event with an invalid target.scrollTop.
    // This invalid event can be detected by comparing event.target to this component's scrollable DOM element.
    // See issue #404 for more information.
    if (event.target !== this.refs.scrollingContainer) {
      return
    }

    // When this component is shrunk drastically, React dispatches a series of back-to-back scroll events,
    // Gradually converging on a scrollTop that is within the bounds of the new, smaller height.
    // This causes a series of rapid renders that is slow for long lists.
    // We can avoid that by doing some simple bounds checking to ensure that scrollTop never exceeds the total height.
    const { height } = this.props
    const totalRowsHeight = this._getTotalRowsHeight()
    const scrollTop = Math.min(totalRowsHeight - height, event.target.scrollTop)

    // Certain devices (like Apple touchpad) rapid-fire duplicate events.
    // Don't force a re-render if this is the case.
    if (this.state.scrollTop === scrollTop) {
      return
    }

    // Prevent pointer events from interrupting a smooth scroll
    this._temporarilyDisablePointerEvents()

    // The mouse may move faster then the animation frame does.
    // Use requestAnimationFrame to avoid over-updating.
    this._setNextState({
      isScrolling: true,
      scrollTop
    })
  }

  _onWheel (event) {
    const scrollTop = this.refs.scrollingContainer.scrollTop

    // Certain devices (like Apple touchpad) rapid-fire duplicate events.
    // Don't force a re-render if this is the case.
    if (this.state.scrollTop === scrollTop) {
      return
    }

    // Prevent pointer events from interrupting a smooth scroll
    this._temporarilyDisablePointerEvents()

    // The mouse may move faster then the animation frame does.
    // Use requestAnimationFrame to avoid over-updating.
    this._setNextState({
      isScrolling: true,
      scrollTop
    })
  }
}
<commit_msg>Fix conditional in VirtualScroll<commit_after>/** @flow */
import shouldPureComponentUpdate from 'react-pure-render/function'
import React, { Component, PropTypes } from 'react'
import cn from 'classnames'
import raf from 'raf'
import {
  getUpdatedOffsetForIndex,
  getVisibleCellIndices,
  initCellMetadata,
  initOnRowsRenderedHelper
} from '../utils'
import styles from './VirtualScroll.css'

const IS_SCROLLING_TIMEOUT = 150

/**
 * It is inefficient to create and manage a large list of DOM elements within a scrolling container
 * if only a few of those elements are visible. The primary purpose of this component is to improve
 * performance by only rendering the DOM nodes that a user is able to see based on their current
 * scroll position.
 *
 * This component renders a simple list of elements with a fixed height. It is similar to the
 * 'react-infinite' library's `Infinite` component but offers some additional functionality such as
 * the ability to programmatically scroll to ensure that a specific row/item is visible within the
 * container.
 */
export default class VirtualScroll extends Component {
  static shouldComponentUpdate = shouldPureComponentUpdate

  static propTypes = {
    /** Optional CSS class name */
    className: PropTypes.string,
    /** Height constraint for list (determines how many actual rows are rendered) */
    height: PropTypes.number.isRequired,
    /** Optional renderer to be used in place of rows when rowsCount is 0 */
    noRowsRenderer: PropTypes.func,
    /**
     * Callback invoked with information about the slice of rows that were just rendered.
     * ({ startIndex, stopIndex }): void
     */
    onRowsRendered: PropTypes.func,
    /** Either a fixed row height (number) or a function that returns the height of a row given its index. */
    rowHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.func]).isRequired,
    /** Responsbile for rendering a row given an index */
    rowRenderer: PropTypes.func.isRequired,
    /** Number of rows in list. */
    rowsCount: PropTypes.number.isRequired,
    /** Row index to ensure visible (by forcefully scrolling if necessary) */
    scrollToIndex: PropTypes.number
  }

  static defaultProps = {
    noRowsRenderer: () => null,
    onRowsRendered: () => null
  }

  constructor (props, context) {
    super(props, context)

    this.state = {
      computeCellMetadataOnNextUpdate: false,
      isScrolling: false,
      scrollTop: 0
    }

    // Invokes onRowsRendered callback only when start/stop row indices change
    this._OnRowsRenderedHelper = initOnRowsRenderedHelper()

    this._onKeyPress = this._onKeyPress.bind(this)
    this._onScroll = this._onScroll.bind(this)
    this._onWheel = this._onWheel.bind(this)
  }

  /**
   * Forced recompute of row heights.
   * This function should be called if dynamic row heights have changed but nothing else has.
   * Since VirtualScroll receives a :rowsCount it has no way of knowing if the underlying list data has changed.
   */
  recomputeRowHeights () {
    this.setState({
      computeCellMetadataOnNextUpdate: true
    })
  }

  /**
   * Scroll the list to ensure the row at the specified index is visible.
   * This method exists so that a user can forcefully scroll to the same row twice.
   * (The :scrollToIndex property would not change in that case, so it would not be picked up by the component.)
   */
  scrollToRow (scrollToIndex) {
    this._updateScrollTopForScrollToIndex(scrollToIndex)
  }

  componentDidMount () {
    const { onRowsRendered, scrollToIndex } = this.props

    if (scrollToIndex >= 0) {
      // Without setImmediate() the initial scrollingContainer.scrollTop assignment doesn't work
      this._scrollTopId = setImmediate(() => {
        this._scrollTopId = null
        this._updateScrollTopForScrollToIndex()
      })
    }

    // Update onRowsRendered callback
    this._OnRowsRenderedHelper({
      onRowsRendered,
      startIndex: this._renderedStartIndex,
      stopIndex: this._renderedStopIndex
    })
  }

  componentDidUpdate (prevProps, prevState) {
    const { height, onRowsRendered, rowsCount, rowHeight, scrollToIndex } = this.props
    const { scrollTop } = this.state

    // Make sure any changes to :scrollTop (from :scrollToIndex) get applied
    if (scrollTop >= 0 && scrollTop !== prevState.scrollTop) {
      this.refs.scrollingContainer.scrollTop = scrollTop
    }

    const hasScrollToIndex = scrollToIndex >= 0 && scrollToIndex < rowsCount
    const sizeHasChanged = (
      height !== prevProps.height ||
      !prevProps.rowHeight ||
      (
        typeof rowHeight === 'number' &&
        rowHeight !== prevProps.rowHeight
      )
    )

    // If we have a new scroll target OR if height/row-height has changed,
    // We should ensure that the scroll target is visible.
    if (hasScrollToIndex && (sizeHasChanged || scrollToIndex !== prevProps.scrollToIndex)) {
      this._updateScrollTopForScrollToIndex()

    // If we don't have a selected item but list size or number of children have decreased,
    // Make sure we aren't scrolled too far past the current content.
    } else if (!hasScrollToIndex && (height < prevProps.height || rowsCount < prevProps.rowsCount)) {
      const calculatedScrollTop = getUpdatedOffsetForIndex({
        cellMetadata: this._cellMetadata,
        containerSize: height,
        currentOffset: scrollTop,
        targetIndex: rowsCount - 1
      })

      // Only adjust the scroll position if we've scrolled below the last set of rows.
      if (calculatedScrollTop < scrollTop) {
        this._updateScrollTopForScrollToIndex(rowsCount - 1)
      }
    }

    // Update onRowsRendered callback if start/stop indices have changed
    this._OnRowsRenderedHelper({
      onRowsRendered,
      startIndex: this._renderedStartIndex,
      stopIndex: this._renderedStopIndex
    })
  }

  componentWillMount () {
    this._computeCellMetadata(this.props)
  }

  componentWillUnmount () {
    if (this._disablePointerEventsTimeoutId) {
      clearTimeout(this._disablePointerEventsTimeoutId)
    }
    if (this._scrollTopId) {
      clearImmediate(this._scrollTopId)
    }
    if (this._setNextStateAnimationFrameId) {
      raf.cancel(this._setNextStateAnimationFrameId)
    }
  }

  componentWillUpdate (nextProps, nextState) {
    if (
      nextProps.rowsCount === 0 &&
      nextState.scrollTop !== 0
    ) {
      this.setState({ scrollTop: 0 })
    }

    // Don't compare rowHeight if it's a function because inline functions would cause infinite loops.
    // In that event users should use recomputeRowHeights() to inform of changes.
    if (
      nextState.computeCellMetadataOnNextUpdate ||
      this.props.rowsCount !== nextProps.rowsCount ||
      (
        (
          typeof this.props.rowHeight === 'number' ||
          typeof nextProps.rowHeight === 'number'
        ) &&
        this.props.rowHeight !== nextProps.rowHeight
      )
    ) {
      this._computeCellMetadata(nextProps)

      this.setState({
        computeCellMetadataOnNextUpdate: false
      })

      // Updated cell metadata may have hidden the previous scrolled-to item.
      // In this case we should also update the scrollTop to ensure it stays visible.
      if (this.props.scrollToIndex === nextProps.scrollToIndex) {
        this._updateScrollTopForScrollToIndex()
      }
    }
  }

  render () {
    const {
      className,
      height,
      noRowsRenderer,
      rowsCount,
      rowRenderer
    } = this.props

    const {
      isScrolling,
      scrollTop
    } = this.state

    let childrenToDisplay = []

    // Render only enough rows to cover the visible (vertical) area of the table.
    if (height > 0) {
      const {
        start,
        stop
      } = getVisibleCellIndices({
        cellCount: rowsCount,
        cellMetadata: this._cellMetadata,
        containerSize: height,
        currentOffset: scrollTop
      })

      // Store for onRowsRendered callback in componentDidUpdate
      this._renderedStartIndex = start
      this._renderedStopIndex = stop

      for (let i = start; i <= stop; i++) {
        let datum = this._cellMetadata[i]
        let child = React.cloneElement(
          rowRenderer(i), {
            style: {
              position: 'absolute',
              top: datum.offset,
              width: '100%',
              height: this._getRowHeight(i)
            }
          }
        )

        childrenToDisplay.push(child)
      }
    }

    return (
      <div
        ref='scrollingContainer'
        className={cn(styles.VirtualScroll, className)}
        onKeyDown={this._onKeyPress}
        onScroll={this._onScroll}
        onWheel={this._onWheel}
        tabIndex={0}
        style={{
          height: height
        }}
      >
        {rowsCount > 0 &&
          <div
            className={styles.InnerScrollContainer}
            style={{
              height: this._getTotalRowsHeight(),
              maxHeight: this._getTotalRowsHeight(),
              pointerEvents: isScrolling ? 'none' : 'auto'
            }}
          >
            {childrenToDisplay}
          </div>
        }
        {rowsCount === 0 &&
          noRowsRenderer()
        }
      </div>
    )
  }

  _computeCellMetadata (props) {
    const { rowHeight, rowsCount } = props

    this._cellMetadata = initCellMetadata({
      cellCount: rowsCount,
      size: rowHeight
    })
  }

  _getRowHeight (index) {
    const { rowHeight } = this.props

    return rowHeight instanceof Function
      ? rowHeight(index)
      : rowHeight
  }

  _getTotalRowsHeight () {
    if (this._cellMetadata.length === 0) {
      return 0
    }

    const datum = this._cellMetadata[this._cellMetadata.length - 1]

    return datum.offset + datum.size
  }

  /**
   * Updates the state during the next animation frame.
   * Use this method to avoid multiple renders in a small span of time.
   * This helps performance for bursty events (like onWheel).
   */
  _setNextState (state) {
    if (this._setNextStateAnimationFrameId) {
      raf.cancel(this._setNextStateAnimationFrameId)
    }

    this._setNextStateAnimationFrameId = raf(() => {
      this._setNextStateAnimationFrameId = null
      this.setState(state)
    })
  }

  _stopEvent (event) {
    event.preventDefault()
    event.stopPropagation()
  }

  /**
   * Sets an :isScrolling flag for a small window of time.
   * This flag is used to disable pointer events on the scrollable portion of the table (the rows).
   * This prevents jerky/stuttery mouse-wheel scrolling.
   */
  _temporarilyDisablePointerEvents () {
    if (this._disablePointerEventsTimeoutId) {
      clearTimeout(this._disablePointerEventsTimeoutId)
    }

    this._disablePointerEventsTimeoutId = setTimeout(() => {
      this._disablePointerEventsTimeoutId = null
      this.setState({
        isScrolling: false
      })
    }, IS_SCROLLING_TIMEOUT)
  }

  /**
   * Calculates and adjusts scrollTop if necessary to ensure that the row at the specified index is visible.
   */
  _updateScrollTopForScrollToIndex (scrollToIndexOverride) {
    const scrollToIndex = scrollToIndexOverride !== undefined
      ? scrollToIndexOverride
      : this.props.scrollToIndex

    const { height } = this.props
    const { scrollTop } = this.state

    if (scrollToIndex >= 0) {
      const calculatedScrollTop = getUpdatedOffsetForIndex({
        cellMetadata: this._cellMetadata,
        containerSize: height,
        currentOffset: scrollTop,
        targetIndex: scrollToIndex
      })

      if (scrollTop !== calculatedScrollTop) {
        this.setState({ scrollTop: calculatedScrollTop })
      }
    }
  }

  _onKeyPress (event) {
    const { height, rowsCount } = this.props
    const { scrollTop } = this.state

    let start, datum, newScrollTop

    switch (event.key) {
      case 'ArrowDown':
        this._stopEvent(event) // Prevent key from also scrolling surrounding window

        start = getVisibleCellIndices({
          cellCount: rowsCount,
          cellMetadata: this._cellMetadata,
          containerSize: height,
          currentOffset: scrollTop
        }).start
        datum = this._cellMetadata[start]
        newScrollTop = Math.min(
          this._getTotalRowsHeight() - height,
          scrollTop + datum.size
        )

        this.setState({
          scrollTop: newScrollTop
        })
        break
      case 'ArrowUp':
        this._stopEvent(event) // Prevent key from also scrolling surrounding window

        start = getVisibleCellIndices({
          cellCount: rowsCount,
          cellMetadata: this._cellMetadata,
          containerSize: height,
          currentOffset: scrollTop
        }).start

        this.scrollToRow(Math.max(0, start - 1))
        break
    }
  }

  _onScroll (event) {
    // In certain edge-cases React dispatches an onScroll event with an invalid target.scrollTop.
    // This invalid event can be detected by comparing event.target to this component's scrollable DOM element.
    // See issue #404 for more information.
    if (event.target !== this.refs.scrollingContainer) {
      return
    }

    // When this component is shrunk drastically, React dispatches a series of back-to-back scroll events,
    // Gradually converging on a scrollTop that is within the bounds of the new, smaller height.
    // This causes a series of rapid renders that is slow for long lists.
    // We can avoid that by doing some simple bounds checking to ensure that scrollTop never exceeds the total height.
    const { height } = this.props
    const totalRowsHeight = this._getTotalRowsHeight()
    const scrollTop = Math.min(totalRowsHeight - height, event.target.scrollTop)

    // Certain devices (like Apple touchpad) rapid-fire duplicate events.
    // Don't force a re-render if this is the case.
    if (this.state.scrollTop === scrollTop) {
      return
    }

    // Prevent pointer events from interrupting a smooth scroll
    this._temporarilyDisablePointerEvents()

    // The mouse may move faster then the animation frame does.
    // Use requestAnimationFrame to avoid over-updating.
    this._setNextState({
      isScrolling: true,
      scrollTop
    })
  }

  _onWheel (event) {
    const scrollTop = this.refs.scrollingContainer.scrollTop

    // Certain devices (like Apple touchpad) rapid-fire duplicate events.
    // Don't force a re-render if this is the case.
    if (this.state.scrollTop === scrollTop) {
      return
    }

    // Prevent pointer events from interrupting a smooth scroll
    this._temporarilyDisablePointerEvents()

    // The mouse may move faster then the animation frame does.
    // Use requestAnimationFrame to avoid over-updating.
    this._setNextState({
      isScrolling: true,
      scrollTop
    })
  }
}
<|endoftext|>


<commit_before>'use strict';

const path = require('path');
const fs = require('fs');
const url = require('url');
const _ = require('lodash');
const express = require('express');
const bodyParser = require('body-parser');
const Watchpack = require('watchpack');
const rekitCore = require('rekit-core');
const fetchProjectData = require('./api/fetchProjectData');
const getFileContent = require('./api/getFileContent');
const runBuild = require('./api/runBuild');
const runTest = require('./api/runTest');

const utils = rekitCore.utils;

// rekitCore.utils.setProjectRoot('/Users/i305656/workspace/a2');
rekitCore.plugin.loadPlugins(rekitCore);

let lastProjectData = null;
module.exports = function() { // eslint-disable-line
  let io = null;

  const bgProcesses = {};

  const wp = new Watchpack({
    // options:
    aggregateTimeout: 1000,
    // fire "aggregated" event when after a change for 1000ms no additonal change occured
    // aggregated defaults to undefined, which doesn't fire an "aggregated" event

    poll: false,
    // poll: true - use polling with the default interval
    // poll: 10000 - use polling with an interval of 10s
    // poll defaults to undefined, which prefer native watching methods
    // Note: enable polling when watching on a network path

    ignored: /node_modules/,
    // anymatch-compatible definition of files/paths to be ignored
    // see https://github.com/paulmillr/chokidar#path-filtering
  });

  // Watchpack.prototype.watch(string[] files, string[] directories, [number startTime])
  // Watch src files change only
  wp.watch([], [path.join(rekitCore.utils.getProjectRoot(), 'src')], Date.now() - 10);
  // starts watching these files and directories
  // calling this again will override the files and directories

  wp.on('aggregated', (changes) => {
    // changes: an array of all changed files
    rekitCore.vio.reset();
    // const newProjectData = fetchProjectData();
    // if (_.isEqual(newProjectData, lastProjectData)) return;
    // lastProjectData = newProjectData;
    if (io) io.emit('fileChanged', changes);
  });

  const rootPath = '/rekit';

  function execCmd(req, res) {
    try {
      const args = req.body;
      rekitCore.handleCommand(args);
      const logs = rekitCore.vio.flush();
      rekitCore.vio.reset();
      res.write(JSON.stringify({
        args,
        logs,
      }));
      res.end();
    } catch (e) {
      res.statusCode = 500;
      res.write(e.toString());
      res.end();
    }
  }

  function setupSocketIo(server) {
    io = require('socket.io')(server);

    io.on('connection', (client) => {
      client.on('disconnect', () => {
        console.log('socket disconnected');
      });
    });
  }

  function reply403(res) {
    res.statusCode = 403;
    res.write('Forbidden: Rekit portal is running on readonly mode.');
    res.end();
  }

  function rekitMiddleware(server, app, args) {
    args = args || {};
    setupSocketIo(server);
    const prjRoot = rekitCore.utils.getProjectRoot();
    app.use('/coverage', express.static(utils.joinPath(prjRoot, 'coverage'), { fallthrough: false }));
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({ extended: true }));

    return (req, res, next) => {
      const urlObject = url.parse(req.originalUrl);
      const p = urlObject.pathname.replace(rootPath, '');

      try {
        switch (p) {
          case '/api/project-data': {
            lastProjectData = fetchProjectData();
            res.write(JSON.stringify(Object.assign({
              bgProcesses,
            }, lastProjectData)));
            res.end();
            break;
          }
          case '/api/file-content':
            if (!_.startsWith(utils.joinPath(prjRoot, req.query.file), prjRoot)) {
              // prevent ../.. in req.query.file
              res.statusCode = 403;
              res.write('Forbidden: not allowed to access file out of the project.');
              res.end();
              break;
            }
            if (!fs.existsSync(req.query.file)) {
              res.statusCode = 404;
              res.write(JSON.stringify({ error: 'Not found.' }));
              res.end();
            } else {
              res.write(JSON.stringify({ content: getFileContent(req.query.file) }));
              res.end();
            }
            break;
          case '/api/exec-cmd':
            if (args.readonly) { reply403(res); break; }
            execCmd(req, res);
            break;
          case '/api/run-build':
            if (args.readonly) { reply403(res); break; }
            if (bgProcesses.runningBuild) {
              res.statusCode = 500;
              res.write(JSON.stringify({ error: 'Build process is running...' }));
              res.end();
            } else {
              bgProcesses.runningBuild = true;
              runBuild(io).then(() => {
                bgProcesses.runningBuild = false;
              }).catch(() => {
                bgProcesses.runningBuild = false;
              });
              res.write('{}');
              res.end();
            }
            break;
          case '/api/run-test':
            if (args.readonly) { reply403(res); break; }
            if (bgProcesses.runningTest) {
              res.statusCode = 500;
              res.write(JSON.stringify({ error: 'Test process is running...' }));
              res.end();
            } else {
              bgProcesses.runningTest = true;
              runTest(io, req.body.testFile || '').then(() => {
                bgProcesses.runningTest = false;
              }).catch(() => {
                bgProcesses.runningTest = false;
              });
              res.write('{}');
              res.end();
            }
            break;
          default: {
            if (/^\/api\//.test(p)) {
              res.statusCode = 404;
              res.write(JSON.stringify({ error: `API not found: ${p}` }));
              res.end();
            } else {
              next();
            }
            break;
          }
        }
      } catch (e) {
        res.statusCode = 500;
        res.write(e.toString());
        res.end();
      }
    };
  }
  return rekitMiddleware;
};
<commit_msg>Bug fix.<commit_after>'use strict';

const path = require('path');
const fs = require('fs');
const url = require('url');
const _ = require('lodash');
const express = require('express');
const bodyParser = require('body-parser');
const Watchpack = require('watchpack');
const rekitCore = require('rekit-core');
const fetchProjectData = require('./api/fetchProjectData');
const getFileContent = require('./api/getFileContent');
const runBuild = require('./api/runBuild');
const runTest = require('./api/runTest');

const utils = rekitCore.utils;

rekitCore.utils.setProjectRoot('/Users/i305656/workspace/rekit');
rekitCore.plugin.loadPlugins(rekitCore);

let lastProjectData = null;
module.exports = function() { // eslint-disable-line
  let io = null;

  const bgProcesses = {};

  const wp = new Watchpack({
    // options:
    aggregateTimeout: 1000,
    // fire "aggregated" event when after a change for 1000ms no additonal change occured
    // aggregated defaults to undefined, which doesn't fire an "aggregated" event

    poll: false,
    // poll: true - use polling with the default interval
    // poll: 10000 - use polling with an interval of 10s
    // poll defaults to undefined, which prefer native watching methods
    // Note: enable polling when watching on a network path

    ignored: /node_modules/,
    // anymatch-compatible definition of files/paths to be ignored
    // see https://github.com/paulmillr/chokidar#path-filtering
  });

  // Watchpack.prototype.watch(string[] files, string[] directories, [number startTime])
  // Watch src files change only
  wp.watch([], [path.join(rekitCore.utils.getProjectRoot(), 'src')], Date.now() - 10);
  // starts watching these files and directories
  // calling this again will override the files and directories

  wp.on('aggregated', (changes) => {
    // changes: an array of all changed files
    rekitCore.vio.reset();
    // const newProjectData = fetchProjectData();
    // if (_.isEqual(newProjectData, lastProjectData)) return;
    // lastProjectData = newProjectData;
    if (io) io.emit('fileChanged', changes);
  });

  const rootPath = '/rekit';

  function execCmd(req, res) {
    try {
      const args = req.body;
      rekitCore.handleCommand(args);
      const logs = rekitCore.vio.flush();
      rekitCore.vio.reset();
      res.write(JSON.stringify({
        args,
        logs,
      }));
      res.end();
    } catch (e) {
      res.statusCode = 500;
      res.write(e.toString());
      res.end();
    }
  }

  function setupSocketIo(server) {
    io = require('socket.io')(server);

    io.on('connection', (client) => {
      client.on('disconnect', () => {
        console.log('socket disconnected');
      });
    });
  }

  function reply403(res) {
    res.statusCode = 403;
    res.write('Forbidden: Rekit portal is running on readonly mode.');
    res.end();
  }

  function rekitMiddleware(server, app, args) {
    args = args || {};
    setupSocketIo(server);
    const prjRoot = rekitCore.utils.getProjectRoot();
    app.use('/coverage', express.static(utils.joinPath(prjRoot, 'coverage'), { fallthrough: false }));
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({ extended: true }));

    return (req, res, next) => {
      const urlObject = url.parse(req.originalUrl);
      const p = urlObject.pathname.replace(rootPath, '');

      try {
        switch (p) {
          case '/api/project-data': {
            lastProjectData = fetchProjectData();
            res.write(JSON.stringify(Object.assign({
              bgProcesses,
            }, lastProjectData)));
            res.end();
            break;
          }
          case '/api/file-content':
            if (!_.startsWith(utils.joinPath(prjRoot, req.query.file), prjRoot)) {
              // prevent ../.. in req.query.file
              res.statusCode = 403;
              res.write('Forbidden: not allowed to access file out of the project.');
              res.end();
              break;
            }
            if (!fs.existsSync(req.query.file)) {
              res.statusCode = 404;
              res.write(JSON.stringify({ error: 'Not found.' }));
              res.end();
            } else {
              res.write(JSON.stringify({ content: getFileContent(req.query.file) }));
              res.end();
            }
            break;
          case '/api/exec-cmd':
            if (args.readonly) { reply403(res); break; }
            execCmd(req, res);
            break;
          case '/api/run-build':
            if (args.readonly) { reply403(res); break; }
            if (bgProcesses.runningBuild) {
              res.statusCode = 500;
              res.write(JSON.stringify({ error: 'Build process is running...' }));
              res.end();
            } else {
              bgProcesses.runningBuild = true;
              runBuild(io).then(() => {
                bgProcesses.runningBuild = false;
              }).catch(() => {
                bgProcesses.runningBuild = false;
              });
              res.write('{}');
              res.end();
            }
            break;
          case '/api/run-test':
            if (args.readonly) { reply403(res); break; }
            if (bgProcesses.runningTest) {
              res.statusCode = 500;
              res.write(JSON.stringify({ error: 'Test process is running...' }));
              res.end();
            } else {
              bgProcesses.runningTest = true;
              runTest(io, req.body.testFile || '').then(() => {
                bgProcesses.runningTest = false;
              }).catch(() => {
                bgProcesses.runningTest = false;
              });
              res.write('{}');
              res.end();
            }
            break;
          default: {
            if (/^\/api\//.test(p)) {
              res.statusCode = 404;
              res.write(JSON.stringify({ error: `API not found: ${p}` }));
              res.end();
            } else {
              next();
            }
            break;
          }
        }
      } catch (e) {
        res.statusCode = 500;
        res.write(e.toString());
        res.end();
      }
    };
  }
  return rekitMiddleware;
};
<|endoftext|>


<commit_before>
module.exports = {
  sayHelloInEnglish: function() {
    return "HELLO";
  },

  sayHelloInSpanish: function() {
    return "Hola";
  }
};

function clickFunc(){
    document.getElementById('bgimg').style.visibility = none;
    document.getElementById('lv1').style.visibility = block;
}

<commit_msg>change visibility to display<commit_after>
module.exports = {
  sayHelloInEnglish: function() {
    return "HELLO";
  },

  sayHelloInSpanish: function() {
    return "Hola";
  }
};

function clickFunc(){
    document.getElementById('bgimg').style.display = none;
    document.getElementById('lv1').style.display = block;
}

<|endoftext|>


<commit_before>import React from 'react'

exports.onRenderBody = (
  { setHeadComponents },
  {
    injectHTML = true,
    icons: {
      android = true,
      appleIcon = true,
      appleStartup = true,
      coast = true,
      favicons = true,
      firefox = true,
      twitter = true,
      yandex = true,
      windows = true,
    } = {}
  } = pluginOptions
) => {
  if (injectHTML) {
    const prefix = typeof __PATH_PREFIX__ !== 'undefined' ? __PATH_PREFIX__ : ''
    const HeadComponents = []

    if (android) {
      HeadComponents.push(
        <link
          rel="manifest"
          href={`${prefix}/favicons/manifest.json`}
          key={`android-manifest`}
        />
      )
    }
    if (appleIcon) {
      HeadComponents.push(
        <link
          rel="apple-touch-icon"
          sizes="57x57"
          href={`${prefix}/favicons/apple-touch-icon-57x57.png`}
          key={`apple-touch-icon-57x57`}
        />,
        <link
          rel="apple-touch-icon"
          sizes="60x60"
          href={`${prefix}/favicons/apple-touch-icon-60x60.png`}
          key={`apple-touch-icon-60x60`}
        />,
        <link
          rel="apple-touch-icon"
          sizes="72x72"
          href={`${prefix}/favicons/apple-touch-icon-72x72.png`}
          key={`apple-touch-icon-72x72`}
        />,
        <link
          rel="apple-touch-icon"
          sizes="76x76"
          href={`${prefix}/favicons/apple-touch-icon-76x76.png`}
          key={`apple-touch-icon-76x76`}
        />,
        <link
          rel="apple-touch-icon"
          sizes="114x114"
          href={`${prefix}/favicons/apple-touch-icon-114x114.png`}
          key={`apple-touch-icon-114x114`}
        />,
        <link
          rel="apple-touch-icon"
          sizes="120x120"
          href={`${prefix}/favicons/apple-touch-icon-120x120.png`}
          key={`apple-touch-icon-120x120`}
        />,
        <link
          rel="apple-touch-icon"
          sizes="144x144"
          href={`${prefix}/favicons/apple-touch-icon-144x144.png`}
          key={`apple-touch-icon-144x144`}
        />,
        <link
          rel="apple-touch-icon"
          sizes="152x152"
          href={`${prefix}/favicons/apple-touch-icon-152x152.png`}
          key={`apple-touch-icon-152x152`}
        />,
        <link
          rel="apple-touch-icon"
          sizes="180x180"
          href={`${prefix}/favicons/apple-touch-icon-180x180.png`}
          key={`apple-touch-icon-180x180`}
        />
      )
    }
    if (appleStartup) {
      HeadComponents.push(
        <link
          rel="apple-touch-startup-image"
          media="(device-width: 320px) and (device-height: 480px) and (-webkit-device-pixel-ratio: 1)"
          href={`${prefix}/favicons/apple-touch-startup-image-320x460.png`}
          key={`apple-touch-startup-image-320x460`}
        />,
        <link
          rel="apple-touch-startup-image"
          media="(device-width: 320px) and (device-height: 480px) and (-webkit-device-pixel-ratio: 2)"
          href={`${prefix}/favicons/apple-touch-startup-image-640x920.png`}
          key={`apple-touch-startup-image-640x920`}
        />,
        <link
          rel="apple-touch-startup-image"
          media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2)"
          href={`${prefix}/favicons/apple-touch-startup-image-640x1096.png`}
          key={`apple-touch-startup-image-640x1096`}
        />,
        <link
          rel="apple-touch-startup-image"
          media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2)"
          href={`${prefix}/favicons/apple-touch-startup-image-750x1294.png`}
          key={`apple-touch-startup-image-750x1294`}
        />,
        <link
          rel="apple-touch-startup-image"
          media="(device-width: 414px) and (device-height: 736px) and (orientation: landscape) and (-webkit-device-pixel-ratio: 3)"
          href={`${prefix}/favicons/apple-touch-startup-image-1182x2208.png`}
          key={`apple-touch-startup-image-1182x2208`}
        />,
        <link
          rel="apple-touch-startup-image"
          media="(device-width: 414px) and (device-height: 736px) and (orientation: portrait) and (-webkit-device-pixel-ratio: 3)"
          href={`${prefix}/favicons/apple-touch-startup-image-1242x2148.png`}
          key={`apple-touch-startup-image-1242x2148`}
        />,
        <link
          rel="apple-touch-startup-image"
          media="(device-width: 768px) and (device-height: 1024px) and (orientation: landscape) and (-webkit-device-pixel-ratio: 1)"
          href={`${prefix}/favicons/apple-touch-startup-image-748x1024.png`}
          key={`apple-touch-startup-image-748x1024`}
        />,
        <link
          rel="apple-touch-startup-image"
          media="(device-width: 768px) and (device-height: 1024px) and (orientation: portrait) and (-webkit-device-pixel-ratio: 1)"
          href={`${prefix}/favicons/apple-touch-startup-image-768x1004.png`}
          key={`apple-touch-startup-image-768x1004`}
        />,
        <link
          rel="apple-touch-startup-image"
          media="(device-width: 768px) and (device-height: 1024px) and (orientation: landscape) and (-webkit-device-pixel-ratio: 2)"
          href={`${prefix}/favicons/apple-touch-startup-image-1496x2048.png`}
          key={`apple-touch-startup-image-1496x2048`}
        />,
        <link
          rel="apple-touch-startup-image"
          media="(device-width: 768px) and (device-height: 1024px) and (orientation: portrait) and (-webkit-device-pixel-ratio: 2)"
          href={`${prefix}/favicons/apple-touch-startup-image-1536x2008.png`}
          key={`apple-touch-startup-image-1536x2008`}
        />
      )
    }
    if (favicons) {
      HeadComponents.push(
        <link
          rel="icon"
          type="image/png"
          sizes="32x32"
          href={`${prefix}/favicons/favicon-32x32.png`}
          key={`favicon-32x32`}
        />,
        <link
          rel="icon"
          type="image/png"
          sizes="16x16"
          href={`${prefix}/favicons/favicon-16x16.png`}
          key={`favicon-16x16`}
        />,
        <link
          rel="shortcut icon"
          href={`${prefix}/favicons/favicon.ico`}
          key={`favicon`}
        />
      )
    }
    if (coast) {
      HeadComponents.push(
        <link
          rel="icon"
          type="image/png"
          sizes="228x228"
          href={`${prefix}/favicons/coast-228x228.png`}
          key={`coast-228x228`}
        />
      )
    }
    if (windows) {
      HeadComponents.push(
        <meta
          name="msapplication-TileImage"
          content={`${prefix}/favicons/mstile-144x144.png`}
          key={`mstile-144x144`}
        />,
        <meta
          name="msapplication-config"
          content={`${prefix}/favicons/browserconfig.xml`}
          key={`browserconfig-xml`}
        />
      )
    }
    if (yandex) {
      HeadComponents.push(
        <link
          rel="yandex-tableau-widget"
          href={`${prefix}/favicons/yandex-browser-manifest.json`}
          key={`yandex-manifest`}
        />
      )
    }

    setHeadComponents(HeadComponents)
  }
}
<commit_msg>Only apply pathPrefix if —prefix-paths set<commit_after>import React from 'react'

exports.onRenderBody = (
  { setHeadComponents },
  {
    injectHTML = true,
    icons: {
      android = true,
      appleIcon = true,
      appleStartup = true,
      coast = true,
      favicons = true,
      firefox = true,
      twitter = true,
      yandex = true,
      windows = true,
    } = {}
  } = pluginOptions
) => {
  if (injectHTML) {
    const prefix = (typeof __PREFIX_PATHS__ !== "undefined" && __PREFIX_PATHS__) ? __PATH_PREFIX__ : '';
    const HeadComponents = [];

    if (android) {
      HeadComponents.push(
        <link
          rel="manifest"
          href={`${prefix}/favicons/manifest.json`}
          key={`android-manifest`}
        />
      )
    }
    if (appleIcon) {
      HeadComponents.push(
        <link
          rel="apple-touch-icon"
          sizes="57x57"
          href={`${prefix}/favicons/apple-touch-icon-57x57.png`}
          key={`apple-touch-icon-57x57`}
        />,
        <link
          rel="apple-touch-icon"
          sizes="60x60"
          href={`${prefix}/favicons/apple-touch-icon-60x60.png`}
          key={`apple-touch-icon-60x60`}
        />,
        <link
          rel="apple-touch-icon"
          sizes="72x72"
          href={`${prefix}/favicons/apple-touch-icon-72x72.png`}
          key={`apple-touch-icon-72x72`}
        />,
        <link
          rel="apple-touch-icon"
          sizes="76x76"
          href={`${prefix}/favicons/apple-touch-icon-76x76.png`}
          key={`apple-touch-icon-76x76`}
        />,
        <link
          rel="apple-touch-icon"
          sizes="114x114"
          href={`${prefix}/favicons/apple-touch-icon-114x114.png`}
          key={`apple-touch-icon-114x114`}
        />,
        <link
          rel="apple-touch-icon"
          sizes="120x120"
          href={`${prefix}/favicons/apple-touch-icon-120x120.png`}
          key={`apple-touch-icon-120x120`}
        />,
        <link
          rel="apple-touch-icon"
          sizes="144x144"
          href={`${prefix}/favicons/apple-touch-icon-144x144.png`}
          key={`apple-touch-icon-144x144`}
        />,
        <link
          rel="apple-touch-icon"
          sizes="152x152"
          href={`${prefix}/favicons/apple-touch-icon-152x152.png`}
          key={`apple-touch-icon-152x152`}
        />,
        <link
          rel="apple-touch-icon"
          sizes="180x180"
          href={`${prefix}/favicons/apple-touch-icon-180x180.png`}
          key={`apple-touch-icon-180x180`}
        />
      )
    }
    if (appleStartup) {
      HeadComponents.push(
        <link
          rel="apple-touch-startup-image"
          media="(device-width: 320px) and (device-height: 480px) and (-webkit-device-pixel-ratio: 1)"
          href={`${prefix}/favicons/apple-touch-startup-image-320x460.png`}
          key={`apple-touch-startup-image-320x460`}
        />,
        <link
          rel="apple-touch-startup-image"
          media="(device-width: 320px) and (device-height: 480px) and (-webkit-device-pixel-ratio: 2)"
          href={`${prefix}/favicons/apple-touch-startup-image-640x920.png`}
          key={`apple-touch-startup-image-640x920`}
        />,
        <link
          rel="apple-touch-startup-image"
          media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2)"
          href={`${prefix}/favicons/apple-touch-startup-image-640x1096.png`}
          key={`apple-touch-startup-image-640x1096`}
        />,
        <link
          rel="apple-touch-startup-image"
          media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2)"
          href={`${prefix}/favicons/apple-touch-startup-image-750x1294.png`}
          key={`apple-touch-startup-image-750x1294`}
        />,
        <link
          rel="apple-touch-startup-image"
          media="(device-width: 414px) and (device-height: 736px) and (orientation: landscape) and (-webkit-device-pixel-ratio: 3)"
          href={`${prefix}/favicons/apple-touch-startup-image-1182x2208.png`}
          key={`apple-touch-startup-image-1182x2208`}
        />,
        <link
          rel="apple-touch-startup-image"
          media="(device-width: 414px) and (device-height: 736px) and (orientation: portrait) and (-webkit-device-pixel-ratio: 3)"
          href={`${prefix}/favicons/apple-touch-startup-image-1242x2148.png`}
          key={`apple-touch-startup-image-1242x2148`}
        />,
        <link
          rel="apple-touch-startup-image"
          media="(device-width: 768px) and (device-height: 1024px) and (orientation: landscape) and (-webkit-device-pixel-ratio: 1)"
          href={`${prefix}/favicons/apple-touch-startup-image-748x1024.png`}
          key={`apple-touch-startup-image-748x1024`}
        />,
        <link
          rel="apple-touch-startup-image"
          media="(device-width: 768px) and (device-height: 1024px) and (orientation: portrait) and (-webkit-device-pixel-ratio: 1)"
          href={`${prefix}/favicons/apple-touch-startup-image-768x1004.png`}
          key={`apple-touch-startup-image-768x1004`}
        />,
        <link
          rel="apple-touch-startup-image"
          media="(device-width: 768px) and (device-height: 1024px) and (orientation: landscape) and (-webkit-device-pixel-ratio: 2)"
          href={`${prefix}/favicons/apple-touch-startup-image-1496x2048.png`}
          key={`apple-touch-startup-image-1496x2048`}
        />,
        <link
          rel="apple-touch-startup-image"
          media="(device-width: 768px) and (device-height: 1024px) and (orientation: portrait) and (-webkit-device-pixel-ratio: 2)"
          href={`${prefix}/favicons/apple-touch-startup-image-1536x2008.png`}
          key={`apple-touch-startup-image-1536x2008`}
        />
      )
    }
    if (favicons) {
      HeadComponents.push(
        <link
          rel="icon"
          type="image/png"
          sizes="32x32"
          href={`${prefix}/favicons/favicon-32x32.png`}
          key={`favicon-32x32`}
        />,
        <link
          rel="icon"
          type="image/png"
          sizes="16x16"
          href={`${prefix}/favicons/favicon-16x16.png`}
          key={`favicon-16x16`}
        />,
        <link
          rel="shortcut icon"
          href={`${prefix}/favicons/favicon.ico`}
          key={`favicon`}
        />
      )
    }
    if (coast) {
      HeadComponents.push(
        <link
          rel="icon"
          type="image/png"
          sizes="228x228"
          href={`${prefix}/favicons/coast-228x228.png`}
          key={`coast-228x228`}
        />
      )
    }
    if (windows) {
      HeadComponents.push(
        <meta
          name="msapplication-TileImage"
          content={`${prefix}/favicons/mstile-144x144.png`}
          key={`mstile-144x144`}
        />,
        <meta
          name="msapplication-config"
          content={`${prefix}/favicons/browserconfig.xml`}
          key={`browserconfig-xml`}
        />
      )
    }
    if (yandex) {
      HeadComponents.push(
        <link
          rel="yandex-tableau-widget"
          href={`${prefix}/favicons/yandex-browser-manifest.json`}
          key={`yandex-manifest`}
        />
      )
    }

    setHeadComponents(HeadComponents)
  }
}
<|endoftext|>