Drop glitch-style account bio fields
Upstream's implementation has been merged a while ago and is the preferred way to set fields, as it is the only one compatible with upstream and is more user-friendly. This commit deletes the legacy glitch-soc FrontMatter functionality in order to clean up the code and make it easier to maintain.
This commit is contained in:
		
							parent
							
								
									c4bec9263c
								
							
						
					
					
						commit
						88b593a63f
					
				
					 5 changed files with 7 additions and 583 deletions
				
			
		| 
						 | 
				
			
			@ -3,8 +3,6 @@
 | 
			
		|||
const { length } = require('stringz');
 | 
			
		||||
const { delegate } = require('rails-ujs');
 | 
			
		||||
 | 
			
		||||
import { processBio } from 'flavours/glitch/util/bio_metadata';
 | 
			
		||||
 | 
			
		||||
delegate(document, '.account_display_name', 'input', ({ target }) => {
 | 
			
		||||
  const nameCounter = document.querySelector('.name-counter');
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -17,8 +15,7 @@ delegate(document, '.account_note', 'input', ({ target }) => {
 | 
			
		|||
  const noteCounter = document.querySelector('.note-counter');
 | 
			
		||||
 | 
			
		||||
  if (noteCounter) {
 | 
			
		||||
    const noteWithoutMetadata = processBio(target.value).text;
 | 
			
		||||
    noteCounter.textContent = 500 - length(noteWithoutMetadata);
 | 
			
		||||
    noteCounter.textContent = 500 - length(target.value);
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,9 +7,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
 | 
			
		|||
import Avatar from 'flavours/glitch/components/avatar';
 | 
			
		||||
import IconButton from 'flavours/glitch/components/icon_button';
 | 
			
		||||
 | 
			
		||||
import emojify from 'flavours/glitch/util/emoji';
 | 
			
		||||
import { me } from 'flavours/glitch/util/initial_state';
 | 
			
		||||
import { processBio } from 'flavours/glitch/util/bio_metadata';
 | 
			
		||||
import classNames from 'classnames';
 | 
			
		||||
 | 
			
		||||
const messages = defineMessages({
 | 
			
		||||
| 
						 | 
				
			
			@ -83,7 +81,7 @@ export default class Header extends ImmutablePureComponent {
 | 
			
		|||
      actionBtn = '';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const { text, metadata } = processBio(account.get('note_emojified'));
 | 
			
		||||
    const content = { __html: account.get('note_emojified') };
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <div className='account__header__wrapper'>
 | 
			
		||||
| 
						 | 
				
			
			@ -104,7 +102,7 @@ export default class Header extends ImmutablePureComponent {
 | 
			
		|||
 | 
			
		||||
            {badge}
 | 
			
		||||
 | 
			
		||||
            <div className='account__header__content' dangerouslySetInnerHTML={{ __html: emojify(text) }} />
 | 
			
		||||
            <div className='account__header__content' dangerouslySetInnerHTML={content} />
 | 
			
		||||
 | 
			
		||||
            {fields.size > 0 && (
 | 
			
		||||
              <div className='account__header__fields'>
 | 
			
		||||
| 
						 | 
				
			
			@ -117,17 +115,6 @@ export default class Header extends ImmutablePureComponent {
 | 
			
		|||
              </div>
 | 
			
		||||
            )}
 | 
			
		||||
 | 
			
		||||
            {fields.size == 0 && metadata.length && (
 | 
			
		||||
              <div className='account__header__fields'>
 | 
			
		||||
                {metadata.map((pair, i) => (
 | 
			
		||||
                  <dl key={i}>
 | 
			
		||||
                    <dt dangerouslySetInnerHTML={{ __html: pair[0] }} title={pair[0]} />
 | 
			
		||||
                    <dd dangerouslySetInnerHTML={{ __html: pair[1] }} title={pair[1]} />
 | 
			
		||||
                  </dl>
 | 
			
		||||
                ))}
 | 
			
		||||
              </div>
 | 
			
		||||
            ) || null}
 | 
			
		||||
 | 
			
		||||
            {info}
 | 
			
		||||
            {mutingInfo}
 | 
			
		||||
            {actionBtn}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,331 +0,0 @@
 | 
			
		|||
/*
 | 
			
		||||
 | 
			
		||||
`util/bio_metadata`
 | 
			
		||||
===================
 | 
			
		||||
 | 
			
		||||
>   For more information on the contents of this file, please contact:
 | 
			
		||||
>
 | 
			
		||||
>   - kibigo! [@kibi@glitch.social]
 | 
			
		||||
 | 
			
		||||
This file provides two functions for dealing with bio metadata. The
 | 
			
		||||
functions are:
 | 
			
		||||
 | 
			
		||||
 -  __`processBio(content)` :__
 | 
			
		||||
    Processes `content` to extract any frontmatter. The returned
 | 
			
		||||
    object has two properties: `text`, which contains the text of
 | 
			
		||||
    `content` sans-frontmatter, and `metadata`, which is an array
 | 
			
		||||
    of key-value pairs (in two-element array format). If no
 | 
			
		||||
    frontmatter was provided in `content`, then `metadata` will be
 | 
			
		||||
    an empty array.
 | 
			
		||||
 | 
			
		||||
 -  __`createBio(note, data)` :__
 | 
			
		||||
    Reverses the process in `processBio()`; takes a `note` and an
 | 
			
		||||
    array of two-element arrays (which should give keys and values)
 | 
			
		||||
    and outputs a string containing a well-formed bio with
 | 
			
		||||
    frontmatter.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
//  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 | 
			
		||||
 | 
			
		||||
/*********************************************************************\
 | 
			
		||||
 | 
			
		||||
                                       To my lovely code maintainers,
 | 
			
		||||
 | 
			
		||||
  The syntax recognized by the Mastodon frontend for its bio metadata
 | 
			
		||||
  feature is a subset of that provided by the YAML 1.2 specification.
 | 
			
		||||
  In particular, Mastodon recognizes metadata which is provided as an
 | 
			
		||||
  implicit YAML map, where each key-value pair takes up only a single
 | 
			
		||||
  line (no multi-line values are permitted). To simplify the level of
 | 
			
		||||
  processing required, Mastodon metadata frontmatter has been limited
 | 
			
		||||
  to only allow those characters in the `c-printable` set, as defined
 | 
			
		||||
  by the YAML 1.2 specification, instead of permitting those from the
 | 
			
		||||
  `nb-json` characters inside double-quoted strings like YAML proper.
 | 
			
		||||
    ¶ It is important to note that Mastodon only borrows the *syntax*
 | 
			
		||||
  of YAML, not its semantics. This is to say, Mastodon won't make any
 | 
			
		||||
  attempt to interpret the data it receives. `true` will not become a
 | 
			
		||||
  boolean; `56` will not be interpreted as a number. Rather, each key
 | 
			
		||||
  and every value will be read as a string, and as a string they will
 | 
			
		||||
  remain. The order of the pairs is unchanged, and any duplicate keys
 | 
			
		||||
  are preserved. However, YAML escape sequences will be replaced with
 | 
			
		||||
  the proper interpretations according to the YAML 1.2 specification.
 | 
			
		||||
    ¶ The implementation provided below interprets `<br>` as `\n` and
 | 
			
		||||
  allows for an open <p> tag at the beginning of the bio. It replaces
 | 
			
		||||
  the escaped character entities `'` and `"` with single or
 | 
			
		||||
  double quotes, respectively, prior to processing. However, no other
 | 
			
		||||
  escaped characters are replaced, not even those which might have an
 | 
			
		||||
  impact on the syntax otherwise. These minor allowances are provided
 | 
			
		||||
  because the Mastodon backend will insert these things automatically
 | 
			
		||||
  into a bio before sending it through the API, so it is important we
 | 
			
		||||
  account for them. Aside from this, the YAML frontmatter must be the
 | 
			
		||||
  very first thing in the bio, leading with three consecutive hyphen-
 | 
			
		||||
  minues (`---`), and ending with the same or, alternatively, instead
 | 
			
		||||
  with three periods (`...`). No limits have been set with respect to
 | 
			
		||||
  the number of characters permitted in the frontmatter, although one
 | 
			
		||||
  should note that only limited space is provided for them in the UI.
 | 
			
		||||
    ¶ The regular expression used to check the existence of, and then
 | 
			
		||||
  process, the YAML frontmatter has been split into a number of small
 | 
			
		||||
  components in the code below, in the vain hope that it will be much
 | 
			
		||||
  easier to read and to maintain. I leave it to the future readers of
 | 
			
		||||
  this code to determine the extent of my successes in this endeavor.
 | 
			
		||||
 | 
			
		||||
  UPDATE 19 Oct 2017: We no longer allow character escapes inside our
 | 
			
		||||
  double-quoted strings for ease of processing. We now internally use
 | 
			
		||||
  the name "ƔAML" in our code to clarify that this is Not Quite YAML.
 | 
			
		||||
 | 
			
		||||
                                       Sending love + warmth eternal,
 | 
			
		||||
                                       - kibigo [@kibi@glitch.social]
 | 
			
		||||
 | 
			
		||||
\*********************************************************************/
 | 
			
		||||
 | 
			
		||||
/*  "u" FLAG COMPATABILITY  */
 | 
			
		||||
 | 
			
		||||
let compat_mode = false;
 | 
			
		||||
try {
 | 
			
		||||
  new RegExp('.', 'u');
 | 
			
		||||
} catch (e) {
 | 
			
		||||
  compat_mode = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*  CONVENIENCE FUNCTIONS  */
 | 
			
		||||
 | 
			
		||||
const unirex = str => compat_mode ? new RegExp(str) : new RegExp(str, 'u');
 | 
			
		||||
const rexstr = exp => '(?:' + exp.source + ')';
 | 
			
		||||
 | 
			
		||||
/*  CHARACTER CLASSES  */
 | 
			
		||||
 | 
			
		||||
const DOCUMENT_START    = /^/;
 | 
			
		||||
const DOCUMENT_END      = /$/;
 | 
			
		||||
const ALLOWED_CHAR      =  unirex( //  `c-printable` in the YAML 1.2 spec.
 | 
			
		||||
  compat_mode ? '[\t\n\r\x20-\x7e\x85\xa0-\ufffd]' : '[\t\n\r\x20-\x7e\x85\xa0-\ud7ff\ue000-\ufffd\u{10000}-\u{10FFFF}]'
 | 
			
		||||
);
 | 
			
		||||
const WHITE_SPACE       = /[ \t]/;
 | 
			
		||||
const LINE_BREAK        = /\r?\n|\r|<br\s*\/?>/;
 | 
			
		||||
const INDICATOR         = /[-?:,[\]{}&#*!|>'"%@`]/;
 | 
			
		||||
const FLOW_CHAR         = /[,[\]{}]/;
 | 
			
		||||
 | 
			
		||||
/*  NEGATED CHARACTER CLASSES  */
 | 
			
		||||
 | 
			
		||||
const NOT_WHITE_SPACE   = unirex('(?!' + rexstr(WHITE_SPACE) + ')[^]');
 | 
			
		||||
const NOT_LINE_BREAK    = unirex('(?!' + rexstr(LINE_BREAK) + ')[^]');
 | 
			
		||||
const NOT_INDICATOR     = unirex('(?!' + rexstr(INDICATOR) + ')[^]');
 | 
			
		||||
const NOT_FLOW_CHAR     = unirex('(?!' + rexstr(FLOW_CHAR) + ')[^]');
 | 
			
		||||
const NOT_ALLOWED_CHAR  = unirex(
 | 
			
		||||
  '(?!' + rexstr(ALLOWED_CHAR) + ')[^]'
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
/*  BASIC CONSTRUCTS  */
 | 
			
		||||
 | 
			
		||||
const ANY_WHITE_SPACE   = unirex(rexstr(WHITE_SPACE) + '*');
 | 
			
		||||
const ANY_ALLOWED_CHARS = unirex(rexstr(ALLOWED_CHAR) + '*');
 | 
			
		||||
const NEW_LINE          = unirex(
 | 
			
		||||
  rexstr(ANY_WHITE_SPACE) + rexstr(LINE_BREAK)
 | 
			
		||||
);
 | 
			
		||||
const SOME_NEW_LINES    = unirex(
 | 
			
		||||
  '(?:' + rexstr(NEW_LINE) + ')+'
 | 
			
		||||
);
 | 
			
		||||
const POSSIBLE_STARTS   = unirex(
 | 
			
		||||
  rexstr(DOCUMENT_START) + rexstr(/<p[^<>]*>/) + '?'
 | 
			
		||||
);
 | 
			
		||||
const POSSIBLE_ENDS     = unirex(
 | 
			
		||||
  rexstr(SOME_NEW_LINES) + '|' +
 | 
			
		||||
  rexstr(DOCUMENT_END) + '|' +
 | 
			
		||||
  rexstr(/<\/p>/)
 | 
			
		||||
);
 | 
			
		||||
const QUOTE_CHAR         = unirex(
 | 
			
		||||
  '(?=' + rexstr(NOT_LINE_BREAK) + ')[^"]'
 | 
			
		||||
);
 | 
			
		||||
const ANY_QUOTE_CHAR    = unirex(
 | 
			
		||||
  rexstr(QUOTE_CHAR) + '*'
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const ESCAPED_APOS      = unirex(
 | 
			
		||||
  '(?=' + rexstr(NOT_LINE_BREAK) + ')' + rexstr(/[^']|''/)
 | 
			
		||||
);
 | 
			
		||||
const ANY_ESCAPED_APOS  = unirex(
 | 
			
		||||
  rexstr(ESCAPED_APOS) + '*'
 | 
			
		||||
);
 | 
			
		||||
const FIRST_KEY_CHAR    = unirex(
 | 
			
		||||
  '(?=' + rexstr(NOT_LINE_BREAK) + ')' +
 | 
			
		||||
  '(?=' + rexstr(NOT_WHITE_SPACE) + ')' +
 | 
			
		||||
  rexstr(NOT_INDICATOR) + '|' +
 | 
			
		||||
  rexstr(/[?:-]/) +
 | 
			
		||||
  '(?=' + rexstr(NOT_LINE_BREAK) + ')' +
 | 
			
		||||
  '(?=' + rexstr(NOT_WHITE_SPACE) + ')' +
 | 
			
		||||
  '(?=' + rexstr(NOT_FLOW_CHAR) + ')'
 | 
			
		||||
);
 | 
			
		||||
const FIRST_VALUE_CHAR  = unirex(
 | 
			
		||||
  '(?=' + rexstr(NOT_LINE_BREAK) + ')' +
 | 
			
		||||
  '(?=' + rexstr(NOT_WHITE_SPACE) + ')' +
 | 
			
		||||
  rexstr(NOT_INDICATOR) + '|' +
 | 
			
		||||
  rexstr(/[?:-]/) +
 | 
			
		||||
  '(?=' + rexstr(NOT_LINE_BREAK) + ')' +
 | 
			
		||||
  '(?=' + rexstr(NOT_WHITE_SPACE) + ')'
 | 
			
		||||
  //  Flow indicators are allowed in values.
 | 
			
		||||
);
 | 
			
		||||
const LATER_KEY_CHAR    = unirex(
 | 
			
		||||
  rexstr(WHITE_SPACE) + '|' +
 | 
			
		||||
  '(?=' + rexstr(NOT_LINE_BREAK) + ')' +
 | 
			
		||||
  '(?=' + rexstr(NOT_WHITE_SPACE) + ')' +
 | 
			
		||||
  '(?=' + rexstr(NOT_FLOW_CHAR) + ')' +
 | 
			
		||||
  rexstr(/[^:#]#?/) + '|' +
 | 
			
		||||
  rexstr(/:/) + '(?=' + rexstr(NOT_WHITE_SPACE) + ')'
 | 
			
		||||
);
 | 
			
		||||
const LATER_VALUE_CHAR  = unirex(
 | 
			
		||||
  rexstr(WHITE_SPACE) + '|' +
 | 
			
		||||
  '(?=' + rexstr(NOT_LINE_BREAK) + ')' +
 | 
			
		||||
  '(?=' + rexstr(NOT_WHITE_SPACE) + ')' +
 | 
			
		||||
  //  Flow indicators are allowed in values.
 | 
			
		||||
  rexstr(/[^:#]#?/) + '|' +
 | 
			
		||||
  rexstr(/:/) + '(?=' + rexstr(NOT_WHITE_SPACE) + ')'
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
/*  YAML CONSTRUCTS  */
 | 
			
		||||
 | 
			
		||||
const ƔAML_START        = unirex(
 | 
			
		||||
  rexstr(ANY_WHITE_SPACE) + '---'
 | 
			
		||||
);
 | 
			
		||||
const ƔAML_END          = unirex(
 | 
			
		||||
  rexstr(ANY_WHITE_SPACE) + '(?:---|\.\.\.)'
 | 
			
		||||
);
 | 
			
		||||
const ƔAML_LOOKAHEAD    = unirex(
 | 
			
		||||
  '(?=' +
 | 
			
		||||
    rexstr(ƔAML_START) +
 | 
			
		||||
    rexstr(ANY_ALLOWED_CHARS) + rexstr(NEW_LINE) +
 | 
			
		||||
    rexstr(ƔAML_END) + rexstr(POSSIBLE_ENDS) +
 | 
			
		||||
  ')'
 | 
			
		||||
);
 | 
			
		||||
const ƔAML_DOUBLE_QUOTE = unirex(
 | 
			
		||||
  '"' + rexstr(ANY_QUOTE_CHAR) + '"'
 | 
			
		||||
);
 | 
			
		||||
const ƔAML_SINGLE_QUOTE = unirex(
 | 
			
		||||
  '\'' + rexstr(ANY_ESCAPED_APOS) + '\''
 | 
			
		||||
);
 | 
			
		||||
const ƔAML_SIMPLE_KEY   = unirex(
 | 
			
		||||
  rexstr(FIRST_KEY_CHAR) + rexstr(LATER_KEY_CHAR) + '*'
 | 
			
		||||
);
 | 
			
		||||
const ƔAML_SIMPLE_VALUE = unirex(
 | 
			
		||||
  rexstr(FIRST_VALUE_CHAR) + rexstr(LATER_VALUE_CHAR) + '*'
 | 
			
		||||
);
 | 
			
		||||
const ƔAML_KEY          = unirex(
 | 
			
		||||
  rexstr(ƔAML_DOUBLE_QUOTE) + '|' +
 | 
			
		||||
  rexstr(ƔAML_SINGLE_QUOTE) + '|' +
 | 
			
		||||
  rexstr(ƔAML_SIMPLE_KEY)
 | 
			
		||||
);
 | 
			
		||||
const ƔAML_VALUE        = unirex(
 | 
			
		||||
  rexstr(ƔAML_DOUBLE_QUOTE) + '|' +
 | 
			
		||||
  rexstr(ƔAML_SINGLE_QUOTE) + '|' +
 | 
			
		||||
  rexstr(ƔAML_SIMPLE_VALUE)
 | 
			
		||||
);
 | 
			
		||||
const ƔAML_SEPARATOR    = unirex(
 | 
			
		||||
  rexstr(ANY_WHITE_SPACE) +
 | 
			
		||||
  ':' + rexstr(WHITE_SPACE) +
 | 
			
		||||
  rexstr(ANY_WHITE_SPACE)
 | 
			
		||||
);
 | 
			
		||||
const ƔAML_LINE         = unirex(
 | 
			
		||||
  '(' + rexstr(ƔAML_KEY) + ')' +
 | 
			
		||||
  rexstr(ƔAML_SEPARATOR) +
 | 
			
		||||
  '(' + rexstr(ƔAML_VALUE) + ')'
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
/*  FRONTMATTER REGEX  */
 | 
			
		||||
 | 
			
		||||
const ƔAML_FRONTMATTER  = unirex(
 | 
			
		||||
  rexstr(POSSIBLE_STARTS) +
 | 
			
		||||
  rexstr(ƔAML_LOOKAHEAD) +
 | 
			
		||||
  rexstr(ƔAML_START) + rexstr(SOME_NEW_LINES) +
 | 
			
		||||
  '(?:' +
 | 
			
		||||
    rexstr(ANY_WHITE_SPACE) + rexstr(ƔAML_LINE) + rexstr(SOME_NEW_LINES) +
 | 
			
		||||
  '){0,5}' +
 | 
			
		||||
  rexstr(ƔAML_END) + rexstr(POSSIBLE_ENDS)
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
/*  SEARCHES  */
 | 
			
		||||
 | 
			
		||||
const FIND_ƔAML_LINE    = unirex(
 | 
			
		||||
  rexstr(NEW_LINE) + rexstr(ANY_WHITE_SPACE) + rexstr(ƔAML_LINE)
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
/*  STRING PROCESSING  */
 | 
			
		||||
 | 
			
		||||
function processString (str) {
 | 
			
		||||
  switch (str.charAt(0)) {
 | 
			
		||||
  case '"':
 | 
			
		||||
    return str.substring(1, str.length - 1);
 | 
			
		||||
  case '\'':
 | 
			
		||||
    return str
 | 
			
		||||
      .substring(1, str.length - 1)
 | 
			
		||||
      .replace(/''/g, '\'');
 | 
			
		||||
  default:
 | 
			
		||||
    return str;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*  BIO PROCESSING  */
 | 
			
		||||
 | 
			
		||||
export function processBio(content) {
 | 
			
		||||
  content = content.replace(/"/g, '"').replace(/'/g, '\'');
 | 
			
		||||
  let result = {
 | 
			
		||||
    text: content,
 | 
			
		||||
    metadata: [],
 | 
			
		||||
  };
 | 
			
		||||
  let ɣaml = content.match(ƔAML_FRONTMATTER);
 | 
			
		||||
  if (!ɣaml) {
 | 
			
		||||
    return result;
 | 
			
		||||
  } else {
 | 
			
		||||
    ɣaml = ɣaml[0];
 | 
			
		||||
  }
 | 
			
		||||
  const start = content.search(ƔAML_START);
 | 
			
		||||
  const end = start + ɣaml.length - ɣaml.search(ƔAML_START);
 | 
			
		||||
  result.text = content.substr(end);
 | 
			
		||||
  let metadata = null;
 | 
			
		||||
  let query = new RegExp(rexstr(FIND_ƔAML_LINE), 'g');  //  Some browsers don't allow flags unless both args are strings
 | 
			
		||||
  while ((metadata = query.exec(ɣaml))) {
 | 
			
		||||
    result.metadata.push([
 | 
			
		||||
      processString(metadata[1]),
 | 
			
		||||
      processString(metadata[2]),
 | 
			
		||||
    ]);
 | 
			
		||||
  }
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*  BIO CREATION  */
 | 
			
		||||
 | 
			
		||||
export function createBio(note, data) {
 | 
			
		||||
  if (!note) note = '';
 | 
			
		||||
  let frontmatter = '';
 | 
			
		||||
  if ((data && data.length) || note.match(/^\s*---\s+/)) {
 | 
			
		||||
    if (!data) frontmatter = '---\n...\n';
 | 
			
		||||
    else {
 | 
			
		||||
      frontmatter += '---\n';
 | 
			
		||||
      for (let i = 0; i < data.length; i++) {
 | 
			
		||||
        let key = '' + data[i][0];
 | 
			
		||||
        let val = '' + data[i][1];
 | 
			
		||||
 | 
			
		||||
        //  Key processing
 | 
			
		||||
        if (key === (key.match(ƔAML_SIMPLE_KEY) || [])[0]) /*  do nothing  */;
 | 
			
		||||
        else if (key === (key.match(ANY_QUOTE_CHAR) || [])[0]) key = '"' + key + '"';
 | 
			
		||||
        else {
 | 
			
		||||
          key = key
 | 
			
		||||
            .replace(/'/g, '\'\'')
 | 
			
		||||
            .replace(new RegExp(rexstr(NOT_ALLOWED_CHAR), compat_mode ? 'g' : 'gu'), '<27>');
 | 
			
		||||
          key = '\'' + key + '\'';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //  Value processing
 | 
			
		||||
        if (val === (val.match(ƔAML_SIMPLE_VALUE) || [])[0]) /*  do nothing  */;
 | 
			
		||||
        else if (val === (val.match(ANY_QUOTE_CHAR) || [])[0]) val = '"' + val + '"';
 | 
			
		||||
        else {
 | 
			
		||||
          key = key
 | 
			
		||||
            .replace(/'/g, '\'\'')
 | 
			
		||||
            .replace(new RegExp(rexstr(NOT_ALLOWED_CHAR), compat_mode ? 'g' : 'gu'), '<27>');
 | 
			
		||||
          key = '\'' + key + '\'';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        frontmatter += key + ': ' + val + '\n';
 | 
			
		||||
      }
 | 
			
		||||
      frontmatter += '...\n';
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return frontmatter + note;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,223 +0,0 @@
 | 
			
		|||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
require 'singleton'
 | 
			
		||||
 | 
			
		||||
#  See also `app/javascript/features/account/util/bio_metadata.js`.
 | 
			
		||||
 | 
			
		||||
class FrontmatterHandler
 | 
			
		||||
  include Singleton
 | 
			
		||||
 | 
			
		||||
  #  CONVENIENCE FUNCTIONS  #
 | 
			
		||||
 | 
			
		||||
  def self.unirex(str)
 | 
			
		||||
    Regexp.new str, Regexp::MULTILINE
 | 
			
		||||
  end
 | 
			
		||||
  def self.rexstr(exp)
 | 
			
		||||
    '(?:' + exp.source + ')'
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  #  CHARACTER CLASSES  #
 | 
			
		||||
 | 
			
		||||
  DOCUMENT_START    = /^/
 | 
			
		||||
  DOCUMENT_END      = /$/
 | 
			
		||||
  ALLOWED_CHAR      =  #  c-printable` in the YAML 1.2 spec.
 | 
			
		||||
    /[\t\n\r\u{20}-\u{7e}\u{85}\u{a0}-\u{d7ff}\u{e000}-\u{fffd}\u{10000}-\u{10ffff}]/u
 | 
			
		||||
  WHITE_SPACE       = /[ \t]/
 | 
			
		||||
  INDENTATION       = / */
 | 
			
		||||
  LINE_BREAK        = /\r?\n|\r|<br\s*\/?>/
 | 
			
		||||
  ESCAPE_CHAR       = /[0abt\tnvfre "\/\\N_LP]/
 | 
			
		||||
  HEXADECIMAL_CHARS = /[0-9a-fA-F]/
 | 
			
		||||
  INDICATOR         = /[-?:,\[\]{}&#*!|>'"%@`]/
 | 
			
		||||
  FLOW_CHAR         = /[,\[\]{}]/
 | 
			
		||||
 | 
			
		||||
  #  NEGATED CHARACTER CLASSES  #
 | 
			
		||||
 | 
			
		||||
  NOT_WHITE_SPACE   = unirex '(?!' + rexstr(WHITE_SPACE) + ').'
 | 
			
		||||
  NOT_LINE_BREAK    = unirex '(?!' + rexstr(LINE_BREAK) + ').'
 | 
			
		||||
  NOT_INDICATOR     = unirex '(?!' + rexstr(INDICATOR) + ').'
 | 
			
		||||
  NOT_FLOW_CHAR     = unirex '(?!' + rexstr(FLOW_CHAR) + ').'
 | 
			
		||||
  NOT_ALLOWED_CHAR  = unirex '(?!' + rexstr(ALLOWED_CHAR) + ').'
 | 
			
		||||
 | 
			
		||||
  #  BASIC CONSTRUCTS  #
 | 
			
		||||
 | 
			
		||||
  ANY_WHITE_SPACE   = unirex rexstr(WHITE_SPACE) + '*'
 | 
			
		||||
  ANY_ALLOWED_CHARS = unirex rexstr(ALLOWED_CHAR) + '*'
 | 
			
		||||
  NEW_LINE          = unirex(
 | 
			
		||||
    rexstr(ANY_WHITE_SPACE) + rexstr(LINE_BREAK)
 | 
			
		||||
  )
 | 
			
		||||
  SOME_NEW_LINES    = unirex(
 | 
			
		||||
    '(?:' + rexstr(ANY_WHITE_SPACE) + rexstr(LINE_BREAK) + ')+'
 | 
			
		||||
  )
 | 
			
		||||
  POSSIBLE_STARTS   = unirex(
 | 
			
		||||
    rexstr(DOCUMENT_START) + rexstr(/<p[^<>]*>/) + '?'
 | 
			
		||||
  )
 | 
			
		||||
  POSSIBLE_ENDS     = unirex(
 | 
			
		||||
    rexstr(SOME_NEW_LINES) + '|' +
 | 
			
		||||
    rexstr(DOCUMENT_END) + '|' +
 | 
			
		||||
    rexstr(/<\/p>/)
 | 
			
		||||
  )
 | 
			
		||||
  CHARACTER_ESCAPE  = unirex(
 | 
			
		||||
    rexstr(/\\/) +
 | 
			
		||||
    '(?:' +
 | 
			
		||||
      rexstr(ESCAPE_CHAR) + '|' +
 | 
			
		||||
      rexstr(/x/) + rexstr(HEXADECIMAL_CHARS) + '{2}' + '|' +
 | 
			
		||||
      rexstr(/u/) + rexstr(HEXADECIMAL_CHARS) + '{4}' + '|' +
 | 
			
		||||
      rexstr(/U/) + rexstr(HEXADECIMAL_CHARS) + '{8}' +
 | 
			
		||||
    ')'
 | 
			
		||||
  )
 | 
			
		||||
  ESCAPED_CHAR      = unirex(
 | 
			
		||||
    rexstr(/(?!["\\])/) + rexstr(NOT_LINE_BREAK) + '|' +
 | 
			
		||||
    rexstr(CHARACTER_ESCAPE)
 | 
			
		||||
  )
 | 
			
		||||
  ANY_ESCAPED_CHARS = unirex(
 | 
			
		||||
    rexstr(ESCAPED_CHAR) + '*'
 | 
			
		||||
  )
 | 
			
		||||
  ESCAPED_APOS      = unirex(
 | 
			
		||||
    '(?=' + rexstr(NOT_LINE_BREAK) + ')' + rexstr(/[^']|''/)
 | 
			
		||||
  )
 | 
			
		||||
  ANY_ESCAPED_APOS  = unirex(
 | 
			
		||||
    rexstr(ESCAPED_APOS) + '*'
 | 
			
		||||
  )
 | 
			
		||||
  FIRST_KEY_CHAR    = unirex(
 | 
			
		||||
    '(?=' + rexstr(NOT_LINE_BREAK) + ')' +
 | 
			
		||||
    '(?=' + rexstr(NOT_WHITE_SPACE) + ')' +
 | 
			
		||||
    rexstr(NOT_INDICATOR) + '|' +
 | 
			
		||||
    rexstr(/[?:-]/) +
 | 
			
		||||
    '(?=' + rexstr(NOT_LINE_BREAK) + ')' +
 | 
			
		||||
    '(?=' + rexstr(NOT_WHITE_SPACE) + ')' +
 | 
			
		||||
    '(?=' + rexstr(NOT_FLOW_CHAR) + ')'
 | 
			
		||||
  )
 | 
			
		||||
  FIRST_VALUE_CHAR  = unirex(
 | 
			
		||||
    '(?=' + rexstr(NOT_LINE_BREAK) + ')' +
 | 
			
		||||
    '(?=' + rexstr(NOT_WHITE_SPACE) + ')' +
 | 
			
		||||
    rexstr(NOT_INDICATOR) + '|' +
 | 
			
		||||
    rexstr(/[?:-]/) +
 | 
			
		||||
    '(?=' + rexstr(NOT_LINE_BREAK) + ')' +
 | 
			
		||||
    '(?=' + rexstr(NOT_WHITE_SPACE) + ')'
 | 
			
		||||
    #  Flow indicators are allowed in values.
 | 
			
		||||
  )
 | 
			
		||||
  LATER_KEY_CHAR    = unirex(
 | 
			
		||||
    rexstr(WHITE_SPACE) + '|' +
 | 
			
		||||
    '(?=' + rexstr(NOT_LINE_BREAK) + ')' +
 | 
			
		||||
    '(?=' + rexstr(NOT_WHITE_SPACE) + ')' +
 | 
			
		||||
    '(?=' + rexstr(NOT_FLOW_CHAR) + ')' +
 | 
			
		||||
    rexstr(/[^:#]#?/) + '|' +
 | 
			
		||||
    rexstr(/:/) + '(?=' + rexstr(NOT_WHITE_SPACE) + ')'
 | 
			
		||||
  )
 | 
			
		||||
  LATER_VALUE_CHAR  = unirex(
 | 
			
		||||
    rexstr(WHITE_SPACE) + '|' +
 | 
			
		||||
    '(?=' + rexstr(NOT_LINE_BREAK) + ')' +
 | 
			
		||||
    '(?=' + rexstr(NOT_WHITE_SPACE) + ')' +
 | 
			
		||||
    #  Flow indicators are allowed in values.
 | 
			
		||||
    rexstr(/[^:#]#?/) + '|' +
 | 
			
		||||
    rexstr(/:/) + '(?=' + rexstr(NOT_WHITE_SPACE) + ')'
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  #  YAML CONSTRUCTS  #
 | 
			
		||||
 | 
			
		||||
  YAML_START        = unirex(
 | 
			
		||||
    rexstr(ANY_WHITE_SPACE) + rexstr(/---/)
 | 
			
		||||
  )
 | 
			
		||||
  YAML_END          = unirex(
 | 
			
		||||
    rexstr(ANY_WHITE_SPACE) + rexstr(/(?:---|\.\.\.)/)
 | 
			
		||||
  )
 | 
			
		||||
  YAML_LOOKAHEAD    = unirex(
 | 
			
		||||
    '(?=' +
 | 
			
		||||
      rexstr(YAML_START) +
 | 
			
		||||
      rexstr(ANY_ALLOWED_CHARS) + rexstr(NEW_LINE) +
 | 
			
		||||
      rexstr(YAML_END) + rexstr(POSSIBLE_ENDS) +
 | 
			
		||||
    ')'
 | 
			
		||||
  )
 | 
			
		||||
  YAML_DOUBLE_QUOTE = unirex(
 | 
			
		||||
    rexstr(/"/) + rexstr(ANY_ESCAPED_CHARS) + rexstr(/"/)
 | 
			
		||||
  )
 | 
			
		||||
  YAML_SINGLE_QUOTE = unirex(
 | 
			
		||||
    rexstr(/'/) + rexstr(ANY_ESCAPED_APOS) + rexstr(/'/)
 | 
			
		||||
  )
 | 
			
		||||
  YAML_SIMPLE_KEY   = unirex(
 | 
			
		||||
    rexstr(FIRST_KEY_CHAR) + rexstr(LATER_KEY_CHAR) + '*'
 | 
			
		||||
  )
 | 
			
		||||
  YAML_SIMPLE_VALUE = unirex(
 | 
			
		||||
    rexstr(FIRST_VALUE_CHAR) + rexstr(LATER_VALUE_CHAR) + '*'
 | 
			
		||||
  )
 | 
			
		||||
  YAML_KEY          = unirex(
 | 
			
		||||
    rexstr(YAML_DOUBLE_QUOTE) + '|' +
 | 
			
		||||
    rexstr(YAML_SINGLE_QUOTE) + '|' +
 | 
			
		||||
    rexstr(YAML_SIMPLE_KEY)
 | 
			
		||||
  )
 | 
			
		||||
  YAML_VALUE        = unirex(
 | 
			
		||||
    rexstr(YAML_DOUBLE_QUOTE) + '|' +
 | 
			
		||||
    rexstr(YAML_SINGLE_QUOTE) + '|' +
 | 
			
		||||
    rexstr(YAML_SIMPLE_VALUE)
 | 
			
		||||
  )
 | 
			
		||||
  YAML_SEPARATOR    = unirex(
 | 
			
		||||
    rexstr(ANY_WHITE_SPACE) +
 | 
			
		||||
    ':' + rexstr(WHITE_SPACE) +
 | 
			
		||||
    rexstr(ANY_WHITE_SPACE)
 | 
			
		||||
  )
 | 
			
		||||
  YAML_LINE         = unirex(
 | 
			
		||||
    '(' + rexstr(YAML_KEY) + ')' +
 | 
			
		||||
    rexstr(YAML_SEPARATOR) +
 | 
			
		||||
    '(' + rexstr(YAML_VALUE) + ')'
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  #  FRONTMATTER REGEX  #
 | 
			
		||||
 | 
			
		||||
  YAML_FRONTMATTER  = unirex(
 | 
			
		||||
    rexstr(POSSIBLE_STARTS) +
 | 
			
		||||
    rexstr(YAML_LOOKAHEAD) +
 | 
			
		||||
    rexstr(YAML_START) + rexstr(SOME_NEW_LINES) +
 | 
			
		||||
    '(?:' +
 | 
			
		||||
      '(' + rexstr(INDENTATION) + ')' +
 | 
			
		||||
      rexstr(YAML_LINE) + rexstr(SOME_NEW_LINES) +
 | 
			
		||||
      '(?:' +
 | 
			
		||||
        '\\1' + rexstr(YAML_LINE) + rexstr(SOME_NEW_LINES) +
 | 
			
		||||
      '){0,4}' +
 | 
			
		||||
    ')?' +
 | 
			
		||||
    rexstr(YAML_END) + rexstr(POSSIBLE_ENDS)
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  #  SEARCHES  #
 | 
			
		||||
 | 
			
		||||
  FIND_YAML_LINES   = unirex(
 | 
			
		||||
    rexstr(NEW_LINE) + rexstr(INDENTATION) + rexstr(YAML_LINE)
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  #  STRING PROCESSING  #
 | 
			
		||||
 | 
			
		||||
  def process_string(str)
 | 
			
		||||
    case str[0]
 | 
			
		||||
    when '"'
 | 
			
		||||
      str[1..-2]
 | 
			
		||||
    when "'"
 | 
			
		||||
      str[1..-2].gsub(/''/, "'")
 | 
			
		||||
    else
 | 
			
		||||
      str
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  #  BIO PROCESSING  #
 | 
			
		||||
 | 
			
		||||
  def process_bio content
 | 
			
		||||
    result = {
 | 
			
		||||
      text: content.gsub(/"/, '"').gsub(/'/, "'"),
 | 
			
		||||
      metadata: []
 | 
			
		||||
    }
 | 
			
		||||
    yaml = YAML_FRONTMATTER.match(result[:text])
 | 
			
		||||
    return result unless yaml
 | 
			
		||||
    yaml = yaml[0]
 | 
			
		||||
    start = YAML_START =~ result[:text]
 | 
			
		||||
    ending = start + yaml.length - (YAML_START =~ yaml)
 | 
			
		||||
    result[:text][start..ending - 1] = ''
 | 
			
		||||
    metadata = nil
 | 
			
		||||
    index = 0
 | 
			
		||||
    while metadata = FIND_YAML_LINES.match(yaml, index) do
 | 
			
		||||
      index = metadata.end(0)
 | 
			
		||||
      result[:metadata].push [
 | 
			
		||||
        process_string(metadata[1]), process_string(metadata[2])
 | 
			
		||||
      ]
 | 
			
		||||
    end
 | 
			
		||||
    return result
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			@ -1,4 +1,3 @@
 | 
			
		|||
- processed_bio = FrontmatterHandler.instance.process_bio Formatter.instance.simplified_format(account, custom_emojify: true)
 | 
			
		||||
.card.h-card.p-author{ style: "background-image: url(#{account.header.url(:original)})" }
 | 
			
		||||
  .card__illustration
 | 
			
		||||
    = render 'accounts/follow_button', account: account
 | 
			
		||||
| 
						 | 
				
			
			@ -24,21 +23,16 @@
 | 
			
		|||
        .roles
 | 
			
		||||
          .account-role.moderator
 | 
			
		||||
            = t 'accounts.roles.moderator'
 | 
			
		||||
    .bio
 | 
			
		||||
      .account__header__content.p-note.emojify!=processed_bio[:text]
 | 
			
		||||
 | 
			
		||||
      - if !account.fields.empty?
 | 
			
		||||
    .bio
 | 
			
		||||
      .account__header__content.p-note.emojify= Formatter.instance.simplified_format(account, custom_emojify: true)
 | 
			
		||||
 | 
			
		||||
      - unless account.fields.empty?
 | 
			
		||||
        .account__header__fields
 | 
			
		||||
          - account.fields.each do |field|
 | 
			
		||||
            %dl
 | 
			
		||||
              %dt.emojify{ title: field.name }= field.name
 | 
			
		||||
              %dd.emojify{ title: field.value }= Formatter.instance.format_field(account, field.value, custom_emojify: true)
 | 
			
		||||
      - elsif processed_bio[:metadata].length > 0
 | 
			
		||||
        .account__header__fields
 | 
			
		||||
          - processed_bio[:metadata].each do |i|
 | 
			
		||||
            %dl
 | 
			
		||||
              %dt.emojify{ title: i[0] }!= i[0]
 | 
			
		||||
              %dd.emojify{ title: i[1] }!= i[1]
 | 
			
		||||
 | 
			
		||||
    .details-counters
 | 
			
		||||
      .counter{ class: active_nav_class(short_account_url(account)) }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue