<commit_before>// eslint-disable-next-line no-unused-vars
const viz = {
  radius: 5,

  init(nodes, links) {
    const svg = d3.select('svg');
    const nodesGroup = svg.append('g');
    const linksGroup = svg.append('g');

    nodesGroup.attr('class', 'nodes');
    linksGroup.attr('class', 'links');

    this.allCircles = nodesGroup.selectAll('circle');
    this.allLabels = nodesGroup.selectAll('text');
    this.allLines = linksGroup.selectAll('line');
    this.simulation = this.simulationStart(nodes, links);

    this.draw(nodes, links);
    this.updatePositions();
    this.simulation.tick();
  },

  simulationStart(nodes, links) {
    const { width, height } = this.getDimensions();
    const linkForce = d3.forceLink(links);
    let simulation;

    if (!this.simulation) {
      simulation = d3.forceSimulation(nodes);
    } else {
      simulation = this.simulation;
      this.simulation.stop();
      simulation.nodes(nodes);
    }

    linkForce.id((d) => d.hostname);
    linkForce.distance(80);
    simulation.force('charge', d3.forceManyBody());
    simulation.force('link', linkForce);
    simulation.force('center', d3.forceCenter(width/2, height/2));
    simulation.force('collide', d3.forceCollide(5));
    simulation.alphaTarget(1);

    return simulation;
  },

  simulationRestart(nodes, links) {
    this.simulation.nodes(nodes);
    const linkForce = this.simulation.force('link');
    linkForce.id((d) => d.hostname);
    linkForce.distance(80);
    linkForce.links(links);
    this.simulation.force('link', linkForce);
    this.simulation.alpha(1);
    this.simulation.restart();

    return this.simulation;
  },

  getDimensions() {
    const visualization = document.getElementById('visualization');
    const { width, height } = visualization.getBoundingClientRect();

    return {
      width,
      height
    };
  },

  updatePositions() {
    const { width, height } = this.getDimensions();
    this.allCircles
      .attr('cx', (d) => (
        d.x = Math.max(this.radius, Math.min(width - this.radius, d.x))
      ))
      .attr('cy', (d) => (
        d.y = Math.max(this.radius, Math.min(height - this.radius, d.y))
      ));
    this.allLabels
      .attr('x', (d) => d.x)
      .attr('y', (d) => d.y);
    this.allLines
      .attr('x1', (d) => d.source.x)
      .attr('y1', (d) => d.source.y)
      .attr('x2', (d) => d.target.x)
      .attr('y2', (d) => d.target.y);
  },

  drawNodes(nodes) {
    this.allCircles = this.allCircles.data(nodes, (d) => d.hostname);
    this.allCircles.exit().remove();

    let newNodes = this.allCircles.enter();
    newNodes = newNodes.append('circle');
    newNodes.attr('fill', 'red');
    newNodes.attr('r', this.radius);

    this.allCircles = newNodes.merge(this.allCircles);
  },

  drawLabels(nodes) {
    this.allLabels = this.allLabels.data(nodes, (d) => d.hostname);
    this.allLabels.exit().remove();

    let newText = this.allLabels.enter();
    newText = newText.append('text');
    newText.attr('class', 'textLabel')
    .text((d) => d.hostname)
    .attr('fill', 'white');

    this.allLabels = newText.merge(this.allLabels);
  },

  drawLinks(links) {
    this.allLines = this.allLines
      .data(links, (d) => `${d.source.hostname}-${d.target.hostname}`);

    this.allLines.exit().remove();

    let newLinks = this.allLines.enter();
    newLinks = newLinks.append('line');
    this.allLines = newLinks.merge(this.allLines);
  },

  draw(nodes, links) {
    this.drawNodes(nodes);
    this.drawLabels(nodes);
    this.drawLinks(links);

    this.simulation = this.simulationRestart(nodes, links);
    this.updatePositions();
    this.simulation.tick();
  }
};
<commit_msg>misc changes. fixes #94<commit_after>// eslint-disable-next-line no-unused-vars
const viz = {
  radius: 5,

  init(nodes, links) {
    const { width, height } = this.getDimensions();
    const svg = d3.select('svg');
    const nodesGroup = svg.append('g');
    const linksGroup = svg.append('g');
    nodesGroup.attr('class', 'nodes');
    linksGroup.attr('class', 'links');

    this.width = width;
    this.height = height;
    this.allCircles = nodesGroup.selectAll('circle');
    this.allLabels = nodesGroup.selectAll('text');
    this.allLines = linksGroup.selectAll('line');
    this.simulation = this.simulationStart(nodes, links);

    this.updatePositions();
    this.simulation.tick();
    this.draw(nodes, links);
  },

  simulationStart(nodes, links) {
    const linkForce = d3.forceLink(links);
    let simulation;

    if (!this.simulation) {
      simulation = d3.forceSimulation(nodes);
    } else {
      simulation = this.simulation;
      this.simulation.stop();
      simulation.nodes(nodes);
    }

    linkForce.id((d) => d.hostname);
    linkForce.distance(100);
    simulation.force('charge', d3.forceManyBody());
    simulation.force('link', linkForce);
    simulation.force('center', d3.forceCenter(this.width/2, this.height/2));
    simulation.force('collide', d3.forceCollide(5));
    simulation.alphaTarget(1);

    return simulation;
  },

  getDimensions() {
    const visualization = document.getElementById('visualization');
    const { width, height } = visualization.getBoundingClientRect();

    return {
      width,
      height
    };
  },

  getX(x) {
    // forces the nodes to be contained within the bounding box's x coordinate
    return Math.max(this.radius, Math.min(this.width - this.radius, x));
  },

  getY(y) {
    // forces the nodes to be contained within the bounding box's y coordinate
    return Math.max(this.radius, Math.min(this.height - this.radius, y));
  },

  updatePositions() {
    this.allCircles
      .attr('cx', (d) => this.getX(d.x))
      .attr('cy', (d) => this.getY(d.y));
    this.allLabels
      .attr('x', (d) => this.getX(d.x))
      .attr('y', (d) => this.getY(d.y));
    this.allLines
      .attr('x1', (d) => d.source.x)
      .attr('y1', (d) => d.source.y)
      .attr('x2', (d) => d.target.x)
      .attr('y2', (d) => d.target.y);
  },

  drawNodes(nodes) {
    this.allCircles = this.allCircles.data(nodes, (d) => d.hostname);
    this.allCircles.exit().remove();

    let newNodes = this.allCircles.enter();
    newNodes = newNodes.append('circle');
    newNodes.attr('fill', (d) => {
      if (d.firstParty) {
        return 'red';
      }
      return 'blue';
    });
    newNodes.attr('r', this.radius);

    this.allCircles = newNodes.merge(this.allCircles);
  },

  drawLabels(nodes) {
    this.allLabels = this.allLabels.data(nodes, (d) => d.hostname);
    this.allLabels.exit().remove();

    let newText = this.allLabels.enter();
    newText = newText.append('text');
    newText.attr('class', 'textLabel')
    .text((d) => d.hostname)
    .attr('fill', 'white');

    this.allLabels = newText.merge(this.allLabels);
  },

  drawLinks(links) {
    this.allLines = this.allLines
      .data(links, (d) => `${d.source.hostname}-${d.target.hostname}`);

    this.allLines.exit().remove();

    let newLinks = this.allLines.enter();
    newLinks = newLinks.append('line');

    this.allLines = newLinks.merge(this.allLines);
  },

  draw(nodes, links) {
    this.drawNodes(nodes);
    this.drawLabels(nodes);
    this.drawLinks(links);

    this.simulation = this.simulationStart(nodes, links);
    this.updatePositions();
    this.simulation.tick();
  }
};
<|endoftext|>


<commit_before>import React from "react";

const board = require("./board.js")
console.log("white = " + board.WHITE)

// TODO: render a pieceless chessboard

const App = React.createClass({
  getInitialState() {
    return { board: new board.Board() }
  },

  render() {
    return (

        hello react world!

    );
  }
})

const Square = React.createClass({
  render() {
    const size = 50
    let style = {
      left: this.props.x * size,
      bottom: this.props.y * size,
    }
    if ((this.props.x + this.props.y) % 2 == 0) {
      // Dark square
    } else {
      // Light square
    }
  }
})

React.render(<App/>, document.body);

<commit_msg>make some Square react things<commit_after>import React from "react";

const board = require("./board.js")
console.log("white = " + board.WHITE)

// TODO: render a pieceless chessboard

const App = React.createClass({
  getInitialState() {
    return { board: new board.Board() }
  },

  render() {
    let squares = []
    for (let x = 0; x < 8; x++) {
      for (let y = 0; y < 8; y++) {
        squares.push(<Square x={x} y={y} />)
      }
    }

    return (

      {squares}

    );
  }
})

const Square = React.createClass({
  render() {
    const size = 50
    let style = {
      left: this.props.x * size,
      bottom: this.props.y * size,
    }
    if ((this.props.x + this.props.y) % 2 == 0) {
      // Dark square
    } else {
      // Light square
    }
  }
})

React.render(<App/>, document.body);

<|endoftext|>


<commit_before>/*
 * JSquiz
 * JavaScript driven Quiz Engine
 *
 * file: jsquiz.js
 * v0.9.2 / 2015.09.05
 *
 * (c) 2015 Bernd Busse
 */

var holder, intro, result,
    progressKeeper, progress,
    notice;

function logErr(msg) {
    var text = "Fehler: " + msg;
    console.log(text);
    alert(text);
}

function validateJson(data) {
    var valid = true;

    if (data.length == 0) {
        logErr("Datei zu kurz.");
        valid = false;
    } else {
        if (!data.title) {
            logErr("Titel fehlt.");
            valid = false;
        }
        if (!data.mode) {
            logErr("Modus fehlt.");
            valid = false;
        }

        if (!data.questions || data.questions.length == 0) {
            logErr("Fragen fehlen.");
            valid = false;
        } else {
            q = data.questions;
            for (i = 0; i < q.length; i++) {
                if (!q[i].question) {
                    logErr("Frage zu Nummer " + i + " fehlt.");
                    valid = false;
                }
                if (!q[i].answers || q[i].answers.length == 0) {
                    logErr("Antworten zu Nummer " + i + " fehlen.");
                    valid = false;
                }
                if (!q[i].correct || !(q[i].correct >= 1 && q[i].correct <= q[i].answers.length)) {
                    logErr("Richtige Antwort zu Nummer " + i + " fehlt.");
                    valid = false;
                }
            }
        }
    }

    return valid;
}

function genQuestionContainer(data, index, mode) {
    var cont = $("<div>");
    cont.addClass("questionContainer");
    cont.addClass("hide");

    var teamContainer = $("<ul>");
    teamContainer.addClass("teamlist");
    if (mode == 1) {
        teamContainer.css("visibility", "hidden");
    }
    for (i = 0; i < mode; i++) {
        var team = $("<li>");
        team.addClass("team");
        team.html("Team " + String.fromCharCode(65 + i));

        if (i == (index % mode)) {
            team.addClass("active");
            cont.attr("data-team", i + 1);
        }

        teamContainer.append(team);
    }

    var question = $("<div>");
    question.addClass("question");
    question.html(data.question);

    var answers = $("<ul>");
    answers.addClass("answers");
    answers.addClass("button_container");

    for (i = 0; i < data.answers.length; i++) {
        var answer = data.answers[i];

        var ansButton = $("<button>");
        ansButton.addClass("check_button");
        ansButton.attr("data-correct", (i == data.correct - 1));
        ansButton.append(answer);

        var ansContainer = $("<li>");
        ansContainer.append(ansButton);
        ansContainer.css("visibility", "hidden");
        ansContainer.css("opacity", 0);

        answers.append(ansContainer);
    }

    var btnContainer = $("<div>");
    btnContainer.addClass("btnContainer");

    if (data.isFirst) {
        var div = $("<div>");
        div.addClass("prev");

        var btnExit = $("<a>");
        btnExit.attr("id", "btnExit");
        btnExit.attr("href", "#");

        btnContainer.append(div.append(btnExit));
    } else {
        var div = $("<div>");
        div.addClass("prev");

        var btnPrev = $("<a>");
        btnPrev.addClass("btnPrev");
        btnPrev.attr("href", "#");

        btnContainer.append(div.append(btnPrev));
    }

    var div = $("<div>");
    div.addClass("next");

    var btnCheck = $("<a>");
    btnCheck.addClass("btnCheck");
    btnCheck.addClass("disabled");
    btnCheck.attr("href", "#");

    div.append(btnCheck);

    if (!data.isLast) {
        var btnNext = $("<a>");
        btnNext.addClass("btnNext");
        btnNext.attr("href", "#");
        btnNext.hide();

        div.append(btnNext);
    } else {
        var btnFinish = $("<a>");
        btnFinish.attr("id", "btnShowResult");
        btnFinish.attr("href", "#");
        btnFinish.hide();

        div.append(btnFinish);
    }

    btnContainer.append(div);

    cont.append(teamContainer);
    cont.append(question);
    cont.append(answers);
    cont.append(btnContainer);
    cont.append($("<div>").addClass("clear"));

    return cont;
}

function genResultTable(mode) {
    var list = $('<table>');
    var head = $('<tr>');

    head.append($('<th>').html("Richtig"));
    head.append($('<th>').html("Prozent"));
    list.append(head);

    if (mode == 1) {
        var myQuestions = $('.questionContainer[data-team]');
        var max = myQuestions.length;
        var corr = max - myQuestions.find('.check_button.wrong').length;

        var row = $('<tr>');
        row.append($('<td>').html(corr + " von " + max));
        row.append($('<td>').html(Math.round(100 * corr / max) + "%"));
        list.append(row);
    } else {
        head.prepend($('<th>').html("Team"));

        for (d = 0; d < mode; d++) {
            var myQuestions = $('.questionContainer[data-team=' + (d + 1) + ']');
            var max = myQuestions.length;
            var corr = max - myQuestions.find('.check_button.wrong').length;

            var row = $('<tr>');
            row.append($('<td>').html("Team " + String.fromCharCode(65 + d)));
            row.append($('<td>').html(corr + " von " + max));
            row.append($('<td>').html(Math.round(100 * corr / max) + "%"));
            list.append(row);
        }
    }

    return list;
}

function loadQuizFromFile(file, callback) {
    console.log("Load Quiz \"" + file.name + "\" ...");

    fr = new FileReader();
    fr.onload = function(e) {
        var data;

        try {
            data = JSON.parse(e.target.result);
        } catch (err) {
            logErr("Kann Datei nicht lesen: " + err.message);
            callback(false, null);
            return;
        }

        valid = validateJson(data);
        callback(valid, data);
    };
    fr.readAsText(file);
}

function checkAnswer(e) {
    $(this).parents('.questionContainer').find('.check_button').each(function(index, elem) {
        var b = $(elem);

        if (b.attr("data-correct") == "true") {
            b.addClass("correct");
        } else if (b.hasClass("selected")) {
            b.addClass("wrong");
        }

        b.removeClass("selected");
        b.css("cursor", "default");
        b.off('click');
    });

    $(this).fadeOut(250, function() {
        $(this).siblings('.btnNext').fadeIn(250);
        $(this).siblings('#btnShowResult').fadeIn(250);
    });
}

function startQuiz(data) {
    console.log("Start Quiz \"" + data.title + "\" ...");

    q = data.questions;
    widthFactor = 100 / q.length;
    last = intro;

    for (c = 0; c < q.length; c++) {
        q[c].isFirst = (c == 0);
        q[c].isLast = (c == q.length - 1);
        cont = genQuestionContainer(q[c], c, data.mode);
        last.after(cont);
        last = cont;
    }

    $(document).off('keydown');
    $(document).on('keydown', quizHandler);
    $(document).on('click', function(evt) {
        var hidden = $('.questionContainer:visible .answers li[style*=hidden]');
        if (hidden.length > 0) {
            var child = hidden.first();
            child.css("visibility", "visible");
            child.fadeTo(500, 1);
        }
    });

    $('.btnPrev').on('click', function(e) {
        $(this).parents('.questionContainer').fadeOut(500, function() {
            var prev = $(this).prev();
            prev.fadeIn(500);
            progress.animate({ width: Math.round((prev.index() - 1) * widthFactor) + "%" }, 500);
        });
    });

    $('.btnNext').on('click', function(e) {
        $(this).parents('.questionContainer').fadeOut(500, function() {
            var next = $(this).next();
            next.fadeIn(500);
            progress.animate({ width: Math.round((next.index() - 1) * widthFactor) + "%" }, 500);
        });
    });

    $('#btnShowResult').on('click', function(e) {
        $(this).parents('.questionContainer').fadeOut(500, function() {
            var table = genResultTable(data.mode);
            result.find('#result_container').html(table);

            result.fadeIn(500);
            progress.animate({ width: "100%" }, 500);
        });
    });

    var animateExit = function(evt) {
        progressKeeper.fadeOut(500);
        notice.fadeIn(500);
        $(this).parents('.questionContainer').fadeOut(500, function() {
            intro.fadeIn(500, function() {
                resetQuiz();
            });
        });
    };

    $('#btnExit').on('click', animateExit);
    $('#btnRetry').on('click', animateExit);

    $('.check_button').on('click', function(e) {
        var container = $(this).parents('.questionContainer');

        if (container.find('.answers li[style*=hidden]').length == 0) {
            container.find('.btnCheck').on('click', checkAnswer);
            container.find('.btnCheck').removeClass("disabled");

            container.find('.check_button').removeClass("selected");
            $(this).addClass("selected");
        }
    });

    progressKeeper.fadeIn(500);
    notice.fadeOut(500);
}

function resetQuiz(time, data) {
    $('.questionContainer').each(function(i, elem) {
        if (elem.id) {
            return;
        }
        elem.remove();
    });

    progress.width(0);

    $(document).off('keydown');
    $(document).off('keydown');
    $(document).on('keydown', mainHandler);
}

/* onload */
function jsquiz_onload() {
    console.log(" === [ J S q u i z ] === ");

    var fileinput = $('input[name="fileinput"]');
    var btnInput = $('#btnInput');
    var btnStart = $('#btnStart');

    holder = $('#main-quiz-holder');
    intro = $('#intro-container');
    result = $('#result-container');
    progressKeeper = $('#progressKeeper');
    progress = $('#progress');
    notice = $('#notice');

    var quizData;

    fileinput.on('change', function() {
        filename = fileinput.val().replace(/C:\\fakepath\\/i, '');

        if (filename == "") {
            btnInput.html("Keine Datei ausgewählt!");
            btnStart.prop("disabled", true);
            btnStart.html("START");
            quizData = null
            return;
        }

        btnInput.html(filename);
        loadQuizFromFile(fileinput[0].files[0], function(valid, data) {
            if (!valid) {
                btnStart.prop("disabled", true);
                btnStart.html("ungültige Datei!");
                quizData = null;
                return;
            }

            btnStart.prop("disabled", false);
            btnStart.html("Starte " + data.title);
            quizData = data;
        });
    });

    btnInput.on('click', function(e) {
        fileinput.click();
    });

    btnStart.on('click', function(e) {
        startQuiz(quizData);
        $(this).parents('#intro-container').fadeOut(500, function() {
            $(this).next().fadeIn(500);
        });
    });

    resetQuiz();

    progressKeeper.hide();
    notice.show();
}

function mainHandler(evt) {
    if (!(evt.altKey || evt.ctrlKey || evt.metaKey || evt.shiftKey) &&
            !(evt.keyCode >= 112 && evt.keyCode <= 123)) {
        evt.preventDefault();

        switch (evt.keyCode) {
            case 76: // l
            case 79: // o
                $('#btnInput').click();
                break;
            case 13: // return
            case 32: // space
                $('#btnStart:enabled').click();
                break;
        }
    }
}

function quizHandler(evt) {
    var selAnswer = function(index) {
        $('.questionContainer:visible li:nth-child(' + index + ') button').click();
    };

    if (!(evt.altKey || evt.ctrlKey || evt.metaKey || evt.shiftKey) &&
            !(evt.keyCode >= 112 && evt.keyCode <= 123)) {
        evt.preventDefault();

        switch (evt.keyCode) {
            case 65: // a
            case 66: // b
            case 67: // c
            case 68: // d
            case 69: // e
            case 70: // f
            case 71: // g
            case 72: // h
                if ($('.questionContainer:visible .answers li[style*=hidden]').length == 0) {
                    selAnswer(evt.keyCode - 64);
                }
                break;
            case 49: // 1
            case 50: // 2
            case 51: // 3
            case 52: // 4
            case 53: // 5
            case 54: // 6
            case 55: // 7
            case 56: // 8
                if ($('.questionContainer:visible .answers li[style*=hidden]').length == 0) {
                    selAnswer(evt.keyCode - 48);
                }
                break;
            case 39: // right
            case 13: // return
            case 32: // space
            case 34: // pg_down
                var hidden = $('.questionContainer:visible .answers li[style*=hidden]');
                if (hidden.length > 0) {
                    var child = hidden.first();
                    child.css("visibility", "visible");
                    child.fadeTo(500, 1);
                } else {
                    $('.questionContainer:visible .btnNext:visible').click();
                    $('.questionContainer:visible #btnShowResult:visible').click();
                    $('.questionContainer:visible .btnCheck:visible').click();
                }
                break;
            case 37: // left
            case 8:  // bspace
            case 33: // pg_up
                $('.questionContainer:visible .btnPrev:visible').click();
                $('.questionContainer:visible #btnExit:visible').click();
                break;
            case 27: // escape
                $('.questionContainer:visible').fadeOut(500);
                $('#btnRetry:visible').click();
                $('#btnExit').click();
                break;
        }
    }
}

$(document).ready(function () {
    // Check for the various File API support.
    if (!window.File || !window.FileReader || !window.Blob) {
        alert("The File APIs are not fully supported by your browser.");
    }
});

<commit_msg>Fixed issue with mousebtn<commit_after>/*
 * JSquiz
 * JavaScript driven Quiz Engine
 *
 * file: jsquiz.js
 * v0.9.2 / 2015.09.05
 *
 * (c) 2015 Bernd Busse
 */

var holder, intro, result,
    progressKeeper, progress,
    notice;

function logErr(msg) {
    var text = "Fehler: " + msg;
    console.log(text);
    alert(text);
}

function validateJson(data) {
    var valid = true;

    if (data.length == 0) {
        logErr("Datei zu kurz.");
        valid = false;
    } else {
        if (!data.title) {
            logErr("Titel fehlt.");
            valid = false;
        }
        if (!data.mode) {
            logErr("Modus fehlt.");
            valid = false;
        }

        if (!data.questions || data.questions.length == 0) {
            logErr("Fragen fehlen.");
            valid = false;
        } else {
            q = data.questions;
            for (i = 0; i < q.length; i++) {
                if (!q[i].question) {
                    logErr("Frage zu Nummer " + i + " fehlt.");
                    valid = false;
                }
                if (!q[i].answers || q[i].answers.length == 0) {
                    logErr("Antworten zu Nummer " + i + " fehlen.");
                    valid = false;
                }
                if (!q[i].correct || !(q[i].correct >= 1 && q[i].correct <= q[i].answers.length)) {
                    logErr("Richtige Antwort zu Nummer " + i + " fehlt.");
                    valid = false;
                }
            }
        }
    }

    return valid;
}

function genQuestionContainer(data, index, mode) {
    var cont = $("<div>");
    cont.addClass("questionContainer");
    cont.addClass("hide");

    var teamContainer = $("<ul>");
    teamContainer.addClass("teamlist");
    if (mode == 1) {
        teamContainer.css("visibility", "hidden");
    }
    for (i = 0; i < mode; i++) {
        var team = $("<li>");
        team.addClass("team");
        team.html("Team " + String.fromCharCode(65 + i));

        if (i == (index % mode)) {
            team.addClass("active");
            cont.attr("data-team", i + 1);
        }

        teamContainer.append(team);
    }

    var question = $("<div>");
    question.addClass("question");
    question.html(data.question);

    var answers = $("<ul>");
    answers.addClass("answers");
    answers.addClass("button_container");

    for (i = 0; i < data.answers.length; i++) {
        var answer = data.answers[i];

        var ansButton = $("<button>");
        ansButton.addClass("check_button");
        ansButton.attr("data-correct", (i == data.correct - 1));
        ansButton.append(answer);

        var ansContainer = $("<li>");
        ansContainer.append(ansButton);
        ansContainer.css("visibility", "hidden");
        ansContainer.css("opacity", 0);

        answers.append(ansContainer);
    }

    var btnContainer = $("<div>");
    btnContainer.addClass("btnContainer");

    if (data.isFirst) {
        var div = $("<div>");
        div.addClass("prev");

        var btnExit = $("<a>");
        btnExit.attr("id", "btnExit");
        btnExit.attr("href", "#");

        btnContainer.append(div.append(btnExit));
    } else {
        var div = $("<div>");
        div.addClass("prev");

        var btnPrev = $("<a>");
        btnPrev.addClass("btnPrev");
        btnPrev.attr("href", "#");

        btnContainer.append(div.append(btnPrev));
    }

    var div = $("<div>");
    div.addClass("next");

    var btnCheck = $("<a>");
    btnCheck.addClass("btnCheck");
    btnCheck.addClass("disabled");
    btnCheck.attr("href", "#");

    div.append(btnCheck);

    if (!data.isLast) {
        var btnNext = $("<a>");
        btnNext.addClass("btnNext");
        btnNext.attr("href", "#");
        btnNext.hide();

        div.append(btnNext);
    } else {
        var btnFinish = $("<a>");
        btnFinish.attr("id", "btnShowResult");
        btnFinish.attr("href", "#");
        btnFinish.hide();

        div.append(btnFinish);
    }

    btnContainer.append(div);

    cont.append(teamContainer);
    cont.append(question);
    cont.append(answers);
    cont.append(btnContainer);
    cont.append($("<div>").addClass("clear"));

    return cont;
}

function genResultTable(mode) {
    var list = $('<table>');
    var head = $('<tr>');

    head.append($('<th>').html("Richtig"));
    head.append($('<th>').html("Prozent"));
    list.append(head);

    if (mode == 1) {
        var myQuestions = $('.questionContainer[data-team]');
        var max = myQuestions.length;
        var corr = max - myQuestions.find('.check_button.wrong').length;

        var row = $('<tr>');
        row.append($('<td>').html(corr + " von " + max));
        row.append($('<td>').html(Math.round(100 * corr / max) + "%"));
        list.append(row);
    } else {
        head.prepend($('<th>').html("Team"));

        for (d = 0; d < mode; d++) {
            var myQuestions = $('.questionContainer[data-team=' + (d + 1) + ']');
            var max = myQuestions.length;
            var corr = max - myQuestions.find('.check_button.wrong').length;

            var row = $('<tr>');
            row.append($('<td>').html("Team " + String.fromCharCode(65 + d)));
            row.append($('<td>').html(corr + " von " + max));
            row.append($('<td>').html(Math.round(100 * corr / max) + "%"));
            list.append(row);
        }
    }

    return list;
}

function loadQuizFromFile(file, callback) {
    console.log("Load Quiz \"" + file.name + "\" ...");

    fr = new FileReader();
    fr.onload = function(e) {
        var data;

        try {
            data = JSON.parse(e.target.result);
        } catch (err) {
            logErr("Kann Datei nicht lesen: " + err.message);
            callback(false, null);
            return;
        }

        valid = validateJson(data);
        callback(valid, data);
    };
    fr.readAsText(file);
}

function checkAnswer(e) {
    $(this).parents('.questionContainer').find('.check_button').each(function(index, elem) {
        var b = $(elem);

        if (b.attr("data-correct") == "true") {
            b.addClass("correct");
        } else if (b.hasClass("selected")) {
            b.addClass("wrong");
        }

        b.removeClass("selected");
        b.css("cursor", "default");
        b.off('click');
    });

    $(this).fadeOut(250, function() {
        $(this).siblings('.btnNext').fadeIn(250);
        $(this).siblings('#btnShowResult').fadeIn(250);
    });
}

function startQuiz(data) {
    console.log("Start Quiz \"" + data.title + "\" ...");

    q = data.questions;
    widthFactor = 100 / q.length;
    last = intro;

    for (c = 0; c < q.length; c++) {
        q[c].isFirst = (c == 0);
        q[c].isLast = (c == q.length - 1);
        cont = genQuestionContainer(q[c], c, data.mode);
        last.after(cont);
        last = cont;
    }

    $(document).off('keydown');
    $(document).on('keydown', quizHandler);
    $(document).on('click', function(evt) {
        var hidden = $('.questionContainer:visible .answers li[style*=hidden]');
        if (hidden.length > 0) {
            var child = hidden.first();
            child.css("visibility", "visible");
            child.fadeTo(500, 1);
        }
    });

    $('.btnPrev').on('click', function(e) {
        $(this).parents('.questionContainer').fadeOut(500, function() {
            var prev = $(this).prev();
            prev.fadeIn(500);
            progress.animate({ width: Math.round((prev.index() - 1) * widthFactor) + "%" }, 500);
        });
    });

    $('.btnNext').on('click', function(e) {
        $(this).parents('.questionContainer').fadeOut(500, function() {
            var next = $(this).next();
            next.fadeIn(500);
            progress.animate({ width: Math.round((next.index() - 1) * widthFactor) + "%" }, 500);
        });
    });

    $('#btnShowResult').on('click', function(e) {
        $(this).parents('.questionContainer').fadeOut(500, function() {
            var table = genResultTable(data.mode);
            result.find('#result_container').html(table);

            result.fadeIn(500);
            progress.animate({ width: "100%" }, 500);
        });
    });

    var animateExit = function(evt) {
        progressKeeper.fadeOut(500);
        notice.fadeIn(500);
        $(document).off('click');
        $(this).parents('.questionContainer').fadeOut(500, function() {
            intro.fadeIn(500, function() {
                resetQuiz();
            });
        });
    };

    $('#btnExit').on('click', animateExit);
    $('#btnRetry').on('click', animateExit);

    $('.check_button').on('click', function(e) {
        var container = $(this).parents('.questionContainer');

        if (container.find('.answers li[style*=hidden]').length == 0) {
            container.find('.btnCheck').on('click', checkAnswer);
            container.find('.btnCheck').removeClass("disabled");

            container.find('.check_button').removeClass("selected");
            $(this).addClass("selected");
        }
    });

    progressKeeper.fadeIn(500);
    notice.fadeOut(500);
}

function resetQuiz(time, data) {
    $('.questionContainer').each(function(i, elem) {
        if (elem.id) {
            return;
        }
        elem.remove();
    });

    progress.width(0);

    $(document).off('keydown');
    $(document).off('keydown');
    $(document).on('keydown', mainHandler);
}

/* onload */
function jsquiz_onload() {
    console.log(" === [ J S q u i z ] === ");

    var fileinput = $('input[name="fileinput"]');
    var btnInput = $('#btnInput');
    var btnStart = $('#btnStart');

    holder = $('#main-quiz-holder');
    intro = $('#intro-container');
    result = $('#result-container');
    progressKeeper = $('#progressKeeper');
    progress = $('#progress');
    notice = $('#notice');

    var quizData;

    fileinput.on('change', function() {
        filename = fileinput.val().replace(/C:\\fakepath\\/i, '');

        if (filename == "") {
            btnInput.html("Keine Datei ausgewählt!");
            btnStart.prop("disabled", true);
            btnStart.html("START");
            quizData = null
            return;
        }

        btnInput.html(filename);
        loadQuizFromFile(fileinput[0].files[0], function(valid, data) {
            if (!valid) {
                btnStart.prop("disabled", true);
                btnStart.html("ungültige Datei!");
                quizData = null;
                return;
            }

            btnStart.prop("disabled", false);
            btnStart.html("Starte " + data.title);
            quizData = data;
        });
    });

    btnInput.on('click', function(e) {
        fileinput.click();
    });

    btnStart.on('click', function(e) {
        startQuiz(quizData);
        $(this).parents('#intro-container').fadeOut(500, function() {
            $(this).next().fadeIn(500);
        });
    });

    resetQuiz();

    progressKeeper.hide();
    notice.show();
}

function mainHandler(evt) {
    if (!(evt.altKey || evt.ctrlKey || evt.metaKey || evt.shiftKey) &&
            !(evt.keyCode >= 112 && evt.keyCode <= 123)) {
        evt.preventDefault();

        switch (evt.keyCode) {
            case 76: // l
            case 79: // o
                $('#btnInput').click();
                break;
            case 13: // return
            case 32: // space
                $('#btnStart:enabled').click();
                break;
        }
    }
}

function quizHandler(evt) {
    var selAnswer = function(index) {
        $('.questionContainer:visible li:nth-child(' + index + ') button').click();
    };

    if (!(evt.altKey || evt.ctrlKey || evt.metaKey || evt.shiftKey) &&
            !(evt.keyCode >= 112 && evt.keyCode <= 123)) {
        evt.preventDefault();

        switch (evt.keyCode) {
            case 65: // a
            case 66: // b
            case 67: // c
            case 68: // d
            case 69: // e
            case 70: // f
            case 71: // g
            case 72: // h
                if ($('.questionContainer:visible .answers li[style*=hidden]').length == 0) {
                    selAnswer(evt.keyCode - 64);
                }
                break;
            case 49: // 1
            case 50: // 2
            case 51: // 3
            case 52: // 4
            case 53: // 5
            case 54: // 6
            case 55: // 7
            case 56: // 8
                if ($('.questionContainer:visible .answers li[style*=hidden]').length == 0) {
                    selAnswer(evt.keyCode - 48);
                }
                break;
            case 39: // right
            case 13: // return
            case 32: // space
            case 34: // pg_down
                var hidden = $('.questionContainer:visible .answers li[style*=hidden]');
                if (hidden.length > 0) {
                    var child = hidden.first();
                    child.css("visibility", "visible");
                    child.fadeTo(500, 1);
                } else {
                    $('.questionContainer:visible .btnNext:visible').click();
                    $('.questionContainer:visible #btnShowResult:visible').click();
                    $('.questionContainer:visible .btnCheck:visible').click();
                }
                break;
            case 37: // left
            case 8:  // bspace
            case 33: // pg_up
                $('.questionContainer:visible .btnPrev:visible').click();
                $('.questionContainer:visible #btnExit:visible').click();
                break;
            case 27: // escape
                $('.questionContainer:visible').fadeOut(500);
                $('#btnRetry:visible').click();
                $('#btnExit').click();
                break;
        }
    }
}

$(document).ready(function () {
    // Check for the various File API support.
    if (!window.File || !window.FileReader || !window.Blob) {
        alert("The File APIs are not fully supported by your browser.");
    }
});

<|endoftext|>


<commit_before>/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/*
 * Copyright 2013 Mozilla Foundation
 *
 * 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.
 */
/*global createEmptyObject */

var SharedObjectDefinition = (function () {

  var _defaultObjectEncoding = 3;

  var sharedObjects = createEmptyObject();

  function invokeWithArgsArray(index, args) {
    var simulated = false, result;
    switch (index) {
    case 4: // get size()
      result = JSON.stringify(this._data).length;
      simulated = true;
      break;
    case 6: // clear
      this._data = {};
      simulated = true;
      break;
    case 2: // flush
      simulated = true;
      result = true;
      break;
    case 3: // close
      simulated = true;
      break;
    }
    (simulated ? somewhatImplemented : notImplemented)(
      "SharedObject.invoke (" + index + ")");
    return result;
  }

  return {
    // ()
    __class__: "flash.net.SharedObject",
    initialize: function () {
      this._data = {};
      this._objectEncoding = _defaultObjectEncoding;
    },
    __glue__: {
      native: {
        static: {
          deleteAll: function deleteAll(url) { // (url:String) -> int
            notImplemented("SharedObject.deleteAll");
          },
          getDiskUsage: function getDiskUsage(url) { // (url:String) -> int
            notImplemented("SharedObject.getDiskUsage");
          },
          getLocal: function getLocal(name, localPath, secure) { // (name:String, localPath:String = null, secure:Boolean = false) -> SharedObject
            var path = localPath + "/" + name;
            return sharedObjects[path] || (sharedObjects[path] = new flash.net.SharedObject());
          },
          getRemote: function getRemote(name, remotePath, persistence, secure) { // (name:String, remotePath:String = null, persistence:Object = false, secure:Boolean = false) -> SharedObject
            notImplemented("SharedObject.getRemote");
          },
          defaultObjectEncoding: {
            get: function defaultObjectEncoding() { // (void) -> uint
              return _defaultObjectEncoding;
            },
            set: function defaultObjectEncoding(version) { // (version:uint) -> void
              _defaultObjectEncoding = version;
            }
          }
        },
        instance: {
          setDirty: function setDirty(propertyName) { // (propertyName:String) -> void
            notImplemented("SharedObject.setDirty");
          },
          invoke: function invoke(index) { // (index:uint) -> any
            return invokeWithArgsArray.call(this, index,
              Array.prototype.slice.call(arguments, 1));
          },
          invokeWithArgsArray: function invokeWithArgsArray(index, args) { // (index:uint, args:Array) -> any
            return invokeWithArgsArray.call(this, index, args);
          },
          data: {
            get: function data() { // (void) -> Object
              return this._data;
            }
          },
          objectEncoding: {
            get: function objectEncoding() { // (void) -> uint
              return this._objectEncoding;
            },
            set: function objectEncoding(version) { // (version:uint) -> void
              this._objectEncoding = version;
            }
          },
          client: {
            get: function client() { // (void) -> Object
              notImplemented("SharedObject.client");
              return this._client;
            },
            set: function client(object) { // (object:Object) -> void
              notImplemented("SharedObject.client");
              this._client = object;
            }
          }
        }
      }
    }
  };
}).call(this);
<commit_msg>Cheat codes for Mining Truck and Mushroomer to help with profiling.<commit_after>/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/*
 * Copyright 2013 Mozilla Foundation
 *
 * 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.
 */
/*global createEmptyObject */

var SharedObjectDefinition = (function () {

  var _defaultObjectEncoding = 3;

  var sharedObjects = createEmptyObject();

  function invokeWithArgsArray(index, args) {
    var simulated = false, result;
    switch (index) {
    case 4: // get size()
      result = JSON.stringify(this._data).length;
      simulated = true;
      break;
    case 6: // clear
      this._data = {};
      simulated = true;
      break;
    case 2: // flush
      simulated = true;
      result = true;
      break;
    case 3: // close
      simulated = true;
      break;
    }
    (simulated ? somewhatImplemented : notImplemented)(
      "SharedObject.invoke (" + index + ")");
    return result;
  }

  return {
    // ()
    __class__: "flash.net.SharedObject",
    initialize: function () {
      this._data = {};
      this._objectEncoding = _defaultObjectEncoding;
      this._data[Multiname.getPublicQualifiedName("levelCompleted")] = 32;
      this._data[Multiname.getPublicQualifiedName("completeLevels")] = 32;
    },
    __glue__: {
      native: {
        static: {
          deleteAll: function deleteAll(url) { // (url:String) -> int
            notImplemented("SharedObject.deleteAll");
          },
          getDiskUsage: function getDiskUsage(url) { // (url:String) -> int
            notImplemented("SharedObject.getDiskUsage");
          },
          getLocal: function getLocal(name, localPath, secure) { // (name:String, localPath:String = null, secure:Boolean = false) -> SharedObject
            var path = localPath + "/" + name;
            return sharedObjects[path] || (sharedObjects[path] = new flash.net.SharedObject());
          },
          getRemote: function getRemote(name, remotePath, persistence, secure) { // (name:String, remotePath:String = null, persistence:Object = false, secure:Boolean = false) -> SharedObject
            notImplemented("SharedObject.getRemote");
          },
          defaultObjectEncoding: {
            get: function defaultObjectEncoding() { // (void) -> uint
              return _defaultObjectEncoding;
            },
            set: function defaultObjectEncoding(version) { // (version:uint) -> void
              _defaultObjectEncoding = version;
            }
          }
        },
        instance: {
          setDirty: function setDirty(propertyName) { // (propertyName:String) -> void
            notImplemented("SharedObject.setDirty");
          },
          invoke: function invoke(index) { // (index:uint) -> any
            return invokeWithArgsArray.call(this, index,
              Array.prototype.slice.call(arguments, 1));
          },
          invokeWithArgsArray: function invokeWithArgsArray(index, args) { // (index:uint, args:Array) -> any
            return invokeWithArgsArray.call(this, index, args);
          },
          data: {
            get: function data() { // (void) -> Object
              return this._data;
            }
          },
          objectEncoding: {
            get: function objectEncoding() { // (void) -> uint
              return this._objectEncoding;
            },
            set: function objectEncoding(version) { // (version:uint) -> void
              this._objectEncoding = version;
            }
          },
          client: {
            get: function client() { // (void) -> Object
              notImplemented("SharedObject.client");
              return this._client;
            },
            set: function client(object) { // (object:Object) -> void
              notImplemented("SharedObject.client");
              this._client = object;
            }
          }
        }
      }
    }
  };
}).call(this);
<|endoftext|>


<commit_before>const { Group } = require('../../renderer');
const Label = require('@antv/component/src/label/base');
// const visualCenter = require('@antv/component/lib/label/utils/visual-center');
const Global = require('../../global');
const Util = require('../../util');
const IGNORE_ARR = [ 'line', 'point', 'path' ];
const ORIGIN = '_origin';

function avg(arr) {
  let sum = 0;
  Util.each(arr, function(value) {
    sum += value;
  });
  return sum / arr.length;
}

// 计算多边形重心: https://en.wikipedia.org/wiki/Centroid#Of_a_polygon
function getCentroid(xs, ys) {
  let i = -1,
    x = 0,
    y = 0;
  let former,
    current = xs.length - 1;
  let diff,
    k = 0;
  while (++i < xs.length) {
    former = current;
    current = i;
    k += diff = xs[former] * ys[current] - xs[current] * ys[former];
    x += (xs[former] + xs[current]) * diff;
    y += (ys[former] + ys[current]) * diff;
  }
  k *= 3;
  return [ x / k, y / k ];
}

class GeomLabels extends Group {
  getDefaultCfg() {
    return {
      label: Global.label,
      /**
       * 用户传入的文本配置信息
       * @type {Object}
       */
      labelCfg: null,
      /**
       * 所在的坐标系
       * @type {Object}
       */
      coord: null,
      /**
       * 图表的类型
       * @type {String}
       */
      geomType: null,
      zIndex: 6
    };
  }

  _renderUI() {
    super._renderUI.call(this);
    this.initLabelsCfg();
    const labelsGroup = this.addGroup();
    const lineGroup = this.addGroup({
      elCls: 'x-line-group'
    });
    const labelRenderer = this.get('labelRenderer');
    this.set('labelsGroup', labelsGroup);
    this.set('lineGroup', lineGroup);
    this.get('labelRenderer').set('group', labelsGroup);
    labelRenderer.set('group', labelsGroup);
    labelRenderer.set('lineGroup', lineGroup);
  }

  // 初始化labels的配置项
  initLabelsCfg() {
    const self = this;
    const labelRenderer = new Label();
    const labels = self.getDefaultLabelCfg();
    const labelCfg = self.get('labelCfg');
    // Util.merge(labels, labelCfg.cfg);
    Util.deepMix(labels, labelCfg.globalCfg || labelCfg.cfg);
    labelRenderer.set('labelCfg', labels);
    if (labels.labelLine) {
      labelRenderer.set('labelLine', labels.labelLine);
    }
    labelRenderer.set('coord', self.get('coord'));
    this.set('labelRenderer', labelRenderer);
    self.set('label', labels);
  }

  /**
   * @protected
   * 默认的文本样式
   * @return {Object} default label config
   */
  getDefaultLabelCfg() {
    const self = this;
    const labelCfg = self.get('labelCfg').cfg;
    const geomType = self.get('geomType');
    const viewTheme = self.get('viewTheme') || Global;
    if (geomType === 'polygon' || (labelCfg && labelCfg.offset < 0 && Util.indexOf(IGNORE_ARR, geomType) === -1)) {
      return Util.deepMix({}, self.get('label'), viewTheme.innerLabels);
    }
    return Util.deepMix({}, viewTheme.label, self.get('label'));
  }

  /**
   * @protected
   * 获取labels
   * @param {Array} points points
   * @return {Array} label items
   */
  getLabelsItems(points) {
    const self = this;
    const items = [];
    const geom = self.get('geom');
    const coord = self.get('coord');

    self._getLabelCfgs(points);
    const labelCfg = self.get('labelItemCfgs');

    // 获取label相关的x，y的值，获取具体的x,y,防止存在数组
    Util.each(points, (point, i) => {
      const origin = point[ORIGIN];
      const label = labelCfg[i];
      if (!label) {
        return;
      }
      if (!Util.isArray(label.text)) {
        label.text = [ label.text ];
      }
      const total = label.length;

      Util.each(label.text, (sub, subIndex) => {
        if (Util.isNil(sub) || sub === '') {
          return;
        }
        let obj = self.getLabelPoint(label, point, subIndex);
        obj = Util.mix({}, label, obj);
        if (!obj.textAlign) {
          obj.textAlign = self.getLabelAlign(obj, subIndex, total);
        }
        if (geom) {
          obj._id = geom._getShapeId(origin) + '-glabel-' + subIndex + '-' + obj.text;
        }
        obj.coord = coord;
        items.push(obj);
      });
    });
    return items;
  }

  /**
   * @protected
   * 如果发生冲突则会调整文本的位置
   * @param {Array} items 文本的集合
   * @param {Array} shapes 关联形状
   * @return {Array} adjusted items
   */
  adjustItems(items) {
    // 多边形shape的label位于其可视中心
    /* if (this.get('geomType') === 'polygon') {
      shapes = shapes.sort((a, b) => a.get('index') - b.get('index'));
      let index,
        shape,
        path,
        center,
        points;
      Util.each(items, (item, i) => {
        shape = shapes[ i ];
        path = shape.attr('path');
        points = [[]];
        index = 0;
        path.forEach((segment, i) => {
          if (segment[ 0 ] === 'z' || segment[ 0 ] === 'Z' && i !== path.length - 1) {
            points.push([]);
            index += 1;
          }
          if (segment.length === 3) {
            points[ index ].push([ segment[ 1 ], segment[ 2 ] ]);
          }
        });
        center = visualCenter(points);
        item.x = center.x;
        item.y = center.y;
      });
    }*/
    return items;
  }

  /**
   * drawing lines to labels
   * @param  {Array} items labels
   * @param  {Object} labelLine configuration for label lines
   */
  drawLines(items) {
    const self = this;
    Util.each(items, function(point) {
      if (point._offset[0] > 0 || point._offset[1] > 0) {
        self.lineToLabel(point);
      }
    });
  }

  // 定义连接线
  lineToLabel() {}

  /**
   * @protected
   * 获取文本的位置信息
   * @param {Array} labelCfg labels
   * @param {Object} point point
   * @param {Number} index index
   * @return {Object} point
   */
  getLabelPoint(labelCfg, point, index) {
    const self = this;
    const coord = self.get('coord');
    const total = labelCfg.text.length;

    function getDimValue(value, idx) {
      if (Util.isArray(value)) {
        if (labelCfg.text.length === 1) { // 如果仅一个label,多个y,取最后一个y
          if (value.length <= 2) {
            value = value[value.length - 1];
            // value = value[0];
          } else {
            value = avg(value);
          }
        } else {
          value = value[idx];
        }
      }
      return value;
    }

    const label = {
      text: labelCfg.text[index]
    };
    // 多边形场景,多用于地图
    if (point && this.get('geomType') === 'polygon') {
      const centroid = getCentroid(point.x, point.y);
      label.x = centroid[0];
      label.y = centroid[1];
    } else {
      label.x = getDimValue(point.x, index);
      label.y = getDimValue(point.y, index);
    }

    // get nearest point of the shape as the label line start point
    if (point && point.nextPoints && (point.shape === 'funnel' || point.shape === 'pyramid')) {
      let maxX = -Infinity;
      point.nextPoints.forEach(p => {
        p = coord.convert(p);
        if (p.x > maxX) {
          maxX = p.x;
        }
      });
      label.x = (label.x + maxX) / 2;
    }
    // sharp edge of the pyramid
    if (point.shape === 'pyramid' && !point.nextPoints && point.points) {
      point.points.forEach(p => {
        p = coord.convert(p);
        if ((Util.isArray(p.x) && point.x.indexOf(p.x) === -1) || (Util.isNumber(p.x) && point.x !== p.x)) {
          label.x = (label.x + p.x) / 2;
        }
      });
    }

    if (labelCfg.position) {
      self.setLabelPosition(label, point, index, labelCfg.position);
    }
    const offsetPoint = self.getLabelOffset(labelCfg, index, total);
    self.transLabelPoint(label);
    label.x += offsetPoint[0];
    label.y += offsetPoint[1];
    label.color = point.color;
    label._offset = offsetPoint;
    return label;
  }
  setLabelPosition() {}
  transLabelPoint(point) {
    const self = this;
    const coord = self.get('coord');
    const tmpPoint = coord.applyMatrix(point.x, point.y, 1);
    point.x = tmpPoint[0];
    point.y = tmpPoint[1];
  }

  // 获取默认的偏移量
  getDefaultOffset(point) {
    const offset = point.offset || [ 0, 0 ];
    const coord = this.get('coord');
    const vector = coord.applyMatrix(offset[0], offset[1]);
    return [ vector[0], vector[1] ];
  }

  // 获取文本的偏移位置，x,y
  getLabelOffset(point, index, total) {
    const self = this;
    const offset = self.getDefaultOffset(point);
    const coord = self.get('coord');
    const transposed = coord.isTransposed;
    const xField = transposed ? 'y' : 'x';
    const yField = transposed ? 'x' : 'y';
    let factor = transposed ? 1 : -1; // y 方向上越大，像素的坐标越小
    const offsetPoint = {
      x: 0,
      y: 0
    };

    // 一个shape对应多个label时，第二个label的offset与第一个相反
    if (index <= 0 && total !== 1) {
      factor *= -1;
    }
    offsetPoint[xField] = offset[0] * factor;
    offsetPoint[yField] = offset[1] * factor;
    return [ offsetPoint.x, offsetPoint.y ];
  }

  getLabelAlign(point, index, total) {
    const self = this;
    let align = 'center';
    const coord = self.get('coord');
    if (coord.isTransposed) {
      const offset = point._offset;
      // var vector = coord.applyMatrix(offset,0);
      if (offset[1] < 0) {
        align = 'right';
      } else if (offset[1] === 0) {
        align = 'center';
      } else {
        align = 'left';
      }
      if (total > 1 && index === 0) {
        if (align === 'right') {
          align = 'left';
        } else if (align === 'left') {
          align = 'right';
        }
      }
    }
    return align;
  }
  _getLabelValue(origin, scales) {
    if (!Util.isArray(scales)) {
      scales = [ scales ];
    }
    const text = [];
    Util.each(scales, scale => {
      let value = origin[scale.field];
      if (Util.isArray(value)) {
        const tmp = [];
        Util.each(value, function(subVal) {
          tmp.push(scale.getText(subVal));
        });
        value = tmp;
      } else {
        value = scale.getText(value);
      }
      if (Util.isNil(value) || value === '') {
        text.push(null);
      }
      text.push(value);
    });
    return text;
  }
  // 获取每个label的配置
  _getLabelCfgs(points) {
    const self = this;
    const labelCfg = this.get('labelCfg');
    const scales = labelCfg.scales;
    const defaultCfg = this.get('label');
    const cfgs = [];
    if (labelCfg.globalCfg && labelCfg.globalCfg.type) {
      self.set('type', labelCfg.globalCfg.type);
    }
    Util.each(points, (point, i) => {
      let cfg = {};
      const origin = point[ORIGIN];
      const originText = self._getLabelValue(origin, scales);
      if (labelCfg.callback) {
        // callback中应使用原始数据，而不是数据字符串
        const originValues = scales.map(scale => origin[scale.field]);
        cfg = labelCfg.callback.apply(null, originValues);
      }
      if (!cfg && cfg !== 0) {
        cfgs.push(null);
        return;
      }
      if (Util.isString(cfg) || Util.isNumber(cfg)) {
        cfg = { text: cfg };
      } else {
        cfg.text = originText[0];
      }
      cfg = Util.mix({}, defaultCfg, labelCfg.globalCfg || {}, cfg);
      // 兼容旧的源数据写在item.point中
      point.point = origin;
      if (cfg.htmlTemplate) {
        cfg.text = cfg.htmlTemplate.call(null, cfg.text, point, i);
        delete cfg.htmlTemplate;
      }
      if (cfg.formatter) {
        cfg.text = cfg.formatter.call(null, cfg.text, point, i);
        delete cfg.formatter;
      }
      if (cfg.label) {
        // 兼容有些直接写在labelCfg.label的配置
        const label = cfg.label;
        delete cfg.label;
        cfg = Util.mix(cfg, label);
      }
      if (cfg.textStyle) {
        // 兼容旧写法，globalCfg的offset优先级高
        delete cfg.textStyle.offset;
        const textStyle = cfg.textStyle;
        if (Util.isFunction(textStyle)) {
          cfg.textStyle = textStyle.call(null, originText, origin, i);
        }
      }
      let offset = cfg.offset || [ 0, 0 ];
      if (!Util.isArray(offset)) {
        offset = [ 0, offset ];
      }
      cfg.labelLine = Util.mix({}, defaultCfg.labelLine, cfg.labelLine);
      cfg.textStyle = Util.mix({}, defaultCfg.textStyle, cfg.textStyle);
      cfg.offset = offset;
      delete cfg.items;
      cfgs.push(cfg);
    });
    this.set('labelItemCfgs', cfgs);
  }
  showLabels(points, shapes) {
    const self = this;
    const labelRenderer = self.get('labelRenderer');
    let items = self.getLabelsItems(points);
    const type = self.get('type');
    self.drawLines(items);
    items = self.adjustItems(items, shapes);
    labelRenderer.set('items', items);
    if (type) {
      labelRenderer.set('shapes', shapes);
      labelRenderer.set('type', type);
      labelRenderer.set('points', points);
    }
    labelRenderer.set('canvas', this.get('canvas'));
    labelRenderer.draw();
  }

  destroy() {
    this.get('labelRenderer').destroy(); // 清理文本
    super.destroy.call(this);
  }
}

// Util.assign(GeomLabels.prototype, Labels.LabelslabelRenderer);

module.exports = GeomLabels;
<commit_msg>fix(geom-labels): change label dependency to lib<commit_after>const { Group } = require('../../renderer');
const { Label } = require('@antv/component/lib');
// const visualCenter = require('@antv/component/lib/label/utils/visual-center');
const Global = require('../../global');
const Util = require('../../util');
const IGNORE_ARR = [ 'line', 'point', 'path' ];
const ORIGIN = '_origin';

function avg(arr) {
  let sum = 0;
  Util.each(arr, function(value) {
    sum += value;
  });
  return sum / arr.length;
}

// 计算多边形重心: https://en.wikipedia.org/wiki/Centroid#Of_a_polygon
function getCentroid(xs, ys) {
  let i = -1,
    x = 0,
    y = 0;
  let former,
    current = xs.length - 1;
  let diff,
    k = 0;
  while (++i < xs.length) {
    former = current;
    current = i;
    k += diff = xs[former] * ys[current] - xs[current] * ys[former];
    x += (xs[former] + xs[current]) * diff;
    y += (ys[former] + ys[current]) * diff;
  }
  k *= 3;
  return [ x / k, y / k ];
}

class GeomLabels extends Group {
  getDefaultCfg() {
    return {
      label: Global.label,
      /**
       * 用户传入的文本配置信息
       * @type {Object}
       */
      labelCfg: null,
      /**
       * 所在的坐标系
       * @type {Object}
       */
      coord: null,
      /**
       * 图表的类型
       * @type {String}
       */
      geomType: null,
      zIndex: 6
    };
  }

  _renderUI() {
    super._renderUI.call(this);
    this.initLabelsCfg();
    const labelsGroup = this.addGroup();
    const lineGroup = this.addGroup({
      elCls: 'x-line-group'
    });
    const labelRenderer = this.get('labelRenderer');
    this.set('labelsGroup', labelsGroup);
    this.set('lineGroup', lineGroup);
    this.get('labelRenderer').set('group', labelsGroup);
    labelRenderer.set('group', labelsGroup);
    labelRenderer.set('lineGroup', lineGroup);
  }

  // 初始化labels的配置项
  initLabelsCfg() {
    const self = this;
    const labelRenderer = new Label();
    const labels = self.getDefaultLabelCfg();
    const labelCfg = self.get('labelCfg');
    // Util.merge(labels, labelCfg.cfg);
    Util.deepMix(labels, labelCfg.globalCfg || labelCfg.cfg);
    labelRenderer.set('labelCfg', labels);
    if (labels.labelLine) {
      labelRenderer.set('labelLine', labels.labelLine);
    }
    labelRenderer.set('coord', self.get('coord'));
    this.set('labelRenderer', labelRenderer);
    self.set('label', labels);
  }

  /**
   * @protected
   * 默认的文本样式
   * @return {Object} default label config
   */
  getDefaultLabelCfg() {
    const self = this;
    const labelCfg = self.get('labelCfg').cfg;
    const geomType = self.get('geomType');
    const viewTheme = self.get('viewTheme') || Global;
    if (geomType === 'polygon' || (labelCfg && labelCfg.offset < 0 && Util.indexOf(IGNORE_ARR, geomType) === -1)) {
      return Util.deepMix({}, self.get('label'), viewTheme.innerLabels);
    }
    return Util.deepMix({}, viewTheme.label, self.get('label'));
  }

  /**
   * @protected
   * 获取labels
   * @param {Array} points points
   * @return {Array} label items
   */
  getLabelsItems(points) {
    const self = this;
    const items = [];
    const geom = self.get('geom');
    const coord = self.get('coord');

    self._getLabelCfgs(points);
    const labelCfg = self.get('labelItemCfgs');

    // 获取label相关的x，y的值，获取具体的x,y,防止存在数组
    Util.each(points, (point, i) => {
      const origin = point[ORIGIN];
      const label = labelCfg[i];
      if (!label) {
        return;
      }
      if (!Util.isArray(label.text)) {
        label.text = [ label.text ];
      }
      const total = label.length;

      Util.each(label.text, (sub, subIndex) => {
        if (Util.isNil(sub) || sub === '') {
          return;
        }
        let obj = self.getLabelPoint(label, point, subIndex);
        obj = Util.mix({}, label, obj);
        if (!obj.textAlign) {
          obj.textAlign = self.getLabelAlign(obj, subIndex, total);
        }
        if (geom) {
          obj._id = geom._getShapeId(origin) + '-glabel-' + subIndex + '-' + obj.text;
        }
        obj.coord = coord;
        items.push(obj);
      });
    });
    return items;
  }

  /**
   * @protected
   * 如果发生冲突则会调整文本的位置
   * @param {Array} items 文本的集合
   * @param {Array} shapes 关联形状
   * @return {Array} adjusted items
   */
  adjustItems(items) {
    // 多边形shape的label位于其可视中心
    /* if (this.get('geomType') === 'polygon') {
      shapes = shapes.sort((a, b) => a.get('index') - b.get('index'));
      let index,
        shape,
        path,
        center,
        points;
      Util.each(items, (item, i) => {
        shape = shapes[ i ];
        path = shape.attr('path');
        points = [[]];
        index = 0;
        path.forEach((segment, i) => {
          if (segment[ 0 ] === 'z' || segment[ 0 ] === 'Z' && i !== path.length - 1) {
            points.push([]);
            index += 1;
          }
          if (segment.length === 3) {
            points[ index ].push([ segment[ 1 ], segment[ 2 ] ]);
          }
        });
        center = visualCenter(points);
        item.x = center.x;
        item.y = center.y;
      });
    }*/
    return items;
  }

  /**
   * drawing lines to labels
   * @param  {Array} items labels
   * @param  {Object} labelLine configuration for label lines
   */
  drawLines(items) {
    const self = this;
    Util.each(items, function(point) {
      if (point._offset[0] > 0 || point._offset[1] > 0) {
        self.lineToLabel(point);
      }
    });
  }

  // 定义连接线
  lineToLabel() {}

  /**
   * @protected
   * 获取文本的位置信息
   * @param {Array} labelCfg labels
   * @param {Object} point point
   * @param {Number} index index
   * @return {Object} point
   */
  getLabelPoint(labelCfg, point, index) {
    const self = this;
    const coord = self.get('coord');
    const total = labelCfg.text.length;

    function getDimValue(value, idx) {
      if (Util.isArray(value)) {
        if (labelCfg.text.length === 1) { // 如果仅一个label,多个y,取最后一个y
          if (value.length <= 2) {
            value = value[value.length - 1];
            // value = value[0];
          } else {
            value = avg(value);
          }
        } else {
          value = value[idx];
        }
      }
      return value;
    }

    const label = {
      text: labelCfg.text[index]
    };
    // 多边形场景,多用于地图
    if (point && this.get('geomType') === 'polygon') {
      const centroid = getCentroid(point.x, point.y);
      label.x = centroid[0];
      label.y = centroid[1];
    } else {
      label.x = getDimValue(point.x, index);
      label.y = getDimValue(point.y, index);
    }

    // get nearest point of the shape as the label line start point
    if (point && point.nextPoints && (point.shape === 'funnel' || point.shape === 'pyramid')) {
      let maxX = -Infinity;
      point.nextPoints.forEach(p => {
        p = coord.convert(p);
        if (p.x > maxX) {
          maxX = p.x;
        }
      });
      label.x = (label.x + maxX) / 2;
    }
    // sharp edge of the pyramid
    if (point.shape === 'pyramid' && !point.nextPoints && point.points) {
      point.points.forEach(p => {
        p = coord.convert(p);
        if ((Util.isArray(p.x) && point.x.indexOf(p.x) === -1) || (Util.isNumber(p.x) && point.x !== p.x)) {
          label.x = (label.x + p.x) / 2;
        }
      });
    }

    if (labelCfg.position) {
      self.setLabelPosition(label, point, index, labelCfg.position);
    }
    const offsetPoint = self.getLabelOffset(labelCfg, index, total);
    self.transLabelPoint(label);
    label.x += offsetPoint[0];
    label.y += offsetPoint[1];
    label.color = point.color;
    label._offset = offsetPoint;
    return label;
  }
  setLabelPosition() {}
  transLabelPoint(point) {
    const self = this;
    const coord = self.get('coord');
    const tmpPoint = coord.applyMatrix(point.x, point.y, 1);
    point.x = tmpPoint[0];
    point.y = tmpPoint[1];
  }

  // 获取默认的偏移量
  getDefaultOffset(point) {
    const offset = point.offset || [ 0, 0 ];
    const coord = this.get('coord');
    const vector = coord.applyMatrix(offset[0], offset[1]);
    return [ vector[0], vector[1] ];
  }

  // 获取文本的偏移位置，x,y
  getLabelOffset(point, index, total) {
    const self = this;
    const offset = self.getDefaultOffset(point);
    const coord = self.get('coord');
    const transposed = coord.isTransposed;
    const xField = transposed ? 'y' : 'x';
    const yField = transposed ? 'x' : 'y';
    let factor = transposed ? 1 : -1; // y 方向上越大，像素的坐标越小
    const offsetPoint = {
      x: 0,
      y: 0
    };

    // 一个shape对应多个label时，第二个label的offset与第一个相反
    if (index <= 0 && total !== 1) {
      factor *= -1;
    }
    offsetPoint[xField] = offset[0] * factor;
    offsetPoint[yField] = offset[1] * factor;
    return [ offsetPoint.x, offsetPoint.y ];
  }

  getLabelAlign(point, index, total) {
    const self = this;
    let align = 'center';
    const coord = self.get('coord');
    if (coord.isTransposed) {
      const offset = point._offset;
      // var vector = coord.applyMatrix(offset,0);
      if (offset[1] < 0) {
        align = 'right';
      } else if (offset[1] === 0) {
        align = 'center';
      } else {
        align = 'left';
      }
      if (total > 1 && index === 0) {
        if (align === 'right') {
          align = 'left';
        } else if (align === 'left') {
          align = 'right';
        }
      }
    }
    return align;
  }
  _getLabelValue(origin, scales) {
    if (!Util.isArray(scales)) {
      scales = [ scales ];
    }
    const text = [];
    Util.each(scales, scale => {
      let value = origin[scale.field];
      if (Util.isArray(value)) {
        const tmp = [];
        Util.each(value, function(subVal) {
          tmp.push(scale.getText(subVal));
        });
        value = tmp;
      } else {
        value = scale.getText(value);
      }
      if (Util.isNil(value) || value === '') {
        text.push(null);
      }
      text.push(value);
    });
    return text;
  }
  // 获取每个label的配置
  _getLabelCfgs(points) {
    const self = this;
    const labelCfg = this.get('labelCfg');
    const scales = labelCfg.scales;
    const defaultCfg = this.get('label');
    const cfgs = [];
    if (labelCfg.globalCfg && labelCfg.globalCfg.type) {
      self.set('type', labelCfg.globalCfg.type);
    }
    Util.each(points, (point, i) => {
      let cfg = {};
      const origin = point[ORIGIN];
      const originText = self._getLabelValue(origin, scales);
      if (labelCfg.callback) {
        // callback中应使用原始数据，而不是数据字符串
        const originValues = scales.map(scale => origin[scale.field]);
        cfg = labelCfg.callback.apply(null, originValues);
      }
      if (!cfg && cfg !== 0) {
        cfgs.push(null);
        return;
      }
      if (Util.isString(cfg) || Util.isNumber(cfg)) {
        cfg = { text: cfg };
      } else {
        cfg.text = originText[0];
      }
      cfg = Util.mix({}, defaultCfg, labelCfg.globalCfg || {}, cfg);
      // 兼容旧的源数据写在item.point中
      point.point = origin;
      if (cfg.htmlTemplate) {
        cfg.text = cfg.htmlTemplate.call(null, cfg.text, point, i);
        delete cfg.htmlTemplate;
      }
      if (cfg.formatter) {
        cfg.text = cfg.formatter.call(null, cfg.text, point, i);
        delete cfg.formatter;
      }
      if (cfg.label) {
        // 兼容有些直接写在labelCfg.label的配置
        const label = cfg.label;
        delete cfg.label;
        cfg = Util.mix(cfg, label);
      }
      if (cfg.textStyle) {
        // 兼容旧写法，globalCfg的offset优先级高
        delete cfg.textStyle.offset;
        const textStyle = cfg.textStyle;
        if (Util.isFunction(textStyle)) {
          cfg.textStyle = textStyle.call(null, originText, origin, i);
        }
      }
      let offset = cfg.offset || [ 0, 0 ];
      if (!Util.isArray(offset)) {
        offset = [ 0, offset ];
      }
      cfg.labelLine = Util.mix({}, defaultCfg.labelLine, cfg.labelLine);
      cfg.textStyle = Util.mix({}, defaultCfg.textStyle, cfg.textStyle);
      cfg.offset = offset;
      delete cfg.items;
      cfgs.push(cfg);
    });
    this.set('labelItemCfgs', cfgs);
  }
  showLabels(points, shapes) {
    const self = this;
    const labelRenderer = self.get('labelRenderer');
    let items = self.getLabelsItems(points);
    const type = self.get('type');
    self.drawLines(items);
    items = self.adjustItems(items, shapes);
    labelRenderer.set('items', items);
    if (type) {
      labelRenderer.set('shapes', shapes);
      labelRenderer.set('type', type);
      labelRenderer.set('points', points);
    }
    labelRenderer.set('canvas', this.get('canvas'));
    labelRenderer.draw();
  }

  destroy() {
    this.get('labelRenderer').destroy(); // 清理文本
    super.destroy.call(this);
  }
}

// Util.assign(GeomLabels.prototype, Labels.LabelslabelRenderer);

module.exports = GeomLabels;
<|endoftext|>


<commit_before>import { PIXELFORMAT_DEPTH, PIXELFORMAT_DEPTHSTENCIL } from './constants.js';

import { GraphicsDevice } from './graphics-device.js';

var defaultOptions = {
    depth: true,
    face: 0
};

/**
 * @class
 * @name RenderTarget
 * @classdesc A render target is a rectangular rendering surface.
 * @description Creates a new render target. A color buffer or a depth buffer must be set.
 * @param {object} options - Object for passing optional arguments.
 * @param {Texture} [options.colorBuffer] - The texture that this render target will treat as a rendering surface.
 * @param {boolean} [options.depth] - If set to true, depth buffer will be created. Defaults to true. Ignored if depthBuffer is defined.
 * @param {boolean} [options.stencil] - If set to true, depth buffer will include stencil. Defaults to false. Ignored if depthBuffer is defined or depth is false.
 * @param {Texture} [options.depthBuffer] - The texture that this render target will treat as a depth/stencil surface (WebGL2 only). If set, the 'depth' and 'stencil' properties are ignored.
 * Texture must have {@link PIXELFORMAT_DEPTH} or PIXELFORMAT_DEPTHSTENCIL format.
 * @param {number} [options.samples] - Number of hardware anti-aliasing samples (WebGL2 only). Default is 1.
 * @param {boolean} [options.autoResolve] - If samples > 1, enables or disables automatic MSAA resolve after rendering to this RT (see {@link RenderTarget#resolve}). Defaults to true;
 * Defaults to true.
 * @param {number} [options.face] - If the colorBuffer parameter is a cubemap, use this option to specify the
 * face of the cubemap to render to. Can be:
 * @param {string} [options.name] - The name of the render target.
 *
 * * {@link CUBEFACE_POSX}
 * * {@link CUBEFACE_NEGX}
 * * {@link CUBEFACE_POSY}
 * * {@link CUBEFACE_NEGY}
 * * {@link CUBEFACE_POSZ}
 * * {@link CUBEFACE_NEGZ}
 *
 * Defaults to {@link CUBEFACE_POSX}.
 * @example
 * // Create a 512x512x24-bit render target with a depth buffer
 * var colorBuffer = new pc.Texture(graphicsDevice, {
 *     width: 512,
 *     height: 512,
 *     format: pc.PIXELFORMAT_R8_G8_B8
 * });
 * var renderTarget = new pc.RenderTarget({
 *     colorBuffer: colorBuffer,
 *     depth: true
 * });
 *
 * // Set the render target on a layer
 * layer.renderTarget = renderTarget;
 */
class RenderTarget {
    constructor(options) {
        var _arg2 = arguments[1];
        var _arg3 = arguments[2];

        if (options instanceof GraphicsDevice) {
            // old constructor
            this._colorBuffer = _arg2;
            options = _arg3;
        } else {
            // new constructor
            this._colorBuffer = options.colorBuffer;
        }

        // mark color buffer texture as render target
        if (this._colorBuffer) {
            this._colorBuffer._isRenderTarget = true;
        }

        // device, gets assigned when the framebuffer is created during the rendering
        this._device = null;

        this._glFrameBuffer = null;
        this._glDepthBuffer = null;

        // Process optional arguments
        options = (options !== undefined) ? options : defaultOptions;
        this._depthBuffer = options.depthBuffer;
        this._face = (options.face !== undefined) ? options.face : 0;

        if (this._depthBuffer) {
            var format = this._depthBuffer._format;
            if (format === PIXELFORMAT_DEPTH) {
                this._depth = true;
                this._stencil = false;
            } else if (format === PIXELFORMAT_DEPTHSTENCIL) {
                this._depth = true;
                this._stencil = true;
            } else {
                // #if _DEBUG
                console.warn('Incorrect depthBuffer format. Must be pc.PIXELFORMAT_DEPTH or pc.PIXELFORMAT_DEPTHSTENCIL');
                // #endif
                this._depth = false;
                this._stencil = false;
            }
        } else {
            this._depth = (options.depth !== undefined) ? options.depth : true;
            this._stencil = (options.stencil !== undefined) ? options.stencil : false;
        }

        this._samples = (options.samples !== undefined) ? options.samples : 1;
        this.autoResolve = (options.autoResolve !== undefined) ? options.autoResolve : true;
        this._glResolveFrameBuffer = null;
        this._glMsaaColorBuffer = null;
        this._glMsaaDepthBuffer = null;

        // use specified name, othewise get one from color or depth buffer
        this.name = options.name;
        if (!this.name) {
            this.name = this._colorBuffer?.name;
        }
        if (!this.name) {
            this.name = this._depthBuffer?.name;
        }
        if (!this.name) {
            this.name = "Untitled";
        }
    }

    /**
     * @function
     * @name RenderTarget#destroy
     * @description Frees resources associated with this render target.
     */
    destroy() {

        var device = this._device;
        if (device) {
            var idx = device.targets.indexOf(this);
            if (idx !== -1) {
                device.targets.splice(idx, 1);
            }

            this.destroyFrameBuffers();
        }
    }

    destroyFrameBuffers() {

        var device = this._device;
        if (device) {
            var gl = device.gl;
            if (this._glFrameBuffer) {
                gl.deleteFramebuffer(this._glFrameBuffer);
                this._glFrameBuffer = null;
            }

            if (this._glDepthBuffer) {
                gl.deleteRenderbuffer(this._glDepthBuffer);
                this._glDepthBuffer = null;
            }

            if (this._glResolveFrameBuffer) {
                gl.deleteFramebuffer(this._glResolveFrameBuffer);
                this._glResolveFrameBuffer = null;
            }

            if (this._glMsaaColorBuffer) {
                gl.deleteRenderbuffer(this._glMsaaColorBuffer);
                this._glMsaaColorBuffer = null;
            }

            if (this._glMsaaDepthBuffer) {
                gl.deleteRenderbuffer(this._glMsaaDepthBuffer);
                this._glMsaaDepthBuffer = null;
            }
        }
    }

    destroyTextureBuffers() {

        if (this._depthBuffer) {
            this._depthBuffer.destroy();
            this._depthBuffer = null;
        }

        if (this._colorBuffer) {
            this._colorBuffer.destroy();
            this._colorBuffer = null;
        }
    }

    // called when context was lost, function releases all context related resources
    loseContext() {
        this._glFrameBuffer = undefined;
        this._glDepthBuffer = undefined;
        this._glResolveFrameBuffer = undefined;
        this._glMsaaColorBuffer = undefined;
        this._glMsaaDepthBuffer = undefined;
    }

    /**
     * @function
     * @name RenderTarget#resolve
     * @description If samples > 1, resolves the anti-aliased render target (WebGL2 only).
     * When you're rendering to an anti-aliased render target, pixels aren't written directly to the readable texture.
     * Instead, they're first written to a MSAA buffer, where each sample for each pixel is stored independently.
     * In order to read the results, you first need to 'resolve' the buffer - to average all samples and create a simple texture with one color per pixel.
     * This function performs this averaging and updates the colorBuffer and the depthBuffer.
     * If autoResolve is set to true, the resolve will happen after every rendering to this render target, otherwise you can do it manually,
     * during the app update or inside a {@link Command}.
     * @param {boolean} [color] - Resolve color buffer. Defaults to true.
     * @param {boolean} [depth] - Resolve depth buffer. Defaults to true if the render target has a depth buffer.
     */
    resolve(color = true, depth = !!this._depthBuffer) {
        if (!this._device) return;
        if (!this._device.webgl2) return;

        var gl = this._device.gl;
        gl.bindFramebuffer(gl.READ_FRAMEBUFFER, this._glFrameBuffer);
        gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this._glResolveFrameBuffer);
        gl.blitFramebuffer(0, 0, this.width, this.height,
                           0, 0, this.width, this.height,
                           (color ? gl.COLOR_BUFFER_BIT : 0) | (depth ? gl.DEPTH_BUFFER_BIT : 0),
                           gl.NEAREST);
        gl.bindFramebuffer(gl.FRAMEBUFFER, this._glFrameBuffer);
    }

    /**
     * @function
     * @name RenderTarget#copy
     * @description Copies color and/or depth contents of source render target to this one. Formats, sizes and anti-aliasing samples must match.
     * Depth buffer can only be copied on WebGL 2.0.
     * @param {RenderTarget} source - Source render target to copy from.
     * @param {boolean} [color] - If true will copy the color buffer. Defaults to false.
     * @param {boolean} [depth] - If true will copy the depth buffer. Defaults to false.
     * @returns {boolean} True if the copy was successful, false otherwise.
     */
    copy(source, color, depth) {
        if (!this._device) {
            if (source._device) {
                this._device = source._device;
            } else {
                // #if _DEBUG
                console.error("Render targets are not initialized");
                // #endif
                return false;
            }
        }
        return this._device.copyRenderTarget(source, this, color, depth);
    }

    /**
     * @readonly
     * @name RenderTarget#colorBuffer
     * @type {Texture}
     * @description Color buffer set up on the render target.
     */
    get colorBuffer() {
        return this._colorBuffer;
    }

    /**
     * @readonly
     * @name RenderTarget#depthBuffer
     * @type {Texture}
     * @description Depth buffer set up on the render target. Only available, if depthBuffer was set in constructor.
     * Not available, if depth property was used instead.
     */
    get depthBuffer() {
        return this._depthBuffer;
    }

    /**
     * @readonly
     * @name RenderTarget#face
     * @type {number}
     * @description If the render target is bound to a cubemap, this property
     * specifies which face of the cubemap is rendered to. Can be:
     *
     * * {@link CUBEFACE_POSX}
     * * {@link CUBEFACE_NEGX}
     * * {@link CUBEFACE_POSY}
     * * {@link CUBEFACE_NEGY}
     * * {@link CUBEFACE_POSZ}
     * * {@link CUBEFACE_NEGZ}
     */
    get face() {
        return this._face;
    }

    /**
     * @readonly
     * @name RenderTarget#width
     * @type {number}
     * @description Width of the render target in pixels.
     */
    get width() {
        return this._colorBuffer ? this._colorBuffer.width : this._depthBuffer.width;
    }

    /**
     * @readonly
     * @name RenderTarget#height
     * @type {number}
     * @description Height of the render target in pixels.
     */
    get height() {
        return this._colorBuffer ? this._colorBuffer.height : this._depthBuffer.height;
    }
}

export { RenderTarget };
<commit_msg>RenderTarget constructor - deprecated warning (#3137)<commit_after>import { PIXELFORMAT_DEPTH, PIXELFORMAT_DEPTHSTENCIL } from './constants.js';

import { GraphicsDevice } from './graphics-device.js';

var defaultOptions = {
    depth: true,
    face: 0
};

/**
 * @class
 * @name RenderTarget
 * @classdesc A render target is a rectangular rendering surface.
 * @description Creates a new render target. A color buffer or a depth buffer must be set.
 * @param {object} options - Object for passing optional arguments.
 * @param {Texture} [options.colorBuffer] - The texture that this render target will treat as a rendering surface.
 * @param {boolean} [options.depth] - If set to true, depth buffer will be created. Defaults to true. Ignored if depthBuffer is defined.
 * @param {boolean} [options.stencil] - If set to true, depth buffer will include stencil. Defaults to false. Ignored if depthBuffer is defined or depth is false.
 * @param {Texture} [options.depthBuffer] - The texture that this render target will treat as a depth/stencil surface (WebGL2 only). If set, the 'depth' and 'stencil' properties are ignored.
 * Texture must have {@link PIXELFORMAT_DEPTH} or PIXELFORMAT_DEPTHSTENCIL format.
 * @param {number} [options.samples] - Number of hardware anti-aliasing samples (WebGL2 only). Default is 1.
 * @param {boolean} [options.autoResolve] - If samples > 1, enables or disables automatic MSAA resolve after rendering to this RT (see {@link RenderTarget#resolve}). Defaults to true;
 * Defaults to true.
 * @param {number} [options.face] - If the colorBuffer parameter is a cubemap, use this option to specify the
 * face of the cubemap to render to. Can be:
 * @param {string} [options.name] - The name of the render target.
 *
 * * {@link CUBEFACE_POSX}
 * * {@link CUBEFACE_NEGX}
 * * {@link CUBEFACE_POSY}
 * * {@link CUBEFACE_NEGY}
 * * {@link CUBEFACE_POSZ}
 * * {@link CUBEFACE_NEGZ}
 *
 * Defaults to {@link CUBEFACE_POSX}.
 * @example
 * // Create a 512x512x24-bit render target with a depth buffer
 * var colorBuffer = new pc.Texture(graphicsDevice, {
 *     width: 512,
 *     height: 512,
 *     format: pc.PIXELFORMAT_R8_G8_B8
 * });
 * var renderTarget = new pc.RenderTarget({
 *     colorBuffer: colorBuffer,
 *     depth: true
 * });
 *
 * // Set the render target on a camera component
 * camera.renderTarget = renderTarget;
 *
 * // Destroy render target at a later stage. Note that the color buffer needs
 * // to be destroyed separately.
 * renderTarget.colorBuffer.destroy();
 * renderTarget.destroy();
 * camera.renderTarget = null;
 */
class RenderTarget {
    constructor(options) {
        var _arg2 = arguments[1];
        var _arg3 = arguments[2];

        if (options instanceof GraphicsDevice) {
            // old constructor
            this._colorBuffer = _arg2;
            options = _arg3;

            // #if _DEBUG
            console.warn('DEPRECATED: pc.RenderTarget constructor no longer accepts GraphicsDevice parameter.');
            // #endif

        } else {
            // new constructor
            this._colorBuffer = options.colorBuffer;
        }

        // mark color buffer texture as render target
        if (this._colorBuffer) {
            this._colorBuffer._isRenderTarget = true;
        }

        // device, gets assigned when the framebuffer is created during the rendering
        this._device = null;

        this._glFrameBuffer = null;
        this._glDepthBuffer = null;

        // Process optional arguments
        options = (options !== undefined) ? options : defaultOptions;
        this._depthBuffer = options.depthBuffer;
        this._face = (options.face !== undefined) ? options.face : 0;

        if (this._depthBuffer) {
            var format = this._depthBuffer._format;
            if (format === PIXELFORMAT_DEPTH) {
                this._depth = true;
                this._stencil = false;
            } else if (format === PIXELFORMAT_DEPTHSTENCIL) {
                this._depth = true;
                this._stencil = true;
            } else {
                // #if _DEBUG
                console.warn('Incorrect depthBuffer format. Must be pc.PIXELFORMAT_DEPTH or pc.PIXELFORMAT_DEPTHSTENCIL');
                // #endif
                this._depth = false;
                this._stencil = false;
            }
        } else {
            this._depth = (options.depth !== undefined) ? options.depth : true;
            this._stencil = (options.stencil !== undefined) ? options.stencil : false;
        }

        this._samples = (options.samples !== undefined) ? options.samples : 1;
        this.autoResolve = (options.autoResolve !== undefined) ? options.autoResolve : true;
        this._glResolveFrameBuffer = null;
        this._glMsaaColorBuffer = null;
        this._glMsaaDepthBuffer = null;

        // use specified name, othewise get one from color or depth buffer
        this.name = options.name;
        if (!this.name) {
            this.name = this._colorBuffer?.name;
        }
        if (!this.name) {
            this.name = this._depthBuffer?.name;
        }
        if (!this.name) {
            this.name = "Untitled";
        }
    }

    /**
     * @function
     * @name RenderTarget#destroy
     * @description Frees resources associated with this render target.
     */
    destroy() {

        var device = this._device;
        if (device) {
            var idx = device.targets.indexOf(this);
            if (idx !== -1) {
                device.targets.splice(idx, 1);
            }

            this.destroyFrameBuffers();
        }
    }

    destroyFrameBuffers() {

        var device = this._device;
        if (device) {
            var gl = device.gl;
            if (this._glFrameBuffer) {
                gl.deleteFramebuffer(this._glFrameBuffer);
                this._glFrameBuffer = null;
            }

            if (this._glDepthBuffer) {
                gl.deleteRenderbuffer(this._glDepthBuffer);
                this._glDepthBuffer = null;
            }

            if (this._glResolveFrameBuffer) {
                gl.deleteFramebuffer(this._glResolveFrameBuffer);
                this._glResolveFrameBuffer = null;
            }

            if (this._glMsaaColorBuffer) {
                gl.deleteRenderbuffer(this._glMsaaColorBuffer);
                this._glMsaaColorBuffer = null;
            }

            if (this._glMsaaDepthBuffer) {
                gl.deleteRenderbuffer(this._glMsaaDepthBuffer);
                this._glMsaaDepthBuffer = null;
            }
        }
    }

    destroyTextureBuffers() {

        if (this._depthBuffer) {
            this._depthBuffer.destroy();
            this._depthBuffer = null;
        }

        if (this._colorBuffer) {
            this._colorBuffer.destroy();
            this._colorBuffer = null;
        }
    }

    // called when context was lost, function releases all context related resources
    loseContext() {
        this._glFrameBuffer = undefined;
        this._glDepthBuffer = undefined;
        this._glResolveFrameBuffer = undefined;
        this._glMsaaColorBuffer = undefined;
        this._glMsaaDepthBuffer = undefined;
    }

    /**
     * @function
     * @name RenderTarget#resolve
     * @description If samples > 1, resolves the anti-aliased render target (WebGL2 only).
     * When you're rendering to an anti-aliased render target, pixels aren't written directly to the readable texture.
     * Instead, they're first written to a MSAA buffer, where each sample for each pixel is stored independently.
     * In order to read the results, you first need to 'resolve' the buffer - to average all samples and create a simple texture with one color per pixel.
     * This function performs this averaging and updates the colorBuffer and the depthBuffer.
     * If autoResolve is set to true, the resolve will happen after every rendering to this render target, otherwise you can do it manually,
     * during the app update or inside a {@link Command}.
     * @param {boolean} [color] - Resolve color buffer. Defaults to true.
     * @param {boolean} [depth] - Resolve depth buffer. Defaults to true if the render target has a depth buffer.
     */
    resolve(color = true, depth = !!this._depthBuffer) {
        if (!this._device) return;
        if (!this._device.webgl2) return;

        var gl = this._device.gl;
        gl.bindFramebuffer(gl.READ_FRAMEBUFFER, this._glFrameBuffer);
        gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this._glResolveFrameBuffer);
        gl.blitFramebuffer(0, 0, this.width, this.height,
                           0, 0, this.width, this.height,
                           (color ? gl.COLOR_BUFFER_BIT : 0) | (depth ? gl.DEPTH_BUFFER_BIT : 0),
                           gl.NEAREST);
        gl.bindFramebuffer(gl.FRAMEBUFFER, this._glFrameBuffer);
    }

    /**
     * @function
     * @name RenderTarget#copy
     * @description Copies color and/or depth contents of source render target to this one. Formats, sizes and anti-aliasing samples must match.
     * Depth buffer can only be copied on WebGL 2.0.
     * @param {RenderTarget} source - Source render target to copy from.
     * @param {boolean} [color] - If true will copy the color buffer. Defaults to false.
     * @param {boolean} [depth] - If true will copy the depth buffer. Defaults to false.
     * @returns {boolean} True if the copy was successful, false otherwise.
     */
    copy(source, color, depth) {
        if (!this._device) {
            if (source._device) {
                this._device = source._device;
            } else {
                // #if _DEBUG
                console.error("Render targets are not initialized");
                // #endif
                return false;
            }
        }
        return this._device.copyRenderTarget(source, this, color, depth);
    }

    /**
     * @readonly
     * @name RenderTarget#colorBuffer
     * @type {Texture}
     * @description Color buffer set up on the render target.
     */
    get colorBuffer() {
        return this._colorBuffer;
    }

    /**
     * @readonly
     * @name RenderTarget#depthBuffer
     * @type {Texture}
     * @description Depth buffer set up on the render target. Only available, if depthBuffer was set in constructor.
     * Not available, if depth property was used instead.
     */
    get depthBuffer() {
        return this._depthBuffer;
    }

    /**
     * @readonly
     * @name RenderTarget#face
     * @type {number}
     * @description If the render target is bound to a cubemap, this property
     * specifies which face of the cubemap is rendered to. Can be:
     *
     * * {@link CUBEFACE_POSX}
     * * {@link CUBEFACE_NEGX}
     * * {@link CUBEFACE_POSY}
     * * {@link CUBEFACE_NEGY}
     * * {@link CUBEFACE_POSZ}
     * * {@link CUBEFACE_NEGZ}
     */
    get face() {
        return this._face;
    }

    /**
     * @readonly
     * @name RenderTarget#width
     * @type {number}
     * @description Width of the render target in pixels.
     */
    get width() {
        return this._colorBuffer ? this._colorBuffer.width : this._depthBuffer.width;
    }

    /**
     * @readonly
     * @name RenderTarget#height
     * @type {number}
     * @description Height of the render target in pixels.
     */
    get height() {
        return this._colorBuffer ? this._colorBuffer.height : this._depthBuffer.height;
    }
}

export { RenderTarget };
<|endoftext|>