Home Reference Source

js/models/Language.js

import languages from '#/languages.json';
import ErrorManager from '~/helpers/ErrorManager';

import Normalize from '~/models/Normalize';
import Encoding from '~/models/Encoding';
import Data from '~/models/Data';
import Query from '~/models/Query';

export const InvalidLanguage = Symbol('LanguageError.InvalidLanguage');

/**
 * Info about a programming language
 * @implements {JSONConvertable}
 */
export default class Language {
    /**
     * Creates lanuage with a language id.
     * @param {string} id Language id from languages.json
     */
    constructor(id) {
        // Check if alias
        let alias = languages.alias[id];
        if (alias) return new Language(alias);

        /** @private */
        this.info = languages.languages[id];

        if (!this.info) {
            ErrorManager.raise(`no such language with id \`${id}\``, InvalidLanguage);
        }

        /** @private */
        this.id = id;
    }

    /**
     * User-friendly language name
     * @type {string}
     */
    get displayName() {
        return (this.info && this.info.display) || (this.id[0].toUpperCase() + this.id.substr(1));
    }

    /**
     * Returns absolute URL of the answer.
     */
    get url() {
        return `${Data.shared.envValueForKey('HOST')}/answer/${this.id}`
    }

    /**
     * User-friendly and machine encoding
     * @return {Encoding}
     */
    encoding() {
        return Encoding.fromName(languages.encoding[this.id] || 'UTF-8');
    }

    /**
     * Returns language icon node.
     * @return {HTMLElement}
     */
    icon() {
        return <img src={ this.iconURL }/>;
    }

    /**
     * @type {string}
     */
    get iconURL() {
        return `/lang/logo/${this.id}.svg`
    }

    /**
     * @return {string}
     */
    toString() {
        return this.id;
    }

    /**
     * TIO language id. `null` if langauge does not support TIO.
     * @type {?string}
     */
    get tioId() {
        let tioid = languages.tio[this.id];
        if (tioid === 0) return null;
        return tioid || this.id;
    }

    /**
     * Returns highlight-js id
     * @type {?string}
     */
    get hljsId() {
        let hljsId = languages.highlight[this.id];
        if (hljsId === 1) return this.id;
        return hljsId;
    }

    /**
     * CodeMirror-editor lang def file name.
     * @type {?string}
     */
    get cmName() {
        let cmName = languages.cm[this.id];
        if (!cmName) return null;
        if (cmName === 1) return this.id;
        if (cmName === 2) return `text/x-${this.id}`;
        return cmName;
    }

    /**
     * Byte-counts a JavaScript string (properly encoded)
     * @param {string} string
     */
    byteCount(string) {
        // let byteCount = 0;
        // for (const char of string) {
        //     const codePoint = char.codePointAt(0);

        // }
    }

    /**
     * Checks if two languages are the same
     * @param  {Language} object other language object.
     * @return {boolean} representing if they are the same langauge or not.
     */
    equal(object) {
        return this.id === object.id;
    }

    /**
     * Unwraps from an API JSON object.
     * @param {Object} json JSON object.
     * @return {?Language} object if succesful, `null` if unauthorized.
     * @throws {TypeError} if invalid JSON object
     */
    static fromJSON(json) {
        try {
            return new Language(json);
        } catch(e) {
            return null;
        }
    }

    /**
     * Query object for languages
     * @type {Query}
     */
    static get query() {
        if (this._query !== null) return this._query;

        let query = new Query(
            Language.allLanguages,
            (lang) => lang.displayName
        );

        return query;
    }

    static _langCache = null;
    /**
     * Returns every language.
     * @return {Language[]}
     */
    static get allLanguages() {
        if (Language._langCache) return Language._langCache;

        let langs = [];
        let langIds = Object.keys(languages.languages);
        let i = langIds.length;
        while (--i >= 0) {
            langs.push(new Language(langIds[i]));
        }
        return langs;
    }

    static _query = null;
}