feat: support CJK & ANS spacing
This commit is contained in:
parent
25cfc2229a
commit
20c70557b5
12 changed files with 1028 additions and 49 deletions
751
_site/heti-addon.debug.js
Normal file
751
_site/heti-addon.debug.js
Normal file
|
@ -0,0 +1,751 @@
|
||||||
|
(function (global, factory) {
|
||||||
|
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
||||||
|
typeof define === 'function' && define.amd ? define(factory) :
|
||||||
|
(global = global || self, global.Heti = factory());
|
||||||
|
}(this, (function () { 'use strict';
|
||||||
|
|
||||||
|
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
|
||||||
|
|
||||||
|
function createCommonjsModule(fn, module) {
|
||||||
|
return module = { exports: {} }, fn(module, module.exports), module.exports;
|
||||||
|
}
|
||||||
|
|
||||||
|
var findAndReplaceDOMText = createCommonjsModule(function (module) {
|
||||||
|
/**
|
||||||
|
* findAndReplaceDOMText v 0.4.6
|
||||||
|
* @author James Padolsey http://james.padolsey.com
|
||||||
|
* @license http://unlicense.org/UNLICENSE
|
||||||
|
*
|
||||||
|
* Matches the text of a DOM node against a regular expression
|
||||||
|
* and replaces each match (or node-separated portions of the match)
|
||||||
|
* in the specified element.
|
||||||
|
*/
|
||||||
|
(function (root, factory) {
|
||||||
|
if ( module.exports) {
|
||||||
|
// Node/CommonJS
|
||||||
|
module.exports = factory();
|
||||||
|
} else {
|
||||||
|
// Browser globals
|
||||||
|
root.findAndReplaceDOMText = factory();
|
||||||
|
}
|
||||||
|
}(commonjsGlobal, function factory() {
|
||||||
|
|
||||||
|
var PORTION_MODE_RETAIN = 'retain';
|
||||||
|
var PORTION_MODE_FIRST = 'first';
|
||||||
|
|
||||||
|
var doc = document;
|
||||||
|
var hasOwn = {}.hasOwnProperty;
|
||||||
|
|
||||||
|
function escapeRegExp(s) {
|
||||||
|
return String(s).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
|
||||||
|
}
|
||||||
|
|
||||||
|
function exposed() {
|
||||||
|
// Try deprecated arg signature first:
|
||||||
|
return deprecated.apply(null, arguments) || findAndReplaceDOMText.apply(null, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
function deprecated(regex, node, replacement, captureGroup, elFilter) {
|
||||||
|
if ((node && !node.nodeType) && arguments.length <= 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var isReplacementFunction = typeof replacement == 'function';
|
||||||
|
|
||||||
|
if (isReplacementFunction) {
|
||||||
|
replacement = (function(original) {
|
||||||
|
return function(portion, match) {
|
||||||
|
return original(portion.text, match.startIndex);
|
||||||
|
};
|
||||||
|
}(replacement));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Awkward support for deprecated argument signature (<0.4.0)
|
||||||
|
var instance = findAndReplaceDOMText(node, {
|
||||||
|
|
||||||
|
find: regex,
|
||||||
|
|
||||||
|
wrap: isReplacementFunction ? null : replacement,
|
||||||
|
replace: isReplacementFunction ? replacement : '$' + (captureGroup || '&'),
|
||||||
|
|
||||||
|
prepMatch: function(m, mi) {
|
||||||
|
|
||||||
|
// Support captureGroup (a deprecated feature)
|
||||||
|
|
||||||
|
if (!m[0]) throw 'findAndReplaceDOMText cannot handle zero-length matches';
|
||||||
|
|
||||||
|
if (captureGroup > 0) {
|
||||||
|
var cg = m[captureGroup];
|
||||||
|
m.index += m[0].indexOf(cg);
|
||||||
|
m[0] = cg;
|
||||||
|
}
|
||||||
|
|
||||||
|
m.endIndex = m.index + m[0].length;
|
||||||
|
m.startIndex = m.index;
|
||||||
|
m.index = mi;
|
||||||
|
|
||||||
|
return m;
|
||||||
|
},
|
||||||
|
filterElements: elFilter
|
||||||
|
});
|
||||||
|
|
||||||
|
exposed.revert = function() {
|
||||||
|
return instance.revert();
|
||||||
|
};
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* findAndReplaceDOMText
|
||||||
|
*
|
||||||
|
* Locates matches and replaces with replacementNode
|
||||||
|
*
|
||||||
|
* @param {Node} node Element or Text node to search within
|
||||||
|
* @param {RegExp} options.find The regular expression to match
|
||||||
|
* @param {String|Element} [options.wrap] A NodeName, or a Node to clone
|
||||||
|
* @param {String} [options.wrapClass] A classname to append to the wrapping element
|
||||||
|
* @param {String|Function} [options.replace='$&'] What to replace each match with
|
||||||
|
* @param {Function} [options.filterElements] A Function to be called to check whether to
|
||||||
|
* process an element. (returning true = process element,
|
||||||
|
* returning false = avoid element)
|
||||||
|
*/
|
||||||
|
function findAndReplaceDOMText(node, options) {
|
||||||
|
return new Finder(node, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
exposed.NON_PROSE_ELEMENTS = {
|
||||||
|
br:1, hr:1,
|
||||||
|
// Media / Source elements:
|
||||||
|
script:1, style:1, img:1, video:1, audio:1, canvas:1, svg:1, map:1, object:1,
|
||||||
|
// Input elements
|
||||||
|
input:1, textarea:1, select:1, option:1, optgroup: 1, button:1
|
||||||
|
};
|
||||||
|
|
||||||
|
exposed.NON_CONTIGUOUS_PROSE_ELEMENTS = {
|
||||||
|
|
||||||
|
// Elements that will not contain prose or block elements where we don't
|
||||||
|
// want prose to be matches across element borders:
|
||||||
|
|
||||||
|
// Block Elements
|
||||||
|
address:1, article:1, aside:1, blockquote:1, dd:1, div:1,
|
||||||
|
dl:1, fieldset:1, figcaption:1, figure:1, footer:1, form:1, h1:1, h2:1, h3:1,
|
||||||
|
h4:1, h5:1, h6:1, header:1, hgroup:1, hr:1, main:1, nav:1, noscript:1, ol:1,
|
||||||
|
output:1, p:1, pre:1, section:1, ul:1,
|
||||||
|
// Other misc. elements that are not part of continuous inline prose:
|
||||||
|
br:1, li: 1, summary: 1, dt:1, details:1, rp:1, rt:1, rtc:1,
|
||||||
|
// Media / Source elements:
|
||||||
|
script:1, style:1, img:1, video:1, audio:1, canvas:1, svg:1, map:1, object:1,
|
||||||
|
// Input elements
|
||||||
|
input:1, textarea:1, select:1, option:1, optgroup:1, button:1,
|
||||||
|
// Table related elements:
|
||||||
|
table:1, tbody:1, thead:1, th:1, tr:1, td:1, caption:1, col:1, tfoot:1, colgroup:1
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
exposed.NON_INLINE_PROSE = function(el) {
|
||||||
|
return hasOwn.call(exposed.NON_CONTIGUOUS_PROSE_ELEMENTS, el.nodeName.toLowerCase());
|
||||||
|
};
|
||||||
|
|
||||||
|
// Presets accessed via `options.preset` when calling findAndReplaceDOMText():
|
||||||
|
exposed.PRESETS = {
|
||||||
|
prose: {
|
||||||
|
forceContext: exposed.NON_INLINE_PROSE,
|
||||||
|
filterElements: function(el) {
|
||||||
|
return !hasOwn.call(exposed.NON_PROSE_ELEMENTS, el.nodeName.toLowerCase());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exposed.Finder = Finder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finder -- encapsulates logic to find and replace.
|
||||||
|
*/
|
||||||
|
function Finder(node, options) {
|
||||||
|
|
||||||
|
var preset = options.preset && exposed.PRESETS[options.preset];
|
||||||
|
|
||||||
|
options.portionMode = options.portionMode || PORTION_MODE_RETAIN;
|
||||||
|
|
||||||
|
if (preset) {
|
||||||
|
for (var i in preset) {
|
||||||
|
if (hasOwn.call(preset, i) && !hasOwn.call(options, i)) {
|
||||||
|
options[i] = preset[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.node = node;
|
||||||
|
this.options = options;
|
||||||
|
|
||||||
|
// Enable match-preparation method to be passed as option:
|
||||||
|
this.prepMatch = options.prepMatch || this.prepMatch;
|
||||||
|
|
||||||
|
this.reverts = [];
|
||||||
|
|
||||||
|
this.matches = this.search();
|
||||||
|
|
||||||
|
if (this.matches.length) {
|
||||||
|
this.processMatches();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Finder.prototype = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches for all matches that comply with the instance's 'match' option
|
||||||
|
*/
|
||||||
|
search: function() {
|
||||||
|
|
||||||
|
var match;
|
||||||
|
var matchIndex = 0;
|
||||||
|
var offset = 0;
|
||||||
|
var regex = this.options.find;
|
||||||
|
var textAggregation = this.getAggregateText();
|
||||||
|
var matches = [];
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
regex = typeof regex === 'string' ? RegExp(escapeRegExp(regex), 'g') : regex;
|
||||||
|
|
||||||
|
matchAggregation(textAggregation);
|
||||||
|
|
||||||
|
function matchAggregation(textAggregation) {
|
||||||
|
for (var i = 0, l = textAggregation.length; i < l; ++i) {
|
||||||
|
|
||||||
|
var text = textAggregation[i];
|
||||||
|
|
||||||
|
if (typeof text !== 'string') {
|
||||||
|
// Deal with nested contexts: (recursive)
|
||||||
|
matchAggregation(text);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (regex.global) {
|
||||||
|
while (match = regex.exec(text)) {
|
||||||
|
matches.push(self.prepMatch(match, matchIndex++, offset));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (match = text.match(regex)) {
|
||||||
|
matches.push(self.prepMatch(match, 0, offset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += text.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matches;
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares a single match with useful meta info:
|
||||||
|
*/
|
||||||
|
prepMatch: function(match, matchIndex, characterOffset) {
|
||||||
|
|
||||||
|
if (!match[0]) {
|
||||||
|
throw new Error('findAndReplaceDOMText cannot handle zero-length matches');
|
||||||
|
}
|
||||||
|
|
||||||
|
match.endIndex = characterOffset + match.index + match[0].length;
|
||||||
|
match.startIndex = characterOffset + match.index;
|
||||||
|
match.index = matchIndex;
|
||||||
|
|
||||||
|
return match;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets aggregate text within subject node
|
||||||
|
*/
|
||||||
|
getAggregateText: function() {
|
||||||
|
|
||||||
|
var elementFilter = this.options.filterElements;
|
||||||
|
var forceContext = this.options.forceContext;
|
||||||
|
|
||||||
|
return getText(this.node);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets aggregate text of a node without resorting
|
||||||
|
* to broken innerText/textContent
|
||||||
|
*/
|
||||||
|
function getText(node) {
|
||||||
|
|
||||||
|
if (node.nodeType === Node.TEXT_NODE) {
|
||||||
|
return [node.data];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elementFilter && !elementFilter(node)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
var txt = [''];
|
||||||
|
var i = 0;
|
||||||
|
|
||||||
|
if (node = node.firstChild) do {
|
||||||
|
|
||||||
|
if (node.nodeType === Node.TEXT_NODE) {
|
||||||
|
txt[i] += node.data;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var innerText = getText(node);
|
||||||
|
|
||||||
|
if (
|
||||||
|
forceContext &&
|
||||||
|
node.nodeType === Node.ELEMENT_NODE &&
|
||||||
|
(forceContext === true || forceContext(node))
|
||||||
|
) {
|
||||||
|
txt[++i] = innerText;
|
||||||
|
txt[++i] = '';
|
||||||
|
} else {
|
||||||
|
if (typeof innerText[0] === 'string') {
|
||||||
|
// Bridge nested text-node data so that they're
|
||||||
|
// not considered their own contexts:
|
||||||
|
// I.e. ['some', ['thing']] -> ['something']
|
||||||
|
txt[i] += innerText.shift();
|
||||||
|
}
|
||||||
|
if (innerText.length) {
|
||||||
|
txt[++i] = innerText;
|
||||||
|
txt[++i] = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (node = node.nextSibling);
|
||||||
|
|
||||||
|
return txt;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Steps through the target node, looking for matches, and
|
||||||
|
* calling replaceFn when a match is found.
|
||||||
|
*/
|
||||||
|
processMatches: function() {
|
||||||
|
|
||||||
|
var matches = this.matches;
|
||||||
|
var node = this.node;
|
||||||
|
var elementFilter = this.options.filterElements;
|
||||||
|
|
||||||
|
var startPortion,
|
||||||
|
endPortion,
|
||||||
|
innerPortions = [],
|
||||||
|
curNode = node,
|
||||||
|
match = matches.shift(),
|
||||||
|
atIndex = 0, // i.e. nodeAtIndex
|
||||||
|
portionIndex = 0,
|
||||||
|
doAvoidNode,
|
||||||
|
nodeStack = [node];
|
||||||
|
|
||||||
|
out: while (true) {
|
||||||
|
|
||||||
|
if (curNode.nodeType === Node.TEXT_NODE) {
|
||||||
|
|
||||||
|
if (!endPortion && curNode.length + atIndex >= match.endIndex) {
|
||||||
|
// We've found the ending
|
||||||
|
// (Note that, in the case of a single portion, it'll be an
|
||||||
|
// endPortion, not a startPortion.)
|
||||||
|
endPortion = {
|
||||||
|
node: curNode,
|
||||||
|
index: portionIndex++,
|
||||||
|
text: curNode.data.substring(match.startIndex - atIndex, match.endIndex - atIndex),
|
||||||
|
|
||||||
|
// If it's the first match (atIndex==0) we should just return 0
|
||||||
|
indexInMatch: atIndex === 0 ? 0 : atIndex - match.startIndex,
|
||||||
|
|
||||||
|
indexInNode: match.startIndex - atIndex,
|
||||||
|
endIndexInNode: match.endIndex - atIndex,
|
||||||
|
isEnd: true
|
||||||
|
};
|
||||||
|
|
||||||
|
} else if (startPortion) {
|
||||||
|
// Intersecting node
|
||||||
|
innerPortions.push({
|
||||||
|
node: curNode,
|
||||||
|
index: portionIndex++,
|
||||||
|
text: curNode.data,
|
||||||
|
indexInMatch: atIndex - match.startIndex,
|
||||||
|
indexInNode: 0 // always zero for inner-portions
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!startPortion && curNode.length + atIndex > match.startIndex) {
|
||||||
|
// We've found the match start
|
||||||
|
startPortion = {
|
||||||
|
node: curNode,
|
||||||
|
index: portionIndex++,
|
||||||
|
indexInMatch: 0,
|
||||||
|
indexInNode: match.startIndex - atIndex,
|
||||||
|
endIndexInNode: match.endIndex - atIndex,
|
||||||
|
text: curNode.data.substring(match.startIndex - atIndex, match.endIndex - atIndex)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
atIndex += curNode.data.length;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
doAvoidNode = curNode.nodeType === Node.ELEMENT_NODE && elementFilter && !elementFilter(curNode);
|
||||||
|
|
||||||
|
if (startPortion && endPortion) {
|
||||||
|
|
||||||
|
curNode = this.replaceMatch(match, startPortion, innerPortions, endPortion);
|
||||||
|
|
||||||
|
// processMatches has to return the node that replaced the endNode
|
||||||
|
// and then we step back so we can continue from the end of the
|
||||||
|
// match:
|
||||||
|
|
||||||
|
atIndex -= (endPortion.node.data.length - endPortion.endIndexInNode);
|
||||||
|
|
||||||
|
startPortion = null;
|
||||||
|
endPortion = null;
|
||||||
|
innerPortions = [];
|
||||||
|
match = matches.shift();
|
||||||
|
portionIndex = 0;
|
||||||
|
|
||||||
|
if (!match) {
|
||||||
|
break; // no more matches
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (
|
||||||
|
!doAvoidNode &&
|
||||||
|
(curNode.firstChild || curNode.nextSibling)
|
||||||
|
) {
|
||||||
|
// Move down or forward:
|
||||||
|
if (curNode.firstChild) {
|
||||||
|
nodeStack.push(curNode);
|
||||||
|
curNode = curNode.firstChild;
|
||||||
|
} else {
|
||||||
|
curNode = curNode.nextSibling;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move forward or up:
|
||||||
|
while (true) {
|
||||||
|
if (curNode.nextSibling) {
|
||||||
|
curNode = curNode.nextSibling;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
curNode = nodeStack.pop();
|
||||||
|
if (curNode === node) {
|
||||||
|
break out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverts ... TODO
|
||||||
|
*/
|
||||||
|
revert: function() {
|
||||||
|
// Reversion occurs backwards so as to avoid nodes subsequently
|
||||||
|
// replaced during the matching phase (a forward process):
|
||||||
|
for (var l = this.reverts.length; l--;) {
|
||||||
|
this.reverts[l]();
|
||||||
|
}
|
||||||
|
this.reverts = [];
|
||||||
|
},
|
||||||
|
|
||||||
|
prepareReplacementString: function(string, portion, match) {
|
||||||
|
var portionMode = this.options.portionMode;
|
||||||
|
if (
|
||||||
|
portionMode === PORTION_MODE_FIRST &&
|
||||||
|
portion.indexInMatch > 0
|
||||||
|
) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
string = string.replace(/\$(\d+|&|`|')/g, function($0, t) {
|
||||||
|
var replacement;
|
||||||
|
switch(t) {
|
||||||
|
case '&':
|
||||||
|
replacement = match[0];
|
||||||
|
break;
|
||||||
|
case '`':
|
||||||
|
replacement = match.input.substring(0, match.startIndex);
|
||||||
|
break;
|
||||||
|
case '\'':
|
||||||
|
replacement = match.input.substring(match.endIndex);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
replacement = match[+t] || '';
|
||||||
|
}
|
||||||
|
return replacement;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (portionMode === PORTION_MODE_FIRST) {
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (portion.isEnd) {
|
||||||
|
return string.substring(portion.indexInMatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.substring(portion.indexInMatch, portion.indexInMatch + portion.text.length);
|
||||||
|
},
|
||||||
|
|
||||||
|
getPortionReplacementNode: function(portion, match) {
|
||||||
|
|
||||||
|
var replacement = this.options.replace || '$&';
|
||||||
|
var wrapper = this.options.wrap;
|
||||||
|
var wrapperClass = this.options.wrapClass;
|
||||||
|
|
||||||
|
if (wrapper && wrapper.nodeType) {
|
||||||
|
// Wrapper has been provided as a stencil-node for us to clone:
|
||||||
|
var clone = doc.createElement('div');
|
||||||
|
clone.innerHTML = wrapper.outerHTML || new XMLSerializer().serializeToString(wrapper);
|
||||||
|
wrapper = clone.firstChild;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof replacement == 'function') {
|
||||||
|
replacement = replacement(portion, match);
|
||||||
|
if (replacement && replacement.nodeType) {
|
||||||
|
return replacement;
|
||||||
|
}
|
||||||
|
return doc.createTextNode(String(replacement));
|
||||||
|
}
|
||||||
|
|
||||||
|
var el = typeof wrapper == 'string' ? doc.createElement(wrapper) : wrapper;
|
||||||
|
|
||||||
|
if (el && wrapperClass) {
|
||||||
|
el.className = wrapperClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
replacement = doc.createTextNode(
|
||||||
|
this.prepareReplacementString(
|
||||||
|
replacement, portion, match
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!replacement.data) {
|
||||||
|
return replacement;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!el) {
|
||||||
|
return replacement;
|
||||||
|
}
|
||||||
|
|
||||||
|
el.appendChild(replacement);
|
||||||
|
|
||||||
|
return el;
|
||||||
|
},
|
||||||
|
|
||||||
|
replaceMatch: function(match, startPortion, innerPortions, endPortion) {
|
||||||
|
|
||||||
|
var matchStartNode = startPortion.node;
|
||||||
|
var matchEndNode = endPortion.node;
|
||||||
|
|
||||||
|
var precedingTextNode;
|
||||||
|
var followingTextNode;
|
||||||
|
|
||||||
|
if (matchStartNode === matchEndNode) {
|
||||||
|
|
||||||
|
var node = matchStartNode;
|
||||||
|
|
||||||
|
if (startPortion.indexInNode > 0) {
|
||||||
|
// Add `before` text node (before the match)
|
||||||
|
precedingTextNode = doc.createTextNode(node.data.substring(0, startPortion.indexInNode));
|
||||||
|
node.parentNode.insertBefore(precedingTextNode, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the replacement node:
|
||||||
|
var newNode = this.getPortionReplacementNode(
|
||||||
|
endPortion,
|
||||||
|
match
|
||||||
|
);
|
||||||
|
|
||||||
|
node.parentNode.insertBefore(newNode, node);
|
||||||
|
|
||||||
|
if (endPortion.endIndexInNode < node.length) { // ?????
|
||||||
|
// Add `after` text node (after the match)
|
||||||
|
followingTextNode = doc.createTextNode(node.data.substring(endPortion.endIndexInNode));
|
||||||
|
node.parentNode.insertBefore(followingTextNode, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
node.parentNode.removeChild(node);
|
||||||
|
|
||||||
|
this.reverts.push(function() {
|
||||||
|
if (precedingTextNode === newNode.previousSibling) {
|
||||||
|
precedingTextNode.parentNode.removeChild(precedingTextNode);
|
||||||
|
}
|
||||||
|
if (followingTextNode === newNode.nextSibling) {
|
||||||
|
followingTextNode.parentNode.removeChild(followingTextNode);
|
||||||
|
}
|
||||||
|
newNode.parentNode.replaceChild(node, newNode);
|
||||||
|
});
|
||||||
|
|
||||||
|
return newNode;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Replace matchStartNode -> [innerMatchNodes...] -> matchEndNode (in that order)
|
||||||
|
|
||||||
|
|
||||||
|
precedingTextNode = doc.createTextNode(
|
||||||
|
matchStartNode.data.substring(0, startPortion.indexInNode)
|
||||||
|
);
|
||||||
|
|
||||||
|
followingTextNode = doc.createTextNode(
|
||||||
|
matchEndNode.data.substring(endPortion.endIndexInNode)
|
||||||
|
);
|
||||||
|
|
||||||
|
var firstNode = this.getPortionReplacementNode(
|
||||||
|
startPortion,
|
||||||
|
match
|
||||||
|
);
|
||||||
|
|
||||||
|
var innerNodes = [];
|
||||||
|
|
||||||
|
for (var i = 0, l = innerPortions.length; i < l; ++i) {
|
||||||
|
var portion = innerPortions[i];
|
||||||
|
var innerNode = this.getPortionReplacementNode(
|
||||||
|
portion,
|
||||||
|
match
|
||||||
|
);
|
||||||
|
portion.node.parentNode.replaceChild(innerNode, portion.node);
|
||||||
|
this.reverts.push((function(portion, innerNode) {
|
||||||
|
return function() {
|
||||||
|
innerNode.parentNode.replaceChild(portion.node, innerNode);
|
||||||
|
};
|
||||||
|
}(portion, innerNode)));
|
||||||
|
innerNodes.push(innerNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastNode = this.getPortionReplacementNode(
|
||||||
|
endPortion,
|
||||||
|
match
|
||||||
|
);
|
||||||
|
|
||||||
|
matchStartNode.parentNode.insertBefore(precedingTextNode, matchStartNode);
|
||||||
|
matchStartNode.parentNode.insertBefore(firstNode, matchStartNode);
|
||||||
|
matchStartNode.parentNode.removeChild(matchStartNode);
|
||||||
|
|
||||||
|
matchEndNode.parentNode.insertBefore(lastNode, matchEndNode);
|
||||||
|
matchEndNode.parentNode.insertBefore(followingTextNode, matchEndNode);
|
||||||
|
matchEndNode.parentNode.removeChild(matchEndNode);
|
||||||
|
|
||||||
|
this.reverts.push(function() {
|
||||||
|
precedingTextNode.parentNode.removeChild(precedingTextNode);
|
||||||
|
firstNode.parentNode.replaceChild(matchStartNode, firstNode);
|
||||||
|
followingTextNode.parentNode.removeChild(followingTextNode);
|
||||||
|
lastNode.parentNode.replaceChild(matchEndNode, lastNode);
|
||||||
|
});
|
||||||
|
|
||||||
|
return lastNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
return exposed;
|
||||||
|
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Heti add-on v 0.1.0
|
||||||
|
* Add right spacing between CJK & ANS characters
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 正则表达式来自 pangu.js https://github.com/vinta/pangu.js
|
||||||
|
const CJK = '\u2e80-\u2eff\u2f00-\u2fdf\u3040-\u309f\u30a0-\u30fa\u30fc-\u30ff\u3100-\u312f\u3200-\u32ff\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff';
|
||||||
|
const A = 'A-Za-z\u0370-\u03ff';
|
||||||
|
const N = '0-9';
|
||||||
|
const S = '`~!@#\\$%\\^&\\*\\(\\)-_=\\+\\[\\]{}\\\\\\|;:\'",<.>\\/\\?';
|
||||||
|
const ANS = `${A}${N}${S}`;
|
||||||
|
const HETI_NON_CONTIGUOUS_ELEMENTS = {
|
||||||
|
// Block Elements
|
||||||
|
address:1, article:1, aside:1, blockquote:1, dd:1, div:1,
|
||||||
|
dl:1, fieldset:1, figcaption:1, figure:1, footer:1, form:1, h1:1, h2:1, h3:1,
|
||||||
|
h4:1, h5:1, h6:1, header:1, hgroup:1, hr:1, main:1, nav:1, noscript:1, ol:1,
|
||||||
|
output:1, p:1, pre:1, section:1, ul:1,
|
||||||
|
// Other misc. elements that are not part of continuous inline prose:
|
||||||
|
br:1, li: 1, summary: 1, dt:1, details:1, rp:1, rt:1, rtc:1,
|
||||||
|
// Media / Source elements:
|
||||||
|
script:1, style:1, img:1, video:1, audio:1, canvas:1, svg:1, map:1, object:1,
|
||||||
|
// Input elements
|
||||||
|
input:1, textarea:1, select:1, option:1, optgroup:1, button:1,
|
||||||
|
// Table related elements:
|
||||||
|
table:1, tbody:1, thead:1, th:1, tr:1, td:1, caption:1, col:1, tfoot:1, colgroup:1
|
||||||
|
};
|
||||||
|
const HETI_SKIPPED_ELEMENTS = {
|
||||||
|
br:1, hr:1,
|
||||||
|
// Media / Source elements:
|
||||||
|
script:1, style:1, img:1, video:1, audio:1, canvas:1, svg:1, map:1, object:1,
|
||||||
|
// Input elements:
|
||||||
|
input:1, textarea:1, select:1, option:1, optgroup: 1, button:1,
|
||||||
|
// Pre elements:
|
||||||
|
pre: 1, code: 1, sup: 1, sub: 1,
|
||||||
|
// Heti elements
|
||||||
|
'heti-spacing': 1,
|
||||||
|
};
|
||||||
|
const HETI_SKIPPED_CLASS = 'heti-skip';
|
||||||
|
const hasOwn = {}.hasOwnProperty;
|
||||||
|
|
||||||
|
class Heti {
|
||||||
|
constructor(rootSelector) {
|
||||||
|
this.rootSelector = rootSelector || '.heti';
|
||||||
|
this.REG_FULL = new RegExp(`(?<=[${CJK}])( *[${ANS}]+(?: +[${ANS}]+)* *)(?=[${CJK}])`, 'g');
|
||||||
|
this.REG_START = new RegExp(`([${ANS}]+(?: +[${ANS}]+)* *)(?=[${CJK}])`, 'g');
|
||||||
|
this.REG_END = new RegExp(`(?<=[${CJK}])( *[${ANS}]+(?: +[${ANS}]+)*)`, 'g');
|
||||||
|
this.funcForceContext = function forceContext(el) {
|
||||||
|
return hasOwn.call(HETI_NON_CONTIGUOUS_ELEMENTS, el.nodeName.toLowerCase())
|
||||||
|
};
|
||||||
|
this.funcFilterElements = function filterElements(el) {
|
||||||
|
return (
|
||||||
|
!(el.classList && el.classList.contains(HETI_SKIPPED_CLASS)) &&
|
||||||
|
!hasOwn.call(HETI_SKIPPED_ELEMENTS, el.nodeName.toLowerCase())
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
spacingElements(elmList) {
|
||||||
|
for (let $$root of elmList) {
|
||||||
|
this.spacingElement($$root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spacingElement($$elm) {
|
||||||
|
const commonConfig = {
|
||||||
|
forceContext: this.funcForceContext,
|
||||||
|
filterElements: this.funcFilterElements
|
||||||
|
};
|
||||||
|
const getWrapper = function (classList, text) {
|
||||||
|
const $$r = document.createElement('heti-spacing');
|
||||||
|
$$r.className = classList;
|
||||||
|
$$r.textContent = text.trim();
|
||||||
|
return $$r
|
||||||
|
};
|
||||||
|
|
||||||
|
findAndReplaceDOMText($$elm, Object.assign(commonConfig, {
|
||||||
|
find: this.REG_FULL,
|
||||||
|
replace: portion => getWrapper('heti-spacing-start heti-spacing-end', portion.text)
|
||||||
|
}));
|
||||||
|
|
||||||
|
findAndReplaceDOMText($$elm, Object.assign(commonConfig, {
|
||||||
|
find: this.REG_START,
|
||||||
|
replace: portion => getWrapper('heti-spacing-start', portion.text)
|
||||||
|
}));
|
||||||
|
|
||||||
|
findAndReplaceDOMText($$elm, Object.assign(commonConfig, {
|
||||||
|
find: this.REG_END,
|
||||||
|
replace: portion => getWrapper('heti-spacing-end', portion.text)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
autoSpacing() {
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const $$rootList = document.querySelectorAll(this.rootSelector);
|
||||||
|
|
||||||
|
for (let $$root of $$rootList) {
|
||||||
|
this.spacingElement($$root);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Heti;
|
||||||
|
|
||||||
|
})));
|
|
@ -182,19 +182,16 @@
|
||||||
margin-block-end: 24px;
|
margin-block-end: 24px;
|
||||||
font-size: 32px;
|
font-size: 32px;
|
||||||
line-height: 48px;
|
line-height: 48px;
|
||||||
letter-spacing: 1.6px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.heti h2 {
|
.heti h2 {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
line-height: 36px;
|
line-height: 36px;
|
||||||
letter-spacing: 1.2px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.heti h3 {
|
.heti h3 {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
line-height: 36px;
|
line-height: 36px;
|
||||||
letter-spacing: 1px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.heti h4 {
|
.heti h4 {
|
||||||
|
@ -212,6 +209,20 @@
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.heti h1,
|
||||||
|
.heti h2,
|
||||||
|
.heti h3 {
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.heti h1:not(:lang(zh)):not(:lang(ja)):not(:lang(kr)), .heti h1:not(:lang(zh)),
|
||||||
|
.heti h2:not(:lang(zh)):not(:lang(ja)):not(:lang(kr)),
|
||||||
|
.heti h2:not(:lang(zh)),
|
||||||
|
.heti h3:not(:lang(zh)):not(:lang(ja)):not(:lang(kr)),
|
||||||
|
.heti h3:not(:lang(zh)) {
|
||||||
|
letter-spacing: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.heti h1 + h2,
|
.heti h1 + h2,
|
||||||
.heti h2 + h3,
|
.heti h2 + h3,
|
||||||
.heti h3 + h4,
|
.heti h3 + h4,
|
||||||
|
@ -391,6 +402,7 @@
|
||||||
margin-inline-end: 0.25em;
|
margin-inline-end: 0.25em;
|
||||||
font-size: 0.75em;
|
font-size: 0.75em;
|
||||||
font-family: "Helvetica Neue", Helvetica, Arial, "Heti Hei", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
font-family: "Helvetica Neue", Helvetica, Arial, "Heti Hei", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||||
|
font-style: normal;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
vertical-align: baseline;
|
vertical-align: baseline;
|
||||||
}
|
}
|
||||||
|
@ -685,6 +697,7 @@
|
||||||
.heti .heti-x-large {
|
.heti .heti-x-large {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
line-height: 30px;
|
line-height: 30px;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.heti .heti-small {
|
.heti .heti-small {
|
||||||
|
@ -748,3 +761,20 @@
|
||||||
.heti .heti-em:not(:lang(zh)):not(:lang(ja)):not(:lang(kr)), .heti .heti-em:not(:lang(zh)) {
|
.heti .heti-em:not(:lang(zh)):not(:lang(ja)):not(:lang(kr)), .heti .heti-em:not(:lang(zh)) {
|
||||||
-webkit-text-emphasis: none;
|
-webkit-text-emphasis: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.heti heti-spacing {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.heti heti-spacing.heti-spacing-start {
|
||||||
|
margin-inline-end: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.heti heti-spacing.heti-spacing-end {
|
||||||
|
margin-inline-start: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.heti heti-spacing + sup,
|
||||||
|
.heti heti-spacing + sub {
|
||||||
|
margin-inline-start: 0;
|
||||||
|
}
|
|
@ -50,8 +50,9 @@ a {
|
||||||
|
|
||||||
.article[data-bg-grid="grid-24"] {
|
.article[data-bg-grid="grid-24"] {
|
||||||
background-size: 100% 24px;
|
background-size: 100% 24px;
|
||||||
background-image: linear-gradient(to right, rgba(255, 255, 255, 0) 31px, #eee 1px, rgba(255, 255, 255, 0) 33px),
|
background-image:
|
||||||
linear-gradient(rgba(255, 255, 255, 0) 23px, #eee 1px);
|
linear-gradient(to right, rgba(255, 255, 255, 0) 31px, #eee 1px, rgba(255, 255, 255, 0) 33px),
|
||||||
|
linear-gradient(rgba(255, 255, 255, 0) 23px, #eee 1px);
|
||||||
outline-color: #eee;
|
outline-color: #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,8 +64,9 @@ a {
|
||||||
|
|
||||||
.article[data-bg-grid="grid-12"] {
|
.article[data-bg-grid="grid-12"] {
|
||||||
background-size: 100% 12px;
|
background-size: 100% 12px;
|
||||||
background-image: linear-gradient(to right, rgba(255, 255, 255, 0) 31px, #eee 1px, rgba(255, 255, 255, 0) 33px),
|
background-image:
|
||||||
linear-gradient(rgba(255, 255, 255, 0) 11px, #eee 12px);
|
linear-gradient(to right, rgba(255, 255, 255, 0) 31px, #eee 1px, rgba(255, 255, 255, 0) 33px),
|
||||||
|
linear-gradient(rgba(255, 255, 255, 0) 11px, #eee 12px);
|
||||||
outline-color: #eee;
|
outline-color: #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="stylesheet" href="./normalize.css">
|
<link rel="stylesheet" href="./normalize.css">
|
||||||
<link rel="stylesheet" href="./index.css">
|
<link rel="stylesheet" href="./index.css">
|
||||||
<link rel="stylesheet" href="./heti.min.css">
|
<link rel="stylesheet" href="./heti.debug.css">
|
||||||
<link rel="icon" href="./favicon.svg">
|
<link rel="icon" href="./favicon.svg">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -15,7 +15,7 @@
|
||||||
<h1>赫蹏</h1>
|
<h1>赫蹏</h1>
|
||||||
<blockquote>古代称用以书写的小幅绢帛。后亦以借指纸。《汉书·外戚传下·孝成赵皇后》:<q>武(籍武 )发篋中,有裹药二枚,赫蹏书。</q><u>颜师古</u>注:<q><u>邓展</u>曰:<q>赫音兄弟鬩墙之鬩。</q><u>应劭</u>曰:<q>赫蹏,薄小纸也。</q></q><u>宋</u><u>赵彦卫</u> 《云麓漫钞》卷七:<q>《赵后传》所谓『赫蹏』者,注云<q>薄小纸</q>,然其寔亦縑帛。</q></blockquote>
|
<blockquote>古代称用以书写的小幅绢帛。后亦以借指纸。《汉书·外戚传下·孝成赵皇后》:<q>武(籍武 )发篋中,有裹药二枚,赫蹏书。</q><u>颜师古</u>注:<q><u>邓展</u>曰:<q>赫音兄弟鬩墙之鬩。</q><u>应劭</u>曰:<q>赫蹏,薄小纸也。</q></q><u>宋</u><u>赵彦卫</u> 《云麓漫钞》卷七:<q>《赵后传》所谓『赫蹏』者,注云<q>薄小纸</q>,然其寔亦縑帛。</q></blockquote>
|
||||||
|
|
||||||
<nav class="article__toc">
|
<nav class="article__toc heti-skip">
|
||||||
<details open>
|
<details open>
|
||||||
<summary>目录</summary>
|
<summary>目录</summary>
|
||||||
<ol>
|
<ol>
|
||||||
|
@ -49,16 +49,14 @@
|
||||||
<h2 id="intro">介绍<a class="anchor" href="#intro">#</a></h2>
|
<h2 id="intro">介绍<a class="anchor" href="#intro">#</a></h2>
|
||||||
<p><dfn>赫蹏</dfn>(<code>hètí</code>)是专为中文内容展示设计的排版样式增强。它基于通行的中文排版规范而来,可以为网站的读者带来更好的文章阅读体验。由于它不同于传统的网页样式重置,而是专注于<em>正文区域</em>的样式增强,因此可以很好的与常见的CSS样式重置方案共存。</p>
|
<p><dfn>赫蹏</dfn>(<code>hètí</code>)是专为中文内容展示设计的排版样式增强。它基于通行的中文排版规范而来,可以为网站的读者带来更好的文章阅读体验。由于它不同于传统的网页样式重置,而是专注于<em>正文区域</em>的样式增强,因此可以很好的与常见的CSS样式重置方案共存。</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>全标签样式统一;</li>
|
|
||||||
<li>贴合网格的排版;</li>
|
<li>贴合网格的排版;</li>
|
||||||
<li>预置多种预设字体族(仅限桌面端);</li>
|
<li>全标签样式美化;</li>
|
||||||
<li>预置横排、直排(竖排)样式;</li>
|
<li>预置多种排版样式(横/竖排、行间注、多栏等);</li>
|
||||||
<li>预置古文、诗词样式;</li>
|
<li>预置古文、诗词样式;</li>
|
||||||
<li>预置行间注排版样式;</li>
|
<li>多种预设字体族(仅限桌面端);</li>
|
||||||
<li>预置多栏排版样式;</li>
|
<li>中西文混排美化(基于JavaScript脚本实现);</li>
|
||||||
<li>兼容<i>normalize.css</i>、<i>CSS Reset</i><sup><a href="#fn-01">[1]</a></sup>;</li>
|
<li>兼容<i>normalize.css</i>、<i>CSS Reset<sup><a href="#fn-01">[1]</a></sup></i>;</li>
|
||||||
<li>移动端支持;</li>
|
<li>移动端支持;</li>
|
||||||
<li>基于BEM<sup><a href="#fn-02">[2]</a></sup>命名;</li>
|
|
||||||
<li>……</li>
|
<li>……</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>总之,用上就会变好看。</p>
|
<p>总之,用上就会变好看。</p>
|
||||||
|
@ -68,25 +66,35 @@
|
||||||
<h2 id="usage">使用方法<a class="anchor" href="#usage">#</a></h2>
|
<h2 id="usage">使用方法<a class="anchor" href="#usage">#</a></h2>
|
||||||
<p>赫蹏的使用方法很简单,只需要引入样式文件并设定作用范围即可:</p>
|
<p>赫蹏的使用方法很简单,只需要引入样式文件并设定作用范围即可:</p>
|
||||||
<ol>
|
<ol>
|
||||||
|
<li>下载项目(<a href="https://github.com/sivan/heti">https://github.com/sivan/heti</a>),将<code>dist</code>文件夹下文件拷出待用;</li>
|
||||||
<li>
|
<li>
|
||||||
在页面的<code><head></code>标签中引入<code>heti.css</code>文件:
|
在页面的<code></head></code>标签前中引入<code>heti.css</code>样式文件:
|
||||||
<pre><code><link rel="stylesheet" href="./heti.css"></code></pre>
|
<pre><code><link rel="stylesheet" href="./heti.css"></code></pre>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
在要作用的容器元素上增加<code>class="heti"</code>的类名即可:
|
在要作用的容器元素上增加<code>class="heti"</code>的类名即可:
|
||||||
<pre><code><article class="entry heti">
|
<pre><code><article class="entry <ins>heti</ins>">
|
||||||
<h1>我的世界观</h1>
|
<h1>我的世界观</h1>
|
||||||
<p>有钱人的生活就是这么朴实无华,且枯燥</p>
|
<p>有钱人的生活就是这么朴实无华,且枯燥</p>
|
||||||
……
|
……
|
||||||
</article></code></pre>
|
</article></code></pre>
|
||||||
</li>
|
</li>
|
||||||
<li>不建议将样式作用在根标签(比如<code><body></code>或<code><div class="container"></code>)上,除非你的页面<u title="就像本页面一样">通篇都是文章的一部分</u>。</li>
|
<li>
|
||||||
|
* 如需中西文混排美化,还要在页面的<code></body></code>标签前引入JavaScript脚本:
|
||||||
|
<pre><code><script src="./heti-addon.min.js"></script>
|
||||||
|
<script>
|
||||||
|
const heti = new Heti();
|
||||||
|
heti.autoSpacing();
|
||||||
|
</script></code></pre>
|
||||||
|
</li>
|
||||||
|
<li>大功告成,查看效果吧~</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
<p>注:赫蹏是正文区域的样式增强,不是<i>normalize.css</i>或<i>CSS Reset</i>的替代。因此<b>不建议</b>将它作用在根标签(如<code><body></code>或<code><div class="container"></code>)上,除非像本页面一样通篇都是文章的一部分。</p>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<h2 id="examples">效果示例<a class="anchor" href="#examples">#</a></h2>
|
<h2 id="examples">效果示例<a class="anchor" href="#examples">#</a></h2>
|
||||||
<p>本站已<em>全页</em>应用了赫蹏样式,下面是赫蹏在特殊排版下的效果演示。</p>
|
<p>本页面<em>全页</em>应用了赫蹏样式,所见即所得。下面是内置的多种排版效果演示。</p>
|
||||||
<details open>
|
<details open>
|
||||||
<summary id="example-1">示例1:古文</summary>
|
<summary id="example-1">示例1:古文</summary>
|
||||||
<section class="demo">
|
<section class="demo">
|
||||||
|
@ -120,12 +128,12 @@
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="heti--poetry">
|
<div class="heti--poetry">
|
||||||
<h2>丑奴儿·书博山道中壁<span class="heti-meta heti-small">[宋]<abbr title="号稼轩">辛弃疾</abbr></span></h2>
|
<h2>赠汪伦<span class="heti-meta heti-small">[唐]<abbr title="号青莲居士">李白</abbr></span></h2>
|
||||||
<p class="heti-x-large">
|
<p class="heti-x-large">
|
||||||
少年不识愁滋味,爱上层楼<span class="heti-hang">。</span><br>
|
李白乘舟将欲行<span class="heti-hang">,</span><br>
|
||||||
爱上层楼,为赋新词强说愁<span class="heti-hang">。</span><br>
|
忽闻岸上踏歌声<span class="heti-hang">。</span><br>
|
||||||
而今识尽愁滋味,欲说还休<span class="heti-hang">。</span><br>
|
桃花潭水深千尺<span class="heti-hang">,</span><br>
|
||||||
欲说还休,却道天凉好个秋<span class="heti-hang">。</span>
|
不及汪伦送我情<span class="heti-hang">。</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
@ -165,7 +173,7 @@
|
||||||
<p>赫蹏预置了多种多栏布局类,可以按栏数或栏宽进行设置。</p>
|
<p>赫蹏预置了多种多栏布局类,可以按栏数或栏宽进行设置。</p>
|
||||||
<details>
|
<details>
|
||||||
<summary>查看使用说明</summary>
|
<summary>查看使用说明</summary>
|
||||||
<p>为容器元素添加名为<code>heti--columns-2</code>class即可实现三栏排版。</p>
|
<p>为容器元素添加名为<code>heti--columns-2</code>的class即可实现三栏排版。</p>
|
||||||
<pre><code><div class="heti heti--columns-2"></div></code></pre>
|
<pre><code><div class="heti heti--columns-2"></div></code></pre>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
|
@ -210,7 +218,7 @@
|
||||||
<p>赫蹏预置了传统的垂直方向排版,同样贴合栅格。</p>
|
<p>赫蹏预置了传统的垂直方向排版,同样贴合栅格。</p>
|
||||||
<details>
|
<details>
|
||||||
<summary>查看使用说明</summary>
|
<summary>查看使用说明</summary>
|
||||||
<p>为容器元素添加名为<code>heti--vertical</code>class即可实现垂直布局:</p>
|
<p>为容器元素添加名为<code>heti--vertical</code>的class即可实现垂直布局:</p>
|
||||||
<pre><code><div class="heti heti--vertical"></div></code></pre>
|
<pre><code><div class="heti heti--vertical"></div></code></pre>
|
||||||
</details>
|
</details>
|
||||||
<figure class="card card--vertical">
|
<figure class="card card--vertical">
|
||||||
|
@ -232,14 +240,14 @@
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<h2 id="guidelines">排版原则<a class="anchor" href="#guidelines">#</a></h2>
|
<h2 id="guidelines">设计原则<a class="anchor" href="#guidelines">#</a></h2>
|
||||||
<h3>核心</h3>
|
<h3>核心</h3>
|
||||||
<p>以美化中文方块字排版为目标,假定作用范围是网页中的文章区域(含标题、正文、元信息等),不包含网页导航、表单、侧边栏等区域(它们也不需要这套格式排版)。</p>
|
<p>以美化中文方块字排版为目标,假定作用范围是网页中的文章区域(含标题、正文、元信息等),不包含网页导航、表单、侧边栏等区域(它们也不需要这套格式排版)。</p>
|
||||||
<p>使用<code>heti.css</code>时建议搭配良好的中文书写习惯,即:使用正确的HTML标签、正确的引号、中文全角标点等。使用Markdown书写文章将助你一臂之力。</p>
|
<p>使用<code>heti.css</code>时建议搭配良好的中文书写习惯,即:使用正确的HTML标签、正确的引号、中文全角标点等。使用Markdown书写文章将助你一臂之力。</p>
|
||||||
<h3>字号</h3>
|
<h3>字号</h3>
|
||||||
<p><code>heti.css</code> 采用16px作为默认字号。在文字较大时(作用于标题等情况),会适当地增加字间距以便获得更好地可读性。</p>
|
<p><code>heti.css</code> 采用16px作为默认字号。在文字较大时(作用于标题等情况),会适当地增加字间距以便获得更好地可读性。</p>
|
||||||
<h3>字体</h3>
|
<h3>字体</h3>
|
||||||
<p>参考《中文排版需求<sup><a href="#fn-03">[3]</a></sup>》中描述的四种常见书籍排版字体:宋体、楷体、黑体、仿宋。赫蹏提供黑体、宋体和<u title="以正文宋体、标题楷体构成的搭配">传统</u>三种字体风格,前两者分别对应无衬线、衬线字体族。</p>
|
<p>参考《中文排版需求<sup><a href="#fn-03">[2]</a></sup>》中描述的四种常见书籍排版字体:宋体、楷体、黑体、仿宋。赫蹏提供黑体、宋体和<u title="以正文宋体、标题楷体构成的搭配">传统</u>三种字体风格,前两者分别对应无衬线、衬线字体族。</p>
|
||||||
<details>
|
<details>
|
||||||
<summary>查看对照表</summary>
|
<summary>查看对照表</summary>
|
||||||
<section class="section">
|
<section class="section">
|
||||||
|
@ -364,6 +372,12 @@
|
||||||
<p>参考《中文排版需求》制定符号样式。唯一差异在于简体中文一律采用直角引号(「」)替代圆角引号(“”),这样可以保持字符等宽。</p>
|
<p>参考《中文排版需求》制定符号样式。唯一差异在于简体中文一律采用直角引号(「」)替代圆角引号(“”),这样可以保持字符等宽。</p>
|
||||||
<h3>间距</h3>
|
<h3>间距</h3>
|
||||||
<p>为保持页面元素总是贴合垂直栅格,大部分块级元素(段落、列表、表格等)采用一行行高作为底边距,半行行高作为顶边距。</p>
|
<p>为保持页面元素总是贴合垂直栅格,大部分块级元素(段落、列表、表格等)采用一行行高作为底边距,半行行高作为顶边距。</p>
|
||||||
|
<h3>其它</h3>
|
||||||
|
<p>赫蹏基于BEM<sup><a href="#fn-02">[3]</a></sup>命名,并提供了一些 helper classes。详细内容看下表:</p>
|
||||||
|
<details>
|
||||||
|
<summary>查看辅助样式列表</summary>
|
||||||
|
<p><i>待补充…</i></p>
|
||||||
|
</details>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
@ -472,9 +486,9 @@
|
||||||
|
|
||||||
<h2 id="wip">待开发功能<a class="anchor" href="#wip">#</a></h2>
|
<h2 id="wip">待开发功能<a class="anchor" href="#wip">#</a></h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li>️☑ 中、西文混排</li>
|
|
||||||
<li>☑ 标点挤压</li>
|
<li>☑ 标点挤压</li>
|
||||||
<li>☑ 标点悬挂</li>
|
<li>☑ 标点悬挂</li>
|
||||||
|
<li>✅ 中、西文混排</li>
|
||||||
<li>✅ 诗词版式</li>
|
<li>✅ 诗词版式</li>
|
||||||
<li>✅ 行间注版式</li>
|
<li>✅ 行间注版式</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -482,8 +496,8 @@
|
||||||
<footer class="heti-fn">
|
<footer class="heti-fn">
|
||||||
<ol>
|
<ol>
|
||||||
<li id="fn-01">CSS Reset:指代类似Eric Meyer's Reset CSS的样式重置方案</li>
|
<li id="fn-01">CSS Reset:指代类似Eric Meyer's Reset CSS的样式重置方案</li>
|
||||||
<li id="fn-02">BEM:由Yandex公司创造的命名方式 https://en.bem.info/</li>
|
<li id="fn-02">《中文排版需求》:https://w3c.github.io/clreq/</li>
|
||||||
<li id="fn-03">《中文排版需求》:https://w3c.github.io/clreq/</li>
|
<li id="fn-03">BEM:由Yandex公司创造的命名方式 https://en.bem.info/</li>
|
||||||
</ol>
|
</ol>
|
||||||
</footer>
|
</footer>
|
||||||
</article>
|
</article>
|
||||||
|
@ -502,6 +516,7 @@
|
||||||
</ul>
|
</ul>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
|
<script src="./heti-addon.debug.js"></script>
|
||||||
<script>
|
<script>
|
||||||
const $$root = document.getElementsByTagName('article')[0]
|
const $$root = document.getElementsByTagName('article')[0]
|
||||||
|
|
||||||
|
@ -518,6 +533,10 @@
|
||||||
addEventListeners(document.getElementsByClassName('J_radioGrid'), 'change', function (e) {
|
addEventListeners(document.getElementsByClassName('J_radioGrid'), 'change', function (e) {
|
||||||
$$root.setAttribute('data-bg-grid', e.target.value)
|
$$root.setAttribute('data-bg-grid', e.target.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const heti = new Heti()
|
||||||
|
heti.autoSpacing()
|
||||||
|
// hetiSpacing.spacingElements(document.getElementsByClassName('heti'))
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
105
add-ons/add-on.js
Normal file
105
add-ons/add-on.js
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
/**
|
||||||
|
* Heti add-on v 0.1.0
|
||||||
|
* Add right spacing between CJK & ANS characters
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Finder from 'findandreplacedomtext'
|
||||||
|
|
||||||
|
// 正则表达式来自 pangu.js https://github.com/vinta/pangu.js
|
||||||
|
const CJK = '\u2e80-\u2eff\u2f00-\u2fdf\u3040-\u309f\u30a0-\u30fa\u30fc-\u30ff\u3100-\u312f\u3200-\u32ff\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff'
|
||||||
|
const A = 'A-Za-z\u0370-\u03ff'
|
||||||
|
const N = '0-9'
|
||||||
|
const S = '`~!@#\\$%\\^&\\*\\(\\)-_=\\+\\[\\]{}\\\\\\|;:\'",<.>\\/\\?'
|
||||||
|
const ANS = `${A}${N}${S}`
|
||||||
|
const HETI_NON_CONTIGUOUS_ELEMENTS = {
|
||||||
|
// Block Elements
|
||||||
|
address:1, article:1, aside:1, blockquote:1, dd:1, div:1,
|
||||||
|
dl:1, fieldset:1, figcaption:1, figure:1, footer:1, form:1, h1:1, h2:1, h3:1,
|
||||||
|
h4:1, h5:1, h6:1, header:1, hgroup:1, hr:1, main:1, nav:1, noscript:1, ol:1,
|
||||||
|
output:1, p:1, pre:1, section:1, ul:1,
|
||||||
|
// Other misc. elements that are not part of continuous inline prose:
|
||||||
|
br:1, li: 1, summary: 1, dt:1, details:1, rp:1, rt:1, rtc:1,
|
||||||
|
// Media / Source elements:
|
||||||
|
script:1, style:1, img:1, video:1, audio:1, canvas:1, svg:1, map:1, object:1,
|
||||||
|
// Input elements
|
||||||
|
input:1, textarea:1, select:1, option:1, optgroup:1, button:1,
|
||||||
|
// Table related elements:
|
||||||
|
table:1, tbody:1, thead:1, th:1, tr:1, td:1, caption:1, col:1, tfoot:1, colgroup:1
|
||||||
|
}
|
||||||
|
const HETI_SKIPPED_ELEMENTS = {
|
||||||
|
br:1, hr:1,
|
||||||
|
// Media / Source elements:
|
||||||
|
script:1, style:1, img:1, video:1, audio:1, canvas:1, svg:1, map:1, object:1,
|
||||||
|
// Input elements:
|
||||||
|
input:1, textarea:1, select:1, option:1, optgroup: 1, button:1,
|
||||||
|
// Pre elements:
|
||||||
|
pre: 1, code: 1, sup: 1, sub: 1,
|
||||||
|
// Heti elements
|
||||||
|
'heti-spacing': 1,
|
||||||
|
}
|
||||||
|
const HETI_SKIPPED_CLASS = 'heti-skip'
|
||||||
|
const hasOwn = {}.hasOwnProperty
|
||||||
|
|
||||||
|
class Heti {
|
||||||
|
constructor(rootSelector) {
|
||||||
|
this.rootSelector = rootSelector || '.heti'
|
||||||
|
this.REG_FULL = new RegExp(`(?<=[${CJK}])( *[${ANS}]+(?: +[${ANS}]+)* *)(?=[${CJK}])`, 'g')
|
||||||
|
this.REG_START = new RegExp(`([${ANS}]+(?: +[${ANS}]+)* *)(?=[${CJK}])`, 'g')
|
||||||
|
this.REG_END = new RegExp(`(?<=[${CJK}])( *[${ANS}]+(?: +[${ANS}]+)*)`, 'g')
|
||||||
|
this.funcForceContext = function forceContext(el) {
|
||||||
|
return hasOwn.call(HETI_NON_CONTIGUOUS_ELEMENTS, el.nodeName.toLowerCase())
|
||||||
|
}
|
||||||
|
this.funcFilterElements = function filterElements(el) {
|
||||||
|
return (
|
||||||
|
!(el.classList && el.classList.contains(HETI_SKIPPED_CLASS)) &&
|
||||||
|
!hasOwn.call(HETI_SKIPPED_ELEMENTS, el.nodeName.toLowerCase())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spacingElements(elmList) {
|
||||||
|
for (let $$root of elmList) {
|
||||||
|
this.spacingElement($$root)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spacingElement($$elm) {
|
||||||
|
const commonConfig = {
|
||||||
|
forceContext: this.funcForceContext,
|
||||||
|
filterElements: this.funcFilterElements
|
||||||
|
}
|
||||||
|
const getWrapper = function (classList, text) {
|
||||||
|
const $$r = document.createElement('heti-spacing')
|
||||||
|
$$r.className = classList
|
||||||
|
$$r.textContent = text.trim()
|
||||||
|
return $$r
|
||||||
|
}
|
||||||
|
|
||||||
|
Finder($$elm, Object.assign(commonConfig, {
|
||||||
|
find: this.REG_FULL,
|
||||||
|
replace: portion => getWrapper('heti-spacing-start heti-spacing-end', portion.text)
|
||||||
|
}))
|
||||||
|
|
||||||
|
Finder($$elm, Object.assign(commonConfig, {
|
||||||
|
find: this.REG_START,
|
||||||
|
replace: portion => getWrapper('heti-spacing-start', portion.text)
|
||||||
|
}))
|
||||||
|
|
||||||
|
Finder($$elm, Object.assign(commonConfig, {
|
||||||
|
find: this.REG_END,
|
||||||
|
replace: portion => getWrapper('heti-spacing-end', portion.text)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
autoSpacing() {
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const $$rootList = document.querySelectorAll(this.rootSelector)
|
||||||
|
|
||||||
|
for (let $$root of $$rootList) {
|
||||||
|
this.spacingElement($$root)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Heti
|
|
@ -23,21 +23,16 @@
|
||||||
margin-block-end: $std-block-unit;
|
margin-block-end: $std-block-unit;
|
||||||
font-size: $font-size-h1;
|
font-size: $font-size-h1;
|
||||||
line-height: $line-height-size-h1;
|
line-height: $line-height-size-h1;
|
||||||
|
|
||||||
// 中文大标题增加微小文字间距
|
|
||||||
letter-spacing: $font-size-h1 * 0.05;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
font-size: $font-size-h2;
|
font-size: $font-size-h2;
|
||||||
line-height: $line-height-size-h2;
|
line-height: $line-height-size-h2;
|
||||||
letter-spacing: $font-size-h2 * 0.05;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
font-size: $font-size-h3;
|
font-size: $font-size-h3;
|
||||||
line-height: $line-height-size-h3;
|
line-height: $line-height-size-h3;
|
||||||
letter-spacing: $font-size-h3 * 0.05;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h4 {
|
h4 {
|
||||||
|
@ -58,6 +53,9 @@
|
||||||
h1,
|
h1,
|
||||||
h2,
|
h2,
|
||||||
h3 {
|
h3 {
|
||||||
|
// 中文大标题增加微小文字间距
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
|
||||||
// 非中文时不加间距
|
// 非中文时不加间距
|
||||||
@include non-cjk-block {
|
@include non-cjk-block {
|
||||||
letter-spacing: 0;
|
letter-spacing: 0;
|
||||||
|
|
|
@ -111,6 +111,7 @@
|
||||||
margin-inline-end: 0.25em;
|
margin-inline-end: 0.25em;
|
||||||
font-size: 0.75em;
|
font-size: 0.75em;
|
||||||
font-family: $font-family-hei;
|
font-family: $font-family-hei;
|
||||||
|
font-style: normal;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
vertical-align: baseline;
|
vertical-align: baseline;
|
||||||
}
|
}
|
||||||
|
@ -155,7 +156,16 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 为所有加划线的元素增加缝隙
|
// 为带划线的元素添加间距,以防止视觉上混为一个元素
|
||||||
|
// 注: 如果设成为两个相连元素添加间距,会有一个问题:
|
||||||
|
// 如果结构是 `<u>倚天</u>和<u>屠龙</u>`,「屠龙」前面仍然会有边距。
|
||||||
|
// 此处跟预期不一致,应该只在两个同名元素紧邻时增加边距,即:`<u>倚天剑</u><u>屠龙刀</u>`
|
||||||
|
//@each $tag in (abbr[title], del, ins, s, u) {
|
||||||
|
// #{$tag} + #{$tag} {
|
||||||
|
// margin-inline-start: 0.125em;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
// 因此采用下面的形式,为所有加划线的元素增加缝隙
|
||||||
abbr[title],
|
abbr[title],
|
||||||
del,
|
del,
|
||||||
ins,
|
ins,
|
||||||
|
@ -164,12 +174,4 @@
|
||||||
margin-inline-start: 1px;
|
margin-inline-start: 1px;
|
||||||
margin-inline-end: 1px;
|
margin-inline-end: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 带边框的元素,两个相连时添加间距,以防止视觉上混为一个元素
|
|
||||||
// @todo: 此处有一个问题:如果结构是 <u>倚天</u>和<u>屠龙</u>,「屠龙」前面仍然会有边距。此处跟预期不一致,应该只在两个同名元素紧邻时增加边距,即:<u>倚天剑</u><u>屠龙刀</u>
|
|
||||||
//@each $tag in (abbr[title], del, ins, s, u) {
|
|
||||||
// #{$tag} + #{$tag} {
|
|
||||||
// margin-inline-start: 0.125em;
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
|
23
lib/helpers/_add-on.scss
Normal file
23
lib/helpers/_add-on.scss
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
// Author: Sivan [sun.sivan@gmail.com]
|
||||||
|
// Description: define add-ons.
|
||||||
|
@import "../variables";
|
||||||
|
|
||||||
|
@mixin hetiAddOns {
|
||||||
|
// 用于中西文混排增加边距
|
||||||
|
heti-spacing {
|
||||||
|
display: inline;
|
||||||
|
|
||||||
|
&.heti-spacing-start {
|
||||||
|
margin-inline-end: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.heti-spacing-end {
|
||||||
|
margin-inline-start: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
& + sup,
|
||||||
|
& + sub {
|
||||||
|
margin-inline-start: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,6 +26,7 @@
|
||||||
&-x-large {
|
&-x-large {
|
||||||
font-size: $font-size-x-large;
|
font-size: $font-size-x-large;
|
||||||
line-height: $line-height-size-x-large;
|
line-height: $line-height-size-x-large;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-small {
|
&-small {
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
@import "modifiers/annotation";
|
@import "modifiers/annotation";
|
||||||
@import "helpers/block";
|
@import "helpers/block";
|
||||||
@import "helpers/inline";
|
@import "helpers/inline";
|
||||||
|
@import "helpers/add-on";
|
||||||
|
|
||||||
#{$root-selector} {
|
#{$root-selector} {
|
||||||
// 中文每行展示文字(CPL)建议在 30~50 之间,默认 42
|
// 中文每行展示文字(CPL)建议在 30~50 之间,默认 42
|
||||||
|
@ -52,4 +53,5 @@
|
||||||
// .heti .heti-verse, .heti .heti-hang
|
// .heti .heti-verse, .heti .heti-hang
|
||||||
@include hetiBlockHelperClasses();
|
@include hetiBlockHelperClasses();
|
||||||
@include hetiInlineHelperClasses();
|
@include hetiInlineHelperClasses();
|
||||||
|
@include hetiAddOns();
|
||||||
}
|
}
|
||||||
|
|
22
package.json
22
package.json
|
@ -10,11 +10,13 @@
|
||||||
"lib": "lib"
|
"lib": "lib"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node-sass -w --output-style=expanded lib/heti.scss _site/heti.min.css",
|
"start": "node-sass -w --output-style=expanded lib/heti.scss _site/heti.debug.css",
|
||||||
"build:expanded": "node-sass lib/heti.scss dist/heti.css --output-style=expanded",
|
"build:expanded": "node-sass lib/heti.scss dist/heti.css --output-style=expanded",
|
||||||
"build:compressed": "node-sass lib/heti.scss dist/heti.min.css --output-style=compressed",
|
"build:compressed": "node-sass lib/heti.scss dist/heti.min.css --output-style=compressed",
|
||||||
"build": "npm run build:expanded && npm run build:compressed",
|
"build:script": "rollup -c",
|
||||||
"test": "npx stylelint --config package.json 'lib/**/*.scss'"
|
"build": "npm run build:expanded && npm run build:compressed && npm run build:script",
|
||||||
|
"test": "npx stylelint --config package.json 'lib/**/*.scss'",
|
||||||
|
"compile": "rollup -c -w"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -33,8 +35,14 @@
|
||||||
"url": "https://github.com/sivan/heti/issues"
|
"url": "https://github.com/sivan/heti/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/sivan/heti#readme",
|
"homepage": "https://github.com/sivan/heti#readme",
|
||||||
|
"dependencies": {
|
||||||
|
"findandreplacedomtext": "^0.4.6"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@rollup/plugin-commonjs": "^11.0.2",
|
||||||
|
"@rollup/plugin-node-resolve": "^7.1.1",
|
||||||
"node-sass": "^4.13.1",
|
"node-sass": "^4.13.1",
|
||||||
|
"rollup-plugin-terser": "^5.2.0",
|
||||||
"stylelint": "^13.0.0",
|
"stylelint": "^13.0.0",
|
||||||
"stylelint-config-recommended-scss": "^4.1.0",
|
"stylelint-config-recommended-scss": "^4.1.0",
|
||||||
"stylelint-config-standard": "^19.0.0",
|
"stylelint-config-standard": "^19.0.0",
|
||||||
|
@ -53,6 +61,14 @@
|
||||||
"selectors-within-list"
|
"selectors-within-list"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"selector-type-no-unknown": [
|
||||||
|
true,
|
||||||
|
{
|
||||||
|
"ignore": [
|
||||||
|
"custom-elements"
|
||||||
|
]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
30
rollup.config.js
Normal file
30
rollup.config.js
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import commonjs from '@rollup/plugin-commonjs';
|
||||||
|
import resolve from '@rollup/plugin-node-resolve';
|
||||||
|
import {terser} from 'rollup-plugin-terser';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
input: 'add-ons/add-on.js',
|
||||||
|
output: [
|
||||||
|
{
|
||||||
|
file: '_site/heti-addon.debug.js',
|
||||||
|
name: 'Heti',
|
||||||
|
format: 'umd'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: 'dist/heti-addon.min.js',
|
||||||
|
format: 'umd',
|
||||||
|
name: 'Heti',
|
||||||
|
plugins: [
|
||||||
|
terser({
|
||||||
|
output: {
|
||||||
|
comments: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]
|
||||||
|
},
|
||||||
|
],
|
||||||
|
plugins: [
|
||||||
|
resolve(),
|
||||||
|
commonjs(),
|
||||||
|
]
|
||||||
|
};
|
Loading…
Add table
Add a link
Reference in a new issue