mirror of
https://github.com/skishore/makemeahanzi.git
synced 2025-11-03 05:48:23 +08:00
Start work on client-side pipeline support
This commit is contained in:
@ -21,3 +21,4 @@ check
|
|||||||
ecmascript
|
ecmascript
|
||||||
meteorhacks:npm
|
meteorhacks:npm
|
||||||
npm-container
|
npm-container
|
||||||
|
less
|
||||||
|
|||||||
@ -29,6 +29,7 @@ http@1.1.1
|
|||||||
id-map@1.0.4
|
id-map@1.0.4
|
||||||
jquery@1.11.4
|
jquery@1.11.4
|
||||||
launch-screen@1.0.4
|
launch-screen@1.0.4
|
||||||
|
less@2.5.0_2
|
||||||
livedata@1.0.15
|
livedata@1.0.15
|
||||||
logging@1.0.8
|
logging@1.0.8
|
||||||
meteor@1.1.7
|
meteor@1.1.7
|
||||||
|
|||||||
35
client/controls.js
vendored
35
client/controls.js
vendored
@ -1,35 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
|
|
||||||
Session.setDefault('controls.show_editor', true);
|
|
||||||
|
|
||||||
Template.content.helpers({
|
|
||||||
show_editor: function() {
|
|
||||||
return Session.get('controls.show_editor');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.controls.events({
|
|
||||||
'click #backup-button': function() {
|
|
||||||
Meteor.call('backup');
|
|
||||||
},
|
|
||||||
'click #restore-button': function() {
|
|
||||||
Meteor.call('restore');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.navbar.helpers({
|
|
||||||
mode: function() {
|
|
||||||
if (Session.get('controls.show_editor')) {
|
|
||||||
return 'editor';
|
|
||||||
}
|
|
||||||
var radical = Session.get('gallery.radical');
|
|
||||||
if (radical === undefined) {
|
|
||||||
return 'all radicals';
|
|
||||||
}
|
|
||||||
return 'radical ' + radical;
|
|
||||||
},
|
|
||||||
percent: function() {
|
|
||||||
var value = Session.get('glyph.fraction_verified');
|
|
||||||
return Math.round(100*(value === undefined ? 0 : value));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@ -1,44 +1,16 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
Session.setDefault('glyph.data', undefined);
|
Session.setDefault('editor.glyph', undefined);
|
||||||
Session.setDefault('glyph.selected_point', undefined);
|
Session.setDefault('glyph.selected_point', undefined);
|
||||||
Session.setDefault('glyph.show_strokes', true);
|
|
||||||
|
|
||||||
var COLORS = ['#0074D9', '#2ECC40', '#FFDC00', '#FF4136', '#7FDBFF',
|
var COLORS = ['#0074D9', '#2ECC40', '#FFDC00', '#FF4136', '#7FDBFF',
|
||||||
'#001F3F', '#39CCCC', '#3D9970', '#01FF70', '#FF851B'];
|
'#001F3F', '#39CCCC', '#3D9970', '#01FF70', '#FF851B'];
|
||||||
var DICTIONARY = 'http://www.archchinese.com/chinese_english_dictionary.html';
|
var DICTIONARY = 'http://www.archchinese.com/chinese_english_dictionary.html';
|
||||||
var EDIT_STROKES = true;
|
|
||||||
|
|
||||||
function change_glyph(method, glyph) {
|
function change_glyph(method, argument) {
|
||||||
glyph = glyph || Session.get('glyph.data');
|
argument = argument || Session.get('editor.glyph');
|
||||||
Meteor.call(method, glyph, function(err, data) {
|
Meteor.call(method, argument, function(err, data) {
|
||||||
Session.set('glyph.data', data);
|
Session.set('editor.glyph', data);
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,7 +48,7 @@ function to_point(pair) {
|
|||||||
var bindings = {
|
var bindings = {
|
||||||
'w': function() {
|
'w': function() {
|
||||||
if (!EDIT_STROKES) {
|
if (!EDIT_STROKES) {
|
||||||
var glyph = Session.get('glyph.data');
|
var glyph = Session.get('editor.glyph');
|
||||||
var character = String.fromCodePoint(parseInt(glyph.name.substr(3), 16));
|
var character = String.fromCodePoint(parseInt(glyph.name.substr(3), 16));
|
||||||
window.open(DICTIONARY + '?find=' + character, '_blank').focus();
|
window.open(DICTIONARY + '?find=' + character, '_blank').focus();
|
||||||
return;
|
return;
|
||||||
@ -84,13 +56,13 @@ var bindings = {
|
|||||||
if (Session.get('glyph.show_strokes')) {
|
if (Session.get('glyph.show_strokes')) {
|
||||||
Session.set('glyph.show_strokes', false);
|
Session.set('glyph.show_strokes', false);
|
||||||
Session.set('glyph.selected_point', undefined);
|
Session.set('glyph.selected_point', undefined);
|
||||||
var glyph = Session.get('glyph.data');
|
var glyph = Session.get('editor.glyph');
|
||||||
glyph.manual.verified = false;
|
glyph.manual.verified = false;
|
||||||
change_glyph('save_glyph', glyph);
|
change_glyph('save_glyph', glyph);
|
||||||
} else {
|
} else {
|
||||||
var glyph = Session.get('glyph.data');
|
var glyph = Session.get('editor.glyph');
|
||||||
delete glyph.manual;
|
delete glyph.manual;
|
||||||
Session.set('glyph.data', fill_glyph_fields(glyph));
|
Session.set('editor.glyph', fill_glyph_fields(glyph));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'a': function() {
|
'a': function() {
|
||||||
@ -103,7 +75,7 @@ var bindings = {
|
|||||||
if (!EDIT_STROKES) {
|
if (!EDIT_STROKES) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var glyph = Session.get('glyph.data');
|
var glyph = Session.get('editor.glyph');
|
||||||
if (!Session.get('glyph.show_strokes')) {
|
if (!Session.get('glyph.show_strokes')) {
|
||||||
Session.set('glyph.show_strokes', true);
|
Session.set('glyph.show_strokes', true);
|
||||||
return;
|
return;
|
||||||
@ -121,13 +93,7 @@ var bindings = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
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({
|
Template.controls.helpers({
|
||||||
w_button_name: function() {
|
w_button_name: function() {
|
||||||
if (!EDIT_STROKES) {
|
if (!EDIT_STROKES) {
|
||||||
@ -179,50 +145,30 @@ Template.glyph.events({
|
|||||||
Session.set('glyph.selected_point', undefined);
|
Session.set('glyph.selected_point', undefined);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
Template.glyph.helpers({
|
Template.editor.helpers({
|
||||||
glyph() {
|
class() {
|
||||||
return !!Session.get('glyph.data');
|
|
||||||
},
|
|
||||||
verified: function() {
|
|
||||||
return false;
|
|
||||||
if (!EDIT_STROKES) {
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
|
||||||
var glyph = Session.get('glyph.data');
|
|
||||||
if (has_errors(glyph)) {
|
|
||||||
return 'error';
|
|
||||||
}
|
|
||||||
return glyph && glyph.manual.verified ? 'verified' : undefined;
|
|
||||||
},
|
},
|
||||||
log: function() {
|
paths() {
|
||||||
return;
|
const glyph = Session.get('editor.glyph');
|
||||||
var glyph = Session.get('glyph.data');
|
if (!glyph) return;
|
||||||
return glyph ? glyph.render.log.map(function(pair) {
|
|
||||||
return {log_class: pair[0], log_message: pair[1]};
|
|
||||||
}) : [];
|
|
||||||
},
|
|
||||||
base_color() {
|
|
||||||
return Session.get('glyph.show_strokes') ? 'black' : 'gray';
|
|
||||||
},
|
|
||||||
d() {
|
|
||||||
return Session.get('glyph.data').stages.path;
|
|
||||||
},
|
|
||||||
show_strokes() {
|
|
||||||
return !!Session.get('glyph.show_strokes');
|
|
||||||
},
|
|
||||||
strokes() {
|
|
||||||
const glyph = Session.get('glyph.data');
|
|
||||||
const result = [];
|
const result = [];
|
||||||
for (let i = 0; i < glyph.stages.strokes.length; i++) {
|
for (let i = 0; i < glyph.stages.strokes.length; i++) {
|
||||||
const stroke = glyph.stages.strokes[i];
|
result.push({
|
||||||
result.push({color: COLORS[i % COLORS.length], stroke: stroke});
|
cls: 'selectable',
|
||||||
|
d: glyph.stages.strokes[i],
|
||||||
|
fill: COLORS[i % COLORS.length],
|
||||||
|
stroke: 'black',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
bridges: function() {
|
lines() {
|
||||||
return;
|
return;
|
||||||
var glyph = Session.get('glyph.data');
|
const glyph = Session.get('editor.glyph');
|
||||||
|
if (!glyph) return;
|
||||||
var original = {};
|
var original = {};
|
||||||
for (var i = 0; i < glyph.render.bridges.length; i++) {
|
for (var i = 0; i < glyph.render.bridges.length; i++) {
|
||||||
var bridge = glyph.render.bridges[i];
|
var bridge = glyph.render.bridges[i];
|
||||||
@ -237,9 +183,10 @@ Template.glyph.helpers({
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
points: function() {
|
points() {
|
||||||
return;
|
return;
|
||||||
var glyph = Session.get('glyph.data');
|
const glyph = Session.get('editor.glyph');
|
||||||
|
if (!glyph) return;
|
||||||
var result = [];
|
var result = [];
|
||||||
for (var i = 0; i < glyph.render.endpoints.length; i++) {
|
for (var i = 0; i < glyph.render.endpoints.length; i++) {
|
||||||
var endpoint = glyph.render.endpoints[i];
|
var endpoint = glyph.render.endpoints[i];
|
||||||
@ -256,6 +203,37 @@ Template.glyph.helpers({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Template.metadata.helpers({
|
||||||
|
character() {
|
||||||
|
const glyph = Session.get('editor.glyph');
|
||||||
|
if (!glyph) return;
|
||||||
|
return glyph.character;
|
||||||
|
},
|
||||||
|
items() {
|
||||||
|
const glyph = Session.get('editor.glyph');
|
||||||
|
if (!glyph) return;
|
||||||
|
const defaults = cjklib.getCharacterData(glyph.character);
|
||||||
|
const fields = ['definition', 'pinyin', 'strokes']
|
||||||
|
return fields.map((x) => ({
|
||||||
|
field: `${x[0].toUpperCase()}${x.substr(1)}:`,
|
||||||
|
value: glyph.metadata[x] || defaults[x] || '(unknown)',
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.status.helpers({
|
||||||
|
stage() {
|
||||||
|
return 'strokes';
|
||||||
|
},
|
||||||
|
lines() {
|
||||||
|
return [
|
||||||
|
{cls: 'success', message: 'asdf'},
|
||||||
|
{cls: 'error', message: 'asdf asdf'},
|
||||||
|
{message: 'asdf asdf asdf asdf asdf asdf asdf asdf asdf asd fasd fasd fasd fas dfa sdfa sdf'},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
Meteor.startup(function() {
|
Meteor.startup(function() {
|
||||||
$('body').on('keypress', function(e) {
|
$('body').on('keypress', function(e) {
|
||||||
var key = String.fromCharCode(e.which);
|
var key = String.fromCharCode(e.which);
|
||||||
@ -263,8 +241,6 @@ Meteor.startup(function() {
|
|||||||
bindings[key]();
|
bindings[key]();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!Session.get('glyph.data')) {
|
cjklib.promise.then(() => change_glyph('get_next_glyph'))
|
||||||
change_glyph('get_next_glyph');
|
.catch(console.error.bind(console));
|
||||||
}
|
|
||||||
refresh_fraction_verified();
|
|
||||||
});
|
});
|
||||||
@ -1,97 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
|
|
||||||
var COLORS = ['#0074D9', '#2ECC40', '#FFDC00', '#FF4136', '#7FDBFF',
|
|
||||||
'#001F3F', '#39CCCC', '#3D9970', '#01FF70', '#FF851B'];
|
|
||||||
var DICTIONARY = 'http://www.archchinese.com/chinese_english_dictionary.html';
|
|
||||||
|
|
||||||
var subscription = undefined;
|
|
||||||
|
|
||||||
function comparison(glyph1, glyph2) {
|
|
||||||
if (glyph1.derived.strokes.length !== glyph2.derived.strokes.length) {
|
|
||||||
return glyph1.derived.strokes.length - glyph2.derived.strokes.length;
|
|
||||||
} else if (glyph1.index.radical !== glyph2.index.radical) {
|
|
||||||
return glyph1.index.radical - glyph2.index.radical;
|
|
||||||
}
|
|
||||||
return glyph1.name < glyph2.name ? -1 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Template.gallery.events({
|
|
||||||
'click svg.radical': function(e) {
|
|
||||||
window.location.hash = this.radical;
|
|
||||||
},
|
|
||||||
'click svg.character': function(e) {
|
|
||||||
var character = String.fromCodePoint(parseInt(this.name.substr(3), 16));
|
|
||||||
window.open(DICTIONARY + '?find=' + character, '_blank').focus();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.gallery.helpers({
|
|
||||||
blocks: function() {
|
|
||||||
if (!Session.get('gallery.ready')) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
var glyphs = Glyphs.find().fetch().sort(comparison);
|
|
||||||
var on_radicals_page = Session.get('gallery.radical') === undefined;
|
|
||||||
var last_num_strokes = -1;
|
|
||||||
var result = [];
|
|
||||||
for (var i = 0; i < glyphs.length; i++) {
|
|
||||||
var glyph = glyphs[i];
|
|
||||||
if (on_radicals_page && i > 0 &&
|
|
||||||
glyph.index.radical === glyphs[i - 1].index.radical) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var strokes = [];
|
|
||||||
if (!on_radicals_page) {
|
|
||||||
for (var j = 0; j < glyph.derived.strokes.length; j++) {
|
|
||||||
var stroke = glyph.derived.strokes[j];
|
|
||||||
strokes.push({color: COLORS[j % COLORS.length], stroke: stroke});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var data = {
|
|
||||||
class: (on_radicals_page ? 'radical' : 'character'),
|
|
||||||
d: Glyphs.get_svg_path(glyph),
|
|
||||||
name: glyph.name,
|
|
||||||
radical: glyph.index.radical,
|
|
||||||
strokes: strokes
|
|
||||||
};
|
|
||||||
var num_strokes =
|
|
||||||
on_radicals_page ? glyph.derived.strokes.length : glyph.index.strokes;
|
|
||||||
if (num_strokes != last_num_strokes) {
|
|
||||||
result.push({
|
|
||||||
glyphs: [],
|
|
||||||
count: num_strokes,
|
|
||||||
show_divider: result.length > 0,
|
|
||||||
});
|
|
||||||
last_num_strokes = num_strokes;
|
|
||||||
}
|
|
||||||
result[result.length - 1].glyphs.push(data);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
loading: function() {
|
|
||||||
return !Session.get('gallery.ready');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
window.onhashchange = function() {
|
|
||||||
var hash = parseInt(window.location.hash.substr(1), 10);
|
|
||||||
if (isNaN(hash)) {
|
|
||||||
Session.set('gallery.radical', undefined);
|
|
||||||
} else {
|
|
||||||
Session.set('gallery.radical', hash);
|
|
||||||
}
|
|
||||||
Session.set('gallery.ready', false);
|
|
||||||
if (subscription) {
|
|
||||||
subscription.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Meteor.startup(function() {
|
|
||||||
window.onhashchange();
|
|
||||||
Tracker.autorun(function() {
|
|
||||||
var radical = Session.get('gallery.radical')
|
|
||||||
subscription = Meteor.subscribe('index', radical, function() {
|
|
||||||
Session.set('gallery.ready', true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -3,17 +3,17 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{{> navbar}}
|
{{> navbar}}
|
||||||
{{> content}}
|
{{> modal}}
|
||||||
{{> progress}}
|
{{> editor}}
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
<template name="navbar">
|
<template name="navbar">
|
||||||
<div id="navbar" class="navbar navbar-default navbar-static-top">
|
<div id="navbar" class="navbar navbar-default navbar-static-top">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="navbar-header">
|
<div class="navbar-header">
|
||||||
<div class="navbar-brand">Hanzi decomposition - {{mode}}</div>
|
<div class="navbar-brand">Hanzi decomposition</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="navbar-progress progress" style="visibility: hidden;">
|
<div class="progress">
|
||||||
<div class="progress-bar progress-bar-warning active"
|
<div class="progress-bar progress-bar-warning active"
|
||||||
role="progressbar" style="width: {{percent}}%"
|
role="progressbar" style="width: {{percent}}%"
|
||||||
aria-valuenow="{{percent}}"
|
aria-valuenow="{{percent}}"
|
||||||
@ -23,12 +23,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template name="progress">
|
<template name="modal">
|
||||||
<div id="progress" class="modal fade" tabIndex="2"
|
<div id="modal" class="modal fade" tabIndex="2"
|
||||||
data-backdrop="static" data-keyboard="false">
|
data-backdrop="static" data-keyboard="false">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-body">Reloading glyph data...</div>
|
<div class="modal-body">{{content}}</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
<div class="progress-bar progress-bar-striped active"
|
<div class="progress-bar progress-bar-striped active"
|
||||||
@ -42,91 +42,56 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template name="content">
|
<template name="editor">
|
||||||
{{#if show_editor}}
|
<div id="editor" class="{{stage}}">
|
||||||
{{> controls}}
|
<div class="left-pane">
|
||||||
{{> glyph}}
|
{{> metadata}}
|
||||||
{{else}}
|
{{> status}}
|
||||||
{{> gallery}}
|
|
||||||
{{/if}}
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template name="controls">
|
|
||||||
<div id="controls" class="btn-group" role="group">
|
|
||||||
<button id="w-button" class="btn btn-default">W: {{w_button_name}}</button>
|
|
||||||
<button id="a-button" class="btn btn-warning">A: Previous</button>
|
|
||||||
<button id="s-button" class="btn btn-success">S: {{s_button_name}}</button>
|
|
||||||
<button id="d-button" class="btn btn-info">D: Next</button>
|
|
||||||
</div>
|
|
||||||
<div id="right-controls" class="btn-group" role="group">
|
|
||||||
<button id="backup-button" class="btn btn-success">Backup</button>
|
|
||||||
<button id="restore-button" class="btn btn-info">Restore</button>
|
|
||||||
<button id="reload-button" class="btn btn-danger">Reload</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template name="glyph">
|
|
||||||
<div id="glyph" class="{{verified}}">
|
|
||||||
<div class="log">
|
|
||||||
{{#each log}}
|
|
||||||
<div class="log-line {{log_class}}">{{log_message}}</div>
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
</div>
|
||||||
<svg viewbox="0 0 1024 1024">
|
<svg viewbox="0 0 1024 1024">
|
||||||
<g transform="scale(1, -1) translate(0, -900)">
|
<g transform="scale(1, -1) translate(0, -900)">
|
||||||
{{#if glyph}}
|
{{#each paths}}
|
||||||
<path fill="{{base_color}}" stroke="{{base_color}}" d="{{d}}"></path>
|
<path class="{{cls}}" fill="{{fill}}"
|
||||||
{{#if show_strokes}}
|
stroke="{{stroke}}" d="{{d}}"></path>
|
||||||
{{#each strokes}}
|
|
||||||
<path class="stroke" fill="{{color}}"
|
|
||||||
stroke="black" d="{{stroke}}"></path>
|
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{else}}
|
{{#each lines}}
|
||||||
{{#each bridges}}
|
<line class="{{cls}}" stroke="{{stroke}}" stroke-width="8"
|
||||||
<line x1={{x1}} y1={{y1}} x2={{x2}} y2={{y2}}
|
x1={{x1}} y1={{y1}} x2={{x2}} y2={{y2}}
|
||||||
stroke="{{color}}" stroke-width="8"
|
|
||||||
data-coordinates="{{coordinates}}"></line>
|
data-coordinates="{{coordinates}}"></line>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{#each points}}
|
{{#each points}}
|
||||||
<circle fill="{{color}}" stroke="{{color}}" cx={{x}} cy={{y}}
|
<circle class="{{cls}}" fill="{{fill}}" stroke="{{stroke}}"
|
||||||
r="8" style="z-index:{{z_index}}"
|
cx={{cx}} cy={{cy}} r="8"
|
||||||
data-coordinates="{{coordinates}}"></circle>
|
data-coordinates="{{coordinates}}"></circle>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template name="gallery">
|
<template name="metadata">
|
||||||
<div id="gallery">
|
<div class="panel panel-primary metadata">
|
||||||
{{#each blocks}}
|
<div class="panel-heading">
|
||||||
{{#if show_divider}}
|
<h3 class="panel-title">Metadata for {{character}}</h3>
|
||||||
<div class="gallery-divider"></div>
|
|
||||||
{{/if}}
|
|
||||||
<div class="gallery-block">
|
|
||||||
<div class="block-header">{{count}}</div>
|
|
||||||
<div class="block-values">
|
|
||||||
{{#each glyphs}}
|
|
||||||
<svg viewbox="0 0 1024 1024"
|
|
||||||
class="{{class}}" data-name="{{name}}">
|
|
||||||
<g transform="scale(1, -1) translate(0, -900)">
|
|
||||||
<path d="{{d}}"></path>
|
|
||||||
{{#each strokes}}
|
|
||||||
<path class="stroke" fill="{{color}}"
|
|
||||||
stroke="black" d="{{stroke}}"></path>
|
|
||||||
{{/each}}
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
</div>
|
||||||
|
{{#each items}}
|
||||||
|
<div class="field">
|
||||||
|
<label class="control-label">{{field}}</label>
|
||||||
|
<span contenteditable="true">{{value}}</span>
|
||||||
</div>
|
</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
{{#if loading}}
|
</template>
|
||||||
<div id="gallery-loader">
|
|
||||||
<div class="whirly-loader"></div>
|
<template name="status">
|
||||||
</div>
|
<div class="panel panel-primary status">
|
||||||
{{/if}}
|
<div class="panel-heading">
|
||||||
|
<h3 class="panel-title">Edit {{stage}}</h3>
|
||||||
|
</div>
|
||||||
|
<ul class="log">
|
||||||
|
{{#each lines}}
|
||||||
|
<li class="line {{cls}}">{{message}}</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -4,6 +4,7 @@ var BATCH_SIZE = 64;
|
|||||||
var CODEPOINTS = [0x2e80, 0x2fdf];
|
var CODEPOINTS = [0x2e80, 0x2fdf];
|
||||||
var FONT_LOADED_PROGRESS = 0.1;
|
var FONT_LOADED_PROGRESS = 0.1;
|
||||||
|
|
||||||
|
/*
|
||||||
Template.controls.events({
|
Template.controls.events({
|
||||||
'click #reload-button': function() {
|
'click #reload-button': function() {
|
||||||
const characters_to_save =
|
const characters_to_save =
|
||||||
@ -11,13 +12,13 @@ Template.controls.events({
|
|||||||
.filter((radical) => !cjklib.gb2312[radical]));
|
.filter((radical) => !cjklib.gb2312[radical]));
|
||||||
const characters_found = new Set;
|
const characters_found = new Set;
|
||||||
|
|
||||||
Session.set('progress.value', 0);
|
Session.set('modal.value', 0);
|
||||||
opentype.load('arphic/UKaiCN.ttf', function(err, font) {
|
opentype.load('arphic/UKaiCN.ttf', function(err, font) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log('Error loading font: ' + err);
|
console.log('Error loading font: ' + err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Session.set('progress.value', FONT_LOADED_PROGRESS);
|
Session.set('modal.value', FONT_LOADED_PROGRESS);
|
||||||
var glyphs_to_save = [];
|
var glyphs_to_save = [];
|
||||||
|
|
||||||
for (var i = 0; i < font.glyphs.length; i++) {
|
for (var i = 0; i < font.glyphs.length; i++) {
|
||||||
@ -40,11 +41,11 @@ Template.controls.events({
|
|||||||
function save_glyphs(glyphs, index) {
|
function save_glyphs(glyphs, index) {
|
||||||
index = index || 0;
|
index = index || 0;
|
||||||
if (index >= glyphs.length) {
|
if (index >= glyphs.length) {
|
||||||
Session.set('progress.value', undefined);
|
Session.set('modal.value', undefined);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var remainder = (1 - FONT_LOADED_PROGRESS)*index/glyphs.length;
|
var remainder = (1 - FONT_LOADED_PROGRESS)*index/glyphs.length;
|
||||||
Session.set('progress.value', remainder + FONT_LOADED_PROGRESS);
|
Session.set('modal.value', remainder + FONT_LOADED_PROGRESS);
|
||||||
var max = Math.min(index + BATCH_SIZE, glyphs.length);
|
var max = Math.min(index + BATCH_SIZE, glyphs.length);
|
||||||
var batch = [];
|
var batch = [];
|
||||||
for (var i = index; i < max; i++) {
|
for (var i = index; i < max; i++) {
|
||||||
@ -54,28 +55,29 @@ function save_glyphs(glyphs, index) {
|
|||||||
Meteor.setTimeout(function() { save_glyphs(glyphs, max); }, 0);
|
Meteor.setTimeout(function() { save_glyphs(glyphs, max); }, 0);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
Template.progress.helpers({
|
Template.modal.helpers({
|
||||||
percent: function() {
|
percent: function() {
|
||||||
var value = Session.get('progress.value');
|
var value = Session.get('modal.value');
|
||||||
return Math.round(100*(value === undefined ? 1 : value));
|
return Math.round(100*(value === undefined ? 1 : value));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Tracker.autorun(function() {
|
Tracker.autorun(function() {
|
||||||
if (Session.get('progress.show')) {
|
if (Session.get('modal.show')) {
|
||||||
$('#progress').modal({background: 'static', keyboard: false});
|
$('#modal').modal({background: 'static', keyboard: false});
|
||||||
} else {
|
} else {
|
||||||
$('#progress').modal('hide');
|
$('#modal').modal('hide');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Tracker.autorun(function() {
|
Tracker.autorun(function() {
|
||||||
var progress = Session.get('progress.value');
|
const value = Session.get('modal.value');
|
||||||
Session.set('progress.show', progress !== undefined);
|
Session.set('modal.show', value !== undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
Meteor.startup(function() {
|
Meteor.startup(function() {
|
||||||
Session.set('progress.show', false);
|
Session.set('modal.show', false);
|
||||||
Session.set('progress.value', undefined);
|
Session.set('modal.value', undefined);
|
||||||
});
|
});
|
||||||
151
client/style.css
151
client/style.css
@ -1,151 +0,0 @@
|
|||||||
.navbar {
|
|
||||||
cursor: default !important;
|
|
||||||
-webkit-user-select: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar .navbar-brand:hover {
|
|
||||||
color: #ffffff !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar .progress {
|
|
||||||
float: right;
|
|
||||||
margin-top: 21px;
|
|
||||||
margin-right: -3px;
|
|
||||||
width: 298px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
outline: 0 !important;
|
|
||||||
padding: 6px 12px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar .progress, #progress .progress {
|
|
||||||
height: 18px;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#controls, #right-controls {
|
|
||||||
position: absolute;
|
|
||||||
top: 72px;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#controls .btn, #right-controls .btn {
|
|
||||||
width: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#controls {
|
|
||||||
left: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#right-controls {
|
|
||||||
right: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#gallery, #gallery-loader, #glyph {
|
|
||||||
position: absolute;
|
|
||||||
top: 60px;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
#gallery-loader, #glyph {
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#gallery-loader {
|
|
||||||
position: fixed !important;
|
|
||||||
top: 0;
|
|
||||||
transform: scale(1.5, 1.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
#gallery-loader .whirly-loader {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
#glyph .log {
|
|
||||||
position: absolute;
|
|
||||||
text-align: left;
|
|
||||||
left: 40px;
|
|
||||||
top: 76px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#glyph .log .log-line {
|
|
||||||
font-size: 20px;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#glyph .log .log-line.error {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
|
|
||||||
#glyph .log .log-line.success {
|
|
||||||
color: green;
|
|
||||||
}
|
|
||||||
|
|
||||||
#glyph.error {
|
|
||||||
background-color: #fdc;
|
|
||||||
}
|
|
||||||
|
|
||||||
#glyph.verified {
|
|
||||||
background-color: #dfc;
|
|
||||||
}
|
|
||||||
|
|
||||||
#glyph svg {
|
|
||||||
height: 100%;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#gallery svg {
|
|
||||||
height: 200px;
|
|
||||||
width: 200px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#gallery .gallery-block {
|
|
||||||
display: inline-block;
|
|
||||||
margin: 40px auto;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
#gallery .gallery-divider {
|
|
||||||
border-top: 1px dotted black;
|
|
||||||
margin: 0 auto;
|
|
||||||
max-width: 1200px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#gallery .gallery-block .block-header {
|
|
||||||
float: left;
|
|
||||||
font-size: 60px;
|
|
||||||
line-height: 200px;
|
|
||||||
margin-right: 80px;
|
|
||||||
text-align: right;
|
|
||||||
width: 80px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#gallery .gallery-block .block-values {
|
|
||||||
float: left;
|
|
||||||
width: 1020px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#gallery svg {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
#gallery svg:hover {
|
|
||||||
background-color: #adf;
|
|
||||||
}
|
|
||||||
|
|
||||||
#gallery svg g path.stroke:hover, #glyph svg g path.stroke:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
stroke: red;
|
|
||||||
stroke-width: 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
#glyph svg g circle:hover, #glyph svg g line:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
fill: blue;
|
|
||||||
stroke: blue;
|
|
||||||
}
|
|
||||||
86
client/style.less
Normal file
86
client/style.less
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
// Bootstrap style overrides.
|
||||||
|
.panel-title {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
height: 18px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Styling for the 'navbar' template.
|
||||||
|
#navbar {
|
||||||
|
cursor: default !important;
|
||||||
|
-webkit-user-select: none !important;
|
||||||
|
|
||||||
|
.navbar-brand:hover {
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
float: right;
|
||||||
|
margin-top: 21px;
|
||||||
|
margin-right: -3px;
|
||||||
|
width: 298px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Styling for the 'editor' template.
|
||||||
|
#editor {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 60px;
|
||||||
|
bottom: 0;
|
||||||
|
|
||||||
|
.left-pane {
|
||||||
|
float: left;
|
||||||
|
width: 680px;
|
||||||
|
|
||||||
|
.metadata, .status {
|
||||||
|
margin: 16px 16px 0 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metadata {
|
||||||
|
.field {
|
||||||
|
margin: 12px 0 4px;
|
||||||
|
|
||||||
|
.control-label {
|
||||||
|
margin-right: 4px;
|
||||||
|
text-align: right;
|
||||||
|
width: 96px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
.log {
|
||||||
|
margin-bottom: 0;
|
||||||
|
|
||||||
|
.line {
|
||||||
|
margin: 6px 0;
|
||||||
|
&.error { color: red; }
|
||||||
|
&.success { color: green; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
float: left;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
path.selectable:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
stroke: blue;
|
||||||
|
stroke-width: 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
circle.selectable:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
fill: blue;
|
||||||
|
stroke: blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user