mirror of
				https://github.com/skishore/makemeahanzi.git
				synced 2025-10-31 19:07:07 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			271 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			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();
 | |
| });
 | 
