blacklight_range_limit 7.2.0 → 7.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (29) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/app/assets/javascripts/blacklight_range_limit.js +7 -0
  4. data/app/assets/javascripts/blacklight_range_limit/range_limit_distro_facets.js +23 -11
  5. data/app/assets/javascripts/blacklight_range_limit/range_limit_slider.js +83 -76
  6. data/app/assets/stylesheets/blacklight_range_limit/blacklight_range_limit.css +26 -1
  7. data/app/helpers/range_limit_helper.rb +3 -3
  8. data/app/views/blacklight_range_limit/_range_limit_panel.html.erb +30 -27
  9. data/app/views/blacklight_range_limit/_range_segments.html.erb +6 -6
  10. data/app/views/blacklight_range_limit/range_limit_panel.html.erb +10 -0
  11. data/config/locales/blacklight_range_limit.en.yml +7 -0
  12. data/lib/blacklight_range_limit.rb +2 -4
  13. data/lib/blacklight_range_limit/controller_override.rb +28 -0
  14. data/lib/blacklight_range_limit/routes.rb +1 -1
  15. data/lib/blacklight_range_limit/routes/range_searchable.rb +1 -0
  16. data/lib/blacklight_range_limit/view_helper_override.rb +0 -13
  17. data/spec/features/a_javascript_spec.rb +11 -4
  18. data/spec/spec_helper.rb +12 -1
  19. data/vendor/assets/javascripts/flot/jquery.canvaswrapper.js +550 -0
  20. data/vendor/assets/javascripts/flot/jquery.colorhelpers.js +199 -0
  21. data/vendor/assets/javascripts/flot/jquery.flot.browser.js +98 -0
  22. data/vendor/assets/javascripts/flot/jquery.flot.drawSeries.js +663 -0
  23. data/vendor/assets/javascripts/flot/jquery.flot.hover.js +360 -0
  24. data/vendor/assets/javascripts/flot/jquery.flot.js +1610 -1928
  25. data/vendor/assets/javascripts/flot/jquery.flot.saturated.js +43 -0
  26. data/vendor/assets/javascripts/flot/jquery.flot.selection.js +237 -68
  27. data/vendor/assets/javascripts/flot/jquery.flot.uiConstants.js +10 -0
  28. metadata +10 -3
  29. data/vendor/assets/javascripts/flot/excanvas.min.js +0 -1
@@ -9,6 +9,7 @@ module BlacklightRangeLimit
9
9
  included do
10
10
  helper BlacklightRangeLimit::ViewHelperOverride
11
11
  helper RangeLimitHelper
12
+ helper_method :has_range_limit_parameters?
12
13
  end
13
14
 
14
15
  # Action method of our own!
@@ -27,5 +28,32 @@ module BlacklightRangeLimit
27
28
  end
28
29
  render('blacklight_range_limit/range_segments', :locals => {:solr_field => params[:range_field]}, :layout => !request.xhr?)
29
30
  end
31
+
32
+ # over-ride, call super, but make sure our range limits count too
33
+ def has_search_parameters?
34
+ super || has_range_limit_parameters?
35
+ end
36
+
37
+ def has_range_limit_parameters?(my_params = params)
38
+ my_params[:range] &&
39
+ my_params[:range].to_unsafe_h.any? do |key, v|
40
+ v.present? && v.respond_to?(:'[]') &&
41
+ (v["begin"].present? || v["end"].present? || v["missing"].present?)
42
+ end
43
+ end
44
+
45
+ def range_limit_panel
46
+ @facet = blacklight_config.facet_fields[params[:id]]
47
+ raise ActionController::RoutingError, 'Not Found' unless @facet
48
+
49
+ @response = search_service.search_results.first
50
+
51
+ respond_to do |format|
52
+ format.html do
53
+ # Draw the partial for the "more" facet modal window:
54
+ return render 'blacklight_range_limit/range_limit_panel', layout: !request.xhr?
55
+ end
56
+ end
57
+ end
30
58
  end
31
59
  end
@@ -2,4 +2,4 @@ module BlacklightRangeLimit
2
2
  module Routes
3
3
  require 'blacklight_range_limit/routes/range_searchable'
4
4
  end
5
- end
5
+ end
@@ -9,6 +9,7 @@ module BlacklightRangeLimit
9
9
  options = @defaults.merge(options)
10
10
 
11
11
  mapper.get 'range_limit', action: 'range_limit'
12
+ mapper.get 'range_limit_panel/:id', action: 'range_limit_panel'
12
13
  end
13
14
  end
14
15
  end
@@ -11,19 +11,6 @@
11
11
  super
12
12
  end
13
13
 
14
- def has_range_limit_parameters?(my_params = params)
15
- my_params[:range] &&
16
- my_params[:range].to_unsafe_h.any? do |key, v|
17
- v.present? && v.respond_to?(:'[]') &&
18
- (v["begin"].present? || v["end"].present? || v["missing"].present?)
19
- end
20
- end
21
-
22
- # over-ride, call super, but make sure our range limits count too
23
- def has_search_parameters?
24
- super || has_range_limit_parameters?
25
- end
26
-
27
14
  def query_has_constraints?(my_params = params)
28
15
  super || has_range_limit_parameters?(my_params)
29
16
  end
@@ -6,11 +6,18 @@ describe 'JavaScript', js: true do
6
6
  it 'initializes canvas chart' do
7
7
  visit search_catalog_path
8
8
 
9
- within('#facets .navbar') do
10
- page.find('button.navbar-toggler').click
11
- end
12
-
13
9
  click_button 'Publication Date Sort'
14
10
  expect(page).to have_css '.flot-base'
15
11
  end
12
+ it 'has a View larger modal' do
13
+ visit search_catalog_path
14
+
15
+ click_button 'Publication Date Sort'
16
+ sleep(1) # resize is debounced
17
+ click_link 'View larger »'
18
+
19
+ within '.modal-body' do
20
+ expect(page).to have_css '.flot-base'
21
+ end
22
+ end
16
23
  end
data/spec/spec_helper.rb CHANGED
@@ -8,7 +8,18 @@ require 'capybara/rspec'
8
8
  require 'selenium-webdriver'
9
9
  require 'webdrivers'
10
10
 
11
- Capybara.javascript_driver = :selenium_chrome_headless
11
+ Capybara.javascript_driver = :headless_chrome
12
+
13
+ Capybara.register_driver :headless_chrome do |app|
14
+ Capybara::Selenium::Driver.load_selenium
15
+ browser_options = ::Selenium::WebDriver::Chrome::Options.new.tap do |opts|
16
+ opts.args << '--headless'
17
+ opts.args << '--disable-gpu'
18
+ opts.args << '--no-sandbox'
19
+ opts.args << '--window-size=1280,1696'
20
+ end
21
+ Capybara::Selenium::Driver.new(app, browser: :chrome, options: browser_options)
22
+ end
12
23
 
13
24
  RSpec.configure do |config|
14
25
  # rspec-rails 3 will no longer automatically infer an example group's spec type
@@ -0,0 +1,550 @@
1
+ /** ## jquery.flot.canvaswrapper
2
+
3
+ This plugin contains the function for creating and manipulating both the canvas
4
+ layers and svg layers.
5
+
6
+ The Canvas object is a wrapper around an HTML5 canvas tag.
7
+ The constructor Canvas(cls, container) takes as parameters cls,
8
+ the list of classes to apply to the canvas adnd the containter,
9
+ element onto which to append the canvas. The canvas operations
10
+ don't work unless the canvas is attached to the DOM.
11
+
12
+ ### jquery.canvaswrapper.js API functions
13
+ */
14
+
15
+ (function($) {
16
+ var Canvas = function(cls, container) {
17
+ var element = container.getElementsByClassName(cls)[0];
18
+
19
+ if (!element) {
20
+ element = document.createElement('canvas');
21
+ element.className = cls;
22
+ element.style.direction = 'ltr';
23
+ element.style.position = 'absolute';
24
+ element.style.left = '0px';
25
+ element.style.top = '0px';
26
+
27
+ container.appendChild(element);
28
+
29
+ // If HTML5 Canvas isn't available, throw
30
+
31
+ if (!element.getContext) {
32
+ throw new Error('Canvas is not available.');
33
+ }
34
+ }
35
+
36
+ this.element = element;
37
+
38
+ var context = this.context = element.getContext('2d');
39
+ this.pixelRatio = $.plot.browser.getPixelRatio(context);
40
+
41
+ // Size the canvas to match the internal dimensions of its container
42
+ var width = $(container).width();
43
+ var height = $(container).height();
44
+ this.resize(width, height);
45
+
46
+ // Collection of HTML div layers for text overlaid onto the canvas
47
+
48
+ this.SVGContainer = null;
49
+ this.SVG = {};
50
+
51
+ // Cache of text fragments and metrics, so we can avoid expensively
52
+ // re-calculating them when the plot is re-rendered in a loop.
53
+
54
+ this._textCache = {};
55
+ }
56
+
57
+ /**
58
+ - resize(width, height)
59
+
60
+ Resizes the canvas to the given dimensions.
61
+ The width represents the new width of the canvas, meanwhile the height
62
+ is the new height of the canvas, both of them in pixels.
63
+ */
64
+
65
+ Canvas.prototype.resize = function(width, height) {
66
+ var minSize = 10;
67
+ width = width < minSize ? minSize : width;
68
+ height = height < minSize ? minSize : height;
69
+
70
+ var element = this.element,
71
+ context = this.context,
72
+ pixelRatio = this.pixelRatio;
73
+
74
+ // Resize the canvas, increasing its density based on the display's
75
+ // pixel ratio; basically giving it more pixels without increasing the
76
+ // size of its element, to take advantage of the fact that retina
77
+ // displays have that many more pixels in the same advertised space.
78
+
79
+ // Resizing should reset the state (excanvas seems to be buggy though)
80
+
81
+ if (this.width !== width) {
82
+ element.width = width * pixelRatio;
83
+ element.style.width = width + 'px';
84
+ this.width = width;
85
+ }
86
+
87
+ if (this.height !== height) {
88
+ element.height = height * pixelRatio;
89
+ element.style.height = height + 'px';
90
+ this.height = height;
91
+ }
92
+
93
+ // Save the context, so we can reset in case we get replotted. The
94
+ // restore ensure that we're really back at the initial state, and
95
+ // should be safe even if we haven't saved the initial state yet.
96
+
97
+ context.restore();
98
+ context.save();
99
+
100
+ // Scale the coordinate space to match the display density; so even though we
101
+ // may have twice as many pixels, we still want lines and other drawing to
102
+ // appear at the same size; the extra pixels will just make them crisper.
103
+
104
+ context.scale(pixelRatio, pixelRatio);
105
+ };
106
+
107
+ /**
108
+ - clear()
109
+
110
+ Clears the entire canvas area, not including any overlaid HTML text
111
+ */
112
+ Canvas.prototype.clear = function() {
113
+ this.context.clearRect(0, 0, this.width, this.height);
114
+ };
115
+
116
+ /**
117
+ - render()
118
+
119
+ Finishes rendering the canvas, including managing the text overlay.
120
+ */
121
+ Canvas.prototype.render = function() {
122
+ var cache = this._textCache;
123
+
124
+ // For each text layer, add elements marked as active that haven't
125
+ // already been rendered, and remove those that are no longer active.
126
+
127
+ for (var layerKey in cache) {
128
+ if (hasOwnProperty.call(cache, layerKey)) {
129
+ var layer = this.getSVGLayer(layerKey),
130
+ layerCache = cache[layerKey];
131
+
132
+ var display = layer.style.display;
133
+ layer.style.display = 'none';
134
+
135
+ for (var styleKey in layerCache) {
136
+ if (hasOwnProperty.call(layerCache, styleKey)) {
137
+ var styleCache = layerCache[styleKey];
138
+ for (var key in styleCache) {
139
+ if (hasOwnProperty.call(styleCache, key)) {
140
+ var val = styleCache[key],
141
+ positions = val.positions;
142
+
143
+ for (var i = 0, position; positions[i]; i++) {
144
+ position = positions[i];
145
+ if (position.active) {
146
+ if (!position.rendered) {
147
+ layer.appendChild(position.element);
148
+ position.rendered = true;
149
+ }
150
+ } else {
151
+ positions.splice(i--, 1);
152
+ if (position.rendered) {
153
+ while (position.element.firstChild) {
154
+ position.element.removeChild(position.element.firstChild);
155
+ }
156
+ position.element.parentNode.removeChild(position.element);
157
+ }
158
+ }
159
+ }
160
+
161
+ if (positions.length === 0) {
162
+ if (val.measured) {
163
+ val.measured = false;
164
+ } else {
165
+ delete styleCache[key];
166
+ }
167
+ }
168
+ }
169
+ }
170
+ }
171
+ }
172
+
173
+ layer.style.display = display;
174
+ }
175
+ }
176
+ };
177
+
178
+ /**
179
+ - getSVGLayer(classes)
180
+
181
+ Creates (if necessary) and returns the SVG overlay container.
182
+ The classes string represents the string of space-separated CSS classes
183
+ used to uniquely identify the text layer. It return the svg-layer div.
184
+ */
185
+ Canvas.prototype.getSVGLayer = function(classes) {
186
+ var layer = this.SVG[classes];
187
+
188
+ // Create the SVG layer if it doesn't exist
189
+
190
+ if (!layer) {
191
+ // Create the svg layer container, if it doesn't exist
192
+
193
+ var svgElement;
194
+
195
+ if (!this.SVGContainer) {
196
+ this.SVGContainer = document.createElement('div');
197
+ this.SVGContainer.className = 'flot-svg';
198
+ this.SVGContainer.style.position = 'absolute';
199
+ this.SVGContainer.style.top = '0px';
200
+ this.SVGContainer.style.left = '0px';
201
+ this.SVGContainer.style.height = '100%';
202
+ this.SVGContainer.style.width = '100%';
203
+ this.SVGContainer.style.pointerEvents = 'none';
204
+ this.element.parentNode.appendChild(this.SVGContainer);
205
+
206
+ svgElement = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
207
+ svgElement.style.width = '100%';
208
+ svgElement.style.height = '100%';
209
+
210
+ this.SVGContainer.appendChild(svgElement);
211
+ } else {
212
+ svgElement = this.SVGContainer.firstChild;
213
+ }
214
+
215
+ layer = document.createElementNS('http://www.w3.org/2000/svg', 'g');
216
+ layer.setAttribute('class', classes);
217
+ layer.style.position = 'absolute';
218
+ layer.style.top = '0px';
219
+ layer.style.left = '0px';
220
+ layer.style.bottom = '0px';
221
+ layer.style.right = '0px';
222
+ svgElement.appendChild(layer);
223
+ this.SVG[classes] = layer;
224
+ }
225
+
226
+ return layer;
227
+ };
228
+
229
+ /**
230
+ - getTextInfo(layer, text, font, angle, width)
231
+
232
+ Creates (if necessary) and returns a text info object.
233
+ The object looks like this:
234
+ ```js
235
+ {
236
+ width //Width of the text's wrapper div.
237
+ height //Height of the text's wrapper div.
238
+ element //The HTML div containing the text.
239
+ positions //Array of positions at which this text is drawn.
240
+ }
241
+ ```
242
+ The positions array contains objects that look like this:
243
+ ```js
244
+ {
245
+ active //Flag indicating whether the text should be visible.
246
+ rendered //Flag indicating whether the text is currently visible.
247
+ element //The HTML div containing the text.
248
+ text //The actual text and is identical with element[0].textContent.
249
+ x //X coordinate at which to draw the text.
250
+ y //Y coordinate at which to draw the text.
251
+ }
252
+ ```
253
+ Each position after the first receives a clone of the original element.
254
+ The idea is that that the width, height, and general 'identity' of the
255
+ text is constant no matter where it is placed; the placements are a
256
+ secondary property.
257
+
258
+ Canvas maintains a cache of recently-used text info objects; getTextInfo
259
+ either returns the cached element or creates a new entry.
260
+
261
+ The layer parameter is string of space-separated CSS classes uniquely
262
+ identifying the layer containing this text.
263
+ Text is the text string to retrieve info for.
264
+ Font is either a string of space-separated CSS classes or a font-spec object,
265
+ defining the text's font and style.
266
+ Angle is the angle at which to rotate the text, in degrees. Angle is currently unused,
267
+ it will be implemented in the future.
268
+ The last parameter is the Maximum width of the text before it wraps.
269
+ The method returns a text info object.
270
+ */
271
+ Canvas.prototype.getTextInfo = function(layer, text, font, angle, width) {
272
+ var textStyle, layerCache, styleCache, info;
273
+
274
+ // Cast the value to a string, in case we were given a number or such
275
+
276
+ text = '' + text;
277
+
278
+ // If the font is a font-spec object, generate a CSS font definition
279
+
280
+ if (typeof font === 'object') {
281
+ textStyle = font.style + ' ' + font.variant + ' ' + font.weight + ' ' + font.size + 'px/' + font.lineHeight + 'px ' + font.family;
282
+ } else {
283
+ textStyle = font;
284
+ }
285
+
286
+ // Retrieve (or create) the cache for the text's layer and styles
287
+
288
+ layerCache = this._textCache[layer];
289
+
290
+ if (layerCache == null) {
291
+ layerCache = this._textCache[layer] = {};
292
+ }
293
+
294
+ styleCache = layerCache[textStyle];
295
+
296
+ if (styleCache == null) {
297
+ styleCache = layerCache[textStyle] = {};
298
+ }
299
+
300
+ var key = generateKey(text);
301
+ info = styleCache[key];
302
+
303
+ // If we can't find a matching element in our cache, create a new one
304
+
305
+ if (!info) {
306
+ var element = document.createElementNS('http://www.w3.org/2000/svg', 'text');
307
+ if (text.indexOf('<br>') !== -1) {
308
+ addTspanElements(text, element, -9999);
309
+ } else {
310
+ var textNode = document.createTextNode(text);
311
+ element.appendChild(textNode);
312
+ }
313
+
314
+ element.style.position = 'absolute';
315
+ element.style.maxWidth = width;
316
+ element.setAttributeNS(null, 'x', -9999);
317
+ element.setAttributeNS(null, 'y', -9999);
318
+
319
+ if (typeof font === 'object') {
320
+ element.style.font = textStyle;
321
+ element.style.fill = font.fill;
322
+ } else if (typeof font === 'string') {
323
+ element.setAttribute('class', font);
324
+ }
325
+
326
+ this.getSVGLayer(layer).appendChild(element);
327
+ var elementRect = element.getBBox();
328
+
329
+ info = styleCache[key] = {
330
+ width: elementRect.width,
331
+ height: elementRect.height,
332
+ measured: true,
333
+ element: element,
334
+ positions: []
335
+ };
336
+
337
+ //remove elements from dom
338
+ while (element.firstChild) {
339
+ element.removeChild(element.firstChild);
340
+ }
341
+ element.parentNode.removeChild(element);
342
+ }
343
+
344
+ info.measured = true;
345
+ return info;
346
+ };
347
+
348
+ function updateTransforms (element, transforms) {
349
+ element.transform.baseVal.clear();
350
+ if (transforms) {
351
+ transforms.forEach(function(t) {
352
+ element.transform.baseVal.appendItem(t);
353
+ });
354
+ }
355
+ }
356
+
357
+ /**
358
+ - addText (layer, x, y, text, font, angle, width, halign, valign, transforms)
359
+
360
+ Adds a text string to the canvas text overlay.
361
+ The text isn't drawn immediately; it is marked as rendering, which will
362
+ result in its addition to the canvas on the next render pass.
363
+
364
+ The layer is string of space-separated CSS classes uniquely
365
+ identifying the layer containing this text.
366
+ X and Y represents the X and Y coordinate at which to draw the text.
367
+ and text is the string to draw
368
+ */
369
+ Canvas.prototype.addText = function(layer, x, y, text, font, angle, width, halign, valign, transforms) {
370
+ var info = this.getTextInfo(layer, text, font, angle, width),
371
+ positions = info.positions;
372
+
373
+ // Tweak the div's position to match the text's alignment
374
+
375
+ if (halign === 'center') {
376
+ x -= info.width / 2;
377
+ } else if (halign === 'right') {
378
+ x -= info.width;
379
+ }
380
+
381
+ if (valign === 'middle') {
382
+ y -= info.height / 2;
383
+ } else if (valign === 'bottom') {
384
+ y -= info.height;
385
+ }
386
+
387
+ y += 0.75 * info.height;
388
+
389
+
390
+ // Determine whether this text already exists at this position.
391
+ // If so, mark it for inclusion in the next render pass.
392
+
393
+ for (var i = 0, position; positions[i]; i++) {
394
+ position = positions[i];
395
+ if (position.x === x && position.y === y && position.text === text) {
396
+ position.active = true;
397
+ // update the transforms
398
+ updateTransforms(position.element, transforms);
399
+
400
+ return;
401
+ } else if (position.active === false) {
402
+ position.active = true;
403
+ position.text = text;
404
+ if (text.indexOf('<br>') !== -1) {
405
+ y -= 0.25 * info.height;
406
+ addTspanElements(text, position.element, x);
407
+ } else {
408
+ position.element.textContent = text;
409
+ }
410
+ position.element.setAttributeNS(null, 'x', x);
411
+ position.element.setAttributeNS(null, 'y', y);
412
+ position.x = x;
413
+ position.y = y;
414
+ // update the transforms
415
+ updateTransforms(position.element, transforms);
416
+
417
+ return;
418
+ }
419
+ }
420
+
421
+ // If the text doesn't exist at this position, create a new entry
422
+
423
+ // For the very first position we'll re-use the original element,
424
+ // while for subsequent ones we'll clone it.
425
+
426
+ position = {
427
+ active: true,
428
+ rendered: false,
429
+ element: positions.length ? info.element.cloneNode() : info.element,
430
+ text: text,
431
+ x: x,
432
+ y: y
433
+ };
434
+
435
+ positions.push(position);
436
+
437
+ if (text.indexOf('<br>') !== -1) {
438
+ y -= 0.25 * info.height;
439
+ addTspanElements(text, position.element, x);
440
+ } else {
441
+ position.element.textContent = text;
442
+ }
443
+
444
+ // Move the element to its final position within the container
445
+ position.element.setAttributeNS(null, 'x', x);
446
+ position.element.setAttributeNS(null, 'y', y);
447
+ position.element.style.textAlign = halign;
448
+ // update the transforms
449
+ updateTransforms(position.element, transforms);
450
+ };
451
+
452
+ var addTspanElements = function(text, element, x) {
453
+ var lines = text.split('<br>'),
454
+ tspan, i, offset;
455
+
456
+ for (i = 0; i < lines.length; i++) {
457
+ if (!element.childNodes[i]) {
458
+ tspan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan');
459
+ element.appendChild(tspan);
460
+ } else {
461
+ tspan = element.childNodes[i];
462
+ }
463
+ tspan.textContent = lines[i];
464
+ offset = i * 1 + 'em';
465
+ tspan.setAttributeNS(null, 'dy', offset);
466
+ tspan.setAttributeNS(null, 'x', x);
467
+ }
468
+ }
469
+
470
+ /**
471
+ - removeText (layer, x, y, text, font, angle)
472
+
473
+ The function removes one or more text strings from the canvas text overlay.
474
+ If no parameters are given, all text within the layer is removed.
475
+
476
+ Note that the text is not immediately removed; it is simply marked as
477
+ inactive, which will result in its removal on the next render pass.
478
+ This avoids the performance penalty for 'clear and redraw' behavior,
479
+ where we potentially get rid of all text on a layer, but will likely
480
+ add back most or all of it later, as when redrawing axes, for example.
481
+
482
+ The layer is a string of space-separated CSS classes uniquely
483
+ identifying the layer containing this text. The following parameter are
484
+ X and Y coordinate of the text.
485
+ Text is the string to remove, while the font is either a string of space-separated CSS
486
+ classes or a font-spec object, defining the text's font and style.
487
+ */
488
+ Canvas.prototype.removeText = function(layer, x, y, text, font, angle) {
489
+ var info, htmlYCoord;
490
+ if (text == null) {
491
+ var layerCache = this._textCache[layer];
492
+ if (layerCache != null) {
493
+ for (var styleKey in layerCache) {
494
+ if (hasOwnProperty.call(layerCache, styleKey)) {
495
+ var styleCache = layerCache[styleKey];
496
+ for (var key in styleCache) {
497
+ if (hasOwnProperty.call(styleCache, key)) {
498
+ var positions = styleCache[key].positions;
499
+ positions.forEach(function(position) {
500
+ position.active = false;
501
+ });
502
+ }
503
+ }
504
+ }
505
+ }
506
+ }
507
+ } else {
508
+ info = this.getTextInfo(layer, text, font, angle);
509
+ positions = info.positions;
510
+ positions.forEach(function(position) {
511
+ htmlYCoord = y + 0.75 * info.height;
512
+ if (position.x === x && position.y === htmlYCoord && position.text === text) {
513
+ position.active = false;
514
+ }
515
+ });
516
+ }
517
+ };
518
+
519
+ /**
520
+ - clearCache()
521
+
522
+ Clears the cache used to speed up the text size measurements.
523
+ As an (unfortunate) side effect all text within the text Layer is removed.
524
+ Use this function before plot.setupGrid() and plot.draw() if the plot just
525
+ became visible or the styles changed.
526
+ */
527
+ Canvas.prototype.clearCache = function() {
528
+ var cache = this._textCache;
529
+ for (var layerKey in cache) {
530
+ if (hasOwnProperty.call(cache, layerKey)) {
531
+ var layer = this.getSVGLayer(layerKey);
532
+ while (layer.firstChild) {
533
+ layer.removeChild(layer.firstChild);
534
+ }
535
+ }
536
+ };
537
+
538
+ this._textCache = {};
539
+ };
540
+
541
+ function generateKey(text) {
542
+ return text.replace(/0|1|2|3|4|5|6|7|8|9/g, '0');
543
+ }
544
+
545
+ if (!window.Flot) {
546
+ window.Flot = {};
547
+ }
548
+
549
+ window.Flot.Canvas = Canvas;
550
+ })(jQuery);