Parse user-defined glyphs in CSS

This commit is contained in:
Ian Prest 2015-08-01 00:39:02 -04:00
parent 0bf77c0d8b
commit 900caca4a7
3 changed files with 86 additions and 73 deletions

View File

@ -88,6 +88,8 @@ Nav Bar / Header
<a class="dropdown-toggle" dropdown-toggle><i class="fa fa-font"></i> Character Picker <b class="caret"></b></a>
<ul class="dropdown-menu">
<li ng-repeat="picker in pickers"><a ng-click="loadCharacterPicker(picker)" href="#">{{picker.name}}</a></li>
<li class="divider"></li>
<li><a ng-click="loadCharacterPicker(null)" href="#">User-Defined Glyphs</a></li>
</ul>
</li>
</ul>

59
kb.js
View File

@ -214,6 +214,15 @@
$scope.kbHeight = bottom;
};
function updateFromCss(css) {
var rules = $cssParser.parse(css);
$scope.customGlyphs = $renderKey.getGlyphsFromRules(rules); // glyphs first, before rules are modified!
$scope.customStyles = $sce.trustAsHtml($renderKey.sanitizeCssRules(rules));
if($scope.picker.sentinel === userGlyphsSentinel) {
$scope.picker.glyphs = $scope.customGlyphs;
}
}
// Given a key, generate the HTML needed to render it
function renderKey(key) {
key.html = $sce.trustAsHtml($renderKey.html(key,$sanitize));
@ -226,7 +235,7 @@
renderKey(key);
});
$scope.meta = angular.copy($scope.keyboard.meta);
$scope.customStyles = $sce.trustAsHtml($renderKey.renderCSS($scope.meta.css));
updateFromCss($scope.meta.css || '');
};
function updateSerialized() {
@ -584,46 +593,23 @@
$scope.calcKbHeight();
};
function parsePickerCSS(css) {
// Parse the CSS
var rules = $cssParser.parse(css);
// Find rules that look like the base slyph-set definition
var classes = [];
rules.forEach(function(rule) {
if(!rule.name && rule.selector.length === 1 && rule.selector[0].match(/^\.[a-zA-Z0-9]+$/)) {
classes.push(rule.selector[0].substring(1));
}
});
// Find rules that look like glyphs
var glyphs = [];
rules.forEach(function(rule) {
if(!rule.name && rule.selector.length === 1) {
var matches = rule.selector[0].match(/^\.([a-zA-Z0-9]+)-([-a-zA-Z0-9]+)\:(before|after)$/);
if(matches) {
var theClass = classes.indexOf(matches[1]);
if(theClass != -1) {
var glyph = { name: matches[2], html: "<i class='" + classes[theClass] + " " + matches[1]+"-"+matches[2] +"'></i>" };
glyphs.push(glyph);
}
}
}
});
glyphs.sort(function(a,b) { return a.name.localeCompare(b.name); });
return glyphs;
}
var userGlyphsSentinel = {};
$scope.loadCharacterPicker = function(picker) {
$scope.picker = picker;
$scope.picker = picker || {
name: "User-Defined Glyphs",
glyphs: $scope.customGlyphs,
href: "https://github.com/ijprest/keyboard-layout-editor/wiki/Custom-Styles",
description: "This list will show any glyphs defined in your layout's 'Custom Styles' tab. See the Commodore VIC-20 sample layout for an example.",
sentinel: userGlyphsSentinel
};
$scope.palette = {}; // turn off the palette
$scope.pickerFilter = '';
$scope.pickerSelection = {};
// Load the CSS if necessary
if(picker.css && !picker.glyphs) {
$http.get(picker.css).success(function(css) {
picker.glyphs = parsePickerCSS(css);
if($scope.picker.css && !$scope.picker.glyphs) {
$http.get($scope.picker.css).success(function(css) {
$scope.picker.glyphs = $renderKey.getGlyphsFromRules($cssParser.parse(css));
});
}
};
@ -763,6 +749,7 @@
$scope.customStylesException = "";
$scope.customStyles = "";
$scope.customGlyphs = [];
$scope.updateCustomStyles = function() {
if(customStylesTimer) {
$timeout.cancel(customStylesTimer);
@ -771,7 +758,7 @@
try {
$scope.customStylesException = "";
transaction("customstyles", function() {
$scope.customStyles = $sce.trustAsHtml($renderKey.renderCSS($scope.meta.css));
updateFromCss($scope.meta.css);
$scope.updateMeta('css');
});
} catch(e) {

View File

@ -199,45 +199,69 @@ var $renderKey = (typeof(exports) !== 'undefined') ? exports : {};
});
};
$renderKey.renderCSS = function(css) {
if(css) {
var rules = $cssParser.parse(css);
if(rules) {
// Sanitize the CSS
rules.forEach(function(rule) {
if(!rule.name) {
for(var i = 0; i < rule.selector.length; ++i) {
rule.selector[i] = "#keyboard .keycap " + rule.selector[i];
}
}
})
// Re-stringify the sanitized CSS
css = "";
rules.forEach(function(rule) {
if(!rule.name) {
css += rule.selector.join(', ') + " { ";
if(rule.decls) {
for(var i = 0; i < rule.decls.length; ++i) {
css += rule.decls[i][0] + ": " + rule.decls[i][1] + "; ";
}
}
css += "}\n";
} else {
var ok = (rule.name === "@font-face")
|| (rule.name === "@import" && !rule.content && rule.selector.match(/^url\(http:\/\/fonts.googleapis.com\/css\?family=[^\)]+\)$/));
if(ok) {
css += rule.name;
if(rule.selector) css += ' ' + rule.selector;
if(rule.content) css += '{ ' + rule.content + ' }\n';
else css += ';\n';
}
}
});
return css;
$renderKey.getGlyphsFromRules = function(rules) {
// Find rules that look like the base slyph-set definition
var classes = [];
rules.forEach(function(rule) {
if(!rule.name && rule.selector.length === 1 && rule.selector[0].match(/^\.[a-zA-Z0-9]+$/)) {
classes.push(rule.selector[0].substring(1));
}
});
// Find rules that look like glyphs
var glyphs = [];
rules.forEach(function(rule) {
if(!rule.name && rule.selector.length === 1) {
var matches = rule.selector[0].match(/^\.([a-zA-Z0-9]+)-([-a-zA-Z0-9]+)\:(before|after)$/);
if(matches) {
var theClass = classes.indexOf(matches[1]);
if(theClass != -1) {
var glyph = { name: matches[2], html: "<i class='" + classes[theClass] + " " + matches[1]+"-"+matches[2] +"'></i>" };
glyphs.push(glyph);
}
}
}
});
glyphs.sort(function(a,b) { return a.name.localeCompare(b.name); });
return glyphs;
}
$renderKey.sanitizeCssRules = function(rules) {
if(rules) {
// Sanitize the CSS
rules.forEach(function(rule) {
if(!rule.name) {
for(var i = 0; i < rule.selector.length; ++i) {
rule.selector[i] = "#keyboard .keycap " + rule.selector[i] + ", #glyphScroller " + rule.selector[i];
}
}
})
// Re-stringify the sanitized CSS
var css = "";
rules.forEach(function(rule) {
if(!rule.name) {
css += rule.selector.join(', ') + " { ";
if(rule.decls) {
for(var i = 0; i < rule.decls.length; ++i) {
css += rule.decls[i][0] + ": " + rule.decls[i][1] + "; ";
}
}
css += "}\n";
} else {
var ok = (rule.name === "@font-face")
|| (rule.name === "@import" && !rule.content && rule.selector.match(/^url\(http:\/\/fonts.googleapis.com\/css\?family=[^\)]+\)$/));
if(ok) {
css += rule.name;
if(rule.selector) css += ' ' + rule.selector;
if(rule.content) css += '{ ' + rule.content + ' }\n';
else css += ';\n';
}
}
});
return css;
}
return "";
};
}
}());