Add tests for textFeature.
Remove the direction property -- it isn't supported by Firefox and only
obscurely supported by Chrome.
MODIFY
src/canvas/textFeature.js
src/canvas/textFeature.js
@@ -77,12 +77,21 @@ var canvas_textFeature = function (arg) {
(parts[3] || '') + ' ' + (parts[4] || '') + ' ' +
(parts[5] || '') + (parts[6] ? '/' + parts[6] : '') + ' ' +
parts[7];
+ font = font.trim().replace(/\s\s+/g, ' ');
}
return font;
};
/**
* Render the data on the canvas.
+ *
+ * This does not currently support multiline text or word wrapping, since
+ * canvas doesn't implement that directly. To support these, each text item
+ * would need to be split on line breaks, and have the width of the text
+ * calculated with context2d.measureText to determine word wrapping. This
+ * would also need to calculate the effective line height from the font
+ * specification.
+ *
* @protected
* @param {CanvasRenderingContext2D} context2d The canvas context to draw in.
* @param {geo.map} map The parent map object.
@@ -126,7 +135,6 @@ var canvas_textFeature = function (arg) {
m_this._canvasProperty(context2d, 'font', m_this.getFontFromStyles(fontFromSubValues, d, i));
m_this._canvasProperty(context2d, 'textAlign', m_this.style.get('textAlign')(d, i) || 'center');
m_this._canvasProperty(context2d, 'textBaseline', m_this.style.get('textBaseline')(d, i) || 'middle');
- m_this._canvasProperty(context2d, 'direction', m_this.style.get('direction')(d, i) || 'inherit');
/* rotation, scale, and offset */
rotation = m_this.style.get('rotation')(d, i) || 0;
rotateWithMap = m_this.style.get('rotateWithMap')(d, i) && mapRotation;
@@ -193,7 +201,6 @@ var canvas_textFeature = function (arg) {
context2d.setTransform(1, 0, 0, 1, 0, 0);
};
- this._init(arg);
return this;
};

MODIFY
src/textFeature.js
src/textFeature.js
@@ -35,8 +35,6 @@ var feature = require('./feature');
* @property {string|function} [style.textBaseline='middle'] The vertical text
* alignment. One of `top`, `hanging`, `middle`, `alphabetic`,
* `ideographic`, or `bottom`.
- * @property {string|function} [style.direction='inherit'] Text direction. One
- * of `ltr`, `rtl`, or `inherit`.
* @property {geo.geoColor|function} [style.color='black'] Text color. May
* include opacity.
* @property {number|function} [style.textOpacity=1] The opacity of the text.
@@ -54,9 +52,6 @@ var feature = require('./feature');
* default position for the text. This is applied before rotation.
* @property {geo.geoColor|function} [style.shadowColor='black'] Text shadow
* color. May include opacity.
- * @property {number|function} [style.shadowOpacity=1] The opacity of the
- * shadow. If the color includes opacity, this is combined with that
- * value.
* @property {geo.screenPosition|function} [style.shadowOffset] Offset for a
* text shadow. This is applied before rotation.
* @property {number|null|function} [style.shadowBlur] If not null, add a text
@@ -149,7 +144,6 @@ var textFeature = function (arg) {
font: 'bold 16px sans-serif',
textAlign: 'center',
textBaseline: 'middle',
- direction: 'inherit',
color: { r: 0, g: 0, b: 0 },
rotation: 0, /* in radians */
rotateWithMap: false,
@@ -177,15 +171,16 @@ var textFeature = function (arg) {
m_this.dataTime().modified();
};
+ this._init(arg);
return m_this;
};
textFeature.usedStyles = [
'visible', 'font', 'fontStyle', 'fontVariant', 'fontWeight', 'fontStretch',
- 'fontSize', 'lineHeight', 'fontFamily', 'textAlign', 'textBaseline',
- 'direction', 'color', 'textOpacity', 'rotation', 'rotateWithMap',
- 'textScaled', 'offset', 'shadowColor', 'shadowOpacity', 'shadowOffset',
- 'shadowBlur', 'shadowRotate', 'textStrokeColor', 'textStrokeWidth'
+ 'fontSize', 'lineHeight', 'fontFamily', 'textAlign', 'textBaseline', 'color',
+ 'textOpacity', 'rotation', 'rotateWithMap', 'textScaled', 'offset',
+ 'shadowColor', 'shadowOffset', 'shadowBlur', 'shadowRotate',
+ 'textStrokeColor', 'textStrokeWidth'
];
/**

ADD
tests/cases/textFeature.js
+// Test geo.textFeature and geo.canvas.textFeature
+
+var $ = require('jquery');
+var mockAnimationFrame = require('../test-utils').mockAnimationFrame;
+var stepAnimationFrame = require('../test-utils').stepAnimationFrame;
+var unmockAnimationFrame = require('../test-utils').unmockAnimationFrame;
+var geo = require('../test-utils').geo;
+var logCanvas2D = require('../test-utils').logCanvas2D;
+
+describe('geo.textFeature', function () {
+ 'use strict';
+
+ var testText = [
+ {
+ text: 'First - Basic',
+ x: 20,
+ y: 10
+ }, {
+ text: 'Second - invisible',
+ x: 20,
+ y: 11,
+ visible: false
+ }, {
+ text: 'Third - font',
+ x: 20,
+ y: 12,
+ font: '40px serif'
+ }, {
+ text: 'Fourth - font by sub-properties',
+ x: 20,
+ y: 13,
+ fontSize: '40px',
+ fontFamily: 'serif',
+ fontStyle: 'italic',
+ fontVariant: 'small-caps',
+ fontWeight: 'bold',
+ fontStretch: 'condensed',
+ lineHeight: '60px'
+ }, {
+ text: 'Fifth - clear',
+ x: 20,
+ y: 14,
+ color: 'rgba(120, 120, 120, 0)'
+ }, {
+ text: 'Sixth - color and opacity',
+ x: 20,
+ y: 15,
+ color: 'pink',
+ textOpacity: 0.8
+ }, {
+ text: 'Seventh - rotation',
+ x: 20,
+ y: 16,
+ rotation: 6 * Math.PI / 180,
+ rotateWithMap: true
+ }, {
+ text: 'Eighth - scale',
+ x: 20,
+ y: 17,
+ textScaled: 8
+ }, {
+ text: 'Ninth - offset',
+ x: 20,
+ y: 18,
+ offset: {x: 10, y: 14}
+ }, {
+ text: 'Tenth - shadow',
+ x: 20,
+ y: 19,
+ shadowColor: '#00F8',
+ shadowOffset: {x: 2, y: -3},
+ shadowBlur: 3,
+ shadowRotate: true,
+ rotation: Math.PI / 2
+ }, {
+ text: 'Eleventh - stroke',
+ x: 20,
+ y: 20,
+ color: 'transparent',
+ textStrokeColor: 'black',
+ textStrokeWidth: 1
+ }, {
+ text: 'Twelfth - invalid main font property',
+ x: 20,
+ y: 21,
+ font: 'not a css font string',
+ lineHeight: '60px'
+ }, {
+ text: 'Thirteenth - \u0646\u0635 \u0628\u0633\u064a\u0637 - mixed direction and unicode - \ud83d\ude03',
+ x: 20,
+ y: 22
+ }, {
+ text: 'Fourteenth - stroke and fill',
+ x: 20,
+ y: 23,
+ color: 'yellow',
+ textStrokeColor: 'black',
+ textStrokeWidth: 4
+ }
+ ];
+
+ function create_map(opts) {
+ var node = $('<div id="map"/>').css({width: '640px', height: '360px'});
+ $('#map').remove();
+ $('body').append(node);
+ opts = $.extend({}, opts);
+ opts.node = node;
+ return geo.map(opts);
+ }
+
+ describe('create', function () {
+ it('create function', function () {
+ var map, layer, text;
+ map = create_map();
+ layer = map.createLayer('feature', {renderer: 'canvas'});
+ text = geo.textFeature.create(layer);
+ expect(text instanceof geo.textFeature).toBe(true);
+ });
+ });
+
+ describe('Check private class mathods', function () {
+ var map, layer, text;
+ it('_init', function () {
+ map = create_map();
+ layer = map.createLayer('feature', {renderer: null});
+ text = geo.textFeature({layer: layer});
+ expect(text.style('font')).toBe('bold 16px sans-serif');
+ text._init({style: {font: 'serif'}});
+ expect(text.style('font')).toBe('serif');
+ text._init({position: 'a', text: 'b'});
+ expect(text.position()).toBe('a');
+ expect(text.text()).toBe('b');
+ text._init({style: {position: 'c', text: 'd'}});
+ expect(text.position()).toBe('c');
+ expect(text.text()).toBe('d');
+ });
+ });
+
+ describe('Check class accessors', function () {
+ var map, layer, text;
+ var pos = [[[0, 0], [10, 5], [5, 10]]];
+ it('position', function () {
+ map = create_map();
+ layer = map.createLayer('feature', {renderer: null});
+ text = geo.textFeature({layer: layer});
+ expect(text.position()('a')).toBe('a');
+ text.position(pos);
+ expect(text.position()).toEqual(pos);
+ text.position(function () { return 'b'; });
+ expect(text.position()('a')).toEqual('b');
+
+ text = geo.textFeature({layer: layer, position: pos});
+ expect(text.position()).toEqual(pos);
+ });
+
+ it('text', function () {
+ map = create_map();
+ layer = map.createLayer('feature', {renderer: null});
+ text = geo.textFeature({layer: layer});
+ expect(text.text()({text: 'a'})).toBe('a');
+ text.text(pos);
+ expect(text.text()).toEqual(pos);
+ text.text(function () { return 'b'; });
+ expect(text.text()('a')).toEqual('b');
+
+ text = geo.textFeature({layer: layer, text: pos});
+ expect(text.text()).toEqual(pos);
+ });
+ });
+
+ /* This is a basic integration test of geo.canvas.textFeature. */
+ describe('geo.canvas.textFeature', function () {
+ var map, layer, text, counts, style = {};
+ it('basic usage', function () {
+ mockAnimationFrame();
+ logCanvas2D();
+ map = create_map();
+ layer = map.createLayer('feature', {renderer: 'canvas'});
+ geo.textFeature.usedStyles.forEach(function (key) {
+ style[key] = function (d, i) {
+ return d[key];
+ };
+ });
+ text = layer.createFeature('text', {style: style}).data(testText);
+ text.draw();
+ stepAnimationFrame();
+ counts = $.extend({}, window._canvasLog.counts);
+ expect(counts.fillText).not.toBeLessThan(12);
+ expect(counts.strokeText).not.toBeLessThan(2);
+ map.rotation(5 * Math.PI / 180);
+ stepAnimationFrame();
+ counts = $.extend({}, window._canvasLog.counts);
+ expect(counts.fillText).not.toBeLessThan(24);
+ expect(counts.strokeText).not.toBeLessThan(4);
+ unmockAnimationFrame();
+ });
+ it('getFontFromStyles', function () {
+ map = create_map();
+ layer = map.createLayer('feature', {renderer: 'canvas'});
+ geo.textFeature.usedStyles.forEach(function (key) {
+ style[key] = function (d, i) {
+ return d[key];
+ };
+ });
+ text = layer.createFeature('text', {style: style}).data(testText);
+ expect(text.getFontFromStyles(true, text.data()[3], 3)).toBe('italic small-caps bold condensed 40px/60px serif');
+ expect(text.getFontFromStyles(true, text.data()[11], 11)).toBe('bold 16px/60px sans-serif');
+ });
+ });
+});

Add tests for textFeature.

Remove the direction property -- it isn't supported by Firefox and only
obscurely supported by Chrome.