PageSourceSearch

https://geodata.ehri-project.eu/geonetwork/catalog/lib/timeline/ti…?v=45f1bcb4ec72f17d5990ffa55469b66a79dcbfc4

js ehri-project.eu collected 2026-10-02 03:13:20 UTC 11,952 bytes, 401 lines download raw bytes

1/**
2 *
3 * @param {HTMLElement} element graph container
4 * @param {Function} callback this callback will be called when values are
5 * changed, ie the graph is zoomed or panned using mouse or buttons
6 * Arguments are the updated `from` and `to` values in DD-MM-YYYY format
7 * @param {Object} options
8 * @param {bool} options.showAsHistogram
9 */
10function TimeLine(element, callback, options) {
11  var me = this;
12
13  var timelineSelection = null;
14  var timelineScale = 1;
15  var timelineWidth;
16  var timelineHeight;
17  var timelineX;
18  var timelineY;
19  var timelineArea;
20  var timelineXAxis;
21  var timelineXTranslate = 0;
22  var timelineXScale = 1;
23  var lastQuery = null;
24  var maxDomain;
25  var maxValue = 0;
26
27  this.initialized = false;
28  this.graphMaxData = null;
29  this.graphData = null;
30  this.model = {};
31  this.callback = callback;
32  this.options = options || {};
33
34  var zoom;
35  var margin = {
36    top: 0,
37    right: 5,
38    bottom: 10,
39    left: 0
40  };
41
42  this.initialize = function() {
43
44    // prevent multi initialization
45    if (this.initialized) { return; }
46
47    var container = d3.select(element);
48    timelineWidth = container.node().offsetWidth - margin.left - margin.right;
49    timelineHeight = container.node().offsetHeight - margin.top - margin.bottom - /* scroll */ 15;
50
51    // check that we have enough space to initialize; otherwise postpone it
52    // (the user will have to call recomputeSize)
53    if (timelineHeight <= 0) {
54      return;
55    }
56
57    // Compute X axis
58    var current_first_time = Number.MAX_VALUE;
59    var timeExtent = d3.extent(this.graphMaxData, function(d) {
60      var begin = d.begin;
61      var end = d.end;
62      if (begin < current_first_time) {
63        current_first_time = begin;
64        return begin;
65      } else {
66        return end;
67      }
68    });
69
70    timelineX = d3.time.scale()
71      .range([0, timelineWidth * timelineScale])
72      .domain(timeExtent);
73    maxDomain = timeExtent;
74
75    var timeFormat = d3.time.format.multi([
76      [".%L", function(d) {
77        return d.getMilliseconds();
78      }],
79      [":%S", function(d) {
80        return d.getSeconds();
81      }],
82      ["%I:%M", function(d) {
83        return d.getMinutes();
84      }],
85      ["%I %p", function(d) {
86        return d.getHours();
87      }],
88      ["%a %d", function(d) {
89        return d.getDay() && d.getDate() != 1;
90      }],
91      ["%b %d", function(d) {
92        return d.getDate() != 1;
93      }],
94      ["%b", function(d) {
95        return d.getMonth();
96      }],
97      ["%Y", function() {
98        return true;
99      }]
100    ]);
101
102    timelineXAxis = d3.svg.axis()
103      .scale(timelineX)
104      .orient('bottom')
105      .ticks(6)
106      .tickPadding(8)
107      .tickFormat(timeFormat);
108
109
110    // Compute Y axis
111    var valueExtent = d3.extent(this.graphMaxData, function(d) {
112      return d.value;
113    });
114
115    timelineY = d3.scale.linear()
116      .range([timelineHeight, 0])
117      .domain([0, valueExtent[1]]);
118
119    var changeRequest;
120    zoom = d3.behavior.zoom().x(timelineX)
121      .y(timelineY)
122      .scaleExtent([0.5, timelineWidth])
123      .on("zoom", timelineZoom)
124      .on('zoomstart', function() {
125        clearTimeout(changeRequest);
126      })
127      .on('zoomend', function() {
128        clearTimeout(changeRequest);
129        timelineSelection = timelineX.domain();
130        changeRequest = setTimeout(function() {
131          var timeQuery = null;
132          if (timelineSelection != null && timelineSelection.length > 0) {
133            timeQuery = (+timelineSelection[0]) + "," + (+timelineSelection[1]);
134          }
135          if (lastQuery != timeQuery) {
136            lastQuery = timeQuery;
137            me.model.from = moment(timelineSelection[0]).format('DD-MM-YYYY');
138            me.model.to = moment(timelineSelection[1]).format('DD-MM-YYYY');
139            me.callback(me.model.from, me.model.to);
140          }
141        }, 500);
142
143      });
144    timelineSvg = container.append('svg')
145      .attr('width', timelineWidth * timelineScale + margin.left + margin.right)
146      .attr('height', timelineHeight + margin.top + margin.bottom)
147      .call(zoom);
148
149    var root = timelineSvg.append('g')
150      .attr('class', 'context')
151      .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
152    root.append('g').attr('class', 'all-data-root');
153    root.append('g').attr('class', 'data-root');
154
155    timelineArea = d3.svg.area()
156      .x(function(d) {
157        return timelineX(d.begin);
158      })
159      .y0(timelineHeight)
160      .y1(function(d) {
161        return timelineY(d.value);
162      });
163
164    timelineSelection = timelineX.domain();
165
166    refreshGraphData();
167
168    timelineSvg
169      .append('g')
170      .attr('class', 'x timeline')
171      .attr('transform', 'scale(1)')
172      .selectAll('rect')
173      .attr('y',0)
174      .attr('height', timelineHeight);
175
176    timelineSvg
177      .append('g')
178      .attr('class', 'x axis')
179      .attr('transform', 'translate(0,' + (timelineHeight) + ')')
180      .call(timelineXAxis);
181
182    initAppControls();
183
184    this.initialized = true;
185  };
186
187  this.recomputeSize = function () {
188    // if we have never been initialized: do it now
189    if (!this.initialized) {
190      this.initialize(this.graphData, this.callback);
191    }
192  };
193
194  function initAppControls() {
195    d3.select(element).selectAll('.zoomBtn').on('click', function(event) {
196      var scale = zoom.scale(),
197        extent = zoom.scaleExtent(),
198        translate = zoom.translate(),
199        x = translate[0],
200        y = translate[1],
201        factor = (this.getAttribute('rel') === 'zoomIn') ? 1.5 : 1 / 1.5,
202        target_scale = scale * factor;
203
204      // If we're already at an extent, done
205      if (target_scale === extent[0] || target_scale === extent[1]) {
206        return false;
207      }
208
209      // If the factor is too much, scale it down to reach the extent exactly
210      var clamped_target_scale = Math.max(extent[0], Math.min(extent[1], target_scale));
211      if (clamped_target_scale != target_scale) {
212        target_scale = clamped_target_scale;
213        factor = target_scale / scale;
214      }
215
216      // Center each vector, stretch, then put back
217      center = [timelineWidth / 2, timelineHeight / 2];
218      x = (x - center[0]) * factor + center[0];
219      y = (y - center[1]) * factor + center[1];
220
221      // Transition to the new view over 350ms
222      d3.transition().duration(350).tween("zoom", function() {
223        var interpolate_scale = d3.interpolate(scale, target_scale),
224          interpolate_trans = d3.interpolate(translate, [x, y]);
225        timelineXTranslate = translate[0];
226        timelineXScale = target_scale;
227
228        return function(t) {
229          zoom.scale(interpolate_scale(t))
230            .translate(interpolate_trans(t));
231          timelineZoom();
232          timelineSvg.call(zoom.event);
233        };
234      });
235    });
236  }
237
238  function timelineZoom() {
239    // prevent event propagation if possible
240    if (d3.event && d3.event.sourceEvent) {
241        d3.event.sourceEvent.preventDefault();
242        d3.event.sourceEvent.stopPropagation();
243    }
244
245    // get transformation
246    if (d3.event !== null) {
247      timelineXTranslate = d3.event.translate[0];
248      timelineXScale = d3.event.scale;
249      applyZoom();
250
251    } else {
252      var container = d3.select(element);
253
254      // calculate the zone
255      refreshGraphData();
256      container.select('svg').select(".x.axis").call(timelineXAxis);
257    }
258  }
259
260  function applyZoom() {
261    // calculate zone
262    refreshGraphData();
263    setZoom(timelineXTranslate, timelineXScale);
264  }
265
266  function setZoom(translate, scale) {
267    var container = d3.select(element);
268    var context = container.select('svg').select('g');
269    container.select('svg').select(".x.axis").call(timelineXAxis);
270    if (!me.options.showAsHistogram) {
271      context.select(".all-data-root").attr("transform", "translate(" + translate + ",0)scale(" + scale + ", 1)");
272    }
273  }
274
275  // will be used to filter out invisible blocks
276  function isVisible(d) {
277    var begin = timelineX(d.begin);
278    var end = timelineX(d.end);
279    return end >= 0 && begin <= timelineWidth;
280  }
281
282  function refreshGraphMaxData() {
283    var container = d3.select(element);
284    var context = container.select('svg').select('g.all-data-root');
285
286    if (me.options.showAsHistogram) {
287      var all = context.selectAll('rect.areaAll')
288        .data(me.graphMaxData, function(d) { return d.begin; });
289      all.enter()
290        .append('rect').attr('class', 'areaAll');
291      all.style('display', 'none')
292        .filter(isVisible).style('display', 'block')
293        .attr("x", function(d) { return timelineX(d.begin); })
294        .attr("width", function(d) {
295          return Math.max(
296            // min width is 3px
297            timelineX(d.end) - timelineX(d.begin),
298            3 / timelineScale
299          );
300        })
301        .attr("y", function(d) { return timelineY(d.value); })
302        .attr("height", function(d) { return timelineHeight - timelineY(d.value); });
303      all.exit()
304        .remove();
305    } else {
306      context
307        .append("path")
308        .datum(me.graphMaxData)
309        .attr("class", "areaAll")
310        .attr("d", timelineArea);
311    }
312  }
313
314  function refreshGraphData() {
315    var container = d3.select(element);
316    var context = container.select('svg').select('g.data-root');
317
318    var valueExtent = d3.extent(me.graphData, function(d) {
319      return d.value;
320    });
321    if (valueExtent[1] > maxValue) {
322      me.graphMaxData = me.graphData;
323      maxValue = valueExtent[1];
324      timelineY = d3.scale.linear()
325        .range([timelineHeight, 0])
326        .domain([0, maxValue]);
327    }
328    refreshGraphMaxData();
329
330    if (me.options.showAsHistogram) {
331      var all = context.selectAll('rect.area')
332        .data(me.graphData, function(d) { return d.begin; });
333      all.enter()
334        .append('rect').attr('class', 'area');
335      all.style('display', 'none')
336        .filter(isVisible).style('display', 'block')
337        .attr("x", function(d) { return timelineX(d.begin); })
338        .attr("width", function(d) {
339          // min width is 3px
340          return Math.max(
341            timelineX(d.end) - timelineX(d.begin),
342            3 / timelineScale
343          );
344        })
345        .attr("y", function(d) { return timelineY(d.value); })
346        .attr("height", function(d) { return timelineHeight - timelineY(d.value); });
347      all.exit()
348        .remove();
349    } else {
350      context.selectAll('path.area').remove();
351      context
352        .append("path")
353        .datum(me.graphData)
354        .attr("class", "area")
355        .attr("d", timelineArea);
356    }
357  }
358
359  this.setTimeline = function (data) {
360    this.graphMaxData = this.graphMaxData || data;
361    this.graphData = data;
362
363    // initialize if it hasn't been done
364    if (!this.initialized) {
365      this.initialize(data, this.callback);
366    } else {
367      refreshGraphData();
368    }
369  };
370
371  // set the dates range; `from` and `to` are dates in DD-MM-YYYY format
372  // if a date is null it is assumed to be the min/max boundary
373  this.setDateRange = function (from, to) {
374    if (!this.initialized) { return; }
375
376    var domain = maxDomain;
377    var currentBegin = moment(timelineX.domain()[0]).startOf('day').valueOf();
378    var currentEnd = moment(timelineX.domain()[1]).startOf('day').valueOf();
379    var begin = from === null ? domain[0] : moment(from, 'DD-MM-YYYY').valueOf();
380    var end = to === null ? domain[1] : moment(to, 'DD-MM-YYYY').valueOf();
381
382    if (currentBegin == begin && currentEnd == end) {
383      return;
384    }
385
386    var graphCenter = [timelineWidth / 2, timelineHeight / 2];
387    var targetCenter = (begin + end) / 2;
388    var targetScale = (domain[1] - domain[0]) / (end - begin);
389    var targetTranslate = [
390      - (begin - domain[0]) / (domain[1] - domain[0]) * timelineWidth * targetScale,
391      graphCenter[1]
392    ];
393
394    // apply scale & translate
395    zoom.scale(targetScale).translate(targetTranslate);
396    timelineZoom();
397    timelineXTranslate = targetTranslate[0];
398    timelineXScale = targetScale;
399    applyZoom();
400  };
401}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.