<commit_before>/* -*- mode: espresso; espresso-indent-level: 2; indent-tabs-mode: nil -*- */
/* vim: set softtabstop=2 shiftwidth=2 tabstop=2 expandtab: */

var ProjectStatistics = new function()
{
  var update_stats_fields = function(data) {
    $("#skeletons_created").text(data.skeletons_created);
    $("#treenodes_created").text(data.treenodes_created);
    $("#connectors_created").text(data.connectors_created);
  }

  var get_formated_entry = function(data) {
    var entry = '', points = 0;
    if( data.hasOwnProperty('new_treenodes') ) {
      entry += data['new_treenodes'] + ' /';
      points += data['new_treenodes'];
    } else {
      entry += '0 /';
    };
    if( data.hasOwnProperty('new_connectors') ) {
      entry += ' ' + data['new_connectors'] + ' /';
      points += data['new_connectors'];
    } else {
      entry += ' 0 /';
    };
    if( data.hasOwnProperty('new_reviewed_nodes') ) {
      entry += ' ' + data['new_reviewed_nodes'] + ' /';
      points += data['new_reviewed_nodes'];
    } else {
      entry += ' 0';
    };
    return {'entry': entry, 'points': points};
  }

  var update_user_history = function(data) {
    $('#project_stats_history_table').empty();
    var header = '';
    header += '
';
    header += 'username	';
    for(var i = 0; i < data['days'].length; i++ ) {
      header += ''+data['daysformatted'][i]+'	';
    }
    header += '
';
    $('#project_stats_history_table').append( header );
    var odd_row = true;
    for(var username in data['stats_table']) {
      var row = '', weekpointcount = 0;
      row += '
';
      if( data['stats_table'].hasOwnProperty( username ) ) {
        row += '' + username + '	';
        for(var i = 0; i < data['days'].length; i++ ) {
          var datekey = data['days'][i],
              formated = get_formated_entry( data['stats_table'][ username ][ datekey ] );
          row += ''+ formated['entry'] +'	';
          weekpointcount += formated['points'];
        }
      }
      row += '
';
      if( weekpointcount === 0 ) {
        continue;
      } else {
        // Flip odd row marker
        odd_row = !odd_row;
        // Add row
        $('#project_stats_history_table').append( row );
      }
    }
  }

  var update_piechart = function(data, chart_name) {
    $(chart_name).empty();
    var x = 90, y = 100, radius = 80, height = 200;
    // Create basic pie chart without any labels
    var rpie = Raphael(chart_name, '100%', height);
    var pie = rpie.piechart(x, y, radius, data.values, {
        colors: ['red', 'blue', 'green', 'yellow', 'orange', 'black', 'gray'],
    });

    /* Manually draw labels, because the legend can easily grow to large and
     * Raphael has no way of dealing with this properly. Therefore, name columns
     * are created manually if the list grows too long in Y. Of course, also the
     * X can be maxed out and this is the reason the SVG's width is set to 100%.
     */
    pie.labels = rpie.set();
    // Top left corner of the whole lefend
    var legend_x = x + radius + 16;
    var legend_y = y - radius;
    // The height of one legend line
    var line_height = 17;
    // Shifting is needed for making columns if the list grows to long
    var shift_x = 0, shift_y = 0;
    // The current maximum label lengths, can increase over time
    var max_label_width = 0;
    // Draw all labels, including a colored circle in front of it
    data.values.forEach(function(e, i, values) {
      var circ_r = 5;
      var text_indent = 2 * circ_r + 10;
      var l_x = legend_x + shift_x;
      var l_y = legend_y + i * line_height + shift_y;
      // Make name columns if names don't fit under below each other
      if (l_y + line_height > y + radius) {
        shift_x = shift_x + max_label_width + text_indent + 16;
        shift_y = shift_y - (i + 1) * line_height;
      }

      var color = pie.series[i].attrs.fill;
      // Draw leading color circle
      var circle = rpie.circle(l_x + circ_r, l_y + circ_r, circ_r)
          .attr({
              'stroke': color,
              'fill': color,
          });
      // Draw label
      var text = rpie.text(l_x + 2 * circ_r + 10, l_y + circ_r,
          e.others ? "Others" : data.users[e.order])
              .attr({
                  'text-anchor': 'start',
                  'font': '12px Arial, sans-serif',
              });
      // Find maximum text width
      var bb = text.getBBox();
      if (bb.width > max_label_width) {
          max_label_width = bb.width;
      }
      // Remember label
      pie.labels.push(rpie.set());
      pie.labels[i].push(circle);
      pie.labels[i].push(text);
    });

    // Add hover functionality
    var current_label = null;
    pie.hover(
      function () {
        // Scale everything up
        this.sector.stop();
        this.sector.scale(1.1, 1.1, this.cx, this.cy);
        if (this.label) {
          this.label[0].stop();
          this.label[0].attr({ r: 7.5 });
          this.label[1].attr({"font-weight": 800});
        }
      },
      function () {
        // Scale everything back to normal
        this.sector.animate({ transform: "s1 1 " + this.cx + " " + this.cy }, 500, "bounce");
        if (this.label) {
          this.label[0].animate({ r: 5 }, 500, "bounce");
          this.label[1].attr({"font-weight": 400});
        }
      });
  };

  var parseDate = function(d) {
    var d_s = d.toString();
    var year = d_s.substring(0, 4);
    var month = d_s.substring(4, 6);
    var day = d_s.substring(6, 8);
    return new Date(year + '-' + month + '-' + day);
  };

  Date.prototype.addDays = function(days){
    var msPerDay = 1000 * 60 * 60 * 24;
    var ms = this.getTime() + (msPerDay * days);
    var added = new Date(ms);
    return added;
  }

  var update_linegraph = function(data) {
    $("#linechart_treenode_holder").empty();

    var w = 600, h = 400, wPadding = 40, hPadding = 65;
    var legendW = 100;
    var endDate = new Date();
    var startDate = endDate.addDays(-30);
    var msPerDay = 1000 * 60 * 60 * 24;

    // Create zero-filled arrays for each user spanning the full date range.
    var dateRange = (endDate - startDate) / msPerDay;
    var counts = {};
    for (d in data) {
      date = parseDate(data[d].date);
      name = data[d].name;
      dayIndex = Math.round((date - startDate) / msPerDay);
      if (!(name in counts)) {
        counts[name] = [];
        for (var i = 0; i < dateRange; i++) {
          counts[name][i] = 0;
        }
      }
      counts[name][dayIndex] = data[d].count;
    }

    // Set up scales to map dates and counts to pixels.
    var dateScale = d3.time.scale()
      .domain([startDate, endDate])
      .range([wPadding, w - wPadding]);
    var countScale = d3.scale.linear()
      .domain([0, d3.max(data.map(function(x) { return x.count; }))])
      .range([h - hPadding, hPadding]);

    // Create a generator that will produce the x,y points defining each line given a count and a day index.
    var lineGen = d3.svg.line()
      .x(function(d, i) { return dateScale(startDate.addDays(i)); })
      .y(countScale);

    // Create the chart
    var chart = d3.select("div#linechart_treenode_holder").append("svg")
      .attr("class", "chart")
      .attr("width", w)
      .attr("height", h);

    // Add a line for each user.
    var lineGroup = chart.append("g")
      .attr("class", "linegroup");
    var colors = ['red', 'blue', 'green', 'yellow', 'orange', 'black', 'gray'];
    var i = 0;
    for (name in counts) {
      lineGroup.append("svg:path")
        .attr("d", lineGen(counts[name]))
        .attr("stroke", colors[i]);
      i = i + 1;
    }

    // Add the x axis.
    var xAxis = d3.svg.axis()
      .scale(dateScale)
      .orient("bottom");
    var xAxisGroup = chart.append("g")
      .attr("class", "axis")
        .attr("transform", "translate(0, " + (h - hPadding) + ")")
      .call(xAxis);
    xAxisGroup.selectAll("text")
      .attr("text-anchor", "start")
      .attr("transform", function(d) {return "rotate(90) translate(8, -" + this.getBBox().height + ")";});

    // Add the y axis.
    var yAxis = d3.svg.axis()
      .scale(countScale)
      .orient("right");
    chart.append("g")
      .attr("class", "axis")
        .attr("transform", "translate(" + (w - wPadding) + ", 0)")
      .call(yAxis);

    // Add the y-axis label.
    chart.append("text")
      .attr("class", "y-label")
      .attr("text-anchor", "middle")
      .attr("x", 0)
      .attr("y", 0)
      .attr("transform", "translate(" + (w - 12) + ", " + (h / 2) + ") rotate(90)")
      .text("Nodes edited");
  }

  var refresh_project_statistics = function() {
    // requestQueue.register(django_url + project.id + '/stats/summary', "GET", {
    // }, function (status, text, xml) {
    //   if (status == 200) {
    //     if (text && text != " ") {
    //       var jso = $.parseJSON(text);
    //       if (jso.error) {
    //         alert(jso.error);
    //       }
    //       else {
    //         update_stats_fields(jso);
    //       }
    //     }
    //   }
    //   return true;
    // });

    requestQueue.register(django_url + project.id + '/stats/nodecount', "GET", {
    }, function (status, text, xml) {
      if (status == 200) {
        if (text && text != " ") {
          var jso = $.parseJSON(text);
          if (jso.error) {
            alert(jso.error);
          } else {
            update_piechart(jso, "piechart_treenode_holder");
          }
        }
      }
      return true;
    });

    // requestQueue.register(django_url + project.id + '/stats/editor', "GET",{
    // }, function (status, text, xml) {
    //   if (status == 200) {
    //     if (text && text != " ") {
    //       var jso = $.parseJSON(text);
    //       if (jso.error) {
    //         alert(jso.error);
    //       } else {
    //         update_piechart(jso, "piechart_editor_holder");
    //       }
    //     }
    //   }
    //   return true;
    // });

    requestQueue.register(django_url + project.id + '/stats/reviewer', "GET", {
    }, function (status, text, xml) {
      if (status == 200) {
        if (text && text != " ") {
          var jso = $.parseJSON(text);
          if (jso.error) {
            alert(jso.error);
          } else {
            update_piechart(jso, "piechart_reviewer_holder");
          }
        }
      }
      return true;
    });

    requestQueue.register(django_url + project.id + '/stats/user-history', "POST", {
      "pid": project.id
    }, function (status, text, xml) {
      if (status == 200) {
        if (text && text != " ") {
          var jso = $.parseJSON(text);
          if (jso.error) {
            alert(jso.error);
          } else {
            update_user_history(jso);
          }
        }
      }
      return true;
    });

    // d3.json(django_url + project.id + '/stats/history', update_linegraph);
  }

  /**
   * Initialized the statistics widget by asking the backend to create the basic
   * layout.
   */
  this.init = function () {
    $('#project_stats_widget').load(django_url + project.id + '/stats')
    refresh_project_statistics();
  };
};
<commit_msg>Tracing statistics: split page refresh in sub methods<commit_after>/* -*- mode: espresso; espresso-indent-level: 2; indent-tabs-mode: nil -*- */
/* vim: set softtabstop=2 shiftwidth=2 tabstop=2 expandtab: */

var ProjectStatistics = new function()
{
  var update_stats_fields = function(data) {
    $("#skeletons_created").text(data.skeletons_created);
    $("#treenodes_created").text(data.treenodes_created);
    $("#connectors_created").text(data.connectors_created);
  }

  var get_formated_entry = function(data) {
    var entry = '', points = 0;
    if( data.hasOwnProperty('new_treenodes') ) {
      entry += data['new_treenodes'] + ' /';
      points += data['new_treenodes'];
    } else {
      entry += '0 /';
    };
    if( data.hasOwnProperty('new_connectors') ) {
      entry += ' ' + data['new_connectors'] + ' /';
      points += data['new_connectors'];
    } else {
      entry += ' 0 /';
    };
    if( data.hasOwnProperty('new_reviewed_nodes') ) {
      entry += ' ' + data['new_reviewed_nodes'] + ' /';
      points += data['new_reviewed_nodes'];
    } else {
      entry += ' 0';
    };
    return {'entry': entry, 'points': points};
  }

  var update_user_history = function(data) {
    $('#project_stats_history_table').empty();
    var header = '';
    header += '
';
    header += 'username	';
    for(var i = 0; i < data['days'].length; i++ ) {
      header += ''+data['daysformatted'][i]+'	';
    }
    header += '
';
    $('#project_stats_history_table').append( header );
    var odd_row = true;
    for(var username in data['stats_table']) {
      var row = '', weekpointcount = 0;
      row += '
';
      if( data['stats_table'].hasOwnProperty( username ) ) {
        row += '' + username + '	';
        for(var i = 0; i < data['days'].length; i++ ) {
          var datekey = data['days'][i],
              formated = get_formated_entry( data['stats_table'][ username ][ datekey ] );
          row += ''+ formated['entry'] +'	';
          weekpointcount += formated['points'];
        }
      }
      row += '
';
      if( weekpointcount === 0 ) {
        continue;
      } else {
        // Flip odd row marker
        odd_row = !odd_row;
        // Add row
        $('#project_stats_history_table').append( row );
      }
    }
  }

  var update_piechart = function(data, chart_name) {
    $(chart_name).empty();
    var x = 90, y = 100, radius = 80, height = 200;
    // Create basic pie chart without any labels
    var rpie = Raphael(chart_name, '100%', height);
    var pie = rpie.piechart(x, y, radius, data.values, {
        colors: ['red', 'blue', 'green', 'yellow', 'orange', 'black', 'gray'],
    });

    /* Manually draw labels, because the legend can easily grow to large and
     * Raphael has no way of dealing with this properly. Therefore, name columns
     * are created manually if the list grows too long in Y. Of course, also the
     * X can be maxed out and this is the reason the SVG's width is set to 100%.
     */
    pie.labels = rpie.set();
    // Top left corner of the whole lefend
    var legend_x = x + radius + 16;
    var legend_y = y - radius;
    // The height of one legend line
    var line_height = 17;
    // Shifting is needed for making columns if the list grows to long
    var shift_x = 0, shift_y = 0;
    // The current maximum label lengths, can increase over time
    var max_label_width = 0;
    // Draw all labels, including a colored circle in front of it
    data.values.forEach(function(e, i, values) {
      var circ_r = 5;
      var text_indent = 2 * circ_r + 10;
      var l_x = legend_x + shift_x;
      var l_y = legend_y + i * line_height + shift_y;
      // Make name columns if names don't fit under below each other
      if (l_y + line_height > y + radius) {
        shift_x = shift_x + max_label_width + text_indent + 16;
        shift_y = shift_y - (i + 1) * line_height;
      }

      var color = pie.series[i].attrs.fill;
      // Draw leading color circle
      var circle = rpie.circle(l_x + circ_r, l_y + circ_r, circ_r)
          .attr({
              'stroke': color,
              'fill': color,
          });
      // Draw label
      var text = rpie.text(l_x + 2 * circ_r + 10, l_y + circ_r,
          e.others ? "Others" : data.users[e.order])
              .attr({
                  'text-anchor': 'start',
                  'font': '12px Arial, sans-serif',
              });
      // Find maximum text width
      var bb = text.getBBox();
      if (bb.width > max_label_width) {
          max_label_width = bb.width;
      }
      // Remember label
      pie.labels.push(rpie.set());
      pie.labels[i].push(circle);
      pie.labels[i].push(text);
    });

    // Add hover functionality
    var current_label = null;
    pie.hover(
      function () {
        // Scale everything up
        this.sector.stop();
        this.sector.scale(1.1, 1.1, this.cx, this.cy);
        if (this.label) {
          this.label[0].stop();
          this.label[0].attr({ r: 7.5 });
          this.label[1].attr({"font-weight": 800});
        }
      },
      function () {
        // Scale everything back to normal
        this.sector.animate({ transform: "s1 1 " + this.cx + " " + this.cy }, 500, "bounce");
        if (this.label) {
          this.label[0].animate({ r: 5 }, 500, "bounce");
          this.label[1].attr({"font-weight": 400});
        }
      });
  };

  var parseDate = function(d) {
    var d_s = d.toString();
    var year = d_s.substring(0, 4);
    var month = d_s.substring(4, 6);
    var day = d_s.substring(6, 8);
    return new Date(year + '-' + month + '-' + day);
  };

  Date.prototype.addDays = function(days){
    var msPerDay = 1000 * 60 * 60 * 24;
    var ms = this.getTime() + (msPerDay * days);
    var added = new Date(ms);
    return added;
  }

  var update_linegraph = function(data) {
    $("#linechart_treenode_holder").empty();

    var w = 600, h = 400, wPadding = 40, hPadding = 65;
    var legendW = 100;
    var endDate = new Date();
    var startDate = endDate.addDays(-30);
    var msPerDay = 1000 * 60 * 60 * 24;

    // Create zero-filled arrays for each user spanning the full date range.
    var dateRange = (endDate - startDate) / msPerDay;
    var counts = {};
    for (d in data) {
      date = parseDate(data[d].date);
      name = data[d].name;
      dayIndex = Math.round((date - startDate) / msPerDay);
      if (!(name in counts)) {
        counts[name] = [];
        for (var i = 0; i < dateRange; i++) {
          counts[name][i] = 0;
        }
      }
      counts[name][dayIndex] = data[d].count;
    }

    // Set up scales to map dates and counts to pixels.
    var dateScale = d3.time.scale()
      .domain([startDate, endDate])
      .range([wPadding, w - wPadding]);
    var countScale = d3.scale.linear()
      .domain([0, d3.max(data.map(function(x) { return x.count; }))])
      .range([h - hPadding, hPadding]);

    // Create a generator that will produce the x,y points defining each line given a count and a day index.
    var lineGen = d3.svg.line()
      .x(function(d, i) { return dateScale(startDate.addDays(i)); })
      .y(countScale);

    // Create the chart
    var chart = d3.select("div#linechart_treenode_holder").append("svg")
      .attr("class", "chart")
      .attr("width", w)
      .attr("height", h);

    // Add a line for each user.
    var lineGroup = chart.append("g")
      .attr("class", "linegroup");
    var colors = ['red', 'blue', 'green', 'yellow', 'orange', 'black', 'gray'];
    var i = 0;
    for (name in counts) {
      lineGroup.append("svg:path")
        .attr("d", lineGen(counts[name]))
        .attr("stroke", colors[i]);
      i = i + 1;
    }

    // Add the x axis.
    var xAxis = d3.svg.axis()
      .scale(dateScale)
      .orient("bottom");
    var xAxisGroup = chart.append("g")
      .attr("class", "axis")
        .attr("transform", "translate(0, " + (h - hPadding) + ")")
      .call(xAxis);
    xAxisGroup.selectAll("text")
      .attr("text-anchor", "start")
      .attr("transform", function(d) {return "rotate(90) translate(8, -" + this.getBBox().height + ")";});

    // Add the y axis.
    var yAxis = d3.svg.axis()
      .scale(countScale)
      .orient("right");
    chart.append("g")
      .attr("class", "axis")
        .attr("transform", "translate(" + (w - wPadding) + ", 0)")
      .call(yAxis);

    // Add the y-axis label.
    chart.append("text")
      .attr("class", "y-label")
      .attr("text-anchor", "middle")
      .attr("x", 0)
      .attr("y", 0)
      .attr("transform", "translate(" + (w - 12) + ", " + (h / 2) + ") rotate(90)")
      .text("Nodes edited");
  }

  var refresh_project_statistics = function() {
    refresh_nodecount();
    refresh_reviewers();
    refresh_history();

    // d3.json(django_url + project.id + '/stats/history', update_linegraph);
  }

  var refresh_history = function() {
    requestQueue.register(django_url + project.id + '/stats/user-history', "POST", {
      "pid": project.id
    }, function (status, text, xml) {
      if (status == 200) {
        if (text && text != " ") {
          var jso = $.parseJSON(text);
          if (jso.error) {
            alert(jso.error);
          } else {
            update_user_history(jso);
          }
        }
      }
      return true;
    });
  };

  var refresh_reviewers = function() {
    requestQueue.register(django_url + project.id + '/stats/reviewer', "GET", {
    }, function (status, text, xml) {
      if (status == 200) {
        if (text && text != " ") {
          var jso = $.parseJSON(text);
          if (jso.error) {
            alert(jso.error);
          } else {
            update_piechart(jso, "piechart_reviewer_holder");
          }
        }
      }
      return true;
    });
  };

  var refresh_editors = function() {
    requestQueue.register(django_url + project.id + '/stats/editor', "GET",{
    }, function (status, text, xml) {
      if (status == 200) {
        if (text && text != " ") {
          var jso = $.parseJSON(text);
          if (jso.error) {
            alert(jso.error);
          } else {
            update_piechart(jso, "piechart_editor_holder");
          }
        }
      }
      return true;
    });
  };

  var refresh_nodecount = function() {
    requestQueue.register(django_url + project.id + '/stats/nodecount', "GET", {
    }, function (status, text, xml) {
      if (status == 200) {
        if (text && text != " ") {
          var jso = $.parseJSON(text);
          if (jso.error) {
            alert(jso.error);
          } else {
            update_piechart(jso, "piechart_treenode_holder");
          }
        }
      }
      return true;
    });
  };

  var refresh_summary = function() {
    requestQueue.register(django_url + project.id + '/stats/summary', "GET", {
    }, function (status, text, xml) {
      if (status == 200) {
        if (text && text != " ") {
          var jso = $.parseJSON(text);
          if (jso.error) {
            alert(jso.error);
          }
          else {
            update_stats_fields(jso);
          }
        }
      }
      return true;
    });
  };

  /**
   * Initialized the statistics widget by asking the backend to create the basic
   * layout.
   */
  this.init = function () {
    $('#project_stats_widget').load(django_url + project.id + '/stats')
    refresh_project_statistics();
  };
};
<|endoftext|>


<commit_before>//= require esphinx/support/jquery
//= require esphinx_ui

"use strict";

var
    jQuery,
    esPhinx;

(function ($module) {

    $module.extend({
        navigators: {
            tabs: {
                new: function () {
                    const
                        MODULE_CSS = "esphinx-ui";

                    var
                        $ = jQuery,
                        Constructor = $module.navigators.tabs.new,
                        args,
                        nav,
                        container,
                        opts,

                        selectOnload = function () {
                            var
                                URLHash = location.hash,
                                href,
                                anchor;

                            if (URLHash) {
                                selectTab(nav
                                    .findByAttr("a", "href", URLHash));
                            } else {
                                href = location.pathname
                                    .match(/[a-z\/\_\-]+[a-z]+/);
                                anchor = nav.findWithoutAttr("a", "href");
                                if (anchor.length) {
                                    selectTab(nav
                                        .findByAttr("a", "href", URLHash));
                                } else {
                                    selectTab(nav.find("a:first"));
                                }
                            }
                        },

                        showContentOnload = function () {
                            var
                                id = nav.findWithoutAttr("a", "href").attr("id");

                            container.find("li." + MODULE_CSS + "-tabs-container-option").hide();
                            container.find("[data-nav='" + id + "']").show();

                            // selected = container.find("li.#{tabAnchor.attr("class")}")
                            // selected = container.find("li#{tabAnchor.attr("href")}")
                            //  if selected.length == 0
                            //    url = $(this).data("url")
                            //    if url
                            //      content = $("

").addClass tabAnchor.attr("class")
                            //      container.append content

                            //      format = $(this).data("format")
                            //      if !format
                            //        format = "js"

                            //      $.get url, {format: format},
                            //        (data, status, xhr)->
                            //          if format == "html"
                            //            content.html data
                        },

                        showContent = function (e) {
                            var
                                target = $(e.target),
                                id = target.attr("id");

                            container.find("li." + MODULE_CSS + "-tabs-container-option").hide();
                            container.find("[data-nav='" + id + "']").show();
                        },

                        selectTab = function (handle) {
                            var
                                tabs,
                                tabSelected,
                                target;

                            if (handle instanceof jQuery.Event) {
                                target = $(handle.target);
                                if (handle.type === "click") {
                                    if (target.closest("ul")
                                        .is("." + MODULE_CSS + "-tabs-select-in-loading")) {
                                        location.hash = target.attr("id");
                                    }
                                }
                            } else {
                                target = handle;
                            }

                            tabs = target.closest("ul").find("li");

                            tabSelected = tabs.findWithoutAttr("a", "href");

                            if (tabSelected.length) {
                                tabSelected.attr("href", location.hash);
                            }

                            tabs.find("a")
                                .removeClass(MODULE_CSS + "-tab-selected");
                            target.addClass(MODULE_CSS + "-tab-selected");

                            target.removeAttr("href");
                        },

                        resolveTabsNav = function () {
                            var
                                parent,
                                tabs,
                                tab,
                                anchor;

                            parent = nav.closest("div");
                            nav.addClass(MODULE_CSS + "-tabs-nav");

                            tabs = nav.find("li");
                            tabs.each(function (i, o) {
                                tab = $(o);
                                anchor = tab.find("a");
                                if (!anchor.length) {
                                    anchor = $("");
                                    anchor.append(tab.text().trim());
                                    tab.empty();
                                    tab.append(anchor);
                                }
                                anchor.attr("id", MODULE_CSS + "-tab-nav-" + (i + 1));
                                anchor.attr("href", "#" + anchor.attr("id"));
                            });

                            if (opts) {
                                if (opts.selectOnload) {
                                    nav.addClass(MODULE_CSS + "-tabs-select-in-loading");
                                }
                            } else {
                                nav.find("a:first").removeAttr("href");
                            }
                        },

                        resolveTabsContainer = function () {
                            var
                                items,
                                li;

                            if (container.prop("tagName") !== "UL") {
                                container = container.closest("ul");
                            }

                            container
                                .addClass(MODULE_CSS + "-tabs-container");
                            container.find("li").addClass(MODULE_CSS + "-tabs-container-option");

                            items = container
                                .find("li." + MODULE_CSS + "-tabs-container-option"),

                            items.each(function (i, o) {
                                li = $(o);
                                li.attr("data-nav",
                                    MODULE_CSS + "-tab-nav-" + (i + 1)
                                        .toString());
                            });

                            showContentOnload();
                        },

                        init = function () {
                            resolveTabsNav();

                            if (container) {
                                if (opts) {
                                    if (opts.selectOnload) {
                                        selectOnload();
                                    }
                                }
                                resolveTabsContainer();
                            } else {
                                selectOnload();
                            }
                        };

                    if (!(this instanceof Constructor)) {
                        return new Constructor(arguments);
                    } else {
                        args = arguments.flatten();
                        nav = args[0];
                        container = args[1];
                        if (args[2]) {
                            opts = args[2];
                        }

                        init();
                    }

                    this.select = function (e) {
                        selectTab(e);
                        showContent(e);
                    };
                }
            }
        }
    });

}(esPhinx.ui));
<commit_msg>adjusts<commit_after>//= require esphinx/support/jquery
//= require esphinx_ui

"use strict";

var
    jQuery,
    esPhinx;

(function ($module) {

    $module.extend({
        navigators: {
            tabs: {
                new: function () {
                    const
                        MODULE_CSS = "esphinx-ui";

                    var
                        $ = jQuery,
                        Constructor = $module.navigators.tabs.new,
                        args,
                        nav,
                        container,
                        opts,

                        selectOnload = function () {
                            var
                                URLHash = location.hash,
                                href,
                                anchor;

                            if (URLHash) {
                                selectTab(nav
                                    .findByAttr("a", "href", URLHash));
                            } else {
                                href = location.pathname
                                    .match(/[a-z\/\_\-]+[a-z]+/);
                                anchor = nav.findWithoutAttr("a", "href");
                                if (anchor.length) {
                                    selectTab(nav
                                        .findByAttr("a", "href", URLHash));
                                } else {
                                    selectTab(nav.find("a:first"));
                                }
                            }
                        },

                        showContentOnload = function () {
                            var
                                id = nav.findWithoutAttr("a", "href").attr("id");

                            container.find("li." + MODULE_CSS + "-tabs-container-option").hide();
                            container.find("[data-nav='" + id + "']").show();

                            // selected = container.find("li.#{tabAnchor.attr("class")}")
                            // selected = container.find("li#{tabAnchor.attr("href")}")
                            //  if selected.length == 0
                            //    url = $(this).data("url")
                            //    if url
                            //      content = $("

").addClass tabAnchor.attr("class")
                            //      container.append content

                            //      format = $(this).data("format")
                            //      if !format
                            //        format = "js"

                            //      $.get url, {format: format},
                            //        (data, status, xhr)->
                            //          if format == "html"
                            //            content.html data
                        },

                        showContent = function (e) {
                            var
                                target = $(e.target),
                                id = target.attr("id");

                            container.find("li." + MODULE_CSS + "-tabs-container-option").hide();
                            container.find("[data-nav='" + id + "']").show();
                        },

                        selectTab = function (handle) {
                            var
                                tabs,
                                tabSelected,
                                target;

                            if (handle instanceof jQuery.Event) {
                                target = $(handle.target);
                                if (handle.type === "click") {
                                    if (target.closest("ul")
                                        .is("." + MODULE_CSS + "-tabs-select-in-loading")) {
                                        location.hash = target.attr("id");
                                    }
                                }
                            } else {
                                target = handle;
                            }

                            tabs = target.closest("ul").find("li");

                            tabSelected = tabs.findWithoutAttr("a", "href");

                            if (tabSelected.length) {
                                tabSelected.attr("href", location.hash);
                            }

                            tabs.find("a")
                                .removeClass(MODULE_CSS + "-tab-selected");
                            target.addClass(MODULE_CSS + "-tab-selected");

                            target.removeAttr("href");
                        },

                        resolveTabsNav = function () {
                            var
                                parent,
                                tabs,
                                tab,
                                anchor;

                            parent = nav.closest("div");
                            nav.addClass(MODULE_CSS + "-tabs-nav");

                            tabs = nav.find("li");
                            tabs.each(function (i, o) {
                                tab = $(o);
                                anchor = tab.find("a");
                                if (!anchor.length) {
                                    anchor = $("");
                                    anchor.append(tab.text().trim());
                                    tab.empty();
                                    tab.append(anchor);
                                }
                                anchor.attr("id", MODULE_CSS + "-tab-nav-" + (i + 1));
                                anchor.attr("href", "#" + anchor.attr("id"));
                            });

                            if (opts) {
                                if (opts.selectOnload) {
                                    nav.addClass(MODULE_CSS + "-tabs-select-in-loading");
                                }
                            } else {
                                nav.find("a:first").removeAttr("href");
                            }
                        },

                        resolveTabsContainer = function () {
                            var
                                items,
                                li;

                            if (container.prop("tagName") !== "UL") {
                                container = container.closest("ul");
                            }

                            container
                                .addClass(MODULE_CSS + "-tabs-container");
                            container.find("li").addClass(MODULE_CSS + "-tabs-container-option");

                            items = container
                                .find("li." + MODULE_CSS + "-tabs-container-option"),

                            items.each(function (i, o) {
                                li = $(o);
                                li.attr("data-nav",
                                    MODULE_CSS + "-tab-nav-" + (i + 1)
                                        .toString());
                            });

                            showContentOnload();
                        },

                        init = function () {
                            resolveTabsNav();

                            if (container) {
                                if (opts) {
                                    if (opts.selectOnload) {
                                        selectOnload();
                                    }
                                }
                                resolveTabsContainer();
                            } else {
                                selectOnload();
                            }
                        };

                    if (!(this instanceof Constructor)) {
                        return new Constructor(arguments);
                    } else {
                        args = arguments.flatten();
                        nav = args[0];
                        container = args[1];
                        if (args[2]) {
                            opts = args[2];
                        }

                        init();
                    }

                    // acho que assim como em tooltip, pode-se informar para não precisar passar o evento
                    // this.select = function (e) {
                    this.select = function () {
                        // selectTab(e);
                        // showContent(e);
                        selectTab();
                        showContent();
                    };
                }
            }
        }
    });

}(esPhinx.ui));
<|endoftext|>


<commit_before>/*
 * Copyright 2016 EuregJUG.
 *
 * 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.
 */
define([], function () {
    function SpringCollections() {
        var self = this;

        /**
         * A function that translates org.springframework.data.domain.Page results
         * to oj.Collection.
         *
         * @param {type} response
         * @returns
         */
        self.mapCollectionsToDataArguments = function (response) {
            return {
                totalResults: response['totalElements'],
                limit: response['size'],
                count: response['numberOfElements'],
                hasMore: !response['last'],
                offset: response['number'] * response['size']
            };
        };

        /**
         * A function that transforms oj.Collections option to the format of an
         * endpoint of an API that takes org.springframework.data.domain.Pageable
         * parameter.
         *
         * @param {type} operation
         * @param {type} col
         * @param {type} options
         * @returns {String}
         */
        self.mapCollectionURLToDataURL = function (operation, col, options) {
            var rv = null;
            if (operation === 'read') {
                rv = options.context.url
                        + '?size=' + options.fetchSize
                        + '&page=' + (options.startIndex / options.fetchSize)
                        + '&sort=' + options.sort + ',' + options.sortDir;
            }
            return rv;
        };

        /**
         * Extracts the content from a org.springframework.data.domain.Page result
         * @param {type} response
         * @returns {unresolved}
         */
        self.extractContent = function(response) {
            return response['content'];
        };
    }
    return new SpringCollections();
});<commit_msg>Fix return type of mapCollectionURLToDataURL<commit_after>/*
 * Copyright 2016 EuregJUG.
 *
 * 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.
 */
define([], function () {
    function SpringCollections() {
        var self = this;

        /**
         * A function that translates org.springframework.data.domain.Page results
         * to oj.Collection.
         *
         * @param {type} response
         * @returns
         */
        self.mapCollectionsToDataArguments = function (response) {
            return {
                totalResults: response['totalElements'],
                limit: response['size'],
                count: response['numberOfElements'],
                hasMore: !response['last'],
                offset: response['number'] * response['size']
            };
        };

        /**
         * A function that transforms oj.Collections option to the format of an
         * endpoint of an API that takes org.springframework.data.domain.Pageable
         * parameter.
         *
         * @param {type} operation
         * @param {type} col
         * @param {type} options
         * @returns {String}
         */
        self.mapCollectionURLToDataURL = function (operation, col, options) {
            var rv = null;
            if (operation === 'read') {
                rv = options.context.url
                        + '?size=' + options.fetchSize
                        + '&page=' + (options.startIndex / options.fetchSize)
                        + '&sort=' + options.sort + ',' + options.sortDir;
            }
            return {url: rv};
        };

        /**
         * Extracts the content from a org.springframework.data.domain.Page result
         * @param {type} response
         * @returns {unresolved}
         */
        self.extractContent = function(response) {
            return response['content'];
        };
    }
    return new SpringCollections();
});<|endoftext|>


<commit_before>import * as React from 'react';
import PropTypes from 'prop-types';
import { TransitionGroup } from 'react-transition-group';
import clsx from 'clsx';
import { keyframes } from '@mui/system';
import styled from '../styles/styled';
import useThemeProps from '../styles/useThemeProps';
import Ripple from './Ripple';
import touchRippleClasses from './touchRippleClasses';

const DURATION = 550;
export const DELAY_RIPPLE = 80;

const enterKeyframe = keyframes`
  0% {
    transform: scale(0);
    opacity: 0.1;
  }

  100% {
    transform: scale(1);
    opacity: 0.3;
  }
`;

const exitKeyframe = keyframes`
  0% {
    opacity: 1;
  }

  100% {
    opacity: 0;
  }
`;

const pulsateKeyframe = keyframes`
  0% {
    transform: scale(1);
  }

  50% {
    transform: scale(0.92);
  }

  100% {
    transform: scale(1);
  }
`;

export const TouchRippleRoot = styled('span', {
  name: 'MuiTouchRipple',
  slot: 'Root',
})({
  overflow: 'hidden',
  pointerEvents: 'none',
  position: 'absolute',
  zIndex: 0,
  top: 0,
  right: 0,
  bottom: 0,
  left: 0,
  borderRadius: 'inherit',
});

// This `styled()` function invokes keyframes. `styled-components` only supports keyframes
// in string templates. Do not convert these styles in JS object as it will break.
export const TouchRippleRipple = styled(Ripple, {
  name: 'MuiTouchRipple',
  slot: 'Ripple',
})`
  opacity: 0;
  position: absolute;

  &.${touchRippleClasses.rippleVisible} {
    opacity: 0.3;
    transform: scale(1);
    animation-name: ${enterKeyframe};
    animation-duration: ${DURATION}ms;
    animation-timing-function: ${({ theme }) => theme.transitions.easing.easeInOut};
  }

  &.${touchRippleClasses.ripplePulsate} {
    animation-duration: ${({ theme }) => theme.transitions.duration.shorter}ms;
  }

  & .${touchRippleClasses.child} {
    opacity: 1;
    display: block;
    width: 100%;
    height: 100%;
    border-radius: 50%;
    background-color: currentColor;
  }

  & .${touchRippleClasses.childLeaving} {
    opacity: 0;
    animation-name: ${exitKeyframe};
    animation-duration: ${DURATION}ms;
    animation-timing-function: ${({ theme }) => theme.transitions.easing.easeInOut};
  }

  & .${touchRippleClasses.childPulsate} {
    position: absolute;
    /* @noflip */
    left: 0px;
    top: 0;
    animation-name: ${pulsateKeyframe};
    animation-duration: 2500ms;
    animation-timing-function: ${({ theme }) => theme.transitions.easing.easeInOut};
    animation-iteration-count: infinite;
    animation-delay: 200ms;
  }
`;

/**
 * @ignore - internal component.
 *
 * TODO v5: Make private
 */
const TouchRipple = React.forwardRef(function TouchRipple(inProps, ref) {
  const props = useThemeProps({ props: inProps, name: 'MuiTouchRipple' });

  const { center: centerProp = false, classes = {}, className, ...other } = props;
  const [ripples, setRipples] = React.useState([]);
  const nextKey = React.useRef(0);
  const rippleCallback = React.useRef(null);

  React.useEffect(() => {
    if (rippleCallback.current) {
      rippleCallback.current();
      rippleCallback.current = null;
    }
  }, [ripples]);

  // Used to filter out mouse emulated events on mobile.
  const ignoringMouseDown = React.useRef(false);
  // We use a timer in order to only show the ripples for touch "click" like events.
  // We don't want to display the ripple for touch scroll events.
  const startTimer = React.useRef(null);

  // This is the hook called once the previous timeout is ready.
  const startTimerCommit = React.useRef(null);
  const container = React.useRef(null);

  React.useEffect(() => {
    return () => {
      clearTimeout(startTimer.current);
    };
  }, []);

  const startCommit = React.useCallback(
    (params) => {
      const { pulsate, rippleX, rippleY, rippleSize, cb } = params;

      setRipples((oldRipples) => [
        ...oldRipples,
        <TouchRippleRipple
          key={nextKey.current}
          classes={{
            ripple: clsx(classes.ripple, touchRippleClasses.ripple),
            rippleVisible: clsx(classes.rippleVisible, touchRippleClasses.rippleVisible),
            ripplePulsate: clsx(classes.ripplePulsate, touchRippleClasses.ripplePulsate),
            child: clsx(classes.child, touchRippleClasses.child),
            childLeaving: clsx(classes.childLeaving, touchRippleClasses.childLeaving),
            childPulsate: clsx(classes.childPulsate, touchRippleClasses.childPulsate),
          }}
          timeout={DURATION}
          pulsate={pulsate}
          rippleX={rippleX}
          rippleY={rippleY}
          rippleSize={rippleSize}
        />,
      ]);
      nextKey.current += 1;
      rippleCallback.current = cb;
    },
    [classes],
  );

  const start = React.useCallback(
    (event = {}, options = {}, cb) => {
      const {
        pulsate = false,
        center = centerProp || options.pulsate,
        fakeElement = false, // For test purposes
      } = options;

      if (event.type === 'mousedown' && ignoringMouseDown.current) {
        ignoringMouseDown.current = false;
        return;
      }

      if (event.type === 'touchstart') {
        ignoringMouseDown.current = true;
      }

      const element = fakeElement ? null : container.current;
      const rect = element
        ? element.getBoundingClientRect()
        : {
            width: 0,
            height: 0,
            left: 0,
            top: 0,
          };

      // Get the size of the ripple
      let rippleX;
      let rippleY;
      let rippleSize;

      if (
        center ||
        (event.clientX === 0 && event.clientY === 0) ||
        (!event.clientX && !event.touches)
      ) {
        rippleX = Math.round(rect.width / 2);
        rippleY = Math.round(rect.height / 2);
      } else {
        const { clientX, clientY } = event.touches ? event.touches[0] : event;
        rippleX = Math.round(clientX - rect.left);
        rippleY = Math.round(clientY - rect.top);
      }

      if (center) {
        rippleSize = Math.sqrt((2 * rect.width ** 2 + rect.height ** 2) / 3);

        // For some reason the animation is broken on Mobile Chrome if the size is even.
        if (rippleSize % 2 === 0) {
          rippleSize += 1;
        }
      } else {
        const sizeX =
          Math.max(Math.abs((element ? element.clientWidth : 0) - rippleX), rippleX) * 2 + 2;
        const sizeY =
          Math.max(Math.abs((element ? element.clientHeight : 0) - rippleY), rippleY) * 2 + 2;
        rippleSize = Math.sqrt(sizeX ** 2 + sizeY ** 2);
      }

      // Touche devices
      if (event.touches) {
        // check that this isn't another touchstart due to multitouch
        // otherwise we will only clear a single timer when unmounting while two
        // are running
        if (startTimerCommit.current === null) {
          // Prepare the ripple effect.
          startTimerCommit.current = () => {
            startCommit({ pulsate, rippleX, rippleY, rippleSize, cb });
          };
          // Delay the execution of the ripple effect.
          startTimer.current = setTimeout(() => {
            if (startTimerCommit.current) {
              startTimerCommit.current();
              startTimerCommit.current = null;
            }
          }, DELAY_RIPPLE); // We have to make a tradeoff with this value.
        }
      } else {
        startCommit({ pulsate, rippleX, rippleY, rippleSize, cb });
      }
    },
    [centerProp, startCommit],
  );

  const pulsate = React.useCallback(() => {
    start({}, { pulsate: true });
  }, [start]);

  const stop = React.useCallback((event, cb) => {
    clearTimeout(startTimer.current);

    // The touch interaction occurs too quickly.
    // We still want to show ripple effect.
    if (event.type === 'touchend' && startTimerCommit.current) {
      startTimerCommit.current();
      startTimerCommit.current = null;
      startTimer.current = setTimeout(() => {
        stop(event, cb);
      });
      return;
    }

    startTimerCommit.current = null;

    setRipples((oldRipples) => {
      if (oldRipples.length > 0) {
        return oldRipples.slice(1);
      }
      return oldRipples;
    });
    rippleCallback.current = cb;
  }, []);

  React.useImperativeHandle(
    ref,
    () => ({
      pulsate,
      start,
      stop,
    }),
    [pulsate, start, stop],
  );

  return (
    <TouchRippleRoot
      className={clsx(classes.root, touchRippleClasses.root, className)}
      ref={container}
      {...other}
    >
      <TransitionGroup component={null} exit>
        {ripples}
      </TransitionGroup>
    </TouchRippleRoot>
  );
});

TouchRipple.propTypes = {
  /**
   * If `true`, the ripple starts at the center of the component
   * rather than at the point of interaction.
   */
  center: PropTypes.bool,
  /**
   * Override or extend the styles applied to the component.
   * See [CSS API](#css) below for more details.
   */
  classes: PropTypes.object,
  /**
   * @ignore
   */
  className: PropTypes.string,
};

export default TouchRipple;
<commit_msg>[TouchRipple] Allows call imperative methods without event (#31955)<commit_after>import * as React from 'react';
import PropTypes from 'prop-types';
import { TransitionGroup } from 'react-transition-group';
import clsx from 'clsx';
import { keyframes } from '@mui/system';
import styled from '../styles/styled';
import useThemeProps from '../styles/useThemeProps';
import Ripple from './Ripple';
import touchRippleClasses from './touchRippleClasses';

const DURATION = 550;
export const DELAY_RIPPLE = 80;

const enterKeyframe = keyframes`
  0% {
    transform: scale(0);
    opacity: 0.1;
  }

  100% {
    transform: scale(1);
    opacity: 0.3;
  }
`;

const exitKeyframe = keyframes`
  0% {
    opacity: 1;
  }

  100% {
    opacity: 0;
  }
`;

const pulsateKeyframe = keyframes`
  0% {
    transform: scale(1);
  }

  50% {
    transform: scale(0.92);
  }

  100% {
    transform: scale(1);
  }
`;

export const TouchRippleRoot = styled('span', {
  name: 'MuiTouchRipple',
  slot: 'Root',
})({
  overflow: 'hidden',
  pointerEvents: 'none',
  position: 'absolute',
  zIndex: 0,
  top: 0,
  right: 0,
  bottom: 0,
  left: 0,
  borderRadius: 'inherit',
});

// This `styled()` function invokes keyframes. `styled-components` only supports keyframes
// in string templates. Do not convert these styles in JS object as it will break.
export const TouchRippleRipple = styled(Ripple, {
  name: 'MuiTouchRipple',
  slot: 'Ripple',
})`
  opacity: 0;
  position: absolute;

  &.${touchRippleClasses.rippleVisible} {
    opacity: 0.3;
    transform: scale(1);
    animation-name: ${enterKeyframe};
    animation-duration: ${DURATION}ms;
    animation-timing-function: ${({ theme }) => theme.transitions.easing.easeInOut};
  }

  &.${touchRippleClasses.ripplePulsate} {
    animation-duration: ${({ theme }) => theme.transitions.duration.shorter}ms;
  }

  & .${touchRippleClasses.child} {
    opacity: 1;
    display: block;
    width: 100%;
    height: 100%;
    border-radius: 50%;
    background-color: currentColor;
  }

  & .${touchRippleClasses.childLeaving} {
    opacity: 0;
    animation-name: ${exitKeyframe};
    animation-duration: ${DURATION}ms;
    animation-timing-function: ${({ theme }) => theme.transitions.easing.easeInOut};
  }

  & .${touchRippleClasses.childPulsate} {
    position: absolute;
    /* @noflip */
    left: 0px;
    top: 0;
    animation-name: ${pulsateKeyframe};
    animation-duration: 2500ms;
    animation-timing-function: ${({ theme }) => theme.transitions.easing.easeInOut};
    animation-iteration-count: infinite;
    animation-delay: 200ms;
  }
`;

/**
 * @ignore - internal component.
 *
 * TODO v5: Make private
 */
const TouchRipple = React.forwardRef(function TouchRipple(inProps, ref) {
  const props = useThemeProps({ props: inProps, name: 'MuiTouchRipple' });

  const { center: centerProp = false, classes = {}, className, ...other } = props;
  const [ripples, setRipples] = React.useState([]);
  const nextKey = React.useRef(0);
  const rippleCallback = React.useRef(null);

  React.useEffect(() => {
    if (rippleCallback.current) {
      rippleCallback.current();
      rippleCallback.current = null;
    }
  }, [ripples]);

  // Used to filter out mouse emulated events on mobile.
  const ignoringMouseDown = React.useRef(false);
  // We use a timer in order to only show the ripples for touch "click" like events.
  // We don't want to display the ripple for touch scroll events.
  const startTimer = React.useRef(null);

  // This is the hook called once the previous timeout is ready.
  const startTimerCommit = React.useRef(null);
  const container = React.useRef(null);

  React.useEffect(() => {
    return () => {
      clearTimeout(startTimer.current);
    };
  }, []);

  const startCommit = React.useCallback(
    (params) => {
      const { pulsate, rippleX, rippleY, rippleSize, cb } = params;

      setRipples((oldRipples) => [
        ...oldRipples,
        <TouchRippleRipple
          key={nextKey.current}
          classes={{
            ripple: clsx(classes.ripple, touchRippleClasses.ripple),
            rippleVisible: clsx(classes.rippleVisible, touchRippleClasses.rippleVisible),
            ripplePulsate: clsx(classes.ripplePulsate, touchRippleClasses.ripplePulsate),
            child: clsx(classes.child, touchRippleClasses.child),
            childLeaving: clsx(classes.childLeaving, touchRippleClasses.childLeaving),
            childPulsate: clsx(classes.childPulsate, touchRippleClasses.childPulsate),
          }}
          timeout={DURATION}
          pulsate={pulsate}
          rippleX={rippleX}
          rippleY={rippleY}
          rippleSize={rippleSize}
        />,
      ]);
      nextKey.current += 1;
      rippleCallback.current = cb;
    },
    [classes],
  );

  const start = React.useCallback(
    (event = {}, options = {}, cb) => {
      const {
        pulsate = false,
        center = centerProp || options.pulsate,
        fakeElement = false, // For test purposes
      } = options;

      if (event?.type === 'mousedown' && ignoringMouseDown.current) {
        ignoringMouseDown.current = false;
        return;
      }

      if (event?.type === 'touchstart') {
        ignoringMouseDown.current = true;
      }

      const element = fakeElement ? null : container.current;
      const rect = element
        ? element.getBoundingClientRect()
        : {
            width: 0,
            height: 0,
            left: 0,
            top: 0,
          };

      // Get the size of the ripple
      let rippleX;
      let rippleY;
      let rippleSize;

      if (
        center ||
        event === undefined ||
        (event.clientX === 0 && event.clientY === 0) ||
        (!event.clientX && !event.touches)
      ) {
        rippleX = Math.round(rect.width / 2);
        rippleY = Math.round(rect.height / 2);
      } else {
        const { clientX, clientY } = event.touches ? event.touches[0] : event;
        rippleX = Math.round(clientX - rect.left);
        rippleY = Math.round(clientY - rect.top);
      }

      if (center) {
        rippleSize = Math.sqrt((2 * rect.width ** 2 + rect.height ** 2) / 3);

        // For some reason the animation is broken on Mobile Chrome if the size is even.
        if (rippleSize % 2 === 0) {
          rippleSize += 1;
        }
      } else {
        const sizeX =
          Math.max(Math.abs((element ? element.clientWidth : 0) - rippleX), rippleX) * 2 + 2;
        const sizeY =
          Math.max(Math.abs((element ? element.clientHeight : 0) - rippleY), rippleY) * 2 + 2;
        rippleSize = Math.sqrt(sizeX ** 2 + sizeY ** 2);
      }

      // Touche devices
      if (event?.touches) {
        // check that this isn't another touchstart due to multitouch
        // otherwise we will only clear a single timer when unmounting while two
        // are running
        if (startTimerCommit.current === null) {
          // Prepare the ripple effect.
          startTimerCommit.current = () => {
            startCommit({ pulsate, rippleX, rippleY, rippleSize, cb });
          };
          // Delay the execution of the ripple effect.
          startTimer.current = setTimeout(() => {
            if (startTimerCommit.current) {
              startTimerCommit.current();
              startTimerCommit.current = null;
            }
          }, DELAY_RIPPLE); // We have to make a tradeoff with this value.
        }
      } else {
        startCommit({ pulsate, rippleX, rippleY, rippleSize, cb });
      }
    },
    [centerProp, startCommit],
  );

  const pulsate = React.useCallback(() => {
    start({}, { pulsate: true });
  }, [start]);

  const stop = React.useCallback((event, cb) => {
    clearTimeout(startTimer.current);

    // The touch interaction occurs too quickly.
    // We still want to show ripple effect.
    if (event?.type === 'touchend' && startTimerCommit.current) {
      startTimerCommit.current();
      startTimerCommit.current = null;
      startTimer.current = setTimeout(() => {
        stop(event, cb);
      });
      return;
    }

    startTimerCommit.current = null;

    setRipples((oldRipples) => {
      if (oldRipples.length > 0) {
        return oldRipples.slice(1);
      }
      return oldRipples;
    });
    rippleCallback.current = cb;
  }, []);

  React.useImperativeHandle(
    ref,
    () => ({
      pulsate,
      start,
      stop,
    }),
    [pulsate, start, stop],
  );

  return (
    <TouchRippleRoot
      className={clsx(classes.root, touchRippleClasses.root, className)}
      ref={container}
      {...other}
    >
      <TransitionGroup component={null} exit>
        {ripples}
      </TransitionGroup>
    </TouchRippleRoot>
  );
});

TouchRipple.propTypes = {
  /**
   * If `true`, the ripple starts at the center of the component
   * rather than at the point of interaction.
   */
  center: PropTypes.bool,
  /**
   * Override or extend the styles applied to the component.
   * See [CSS API](#css) below for more details.
   */
  classes: PropTypes.object,
  /**
   * @ignore
   */
  className: PropTypes.string,
};

export default TouchRipple;
<|endoftext|>


<commit_before>/*
 * Copyright 2016 Resin.io
 *
 * 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.
 */

'use strict';

/**
 * @module Etcher.analytics
 */

const _ = require('lodash');
const angular = require('angular');
const username = require('username');
const app = require('electron').remote.app;

// Force Mixpanel snippet to load Mixpanel locally
// instead of using a CDN for performance reasons
window.MIXPANEL_CUSTOM_LIB_URL = '../bower_components/mixpanel/mixpanel.js';

require('../../../bower_components/mixpanel/mixpanel-jslib-snippet.js');
require('../../../bower_components/angular-mixpanel/src/angular-mixpanel');
const analytics = angular.module('Etcher.analytics', [
  'analytics.mixpanel',
  'Etcher.settings'
]);

analytics.config(function($mixpanelProvider) {
  $mixpanelProvider.apiKey('63e5fc4563e00928da67d1226364dd4c');

  $mixpanelProvider.superProperties({

    // jscs:disable requireCamelCaseOrUpperCaseIdentifiers

    distinct_id: username.sync(),

    // jscs:enable requireCamelCaseOrUpperCaseIdentifiers

    electron: app.getVersion(),
    node: process.version,
    arch: process.arch
  });
});

// TrackJS integration
// http://docs.trackjs.com/tracker/framework-integrations
analytics.config(function($provide) {
  $provide.decorator('$exceptionHandler', function($delegate, $window, $injector) {
    return function(exception, cause) {
      const SettingsService = $injector.get('SettingsService');

      if (SettingsService.data.errorReporting) {
        $window.trackJs.track(exception);
      }

      $delegate(exception, cause);
    };
  });

  $provide.decorator('$log', function($delegate, $window, $injector) {

    // Save the original $log.debug()
    let debugFn = $delegate.debug;

    $delegate.debug = function(message) {
      message = new Date() + ' ' + message;

      const SettingsService = $injector.get('SettingsService');

      if (SettingsService.data.errorReporting) {
        $window.trackJs.console.debug(message);
      }

      debugFn.call(null, message);
    };

    return $delegate;
  });
});

analytics.service('AnalyticsService', function($log, $mixpanel, SettingsService) {
  let self = this;

  /**
   * @summary Log a debug message
   * @function
   * @public
   *
   * @description
   * This function sends the debug message to TrackJS only.
   *
   * @param {String} message - message
   *
   * @example
   * AnalyticsService.log('Hello World');
   */
  this.log = function(message) {
    $log.debug(message);
  };

  /**
   * @summary Log an event
   * @function
   * @public
   *
   * @description
   * This function sends the debug message to TrackJS and Mixpanel.
   *
   * @param {String} message - message
   * @param {Object} [data] - event data
   *
   * @example
   * AnalyticsService.logEvent('Select image', {
   *   image: '/dev/disk2'
   * });
   */
  this.logEvent = function(message, data) {

    if (SettingsService.data.errorReporting) {

      // Clone data before passing it to `mixpanel.track`
      // since this function mutates the object adding
      // some custom private Mixpanel properties.
      $mixpanel.track(message, _.clone(data));

    }

    if (data) {
      message += ` (${JSON.stringify(data)})`;
    }

    self.log(message);
  };

});
<commit_msg>Configure TrackJS with userId<commit_after>/*
 * Copyright 2016 Resin.io
 *
 * 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.
 */

'use strict';

/**
 * @module Etcher.analytics
 */

const _ = require('lodash');
const angular = require('angular');
const username = require('username');
const app = require('electron').remote.app;

// Force Mixpanel snippet to load Mixpanel locally
// instead of using a CDN for performance reasons
window.MIXPANEL_CUSTOM_LIB_URL = '../bower_components/mixpanel/mixpanel.js';

require('../../../bower_components/mixpanel/mixpanel-jslib-snippet.js');
require('../../../bower_components/angular-mixpanel/src/angular-mixpanel');
const analytics = angular.module('Etcher.analytics', [
  'analytics.mixpanel',
  'Etcher.settings'
]);

analytics.config(function($mixpanelProvider) {
  $mixpanelProvider.apiKey('63e5fc4563e00928da67d1226364dd4c');

  $mixpanelProvider.superProperties({

    // jscs:disable requireCamelCaseOrUpperCaseIdentifiers

    distinct_id: username.sync(),

    // jscs:enable requireCamelCaseOrUpperCaseIdentifiers

    electron: app.getVersion(),
    node: process.version,
    arch: process.arch
  });
});

// TrackJS integration
// http://docs.trackjs.com/tracker/framework-integrations

analytics.run(function($window) {
  $window.trackJs.configure({
    userId: username.sync()
  });
});

analytics.config(function($provide) {
  $provide.decorator('$exceptionHandler', function($delegate, $window, $injector) {
    return function(exception, cause) {
      const SettingsService = $injector.get('SettingsService');

      if (SettingsService.data.errorReporting) {
        $window.trackJs.track(exception);
      }

      $delegate(exception, cause);
    };
  });

  $provide.decorator('$log', function($delegate, $window, $injector) {

    // Save the original $log.debug()
    let debugFn = $delegate.debug;

    $delegate.debug = function(message) {
      message = new Date() + ' ' + message;

      const SettingsService = $injector.get('SettingsService');

      if (SettingsService.data.errorReporting) {
        $window.trackJs.console.debug(message);
      }

      debugFn.call(null, message);
    };

    return $delegate;
  });
});

analytics.service('AnalyticsService', function($log, $mixpanel, SettingsService) {
  let self = this;

  /**
   * @summary Log a debug message
   * @function
   * @public
   *
   * @description
   * This function sends the debug message to TrackJS only.
   *
   * @param {String} message - message
   *
   * @example
   * AnalyticsService.log('Hello World');
   */
  this.log = function(message) {
    $log.debug(message);
  };

  /**
   * @summary Log an event
   * @function
   * @public
   *
   * @description
   * This function sends the debug message to TrackJS and Mixpanel.
   *
   * @param {String} message - message
   * @param {Object} [data] - event data
   *
   * @example
   * AnalyticsService.logEvent('Select image', {
   *   image: '/dev/disk2'
   * });
   */
  this.logEvent = function(message, data) {

    if (SettingsService.data.errorReporting) {

      // Clone data before passing it to `mixpanel.track`
      // since this function mutates the object adding
      // some custom private Mixpanel properties.
      $mixpanel.track(message, _.clone(data));

    }

    if (data) {
      message += ` (${JSON.stringify(data)})`;
    }

    self.log(message);
  };

});
<|endoftext|>


<commit_before>// https://github.com/steida/este-todomvc/blob/master/src/lib/state.js
import EventEmitter from 'eventemitter3'
import Immutable from 'immutable'

export default class State extends EventEmitter {

    constructor(opt_json) {
        this._state = null;
        this._previousState = null;
        this.load(opt_json || {});
    }

    load(json: Object) {
        this.set(Immutable.fromJS(json));
    }

    set(state) {
        if (this._state === state) {
            return;
        }

        this._previousState = this._state;
        this._state = state;
        this.emit('change', this._state, this._previousState);
    }

    get() {
        return this._state;
    }

    save(): Object {
        return this._state.toJS();
    }

    cursor(path) {
        return (update) => {
            if (update) {
                this.set(this._state.updateIn(path, update));
            } else {
                return this._state.getIn(path);
            }
        }
    }
}
<commit_msg>state fixed<commit_after>// https://github.com/steida/este-todomvc/blob/master/src/lib/state.js
import EventEmitter from 'eventemitter3'
import Immutable from 'immutable'

export default class State extends EventEmitter {

    constructor(opt_json) {
        super();
        this._state = null;
        this._previousState = null;
        this.load(opt_json || {});
    }

    load(json: Object) {
        this.set(Immutable.fromJS(json));
    }

    set(state) {
        if (this._state === state) {
            return;
        }

        this._previousState = this._state;
        this._state = state;
        this.emit('change', this._state, this._previousState);
    }

    get() {
        return this._state;
    }

    save(): Object {
        return this._state.toJS();
    }

    cursor(path) {
        return (update) => {
            if (update) {
                this.set(this._state.updateIn(path, update));
            } else {
                return this._state.getIn(path);
            }
        }
    }
}
<|endoftext|>


<commit_before>var props = require('./index');

test('should get value by key', function () {
	var data = {x: 1, y: 2};
	var prop = props(data);
	expect(prop('x')).toBe(1);
});

test('should get empty value', function () {
	var prop = props({});
	expect(prop('a')).toBe(false);
});

test('should get default value', function () {
	var prop = props({});
	expect(prop('q', 'test value')).toBe('test value');
});

test('should get deep value', function () {
	var data = {x: {b: {c: 1}}, y: 2};
	var prop = props(data);
	expect(prop().deep(['x', 'b', 'c'])).toBe(1);
});

test('should get default value, deep, all', function () {
	var data = {x: {b: {c: 1}}, y: 2};
	var prop = props(data);
	expect(prop().deep(['x', 'b', 'c', 'e'], 'empty')).toBe('empty');
});

test('should get object by keys', function () {
	var data = {x: 1, y: 2, z: 'string'};
	var prop = props(data);
	var result = prop().all(['x', 'y', 'z']);
	expect(result).toEqual(data);
});
<commit_msg>test: update<commit_after>var props = require('./index');

test('should get value by key', function () {
	var data = {x: 1, y: 2};
	var prop = props(data);
	expect(prop('x')).toBe(1);
});

test('should get empty value', function () {
	var prop = props({});
	expect(prop('a')).toBe(false);
});

test('should get default value', function () {
	var prop = props({});
	expect(prop('q', 'test value')).toBe('test value');
});

test('should get deep value', function () {
	var data = {x: {b: {c: 1}}, y: 2};
	var prop = props(data);
	expect(prop(['x', 'b', 'c'])).toBe(1);
});

test('should get default value, deep, all', function () {
	var data = {x: {b: {c: 1}}, y: 2};
	var prop = props(data);
	expect(prop(['x', 'b', 'c', 'e'], 'empty')).toBe('empty');
});

test('should get object by keys', function () {
	var data = {x: 1, y: 2, z: 'string'};
	var prop = props(data);
	var result = prop().all(['x', 'y', 'z']);
	expect(result).toEqual(data);
});
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2011-2015 Berkeley Model United Nations. All rights reserved.
 * Use of this source code is governed by a BSD License (see LICENSE).
 */

'use strict';

var React = require('react');
var ReactRouter = require('react-router');

var NavTab = require('components/NavTab');
var PermissionDeniedView = require('components/PermissionDeniedView');
var TopBar = require('components/TopBar');
var User = require('utils/User');

var ChairView = React.createClass ({
  mixins: [ReactRouter.History],

  render: function() {
    return (

        <TopBar user={this.props.user} />

          <NavTab href="/chair/attendance">
            Attendance
          </NavTab>

        {this.props.children}

    );
  },
});

module.exports = ChairView;
<commit_msg>Chair app<commit_after>/**
 * Copyright (c) 2011-2015 Berkeley Model United Nations. All rights reserved.
 * Use of this source code is governed by a BSD License (see LICENSE).
 */

'use strict';

var React = require('react');
var ReactRouter = require('react-router');

var NavTab = require('components/NavTab');
var TopBar = require('components/TopBar');
var User = require('utils/User');

var ChairView = React.createClass ({
  mixins: [ReactRouter.History],

  render: function() {
    return (

        <TopBar user={this.props.user} />

          <NavTab href="/chair/attendance">
            Attendance
          </NavTab>

        {this.props.children}

    );
  },
});

module.exports = ChairView;
<|endoftext|>


<commit_before>if (!!$.prototype.justifiedGallery) {  // if justifiedGallery method is defined
  var options = {
    rowHeight: 140,
    margins: 4,
    lastRow: 'justify'
  };
  $('.article-gallery').justifiedGallery(options);
}

$(document).ready(function(){
  $("#menu-icon, #menu-icon-tablet").click(function(){
    if ( $('#menu').css('visibility') == 'hidden' ) {
      $('#menu').css('visibility','visible');
      $('#menu-icon, #menu-icon-tablet').addClass('active');

      var topDistance = $("#menu > #nav").offset().top;

      if ( $('#menu').css('visibility') != 'hidden' && topDistance < 50 ) {
        $("#menu > #nav").show();
      } else if ($('#menu').css('visibility') != 'hidden' && topDistance > 100) {
        $("#menu > #nav").hide();
      }
      return false;
    } else {
      $('#menu').css('visibility','hidden');
      $('#menu-icon, #menu-icon-tablet').removeClass('active');
      return false;
    }
  });

  /* Toggle between adding and removing the "responsive" class to topnav when the user clicks on the icon */
  $("#header > #nav > ul > .icon").click(function() {
    $("#header > #nav > ul").toggleClass("responsive");
  });

  if ( $( "#menu" ).length ) {
    $(window).on('scroll', function() {
      var topDistance = $("#menu > #nav").offset().top;

      if ( $('#menu').css('visibility') != 'hidden' && topDistance < 50 ) {
        $("#menu > #nav").show();
      } else if ($('#menu').css('visibility') != 'hidden' && topDistance > 100) {
        $("#menu > #nav").hide();
      }
    });
  }

  $(window).on('scroll', function() {
    var topDistance = $("#menu > #nav").offset().top;

    if ( ! $( "#menu-icon" ).is(":visible") && topDistance < 50 ) {
      $("#menu-icon-tablet").show();
      $("#top-icon-tablet").hide();
    } else if (! $( "#menu-icon" ).is(":visible") && topDistance > 100) {
      $("#menu-icon-tablet").hide();
      $("#top-icon-tablet").show();
    }
  });

  if ( $( "#footer-post" ).length ) {
    var lastScrollTop = 0;
    $(window).on('scroll', function() {
      var topDistance = $(window).scrollTop();

    if (topDistance > lastScrollTop){
      // downscroll code
      $("#footer-post").hide();
    } else {
      // upscroll code
      $("#footer-post").show();
    }
    lastScrollTop = topDistance;

    $("#nav-footer").hide();
    $("#toc-footer").hide();
    $("#share-footer").hide();

    if ( topDistance < 50 ) {
      $("#actions-footer > ul > #top").hide();
      $("#actions-footer > ul > #menu").show();
    } else if ( topDistance > 100 ) {
      $("#actions-footer > ul > #menu").hide();
      $("#actions-footer > ul > #top").show();
    }
    });
  }
});
<commit_msg>Fix error for scrollListener in js<commit_after>if (!!$.prototype.justifiedGallery) {  // if justifiedGallery method is defined
  var options = {
    rowHeight: 140,
    margins: 4,
    lastRow: 'justify'
  };
  $('.article-gallery').justifiedGallery(options);
}

$(document).ready(function(){
  $("#menu-icon, #menu-icon-tablet").click(function(){
    if ( $('#menu').css('visibility') == 'hidden' ) {
      $('#menu').css('visibility','visible');
      $('#menu-icon, #menu-icon-tablet').addClass('active');

      var topDistance = $("#menu > #nav").offset().top;

      if ( $('#menu').css('visibility') != 'hidden' && topDistance < 50 ) {
        $("#menu > #nav").show();
      } else if ($('#menu').css('visibility') != 'hidden' && topDistance > 100) {
        $("#menu > #nav").hide();
      }
      return false;
    } else {
      $('#menu').css('visibility','hidden');
      $('#menu-icon, #menu-icon-tablet').removeClass('active');
      return false;
    }
  });

  /* Toggle between adding and removing the "responsive" class to topnav when the user clicks on the icon */
  $("#header > #nav > ul > .icon").click(function() {
    $("#header > #nav > ul").toggleClass("responsive");
  });

  if ( $( "#menu" ).length ) {
    $(window).on('scroll', function() {
      var topDistance = $("#menu > #nav").offset().top;

      if ( $('#menu').css('visibility') != 'hidden' && topDistance < 50 ) {
        $("#menu > #nav").show();
      } else if ($('#menu').css('visibility') != 'hidden' && topDistance > 100) {
        $("#menu > #nav").hide();
      }

      if ( ! $( "#menu-icon" ).is(":visible") && topDistance < 50 ) {
        $("#menu-icon-tablet").show();
        $("#top-icon-tablet").hide();
      } else if (! $( "#menu-icon" ).is(":visible") && topDistance > 100) {
        $("#menu-icon-tablet").hide();
        $("#top-icon-tablet").show();
      }
    });
  }

  if ( $( "#footer-post" ).length ) {
    var lastScrollTop = 0;
    $(window).on('scroll', function() {
      var topDistance = $(window).scrollTop();

    if (topDistance > lastScrollTop){
      // downscroll code
      $("#footer-post").hide();
    } else {
      // upscroll code
      $("#footer-post").show();
    }
    lastScrollTop = topDistance;

    $("#nav-footer").hide();
    $("#toc-footer").hide();
    $("#share-footer").hide();

    if ( topDistance < 50 ) {
      $("#actions-footer > ul > #top").hide();
      $("#actions-footer > ul > #menu").show();
    } else if ( topDistance > 100 ) {
      $("#actions-footer > ul > #menu").hide();
      $("#actions-footer > ul > #top").show();
    }
    });
  }
});
<|endoftext|>