<commit_before>import Component from '@glimmer/component';
import { action, computed, set } from "@ember/object";
import { tracked } from '@glimmer/tracking';
import { getOwner } from '@ember/application';
import { isArray } from '@ember/array';
import { debug } from '@ember/debug';

class Field {
    @tracked type;
    @tracked name;
    @tracked _refName;
    @tracked value;
    @tracked label;
    @tracked tooltip;
    @tracked prompt;
    @tracked placeholder;
    @tracked clonable;
    @tracked options;
    @tracked keys;
    @tracked is_optional;
    @tracked canDelete;
    @tracked actionOnChange;

    clone() {
        let field = new Field();
        Object.keys(Object.getPrototypeOf(this)).forEach(k => field[k] = this[k]);
        return field;
    }
}

export default class OxisectionFormComponent extends Component {
    @tracked loading = false;
    @tracked fields = [];

    constructor() {
        super(...arguments);
        this.fields = this._prepareFields(this.args.content.content.fields);
    }

    get submitLabel() {
        return this.args.content.content.submit_label || "send";
    }

    _prepareFields(fields) {
        let result = [];
        let clonableRefNames = [];
        for (const fieldHash of fields) {
            // convert hash into field
            let field = new Field();
            for (const attr of Object.keys(fieldHash)) {
                if (! Field.prototype.hasOwnProperty(attr)) {
                    console.error(`oxisection-form: unknown field property "${attr}" (field.name = "${fieldHash.name}")`);
                }
                else {
                    field[attr] = fieldHash[attr];
                }
            }

            // dynamic input fields will change the form field name depending on the
            // selected option, so we need an internal reference to the original name ("_refName")
            field._refName = field.name;
            // set placeholder
            if (typeof field.placeholder === "undefined") {
                field.placeholder = "";
            }

            // standard fields
            if (! field.clonable) {
                result.push(field);
            }
            // clonable field
            else {
                // process presets (array of key/value pairs): insert clones
                // NOTE: this does NOT support dynamic input fields
                if (isArray(field.value)) {
                    let values = field.value.length ? field.value : [""];
                    // add clones to field list
                    result.push(...values.map(v => {
                        let clone = field.clone();
                        // dynamic input fields: presets of clones are key/value hashes.
                        // we need to convert `value: { key: NAME, value: VALUE }` to `name: NAME, value: VALUE`
                        if (v && typeof v === "object") {
                            clone.name = v.key;
                            clone.value = v.value;
                        }
                        // standard fields: presets of clones are plain values
                        else {
                            clone.value = v;
                        }
                        return clone;
                    }));
                }
                if (clonableRefNames.indexOf(field._refName) < 0) {
                    clonableRefNames.push(field._refName);
                }
            }
        }
        for (const name of clonableRefNames) {
            let clones = result.filter(f => f._refName === name);
            for (const clone of clones) { clone.canDelete = true; }
            if (clones.length === 1) {
                clones[0].canDelete = false;
            }
        }

        return result;
    }

    get uniqueFieldNames() {
        let result = [];
        for (const field of this.fields) {
            if (result.indexOf(field.name) < 0) { result.push(field.name) }
        }
        return result;
    }

    get visibleFields() {
        return this.fields.filter(f => f.type !== "hidden");
    }

    @action
    buttonClick(button) {
        this.args.buttonClick(button);
    }

    @action
    addClone(field) {
        let fields = this.fields;
        let index = fields.indexOf(field);
        let fieldCopy = field.clone();
        fieldCopy.value = "";
        fields.insertAt(index + 1, fieldCopy);
    }

    @action
    delClone(field) {
        let fields = this.fields;
        let index = fields.indexOf(field);
        fields.removeAt(index);
    }

    // Turns all fields into request parameters
    _fields2request() {
        let result = [];
        // send clonables as list (even if there's only one field) and other fields as plain values
        for (const name of this.uniqueFieldNames) {
            let potentialClones = this.fields.filter(f => f.name === name);
            result[name] = potentialClones[0].clonable
                ? potentialClones.map(c => c.value)
                : potentialClones[0].value;
        }
        return result;
    }

    /**
     * @param field { hash } - field definition (gets passed in via this components' template, i.e. is a reference to this components' "model")
     */
    @action
    setFieldValue(field, value) {
        debug(`oxisection-form: setFieldValue (${field.name} = "${value}")`);
        field.value = value;

        // action on change?
        if (!field.actionOnChange) { return }

        debug(`oxisection-form: executing actionOnChange ("${field.actionOnChange}")`);
        let request = {
            action: field.actionOnChange,
            _sourceField: field.name,
            ...this._fields2request(),
        };

        let fields = this.fields;

        return getOwner(this).lookup("route:openxpki").sendAjax(request)
        .then((doc) => {
            for (const newField of doc.fields) {
                for (const oldField of fields) {
                    if (oldField.name === newField.name) {
                        let idx = fields.indexOf(oldField);
                        fields[idx] = newField;
                        this.fields = fields; // trigger refresh FIXME remove as Array changes are auto-tracked?! (Ember enhances JS array as https://api.emberjs.com/ember/3.17/classes/Ember.NativeArray)
                    }
                }
            }
            return null;
        });
    }

    /**
     * @param field { hash } - field definition (gets passed in via this components' template, i.e. is a reference to this components' "model")
     */
    @action
    setFieldName(field, name) {
        debug(`oxisection-form: setFieldName (${field.name} -> ${name})`);
        field.name = name;
    }

    /**
     * @param field { hash } - field definition (gets passed in via this components' template, i.e. is a reference to this components' "model")
     */
    @action
    setFieldError(field, message) {
        debug(`oxisection-form: setFieldError (${field.name})`);
        field.error = message;
    }

    @action
    reset() {
        this.args.buttonClick({ page: this.args.content.reset });
    }

    @action
    submit() {
        debug("oxisection-form: submit");

        // check validity and gather form data
        let isError = false;
        for (const field of this.fields) {
            if (!field.is_optional && !field.value) {
                isError = true;
                set(field, "error", "Please specify a value");
            } else {
                if (field.error) {
                    isError = true;
                } else {
                    delete field.error;
                }
            }
        }
        if (isError) { return }

        let data = {
            action: this.args.content.action,
            ...this._fields2request(),
        };

        this.loading = true;
        return getOwner(this).lookup("route:openxpki").sendAjax(data)
        .then((res) => {
            this.loading = false;
            let errors = res.status != null ? res.status.field_errors : null;
            if (errors) {
                for (const error of errors) {
                    let clones = this.fields.filter(f => f.name === error.name);
                    if (typeof error.index === "undefined") {
                        clones.setEach("error", error.error);
                    } else {
                        set(clones[error.index], "error", error.error);
                    }
                }
            }
        })
        .finally(() => this.loading = false);
    }
}
<commit_msg>JS: decorate submit_label as @computed (just in case) in oxisection-form<commit_after>import Component from '@glimmer/component';
import { action, computed, set } from "@ember/object";
import { tracked } from '@glimmer/tracking';
import { getOwner } from '@ember/application';
import { isArray } from '@ember/array';
import { debug } from '@ember/debug';

class Field {
    @tracked type;
    @tracked name;
    @tracked _refName;
    @tracked value;
    @tracked label;
    @tracked tooltip;
    @tracked prompt;
    @tracked placeholder;
    @tracked clonable;
    @tracked options;
    @tracked keys;
    @tracked is_optional;
    @tracked canDelete;
    @tracked actionOnChange;

    clone() {
        let field = new Field();
        Object.keys(Object.getPrototypeOf(this)).forEach(k => field[k] = this[k]);
        return field;
    }
}

export default class OxisectionFormComponent extends Component {
    @tracked loading = false;
    @tracked fields = [];

    constructor() {
        super(...arguments);
        this.fields = this._prepareFields(this.args.content.content.fields);
    }

    @computed("args.content.content.submit_label")
    get submitLabel() {
        return this.args.content.content.submit_label || "send";
    }

    _prepareFields(fields) {
        let result = [];
        let clonableRefNames = [];
        for (const fieldHash of fields) {
            // convert hash into field
            let field = new Field();
            for (const attr of Object.keys(fieldHash)) {
                if (! Field.prototype.hasOwnProperty(attr)) {
                    console.error(`oxisection-form: unknown field property "${attr}" (field.name = "${fieldHash.name}")`);
                }
                else {
                    field[attr] = fieldHash[attr];
                }
            }

            // dynamic input fields will change the form field name depending on the
            // selected option, so we need an internal reference to the original name ("_refName")
            field._refName = field.name;
            // set placeholder
            if (typeof field.placeholder === "undefined") {
                field.placeholder = "";
            }

            // standard fields
            if (! field.clonable) {
                result.push(field);
            }
            // clonable field
            else {
                // process presets (array of key/value pairs): insert clones
                // NOTE: this does NOT support dynamic input fields
                if (isArray(field.value)) {
                    let values = field.value.length ? field.value : [""];
                    // add clones to field list
                    result.push(...values.map(v => {
                        let clone = field.clone();
                        // dynamic input fields: presets of clones are key/value hashes.
                        // we need to convert `value: { key: NAME, value: VALUE }` to `name: NAME, value: VALUE`
                        if (v && typeof v === "object") {
                            clone.name = v.key;
                            clone.value = v.value;
                        }
                        // standard fields: presets of clones are plain values
                        else {
                            clone.value = v;
                        }
                        return clone;
                    }));
                }
                if (clonableRefNames.indexOf(field._refName) < 0) {
                    clonableRefNames.push(field._refName);
                }
            }
        }
        for (const name of clonableRefNames) {
            let clones = result.filter(f => f._refName === name);
            for (const clone of clones) { clone.canDelete = true; }
            if (clones.length === 1) {
                clones[0].canDelete = false;
            }
        }

        return result;
    }

    get uniqueFieldNames() {
        let result = [];
        for (const field of this.fields) {
            if (result.indexOf(field.name) < 0) { result.push(field.name) }
        }
        return result;
    }

    get visibleFields() {
        return this.fields.filter(f => f.type !== "hidden");
    }

    @action
    buttonClick(button) {
        this.args.buttonClick(button);
    }

    @action
    addClone(field) {
        let fields = this.fields;
        let index = fields.indexOf(field);
        let fieldCopy = field.clone();
        fieldCopy.value = "";
        fields.insertAt(index + 1, fieldCopy);
    }

    @action
    delClone(field) {
        let fields = this.fields;
        let index = fields.indexOf(field);
        fields.removeAt(index);
    }

    // Turns all fields into request parameters
    _fields2request() {
        let result = [];
        // send clonables as list (even if there's only one field) and other fields as plain values
        for (const name of this.uniqueFieldNames) {
            let potentialClones = this.fields.filter(f => f.name === name);
            result[name] = potentialClones[0].clonable
                ? potentialClones.map(c => c.value)
                : potentialClones[0].value;
        }
        return result;
    }

    /**
     * @param field { hash } - field definition (gets passed in via this components' template, i.e. is a reference to this components' "model")
     */
    @action
    setFieldValue(field, value) {
        debug(`oxisection-form: setFieldValue (${field.name} = "${value}")`);
        field.value = value;

        // action on change?
        if (!field.actionOnChange) { return }

        debug(`oxisection-form: executing actionOnChange ("${field.actionOnChange}")`);
        let request = {
            action: field.actionOnChange,
            _sourceField: field.name,
            ...this._fields2request(),
        };

        let fields = this.fields;

        return getOwner(this).lookup("route:openxpki").sendAjax(request)
        .then((doc) => {
            for (const newField of doc.fields) {
                for (const oldField of fields) {
                    if (oldField.name === newField.name) {
                        let idx = fields.indexOf(oldField);
                        fields[idx] = newField;
                        this.fields = fields; // trigger refresh FIXME remove as Array changes are auto-tracked?! (Ember enhances JS array as https://api.emberjs.com/ember/3.17/classes/Ember.NativeArray)
                    }
                }
            }
            return null;
        });
    }

    /**
     * @param field { hash } - field definition (gets passed in via this components' template, i.e. is a reference to this components' "model")
     */
    @action
    setFieldName(field, name) {
        debug(`oxisection-form: setFieldName (${field.name} -> ${name})`);
        field.name = name;
    }

    /**
     * @param field { hash } - field definition (gets passed in via this components' template, i.e. is a reference to this components' "model")
     */
    @action
    setFieldError(field, message) {
        debug(`oxisection-form: setFieldError (${field.name})`);
        field.error = message;
    }

    @action
    reset() {
        this.args.buttonClick({ page: this.args.content.reset });
    }

    @action
    submit() {
        debug("oxisection-form: submit");

        // check validity and gather form data
        let isError = false;
        for (const field of this.fields) {
            if (!field.is_optional && !field.value) {
                isError = true;
                set(field, "error", "Please specify a value");
            } else {
                if (field.error) {
                    isError = true;
                } else {
                    delete field.error;
                }
            }
        }
        if (isError) { return }

        let data = {
            action: this.args.content.action,
            ...this._fields2request(),
        };

        this.loading = true;
        return getOwner(this).lookup("route:openxpki").sendAjax(data)
        .then((res) => {
            this.loading = false;
            let errors = res.status != null ? res.status.field_errors : null;
            if (errors) {
                for (const error of errors) {
                    let clones = this.fields.filter(f => f.name === error.name);
                    if (typeof error.index === "undefined") {
                        clones.setEach("error", error.error);
                    } else {
                        set(clones[error.index], "error", error.error);
                    }
                }
            }
        })
        .finally(() => this.loading = false);
    }
}
<|endoftext|>


<commit_before>var m = [20, 20, 20, 20],
    w = 970 - m[1] - m[3],
    h = 600 - m[0] - m[2];

var svg = d3.select("#chart").append("svg:svg")
    .attr("class", "muni")
    .attr("width", w + m[1] + m[3])
    .attr("height", h + m[0] + m[2])
var vis = svg.append("svg:g")
    .attr("transform", "translate(" + m[3] + "," + m[0] + ")");

var lastTime;
var timeScale = d3.time.scale().range([0,790]);
var stopsScale = d3.scale.linear().domain([0.0,1.0]).range([5,540]);
var axis = d3.svg.axis().scale(timeScale).orient("top")
var toggles = d3.select("#toggles");

var static_endpoint = "http://localhost:8081/static/";
var live_endpoint = "http://localhost:8080";

var data = [];
var ends = [];
var guides = [];

var line = d3.svg.line()
  .x(function(d) { return timeScale(d.time*1000) })
  .y(function(d) { return stopsScale(d.index) })
  .interpolate("linear");

toggles.append("input")
  .attr("id","inbound")
  .attr("type", "checkbox")
  .attr("checked",true)
  .on("click", function() {
    d3.selectAll(".inbound").classed("hidden",!this.checked);
  });

toggles.append("label")
  .attr("for", "inbound")
  .text("Inbound");

toggles.append("input")
  .attr("id","outbound")
  .attr("type", "checkbox")
  .on("click", function() {
    d3.selectAll(".outbound").classed("hidden",!this.checked);
  });

toggles.append("label")
  .attr("for", "outbound")
  .text("Outbound");

toggles.append("input")
  .attr("id","showSchedule")
  .attr("type", "checkbox")
  .attr("checked",true)
  .on("click", function() {
    d3.selectAll(".guide").classed("guideHidden",!this.checked);
  });

toggles.append("label")
  .attr("for", "showSchedule")
  .text("Show Schedule");

function vehicleSymbolTransform(d) {
  var x1 = timeScale(d.one.time*1000);
  var y1 = stopsScale(d.one.index);
  var x2 = timeScale(d.two.time*1000);
  var y2 = stopsScale(d.two.index);
  var angle = Math.atan2(-(y2-y1),x2-x1)*180/Math.PI;
  return "translate(" + x2 + "," + y2 + ") rotate(" + -angle + ")";
}

function drawUnanimated() {
  subDraw();
  vis.selectAll(".vehicleSymbol")
    .style("fill", "white")
    .attr("transform", vehicleSymbolTransform);
}

function drawAnimated() {
  subDraw();
  vis.selectAll(".vehicleSymbol")
    .style("fill", "#96ff30")
    .transition()
    .duration(1000)
    .style("fill", "white")
    .attr("transform", vehicleSymbolTransform);
}

var zoom = d3.behavior.zoom()
    .scaleExtent([0.5,2.0])
    .on("zoom", drawUnanimated);

var mainChartWAxis = vis.append("g").attr("transform","translate(70,0)")
mainChartWAxis.append("g")
  .attr("transform", "translate(0,10)")
  .attr("class", "time axis")

var mainChart = mainChartWAxis.append("g").attr("transform","translate(0,20)")

mainChart.append("clipPath")
  .attr("id", "clip")
  .append("rect")
    .attr("width","790")
    .attr("height","550");

mainChart.append("rect")
  .attr("class", "pane")
  .attr("width","790")
  .attr("height","550")
  .call(zoom);

var clipped = mainChart.append("g")
  .attr("clip-path", "url(#clip)")
var clippedBack = clipped.append("g");
var clippedMid = clipped.append("g");
var clippedFore = clipped.append("g");
var tooltip = clippedFore.append("text")
    .attr("class", "vehicleTooltip")

function subDraw () {
  clippedFore.selectAll(".vehiclePath").data(data, function(d) { return d.key }).enter()
    .append("path")
    .attr("id", function(d) { return "vp_" + d.key })
    .attr("class","vehiclePath")
    .classed("inbound", function(d) { return d.run.dir == 0 })
    .classed("outbound", function(d) { return d.run.dir == 1 })

  clippedFore.selectAll(".vehiclePathIx").data(data, function(d) { return d.key }).enter()
    .append("path")
    .attr("class","vehiclePathIx")
    .classed("inbound", function(d) { return d.run.dir == 0 })
    .classed("outbound", function(d) { return d.run.dir == 1 })
    .on("mouseover", function(d) {
      tooltip.attr("x", d3.mouse(this)[0]+10)
        .attr("y", d3.mouse(this)[1])
        .text("Vehicle #" + d.run.vehicle_id)
        .style("fill", "white")
        .style("opacity", "1.0")
        .style("font-size", "10px")

        d3.select("#vp_" + d.key).classed("hovering",true);
    })
    .on("mouseout", function(d) {
      d3.select("#vp_" + d.key).classed("hovering",false);
      tooltip.style("opacity", "0")
    })

  clippedFore.selectAll(".vehicleSymbol").data(ends, function(d) { return d.key }).enter()
    .append("polygon")
    .attr("class", "vehicleSymbol")
    .attr("points", "0,5 10,0 0,-5")
    .classed("inbound", function(d) { return d.dir == 0 })
    .classed("outbound", function(d) { return d.dir == 1 })

  vis.selectAll(".time.axis").call(axis);

  vis.selectAll(".vehiclePath").attr("d", function(d) { return line(d.run.states); })
  vis.selectAll(".vehiclePathIx").attr("d", function(d) { return line(d.run.states); })
  vis.selectAll(".guide").attr("d", function(d) { return line(d.stops) });
}

function endsWithFlattenedData(flattened) {
  var ends = [];
  for (l in flattened) {
    var states = flattened[l].run.states;
    if (states.length == 1) {
      ends.push({"key":flattened[l].key,
                 "two":states[0],
                 "one":states[0],
                 "dir":flattened[l].run.dir});
    } else {
      ends.push({"key":flattened[l].key,
                 "two":states[states.length - 1],
                 "one":states[states.length - 2],
                 "dir":flattened[l].run.dir});
    }
  }
  return ends;
}

function getPastData() {
  d3.json(live_endpoint + "/locations.json", function(response) {

    var flattened = [];
    for(var key in response) {
      flattened.push({"run":response[key],"key":key});
    }

    data = flattened;

    var max = d3.max(flattened, function(run) {
      return d3.max(run.run.states, function(state) { return state.time })
    })

    lastTime = (max + 60 * 15) * 1000;
    timeScale.domain([(lastTime/1000 - 60 * 60) * 1000, lastTime]);
    zoom.x(timeScale);

    ends = endsWithFlattenedData(data);
    drawUnanimated();
    d3.selectAll(".outbound").classed("hidden",!this.checked);
  });
}

function getDataSince(timestamp) {
  d3.json("http://localhost:8080/locations.json?after=" + Math.floor(timestamp/1000), function(response) {
    // delta join.
    for (var run in response) {
      var match = data.filter(function(d) { return d.key == run})
      if (match.length > 0) {
        for (var stat in response[run].states) {
          match[0].run.states.push(response[run].states[stat]);
        }
      } else {
        data.push({"run":response[run].key,"run":response[run]});
      }
    }
    ends = endsWithFlattenedData(data);
    drawAnimated();
  });
}

getPastData();
var timestamp = new Date().getTime();

d3.json("schedule_1.json", function(trips) {
  var min = d3.min(trips, function(trip) {
    return d3.min(trip.stops, function(stop) { return stop.time })
  })

  var midnight = new Date();
  midnight.setHours(0,0,0,0);
  var midnightUnix = midnight.getTime() / 1000;

  // if we're beyond the first departure of the day, do it for the current day
  // otherwise, midnight of yesterday
  if ((new Date()).getTime()/1000 - midnightUnix < min) {
    midnightUnix = midnightUnix - 24 * 60 * 60;
  }

  for (var trip in trips) {
    for (var stop in trips[trip].stops) {
      trips[trip].stops[stop].time = trips[trip].stops[stop].time + midnightUnix;
    }
  }
  clippedBack.selectAll(".guide").data(trips).enter().append("path")
    .attr("class","guide")
    .classed("outbound", function(d) { return d.dir == "0" })
    .classed("inbound", function(d) { return d.dir == "1" });
  d3.selectAll(".outbound").classed("hidden",!this.checked);
});

d3.json(static_endpoint + "/stops/1093.json", function(stops) {
  vis.append("g").attr("transform","translate(64,23)").selectAll(".stop").data(stops).enter().append("text")
      .attr("class", "stop")
      .attr("text-anchor", "end")
      .attr("y", function(d) { return stopsScale(d.index) })
      .text(function(d) { return d.name });

  vis.append("g").attr("transform","translate(866,23)").selectAll(".stop").data(stops).enter().append("text")
      .attr("class", "stop")
      .attr("text-anchor", "begin")
      .attr("y", function(d) { return stopsScale(d.index) })
      .text(function(d) { return d.name });

  clippedMid.append("g").selectAll(".rule").data(stops).enter().append("line")
      .attr("class", "rule")
      .attr("y1", function(d) { return stopsScale(d.index) })
      .attr("y2", function(d) { return stopsScale(d.index) })
      .attr("x1", "0")
      .attr("x2", "790");

  drawUnanimated();
});

<commit_msg>Revert "remove annoying ticker"<commit_after>var m = [20, 20, 20, 20],
    w = 970 - m[1] - m[3],
    h = 600 - m[0] - m[2];

var svg = d3.select("#chart").append("svg:svg")
    .attr("class", "muni")
    .attr("width", w + m[1] + m[3])
    .attr("height", h + m[0] + m[2])
var vis = svg.append("svg:g")
    .attr("transform", "translate(" + m[3] + "," + m[0] + ")");

var lastTime;
var timeScale = d3.time.scale().range([0,790]);
var stopsScale = d3.scale.linear().domain([0.0,1.0]).range([5,540]);
var axis = d3.svg.axis().scale(timeScale).orient("top")
var toggles = d3.select("#toggles");
var ticker = d3.select("#ticker");
var wheel = d3.svg.arc()
      .outerRadius(10)
      .startAngle(function(d) { return d <= 10 ? 2*Math.PI * (1 - 0.1 * d) : 0; })
      .endAngle(function(d) { return d > 10 ? 2*Math.PI * (1 - 0.1 * d) : 0; })
var tickerWheel = d3.select("#tickerSvg").append("path")
  .attr("id","tickerWheel")
  .datum(10)
  .attr("transform", "translate(10,10)")
  .attr("d", wheel);

var static_endpoint = "http://localhost:8081/static/";
var live_endpoint = "http://localhost:8080";

var data = [];
var ends = [];
var guides = [];

var line = d3.svg.line()
  .x(function(d) { return timeScale(d.time*1000) })
  .y(function(d) { return stopsScale(d.index) })
  .interpolate("linear");

toggles.append("input")
  .attr("id","inbound")
  .attr("type", "checkbox")
  .attr("checked",true)
  .on("click", function() {
    d3.selectAll(".inbound").classed("hidden",!this.checked);
  });

toggles.append("label")
  .attr("for", "inbound")
  .text("Inbound");

toggles.append("input")
  .attr("id","outbound")
  .attr("type", "checkbox")
  .on("click", function() {
    d3.selectAll(".outbound").classed("hidden",!this.checked);
  });

toggles.append("label")
  .attr("for", "outbound")
  .text("Outbound");

toggles.append("input")
  .attr("id","showSchedule")
  .attr("type", "checkbox")
  .attr("checked",true)
  .on("click", function() {
    d3.selectAll(".guide").classed("guideHidden",!this.checked);
  });

toggles.append("label")
  .attr("for", "showSchedule")
  .text("Show Schedule");

function vehicleSymbolTransform(d) {
  var x1 = timeScale(d.one.time*1000);
  var y1 = stopsScale(d.one.index);
  var x2 = timeScale(d.two.time*1000);
  var y2 = stopsScale(d.two.index);
  var angle = Math.atan2(-(y2-y1),x2-x1)*180/Math.PI;
  return "translate(" + x2 + "," + y2 + ") rotate(" + -angle + ")";
}

function drawUnanimated() {
  subDraw();
  vis.selectAll(".vehicleSymbol")
    .style("fill", "white")
    .attr("transform", vehicleSymbolTransform);
}

function drawAnimated() {
  subDraw();
  vis.selectAll(".vehicleSymbol")
    .style("fill", "#96ff30")
    .transition()
    .duration(1000)
    .style("fill", "white")
    .attr("transform", vehicleSymbolTransform);
}

var zoom = d3.behavior.zoom()
    .scaleExtent([0.5,2.0])
    .on("zoom", drawUnanimated);

var mainChartWAxis = vis.append("g").attr("transform","translate(70,0)")
mainChartWAxis.append("g")
  .attr("transform", "translate(0,10)")
  .attr("class", "time axis")

var mainChart = mainChartWAxis.append("g").attr("transform","translate(0,20)")

mainChart.append("clipPath")
  .attr("id", "clip")
  .append("rect")
    .attr("width","790")
    .attr("height","550");

mainChart.append("rect")
  .attr("class", "pane")
  .attr("width","790")
  .attr("height","550")
  .call(zoom);

var clipped = mainChart.append("g")
  .attr("clip-path", "url(#clip)")
var clippedBack = clipped.append("g");
var clippedMid = clipped.append("g");
var clippedFore = clipped.append("g");
var tooltip = clippedFore.append("text")
    .attr("class", "vehicleTooltip")

function subDraw () {
  clippedFore.selectAll(".vehiclePath").data(data, function(d) { return d.key }).enter()
    .append("path")
    .attr("id", function(d) { return "vp_" + d.key })
    .attr("class","vehiclePath")
    .classed("inbound", function(d) { return d.run.dir == 0 })
    .classed("outbound", function(d) { return d.run.dir == 1 })

  clippedFore.selectAll(".vehiclePathIx").data(data, function(d) { return d.key }).enter()
    .append("path")
    .attr("class","vehiclePathIx")
    .classed("inbound", function(d) { return d.run.dir == 0 })
    .classed("outbound", function(d) { return d.run.dir == 1 })
    .on("mouseover", function(d) {
      tooltip.attr("x", d3.mouse(this)[0]+10)
        .attr("y", d3.mouse(this)[1])
        .text("Vehicle #" + d.run.vehicle_id)
        .style("fill", "white")
        .style("opacity", "1.0")
        .style("font-size", "10px")

        d3.select("#vp_" + d.key).classed("hovering",true);
    })
    .on("mouseout", function(d) {
      d3.select("#vp_" + d.key).classed("hovering",false);
      tooltip.style("opacity", "0")
    })

  clippedFore.selectAll(".vehicleSymbol").data(ends, function(d) { return d.key }).enter()
    .append("polygon")
    .attr("class", "vehicleSymbol")
    .attr("points", "0,5 10,0 0,-5")
    .classed("inbound", function(d) { return d.dir == 0 })
    .classed("outbound", function(d) { return d.dir == 1 })

  vis.selectAll(".time.axis").call(axis);

  vis.selectAll(".vehiclePath").attr("d", function(d) { return line(d.run.states); })
  vis.selectAll(".vehiclePathIx").attr("d", function(d) { return line(d.run.states); })
  vis.selectAll(".guide").attr("d", function(d) { return line(d.stops) });
}

function endsWithFlattenedData(flattened) {
  var ends = [];
  for (l in flattened) {
    var states = flattened[l].run.states;
    if (states.length == 1) {
      ends.push({"key":flattened[l].key,
                 "two":states[0],
                 "one":states[0],
                 "dir":flattened[l].run.dir});
    } else {
      ends.push({"key":flattened[l].key,
                 "two":states[states.length - 1],
                 "one":states[states.length - 2],
                 "dir":flattened[l].run.dir});
    }
  }
  return ends;
}

function getPastData() {
  d3.json(live_endpoint + "/locations.json", function(response) {

    var flattened = [];
    for(var key in response) {
      flattened.push({"run":response[key],"key":key});
    }

    data = flattened;

    var max = d3.max(flattened, function(run) {
      return d3.max(run.run.states, function(state) { return state.time })
    })

    lastTime = (max + 60 * 15) * 1000;
    timeScale.domain([(lastTime/1000 - 60 * 60) * 1000, lastTime]);
    zoom.x(timeScale);

    ends = endsWithFlattenedData(data);
    drawUnanimated();
    d3.selectAll(".outbound").classed("hidden",!this.checked);
  });
}

function getDataSince(timestamp) {
  d3.json("http://localhost:8080/locations.json?after=" + Math.floor(timestamp/1000), function(response) {
    // delta join.
    for (var run in response) {
      var match = data.filter(function(d) { return d.key == run})
      if (match.length > 0) {
        for (var stat in response[run].states) {
          match[0].run.states.push(response[run].states[stat]);
        }
      } else {
        data.push({"run":response[run].key,"run":response[run]});
      }
    }
    ends = endsWithFlattenedData(data);
    drawAnimated();
  });
}

getPastData();
var timestamp = new Date().getTime();
var timeTilUpdate = 19;

setInterval(function() {
  d3.select("#tickerWheel").datum(timeTilUpdate)
    .attr("d", wheel);
  if (timeTilUpdate % 10 == 0) {
    ticker.text("Updating...");
  } else {
    ticker.text("Updating in " + timeTilUpdate % 10 + "...");
  }

  if (timeTilUpdate % 10 == 0) {
    getDataSince(timestamp);
    timestamp = new Date().getTime();
  }

  if (timeTilUpdate <= 0) {
    timeTilUpdate = 20;
  }
  timeTilUpdate--;
}, 1 * 1000)

d3.json("schedule_1.json", function(trips) {
  var min = d3.min(trips, function(trip) {
    return d3.min(trip.stops, function(stop) { return stop.time })
  })

  var midnight = new Date();
  midnight.setHours(0,0,0,0);
  var midnightUnix = midnight.getTime() / 1000;

  // if we're beyond the first departure of the day, do it for the current day
  // otherwise, midnight of yesterday
  if ((new Date()).getTime()/1000 - midnightUnix < min) {
    midnightUnix = midnightUnix - 24 * 60 * 60;
  }

  for (var trip in trips) {
    for (var stop in trips[trip].stops) {
      trips[trip].stops[stop].time = trips[trip].stops[stop].time + midnightUnix;
    }
  }
  clippedBack.selectAll(".guide").data(trips).enter().append("path")
    .attr("class","guide")
    .classed("outbound", function(d) { return d.dir == "0" })
    .classed("inbound", function(d) { return d.dir == "1" });
  d3.selectAll(".outbound").classed("hidden",!this.checked);
});

d3.json(static_endpoint + "/stops/1093.json", function(stops) {
  vis.append("g").attr("transform","translate(64,23)").selectAll(".stop").data(stops).enter().append("text")
      .attr("class", "stop")
      .attr("text-anchor", "end")
      .attr("y", function(d) { return stopsScale(d.index) })
      .text(function(d) { return d.name });

  vis.append("g").attr("transform","translate(866,23)").selectAll(".stop").data(stops).enter().append("text")
      .attr("class", "stop")
      .attr("text-anchor", "begin")
      .attr("y", function(d) { return stopsScale(d.index) })
      .text(function(d) { return d.name });

  clippedMid.append("g").selectAll(".rule").data(stops).enter().append("line")
      .attr("class", "rule")
      .attr("y1", function(d) { return stopsScale(d.index) })
      .attr("y2", function(d) { return stopsScale(d.index) })
      .attr("x1", "0")
      .attr("x2", "790");

  drawUnanimated();
});

<|endoftext|>


<commit_before>/* global $, FD, verificatum, alert */

$('document').ready(function () {
  'use strict';

  window.debug = function () {
    $('#btn-calculate-local').show();
    $('#btn-calculate-remote').show();
  };

  function reset () {
    FD.resetResults();

    // bring back form to initial state
    $('#form-modexp').trigger('reset');
    $('#input-server-1-1').attr('placeholder', FD.DEFAULT_SERVER);
    $('#input-server-2-1').attr('placeholder', FD.DEFAULT_SERVER);
    $('#input-server-2-2').attr('placeholder', FD.DEFAULT_SERVER);
    $('#div-two-servers').hide();
    $('#div-one-server').show();

    if (typeof (verificatum) !== 'undefined') $('#btn-verificatum').prop('disabled', true);
    else $('#btn-verificatum').prop('disabled', false);
  }

  function addRandomModexps (times, bits, modulus) {
    times = times.length > 0 ? times : 1;
    bits = bits.length > 0 ? bits : 2040;

    for (let i = 0; i < times; i++) {
      FD.appendTo('#input-bases', FD.randString(bits));
      FD.appendTo('#input-exponents', FD.randString(bits));
      FD.appendTo('#input-moduli', modulus);
    }
  }

  function prepareCalculation () {
    if ($('#input-bases').val().length === 0 && $('#input-base-default').val().length === 0) alert('No base and no default base specified!');
    else if ($('#input-exponents').val().length === 0 && $('#input-exponent-default').val().length === 0) alert('No exponent and no default exponent specified!');
    else if ($('#input-moduli').val().length === 0 && $('#input-modulus-default').val().length === 0) alert('No modulus and no default modulus specified!');
    else {
      FD.setAlgorithm($('#select-method').val());
      FD.resetResults();
      FD.showResults();
      return FD.parseFields();
    }
  }

  $('#select-method').change(function () {
    switch ($('#select-method').val()) {
      case 'direct':
        $('#div-two-servers').hide();
        $('#div-one-server').show();
        break;
      case 'dec2':
        $('#div-one-server').hide();
        $('#div-two-servers').show();
        break;
      case 'dec2-checked':
        $('#div-one-server').hide();
        $('#div-two-servers').show();
        break;
    }
  });

  $('#btn-default-p1024').click(function () {
    $('#input-modulus-default').val(FD.P_1024);
  });

  $('#btn-default-p2048').click(function () {
    $('#input-modulus-default').val(FD.P_2048);
  });

  $('#btn-default-p3072').click(function () {
    $('#input-modulus-default').val(FD.P_3072);
  });

  $('#btn-add-p1024').click(function () {
    addRandomModexps($('#input-rand-times').val(), $('#input-rand-bits').val(), FD.P_1024);
  });

  $('#btn-add-p2048').click(function () {
    addRandomModexps($('#input-rand-times').val(), $('#input-rand-bits').val(), FD.P_2048);
  });

  $('#btn-add-p3072').click(function () {
    addRandomModexps($('#input-rand-times').val(), $('#input-rand-bits').val(), FD.P_3072);
  });

  $('#btn-calculate').click(function () {
    let data = prepareCalculation();
    if (data) {
      FD.modexpLocal(data);
      FD.modexpRemote(data);
    }
  });

  $('#btn-calculate-local').click(function () {
    let data = prepareCalculation();
    if (data) FD.modexpLocal(data);
  });

  $('#btn-calculate-remote').click(function () {
    let data = prepareCalculation();
    if (data) FD.modexpRemote(data);
  });

  $('#btn-reset').click(function () {
    reset();
  });

  $('#btn-verificatum').click(function () {
    FD.loadVerificatum();
  });

  reset();
});
<commit_msg>documented, using lambdas<commit_after>/* global $, FD, verificatum, alert */

$('document').ready(function () {
  'use strict';

  /**
   * Function to be called from developer console, adds additional buttons for
   * local and remote only calculation.
   */
  window.debug = function () {
    $('#btn-calculate-local').show();
    $('#btn-calculate-remote').show();
  };

  /**
   * Resets the whole application (form fields and data).
   */
  function reset () {
    FD.resetResults();

    $('#form-modexp').trigger('reset');
    $('#input-server-1-1').attr('placeholder', FD.DEFAULT_SERVER);
    $('#input-server-2-1').attr('placeholder', FD.DEFAULT_SERVER);
    $('#input-server-2-2').attr('placeholder', FD.DEFAULT_SERVER);
    $('#div-two-servers').hide();
    $('#div-one-server').show();

    if (typeof (verificatum) !== 'undefined') $('#btn-verificatum').prop('disabled', true);
    else $('#btn-verificatum').prop('disabled', false);
  }

  /**
   * Adds <times> random modexps with <bits> size and given modulus to the input fields.
   *
   * @param {Number} times      Number of modexps to add
   * @param {Number} bits       Size in bits of modexps to add
   * @param {String} modulus    Modulus to be used
   */
  function addRandomModexps (times, bits, modulus) {
    times = times.length > 0 ? times : 1;
    bits = bits.length > 0 ? bits : 2040;

    for (let i = 0; i < times; i++) {
      FD.appendTo('#input-bases', FD.randString(bits));
      FD.appendTo('#input-exponents', FD.randString(bits));
      FD.appendTo('#input-moduli', modulus);
    }
  }

  /**
   * Checks if parameters for calculation are complete, sets algorithm and resets results.
   */
  function prepareCalculation () {
    if ($('#input-bases').val().length === 0 && $('#input-base-default').val().length === 0) alert('No base and no default base specified!');
    else if ($('#input-exponents').val().length === 0 && $('#input-exponent-default').val().length === 0) alert('No exponent and no default exponent specified!');
    else if ($('#input-moduli').val().length === 0 && $('#input-modulus-default').val().length === 0) alert('No modulus and no default modulus specified!');
    else {
      FD.setAlgorithm($('#select-method').val());
      FD.resetResults();
      FD.showResults();
      return FD.parseFields();
    }
  }

  $('#select-method').change(() => {
    switch ($('#select-method').val()) {
      case 'direct':
        $('#div-two-servers').hide();
        $('#div-one-server').show();
        break;
      case 'dec2':
        $('#div-one-server').hide();
        $('#div-two-servers').show();
        break;
      case 'dec2-checked':
        $('#div-one-server').hide();
        $('#div-two-servers').show();
        break;
    }
  });

  $('#btn-default-p1024').click(() => {
    $('#input-modulus-default').val(FD.P_1024);
  });

  $('#btn-default-p2048').click(() => {
    $('#input-modulus-default').val(FD.P_2048);
  });

  $('#btn-default-p3072').click(() => {
    $('#input-modulus-default').val(FD.P_3072);
  });

  $('#btn-add-p1024').click(() => {
    addRandomModexps($('#input-rand-times').val(), $('#input-rand-bits').val(), FD.P_1024);
  });

  $('#btn-add-p2048').click(() => {
    addRandomModexps($('#input-rand-times').val(), $('#input-rand-bits').val(), FD.P_2048);
  });

  $('#btn-add-p3072').click(() => {
    addRandomModexps($('#input-rand-times').val(), $('#input-rand-bits').val(), FD.P_3072);
  });

  $('#btn-calculate').click(() => {
    let data = prepareCalculation();
    if (data) {
      FD.modexpLocal(data);
      FD.modexpRemote(data);
    }
  });

  $('#btn-calculate-local').click(() => {
    let data = prepareCalculation();
    if (data) FD.modexpLocal(data);
  });

  $('#btn-calculate-remote').click(() => {
    let data = prepareCalculation();
    if (data) FD.modexpRemote(data);
  });

  $('#btn-reset').click(() => {
    reset();
  });

  $('#btn-verificatum').click(() => {
    FD.loadVerificatum();
  });

  reset();
});
<|endoftext|>


<commit_before>import React from "react/addons";
import _ from "underscore";
import $ from "jquery";
import Settings from "../config/settings";
import Row from "./row";
import HCell from "./hcell";
import Cell from "./cell";
import "floatthead";

function countChildren(cell) {
	let ccount = 0, haschildren = false;

	if (cell.children && cell.children.length) {
		haschildren = !!_.find(cell.children, (child) => {
			return child.children && child.children.length;
		});

		if (!haschildren) {
			return cell.children.length;
		} else {
			_.each(cell.children, (child) => {
				if (child.children && child.children.length) {
					ccount += countChildren(child);
				}
			});

			return cell.children.length + ccount;
		}
	}

	return null;
}

export default React.createClass({
	getDefaultProps() {
		return {
			className: '',
			cols: [],
			data: [],
			uniqueId: _.uniqueId('propertable-'),
			afterSort: null,
			fixedHeader: true
		}
	},

	getInitialState() {
		return {
			cols: $.extend(true, this.props.cols, []),
			data: $.extend(true, this.props.data, []),
			sort: null
		};
	},

	componentDidMount() {
		this.fixHeader();

		$(window).on('resize', this.fixHeader);
	},

	componentDidUpdate() {
		this.fixHeader();
	},

	componentWillUnmount() {
		$(window).off('resize', this.fixHeader);
	},

	fixHeader: _.debounce(function() {
		let $container = null, $table = null;
		let parentHeight = null, parentTag;

		if (this.isMounted()) {
			$container = $(React.findDOMNode(this));
			$table = $(React.findDOMNode(this.refs.table));
			$table.floatThead('destroy');
		}

		if (this.isMounted() && this.props.fixedHeader && this.refs.table) {
			parentHeight = $container.parent().height();
			parentTag = $container.parent().prop('tagName');
			if (parentTag === 'BODY') {
				parentHeight = $(document).height();
			}
			$container.height(parentHeight);

			$table.floatThead({
				scrollContainer: function($table) {
					return $container;
				}
			});
		}
	}, 200),

	buildPlainColumns(cols) {
		let rows = [], row = [], crow = [], nextrow = cols, levels = 0;
		let haschildren = false;

		while(nextrow && nextrow.length) {
			levels++;
			row = [];
			crow = nextrow;
			nextrow = [];
			haschildren = !!_.find(crow, (cell) => {
				return cell.children && cell.children.length;
			});

			row = _.map(crow, (cell) => {
				if (cell.children && cell.children.length) {
					_.each(cell.children, (child) => {
						nextrow.push(child);
					});
				}

				return $.extend(true, cell, {
					level: levels,
					colspan: countChildren(cell)
				});
			});

			rows.push(row);
		}

		rows = _.map(rows, (row) => {
			let cells = _.map(row, (cell) => {
				if (!cell.children || !cell.children.length) {
					cell.rowspan = levels - cell.level || null;
				}

				return cell;
			});

			return cells;
		});

		return rows;
	},

	handleSort(direction, col) {
		let field = col.field || null;
		let data = $.extend(true, {}, this.state.data);

		if (field) {
			if (!direction) {
				data = $.extend(true, {}, this.props.data);
				this.setState({
					data: data,
					sort: null
				});
			} else {
				data = _.sortBy(data, (item) => {
					let val = item[field];

					if (col.sortVal && typeof col.sortVal == 'function') {
						val = col.sortVal(val);
					}

					return val;
				});

				if (direction == 'desc') {
					data.reverse();
				}

				this.setState({
					data: data,
					sort: {
						field: field,
						direction: direction
					}
				});
			}
		}
	},

	buildCols(cols) {
		let plain = this.buildPlainColumns(cols), rows = [];
		let rowcount = 1;

		this.fieldsOrder = [];
		this.columnIndex = {};

		rows = _.map(plain, (row) => {
			return _.map(row, (item) => {
				item.sorted = false;

				if (typeof item.field != 'undefined' && item.field) {
					this.fieldsOrder.push(item.field);
					this.columnIndex[item.field] = item;

					if (this.state.sort && item.field == this.state.sort.field) {
						item.sorted = this.state.sort.direction;
					}
				}

				return <HCell onSort={this.handleSort} key={'header' + item.name} {...item}>{item.label}</HCell>;
			});
		});

		return _.map(rows, (row) => {
			return <Row key={'header-row-'+(rowcount++)}>{row}</Row>;
		});
	},

	buildDataRows(data) {
		let result = null, rdata = [], curCell = 1, curRow = 1;
		let defaults = {
			visible: true,
			sortable: true
		};

		result = _.map(data, (rowdata) => {
			let cells = _.map(this.fieldsOrder, (field) => {
				let col = this.columnIndex[field];
				let value = rowdata[field];

				if (typeof col.formatter == 'function') {
					value = col.formatter(value, col);
				}

				return <Cell key={'ccel-'+(curCell++)} className={col.className || ''} col={col}>{value}</Cell>;
			});

			return <Row key={'crow-'+(curRow++)}>{cells}</Row>;
		});

		return result;
	},

	render() {
		let className = this.props.className;
		let cols = [];
		let rows = [];

		if (!this.state.cols.length) {
			return

{Settings.msg('emptymsg')}

;
		}

		cols = this.buildCols(this.state.cols);
		rows = this.buildDataRows(this.state.data);

		return

{cols}

					{rows}

;
	}
});
<commit_msg>adding internal ids to rows in order to keep track of components<commit_after>import React from "react/addons";
import _ from "underscore";
import $ from "jquery";
import Settings from "../config/settings";
import Row from "./row";
import HCell from "./hcell";
import Cell from "./cell";
import "floatthead";

function countChildren(cell) {
	let ccount = 0, haschildren = false;

	if (cell.children && cell.children.length) {
		haschildren = !!_.find(cell.children, (child) => {
			return child.children && child.children.length;
		});

		if (!haschildren) {
			return cell.children.length;
		} else {
			_.each(cell.children, (child) => {
				if (child.children && child.children.length) {
					ccount += countChildren(child);
				}
			});

			return cell.children.length + ccount;
		}
	}

	return null;
}

export default React.createClass({
	getDefaultProps() {
		return {
			className: '',
			cols: [],
			data: [],
			uniqueId: _.uniqueId('propertable-'),
			afterSort: null,
			fixedHeader: true
		}
	},

	getInitialState() {
		return {
			cols: $.extend(true, this.props.cols, []),
			data: null,
			sort: null
		};
	},

	componentDidMount() {
		this.initData();

		$(window).on('resize', this.fixHeader);
	},

	initData() {
		let data = $.extend(true, this.props.data, []);

		this.setState({
			data: _.map(data, (row) => {
				row._properId = _.uniqueId();

				return row;
			})
		});
	},

	componentDidUpdate() {
		this.fixHeader();
	},

	componentWillUnmount() {
		$(window).off('resize', this.fixHeader);
	},

	fixHeader: _.debounce(function() {
		let $container = null, $table = null;
		let parentHeight = null, parentTag;

		if (this.isMounted()) {
			$container = $(React.findDOMNode(this));
			$table = $(React.findDOMNode(this.refs.table));
			$table.floatThead('destroy');
		}

		if (this.isMounted() && this.props.fixedHeader && this.refs.table) {
			parentHeight = $container.parent().height();
			parentTag = $container.parent().prop('tagName');
			if (parentTag === 'BODY') {
				parentHeight = $(document).height();
			}
			$container.height(parentHeight);

			$table.floatThead({
				scrollContainer: function($table) {
					return $container;
				}
			});
		}
	}, 200),

	buildPlainColumns(cols) {
		let rows = [], row = [], crow = [], nextrow = cols, levels = 0;
		let haschildren = false;

		while(nextrow && nextrow.length) {
			levels++;
			row = [];
			crow = nextrow;
			nextrow = [];
			haschildren = !!_.find(crow, (cell) => {
				return cell.children && cell.children.length;
			});

			row = _.map(crow, (cell) => {
				if (cell.children && cell.children.length) {
					_.each(cell.children, (child) => {
						nextrow.push(child);
					});
				}

				return $.extend(true, cell, {
					level: levels,
					colspan: countChildren(cell)
				});
			});

			rows.push(row);
		}

		rows = _.map(rows, (row) => {
			let cells = _.map(row, (cell) => {
				if (!cell.children || !cell.children.length) {
					cell.rowspan = levels - cell.level || null;
				}

				return cell;
			});

			return cells;
		});

		return rows;
	},

	handleSort(direction, col) {
		let field = col.field || null;
		let data = $.extend(true, {}, this.state.data);

		if (field) {
			if (!direction) {
				data = $.extend(true, {}, this.props.data);
				this.setState({
					data: data,
					sort: null
				});
			} else {
				data = _.sortBy(data, (item) => {
					let val = item[field];

					if (col.sortVal && typeof col.sortVal == 'function') {
						val = col.sortVal(val);
					}

					return val;
				});

				if (direction == 'desc') {
					data.reverse();
				}

				this.setState({
					data: data,
					sort: {
						field: field,
						direction: direction
					}
				});
			}
		}
	},

	buildCols(cols) {
		let plain = this.buildPlainColumns(cols), rows = [];
		let rowcount = 1;

		this.fieldsOrder = [];
		this.columnIndex = {};

		rows = _.map(plain, (row) => {
			return _.map(row, (item) => {
				item.sorted = false;

				if (typeof item.field != 'undefined' && item.field) {
					this.fieldsOrder.push(item.field);
					this.columnIndex[item.field] = item;

					if (this.state.sort && item.field == this.state.sort.field) {
						item.sorted = this.state.sort.direction;
					}
				}

				return <HCell onSort={this.handleSort} key={'header' + item.name} {...item}>{item.label}</HCell>;
			});
		});

		return _.map(rows, (row) => {
			return <Row key={'header-row-'+(rowcount++)}>{row}</Row>;
		});
	},

	buildDataRows(data) {
		let result = null, rdata = [], curCell = 1, curRow = 1;
		let defaults = {
			visible: true,
			sortable: true
		};

		result = _.map(data, (rowdata) => {
			let cells = _.map(this.fieldsOrder, (field) => {
				let col = this.columnIndex[field];
				let value = rowdata[field];

				if (typeof col.formatter == 'function') {
					value = col.formatter(value, col);
				}

				return <Cell key={'ccel-'+(curCell++)} className={col.className || ''} col={col}>{value}</Cell>;
			});
			let nextRow = rowdata._properId;

			return <Row key={'crow-'+nextRow} uniqueId={'propertable-row-' + nextRow}>{cells}</Row>;
		});

		return result;
	},

	render() {
		let className = this.props.className;
		let cols = [];
		let rows = [];

		if (!this.state.cols.length) {
			return

{Settings.msg('emptymsg')}

;
		}

		cols = this.buildCols(this.state.cols);
		rows = this.buildDataRows(this.state.data);

		return

{cols}

					{rows}

;
	}
});
<|endoftext|>


<commit_before>define(['text!./filter-report.html', 'app', 'lodash','angular','jquery'], function(template, app, _,angular,$) { 'use strict';

app.directive('filterReport',['$http','Thesaurus','$timeout', function ($http,thesaurus,$timeout) {
    return {
        restrict: 'EAC',
        template: template,
        replace: true,
        require : '^reportingDisplay',
        scope: {
              title: '@title',
              items: '=ngModel',
              facet: '@facet',
              count: '=count' // total count of all children subquires needed for 0 result combinations
        },
          link : function ($scope, $element, $attr, reportingDisplayCtrl)
        {
            $scope.expanded = false;
            var termsMap =[];
            $scope.termsModal = {};

            $scope.terms = [{
              id: '272B0A17-5569-429D-ADF5-2A55C588F7A7',
              title: '5th National Report',
              count:0,
              selected: false
            }, {
              id: 'DA7E04F1-D2EA-491E-9503-F7923B1FD7D4',
              title: '4th National Report',
              count:0,
              selected: false
            }, {
              id: 'A49393CA-2950-4EFD-8BCC-33266D69232F',
              title: '3rd National Report',
              count:0,
              selected: false
            }, {
              id: 'F27DBC9B-FF25-471B-B624-C0F73E76C8B3',
              title: '2nd National Report',
              count:0,
              selected: false
            }, {
              id: '5471756B-6B33-46AD-9D51-15443C5E5315',
              title: '1st National Report',
              count:0,
              selected: false
            }];

            $scope.$watch('items',function(){reportingDisplayCtrl.updateTerms(termsMap,$scope.items,$scope.facet);});

            //=======================================================================
            //
            //=======================================================================
            function buildTermsAndQuery() {

              $scope.terms = searchCtrl.updateTerms($scope.terms,$scope.items,$scope.facet);
              searchCtrl.buildChildQuery($scope.terms,$scope.items,$scope.facet);
            }//buildTermsAndQuery()

            //============================================================
        		//
        		//
        		//============================================================
        		function getAichiTargets() {

          			var  queryParameters = {
          							'q': 'schema_s:nationalReport',
          							'fl': '*',
          							'wt': 'json',
          							'start': 0,
          							'rows': 20,
          			};
          			//return defer;
          			return $http.get('https://api.cbd.int/api/v2013/index/select', { params: queryParameters, cache:true })				.then(function (data) {
                					return {'data':data.data.response.docs};
                });

        		}// getOrganizations

              //=======================================================================
              //
              //=======================================================================
              $scope.loadReports = function (type) {

                if(type)
                    reportingDisplayCtrl.addSubQuery('nationalReport','schema_s:nationalReport AND _latest_s:true AND _state_s:public AND reportType_s:'+type+' ',true);
                else
                    reportingDisplayCtrl.deleteAllSubQuery('nationalReport');
                reportingDisplayCtrl.search();

              };// flatten

        }//link
    }; // return
  }]);  //app.directive('searchFilterCountries
});// define
<commit_msg>Fix mismatch title and actual reports queried<commit_after>define(['text!./filter-report.html', 'app', 'lodash','angular','jquery'], function(template, app, _,angular,$) { 'use strict';

app.directive('filterReport',['$http','Thesaurus','$timeout', function ($http,thesaurus,$timeout) {
    return {
        restrict: 'EAC',
        template: template,
        replace: true,
        require : '^reportingDisplay',
        scope: {
              title: '@title',
              items: '=ngModel',
              facet: '@facet',
              count: '=count' // total count of all children subquires needed for 0 result combinations
        },
          link : function ($scope, $element, $attr, reportingDisplayCtrl)
        {
            $scope.expanded = false;
            var termsMap =[];
            $scope.termsModal = {};

            $scope.terms = [{
              id: 'B3079A36-32A3-41E2-BDE0-65E4E3A51601',
              title: '5th National Report',
              count:0,
              selected: 0
            }, {
              id: '272B0A17-5569-429D-ADF5-2A55C588F7A7',
              title: '4th National Report',
              count:0,
              selected: 0
            }, {
              id: 'DA7E04F1-D2EA-491E-9503-F7923B1FD7D4',
              title: '3rd National Report',
              count:0,
              selected: 0
            }, {
              id: 'A49393CA-2950-4EFD-8BCC-33266D69232F',
              title: '2nd National Report',
              count:0,
              selected: 0
            }, {
              id: 'F27DBC9B-FF25-471B-B624-C0F73E76C8B3',
              title: '1st National Report',
              count:0,
              selected: 0
            }];

            $scope.$watch('items',function(){reportingDisplayCtrl.updateTerms(termsMap,$scope.items,$scope.facet);});

            //=======================================================================
            //
            //=======================================================================
            function buildTermsAndQuery() {

              $scope.terms = searchCtrl.updateTerms($scope.terms,$scope.items,$scope.facet);
              searchCtrl.buildChildQuery($scope.terms,$scope.items,$scope.facet);
            }//buildTermsAndQuery()

            //=======================================================================
            //
            //=======================================================================
            $scope.termsNotSelected = function () {
                  _.each($scope.terms, function (term) {
                      term.selected=0;
                  });
            }//buildTermsAndQuery()

            //============================================================
        		//
        		//
        		//============================================================
        		function getAichiTargets() {

          			var  queryParameters = {
          							'q': 'schema_s:nationalReport',
          							'fl': '*',
          							'wt': 'json',
          							'start': 0,
          							'rows': 20,
          			};
          			//return defer;
          			return $http.get('https://api.cbd.int/api/v2013/index/select', { params: queryParameters, cache:true })				.then(function (data) {
                					return {'data':data.data.response.docs};
                });

        		}// getOrganizations

              //=======================================================================
              //
              //=======================================================================
              $scope.loadReports = function (type) {

                if(type)
                    reportingDisplayCtrl.addSubQuery('nationalReport','schema_s:nationalReport AND _latest_s:true AND _state_s:public AND reportType_s:'+type+' ',true);
                else
                    reportingDisplayCtrl.deleteAllSubQuery('nationalReport');
                reportingDisplayCtrl.search();

              };// flatten

        }//link
    }; // return
  }]);  //app.directive('searchFilterCountries
});// define
<|endoftext|>


<commit_before><commit_msg>Revert "Revert "Remove "Check all Purple Flowers" requirement for Quantum Bee""<commit_after><|endoftext|>


<commit_before>'use strict';

import React from "react";
import PostTable from "./postTable";
import FilterBar from "./filterBar";
import EmptyPostModal from "./emptyPostModal";
import {Button} from "react-bootstrap";

class FilterablePostTable extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            showIgnored: props.showIgnored || false,
            showPosted: props.showPosted || false
        };
        this.handleIgnoredInput = this.handleIgnoredInput.bind(this);
        this.handleIgnored = this.handleIgnored.bind(this);
        this.handlePostedInput = this.handlePostedInput.bind(this);
        this.handlePosted = this.handlePosted.bind(this);
    }

    handleIgnoredInput(showIgnored) {
        this.setState({
            showIgnored: showIgnored
        })
    }

    handlePostedInput(showPosted) {
        this.setState({
            showPosted: showPosted
        })
    }

    handleIgnored(ignored) {
        this.setState({
            ignored: ignored
        })
    }

    handlePosted(posted) {
        this.setState({
            posted: posted
        })
    }

    render() {
        return (

                <FilterBar
                    showIgnored={this.state.showIgnored}
                    showPosted={this.state.showPosted}
                    onIgnoredInputChange={this.handleIgnoredInput}
                    onPostedInputChange={this.handlePostedInput}
                />
                <Button bsStyle="primary" bsSize="small" onClick={() => this.refs.modal.open()}>Create news
                    post</Button>

                <EmptyPostModal ref="modal"/>
                <PostTable
                    key="postTable"
                    showIgnored={this.state.showIgnored}
                    showPosted={this.state.showPosted}
                    onIgnoredRecord={this.handleIgnored}
                    onPostedRecord={this.handlePosted}
                />

        );
    }
}

export default FilterablePostTable;<commit_msg>fixed show posted for empty posts<commit_after>'use strict';

import React from "react";
import PostTable from "./postTable";
import FilterBar from "./filterBar";
import EmptyPostModal from "./emptyPostModal";
import {Button} from "react-bootstrap";

class FilterablePostTable extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            showIgnored: props.showIgnored || false,
            showPosted: props.showPosted || false
        };
        this.handleIgnoredInput = this.handleIgnoredInput.bind(this);
        this.handleIgnored = this.handleIgnored.bind(this);
        this.handlePostedInput = this.handlePostedInput.bind(this);
        this.handlePosted = this.handlePosted.bind(this);
        this.handleEmptyPostModal = this.handleEmptyPostModal.bind(this);
    }

    handleIgnoredInput(showIgnored) {
        this.setState({
            showIgnored: showIgnored
        })
    }

    handlePostedInput(showPosted) {
        this.setState({
            showPosted: showPosted
        })
    }

    handleIgnored(ignored) {
        this.setState({
            ignored: ignored
        })
    }

    handlePosted(posted) {
        this.setState({
            posted: posted
        })
    }

    handleEmptyPostModal() {
        console.log("handleEmptyPostModal");
    }

    render() {
        return (

                <FilterBar
                    showIgnored={this.state.showIgnored}
                    showPosted={this.state.showPosted}
                    onIgnoredInputChange={this.handleIgnoredInput}
                    onPostedInputChange={this.handlePostedInput}
                />
                <Button bsStyle="primary" bsSize="small" onClick={() => this.refs.modal.open()}>Create news
                    post</Button>

                <EmptyPostModal ref="modal" onModalPosted = {this.handleEmptyPostModal}/>
                <PostTable
                    key="postTable"
                    showIgnored={this.state.showIgnored}
                    showPosted={this.state.showPosted}
                    onIgnoredRecord={this.handleIgnored}
                    onPostedRecord={this.handlePosted}
                />

        );
    }
}

export default FilterablePostTable;<|endoftext|>


<commit_before>"use strict";

var request = require("request");
var cheerio = require("cheerio");
var mongoose = require("mongoose");
var async = require("async");
var crypto = require("crypto");

// how many days should we parse?
var daysToParse = 5;

// simple hash-algo to generate 12 byte long objectId
var hashCode = function(s){
    var hash = crypto.createHash('md5').update(s).digest('hex').substring(0, 12);
    return hash;
};

// parsing
var parse = function(html, cb) {
    // create models from our schemas
    var Lecture = require("./models/model_lecture");

    // init cheerio with our html
    var $ = cheerio.load(html);

    // get date for this day
    var curDate = $("h2").text().split(", ")[1];
    var curDateArr = curDate.split(".");
    var intDate = curDateArr[1] + "/" + curDateArr[0] + "/" + curDateArr[2];

    // parse each single lecture
    async.each($("tr").not(":first-child"), function(lectureLine, trcb) {

        // prepare docents
        var docents = [];
        $(lectureLine).children().eq(7).text().trim().split(" , ").forEach(function(docent){
            if(docent !== "") {
                docents.push(docent.replace(/.*Professor/g, "").replace(/ */g, ""));
            }
        });

        // prepare group & room (not really needed but more readable)
        var group = $(lectureLine).children().eq(6).text().trim();
        var room = $(lectureLine).children().eq(5).text().trim();

        // create Lecture from our Model
        var Lec = new Lecture();
        // set attributes
        Lec.lectureName = $(lectureLine).children().eq(3).text().trim();
        Lec.startTime = new Date(intDate + " " + $(lectureLine).children().eq(0).text().trim());
        Lec.endTime = new Date(intDate + " " + $(lectureLine).children().eq(1).text().trim());
        Lec.docents = docents;
        Lec.hashCode = hashCode(Lec.lectureName+curDate+Lec.startTime);
        Lec._id = mongoose.Types.ObjectId(Lec.hashCode);

        // create an object from our document
        var upsertData = Lec.toObject();
        // delete attributes to upsert
        delete upsertData.rooms;
        delete upsertData.groups;

        // lectures without a group/room are useless...
        if(group !== "" && room !== "") {
            // save lecture to db & call callback
            Lecture.update({ _id: Lec.id }, { $set: upsertData, $addToSet: { rooms: room, groups: group }  }, { upsert: true }, trcb);
        } else {
            trcb();
        }
    }, function() {
        // day done
        cb();
    });
};

// create urls including the date which we want to parse
var createUrls = function() {
    var urls = [];
    for(var i = 0; i < daysToParse; i++) {
        // get date to parse
        var today = new Date();
        today.setDate(today.getDate() + i);
        // assemble datestring & url
        var currDay = today.getDate() + "." + (today.getMonth()+1) + "." + today.getFullYear();
        var url = "https://lsf.hft-stuttgart.de/qisserver/rds?state=currentLectures&type=0&next=CurrentLectures.vm&nextdir=ressourcenManager&navigationPosition=lectures&currentLectures&breadcrumb=currentLectures&topitem=lectures&subitem=currentLectures&P.Print=&&HISCalendar_Date=" + currDay + "&asi=";
        urls.push(url);
    }
    return urls;
};

var startParser = function() {
    // connect to mongodb (if not already)
    if(mongoose.connection.readyState === 0) {
        mongoose.connect("mongodb://localhost:27017/wuwNew");
    }

    // create model from our schema (needed for drop)
    var Lecture = require("./models/model_lecture");

    // create the urls to parse
    var urls = createUrls();

    console.log("started parsing of " + daysToParse + " days (this inlcuded)...");

    // drop current lecture collection to get a fresh result
    mongoose.connection.collections.lectures.drop(function(err) {
        if(err) { console.log(err); }

        console.log("dropped old \""+ Lecture.collection.name + "\" collection, lets start clean...");

        // parse every url (max. 5 parallel, dont fuck the lsf)
        async.eachLimit(urls, 5, function(url, cb) {
            request(url, function(error, response, html) {
                if(!error) {
                    // parse html with all lectures for choosen date
                    parse(html, cb);
                }
            });
        }, function() {
            // everything done
            mongoose.disconnect();
            console.log("...done!");
        });
    });
};

// start the magic
startParser();

module.exports = { startParser: startParser, parse: parse };
<commit_msg>use production database in parser<commit_after>"use strict";

var request = require("request");
var cheerio = require("cheerio");
var mongoose = require("mongoose");
var async = require("async");
var crypto = require("crypto");

// how many days should we parse?
var daysToParse = 5;

// mongodb
var mongohost="localhost:27017";
var mongodb=process.env.WUWDB || "wuw";
var mongoConnection="mongodb://" + mongohost + "/" + mongodb;
// connect
mongoose.connect(mongoConnection);

// simple hash-algo to generate 12 byte long objectId
var hashCode = function(s){
    var hash = crypto.createHash('md5').update(s).digest('hex').substring(0, 12);
    return hash;
};

// parsing
var parse = function(html, cb) {
    // create models from our schemas
    var Lecture = require("./models/model_lecture");

    // init cheerio with our html
    var $ = cheerio.load(html);

    // get date for this day
    var curDate = $("h2").text().split(", ")[1];
    var curDateArr = curDate.split(".");
    var intDate = curDateArr[1] + "/" + curDateArr[0] + "/" + curDateArr[2];

    // parse each single lecture
    async.each($("tr").not(":first-child"), function(lectureLine, trcb) {

        // prepare docents
        var docents = [];
        $(lectureLine).children().eq(7).text().trim().split(" , ").forEach(function(docent){
            if(docent !== "") {
                docents.push(docent.replace(/.*Professor/g, "").replace(/ */g, ""));
            }
        });

        // prepare group & room (not really needed but more readable)
        var group = $(lectureLine).children().eq(6).text().trim();
        var room = $(lectureLine).children().eq(5).text().trim();

        // create Lecture from our Model
        var Lec = new Lecture();
        // set attributes
        Lec.lectureName = $(lectureLine).children().eq(3).text().trim();
        Lec.startTime = new Date(intDate + " " + $(lectureLine).children().eq(0).text().trim());
        Lec.endTime = new Date(intDate + " " + $(lectureLine).children().eq(1).text().trim());
        Lec.docents = docents;
        Lec.hashCode = hashCode(Lec.lectureName+curDate+Lec.startTime);
        Lec._id = mongoose.Types.ObjectId(Lec.hashCode);

        // create an object from our document
        var upsertData = Lec.toObject();
        // delete attributes to upsert
        delete upsertData.rooms;
        delete upsertData.groups;

        // lectures without a group/room are useless...
        if(group !== "" && room !== "") {
            // save lecture to db & call callback
            Lecture.update({ _id: Lec.id }, { $set: upsertData, $addToSet: { rooms: room, groups: group }  }, { upsert: true }, trcb);
        } else {
            trcb();
        }
    }, function() {
        // day done
        cb();
    });
};

// create urls including the date which we want to parse
var createUrls = function() {
    var urls = [];
    for(var i = 0; i < daysToParse; i++) {
        // get date to parse
        var today = new Date();
        today.setDate(today.getDate() + i);
        // assemble datestring & url
        var currDay = today.getDate() + "." + (today.getMonth()+1) + "." + today.getFullYear();
        var url = "https://lsf.hft-stuttgart.de/qisserver/rds?state=currentLectures&type=0&next=CurrentLectures.vm&nextdir=ressourcenManager&navigationPosition=lectures&currentLectures&breadcrumb=currentLectures&topitem=lectures&subitem=currentLectures&P.Print=&&HISCalendar_Date=" + currDay + "&asi=";
        urls.push(url);
    }
    return urls;
};

var startParser = function() {
    // connect to mongodb (if not already)
    if(mongoose.connection.readyState === 0) {
        mongoose.connect(mongoConnection);
    }

    // create model from our schema (needed for drop)
    var Lecture = require("./models/model_lecture");

    // create the urls to parse
    var urls = createUrls();

    console.log("started parsing of " + daysToParse + " days (this inlcuded)...");

    // drop current lecture collection to get a fresh result
    mongoose.connection.collections.lectures.drop(function(err) {
        if(err) { console.log(err); }

        console.log("dropped old \""+ Lecture.collection.name + "\" collection, lets start clean...");

        // parse every url (max. 5 parallel, dont fuck the lsf)
        async.eachLimit(urls, 5, function(url, cb) {
            request(url, function(error, response, html) {
                if(!error) {
                    // parse html with all lectures for choosen date
                    parse(html, cb);
                }
            });
        }, function() {
            // everything done
            mongoose.disconnect();
            console.log("...done!");
        });
    });
};

// start the magic
startParser();

module.exports = { startParser: startParser, parse: parse };
<|endoftext|>


<commit_before>'use strict';

/**
 * @ngdoc function
 * @name Teem.controller:HelpCtrl
 * @description
 * # HelpCtrl
 * Controller of the Teem
 */

angular.module('Teem')
  .controller('MenuCtrl', [
  '$scope', 'config', 'url', 'SessionSvc', 'CommunitiesSvc', 'ProjectsSvc',
  'User', '$timeout', 'SharedState',
  function($scope, config, url, SessionSvc, CommunitiesSvc, ProjectsSvc,
           User, $timeout, SharedState){
    if (config.support) {
      $scope.support = {
        communityId: url.urlId(config.support.communityId),
        projectId:   url.urlId(config.support.projectId)
      };
    }

    $scope.register = function () {
      SharedState.set('shouldLoginSharedState', 'register');
    };

    $scope.loggedIn = function () {
      return SessionSvc.users.loggedIn();
    };

    $scope.logout = function () {
      SessionSvc.stopSession();

      SessionSvc.users.on('login', userData);
    };

    // We probably need to refactor this

    function userData () {
      $scope.userNick = User.current().nick;

      CommunitiesSvc.participating().then(function(communities) {
        $timeout(function () {
          $scope.userCommunitiesCount = communities.length;
        });
      });

      ProjectsSvc.contributing().then(function (projects) {
        $timeout(function () {
          $scope.userProjectsCount = projects.length;
        });
      });
    }

    SessionSvc.onLoad(function() {
      if (User.loggedIn()) {
        userData();
      } else {
        SessionSvc.users.on('login', userData);
      }
    });
  }]);
<commit_msg>Update user data after login<commit_after>'use strict';

/**
 * @ngdoc function
 * @name Teem.controller:HelpCtrl
 * @description
 * # HelpCtrl
 * Controller of the Teem
 */

angular.module('Teem')
  .controller('MenuCtrl', [
  '$scope', 'config', 'url', 'SessionSvc', 'CommunitiesSvc', 'ProjectsSvc',
  'User', '$timeout', 'SharedState',
  function($scope, config, url, SessionSvc, CommunitiesSvc, ProjectsSvc,
           User, $timeout, SharedState){
    if (config.support) {
      $scope.support = {
        communityId: url.urlId(config.support.communityId),
        projectId:   url.urlId(config.support.projectId)
      };
    }

    $scope.register = function () {
      SharedState.set('shouldLoginSharedState', 'register');
    };

    $scope.loggedIn = function () {
      return SessionSvc.users.loggedIn();
    };

    $scope.logout = function () {
      SessionSvc.stopSession();

      SessionSvc.users.on('login', userData);
    };

    // We probably need to refactor this

    function userData () {
      $scope.userNick = User.current().nick;

      CommunitiesSvc.participating().then(function(communities) {
        $timeout(function () {
          $scope.userCommunitiesCount = communities.length;
        });
      });

      ProjectsSvc.contributing().then(function (projects) {
        $timeout(function () {
          $scope.userProjectsCount = projects.length;
        });
      });
    }

    SessionSvc.onLoad(function() {
      if (User.loggedIn()) {
        userData();
      }

      $scope.$on('teem.login', userData);
    });
  }]);
<|endoftext|>


<commit_before>
/**  An index all entities.  entity === entities[entity['/rec_ui/id']]
 *   @type {Array.<(!tEntity|undefined)>}
*/
var entities;
/** @type {number}*/
var internalIDCounter;

function resetEntities() {
    entities = [];
    internalIDCounter = 0;
}
resetEntities();

/** @constructor
  * @param {Object=} initialVals
  */
function tEntity(initialVals) {
    this["/rec_ui/id"] = internalIDCounter++
    this["/rec_ui/mql_props"] = [];
    this["/rec_ui/mql_paths"] = []
    this["/rec_ui/headers"] = [];
    this["/rec_ui/headerPaths"] = [];
    entities[this["/rec_ui/id"]] = this;
    for (var key in initialVals)
        this[key] = initialVals[key];
}

tEntity.prototype.setInitialHeaders = function(headers) {
    headers = headers.slice();
    var self = this;
    $.each(headers, function(_,header) {
        self.propSeen(header);
    });
}

/** @param {string} prop
  * @return {Array}
  */
tEntity.prototype.getChainedProperty = function(prop) {
    return getChainedProperty(this,prop);
}

/** @param {loader.path} path
  * @param {boolean=} preservePlace
  * @return {Array}
  */
tEntity.prototype.get = function(path, preservePlace) {
    var slots = [this];
    $.each(path.parts, function(_,part) {
        var newSlots = [];
        $.each(slots, function(_,slot) {
            if (!slot) {
                newSlots.push(undefined);
                return;
            }

            var prop = part.prop;
            if (prop === "id" && ('getIdentifier' in slot)) {
                newSlots.push(slot.getID());
                return;
            }

            var vals = slot[part.prop];
            if (vals === undefined) {
                newSlots.push(undefined);
                return;
            }

            vals = $.makeArray(vals);
            if (part.index !== undefined)
                newSlots = newSlots.concat($.makeArray(vals[part.index]));
            else
                newSlots = newSlots.concat(vals);
        })
        slots = newSlots;
    });
    if (!preservePlace) {
        return Arr.filter(slots, function(v) {
            return v !== undefined;
        });
    }
    return slots;
}

/** @param {string=} linkText */
tEntity.prototype.freebaseLink = function(linkText) {
    linkText = linkText || this.name || this.id;
    return freebase.link(linkText,this.id);
}

tEntity.prototype.displayValue = function() {
    if (!this.id)
        return displayValue(this['/type/object/name'] || this.id);
    return this.freebaseLink();
};

/** @param {!string} id
  * @param {!boolean} automatic
  */
tEntity.prototype.reconcileWith = function(id, automatic) {
    var recGroup = internalReconciler.getRecGroup(this);
    var self = this;
    freebase.getCanonicalID(id, function(new_id) {
        if (recGroup.shouldMerge)
            recGroup.setID(new_id);
        else
            self.id = new_id;
    });
    addReviewItem(recGroup.shouldMerge ? recGroup : this);
    var feedback = {
        query:this['/rec_ui/recon_query'],
        reconciledWith:id,
        automatic:automatic,
        softwareTool: "/guid/9202a8c04000641f800000000df257ed"
    }
    $.getJSON("http://data.labs.freebase.com/recon/feedback", {feedback:JSON.stringify(feedback)}, function(){});
}

/** @param {!string} prop
  * @param {*} value
  */
tEntity.prototype.addProperty = function(prop, value) {
    if (getType(prop) !== "string"){
        warn("called tEntity.property.addProperty with prop of type `" + getType(prop) + "`, expected string.  The value was: " + JSON.stringify(JsObjDump.annotate(value)));
        return;
    }

    if (prop === "id")
        this[prop] = $.makeArray(value)[0];
    else {
        value = Arr.filter($.makeArray(value), function(v) {return v !== undefined;})
        this[prop] = value;
    }

    if (prop === "/type/object/type")
        typesSeen.addAll(this[prop]);
    this.propSeen(prop);
}

/** Keep up with needed metadata about a given property
  * @param {string} prop
  */
tEntity.prototype.propSeen = function(prop) {
    if (!Arr.contains(this['/rec_ui/headers'], prop)) {
        this['/rec_ui/headers'].push(prop);
        this['/rec_ui/headerPaths'].push(new loader.path(prop));
    }
    if (isMqlProp(prop) && !isCVTProperty(prop) && !Arr.contains(this['/rec_ui/mql_props'], prop)) {
        this['/rec_ui/mql_props'].push(prop);
        this['/rec_ui/mql_paths'].push(new loader.path(prop));
    }
}

/** @param {tEntity} parent
  * @param {string=} prop
  */
tEntity.prototype.addParent = function(parent, prop) {
    this['/rec_ui/parent'] = $.makeArray(parent)[0];
    if (prop != undefined)
        this.addProperty(prop, parent);
}

/** @return {boolean} */
tEntity.prototype.isCVT = function() {
    //an entity is a CVT if any of its types are CVT types
    return Arr.any($.makeArray(this['/type/object/type']), isCVTType);
}

/** @return {!string|undefined}*/
tEntity.prototype.getID = function() {
    var recGroup = internalReconciler.getRecGroup(this);
    if (recGroup && recGroup.shouldMerge)
        return recGroup.reconciledTo;
    return this.id;
}

tEntity.prototype.setID = function(id) {
    this.id = id;
}

tEntity.prototype.getIdentifier = function() {
    var id = this.getID();
    if (id !== "None")
        return id;

    var recGroup = internalReconciler.getRecGroup(this);
    if (recGroup && recGroup.shouldMerge)
        return "$recGroup" + recGroup.internal_id
    else
        return "$entity" + this['/rec_ui/id'];
}

/** @return {string} */
tEntity.prototype.toString = function() {
    return textValue(this);
}

/** @return {Object} */
tEntity.prototype.toJSON = function() {
    var js = {};
    for (var key in this) {
        if (startsWith("/rec_ui/",key) || key === "reconResults")
            continue;
        var value = this[key];

        //remove cyclic links
        var parent = this['/rec_ui/parent'];
        if (parent && Arr.filter(value, function(e) {return e !== parent;}).length === 0)
            continue;

        value = toJSON(value);
        if (value !== undefined)
            js[key] = value;
    }
    return js;
}<commit_msg>Change entity.id to entity.getID() a couple places<commit_after>
/**  An index all entities.  entity === entities[entity['/rec_ui/id']]
 *   @type {Array.<(!tEntity|undefined)>}
*/
var entities;
/** @type {number}*/
var internalIDCounter;

function resetEntities() {
    entities = [];
    internalIDCounter = 0;
}
resetEntities();

/** @constructor
  * @param {Object=} initialVals
  */
function tEntity(initialVals) {
    this["/rec_ui/id"] = internalIDCounter++
    this["/rec_ui/mql_props"] = [];
    this["/rec_ui/mql_paths"] = []
    this["/rec_ui/headers"] = [];
    this["/rec_ui/headerPaths"] = [];
    entities[this["/rec_ui/id"]] = this;
    for (var key in initialVals)
        this[key] = initialVals[key];
}

tEntity.prototype.setInitialHeaders = function(headers) {
    headers = headers.slice();
    var self = this;
    $.each(headers, function(_,header) {
        self.propSeen(header);
    });
}

/** @param {string} prop
  * @return {Array}
  */
tEntity.prototype.getChainedProperty = function(prop) {
    return getChainedProperty(this,prop);
}

/** @param {loader.path} path
  * @param {boolean=} preservePlace
  * @return {Array}
  */
tEntity.prototype.get = function(path, preservePlace) {
    var slots = [this];
    $.each(path.parts, function(_,part) {
        var newSlots = [];
        $.each(slots, function(_,slot) {
            if (!slot) {
                newSlots.push(undefined);
                return;
            }

            var prop = part.prop;
            if (prop === "id" && ('getIdentifier' in slot)) {
                newSlots.push(slot.getID());
                return;
            }

            var vals = slot[part.prop];
            if (vals === undefined) {
                newSlots.push(undefined);
                return;
            }

            vals = $.makeArray(vals);
            if (part.index !== undefined)
                newSlots = newSlots.concat($.makeArray(vals[part.index]));
            else
                newSlots = newSlots.concat(vals);
        })
        slots = newSlots;
    });
    if (!preservePlace) {
        return Arr.filter(slots, function(v) {
            return v !== undefined;
        });
    }
    return slots;
}

/** @param {string=} linkText */
tEntity.prototype.freebaseLink = function(linkText) {
    linkText = linkText || this.name || this.getID();
    return freebase.link(linkText,this.getID());
}

tEntity.prototype.displayValue = function() {
    if (!this.id)
        return displayValue(this['/type/object/name'] || this.getID());
    return this.freebaseLink();
};

/** @param {!string} id
  * @param {!boolean} automatic
  */
tEntity.prototype.reconcileWith = function(id, automatic) {
    var recGroup = internalReconciler.getRecGroup(this);
    var self = this;
    freebase.getCanonicalID(id, function(new_id) {
        if (recGroup.shouldMerge)
            recGroup.setID(new_id);
        else
            self.id = new_id;
    });
    addReviewItem(recGroup.shouldMerge ? recGroup : this);
    var feedback = {
        query:this['/rec_ui/recon_query'],
        reconciledWith:id,
        automatic:automatic,
        softwareTool: "/guid/9202a8c04000641f800000000df257ed"
    }
    $.getJSON("http://data.labs.freebase.com/recon/feedback", {feedback:JSON.stringify(feedback)}, function(){});
}

/** @param {!string} prop
  * @param {*} value
  */
tEntity.prototype.addProperty = function(prop, value) {
    if (getType(prop) !== "string"){
        warn("called tEntity.property.addProperty with prop of type `" + getType(prop) + "`, expected string.  The value was: " + JSON.stringify(JsObjDump.annotate(value)));
        return;
    }

    if (prop === "id")
        this[prop] = $.makeArray(value)[0];
    else {
        value = Arr.filter($.makeArray(value), function(v) {return v !== undefined;})
        this[prop] = value;
    }

    if (prop === "/type/object/type")
        typesSeen.addAll(this[prop]);
    this.propSeen(prop);
}

/** Keep up with needed metadata about a given property
  * @param {string} prop
  */
tEntity.prototype.propSeen = function(prop) {
    if (!Arr.contains(this['/rec_ui/headers'], prop)) {
        this['/rec_ui/headers'].push(prop);
        this['/rec_ui/headerPaths'].push(new loader.path(prop));
    }
    if (isMqlProp(prop) && !isCVTProperty(prop) && !Arr.contains(this['/rec_ui/mql_props'], prop)) {
        this['/rec_ui/mql_props'].push(prop);
        this['/rec_ui/mql_paths'].push(new loader.path(prop));
    }
}

/** @param {tEntity} parent
  * @param {string=} prop
  */
tEntity.prototype.addParent = function(parent, prop) {
    this['/rec_ui/parent'] = $.makeArray(parent)[0];
    if (prop != undefined)
        this.addProperty(prop, parent);
}

/** @return {boolean} */
tEntity.prototype.isCVT = function() {
    //an entity is a CVT if any of its types are CVT types
    return Arr.any($.makeArray(this['/type/object/type']), isCVTType);
}

/** @return {!string|undefined}*/
tEntity.prototype.getID = function() {
    var recGroup = internalReconciler.getRecGroup(this);
    if (recGroup && recGroup.shouldMerge)
        return recGroup.reconciledTo;
    return this.id;
}

tEntity.prototype.setID = function(id) {
    this.id = id;
}

tEntity.prototype.getIdentifier = function() {
    var id = this.getID();
    if (id !== "None")
        return id;

    var recGroup = internalReconciler.getRecGroup(this);
    if (recGroup && recGroup.shouldMerge)
        return "$recGroup" + recGroup.internal_id
    else
        return "$entity" + this['/rec_ui/id'];
}

/** @return {string} */
tEntity.prototype.toString = function() {
    return textValue(this);
}

/** @return {Object} */
tEntity.prototype.toJSON = function() {
    var js = {};
    for (var key in this) {
        if (startsWith("/rec_ui/",key) || key === "reconResults")
            continue;
        var value = this[key];

        //remove cyclic links
        var parent = this['/rec_ui/parent'];
        if (parent && Arr.filter(value, function(e) {return e !== parent;}).length === 0)
            continue;

        value = toJSON(value);
        if (value !== undefined)
            js[key] = value;
    }
    return js;
}<|endoftext|>


<commit_before>'use strict';
// Generated on <%= (new Date).toISOString().split('T')[0] %> using <%= pkg.name %> <%= pkg.version %>

var gulp = require('gulp');
var open = require('open');
var wiredep = require('wiredep').stream;

// Load plugins
var $ = require('gulp-load-plugins')();

<% if (includeSass) { %>
// Styles
gulp.task('styles', function () {
    return gulp.src('app/styles/main.scss')
        .pipe($.rubySass({
          style: 'expanded',
          loadPath: ['app/bower_components']
        }))
        .pipe($.autoprefixer('last 1 version'))
        .pipe(gulp.dest('app/styles'))
        .pipe($.size());
});
<% } else { %>
// Styles
gulp.task('styles', function () {
    return gulp.src('app/styles/main.css')
        .pipe($.autoprefixer('last 1 version'))
        .pipe(gulp.dest('app/styles'))
        .pipe($.size());
});
<% } %>
// Scripts
gulp.task('scripts', function () {
    return gulp.src('app/scripts/**/*.js')
        .pipe($.jshint('.jshintrc'))
        .pipe($.jshint.reporter('default'))
        .pipe($.size());
});

// HTML
gulp.task('html', ['styles', 'scripts'], function () {
    var jsFilter = $.filter('**/*.js');
    var cssFilter = $.filter('**/*.css');

    return gulp.src('app/*.html')
        .pipe($.useref.assets())
        .pipe(jsFilter)
        .pipe($.uglify())
        .pipe(jsFilter.restore())
        .pipe(cssFilter)
        .pipe($.csso())
        .pipe(cssFilter.restore())
        .pipe($.useref.restore())
        .pipe($.useref())
        .pipe(gulp.dest('dist'))
        .pipe($.size());
});

// Images
gulp.task('images', function () {
    return gulp.src('app/images/**/*')
        .pipe($.cache($.imagemin({
            optimizationLevel: 3,
            progressive: true,
            interlaced: true
        })))
        .pipe(gulp.dest('dist/images'))
        .pipe($.size());
});

gulp.task('fonts', function () {
    return $.bowerFiles()
        .pipe($.filter([
            '**/*.eot',
            '**/*.svg',
            '**/*.ttf',
            '**/*.woff'
        ]))
        .pipe($.flatten())
        .pipe(gulp.dest('dist/fonts'))
        .pipe($.size());
});

// Clean
gulp.task('clean', function () {
    return gulp.src(['dist/styles', 'dist/scripts', 'dist/images', 'dist/fonts'], { read: false }).pipe($.clean());
});

// Build
gulp.task('build', ['html', 'images', 'fonts']);

// Default task
gulp.task('default', ['clean'], function () {
    gulp.start('build');
});

// Connect
gulp.task('connect', function () {
  $.connect.server({
    root: ['app'],
    port: 9000,
    livereload: true
  });
});

// Open
gulp.task('serve', ['connect'<% if (includeSass) { %>, 'styles'<% } %>], function() {
  open("http://localhost:9000");
});

// Inject Bower components
gulp.task('wiredep', function () {
    gulp.src('app/styles/*.<% if (includeSass) { %>scss<% } else { %>css<% } %>')
        .pipe(wiredep({
            directory: 'app/bower_components',
            ignorePath: 'app/bower_components/'
        }))
        .pipe(gulp.dest('app/styles'));

    gulp.src('app/*.html')
        .pipe(wiredep({
            directory: 'app/bower_components',
            ignorePath: 'app/'
        }))
        .pipe(gulp.dest('app'));
});

// Watch
gulp.task('watch', ['connect', 'serve'], function () {
    // Watch for changes in `app` folder
    gulp.watch([
        'app/*.html',<% if (includeSass) { %>
        'app/styles/**/*.scss',<% } else { %>
        'app/styles/**/*.css',<% } %>
        'app/scripts/**/*.js',
        'app/images/**/*'
    ], function (event) {
        return gulp.src(event.path)
            .pipe($.connect.reload());
    });

    <% if (includeSass) { %>// Watch .scss files
    gulp.watch('app/styles/**/*.scss', ['styles']);

    <% } else { %>// Watch .css files
    gulp.watch('app/styles/**/*.css', ['styles']);

    <% }  %>// Watch .js files
    gulp.watch('app/scripts/**/*.js', ['scripts']);

    // Watch image files
    gulp.watch('app/images/**/*', ['images']);

    // Watch bower files
    gulp.watch('bower.json', ['wiredep']);
});
<commit_msg>Fix indenting and quotes in gulpfile.js.<commit_after>'use strict';
// Generated on <%= (new Date).toISOString().split('T')[0] %> using <%= pkg.name %> <%= pkg.version %>

var gulp = require('gulp');
var open = require('open');
var wiredep = require('wiredep').stream;

// Load plugins
var $ = require('gulp-load-plugins')();

<% if (includeSass) { %>
// Styles
gulp.task('styles', function () {
    return gulp.src('app/styles/main.scss')
        .pipe($.rubySass({
            style: 'expanded',
            loadPath: ['app/bower_components']
        }))
        .pipe($.autoprefixer('last 1 version'))
        .pipe(gulp.dest('app/styles'))
        .pipe($.size());
});
<% } else { %>
// Styles
gulp.task('styles', function () {
    return gulp.src('app/styles/main.css')
        .pipe($.autoprefixer('last 1 version'))
        .pipe(gulp.dest('app/styles'))
        .pipe($.size());
});
<% } %>
// Scripts
gulp.task('scripts', function () {
    return gulp.src('app/scripts/**/*.js')
        .pipe($.jshint('.jshintrc'))
        .pipe($.jshint.reporter('default'))
        .pipe($.size());
});

// HTML
gulp.task('html', ['styles', 'scripts'], function () {
    var jsFilter = $.filter('**/*.js');
    var cssFilter = $.filter('**/*.css');

    return gulp.src('app/*.html')
        .pipe($.useref.assets())
        .pipe(jsFilter)
        .pipe($.uglify())
        .pipe(jsFilter.restore())
        .pipe(cssFilter)
        .pipe($.csso())
        .pipe(cssFilter.restore())
        .pipe($.useref.restore())
        .pipe($.useref())
        .pipe(gulp.dest('dist'))
        .pipe($.size());
});

// Images
gulp.task('images', function () {
    return gulp.src('app/images/**/*')
        .pipe($.cache($.imagemin({
            optimizationLevel: 3,
            progressive: true,
            interlaced: true
        })))
        .pipe(gulp.dest('dist/images'))
        .pipe($.size());
});

gulp.task('fonts', function () {
    return $.bowerFiles()
        .pipe($.filter([
            '**/*.eot',
            '**/*.svg',
            '**/*.ttf',
            '**/*.woff'
        ]))
        .pipe($.flatten())
        .pipe(gulp.dest('dist/fonts'))
        .pipe($.size());
});

// Clean
gulp.task('clean', function () {
    return gulp.src(['dist/styles', 'dist/scripts', 'dist/images', 'dist/fonts'], { read: false }).pipe($.clean());
});

// Build
gulp.task('build', ['html', 'images', 'fonts']);

// Default task
gulp.task('default', ['clean'], function () {
    gulp.start('build');
});

// Connect
gulp.task('connect', function () {
    $.connect.server({
        root: ['app'],
        port: 9000,
        livereload: true
    });
});

// Open
gulp.task('serve', ['connect'<% if (includeSass) { %>, 'styles'<% } %>], function() {
    open('http://localhost:9000');
});

// Inject Bower components
gulp.task('wiredep', function () {
    gulp.src('app/styles/*.<% if (includeSass) { %>scss<% } else { %>css<% } %>')
        .pipe(wiredep({
            directory: 'app/bower_components',
            ignorePath: 'app/bower_components/'
        }))
        .pipe(gulp.dest('app/styles'));

    gulp.src('app/*.html')
        .pipe(wiredep({
            directory: 'app/bower_components',
            ignorePath: 'app/'
        }))
        .pipe(gulp.dest('app'));
});

// Watch
gulp.task('watch', ['connect', 'serve'], function () {
    // Watch for changes in `app` folder
    gulp.watch([
        'app/*.html',<% if (includeSass) { %>
        'app/styles/**/*.scss',<% } else { %>
        'app/styles/**/*.css',<% } %>
        'app/scripts/**/*.js',
        'app/images/**/*'
    ], function (event) {
        return gulp.src(event.path)
            .pipe($.connect.reload());
    });

    <% if (includeSass) { %>// Watch .scss files
    gulp.watch('app/styles/**/*.scss', ['styles']);

    <% } else { %>// Watch .css files
    gulp.watch('app/styles/**/*.css', ['styles']);

    <% }  %>// Watch .js files
    gulp.watch('app/scripts/**/*.js', ['scripts']);

    // Watch image files
    gulp.watch('app/images/**/*', ['images']);

    // Watch bower files
    gulp.watch('bower.json', ['wiredep']);
});
<|endoftext|>