* Lots of work with fonts: Support mappings of various cterm/SyncTERM fonts.
* Load font info from SAUCE * Better work with defaults & theme values
This commit is contained in:
parent
586f3d60b3
commit
5faa11664b
|
@ -14,6 +14,8 @@ var assert = require('assert');
|
|||
var binary = require('binary');
|
||||
var miscUtil = require('./misc_util.js');
|
||||
|
||||
var _ = require('lodash');
|
||||
|
||||
exports.getFGColorValue = getFGColorValue;
|
||||
exports.getBGColorValue = getBGColorValue;
|
||||
exports.sgr = sgr;
|
||||
|
@ -22,7 +24,7 @@ exports.resetScreen = resetScreen;
|
|||
exports.normal = normal;
|
||||
exports.goHome = goHome;
|
||||
exports.disableVT100LineWrapping = disableVT100LineWrapping;
|
||||
exports.setSyncTermFont = setSyncTermFont;
|
||||
exports.setFont = setFont;
|
||||
exports.fromPipeCode = fromPipeCode;
|
||||
|
||||
|
||||
|
@ -115,6 +117,95 @@ function getBGColorValue(name) {
|
|||
|
||||
// See http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/src/conio/cterm.txt
|
||||
// :TODO: document
|
||||
// :TODO: Create mappings for aliases... maybe make this a map to values instead
|
||||
var FONT_MAP = {
|
||||
// Codepage 437 English
|
||||
'cp437' : 0,
|
||||
'ibmpc' : 0,
|
||||
'ibm_pc' : 0,
|
||||
'ibm_vga' : 0,
|
||||
'pc' : 0,
|
||||
'cp437_art' : 0,
|
||||
'ibmpcart' : 0,
|
||||
'ibmpc_art' : 0,
|
||||
'ibm_pc_art' : 0,
|
||||
'msdos_art' : 0,
|
||||
'msdosart' : 0,
|
||||
'pc_art' : 0,
|
||||
'pcart' : 0,
|
||||
|
||||
// Codepage 1251 Cyrillic, (swiss)
|
||||
'cp1251-swiss' : 1,
|
||||
|
||||
// Russian koi8-r
|
||||
'koi8_r' : 2,
|
||||
'koi8-r' : 2,
|
||||
'koi8r' : 2,
|
||||
|
||||
// ISO-8859-2 Central European
|
||||
'iso8859_2' : 3,
|
||||
'iso8859-2' : 3,
|
||||
|
||||
// ISO-8859-4 Baltic wide (VGA 9bit mapped)
|
||||
'iso8859_4-baltic9b' : 4,
|
||||
|
||||
// Codepage 866 (c) Russian
|
||||
'cp866-c' : 5,
|
||||
|
||||
'iso8859_9' : 6,
|
||||
'haik8' : 7,
|
||||
'iso8859_8' : 8,
|
||||
'koi8_u' : 9,
|
||||
'iso8859_15-thin' : 10,
|
||||
'iso8859_4' : 11,
|
||||
'koi8_r_b' : 12,
|
||||
'iso8859_4-baltic-wide' : 13,
|
||||
'iso8859_5' : 14,
|
||||
'ARMSCII_8' : 15,
|
||||
'iso8859_15' : 16,
|
||||
'cp850' : 17,
|
||||
'cp850-thin' : 18,
|
||||
'cp885-thin' : 19,
|
||||
'cp1251' : 20,
|
||||
'iso8859_7' : 21,
|
||||
'koi8-r_c' : 22,
|
||||
'iso8859_4-baltic' : 23,
|
||||
'iso8859_1' : 24,
|
||||
'cp866' : 25,
|
||||
'cp437-thin' : 26,
|
||||
'cp866-b' : 27,
|
||||
'cp885' : 28,
|
||||
'cp866_u' : 29,
|
||||
'iso8859_1-thin' : 30,
|
||||
'cp1131' : 31,
|
||||
'c64_upper' : 32,
|
||||
'c64_lower' : 33,
|
||||
'c128_upper' : 34,
|
||||
'c128_lower' : 35,
|
||||
|
||||
'atari' : 36,
|
||||
'atarist' : 36,
|
||||
|
||||
'pot_noodle' : 37,
|
||||
'p0tnoodle' : 37,
|
||||
|
||||
'mo_soul' : 38,
|
||||
'mosoul' : 38,
|
||||
'mO\'sOul' : 38,
|
||||
|
||||
'microknight_plus' : 39,
|
||||
|
||||
'topaz_plus' : 40,
|
||||
'topazplus' : 40,
|
||||
'amiga_topaz_2+' : 40,
|
||||
'topaz2plus' : 40,
|
||||
|
||||
'microknight' : 41,
|
||||
'topaz' : 42,
|
||||
|
||||
};
|
||||
|
||||
|
||||
var SYNC_TERM_FONTS = [
|
||||
'cp437',
|
||||
'cp1251',
|
||||
|
@ -155,8 +246,10 @@ var SYNC_TERM_FONTS = [
|
|||
'atari',
|
||||
'pot_noodle',
|
||||
'mo_soul',
|
||||
'microknight',
|
||||
'topaz'
|
||||
'microknight_plus',
|
||||
'topaz_plus',
|
||||
'microknight',
|
||||
'topaz',
|
||||
];
|
||||
|
||||
// Create methods such as up(), nextLine(),...
|
||||
|
@ -242,7 +335,12 @@ function disableVT100LineWrapping() {
|
|||
return ESC_CSI + '7l';
|
||||
}
|
||||
|
||||
function setSyncTermFont(name, fontPage) {
|
||||
//
|
||||
// See http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/src/conio/cterm.txt
|
||||
//
|
||||
// :TODO: allow full spec here.
|
||||
/*
|
||||
function setFont(name, fontPage) {
|
||||
fontPage = miscUtil.valueWithDefault(fontPage, 0);
|
||||
|
||||
assert(fontPage === 0 || fontPage === 1); // see spec
|
||||
|
@ -253,6 +351,22 @@ function setSyncTermFont(name, fontPage) {
|
|||
}
|
||||
return '';
|
||||
}
|
||||
*/
|
||||
|
||||
function setFont(name, fontPage) {
|
||||
name = name.toLowerCase().replace(/ /g, '_'); // conform to map
|
||||
|
||||
var p1 = miscUtil.valueWithDefault(fontPage, 0);
|
||||
|
||||
assert(p1 >= 0 && p1 <= 3);
|
||||
|
||||
var p2 = FONT_MAP[name];
|
||||
if(_.isNumber(p2)) {
|
||||
return ESC_CSI + p1 + ';' + p2 + ' D';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
// Also add:
|
||||
// * fromRenegade(): |<0-23>
|
||||
|
|
30
core/art.js
30
core/art.js
|
@ -189,6 +189,12 @@ function parseCharacterSAUCE(sauce) {
|
|||
return result;
|
||||
}
|
||||
|
||||
function getFontNameFromSAUCE(sauce) {
|
||||
if(sauce.Character) {
|
||||
return sauce.Character.fontName;
|
||||
}
|
||||
}
|
||||
|
||||
function sliceAtEOF(data, eofMarker) {
|
||||
var eof = data.length;
|
||||
// :TODO: max scan back or other beter way of doing this?!
|
||||
|
@ -213,7 +219,7 @@ function getArtFromPath(path, options, cb) {
|
|||
//
|
||||
var ext = paths.extname(path).toLowerCase();
|
||||
var encoding = options.encodedAs || defaultEncodingFromExtension(ext);
|
||||
|
||||
|
||||
// :TODO: how are BOM's currently handled if present? Are they removed? Do we need to?
|
||||
|
||||
function sliceOfData() {
|
||||
|
@ -248,12 +254,14 @@ function getArtFromPath(path, options, cb) {
|
|||
// the information provided by SAUCE, use that.
|
||||
//
|
||||
if(!options.encodedAs) {
|
||||
/*
|
||||
if(sauce.Character && sauce.Character.fontName) {
|
||||
var enc = SAUCE_FONT_TO_ENCODING_HINT[sauce.Character.fontName];
|
||||
if(enc) {
|
||||
encoding = enc;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
cb(null, getResult(sauce));
|
||||
}
|
||||
|
@ -469,6 +477,26 @@ function display(options, cb) {
|
|||
}
|
||||
});
|
||||
|
||||
// :TODO: If options.font, set the font via ANSI
|
||||
// ...this should come from sauce, be passed in, or defaulted
|
||||
|
||||
var ansiFont = '';
|
||||
if(options.font) {
|
||||
// :TODO: how to set to ignore SAUCE?
|
||||
ansiFont = ansi.setFont(options.font);
|
||||
} else if(options.sauce) {
|
||||
var fontName = getFontNameFromSAUCE(options.sauce);
|
||||
|
||||
if(fontName) {
|
||||
ansiFont = ansi.setFont(fontName);
|
||||
}
|
||||
}
|
||||
|
||||
if(ansiFont.length > 1) {
|
||||
options.client.term.write(ansiFont);
|
||||
}
|
||||
|
||||
|
||||
if(iceColors) {
|
||||
options.client.term.write(ansi.blinkToBrightIntensity());
|
||||
}
|
||||
|
|
|
@ -29,6 +29,6 @@ ButtonView.prototype.onKeyPress = function(key, isSpecial) {
|
|||
}
|
||||
};
|
||||
|
||||
ButtonView.prototype.getViewData = function() {
|
||||
ButtonView.prototype.getData = function() {
|
||||
return null;
|
||||
};
|
||||
|
|
|
@ -79,10 +79,11 @@ function Client(input, output) {
|
|||
|
||||
var self = this;
|
||||
|
||||
this.input = input;
|
||||
this.output = output;
|
||||
this.term = new term.ClientTerminal(this.output);
|
||||
this.user = new user.User();
|
||||
this.input = input;
|
||||
this.output = output;
|
||||
this.term = new term.ClientTerminal(this.output);
|
||||
this.user = new user.User();
|
||||
this.currentThemeInfo = { name : 'N/A', description : 'None' };
|
||||
|
||||
//
|
||||
// Peek at |data| and emit for any specialized handling
|
||||
|
|
|
@ -5,6 +5,8 @@ var fs = require('fs');
|
|||
var paths = require('path');
|
||||
var miscUtil = require('./misc_util.js');
|
||||
|
||||
// :TODO: it would be nice to allow for defaults here & .json file only overrides -- e.g. merge the two
|
||||
|
||||
module.exports = {
|
||||
defaultPath : function() {
|
||||
var base = miscUtil.resolvePath('~/');
|
||||
|
@ -15,6 +17,7 @@ module.exports = {
|
|||
|
||||
initFromFile : function(path, cb) {
|
||||
var data = fs.readFileSync(path, 'utf8');
|
||||
// :TODO: strip comments
|
||||
this.config = JSON.parse(data);
|
||||
},
|
||||
|
||||
|
@ -32,7 +35,11 @@ module.exports = {
|
|||
usernameMax : 22,
|
||||
passwordMin : 6,
|
||||
requireActivation : true, // require SysOp activation?
|
||||
defaultTheme : 'NU-MAYA',
|
||||
},
|
||||
|
||||
defaults : {
|
||||
theme : 'NU-MAYA',
|
||||
passwordChar : '*',
|
||||
},
|
||||
|
||||
paths : {
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
'use strict';
|
||||
|
||||
var ansi = require('./ansi_term.js');
|
||||
var artwork = require('./art.js');
|
||||
//var artwork = require('./art.js');
|
||||
var theme = require('./theme.js');
|
||||
var moduleUtil = require('./module_util.js');
|
||||
var Log = require('./logger.js').log;
|
||||
var Config = require('./config.js').config;
|
||||
|
@ -74,7 +75,23 @@ function connectEntry(client) {
|
|||
setTimeout(function onTimeout() {
|
||||
term.write(ansi.clearScreen());
|
||||
|
||||
artwork.getArt('CONNECT', { random : true, readSauce : true }, function onArt(err, art) {
|
||||
|
||||
var dispOptions = {
|
||||
name : 'CONNECT',
|
||||
client : client,
|
||||
};
|
||||
|
||||
// :TODO: if connect.js were a MenuModule, MCI/etc. would function here!
|
||||
// ... could also avoid some of the boilerplate code
|
||||
theme.displayThemeArt(dispOptions, function artDisplayed(err) {
|
||||
var timeout = err ? 0 : 2000;
|
||||
|
||||
setTimeout(function timeout() {
|
||||
client.gotoMenuModule( { name : Config.entryMod } );
|
||||
}, timeout);
|
||||
});
|
||||
|
||||
/*artwork.getArt('CONNECT', { random : true, readSauce : true }, function onArt(err, art) {
|
||||
var timeout = 0;
|
||||
|
||||
if(!err) {
|
||||
|
@ -88,6 +105,7 @@ function connectEntry(client) {
|
|||
client.gotoMenuModule({ name : Config.entryMod } );
|
||||
}, timeout);
|
||||
});
|
||||
*/
|
||||
}, 500);
|
||||
}
|
||||
|
||||
|
|
|
@ -100,20 +100,9 @@ MCIViewFactory.prototype.createFromMCI = function(mci) {
|
|||
setOption(1, 'textStyle');
|
||||
|
||||
if(options.textStyle === 'P') {
|
||||
// Supply from theme, if available
|
||||
// :TODO: Move this logic elsewhere
|
||||
if(this.client.currentThemeInfo && this.client.currentThemeInfo.config) {
|
||||
var themePwChar = this.client.currentThemeInfo.config.passwordChar;
|
||||
if(_.isString(themePwChar)) {
|
||||
options.textMaskChar = themePwChar.substr(0, 1);
|
||||
} else if(_.isNumber(themePwChar)) {
|
||||
options.textMaskChar = String.fromCharCode(themePwChar);
|
||||
} else {
|
||||
options.textMaskChar = '*';
|
||||
}
|
||||
} else {
|
||||
options.textMaskChar = '*';
|
||||
}
|
||||
// Assign the proper password character / text mask
|
||||
assert(_.isObject(this.client.currentThemeInfo));
|
||||
options.textMaskChar = this.client.currentThemeInfo.getPasswordChar();
|
||||
}
|
||||
|
||||
setFocusOption(0, 'focusTextStyle');
|
||||
|
|
|
@ -27,8 +27,14 @@ function MenuModule(options) {
|
|||
self.beforeArt();
|
||||
callback(null);
|
||||
},
|
||||
function displayArt(callback) {
|
||||
theme.displayThemeArt(self.menuConfig.art, self.client, function onArt(err, mciMap) {
|
||||
function displayArt(callback) {
|
||||
var dispOptions = {
|
||||
name : self.menuConfig.art,
|
||||
font : self.menuConfig.font,
|
||||
client : self.client,
|
||||
};
|
||||
|
||||
theme.displayThemeArt(dispOptions, function onArt(err, mciMap) {
|
||||
// :TODO: If the art simply is not found, or failed to load... we need to continue
|
||||
if(err) {
|
||||
console.log('TODO: log this error properly... maybe handle slightly diff.');
|
||||
|
|
|
@ -80,7 +80,7 @@ TextView.prototype.setFocus = function(focused) {
|
|||
this.client.term.write(this.getANSIColor(this.getFocusColor()));
|
||||
};
|
||||
|
||||
TextView.prototype.getViewData = function() {
|
||||
TextView.prototype.getData = function() {
|
||||
return this.text;
|
||||
};
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ var fs = require('fs');
|
|||
var paths = require('path');
|
||||
var async = require('async');
|
||||
var _ = require('lodash');
|
||||
var assert = require('assert');
|
||||
|
||||
exports.getThemeInfo = getThemeInfo;
|
||||
exports.getThemeArt = getThemeArt;
|
||||
|
@ -27,6 +28,22 @@ function getThemeInfo(themeID, cb) {
|
|||
try {
|
||||
// :TODO: strip comments/etc. ala menu.json
|
||||
var info = JSON.parse(data);
|
||||
|
||||
//
|
||||
// Create some handy helpers
|
||||
//
|
||||
info.getPasswordChar = function() {
|
||||
var pwChar = Config.defaults.passwordChar;
|
||||
if(_.isObject(info.config)) {
|
||||
if(_.isString(info.config.passwordChar)) {
|
||||
pwChar = info.config.passwordChar.substr(0, 1);
|
||||
} else if(_.isNumber(info.config.passwordChar)) {
|
||||
pwChar = String.fromCharCode(info.config.passwordChar);
|
||||
}
|
||||
}
|
||||
return pwChar;
|
||||
};
|
||||
|
||||
cb(null, info);
|
||||
} catch(e) {
|
||||
cb(err);
|
||||
|
@ -116,8 +133,12 @@ function getThemeArt(name, themeID, options, cb) {
|
|||
});
|
||||
}
|
||||
|
||||
function displayThemeArt(name, client, cb) {
|
||||
getThemeArt(name, client.user.properties.art_theme_id, function onArt(err, artInfo) {
|
||||
function displayThemeArt(options, cb) {
|
||||
assert(_.isObject(options));
|
||||
assert(_.isObject(options.client));
|
||||
assert(_.isString(options.name));
|
||||
|
||||
getThemeArt(options.name, options.client.user.properties.art_theme_id, function onArt(err, artInfo) {
|
||||
if(err) {
|
||||
cb(err);
|
||||
} else {
|
||||
|
@ -128,7 +149,16 @@ function displayThemeArt(name, client, cb) {
|
|||
}
|
||||
}
|
||||
|
||||
art.display( { art : artInfo.data, client : client, iceColors : iceColors }, function onDisplayed(err, mci) {
|
||||
var dispOptions = {
|
||||
art : artInfo.data,
|
||||
sauce : artInfo.sauce,
|
||||
client : options.client,
|
||||
iceColors : iceColors,
|
||||
font : options.font,
|
||||
};
|
||||
|
||||
|
||||
art.display(dispOptions, function onDisplayed(err, mci) {
|
||||
cb(err, mci, artInfo);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -112,7 +112,7 @@ VerticalMenuView.prototype.onSpecialKeyPress = function(keyName) {
|
|||
VerticalMenuView.super_.prototype.onSpecialKeyPress.call(this, keyName);
|
||||
};
|
||||
|
||||
VerticalMenuView.prototype.getViewData = function() {
|
||||
VerticalMenuView.prototype.getData = function() {
|
||||
return this.focusedItemIndex;
|
||||
};
|
||||
|
||||
|
|
|
@ -187,5 +187,5 @@ View.prototype.onSpecialKeyPress = function(keyName) {
|
|||
}
|
||||
};
|
||||
|
||||
View.prototype.getViewData = function() {
|
||||
View.prototype.getData = function() {
|
||||
};
|
|
@ -86,7 +86,7 @@ function ViewController(options) {
|
|||
var viewData;
|
||||
for(var id in self.views) {
|
||||
try {
|
||||
viewData = self.views[id].getViewData();
|
||||
viewData = self.views[id].getData();
|
||||
if(typeof viewData !== 'undefined') {
|
||||
formData.value[id] = viewData;
|
||||
}
|
||||
|
@ -315,7 +315,7 @@ ViewController.prototype.loadFromMCIMapAndConfig = function(options, cb) {
|
|||
//
|
||||
if(_.isObject(formConfig.submit)) {
|
||||
// :TODO: If this model is kept, formData does not need to include actual data, just form ID & submitID
|
||||
// we can get the rest here via each view in form -> getViewData()
|
||||
// we can get the rest here via each view in form -> getData()
|
||||
self.on('submit', function onSubmit(formData) {
|
||||
Log.debug( { formData : formData }, 'Submit form');
|
||||
|
||||
|
@ -419,7 +419,7 @@ ViewController.prototype.formatMCIString = function(format) {
|
|||
return match;
|
||||
}
|
||||
|
||||
return view.getViewData();
|
||||
return view.getData();
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ function ApplyModule(menuConfig) {
|
|||
affiliation : args.affils,
|
||||
email_address : args.email,
|
||||
web_address : args.web,
|
||||
art_theme_id : Config.users.defaultTheme, // :TODO: allow '*' = random
|
||||
art_theme_id : Config.defaults.theme, // :TODO: allow '*' = random
|
||||
account_status : user.User.AccountStatus.inactive,
|
||||
|
||||
// :TODO: Other defaults
|
||||
|
|
|
@ -167,6 +167,7 @@
|
|||
"newUserActive" : {
|
||||
"art" : "NEWACT",
|
||||
"options" : {
|
||||
// :TODO: implement MCI codes for this
|
||||
"clearScreen" : true
|
||||
},
|
||||
"form" : {
|
||||
|
|
Loading…
Reference in New Issue