/*
* niViz -- snow profiles visualization
* Copyright (C) 2015 WSL/SLF - Fluelastrasse 11 - 7260 Davos Dorf - Switzerland.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
(function (niviz) {
'use strict';
// --- Module Dependencies ---
var Common = niviz.Common;
var t = niviz.Translate.gettext;
/** @module niviz */
/**
* The static Visuals class encompasses a few static methods to draw common
* visual elements such as buttons or arrows. No instantiation or
* initialization is necessary.
*
* @class Visuals
* @static
*/
var Visuals = {};
/**
* Create a grey button with a text on top, height 25px, width 80px
*
* @static
* @method Button
* @param {Object} paper SnapSVG paper object
* @param {Number} x Top left corner x-coordinate
* @param {Number} y Top left corner y-coordinate
* @param {String} text Text to be show on button
* @param {Object} self Context for callback function
* @param {Function} callback Callback function executed on click
* @return {Object} svg group container which holds all the visual elements
*/
Visuals.Button = function (paper, x, y, text, self, callback) {
var set = paper.g();
var font = {
fontSize: '11px',
fontFamily: 'Helvetica, Arial',
fill: '#707070',
textAnchor: 'middle'
}, grad1 = 'l(1,1,1,0)#ccc-#eee', grad2 = 'l(1,1,1,0)#ddd-#eee';
var btn = paper.rect(x, y, 80, 25).attr({
fill: grad1, opacity: 0.9, strokeWidth: 0.5, stroke: '#aaa'
});
var bounds = btn.getBBox();
var label = paper.text(bounds.x + (bounds.x2 - bounds.x) / 2,
bounds.y2 - 9, text).attr(font);
set.add(btn);
set.add(label);
set
.hover(
function () {
btn.attr({'fill': grad1, 'opacity': 0.9});
label.attr({'cursor': 'default'});
},
function () {
btn.attr({'fill': grad2, 'opacity': 0.9});
}
)
.mousedown(function () {
btn.attr({'fill': grad2, 'opacity': 0.9});
})
.mouseup(function () {
btn.attr({'fill': grad1, 'opacity': 0.9});
})
.click(function() {
btn.attr({'fill': grad1, 'opacity': 0.9});
callback.call(self);
});
return set;
};
/*
* Arrows used for indicating instabilites
*/
var arrow = function (paper, p, left, right) {
var center = right - (right - left) / 2, path,
font = p.font || {}, fontsize = p.fontsize || 11;
return function (y, top, bottom, popup, fs) {
var set = paper.g();
y = Math.round(y);
path = paper.line(right, y, left, y);
set.add(paper.polygon(left, y, left + 10, y - 5, left + 10, y + 5, left, y));
set.add(path.attr({ stroke: '#000', strokeWidth: 2.5 }));
if (top) set.add(paper.text(center, y - 3, top).attr(font));
if (bottom) {
if (!fs) fs = fontsize;
var b = paper.multitext(center, y + fs, bottom).attr(font).attr({ 'font-size': fs});
b.transform('t0,' + (y - b.getBBox().y + 2));
set.add(b);
}
set.attr('cursor', 'default');
if (popup) {
set.attr('cursor', 'help');
var lbl, pop;
set.hover( function () {
lbl = paper.text(0, 0, popup).attr(font);
pop = paper.popup(center, bottom ? y + fontsize + 2 : y + 3, lbl, 'bottom', 10)
.attr({
fill: '#fff', stroke: '#333', 'stroke-width': 1, 'fill-opacity': .9
});
}, function () {
lbl.remove();
pop.remove();
});
}
return set;
};
};
/**
* Pass sk38, rb, ect or ct profile feature and draw black pointed arrows
* at the respective heights.
* Note: Only the first profile of each test is considered!
*
* @static
* @method Arrows
* @param {Object} ctx 'this' context to be utilized
* @param {Feature} data A feature such as rb, ect, ct or sk38 of a profile
* @param {Number} left left x-coordinate (where the arrow points)
* @param {Number} right right x-coordinate
* @param {Number} min Minimal height for arrow to be drawn
* @param {Number} max Maximal height for arrow to be drawn
* @param {Function} coord Function to be map height to actual pixel value
* @return {Object} svg group container which holds all the visual elements
*/
Visuals.Arrows = function (ctx, data, left, right, min, max, coord) {
var current, paper = ctx.paper, set = paper.g(), tops = [],
p = ctx.properties, draw = arrow(paper, p, left, right), fs;
if (!data || !data.layers) return undefined;
data.layers.forEach(function (layer) {
top = layer.top;
if (layer.nofailure) top = ctx.inverted ? 0 : ctx.profile.top;
if (top > max || top < min || tops.indexOf(top) !== -1) return;
tops.push(top);
current = coord(top);
var top, bottom, popup;
if (data.type === 'sk38') {
top = layer.text !== '-1' ? 'cl: ' + layer.text : undefined;
bottom = 'SK38=' + layer.sk38;
} else if (data.type === 'ct' || data.type === 'saw' || data.type === 'sf') {
top = layer.text;
if (layer.character) top = top + ' ' + layer.character;
if (layer.comment) popup = layer.comment;
} else if (data.type === 'ect') {
top = layer.text;
if (Common.defaults.ECT_format === 'Swiss') top = layer.swisscode || layer.text;
if (layer.comment) popup = layer.comment;
} else if (data.type === 'rb') {
bottom = '';
if (layer.releasetype) bottom += t(layer.releasetype) + '\n';
if (layer.character) bottom += t(layer.character);
if (layer.text) top = layer.text;
if (layer.comment) popup = layer.comment;
fs = 10;
}
set.add(draw(current, top, bottom, popup, fs));
}, ctx);
return set;
};
/**
* Try to print the text handed over in a textbox with a maximum width
* of maxwidth
*
* @method format
* @private
* @param {Object} textbox SnapSVG text element
* @param {String} current The current string
* @param {String} addendum Text string to be added
* @param {Number} maxwidth Max width of the text box, default: 250px
* @return {String} The altered comment string
*/
Visuals.format = function (textbox, current, addendum, maxwidth) {
maxwidth = maxwidth || 250;
addendum += '';
var words = addendum.split(/[ ]/) || [], ii, last = current.lastIndexOf('\n'), index;
if (last) last = current.substring(last);
else last = '';
for (ii = 0; ii < words.length; ++ii) {
last = last + (ii === 0 ? '' : ' ') + words[ii];
textbox.attr('text', last);
if (textbox.getBBox().width > maxwidth) {
current += '\n' + words[ii];
last = '';
} else {
if (words[ii] === '\n') {
current += '\u200C\n';
last = '';
} else if (words[ii] === '') {
current = current;
} else {
current += (ii === 0 ? '' : ' ') + words[ii];
index = words[ii].lastIndexOf('\n');
if (index > -1) last = words[ii].substring(index);
}
}
}
return current;
};
/**
* Return text with linebreaks after maxwidth chars
*
* @method format
* @private
* @param {String} text Text string to be added
* @param {Number} maxwidth Max width of the text box, default: 60 chars
* @return {String} The altered string
*/
Visuals.formatSimple = function (text, maxlength) {
maxlength = maxlength || 60;
var words = text.split(/[ ]/) || [], result = '', sum = 0;
for (var ii = 0; ii < words.length; ++ii) {
if (sum + words[ii].length > maxlength) {
result = result + '\n' + words[ii];
sum = words[ii].length;
} else {
result += ' ' + words[ii];
sum += words[ii].length + 1;
}
}
return result;
};
// --- Module Exports ---
niviz.Visuals = Visuals;
}(niviz));