Files
makemeahanzi/client/glyph.js
2015-09-23 23:30:02 -04:00

271 lines
7.9 KiB
JavaScript

"use strict";
Session.setDefault('glyph.data', undefined);
Session.setDefault('glyph.selected_point', undefined);
Session.setDefault('glyph.show_strokes', true);
var COLORS = ['#0074D9', '#2ECC40', '#FFDC00', '#FF4136', '#7FDBFF',
'#001F3F', '#39CCCC', '#3D9970', '#01FF70', '#FF851B'];
var DICTIONARY = 'http://www.archchinese.com/chinese_english_dictionary.html';
var EDIT_STROKES = true;
function change_glyph(method, glyph) {
glyph = glyph || Session.get('glyph.data');
Meteor.call(method, glyph, function(err, data) {
data = fill_glyph_fields(data);
Session.set('glyph.data', data);
if (method === 'save_glyph') {
refresh_fraction_verified();
} else {
Session.set('glyph.show_strokes', true);
}
});
}
function fill_glyph_fields(glyph) {
glyph.manual = glyph.manual || {};
glyph.manual.verified = glyph.manual.verified || false;
if (EDIT_STROKES) {
glyph.render = get_glyph_render_data(glyph, glyph.manual.bridges);
} else {
glyph.render = {d: '', log: [], strokes: glyph.derived.strokes};
}
glyph.manual.bridges = glyph.manual.bridges || glyph.render.bridges;
return glyph;
}
function has_errors(glyph) {
var error = function(pair) { return pair[0] != 'success'; };
return glyph && glyph.render.log.filter(error).length > 0;
}
function refresh_fraction_verified() {
Meteor.call('get_fraction_verified', function(err, data) {
if (!err) {
Session.set('glyph.fraction_verified', data);
}
});
}
window.get_glyph = function(name) {
change_glyph('get_glyph', name);
}
function contains(bridges, bridge) {
return remove_bridges(bridges, [bridge]).length < bridges.length;
}
function remove_bridges(add, remove) {
var set = {};
for (var i = 0; i < remove.length; i++) {
set[to_line(remove[i]).coordinates] = true;
set[to_line([remove[i][1], remove[i][0]]).coordinates] = true;
}
return add.filter(function(x) { return !set[to_line(x).coordinates]; });
}
function to_line(pairs) {
return {
x1: pairs[0][0],
y1: pairs[0][1],
x2: pairs[1][0],
y2: pairs[1][1],
coordinates: pairs[0].join(',') + ',' + pairs[1].join(','),
};
}
function to_point(pair) {
return {x: pair[0], y: pair[1], coordinates: pair.join(',')};
}
var bindings = {
'w': function() {
if (!EDIT_STROKES) {
var glyph = Session.get('glyph.data');
var character = String.fromCodePoint(parseInt(glyph.name.substr(3), 16));
window.open(DICTIONARY + '?find=' + character, '_blank').focus();
return;
}
if (Session.get('glyph.show_strokes')) {
Session.set('glyph.show_strokes', false);
Session.set('glyph.selected_point', undefined);
var glyph = Session.get('glyph.data');
glyph.manual.verified = false;
change_glyph('save_glyph', glyph);
} else {
var glyph = Session.get('glyph.data');
delete glyph.manual;
Session.set('glyph.data', fill_glyph_fields(glyph));
}
},
'a': function() {
change_glyph('get_previous_glyph');
},
'A': function() {
change_glyph('get_previous_glyph_skip_verified');
},
's': function() {
if (!EDIT_STROKES) {
return;
}
var glyph = Session.get('glyph.data');
if (!Session.get('glyph.show_strokes')) {
Session.set('glyph.show_strokes', true);
return;
}
if (glyph.manual.verified || !has_errors(glyph)) {
glyph.manual.verified = !glyph.manual.verified;
change_glyph('save_glyph', glyph);
}
},
'd': function() {
change_glyph('get_next_glyph');
},
'D': function() {
change_glyph('get_next_glyph_skip_verified');
},
};
Template.controls.events({
'click #w-button': bindings.w,
'click #a-button': bindings.a,
'click #s-button': bindings.s,
'click #d-button': bindings.d,
});
Template.controls.helpers({
w_button_name: function() {
if (!EDIT_STROKES) {
return 'Check';
}
return Session.get('glyph.show_strokes') ? 'Edit' : 'Reset';
},
s_button_name: function() {
if (!Session.get('glyph.show_strokes')) {
return 'View';
}
var glyph = Session.get('glyph.data');
return glyph && glyph.manual.verified ? 'Flag' : 'Verify';
},
});
Template.glyph.events({
'click #glyph svg g line': function(e) {
var coordinates = $(e.target).attr('data-coordinates');
var xs = coordinates.split(',').map(function(x) {
return parseInt(x, 10);
});
var bridge = [[xs[0], xs[1]], [xs[2], xs[3]]];
var glyph = Session.get('glyph.data');
glyph.manual.bridges = remove_bridges(glyph.manual.bridges, [bridge]);
Session.set('glyph.data', fill_glyph_fields(glyph));
Session.set('glyph.selected_point', undefined);
},
'click #glyph svg g circle': function(e) {
var coordinates = $(e.target).attr('data-coordinates');
var selected_point = Session.get('glyph.selected_point');
if (selected_point === coordinates) {
Session.set('glyph.selected_point', undefined);
return;
} else if (!selected_point) {
Session.set('glyph.selected_point', coordinates);
return;
}
var xs = (coordinates + ',' + selected_point).split(',').map(function(x) {
return parseInt(x, 10);
});
var bridge = [[xs[0], xs[1]], [xs[2], xs[3]]];
var glyph = Session.get('glyph.data');
if (!contains(glyph.manual.bridges, bridge)) {
glyph.manual.bridges.push(bridge);
Session.set('glyph.data', fill_glyph_fields(glyph));
}
Session.set('glyph.selected_point', undefined);
},
});
Template.glyph.helpers({
glyph: function() {
return !!Session.get('glyph.data');
},
verified: function() {
if (!EDIT_STROKES) {
return undefined;
}
var glyph = Session.get('glyph.data');
if (has_errors(glyph)) {
return 'error';
}
return glyph && glyph.manual.verified ? 'verified' : undefined;
},
log: function() {
var glyph = Session.get('glyph.data');
return glyph ? glyph.render.log.map(function(pair) {
return {log_class: pair[0], log_message: pair[1]};
}) : [];
},
base_color: function() {
return Session.get('glyph.show_strokes') ? 'black' : 'gray';
},
d: function() {
return Session.get('glyph.data').render.d;
},
show_strokes: function() {
return !!Session.get('glyph.show_strokes');
},
strokes: function() {
var glyph = Session.get('glyph.data');
var result = [];
for (var i = 0; i < glyph.render.strokes.length; i++) {
var stroke = glyph.render.strokes[i];
result.push({color: COLORS[i % COLORS.length], stroke: stroke});
}
return result;
},
bridges: function() {
var glyph = Session.get('glyph.data');
var original = {};
for (var i = 0; i < glyph.render.bridges.length; i++) {
var bridge = glyph.render.bridges[i];
original[to_line(bridge).coordinates] = true;
original[to_line([bridge[1], bridge[0]]).coordinates] = true;
}
var result = [];
for (var i = 0; i < glyph.manual.bridges.length; i++) {
var line = to_line(glyph.manual.bridges[i]);
line.color = original[line.coordinates] ? 'red' : 'purple';
result.push(line);
}
return result;
},
points: function() {
var glyph = Session.get('glyph.data');
var result = [];
for (var i = 0; i < glyph.render.endpoints.length; i++) {
var endpoint = glyph.render.endpoints[i];
var point = to_point(endpoint.point);
point.color = endpoint.corner ? 'red' : 'black';
point.z_index = endpoint.corner ? 1 : 0;
if (point.coordinates === Session.get('glyph.selected_point')) {
point.color = 'purple';
}
result.push(point);
}
result.sort(function(p1, p2) { return p1.z_index - p2.z_index; });
return result;
},
});
Meteor.startup(function() {
$('body').on('keypress', function(e) {
var key = String.fromCharCode(e.which);
if (bindings.hasOwnProperty(key)) {
bindings[key]();
}
});
if (!Session.get('glyph.data')) {
change_glyph('get_next_glyph');
}
refresh_fraction_verified();
});