<commit_before>var whereIsMyBuild = function ($, d3) {
  var my = {
    width: 960,
    height: 2000,
    jenkinsUrl: "http://localhost:8080",
    startJob: "chain-start",
    updateInterval: 2000,
    commitUpdateInterval: 20000,
    bulkUpdateSize: 10
  };

  var data;
  var changes = {
    commits: []
  };
  var needsUpdate = true;

  var getJSONMaskingBackslash = function (url) {
    return $.ajax({
      dataType: "json",
      dataFilter: function (data) {
        return data.replace(/\\/g, '\\\\');
      },
      url: url
    });
  };

  //changes.commits = [
  //    {
  //        commitId: "1234660",
  //        user: "wolfs",
  //        msg: "Some commit"
  //    },
  //    {
  //        commitId: "1234661",
  //        user: "wolfs",
  //        msg: "Some other commit"
  //    },
  //    {
  //        commitId: "1234662",
  //        user: "wolfs",
  //        msg: "Some other commit"
  //    },
  //    {
  //        commitId: "1234663",
  //        user: "wolfs",
  //        msg: "Some other commit"
  //    },
  //    {
  //        commitId: "1234664",
  //        user: "wolfs",
  //        msg: "Some other commit"
  //    },
  //    {
  //        commitId: "1234665",
  //        user: "wolfs",
  //        msg: "Some other commit"
  //    },
  //    {
  //        commitId: "1234666",
  //        user: "wolfs",
  //        msg: "Some other commit"
  //    },
  //    {
  //        commitId: "1234667",
  //        user: "wolfs",
  //        msg: "Some other commit"
  //    },
  //    {
  //        commitId: "1234668",
  //        user: "wolfs",
  //        msg: "Some other commit"
  //    },
  //    {
  //        commitId: "1234669",
  //        user: "wolfs",
  //        msg: "Some other commit"
  //    },
  //    {
  //        commitId: "1234670",
  //        user: "wolfs",
  //        msg: "Some other commit"
  //    },
  //    {
  //        commitId: "1234671",
  //        user: "wolfs",
  //        msg: "Some other commit"
  //    },
  //    {
  //        commitId: "1234672",
  //        user: "wolfs",
  //        msg: "Some other commit"
  //    },
  //    {
  //        commitId: "1234671",
  //        user: "wolfs",
  //        msg: "Some other commit"
  //    },
  //    {
  //        commitId: "1234673",
  //        user: "wolfs",
  //        msg: "Some other commit"
  //    },
  //    {
  //        commitId: "1234674",
  //        user: "wolfs",
  //        msg: "Some other commit"
  //    },
  //];

  var updateChanges = function () {
    var jobRequest = $.getJSON(
      my.jenkinsUrl + "/job/" + my.startJob + "/api/json?tree=builds[changeSet[*[*]]]{,10}"
    );
    jobRequest.then(function (job) {
      var builds = job.builds;

      changes.commits = builds.map(function (build) {
        return build.changeSet.items;
      }).reduce(function (a, b) {
        return a.concat(b);
      });

      $(changes).trigger("change");
    });
  };

  var getQueryVariable = function (variable) {
    var query = window.location.search.substring(1),
      results = query.split("&").map(function (el) {
        return el.split("=")
      }).filter(function (el) {
        return (el[0] === variable);
      }).map(function (el) {
        return el[1];
      });
    return results.length === 0 ? false : results[0];
  };

  var baseBuildNode = {
    getNewFailCount: function () {
      if (this.newFailCount) {
        return this.newFailCount;
      }
      return 0;
    }

  };

  var buildNode = function (jobName, revision, url) {
    var n = Object.create(baseBuildNode);
    n.jobName = jobName;
    n.revision = revision;
    n.url = url;
    n.status = "pending";
    n.downstreamProjects = [];

    return n;
  };

  var toUpdate = [];

  var renderer = function (conf, data) {
    var my = {};

    var cluster = d3.layout.tree().nodeSize([200, 200]);
    var diagonal = d3.svg.diagonal()
      .projection(function (d) {
        return [d.x, d.y];
      });

    var canvas = d3.select("body").append("svg")
      .attr("width", conf.width)
      .attr("height", conf.height);

    var svg = canvas
      .append("g")
      .attr("transform", "translate(" + (conf.width / 2) + ",200)");

    d3.select(self.frameElement).style("height", conf.height + "px");

    var jobName = function (job) {
      return job.jobName;
    };

    var jobUrl = function (job) {
      return job.url;
    };

    var addBuildNode = function (node, radius, cssClass) {
      var arc = d3.svg.arc()
        .innerRadius(radius / 2)
        .outerRadius(radius)
        .startAngle(0)
        .endAngle(Math.PI * 2);

      node.append("circle")
        .attr("r", radius);
      node.append("path")
        .attr("class", cssClass)
        .attr("d", arc);
      node.append("text")
        .style("text-anchor", "middle")
        .attr("dy", "0.3em")
        .attr("class", "testcount");
    };

    my.renderData = function () {
      var nodes = cluster.nodes(data);

      var maxY = nodes.reduce(function (acc, current) {
        return Math.max(acc, current.y);
      }, 400);

      canvas.attr("height", (maxY + 400) + "px");

      var links = cluster.links(nodes);

      var link = svg.selectAll(".link")
        .data(links, function (d) {
          return d.source.jobName + "->" + d.target.jobName;
        });

      link.enter().insert("path", ".node")
        .attr("class", "link");

      link.transition().attr("d", diagonal);

      link.exit().remove();

      var node = svg.selectAll(".node")
        .data(nodes, jobName);

      var parentNode = node.enter().append("g")
        .attr("class", "node");

      var coreNode = parentNode
        .append("a");

      var textNode = coreNode
        .append("text")
        .attr("transform", "rotate(10)")
        .attr("class", "core");

      addBuildNode(coreNode, 20, "core");
      var dxChildren = function () {
        return 40;
      };

      textNode
        .append("tspan")
        .text(jobName);

      textNode
        .append("tspan")
        .attr("class", "revision")
        .attr("dy", "1.2em")
        .text(function (d) {
          return d.revision
        });

      var downstreamNodes = node.selectAll(".downstream").data(function (coreNode) {
        return coreNode.downstreamProjects;
      }, jobName);

      var downstreamContainer = downstreamNodes.enter()
        .append("a")
        .attr("class", "downstream")
        .attr("transform", function (d, i) {
          return "rotate(" + (-10 + 35 * i) + ")translate(-40,0)";
        });

      addBuildNode(downstreamContainer, 10, "downstream");

      downstreamContainer
        .append("text")
        .text(function (d) {
          var parentData = d3.select(this.parentNode.parentNode).datum();
          return d.jobName.replace(new RegExp(parentData.jobName + '(-|~~)*'), '')
            .split('-')
            .map(function (s) {
              return s[0]
            })
            .join('');
        })
        .attr("text-anchor", "end")
        .attr("dx", "-15")
        .attr("dy", "0.3em");

      downstreamNodes.exit().remove();

      node.selectAll("a")
        .attr("xlink:href", jobUrl);

      node.selectAll("a text tspan")
        .attr("x", "0")
        .attr("dx", dxChildren);

      node.selectAll("text.testcount")
        .text(function (d) {
          var newFailCount = d.getNewFailCount();
          return newFailCount === 0 ? undefined : (newFailCount > 0 ? '+' + newFailCount : newFailCount);
        })
        .classed('worse', function (d) {
          return d.getNewFailCount() > 0;
        })
        .classed('better', function (d) {
          return d.getNewFailCount() < 0;
        });

      node.selectAll("path")
        .attr("class", function (d) {
          return d.status;
        });

      node.selectAll("a text.core").transition()
        .attr("dy", 0);

      node.selectAll("a text tspan.revision").transition()
        .text(function (d) {
          return d.revision;
        });

      node.transition().attr("transform", function (d) {
        return "translate(" + d.x + "," + d.y + ")";
      });

      node.exit().remove();
    };

    return my;
  };

  var changesRenderer = function(changes) {
    return {
      renderChanges: function() {
        var revisions = d3.select("#commits").selectAll(".revision").data(changes.commits, function (d) {
          return d.commitId;
        });

        revisions.enter()
          .append("li")
          .attr("class", "revision")
          .html(function (el) {
            return "" + el.commitId + " - " + el.user + "
" + el.msg.replace("\n", "
");
          });

        revisions.order();

        revisions.exit().remove();

        d3.selectAll(".loading").remove();
      }
  }
};

var buildData = function (nodeToUpdate) {
  var jobName = nodeToUpdate.jobName;
  var resultDef = $.Deferred();
  var findRevision = function (envVars) {
    for (var i = 0; i < envVars.length; i++) {
      var env = envVars[i];
      if (env.name === "REV") {
        return parseInt(env.value, 10);
      }
    }
  };

  var nodeFromProject = function (project) {
    return buildNode(project.name, nodeToUpdate.revision, project.url);
  };

  var buildKeys =
    "number,url,result,actions[triggeredProjects[name,url,downstreamProjects[url,name]],failCount,skipCount,totalCount,urlName],changeSet[items[commitId,author,msg]]";

  var jobRequest = $.getJSON(
    my.jenkinsUrl + "/job/" + jobName +
    "/api/json?tree=url,downstreamProjects[url,name],lastCompletedBuild[" + buildKeys + "]"
  ).then(function (job) {
      return job;
    });

  var buildDef = jobRequest.then(function (job) {
    if (nodeToUpdate.url === undefined) {
      nodeToUpdate.url = job.url;
      $(data).trigger("change");
    }
    return job.lastCompletedBuild;
  });

  var getEnvVars = function (build) {
    return build === undefined ? undefined : getJSONMaskingBackslash(build.url +
    "injectedEnvVars/export");
  };
  var getRevision = function (build) {
    if (build === undefined) {
      return undefined;
    }
    return getEnvVars(build).then(function (envVars) {
      return findRevision(envVars.envVars.envVar)
    })
  };

  var buildUrl = function (buildNumber) {
    return my.jenkinsUrl + "/job/" + nodeToUpdate.jobName + "/" + buildNumber +
      "/api/json?tree=" + buildKeys;
  };

  var getBuildDef = function (buildNumber) {
    return $.getJSON(buildUrl(buildNumber)).then(function (build) {
      return build;
    });
  };

  var buildForRevision = function (buildDef) {
    return $.when(buildDef, buildDef.then(getRevision)).then(
      function (build, revision) {
        if (build === undefined) {
          return undefined;
        }
        build.revision = revision;
        if (revision < nodeToUpdate.revision) {
          return undefined;
        } else if (revision === nodeToUpdate.revision) {
          return build;
        } else {
          return buildForRevision(getBuildDef(build.number - 1))
            .then(function (previousBuild) {
              if (previousBuild === undefined) {
                return build;
              } else {
                return previousBuild;
              }
            })
        }
      })
  };

  var getTriggeredProjects = function (build) {
    return build.actions.filter(function (el) {
      return el.triggeredProjects;
    }).map(function (el) {
      return el.triggeredProjects;
    }).reduce(function (a, b) {
      return a.concat(b);
    }, []);
  };

  var getTestResult = function (build) {
    var actions = build.actions;

    var testReports = actions.filter(function (action) {
      return action.urlName === "testReport";
    });

    if (testReports.length > 0) {
      return testReports[0];
    }
    return {
      failCount: 0,
      skipCount: 0,
      totalCount: 0
    };
  };

  var foundBuildDef = buildForRevision(buildDef);

  var previousBuildDef = foundBuildDef.then(function (build) {
    return build ? (build.number > 1 ? getBuildDef(build.number - 1) : undefined) : undefined;
  });

  $.when(foundBuildDef, previousBuildDef).then(function (build, previousBuild) {
    if (build === undefined) {
      toUpdate.push(nodeToUpdate);
      resultDef.resolve(nodeToUpdate);
    } else {
      nodeToUpdate.status = build.result.toLowerCase();
      nodeToUpdate.revision = build.revision;
      nodeToUpdate.url = build.url;
      nodeToUpdate.testResult = getTestResult(build);
      if (previousBuild !== undefined) {
        var previousTestResult = getTestResult(previousBuild);
        nodeToUpdate.newFailCount = nodeToUpdate.testResult.failCount - previousTestResult.failCount;
      }

      var triggeredProjects = getTriggeredProjects(build);
      var children = triggeredProjects.map(function (project) {
        var node = nodeFromProject(project);
        node.downstreamProjects = project.downstreamProjects.map(nodeFromProject);
        return node;
      });

      children.map(buildData);
      nodeToUpdate.downstreamProjects.map(buildData);

      nodeToUpdate.children = children;

      $(data).trigger("change");

      resultDef.resolve(nodeToUpdate);
    }
  }, function () {
    toUpdate.push(nodeToUpdate);
  });

  return resultDef;
};

var updateNext = function () {
  if (toUpdate.length > 0) {
    var toUpdateNow = toUpdate.slice(0, my.bulkUpdateSize);
    toUpdate = toUpdate.slice(my.bulkUpdateSize);
    toUpdateNow.map(buildData);
  }
};

my.init = function () {
  var revisionString = getQueryVariable("revision"),
    r,
    changesR = changesRenderer(changes);

  if (revisionString) {
    data = buildNode(my.startJob, parseInt(revisionString, 10));
    r = renderer(this, data);
    toUpdate.push(data);

    $(data).bind("change", function () {
      needsUpdate = true;
      setTimeout(function () {
        if (needsUpdate) {
          r.renderData();
          needsUpdate = false;
        }
      }, 0);
    });
    $(data).trigger("change");
    updateNext();
    setInterval(updateNext, my.updateInterval);
  }

  $(changes).bind("change", changesR.renderChanges);
  updateChanges();
  setInterval(updateChanges, my.commitUpdateInterval);

  //setTimeout(function () {
  //  changes.commits.splice(0, 0,
  //    {
  //      commitId: "1234664",
  //      user: "wolfs",
  //      msg: "Some third commit"
  //    },
  //    {
  //      commitId: "1234663",
  //      user: "wolfs",
  //      msg: "Some third commit"
  //    }
  //  );
  //  //changes.commits.pop();
  //}, 5000);
};

return my;

}($, d3);
<commit_msg>Use JSON-API to get envVars<commit_after>var whereIsMyBuild = function ($, d3) {
  var my = {
    width: 960,
    height: 2000,
    jenkinsUrl: "http://localhost:8080",
    startJob: "chain-start",
    updateInterval: 2000,
    commitUpdateInterval: 20000,
    bulkUpdateSize: 10
  };

  var data;
  var changes = {
    commits: []
  };
  var needsUpdate = true;

  //changes.commits = [
  //    {
  //        commitId: "1234660",
  //        user: "wolfs",
  //        msg: "Some commit"
  //    },
  //    {
  //        commitId: "1234661",
  //        user: "wolfs",
  //        msg: "Some other commit"
  //    },
  //    {
  //        commitId: "1234662",
  //        user: "wolfs",
  //        msg: "Some other commit"
  //    },
  //    {
  //        commitId: "1234663",
  //        user: "wolfs",
  //        msg: "Some other commit"
  //    },
  //    {
  //        commitId: "1234664",
  //        user: "wolfs",
  //        msg: "Some other commit"
  //    },
  //    {
  //        commitId: "1234665",
  //        user: "wolfs",
  //        msg: "Some other commit"
  //    },
  //    {
  //        commitId: "1234666",
  //        user: "wolfs",
  //        msg: "Some other commit"
  //    },
  //    {
  //        commitId: "1234667",
  //        user: "wolfs",
  //        msg: "Some other commit"
  //    },
  //    {
  //        commitId: "1234668",
  //        user: "wolfs",
  //        msg: "Some other commit"
  //    },
  //    {
  //        commitId: "1234669",
  //        user: "wolfs",
  //        msg: "Some other commit"
  //    },
  //    {
  //        commitId: "1234670",
  //        user: "wolfs",
  //        msg: "Some other commit"
  //    },
  //    {
  //        commitId: "1234671",
  //        user: "wolfs",
  //        msg: "Some other commit"
  //    },
  //    {
  //        commitId: "1234672",
  //        user: "wolfs",
  //        msg: "Some other commit"
  //    },
  //    {
  //        commitId: "1234671",
  //        user: "wolfs",
  //        msg: "Some other commit"
  //    },
  //    {
  //        commitId: "1234673",
  //        user: "wolfs",
  //        msg: "Some other commit"
  //    },
  //    {
  //        commitId: "1234674",
  //        user: "wolfs",
  //        msg: "Some other commit"
  //    },
  //];

  var updateChanges = function () {
    var jobRequest = $.getJSON(
      my.jenkinsUrl + "/job/" + my.startJob + "/api/json?tree=builds[changeSet[*[*]]]{,10}"
    );
    jobRequest.then(function (job) {
      var builds = job.builds;

      changes.commits = builds.map(function (build) {
        return build.changeSet.items;
      }).reduce(function (a, b) {
        return a.concat(b);
      });

      $(changes).trigger("change");
    });
  };

  var getQueryVariable = function (variable) {
    var query = window.location.search.substring(1),
      results = query.split("&").map(function (el) {
        return el.split("=")
      }).filter(function (el) {
        return (el[0] === variable);
      }).map(function (el) {
        return el[1];
      });
    return results.length === 0 ? false : results[0];
  };

  var baseBuildNode = {
    getNewFailCount: function () {
      if (this.newFailCount) {
        return this.newFailCount;
      }
      return 0;
    }

  };

  var buildNode = function (jobName, revision, url) {
    var n = Object.create(baseBuildNode);
    n.jobName = jobName;
    n.revision = revision;
    n.url = url;
    n.status = "pending";
    n.downstreamProjects = [];

    return n;
  };

  var toUpdate = [];

  var renderer = function (conf, data) {
    var my = {};

    var cluster = d3.layout.tree().nodeSize([200, 200]);
    var diagonal = d3.svg.diagonal()
      .projection(function (d) {
        return [d.x, d.y];
      });

    var canvas = d3.select("body").append("svg")
      .attr("width", conf.width)
      .attr("height", conf.height);

    var svg = canvas
      .append("g")
      .attr("transform", "translate(" + (conf.width / 2) + ",200)");

    d3.select(self.frameElement).style("height", conf.height + "px");

    var jobName = function (job) {
      return job.jobName;
    };

    var jobUrl = function (job) {
      return job.url;
    };

    var addBuildNode = function (node, radius, cssClass) {
      var arc = d3.svg.arc()
        .innerRadius(radius / 2)
        .outerRadius(radius)
        .startAngle(0)
        .endAngle(Math.PI * 2);

      node.append("circle")
        .attr("r", radius);
      node.append("path")
        .attr("class", cssClass)
        .attr("d", arc);
      node.append("text")
        .style("text-anchor", "middle")
        .attr("dy", "0.3em")
        .attr("class", "testcount");
    };

    my.renderData = function () {
      var nodes = cluster.nodes(data);

      var maxY = nodes.reduce(function (acc, current) {
        return Math.max(acc, current.y);
      }, 400);

      canvas.attr("height", (maxY + 400) + "px");

      var links = cluster.links(nodes);

      var link = svg.selectAll(".link")
        .data(links, function (d) {
          return d.source.jobName + "->" + d.target.jobName;
        });

      link.enter().insert("path", ".node")
        .attr("class", "link");

      link.transition().attr("d", diagonal);

      link.exit().remove();

      var node = svg.selectAll(".node")
        .data(nodes, jobName);

      var parentNode = node.enter().append("g")
        .attr("class", "node");

      var coreNode = parentNode
        .append("a");

      var textNode = coreNode
        .append("text")
        .attr("transform", "rotate(10)")
        .attr("class", "core");

      addBuildNode(coreNode, 20, "core");
      var dxChildren = function () {
        return 40;
      };

      textNode
        .append("tspan")
        .text(jobName);

      textNode
        .append("tspan")
        .attr("class", "revision")
        .attr("dy", "1.2em")
        .text(function (d) {
          return d.revision
        });

      var downstreamNodes = node.selectAll(".downstream").data(function (coreNode) {
        return coreNode.downstreamProjects;
      }, jobName);

      var downstreamContainer = downstreamNodes.enter()
        .append("a")
        .attr("class", "downstream")
        .attr("transform", function (d, i) {
          return "rotate(" + (-10 + 35 * i) + ")translate(-40,0)";
        });

      addBuildNode(downstreamContainer, 10, "downstream");

      downstreamContainer
        .append("text")
        .text(function (d) {
          var parentData = d3.select(this.parentNode.parentNode).datum();
          return d.jobName.replace(new RegExp(parentData.jobName + '(-|~~)*'), '')
            .split('-')
            .map(function (s) {
              return s[0]
            })
            .join('');
        })
        .attr("text-anchor", "end")
        .attr("dx", "-15")
        .attr("dy", "0.3em");

      downstreamNodes.exit().remove();

      node.selectAll("a")
        .attr("xlink:href", jobUrl);

      node.selectAll("a text tspan")
        .attr("x", "0")
        .attr("dx", dxChildren);

      node.selectAll("text.testcount")
        .text(function (d) {
          var newFailCount = d.getNewFailCount();
          return newFailCount === 0 ? undefined : (newFailCount > 0 ? '+' + newFailCount : newFailCount);
        })
        .classed('worse', function (d) {
          return d.getNewFailCount() > 0;
        })
        .classed('better', function (d) {
          return d.getNewFailCount() < 0;
        });

      node.selectAll("path")
        .attr("class", function (d) {
          return d.status;
        });

      node.selectAll("a text.core").transition()
        .attr("dy", 0);

      node.selectAll("a text tspan.revision").transition()
        .text(function (d) {
          return d.revision;
        });

      node.transition().attr("transform", function (d) {
        return "translate(" + d.x + "," + d.y + ")";
      });

      node.exit().remove();
    };

    return my;
  };

  var changesRenderer = function(changes) {
    return {
      renderChanges: function() {
        var revisions = d3.select("#commits").selectAll(".revision").data(changes.commits, function (d) {
          return d.commitId;
        });

        revisions.enter()
          .append("li")
          .attr("class", "revision")
          .html(function (el) {
            return "" + el.commitId + " - " + el.user + "
" + el.msg.replace("\n", "
");
          });

        revisions.order();

        revisions.exit().remove();

        d3.selectAll(".loading").remove();
      }
    }
  };

  var buildData = function (nodeToUpdate) {
    var jobName = nodeToUpdate.jobName;
    var resultDef = $.Deferred();
    var findRevision = function (envVars) {
      var revision = envVars["REV"];
      return revision === undefined ? undefined : parseInt(revision, 10);
    };

    var nodeFromProject = function (project) {
      return buildNode(project.name, nodeToUpdate.revision, project.url);
    };

    var buildKeys =
      "number,url,result,actions[triggeredProjects[name,url,downstreamProjects[url,name]],failCount,skipCount,totalCount,urlName],changeSet[items[commitId,author,msg]]";

    var jobRequest = $.getJSON(
      my.jenkinsUrl + "/job/" + jobName +
      "/api/json?tree=url,downstreamProjects[url,name],lastCompletedBuild[" + buildKeys + "]"
    ).then(function (job) {
        return job;
      });

    var buildDef = jobRequest.then(function (job) {
      if (nodeToUpdate.url === undefined) {
        nodeToUpdate.url = job.url;
        $(data).trigger("change");
      }
      return job.lastCompletedBuild;
    });

    var getEnvVars = function (build) {
      return build === undefined ? undefined : $.getJSON(build.url +
      "injectedEnvVars/api/json?tree=envMap[*]");
    };
    var getRevision = function (build) {
      if (build === undefined) {
        return undefined;
      }
      return getEnvVars(build).then(function (envVars) {
        return findRevision(envVars.envMap)
      })
    };

    var buildUrl = function (buildNumber) {
      return my.jenkinsUrl + "/job/" + nodeToUpdate.jobName + "/" + buildNumber +
        "/api/json?tree=" + buildKeys;
    };

    var getBuildDef = function (buildNumber) {
      return $.getJSON(buildUrl(buildNumber)).then(function (build) {
        return build;
      });
    };

    var buildForRevision = function (buildDef) {
      return $.when(buildDef, buildDef.then(getRevision)).then(
        function (build, revision) {
          if (build === undefined) {
            return undefined;
          }
          build.revision = revision;
          if (revision < nodeToUpdate.revision) {
            return undefined;
          } else if (revision === nodeToUpdate.revision) {
            return build;
          } else {
            return buildForRevision(getBuildDef(build.number - 1))
              .then(function (previousBuild) {
                if (previousBuild === undefined) {
                  return build;
                } else {
                  return previousBuild;
                }
              })
          }
        })
    };

    var getTriggeredProjects = function (build) {
      return build.actions.filter(function (el) {
        return el.triggeredProjects;
      }).map(function (el) {
        return el.triggeredProjects;
      }).reduce(function (a, b) {
        return a.concat(b);
      }, []);
    };

    var getTestResult = function (build) {
      var actions = build.actions;

      var testReports = actions.filter(function (action) {
        return action.urlName === "testReport";
      });

      if (testReports.length > 0) {
        return testReports[0];
      }
      return {
        failCount: 0,
        skipCount: 0,
        totalCount: 0
      };
    };

    var foundBuildDef = buildForRevision(buildDef);

    var previousBuildDef = foundBuildDef.then(function (build) {
      return build ? (build.number > 1 ? getBuildDef(build.number - 1) : undefined) : undefined;
    });

    $.when(foundBuildDef, previousBuildDef).then(function (build, previousBuild) {
      if (build === undefined) {
        toUpdate.push(nodeToUpdate);
        resultDef.resolve(nodeToUpdate);
      } else {
        nodeToUpdate.status = build.result.toLowerCase();
        nodeToUpdate.revision = build.revision;
        nodeToUpdate.url = build.url;
        nodeToUpdate.testResult = getTestResult(build);
        if (previousBuild !== undefined) {
          var previousTestResult = getTestResult(previousBuild);
          nodeToUpdate.newFailCount = nodeToUpdate.testResult.failCount - previousTestResult.failCount;
        }

        var triggeredProjects = getTriggeredProjects(build);
        var children = triggeredProjects.map(function (project) {
          var node = nodeFromProject(project);
          node.downstreamProjects = project.downstreamProjects.map(nodeFromProject);
          return node;
        });

        children.map(buildData);
        nodeToUpdate.downstreamProjects.map(buildData);

        nodeToUpdate.children = children;

        $(data).trigger("change");

        resultDef.resolve(nodeToUpdate);
      }
    }, function () {
      toUpdate.push(nodeToUpdate);
    });

    return resultDef;
  };

  var updateNext = function () {
    if (toUpdate.length > 0) {
      var toUpdateNow = toUpdate.slice(0, my.bulkUpdateSize);
      toUpdate = toUpdate.slice(my.bulkUpdateSize);
      toUpdateNow.map(buildData);
    }
  };

  my.init = function () {
    var revisionString = getQueryVariable("revision"),
      r,
      changesR = changesRenderer(changes);

    if (revisionString) {
      data = buildNode(my.startJob, parseInt(revisionString, 10));
      r = renderer(this, data);
      toUpdate.push(data);

      $(data).bind("change", function () {
        needsUpdate = true;
        setTimeout(function () {
          if (needsUpdate) {
            r.renderData();
            needsUpdate = false;
          }
        }, 0);
      });
      $(data).trigger("change");
      updateNext();
      setInterval(updateNext, my.updateInterval);
    }

    $(changes).bind("change", changesR.renderChanges);
    updateChanges();
    setInterval(updateChanges, my.commitUpdateInterval);

    //setTimeout(function () {
    //  changes.commits.splice(0, 0,
    //    {
    //      commitId: "1234664",
    //      user: "wolfs",
    //      msg: "Some third commit"
    //    },
    //    {
    //      commitId: "1234663",
    //      user: "wolfs",
    //      msg: "Some third commit"
    //    }
    //  );
    //  //changes.commits.pop();
    //}, 5000);
  };

  return my;

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


<commit_before>/**
 * @fileOverview Tweet utilities that convert different data combinations to
 * 140 character tweets.
 */

/** @const {string} */
var FACEBOOK_APP_ID = '595267417193679';

/** @type {Object} */
var ShareFormat = {};

/**
 * Clean html off of a string of content.
 * @param {string} str The string to clean.
 * @param {boolean} useLineBreaks Whether to use line breaks for replacements.
 * @return {string} The cleaned string.
 */
function cleanHtml(str, useLineBreaks) {
    return str.replace('

', !!useLineBreaks ? '\n' : ' ')
              .replace('
', '')
              .replace('
', '');
}

/**
 * Adapt content to share to specific providers.
 * @param {Object|Comment} data The data to adapt. Keeping a similar object
 *    structure to the Comment model so that either can be used.
 * @param {string} provider The provider type being shared to.
 * @return {Object}
 */
ShareFormat.contentToShare = function(data, provider) {
    var fn = ShareFormat.contentToTweet;
    if (provider === 'facebook') {
        fn = ShareFormat.contentToFacebookMessage;
    }
    return fn(data);
};

/**
 * Adapt content to share to Facebook.
 * @param {Object|Comment} data The data to adapt.
 * @return {Object}
 */
ShareFormat.contentToFacebookMessage = function(data) {
    return {
        body: null,
        url: data.permalink
    };
};

/**
 * Adapt content to share to Twitter.
 * @param {Object|Comment} data The data to adapt.
 * @return {Object}
 */
ShareFormat.contentToTweet = function(data) {
    var body = cleanHtml(data.body);
    var username = data.author.displayName;
    username = username ? '- ' + username : '';
    var permalink = data.permalink;
    var urlLength = permalink.length + 1; // +1 for the space in front of it.

    // Tweets are always 140 characters.
    var remaining = 140 - urlLength - username.length;

    if (remaining < body.length + 2) {
        body = body.substring(0, remaining - 5) + '...';
    }

    var finalBody = (140 - permalink.length - body.length - 2 - username.length < 0) ? '' :  '"' + body + '"' + username;

    return {
        body: finalBody,
        url: permalink
    };
};

/**
 * Generate provider specific params.
 * @param {Object} params All necessary params.
 * @return {string} Url formatted params.
 */
ShareFormat.generateParams = function(params) {
    if (params.provider === 'facebook') {
        return generateFacebookParams(params);
    }
    return generateTwitterParams(params);
};

/**
 * Generate Facebook specific params.
 * @param {Object} params All necessary params.
 * @return {string} Url formatted params.
 */
function generateFacebookParams(params) {
    var caption = 'Livefyre on "{title}"';
    var uri = [
        window.location.protocol,
        params.assetServer,
        '/facebook-uri.html'
    ].join('');
    caption = encodeURIComponent(caption.replace('{title}', document.title));
    return ['?app_id=', FACEBOOK_APP_ID,
            '&caption=', caption,
            '&display=popup',
            '&link=', encodeURIComponent(params.url),
            '&redirect_uri=', encodeURIComponent(uri)].join('');
}

/**
 * Generate Twitter specific params.
 * @param {Object} params All necessary params.
 * @return {string} Url formatted params.
 */
function generateTwitterParams(params) {
    return ['?text=', encodeURIComponent(params.body),
            '&url=', encodeURIComponent(params.url)].join('');
}

module.exports = ShareFormat;
<commit_msg>Update facebook share url<commit_after>/**
 * @fileOverview Tweet utilities that convert different data combinations to
 * 140 character tweets.
 */

/** @const {string} */
var FACEBOOK_APP_ID = '595267417193679';
var LIVEFYRE_CDN = '//cdn.livefyre.com';

/** @type {Object} */
var ShareFormat = {};

/**
 * Clean html off of a string of content.
 * @param {string} str The string to clean.
 * @param {boolean} useLineBreaks Whether to use line breaks for replacements.
 * @return {string} The cleaned string.
 */
function cleanHtml(str, useLineBreaks) {
    return str.replace('

', !!useLineBreaks ? '\n' : ' ')
              .replace('
', '')
              .replace('
', '');
}

/**
 * Adapt content to share to specific providers.
 * @param {Object|Comment} data The data to adapt. Keeping a similar object
 *    structure to the Comment model so that either can be used.
 * @param {string} provider The provider type being shared to.
 * @return {Object}
 */
ShareFormat.contentToShare = function(data, provider) {
    var fn = ShareFormat.contentToTweet;
    if (provider === 'facebook') {
        fn = ShareFormat.contentToFacebookMessage;
    }
    return fn(data);
};

/**
 * Adapt content to share to Facebook.
 * @param {Object|Comment} data The data to adapt.
 * @return {Object}
 */
ShareFormat.contentToFacebookMessage = function(data) {
    return {
        body: null,
        url: data.permalink
    };
};

/**
 * Adapt content to share to Twitter.
 * @param {Object|Comment} data The data to adapt.
 * @return {Object}
 */
ShareFormat.contentToTweet = function(data) {
    var body = cleanHtml(data.body);
    var username = data.author.displayName;
    username = username ? '- ' + username : '';
    var permalink = data.permalink;
    var urlLength = permalink.length + 1; // +1 for the space in front of it.

    // Tweets are always 140 characters.
    var remaining = 140 - urlLength - username.length;

    if (remaining < body.length + 2) {
        body = body.substring(0, remaining - 5) + '...';
    }

    var finalBody = (140 - permalink.length - body.length - 2 - username.length < 0) ? '' :  '"' + body + '"' + username;

    return {
        body: finalBody,
        url: permalink
    };
};

/**
 * Generate provider specific params.
 * @param {Object} params All necessary params.
 * @return {string} Url formatted params.
 */
ShareFormat.generateParams = function(params) {
    if (params.provider === 'facebook') {
        return generateFacebookParams(params);
    }
    return generateTwitterParams(params);
};

/**
 * Generate Facebook specific params.
 * @param {Object} params All necessary params.
 * @return {string} Url formatted params.
 */
function generateFacebookParams(params) {
    var caption = 'Livefyre on "{title}"';
    var uri = [
        window.location.protocol,
        params.assetServer || LIVEFYRE_CDN,
        '/libs/share/facebook-uri.html'
    ].join('');
    caption = encodeURIComponent(caption.replace('{title}', document.title));
    return ['?app_id=', FACEBOOK_APP_ID,
            '&caption=', caption,
            '&display=popup',
            '&link=', encodeURIComponent(params.url),
            '&redirect_uri=', encodeURIComponent(uri)].join('');
}

/**
 * Generate Twitter specific params.
 * @param {Object} params All necessary params.
 * @return {string} Url formatted params.
 */
function generateTwitterParams(params) {
    return ['?text=', encodeURIComponent(params.body),
            '&url=', encodeURIComponent(params.url)].join('');
}

module.exports = ShareFormat;
<|endoftext|>


<commit_before>var CLIENT_ID =
  "840179112792-bhg3k1h0dcnp9ltelj21o6vibphjcufe.apps.googleusercontent.com";

// Array of API discovery doc URLs for APIs used
var DISCOVERY_DOCS = [
  "https://sheets.googleapis.com/$discovery/rest?version=v4"
];

// Authorization scopes required by the API; multiple scopes can be
// included, separated by spaces.
var SCOPES = "https://www.googleapis.com/auth/spreadsheets";

// expense sheet id
var SPREADSHEET_ID = "1tAd4YjX8VgRkRG8-LXuYLhlUsyZgxLsRKqvbK74fetY";

var authorizeButton = document.getElementById("authorize-button");
var signoutButton = document.getElementById("signout-button");
var expenseForm = document.getElementById("expense-form");

var description = document.getElementById("description");
var date = document.getElementById("date");
var accountSelect = document.getElementById("account");
var categorySelect = document.getElementById("category");
var amount = document.getElementById("amount");
var income = document.getElementById("is-income");
var formLoader = document.getElementById("form-loader");
var snackbarContainer = document.getElementById("toast-container");

/**
 *  On load, called to load the auth2 library and API client library.
 */
function handleClientLoad() {
  gapi.load("client:auth2", initClient);
}

/**
 *  Initializes the API client library and sets up sign-in state
 *  listeners.
 */
function initClient() {
  gapi.client
    .init({
      discoveryDocs: DISCOVERY_DOCS,
      clientId: CLIENT_ID,
      scope: SCOPES
    })
    .then(function() {
      // Listen for sign-in state changes.
      gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus);

      // Handle the initial sign-in state.
      updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
      authorizeButton.onclick = handleAuthClick;
      signoutButton.onclick = handleSignoutClick;
    });
}

/**
 *  Called when the signed in status changes, to update the UI
 *  appropriately. After a sign-in, the API is called.
 */
function updateSigninStatus(isSignedIn) {
  if (isSignedIn) {
    authorizeButton.style.display = "none";
    signoutButton.style.display = "block";
    expenseForm.style.display = "flex";
    formLoader.style.display = "none";
    updateAccounts();
    updateCategories();
  } else {
    authorizeButton.style.display = "block";
    signoutButton.style.display = "none";
    expenseForm.style.display = "none";
    formLoader.style.display = "none";
  }
}

/**
 *  Sign in the user upon button click.
 */
function handleAuthClick(event) {
  gapi.auth2.getAuthInstance().signIn();
}

/**
 *  Sign out the user upon button click.
 */
function handleSignoutClick(event) {
  gapi.auth2.getAuthInstance().signOut();
}

/**
 * Add expense to the sheet
 */
function addExpense(event) {
  if (!expenseForm.checkValidity()) {
    return;
  }

  event.preventDefault();
  formLoader.style.display = "block";
  expenseForm.style.display = "none";

  var epochDay = new Date(1899, 11, 31);
  var expenseDate = new Date(date.value);
  var oneDay = 24 * 60 * 60 * 1000;

  var days = Math.round(
    Math.abs(epochDay.getTime() - expenseDate.getTime()) / oneDay
  );
  var description = desc.value;
  var account = accountSelect.value;
  var category = categorySelect.value;
  var amountVal = amount.value;
  var isIncome = income.checked;

  var request = {
    // The ID of the spreadsheet to update.
    spreadsheetId: SPREADSHEET_ID,

    // The A1 notation of a range to search for a logical table of data.
    // Values will be appended after the last row of the table.
    range: "Expenses!A1",

    includeValuesInResponse: true,

    responseDateTimeRenderOption: "FORMATTED_STRING",

    responseValueRenderOption: "FORMATTED_VALUE",

    // How the input data should be interpreted.
    valueInputOption: "USER_ENTERED",

    // How the input data should be inserted.
    insertDataOption: "INSERT_ROWS",

    resource: {
      values: [
        [
          days,
          description,
          account,
          category,
          isIncome ? 0 : amountVal,
          isIncome ? amountVal : 0
        ]
      ]
    }
  };

  gapi.client.sheets.spreadsheets.values
    .append(request)
    .then(function(response) {
      if (response.status !== 200) {
        console.log(response);
        snackbarContainer.MaterialSnackbar.showSnackbar({
          message: "Sorry something went wrong!"
        });
        return;
      }

      date.value = "";
      desc.value = "";
      accountSelect.value = "";
      categorySelect.value = "";
      amount.value = "";
      income.checked = false;
      formLoader.style.display = "none";
      expenseForm.style.display = "flex";

      snackbarContainer.MaterialSnackbar.showSnackbar({
        message: "Expense added!"
      });
    });
  return false;
}

/**
 * Fetch all accounts from sheet and update the select dropdown
 */
function updateAccounts() {
  gapi.client.sheets.spreadsheets.values
    .get(getRequestObj("Data!A2:A50"))
    .then(function(response) {
      var accounts = "";
      var allValues = response.result.values[0];
      allValues.forEach(function(value) {
        accounts += wrapInOption(value);
      });

      accountSelect.innerHTML = accounts;
    });
}

/**
 * Fetch expense categories from sheet and update the select dropdown
 */
function updateCategories() {
  gapi.client.sheets.spreadsheets.values
    .get(getRequestObj("Data!E2:E50"))
    .then(function(response) {
      var categories = "";
      var allValues = response.result.values[0];
      allValues.forEach(function(value) {
        categories += wrapInOption(value);
      });

      categorySelect.innerHTML = categories;
    });
}

function getRequestObj(range) {
  return {
    spreadsheetId: SPREADSHEET_ID,
    range: range,
    dateTimeRenderOption: "FORMATTED_STRING",
    majorDimension: "COLUMNS",
    valueRenderOption: "FORMATTED_VALUE"
  };
}

function wrapInOption(option) {
  return `<option value='${option}'>${option}</option>`;
}

// register for service worker
if ("serviceWorker" in navigator) {
  window.addEventListener("load", function() {
    navigator.serviceWorker.register("sw.js").then(
      function(registration) {
        // Registration was successful
        console.log(
          "ServiceWorker registration successful with scope: ",
          registration.scope
        );
      },
      function(err) {
        // registration failed :(
        console.log("ServiceWorker registration failed: ", err);
      }
    );
  });
}
<commit_msg>Introduct Google Drive APIs<commit_after>var CLIENT_ID =
  "840179112792-bhg3k1h0dcnp9ltelj21o6vibphjcufe.apps.googleusercontent.com";

// Array of API discovery doc URLs for APIs used
var DISCOVERY_DOCS = [
  "https://sheets.googleapis.com/$discovery/rest?version=v4",
  "https://www.googleapis.com/discovery/v1/apis/drive/v3/rest"
];

// Authorization scopes required by the API; multiple scopes can be
// included, separated by spaces.
var SCOPES =
  "https://www.googleapis.com/auth/spreadsheets https://www.googleapis.com/auth/drive.metadata.readonly";

// expense sheet id
var SPREADSHEET_ID = "";

var authorizeButton = document.getElementById("authorize-button");
var signoutButton = document.getElementById("signout-button");
var expenseForm = document.getElementById("expense-form");

var description = document.getElementById("description");
var date = document.getElementById("date");
var accountSelect = document.getElementById("account");
var categorySelect = document.getElementById("category");
var amount = document.getElementById("amount");
var income = document.getElementById("is-income");
var formLoader = document.getElementById("form-loader");
var snackbarContainer = document.getElementById("toast-container");

/**
 *  On load, called to load the auth2 library and API client library.
 */
function handleClientLoad() {
  gapi.load("client:auth2", initClient);
}

/**
 *  Initializes the API client library and sets up sign-in state
 *  listeners.
 */
function initClient() {
  gapi.client
    .init({
      discoveryDocs: DISCOVERY_DOCS,
      clientId: CLIENT_ID,
      scope: SCOPES
    })
    .then(function() {
      // Listen for sign-in state changes.
      gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus);

      // Handle the initial sign-in state.
      updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
      authorizeButton.onclick = handleAuthClick;
      signoutButton.onclick = handleSignoutClick;
    });
}

/**
 *  Called when the signed in status changes, to update the UI
 *  appropriately. After a sign-in, the API is called.
 */
function updateSigninStatus(isSignedIn) {
  if (isSignedIn) {
    authorizeButton.style.display = "none";
    signoutButton.style.display = "block";
    getSheetID();
  } else {
    authorizeButton.style.display = "block";
    signoutButton.style.display = "none";
    expenseForm.style.display = "none";
    formLoader.style.display = "none";
  }
}

function getSheetID() {
  gapi.client.drive.files
    .list({
      q: "name='Expense Sheet' and mimeType='application/vnd.google-apps.spreadsheet'",
      orderBy: "starred"
    })
    .then(function(response) {
      if (response.result.files.length === 0) {
        snackbarContainer.MaterialSnackbar.showSnackbar({
          message: "Something went wrong!",
          actionHandler: function() {
            window.open(
              "https://github.com/mitul45/expense-manager/blob/master/README.md",
              "_blank"
            );
          },
          actionText: "Details",
          timeout: 5 * 60 * 1000
        });
        return;
      }

      SPREADSHEET_ID = response.result.files[0].id;
      expenseForm.style.display = "flex";
      formLoader.style.display = "none";
      updateSelectFields();
    });
}

/**
 *  Sign in the user upon button click.
 */
function handleAuthClick(event) {
  gapi.auth2.getAuthInstance().signIn();
}

/**
 *  Sign out the user upon button click.
 */
function handleSignoutClick(event) {
  gapi.auth2.getAuthInstance().signOut();
}

/**
 * Add expense to the sheet
 */
function addExpense(event) {
  if (!expenseForm.checkValidity()) {
    return;
  }

  event.preventDefault();
  formLoader.style.display = "block";
  expenseForm.style.display = "none";

  var epochDay = new Date(1899, 11, 31);
  var expenseDate = new Date(date.value);
  var oneDay = 24 * 60 * 60 * 1000;

  var days = Math.round(
    Math.abs(epochDay.getTime() - expenseDate.getTime()) / oneDay
  );
  var description = desc.value;
  var account = accountSelect.value;
  var category = categorySelect.value;
  var amountVal = amount.value;
  var isIncome = income.checked;

  var request = {
    // The ID of the spreadsheet to update.
    spreadsheetId: SPREADSHEET_ID,

    // The A1 notation of a range to search for a logical table of data.
    // Values will be appended after the last row of the table.
    range: "Expenses!A1",

    includeValuesInResponse: true,

    responseDateTimeRenderOption: "FORMATTED_STRING",

    responseValueRenderOption: "FORMATTED_VALUE",

    // How the input data should be interpreted.
    valueInputOption: "USER_ENTERED",

    // How the input data should be inserted.
    insertDataOption: "INSERT_ROWS",

    resource: {
      values: [
        [
          days,
          description,
          account,
          category,
          isIncome ? 0 : amountVal,
          isIncome ? amountVal : 0
        ]
      ]
    }
  };

  gapi.client.sheets.spreadsheets.values
    .append(request)
    .then(function(response) {
      if (response.status !== 200) {
        console.log(response);
        snackbarContainer.MaterialSnackbar.showSnackbar({
          message: "Sorry something went wrong!"
        });
        return;
      }

      date.value = "";
      desc.value = "";
      accountSelect.value = "";
      categorySelect.value = "";
      amount.value = "";
      income.checked = false;
      formLoader.style.display = "none";
      expenseForm.style.display = "flex";

      snackbarContainer.MaterialSnackbar.showSnackbar({
        message: "Expense added!"
      });
    });
  return false;
}

/**
 * Fetch all accounts from sheet and update the select dropdown
 */
function updateSelectFields() {
  gapi.client.sheets.spreadsheets.values
    .batchGet(getRequestObj(["Data!A2:A50", "Data!E2:E50"]))
    .then(function(response) {
      var accounts = "";
      var categories = "";
      var allAccounts = response.result.valueRanges[0].values[0];
      var allCategories = response.result.valueRanges[1].values[0];
      allAccounts.forEach(function(value) {
        accounts += wrapInOption(value);
      });
      allCategories.forEach(function(value) {
        categories += wrapInOption(value);
      });

      accountSelect.innerHTML = accounts;
      categorySelect.innerHTML = categories;
    });
}

function getRequestObj(ranges) {
  return {
    spreadsheetId: SPREADSHEET_ID,
    ranges: ranges,
    dateTimeRenderOption: "FORMATTED_STRING",
    majorDimension: "COLUMNS",
    valueRenderOption: "FORMATTED_VALUE"
  };
}

function wrapInOption(option) {
  return `<option value='${option}'>${option}</option>`;
}

// register for service worker
if ("serviceWorker" in navigator) {
  window.addEventListener("load", function() {
    navigator.serviceWorker.register("sw.js").then(
      function(registration) {
        // Registration was successful
        console.log(
          "ServiceWorker registration successful with scope: ",
          registration.scope
        );
      },
      function(err) {
        // registration failed :(
        console.log("ServiceWorker registration failed: ", err);
      }
    );
  });
}
<|endoftext|>


<commit_before>import _ from 'underscore';
import underscoreDeepExtend from 'underscore-deep-extend';
import {DateConverter, NumberConverter} from '../converters';

const DEFAULT_FORMAT_INFO = {
  dateFormat: 'dd/MM/yyyy',
  numberFormat: '#,##0',
  decimalNumberFormat: '#,##0.00',
  decimalNumberSeparator: '.',
  numberGroupingSeparator: ',',
  numberGroupingSeparatorUse: true
};

/**
 * Performs initialization and handling of intl data.
 * It supports intl data in React Intl format.
 *
 * @author Alexander Frolov
 */
class I18nManager {
  /**
   * Creates and initialize new manager instance.
   * Where locale is current locale and intDatas
   * is an list with messages for different locales.
   * (see React Intl Data format)
   */
  constructor(locale, intlDatas, formatInfos) {
    _.mixin({deepExtend: underscoreDeepExtend(_)});
    this._intlData = {locales:[locale], messages: {}};
    this._intlDatas = [this._intlData];
    this._components = [];
    // current locale
    this.locale = locale;
    if(intlDatas) {
      this.register("default", intlDatas);
    }

    this._formatInfos = formatInfos;
    if (formatInfos) {
      this._formatInfo = formatInfos[locale] || DEFAULT_FORMAT_INFO;
    } else {
      this._formatInfo = DEFAULT_FORMAT_INFO;
    }

    let numberGroupingSeparator = this._formatInfo.numberGroupingSeparatorUse ? this._formatInfo.numberGroupingSeparator : null;
    this._dateConverter = new DateConverter(this._formatInfo.dateFormat);
    this._decimalNumberConverter = new NumberConverter(
      this._formatInfo.decimalNumberFormat,
      numberGroupingSeparator,
      this._formatInfo.decimalNumberSeparator
    );
    this._numberConverter = new NumberConverter(
      this._formatInfo.numberFormat,
      numberGroupingSeparator
    );
  }

  register = (component, intlDatas) => {

    if(this._components.indexOf(component) < 0) {
      // function to check if locales have common elements
      const intersects = (l1, l2) => {
        for (var i = 0; i < l1.length; i++) {
          if(l2.indexOf(l1[i]) >= 0) {
            return true;
          }
        }
      }

      intlDatas.forEach((intlData) => {
        for (var i = 0; i < this._intlDatas.length; i++) {
          if(intersects(this._intlDatas[i].locales, intlData.locales)) {
            this._intlDatas[i] = _.extend(
              {},
              {
                locales:_.union(this._intlDatas[i].locales, intlData.locales)
              },
              {
                messages:_.deepExtend({},this._intlDatas[i].messages, intlData.messages)
              }
            );
          }
          var that = this;
          if(_.size(_.find(this._intlDatas[i].locales,function(locale){
            return locale === that.locale
          })) >= 0 ){
            that._intlData = that._intlDatas[i];
          }
        }
      });
      this._components.push(component);
    }

    return this;
  }

  getMessage = (path, args = {}) => {
    let messages = this._intlData.messages;

    const pathParts = path.split(".");

    let message;

    try {
      message = pathParts.reduce((obj, pathPart) => obj[pathPart], messages);
    } finally {
      if (message === undefined) {
        throw new ReferenceError("Could not find Intl message: " + path);
      }
    }

    for (let param of Object.keys(args)) {
      let paramValue = args[param];

      if (paramValue !== null && paramValue !== undefined) {
        message = message.replace(`{${param}}`, paramValue.toString());
      }
    }

    return message;
  }

  get dateFormat() {
    return this._formatInfo.dateFormat;
  }

  formatDate = (date) => {
    return this._dateConverter.valueToString(date);
  }

  formatDecimalNumber = (number) => {
    return this._decimalNumberConverter.valueToString(number)
  }

  formatNumber = (number) => {
    return this._numberConverter.valueToString(number);
  }

  parseDate = (string) => {
    return this._dateConverter.stringToValue(string);
  }

  parseDecimalNumber = (string) => {
    return this._decimalNumberConverter.stringToValue(string);
  }

  parseNumber = (string) => {
    return this._numberConverter.stringToValue(string);
  }
}

export default I18nManager;
<commit_msg>Fixing problems with IE compatibility.<commit_after>import _ from 'underscore';
import underscoreDeepExtend from 'underscore-deep-extend';
import {DateConverter, NumberConverter} from '../converters';

const DEFAULT_FORMAT_INFO = {
  dateFormat: 'dd/MM/yyyy',
  numberFormat: '#,##0',
  decimalNumberFormat: '#,##0.00',
  decimalNumberSeparator: '.',
  numberGroupingSeparator: ',',
  numberGroupingSeparatorUse: true
};

/**
 * Performs initialization and handling of intl data.
 * It supports intl data in React Intl format.
 *
 * @author Alexander Frolov
 */
class I18nManager {
  /**
   * Creates and initialize new manager instance.
   * Where locale is current locale and intDatas
   * is an list with messages for different locales.
   * (see React Intl Data format)
   */
  constructor(locale, intlDatas, formatInfos) {
    _.mixin({deepExtend: underscoreDeepExtend(_)});
    this._intlData = {locales:[locale], messages: {}};
    this._intlDatas = [this._intlData];
    this._components = [];
    // current locale
    this.locale = locale;
    if(intlDatas) {
      this.register("default", intlDatas);
    }

    this._formatInfos = formatInfos;
    if (formatInfos) {
      this._formatInfo = formatInfos[locale] || DEFAULT_FORMAT_INFO;
    } else {
      this._formatInfo = DEFAULT_FORMAT_INFO;
    }

    let numberGroupingSeparator = this._formatInfo.numberGroupingSeparatorUse ? this._formatInfo.numberGroupingSeparator : null;
    this._dateConverter = new DateConverter(this._formatInfo.dateFormat);
    this._decimalNumberConverter = new NumberConverter(
      this._formatInfo.decimalNumberFormat,
      numberGroupingSeparator,
      this._formatInfo.decimalNumberSeparator
    );
    this._numberConverter = new NumberConverter(
      this._formatInfo.numberFormat,
      numberGroupingSeparator
    );
  }

  register = (component, intlDatas) => {

    if(this._components.indexOf(component) < 0) {
      // function to check if locales have common elements
      const intersects = (l1, l2) => {
        for (var i = 0; i < l1.length; i++) {
          if(l2.indexOf(l1[i]) >= 0) {
            return true;
          }
        }
      }

      intlDatas.forEach((intlData) => {
        for (var i = 0; i < this._intlDatas.length; i++) {
          if(intersects(this._intlDatas[i].locales, intlData.locales)) {
            this._intlDatas[i] = _.extend(
              {},
              {
                locales:_.union(this._intlDatas[i].locales, intlData.locales)
              },
              {
                messages:_.deepExtend({},this._intlDatas[i].messages, intlData.messages)
              }
            );
          }
          var that = this;
          if(_.size(_.find(this._intlDatas[i].locales,function(locale){
            return locale === that.locale
          })) >= 0 ){
            that._intlData = that._intlDatas[i];
          }
        }
      });
      this._components.push(component);
    }

    return this;
  }

  getMessage = (path, args = {}) => {
    let messages = this._intlData.messages;

    const pathParts = path.split(".");

    let message;

    try {
      message = pathParts.reduce((obj, pathPart) => obj[pathPart], messages);
    } finally {
      if (message === undefined) {
        throw new ReferenceError("Could not find Intl message: " + path);
      }
    }
    _.each(_.keys(args), function(param){
      let paramValue = args[param];

      if (paramValue !== null && paramValue !== undefined) {
        message = message.replace(`{${param}}`, paramValue.toString());
      }
    });
    return message;
  }

  get dateFormat() {
    return this._formatInfo.dateFormat;
  }

  formatDate = (date) => {
    return this._dateConverter.valueToString(date);
  }

  formatDecimalNumber = (number) => {
    return this._decimalNumberConverter.valueToString(number)
  }

  formatNumber = (number) => {
    return this._numberConverter.valueToString(number);
  }

  parseDate = (string) => {
    return this._dateConverter.stringToValue(string);
  }

  parseDecimalNumber = (string) => {
    return this._decimalNumberConverter.stringToValue(string);
  }

  parseNumber = (string) => {
    return this._numberConverter.stringToValue(string);
  }
}

export default I18nManager;
<|endoftext|>


<commit_before>$(document).ready(function() {
    var canvas = document.getElementById('waves-canvas');
    var context = canvas.getContext('2d');
    var t_0 = new Date().getTime() / 1000;
    var t = 0;
    var dt = 0.01; // simulation step size in seconds
    var n = 100; // number of samples

    var dragState = {
        isDragging: false,
        x0: 0,
        y0: 0,
        dy: 0
    }
    $(canvas)
        .mousedown(function(evt) {
            dragState.isDragging = true;
            dragState.x0 = evt.offsetX;
            dragState.y0 = evt.offsetY;
        })
        .mousemove(function(evt) {
            if (dragState.isDragging) {
                dragState.dy = evt.offsetY - dragState.y0;
                dragState.y0 = evt.offsetY;
            }
        })
        .on('mouseup mouseout mouseleave', function(evt) {
            dragState.isDragging = false;
        });

    // Individual waves that will be rendered on canvas
    var waves = [
        wave(
            canvas,
            n,
            0.2,
            [
                {
                    amplitude: 0.5,
                    period: 0.5,
                    dampener: 0.97,
                    lengthener: 1.001,
                    crestOffset: 0
                },
                {
                    amplitude: 0.1,
                    period: 0.25,
                    dampener: 0.99,
                    lengthener: 1.0,
                    crestOffset: 0.2
                }
            ]
        ),
        wave(
            canvas,
            n,
            0.15,
            [
                {
                    amplitude: 0.5,
                    period: 0.8,
                    dampener: 0.96,
                    lengthener: 1.002,
                    crestOffset: 0
                },
                {
                    amplitude: 0.1,
                    period: 0.2,
                    dampener: 0.9,
                    lengthener: 1.0,
                    crestOffset: -0.2
                },
                {
                    amplitude: 0.15,
                    period: 0.3,
                    dampener: 0.8,
                    lengthener: 1.1,
                    crestOffset: 0.25
                }
            ]
        ),
        wave(
            canvas,
            n,
            0.25,
            [
                {
                    amplitude: 0.3,
                    period: 0.6,
                    dampener: 0.92,
                    lengthener: 1.0,
                    crestOffset: 0
                },
                {
                    amplitude: 0.04,
                    period: 0.1,
                    dampener: 0.90,
                    lengthener: 1.0,
                    crestOffset: -0.1
                },
                {
                    amplitude: 0.05,
                    period: 0.5,
                    dampener: 0.8,
                    lengthener: 1.1,
                    crestOffset: 0.4
                }
            ]
        )
    ];

    requestAnimationFrame(frame);

    function frame() {
        physics();
        render();
        requestAnimationFrame(frame);
    }

    function physics() {
        var t_now = new Date().getTime() / 1000 - t_0; // time in seconds
        while(t < t_now) {
            waves.forEach(function(waveInstance) {
                waveInstance.physics(dt, dragState);
            });
            t += dt;
        }
    }

    function render() {
        context.fillStyle = 'rgba(153, 170, 255, 0.5)';
        context.clearRect(0, 0, canvas.width, canvas.height);
        var base = 0.5 * canvas.height;
        var unitWidth = canvas.width / (n - 1); // dx per sample in pixels

        // radius of bowl and radius squared
        var r = 0.5 * canvas.width;
        var r2 = r * r;

        waves.forEach(function(w) {
            var heightMap = w.heightMap();

            // Compute the points that make up the wave surface and the bowl angles
            var startAngle = null, endAngle = null;
            var points = []
            for(var i = 0; i < n; i++) {
                var x = i * unitWidth;
                var bowlDepth = Math.sqrt(Math.max(0, r2 - (r-x)*(r-x)));
                var h = heightMap[i];

                if (h < -bowlDepth || h > bowlDepth) {
                    if (x < r) {
                        endAngle = Math.atan2(h, x - r);
                        points = []
                    } else if (startAngle === null) {
                        startAngle = Math.atan2(h, x - r);
                    } else {
                        break;
                    }
                } else {
                    points.push([x, base - h])
                }
            }

            // Draw the path
            context.beginPath();
            context.moveTo(points[0][0], points[0][1]);
            points.forEach(function(point) {
                context.lineTo(point[0], point[1]);
            })
            context.arc(
                r, r, r,
                -startAngle || 0,
                -endAngle || Math.PI
            );
            context.closePath();
            context.fill();
        });
    };
});

/**
 * Defines a sin wave.
 * @typedef {Object} WaveSettings
 * @property {number} amplitude
 * @property {number} period
 * @property {number} dampener - the base in an exponential decay function
 *      that dampenes the amplitude of the wave further from the crest.
 *      Should be [0, 1] to have a dampening effect; otherwise will amplify.
 * @property {number} lengthener - the base in an exponential growth function
 *      that lengthens the period of the wave further from the crest.
 *      Should be > 1. to have a lengthening effect; otherwise will shorten.
 * @property {number} crestOffset - the offset of the peak from the given crest,
 *      used to create a staggered effect for the final wave
 */

/**
 * Stores the state of mouse drag.
 * @typedef {Object} DragState
 * @property {boolean} isDragging
 * @property {number} x0 - the initial mouse x, defines the crest
 * @property {number} y0 - the last mouse y
 * @property {number} dy - the y velocity
 */

/**
 * Defines a single wave with the given settings.
 * @param {HTMLElement} canvas
 * @param {number} n - The number of samples
 * @param {number} waveEquationConstant -  bigger the constant, the
 *      faster the wave propagates
 * @param {Array.<WaveSettings>} waveSettings - the final wave is a
 *      sum of partial waves, defined by these settings
 */
var wave = function(canvas, n, waveEquationConstant, waveSettings) {
    var _canvas = canvas,
        _n = n,
        _c = waveEquationConstant,
        _waves = waveSettings;

    var u = new Float32Array(n); // value of wave at each sample idx
    var u_t = new Float32Array(n); // velocity at each sample idx
    var u_x = new Float32Array(n - 1);
    var u_tt = new Float32Array(n - 2);

    /**
     * Update the height map according to the wave equation.
     * @param {number} dt
     * @param {DragState} dragState
     */
    var _physics = function(dt, dragState) {
        var dx = 1. / n; // change in x per value in u

        // compute dx for each increment of x
        for (var i = 0; i < n - 1; i++) {
            u_x[i] = (u[i + 1] - u[i]) / dx;
        }

        var c2 = _c * _c; // Constant C^2
        for (var i = 0; i < n - 2; i++) {
            // compute second derivative wrt x
            var u_xx_i = (u_x[i + 1] - u_x[i]) / dx;

            // then use it to compute second derivative wrt t
            u_tt[i] = u_xx_i * c2;
        }

        // update u_t according to u_tt
        for (var i = 1; i < n - 1; i++) {
            u_t[i] += u_tt[i - 1] * dt;
            u_t[i] *= 0.999
        }

        // update u
        for (var i = 0; i < n; i++) {
            u[i] += u_t[i] * dt;
        }

        // cheat to send the first and last sample
        u[n - 1] = u[n - 2];
        u[0] = u[1];

        // update u while being dragged
        if (dragState.isDragging) {
            var crestIdx = dragState.x0 * n / canvas.width;
            for(var i = 0; i < n; i++) {
                var offset = 0;
                _waves.forEach(function(settings) {
                    offset += _compute(i, crestIdx, settings);
                });

                // positive dy means wave being pushed down, so
                // subtract artificial wave multiplied by dy
                u[i] -= 0.002 * dragState.dy * offset;
            }
        }
    };

    /**
     * Computes the height of the wave at sample i.
     * @param {number} i - index of the sample to compute the wave height for
     * @param {nubmer} crestIdx - index of the crest of the wave
     * @param {WaveSettings} settings
     */
    var _compute = function(i, crestIdx, settings) {
        var amplitude, period, phaseShift, crestOffset;
        var dampener, lengthener;

        // dampened further from the crest
        dampener = Math.pow(settings.dampener, Math.abs(crestIdx - i))
        amplitude = settings.amplitude * n * dampener;

        // period for big wave spans half the length of the pool, but
        // lengthens further away from the crest
        lengthener = Math.pow(settings.lengthener, Math.abs(crestIdx - i));
        period = settings.period * n * lengthener;

        // crestIdx = phaseShift + .25 * period
        crestOffset = settings.crestOffset * n;
        phaseShift = crestIdx - .25 * period - crestOffset;

        // value of u at i (wave's height)
        return amplitude * Math.sin((2.0 * Math.PI / period) * (i - phaseShift));
    };

    var _heightMap = function() {
        return u;
    };

    return {
        physics: _physics,
        heightMap: _heightMap,
    }
};
<commit_msg>Made things more watery<commit_after>$(document).ready(function() {
    var canvas = document.getElementById('waves-canvas');
    var context = canvas.getContext('2d');
    var t_0 = new Date().getTime() / 1000;
    var t = 0;
    var dt = 0.01; // simulation step size in seconds
    var n = 100; // number of samples

    var dragState = {
        isDragging: false,
        x0: 0,
        y0: 0,
        dy: 0
    }
    $(canvas)
        .mousedown(function(evt) {
            dragState.isDragging = true;
            dragState.x0 = evt.offsetX;
            dragState.y0 = evt.offsetY;
        })
        .mousemove(function(evt) {
            if (dragState.isDragging) {
                dragState.dy = evt.offsetY - dragState.y0;
                dragState.y0 = evt.offsetY;
            }
        })
        .on('mouseup mouseout mouseleave', function(evt) {
            dragState.isDragging = false;
        });

    // Individual waves that will be rendered on canvas
    var waves = [
        wave(
            canvas,
            n,
            [
                {
                    amplitude: 0.5,
                    period: 0.5,
                    dampener: 0.97,
                    lengthener: 1.001,
                    crestOffset: 0
                },
                {
                    amplitude: 0.1,
                    period: 0.25,
                    dampener: 0.99,
                    lengthener: 1.0,
                    crestOffset: 0.2
                }
            ]
        ),
        wave(
            canvas,
            n,
            [
                {
                    amplitude: 0.5,
                    period: 0.8,
                    dampener: 0.96,
                    lengthener: 1.002,
                    crestOffset: 0
                },
                {
                    amplitude: 0.1,
                    period: 0.2,
                    dampener: 0.9,
                    lengthener: 1.0,
                    crestOffset: -0.2
                },
                {
                    amplitude: 0.15,
                    period: 0.3,
                    dampener: 0.8,
                    lengthener: 1.1,
                    crestOffset: 0.25
                }
            ]
        ),
        wave(
            canvas,
            n,
            [
                {
                    amplitude: 0.3,
                    period: 0.6,
                    dampener: 0.92,
                    lengthener: 1.0,
                    crestOffset: 0
                },
                {
                    amplitude: 0.04,
                    period: 0.1,
                    dampener: 0.90,
                    lengthener: 1.0,
                    crestOffset: -0.1
                },
                {
                    amplitude: 0.05,
                    period: 0.5,
                    dampener: 0.8,
                    lengthener: 1.1,
                    crestOffset: 0.4
                }
            ]
        )
    ];

    requestAnimationFrame(frame);

    function frame() {
        physics();
        render();
        requestAnimationFrame(frame);
    }

    function physics() {
        var t_now = new Date().getTime() / 1000 - t_0; // time in seconds
        while(t < t_now) {
            waves.forEach(function(waveInstance) {
                waveInstance.physics(dt, dragState);
            });
            t += dt;
        }
    }

    function render() {
        context.fillStyle = 'rgba(153, 170, 255, 0.5)';
        context.clearRect(0, 0, canvas.width, canvas.height);
        var base = 0.5 * canvas.height;
        var unitWidth = canvas.width / (n - 1); // dx per sample in pixels

        // radius of bowl and radius squared
        var r = 0.5 * canvas.width;
        var r2 = r * r;

        waves.forEach(function(w) {
            var heightMap = w.heightMap();

            // Compute the points that make up the wave surface and the bowl angles
            var startAngle = null, endAngle = null;
            var points = []
            for(var i = 0; i < n; i++) {
                var x = i * unitWidth;
                var bowlDepth = Math.sqrt(Math.max(0, r2 - (r-x)*(r-x)));
                var h = heightMap[i];

                if (h < -bowlDepth || h > bowlDepth) {
                    if (x < r) {
                        endAngle = Math.atan2(h, x - r);
                        points = []
                    } else if (startAngle === null) {
                        startAngle = Math.atan2(h, x - r);
                    } else {
                        break;
                    }
                } else {
                    points.push([x, base - h])
                }
            }

            // Draw the path
            context.beginPath();
            context.moveTo(points[0][0], points[0][1]);
            points.forEach(function(point) {
                context.lineTo(point[0], point[1]);
            })
            context.arc(
                r, r, r,
                -startAngle || 0,
                -endAngle || Math.PI
            );
            context.closePath();
            context.fill();
        });
    };
});

/**
 * Defines a sin wave.
 * @typedef {Object} WaveSettings
 * @property {number} amplitude
 * @property {number} period
 * @property {number} dampener - the base in an exponential decay function
 *      that dampenes the amplitude of the wave further from the crest.
 *      Should be [0, 1] to have a dampening effect; otherwise will amplify.
 * @property {number} lengthener - the base in an exponential growth function
 *      that lengthens the period of the wave further from the crest.
 *      Should be > 1. to have a lengthening effect; otherwise will shorten.
 * @property {number} crestOffset - the offset of the peak from the given crest,
 *      used to create a staggered effect for the final wave
 */

/**
 * Stores the state of mouse drag.
 * @typedef {Object} DragState
 * @property {boolean} isDragging
 * @property {number} x0 - the initial mouse x, defines the crest
 * @property {number} y0 - the last mouse y
 * @property {number} dy - the y velocity
 */

/**
 * Defines a single wave with the given settings.
 * @param {HTMLElement} canvas
 * @param {number} n - The number of samples
 * @param {number} waveEquationConstant -  bigger the constant, the
 *      faster the wave propagates
 * @param {Array.<WaveSettings>} waveSettings - the final wave is a
 *      sum of partial waves, defined by these settings
 */
var wave = function(canvas, n, waveSettings) {
    var _canvas = canvas,
        _n = n,
        _waves = waveSettings;

    var c = 0.35; // wave equation constant
    var damping = 0.995;

    var u = new Float32Array(n); // value of wave at each sample idx
    var u_t = new Float32Array(n); // velocity at each sample idx
    var u_x = new Float32Array(n - 1);
    var u_tt = new Float32Array(n - 2);

    /**
     * Update the height map according to the wave equation.
     * @param {number} dt
     * @param {DragState} dragState
     */
    var _physics = function(dt, dragState) {
        var dx = 1. / n; // change in x per value in u

        // compute dx for each increment of x
        for (var i = 0; i < n - 1; i++) {
            u_x[i] = (u[i + 1] - u[i]) / dx;
        }

        var c2 = c * c; // Constant C^2
        for (var i = 0; i < n - 2; i++) {
            // compute second derivative wrt x
            var u_xx_i = (u_x[i + 1] - u_x[i]) / dx;

            // then use it to compute second derivative wrt t
            u_tt[i] = u_xx_i * c2;
        }

        // update u_t according to u_tt
        for (var i = 1; i < n - 1; i++) {
            u_t[i] += u_tt[i - 1] * dt;
            u_t[i] *= damping;
        }

        // update u
        for (var i = 0; i < n; i++) {
            u[i] += u_t[i] * dt;
        }

        // cheat to send the first and last sample
        u[n - 1] = u[n - 2];
        u[0] = u[1];

        // update u while being dragged
        // TODO: mouse math can be simpler. Needs to keep total water level the same.
        if (dragState.isDragging) {
            var crestIdx = dragState.x0 * n / canvas.width;
            for(var i = 0; i < n; i++) {
                var offset = 0;
                _waves.forEach(function(settings) {
                    offset += _compute(i, crestIdx, settings);
                });

                // positive dy means wave being pushed down, so
                // subtract artificial wave multiplied by dy
                u[i] -= 0.002 * dragState.dy * offset;
            }
        }
    };

    /**
     * Computes the height of the wave at sample i.
     * @param {number} i - index of the sample to compute the wave height for
     * @param {nubmer} crestIdx - index of the crest of the wave
     * @param {WaveSettings} settings
     */
    var _compute = function(i, crestIdx, settings) {
        var amplitude, period, phaseShift, crestOffset;
        var dampener, lengthener;

        // dampened further from the crest
        dampener = Math.pow(settings.dampener, Math.abs(crestIdx - i))
        amplitude = settings.amplitude * n * dampener;

        // period for big wave spans half the length of the pool, but
        // lengthens further away from the crest
        lengthener = Math.pow(settings.lengthener, Math.abs(crestIdx - i));
        period = settings.period * n * lengthener;

        // crestIdx = phaseShift + .25 * period
        crestOffset = settings.crestOffset * n;
        phaseShift = crestIdx - .25 * period - crestOffset;

        // value of u at i (wave's height)
        return amplitude * Math.sin((2.0 * Math.PI / period) * (i - phaseShift));
    };

    var _heightMap = function() {
        return u;
    };

    return {
        physics: _physics,
        heightMap: _heightMap,
    }
};
<|endoftext|>


<commit_before>define([
    'jquery',
    '../utils/constants',
    '../core/SPServices.utils'
], function (
    $,
    constants,
    utils
) {

    "use strict";

    // This function converts an XML node set to JSON
    // Initial implementation focuses only on GetListItems
    $.fn.SPXmlToJson = function (options) {

        var opt = $.extend({}, {
            mapping: {}, // columnName: mappedName: "mappedName", objectType: "objectType"
            includeAllAttrs: false, // If true, return all attributes, regardless whether they are in the mapping
            removeOws: true, // Specifically for GetListItems, if true, the leading ows_ will be stripped off the field name
            sparse: false // If true, empty ("") values will not be returned
        }, options);

        var attrNum;
        var jsonObject = [];

        this.each(function () {
            var row = {};
            var rowAttrs = this.attributes;

            if (!opt.sparse) {
                // Bring back all mapped columns, even those with no value
                $.each(opt.mapping, function () {
                    row[this.mappedName] = "";
                });
            }

            // Parse through the element's attributes
            for (attrNum = 0; attrNum < rowAttrs.length; attrNum++) {
                var thisAttrName = rowAttrs[attrNum].name;
                var thisMapping = opt.mapping[thisAttrName];
                var thisObjectName = thisMapping && thisMapping.mappedName ? thisMapping.mappedName : opt.removeOws ? thisAttrName.split("ows_")[1] : thisAttrName;
                var thisObjectType = thisMapping !== undefined ? thisMapping.objectType : undefined;
                if (opt.includeAllAttrs || thisMapping !== undefined) {
                    row[thisObjectName] = attrToJson(rowAttrs[attrNum].value, thisObjectType);
                }
            }
            // Push this item into the JSON Object
            jsonObject.push(row);

        });

        // Return the JSON object
        return jsonObject;

    }; // End $.fn.SPServices.SPXmlToJson

    function attrToJson(v, objectType) {
        function identity(x) { return x; }

        var result = {

            /* Generic [Reusable] Functions */
            "Integer": intToJsonObject,
            "Number": floatToJsonObject
            "Boolean": booleanToJsonObject,
            "DateTime": dateToJsonObject,
            "User": userToJsonObject,
            "UserMulti": userMultiToJsonObject,
            "Lookup": lookupToJsonObject,
            "lookupMulti": lookupMultiToJsonObject,
            "MultiChoice": choiceMultiToJsonObject,
            "Calculated": calcToJsonObject,
            "Attachments": attachmentsToJsonObject,
            "URL": urlToJsonObject,
            "JSON": jsonToJsonObject, // Special case for text JSON stored in text columns

            /* These objectTypes reuse above functions */
            "Text": result.Default,
            "Counter": result.Integer,
            "datetime": result.DateTime,    // For calculated columns, stored as datetime;#value
            "AllDayEvent": result.Boolean,
            "Recurrence": result.Boolean,
            "Currency": result.Number,
            "float": result.Number, // For calculated columns, stored as float;#value
            "RelatedItems": result.JSON,

            "Default": identity
        };

        return (result[objectType] || identity)(v);

/*
        switch (objectType) {

            case "Text":
                colValue = v;
                break;
            case "DateTime":
            case "datetime": // For calculated columns, stored as datetime;#value
                // Dates have dashes instead of slashes: ows_Created="2009-08-25 14:24:48"
                colValue = dateToJsonObject(v);
                break;
            case "User":
                colValue = userToJsonObject(v);
                break;
            case "UserMulti":
                colValue = userMultiToJsonObject(v);
                break;
            case "Lookup":
                colValue = lookupToJsonObject(v);
                break;

            case "LookupMulti":
                colValue = lookupMultiToJsonObject(v);
                break;
            case "Boolean":
            case "AllDayEvent":
            case "Recurrence":
                colValue = booleanToJsonObject(v);
                break;

            case "Integer":
                colValue = intToJsonObject(v);
                break;

            case "Counter":
                colValue = intToJsonObject(v);
                break;

            case "MultiChoice":
                colValue = choiceMultiToJsonObject(v);
                break;
            case "Number":
            case "Currency":
            case "float": // For calculated columns, stored as float;#value
                colValue = floatToJsonObject(v);
                break;
            case "Calculated":
                colValue = calcToJsonObject(v);
                break;
            case "Attachments":
                colValue = attachmentsToJsonObject(v);
                break;
            case "URL":
                colValue = urlToJsonObject(v);
                break;
            case "JSON":
            case "RelatedItems":
                colValue = jsonToJsonObject(v); // Special case for text JSON stored in text columns
                break;

            default:
                // All other objectTypes will be simple strings
                colValue = v;
                break;
        }
        return colValue;
 */
    }

    function intToJsonObject(s) {
        return parseInt(s, 10);
    }

    function floatToJsonObject(s) {
        return parseFloat(s);
    }

    function booleanToJsonObject(s) {
        return s !== "0";
    }

    function dateToJsonObject(s) {

        var dt = s.split("T")[0] !== s ? s.split("T") : s.split(" ");
        var d = dt[0].split("-");
        var t = dt[1].split(":");
        var t3 = t[2].split("Z");
        return new Date(d[0], (d[1] - 1), d[2], t[0], t[1], t3[0]);
    }

    function userToJsonObject(s) {
        if (s.length === 0) {
            return null;
        } else {
            var thisUser = new utils.SplitIndex(s);
            var thisUserExpanded = thisUser.value.split(",#");
            if (thisUserExpanded.length === 1) {
                return {
                    userId: thisUser.id,
                    userName: thisUser.value
                };
            } else {
                return {
                    userId: thisUser.id,
                    userName: thisUserExpanded[0].replace(/(,,)/g, ","),
                    loginName: thisUserExpanded[1].replace(/(,,)/g, ","),
                    email: thisUserExpanded[2].replace(/(,,)/g, ","),
                    sipAddress: thisUserExpanded[3].replace(/(,,)/g, ","),
                    title: thisUserExpanded[4].replace(/(,,)/g, ",")
                };
            }
        }
    }

    function userMultiToJsonObject(s) {
        if (s.length === 0) {
            return null;
        } else {
            var thisUserMultiObject = [];
            var thisUserMulti = s.split(constants.spDelim);
            for (var i = 0; i < thisUserMulti.length; i = i + 2) {
                var thisUser = userToJsonObject(thisUserMulti[i] + constants.spDelim + thisUserMulti[i + 1]);
                thisUserMultiObject.push(thisUser);
            }
            return thisUserMultiObject;
        }
    }

    function lookupToJsonObject(s) {
        if (s.length === 0) {
            return null;
        } else {
            var thisLookup = s.split(constants.spDelim);
            return {
                lookupId: thisLookup[0],
                lookupValue: thisLookup[1]
            };
        }
    }

    function lookupMultiToJsonObject(s) {
        if (s.length === 0) {
            return null;
        } else {
            var thisLookupMultiObject = [];
            var thisLookupMulti = s.split(constants.spDelim);
            for (var i = 0; i < thisLookupMulti.length; i = i + 2) {
                var thisLookup = lookupToJsonObject(thisLookupMulti[i] + constants.spDelim + thisLookupMulti[i + 1]);
                thisLookupMultiObject.push(thisLookup);
            }
            return thisLookupMultiObject;
        }
    }

    function choiceMultiToJsonObject(s) {
        if (s.length === 0) {
            return null;
        } else {
            var thisChoiceMultiObject = [];
            var thisChoiceMulti = s.split(constants.spDelim);
            for (var i = 0; i < thisChoiceMulti.length; i++) {
                if (thisChoiceMulti[i].length !== 0) {
                    thisChoiceMultiObject.push(thisChoiceMulti[i]);
                }
            }
            return thisChoiceMultiObject;
        }
    }

    function attachmentsToJsonObject(s) {
        if (s.length === 0) {
            return null;
        } else if (s === "0" || s === "1") {
            return s;
        } else {
            var thisObject = [];
            var thisString = s.split(constants.spDelim);
            for (var i = 0; i < thisString.length; i++) {
                if (thisString[i].length !== 0) {
                    var fileName = thisString[i];
                    if (thisString[i].lastIndexOf("/") !== -1) {
                        var tokens = thisString[i].split("/");
                        fileName = tokens[tokens.length - 1];
                    }
                    thisObject.push({
                        attachment: thisString[i],
                        fileName: fileName
                    });
                }
            }
            return thisObject;
        }
    }

    function urlToJsonObject(s) {
        if (s.length === 0) {
            return null;
        } else {
            var thisUrl = s.split(", ");
            return {
                Url: thisUrl[0],
                Description: thisUrl[1]
            };
        }
    }

    function calcToJsonObject(s) {
        if (s.length === 0) {
            return null;
        } else {
            var thisCalc = s.split(constants.spDelim);
            // The first value will be the calculated column value type, the second will be the value
            return attrToJson(thisCalc[1], thisCalc[0]);
        }
    }

    function jsonToJsonObject(s) {
        if (s.length === 0) {
            return null;
        } else {
            return $.parseJSON(s);
        }
    }

    return $;

});<commit_msg>Fixed missing comma<commit_after>define([
    'jquery',
    '../utils/constants',
    '../core/SPServices.utils'
], function (
    $,
    constants,
    utils
) {

    "use strict";

    // This function converts an XML node set to JSON
    // Initial implementation focuses only on GetListItems
    $.fn.SPXmlToJson = function (options) {

        var opt = $.extend({}, {
            mapping: {}, // columnName: mappedName: "mappedName", objectType: "objectType"
            includeAllAttrs: false, // If true, return all attributes, regardless whether they are in the mapping
            removeOws: true, // Specifically for GetListItems, if true, the leading ows_ will be stripped off the field name
            sparse: false // If true, empty ("") values will not be returned
        }, options);

        var attrNum;
        var jsonObject = [];

        this.each(function () {
            var row = {};
            var rowAttrs = this.attributes;

            if (!opt.sparse) {
                // Bring back all mapped columns, even those with no value
                $.each(opt.mapping, function () {
                    row[this.mappedName] = "";
                });
            }

            // Parse through the element's attributes
            for (attrNum = 0; attrNum < rowAttrs.length; attrNum++) {
                var thisAttrName = rowAttrs[attrNum].name;
                var thisMapping = opt.mapping[thisAttrName];
                var thisObjectName = thisMapping && thisMapping.mappedName ? thisMapping.mappedName : opt.removeOws ? thisAttrName.split("ows_")[1] : thisAttrName;
                var thisObjectType = thisMapping !== undefined ? thisMapping.objectType : undefined;
                if (opt.includeAllAttrs || thisMapping !== undefined) {
                    row[thisObjectName] = attrToJson(rowAttrs[attrNum].value, thisObjectType);
                }
            }
            // Push this item into the JSON Object
            jsonObject.push(row);

        });

        // Return the JSON object
        return jsonObject;

    }; // End $.fn.SPServices.SPXmlToJson

    function attrToJson(v, objectType) {
        function identity(x) { return x; }

        var result = {

            /* Generic [Reusable] Functions */
            "Integer": intToJsonObject,
            "Number": floatToJsonObject,
            "Boolean": booleanToJsonObject,
            "DateTime": dateToJsonObject,
            "User": userToJsonObject,
            "UserMulti": userMultiToJsonObject,
            "Lookup": lookupToJsonObject,
            "lookupMulti": lookupMultiToJsonObject,
            "MultiChoice": choiceMultiToJsonObject,
            "Calculated": calcToJsonObject,
            "Attachments": attachmentsToJsonObject,
            "URL": urlToJsonObject,
            "JSON": jsonToJsonObject, // Special case for text JSON stored in text columns

            /* These objectTypes reuse above functions */
            "Text": result.Default,
            "Counter": result.Integer,
            "datetime": result.DateTime,    // For calculated columns, stored as datetime;#value
            "AllDayEvent": result.Boolean,
            "Recurrence": result.Boolean,
            "Currency": result.Number,
            "float": result.Number, // For calculated columns, stored as float;#value
            "RelatedItems": result.JSON,

            "Default": identity
        };

        return (result[objectType] || identity)(v);

/*
        switch (objectType) {

            case "Text":
                colValue = v;
                break;
            case "DateTime":
            case "datetime": // For calculated columns, stored as datetime;#value
                // Dates have dashes instead of slashes: ows_Created="2009-08-25 14:24:48"
                colValue = dateToJsonObject(v);
                break;
            case "User":
                colValue = userToJsonObject(v);
                break;
            case "UserMulti":
                colValue = userMultiToJsonObject(v);
                break;
            case "Lookup":
                colValue = lookupToJsonObject(v);
                break;

            case "LookupMulti":
                colValue = lookupMultiToJsonObject(v);
                break;
            case "Boolean":
            case "AllDayEvent":
            case "Recurrence":
                colValue = booleanToJsonObject(v);
                break;

            case "Integer":
                colValue = intToJsonObject(v);
                break;

            case "Counter":
                colValue = intToJsonObject(v);
                break;

            case "MultiChoice":
                colValue = choiceMultiToJsonObject(v);
                break;
            case "Number":
            case "Currency":
            case "float": // For calculated columns, stored as float;#value
                colValue = floatToJsonObject(v);
                break;
            case "Calculated":
                colValue = calcToJsonObject(v);
                break;
            case "Attachments":
                colValue = attachmentsToJsonObject(v);
                break;
            case "URL":
                colValue = urlToJsonObject(v);
                break;
            case "JSON":
            case "RelatedItems":
                colValue = jsonToJsonObject(v); // Special case for text JSON stored in text columns
                break;

            default:
                // All other objectTypes will be simple strings
                colValue = v;
                break;
        }
        return colValue;
 */
    }

    function intToJsonObject(s) {
        return parseInt(s, 10);
    }

    function floatToJsonObject(s) {
        return parseFloat(s);
    }

    function booleanToJsonObject(s) {
        return s !== "0";
    }

    function dateToJsonObject(s) {

        var dt = s.split("T")[0] !== s ? s.split("T") : s.split(" ");
        var d = dt[0].split("-");
        var t = dt[1].split(":");
        var t3 = t[2].split("Z");
        return new Date(d[0], (d[1] - 1), d[2], t[0], t[1], t3[0]);
    }

    function userToJsonObject(s) {
        if (s.length === 0) {
            return null;
        } else {
            var thisUser = new utils.SplitIndex(s);
            var thisUserExpanded = thisUser.value.split(",#");
            if (thisUserExpanded.length === 1) {
                return {
                    userId: thisUser.id,
                    userName: thisUser.value
                };
            } else {
                return {
                    userId: thisUser.id,
                    userName: thisUserExpanded[0].replace(/(,,)/g, ","),
                    loginName: thisUserExpanded[1].replace(/(,,)/g, ","),
                    email: thisUserExpanded[2].replace(/(,,)/g, ","),
                    sipAddress: thisUserExpanded[3].replace(/(,,)/g, ","),
                    title: thisUserExpanded[4].replace(/(,,)/g, ",")
                };
            }
        }
    }

    function userMultiToJsonObject(s) {
        if (s.length === 0) {
            return null;
        } else {
            var thisUserMultiObject = [];
            var thisUserMulti = s.split(constants.spDelim);
            for (var i = 0; i < thisUserMulti.length; i = i + 2) {
                var thisUser = userToJsonObject(thisUserMulti[i] + constants.spDelim + thisUserMulti[i + 1]);
                thisUserMultiObject.push(thisUser);
            }
            return thisUserMultiObject;
        }
    }

    function lookupToJsonObject(s) {
        if (s.length === 0) {
            return null;
        } else {
            var thisLookup = s.split(constants.spDelim);
            return {
                lookupId: thisLookup[0],
                lookupValue: thisLookup[1]
            };
        }
    }

    function lookupMultiToJsonObject(s) {
        if (s.length === 0) {
            return null;
        } else {
            var thisLookupMultiObject = [];
            var thisLookupMulti = s.split(constants.spDelim);
            for (var i = 0; i < thisLookupMulti.length; i = i + 2) {
                var thisLookup = lookupToJsonObject(thisLookupMulti[i] + constants.spDelim + thisLookupMulti[i + 1]);
                thisLookupMultiObject.push(thisLookup);
            }
            return thisLookupMultiObject;
        }
    }

    function choiceMultiToJsonObject(s) {
        if (s.length === 0) {
            return null;
        } else {
            var thisChoiceMultiObject = [];
            var thisChoiceMulti = s.split(constants.spDelim);
            for (var i = 0; i < thisChoiceMulti.length; i++) {
                if (thisChoiceMulti[i].length !== 0) {
                    thisChoiceMultiObject.push(thisChoiceMulti[i]);
                }
            }
            return thisChoiceMultiObject;
        }
    }

    function attachmentsToJsonObject(s) {
        if (s.length === 0) {
            return null;
        } else if (s === "0" || s === "1") {
            return s;
        } else {
            var thisObject = [];
            var thisString = s.split(constants.spDelim);
            for (var i = 0; i < thisString.length; i++) {
                if (thisString[i].length !== 0) {
                    var fileName = thisString[i];
                    if (thisString[i].lastIndexOf("/") !== -1) {
                        var tokens = thisString[i].split("/");
                        fileName = tokens[tokens.length - 1];
                    }
                    thisObject.push({
                        attachment: thisString[i],
                        fileName: fileName
                    });
                }
            }
            return thisObject;
        }
    }

    function urlToJsonObject(s) {
        if (s.length === 0) {
            return null;
        } else {
            var thisUrl = s.split(", ");
            return {
                Url: thisUrl[0],
                Description: thisUrl[1]
            };
        }
    }

    function calcToJsonObject(s) {
        if (s.length === 0) {
            return null;
        } else {
            var thisCalc = s.split(constants.spDelim);
            // The first value will be the calculated column value type, the second will be the value
            return attrToJson(thisCalc[1], thisCalc[0]);
        }
    }

    function jsonToJsonObject(s) {
        if (s.length === 0) {
            return null;
        } else {
            return $.parseJSON(s);
        }
    }

    return $;

});<|endoftext|>