/**
* Module that populates the {@link convenientDiscussions} object.
*
* @module convenientDiscussions
*/
import Comment from './Comment';
import cd from './cd';
import controller from './controller';
import debug from './debug';
import pageRegistry from './pageRegistry';
import { buildEditSummary, getQueryParamBooleanValue, underlinesToSpaces } from './utils-general';
import { wrapDiffBody, wrapHtml } from './utils-window';
const mwStringsCache = {};
let isQqxMode;
const serverName = mw.config.get('wgServerName');
// For historical reasons, ru.wikipedia.org has 'cd'.
const localOptionsPrefix = serverName === 'ru.wikipedia.org' ? 'cd' : 'convenientDiscussions';
// For historical reasons, ru.wikipedia.org has 'watchedTopics'.
const subscriptionsOptionNameEnding = serverName === 'ru.wikipedia.org' ?
'watchedTopics' :
'watchedSections';
let server = mw.config.get('wgServer');
if (server.startsWith('//')) {
server = location.protocol + server;
}
const bodyClassList = document.body.classList;
// Some users increase the font size (zoom), which leads to some short distances jumping between 3
// and 4 physical pixels and similar. With the help of pixelDeviationRatio, we can make all widths
// look the same by using a variable that stores deviation from standard values. Also unround device
// pixel ratios (like 1.75) may have the same effect (not sure; need to confirm). We can use a
// formula here of course, but that would be less readable :)
const devicePixelRatioToDivisor = [
// Thread lines are 4 or more physical pixels, comment markers are 12 or more physical pixels, 4 or more from each side
[4, 4],
// Thread lines are 3 physical pixels, comment markers are 11 physical pixels, 4 from each side
[3.6666666, 3.6666666],
// Thread lines are 3 physical pixels, comment markers are 9 physical pixels, 3 from each side
[3, 3],
// Thread lines are 2 physical pixels, comment markers are 8 physical pixels, 3 from each side
[2.6666666, 2.6666666],
// Thread lines are 2 physical pixels, comment markers are 6 physical pixels, 2 from each side
[2, 2],
// Thread lines are 1 physical pixel, comment markers are 5 physical pixels, 2 from each side
[1.6666666, 1.6666666],
// Thread lines are 1 physical pixel, comment markers are 3 physical pixels, 1 from each side (default)
[0, 1],
];
const pixelDeviationRatio = devicePixelRatioToDivisor.reduce((value, [dpr, divisor]) => (
value ||
(window.devicePixelRatio >= dpr ? window.devicePixelRatio / divisor : value)
), undefined);
Object.assign(cd, {
/**
* Get a language string.
*
* @param {string} name String name.
* @param {...*} [params] String parameters (substituted strings, also
* {@link module:userRegistry.User User} objects for use in `{{gender:}}`). The last parameter
* can be an object that can have a boolean property `parse` (should the message be returned in
* a parsed form). In the `parse` form, wikilinks are replaced with HTML tags, the code is
* sanitized. Use this for strings that have their raw HTML inserted into the page.
* @returns {?string}
* @memberof convenientDiscussions
*/
s(name, ...params) {
if (!name) {
return null;
}
const fullName = `convenient-discussions-${name}`;
let options = {};
let lastParam = params[params.length - 1];
// lastParam.options is a mw.user-like object to provide to {{gender:}}
if (typeof lastParam === 'object' && !lastParam.options) {
options = lastParam;
params.splice(params.length - 1);
}
isQqxMode ??= /[?&]uselang=qqx(?=&|$)/.test(location.search);
if (!isQqxMode && mw.messages.get(fullName) !== null) {
return mw.message(fullName, ...params)[options.parse ? 'parse' : 'text']();
} else {
const paramsString = params.length ? `: ${params.join(', ')}` : '';
return `(${fullName}${paramsString})`;
}
},
/**
* Get a language string in the "parse" format. Wikilinks are replaced with HTML tags, the code is
* sanitized. Use this for strings that have their raw HTML inserted into the page.
*
* @param {string} name String name.
* @param {...*} [params] String parameters (substituted strings, also
* {@link module:userRegistry.User User} objects for use in `{{gender:}}`).
* @returns {?string}
* @memberof convenientDiscussions
*/
sParse(name, ...params) {
if (params.some((param) => /[<>]/.test(param))) {
// mw.message().parse() escapes tags in parameters - work around that.
mw.messages.set('convenient-discussions-parsehack', cd.s(name, ...params));
return mw.message('convenient-discussions-parsehack').parse();
}
return cd.s(name, ...params, { parse: true });
},
/**
* A foolproof method to access MediaWiki messages intended to be used instead of
* {@link https://doc.wikimedia.org/mediawiki-core/master/js/mw.html#.msg mw.msg()} to eliminate
* any possibility of an XSS injection. By programmer's mistake some `mw.msg()` value could be
* inserted into a page in a raw HTML form. To prevent this, this function should be used, so if
* the message contains an injection (for example, brought from Translatewiki or inserted by a
* user who doesn't have the `editsitejs` right but does have the `editinterface` right), the
* function would sanitize the value.
*
* @param {string} name String name.
* @param {...*} [params] String parameters (substituted strings, also
* {@link module:userRegistry.User User} objects for use in {{gender:}}). The last parameter can
* be an object that can have a string property `language`. If `language` is `'content'`, the
* returned message will be in the content langage (not the interface language).
* @returns {string}
* @memberof convenientDiscussions
*/
mws(name, ...params) {
let options;
let lastParam = params[params.length - 1];
if (typeof lastParam === 'object') {
options = lastParam;
params.splice(params.length - 1);
}
if (options && options.language === 'content') {
name = '(content)' + name;
}
if (!params.length && mwStringsCache[name]) {
return mwStringsCache[name];
}
let message;
if (/^(discussiontools|visualeditor)-/.test(name)) {
message = mw.messages.exists(name) ?
mw.message(name, ...params).parse() :
cd.sParse(name.slice(name.indexOf('-') + 1));
} else {
message = mw.message(name, ...params).parse();
}
if (!params.length && !message.startsWith('⧼')) {
// Use cache since in some places a message could be requested very frequently. Don't cache
// missing messages (they can come from dialogs opened with a network error, like
// UploadDialog; messages can still be accessed for some silly reason).
mwStringsCache[name] = message;
}
return message;
},
getApiConfig() {
return {
parameters: {
formatversion: 2,
uselang: cd.g.userLanguage,
},
ajax: {
headers: {
'Api-User-Agent': 'c:User:Jack who built the house/Convenient Discussions',
},
},
};
},
/**
* Reference to the {@link module:debug debug} module.
*
* @memberof convenientDiscussions
* @see module:debug
*/
debug,
// Kind of a temporary storage of objects of some script's features. Could be removed at any
// moment.
tests: { controller },
/**
* Script's publicly available API. Here there are some utilities that we believe should be
* accessible for external use.
*
* If you need some internal method to be available publicly, contact the script's maintainer (or
* just make a relevant pull request).
*
* @namespace api
* @memberof convenientDiscussions
*/
api: {
/**
* If CD isn't loaded (on pages where CD clearly doesn't need to run), only
* {@link module:pageRegistry.get} and {@link module:pageRegistry.Page#isProbablyTalkPage} are
* guaranteed to run.
*
* @see module:pageRegistry
* @name pageRegistry
* @type {object}
* @memberof convenientDiscussions.api
*/
pageRegistry,
/**
* @see CommentSkeleton.generateId
* @function generateCommentId
* @memberof convenientDiscussions.api
*/
generateCommentId: Comment.generateId.bind(Comment),
/**
* @see module:Comment.parseId
* @function parseCommentId
* @memberof convenientDiscussions.api
*/
parseCommentId: Comment.parseId.bind(Comment),
/**
* @see module:util.buildEditSummary
* @function buildEditSummary
* @memberof convenientDiscussions.api
*/
buildEditSummary,
/**
* @see module:controller.isPageOverlayOn
* @function isPageOverlayOn
* @memberof convenientDiscussions.api
*/
isPageOverlayOn: controller.isPageOverlayOn.bind(controller),
/**
* @see module:util.wrapHtml
* @function wrapHtml
* @memberof convenientDiscussions.api
*/
wrapHtml,
// TODO: Remove after wiki configurations are updated.
wrap: wrapHtml,
/**
* @see module:util.wrapDiffBody
* @function wrapDiffBody
* @memberof convenientDiscussions.api
*/
wrapDiffBody,
},
/**
* Is the code running in the worker context.
*
* @name isWorker
* @type {boolean}
* @memberof convenientDiscussions
*/
isWorker: false,
});
/**
* Collection of properties accessible from anywhere in the script that are not grouped in any other
* way. This is an incomplete list. "g" is for "global".
*
* @namespace g
* @memberof convenientDiscussions
*/
Object.assign(cd.g, {
// These are properties known from the beginning. We assume that there is no point to make them
// subject to change by site administrators although this may be disputable. Some of them are
// extensible in the configuration file (such as noHighlightClasses).
debug: getQueryParamBooleanValue('cddebug') ?? Boolean(IS_DEV),
/**
* A replacement for
* {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Unicode_Property_Escapes unicode property escapes}
* while they are not supported in major browsers. {@link https://github.com/slevithan/xregexp}
* can be used also.
*
* @type {string}
* @memberof convenientDiscussions.g
*/
letterPattern: 'A-Za-z\\u00aa\\u00b5\\u00ba\\u00c0-\\u00d6\\u00d8-\\u00f6\\u00f8-\\u02c1\\u02c6-\\u02d1\\u02e0-\\u02e4\\u02ec\\u02ee\\u0370-\\u0374\\u0376\\u0377\\u037a-\\u037d\\u037f\\u0386\\u0388-\\u038a\\u038c\\u038e-\\u03a1\\u03a3-\\u03f5\\u03f7-\\u0481\\u048a-\\u052f\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05d0-\\u05ea\\u05f0-\\u05f2\\u0620-\\u064a\\u066e\\u066f\\u0671-\\u06d3\\u06d5\\u06e5\\u06e6\\u06ee\\u06ef\\u06fa-\\u06fc\\u06ff\\u0710\\u0712-\\u072f\\u074d-\\u07a5\\u07b1\\u07ca-\\u07ea\\u07f4\\u07f5\\u07fa\\u0800-\\u0815\\u081a\\u0824\\u0828\\u0840-\\u0858\\u08a0-\\u08b4\\u0904-\\u0939\\u093d\\u0950\\u0958-\\u0961\\u0971-\\u0980\\u0985-\\u098c\\u098f\\u0990\\u0993-\\u09a8\\u09aa-\\u09b0\\u09b2\\u09b6-\\u09b9\\u09bd\\u09ce\\u09dc\\u09dd\\u09df-\\u09e1\\u09f0\\u09f1\\u0a05-\\u0a0a\\u0a0f\\u0a10\\u0a13-\\u0a28\\u0a2a-\\u0a30\\u0a32\\u0a33\\u0a35\\u0a36\\u0a38\\u0a39\\u0a59-\\u0a5c\\u0a5e\\u0a72-\\u0a74\\u0a85-\\u0a8d\\u0a8f-\\u0a91\\u0a93-\\u0aa8\\u0aaa-\\u0ab0\\u0ab2\\u0ab3\\u0ab5-\\u0ab9\\u0abd\\u0ad0\\u0ae0\\u0ae1\\u0af9\\u0b05-\\u0b0c\\u0b0f\\u0b10\\u0b13-\\u0b28\\u0b2a-\\u0b30\\u0b32\\u0b33\\u0b35-\\u0b39\\u0b3d\\u0b5c\\u0b5d\\u0b5f-\\u0b61\\u0b71\\u0b83\\u0b85-\\u0b8a\\u0b8e-\\u0b90\\u0b92-\\u0b95\\u0b99\\u0b9a\\u0b9c\\u0b9e\\u0b9f\\u0ba3\\u0ba4\\u0ba8-\\u0baa\\u0bae-\\u0bb9\\u0bd0\\u0c05-\\u0c0c\\u0c0e-\\u0c10\\u0c12-\\u0c28\\u0c2a-\\u0c39\\u0c3d\\u0c58-\\u0c5a\\u0c60\\u0c61\\u0c85-\\u0c8c\\u0c8e-\\u0c90\\u0c92-\\u0ca8\\u0caa-\\u0cb3\\u0cb5-\\u0cb9\\u0cbd\\u0cde\\u0ce0\\u0ce1\\u0cf1\\u0cf2\\u0d05-\\u0d0c\\u0d0e-\\u0d10\\u0d12-\\u0d3a\\u0d3d\\u0d4e\\u0d5f-\\u0d61\\u0d7a-\\u0d7f\\u0d85-\\u0d96\\u0d9a-\\u0db1\\u0db3-\\u0dbb\\u0dbd\\u0dc0-\\u0dc6\\u0e01-\\u0e30\\u0e32\\u0e33\\u0e40-\\u0e46\\u0e81\\u0e82\\u0e84\\u0e87\\u0e88\\u0e8a\\u0e8d\\u0e94-\\u0e97\\u0e99-\\u0e9f\\u0ea1-\\u0ea3\\u0ea5\\u0ea7\\u0eaa\\u0eab\\u0ead-\\u0eb0\\u0eb2\\u0eb3\\u0ebd\\u0ec0-\\u0ec4\\u0ec6\\u0edc-\\u0edf\\u0f00\\u0f40-\\u0f47\\u0f49-\\u0f6c\\u0f88-\\u0f8c\\u1000-\\u102a\\u103f\\u1050-\\u1055\\u105a-\\u105d\\u1061\\u1065\\u1066\\u106e-\\u1070\\u1075-\\u1081\\u108e\\u10a0-\\u10c5\\u10c7\\u10cd\\u10d0-\\u10fa\\u10fc-\\u1248\\u124a-\\u124d\\u1250-\\u1256\\u1258\\u125a-\\u125d\\u1260-\\u1288\\u128a-\\u128d\\u1290-\\u12b0\\u12b2-\\u12b5\\u12b8-\\u12be\\u12c0\\u12c2-\\u12c5\\u12c8-\\u12d6\\u12d8-\\u1310\\u1312-\\u1315\\u1318-\\u135a\\u1380-\\u138f\\u13a0-\\u13f5\\u13f8-\\u13fd\\u1401-\\u166c\\u166f-\\u167f\\u1681-\\u169a\\u16a0-\\u16ea\\u16f1-\\u16f8\\u1700-\\u170c\\u170e-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176c\\u176e-\\u1770\\u1780-\\u17b3\\u17d7\\u17dc\\u1820-\\u1877\\u1880-\\u18a8\\u18aa\\u18b0-\\u18f5\\u1900-\\u191e\\u1950-\\u196d\\u1970-\\u1974\\u1980-\\u19ab\\u19b0-\\u19c9\\u1a00-\\u1a16\\u1a20-\\u1a54\\u1aa7\\u1b05-\\u1b33\\u1b45-\\u1b4b\\u1b83-\\u1ba0\\u1bae\\u1baf\\u1bba-\\u1be5\\u1c00-\\u1c23\\u1c4d-\\u1c4f\\u1c5a-\\u1c7d\\u1ce9-\\u1cec\\u1cee-\\u1cf1\\u1cf5\\u1cf6\\u1d00-\\u1dbf\\u1e00-\\u1f15\\u1f18-\\u1f1d\\u1f20-\\u1f45\\u1f48-\\u1f4d\\u1f50-\\u1f57\\u1f59\\u1f5b\\u1f5d\\u1f5f-\\u1f7d\\u1f80-\\u1fb4\\u1fb6-\\u1fbc\\u1fbe\\u1fc2-\\u1fc4\\u1fc6-\\u1fcc\\u1fd0-\\u1fd3\\u1fd6-\\u1fdb\\u1fe0-\\u1fec\\u1ff2-\\u1ff4\\u1ff6-\\u1ffc\\u2071\\u207f\\u2090-\\u209c\\u2102\\u2107\\u210a-\\u2113\\u2115\\u2119-\\u211d\\u2124\\u2126\\u2128\\u212a-\\u212d\\u212f-\\u2139\\u213c-\\u213f\\u2145-\\u2149\\u214e\\u2183\\u2184\\u2c00-\\u2c2e\\u2c30-\\u2c5e\\u2c60-\\u2ce4\\u2ceb-\\u2cee\\u2cf2\\u2cf3\\u2d00-\\u2d25\\u2d27\\u2d2d\\u2d30-\\u2d67\\u2d6f\\u2d80-\\u2d96\\u2da0-\\u2da6\\u2da8-\\u2dae\\u2db0-\\u2db6\\u2db8-\\u2dbe\\u2dc0-\\u2dc6\\u2dc8-\\u2dce\\u2dd0-\\u2dd6\\u2dd8-\\u2dde\\u2e2f\\u3005\\u3006\\u3031-\\u3035\\u303b\\u303c\\u3041-\\u3096\\u309d-\\u309f\\u30a1-\\u30fa\\u30fc-\\u30ff\\u3105-\\u312d\\u3131-\\u318e\\u31a0-\\u31ba\\u31f0-\\u31ff\\u3400-\\u4db5\\u4e00-\\u9fd5\\ua000-\\ua48c\\ua4d0-\\ua4fd\\ua500-\\ua60c\\ua610-\\ua61f\\ua62a\\ua62b\\ua640-\\ua66e\\ua67f-\\ua69d\\ua6a0-\\ua6e5\\ua717-\\ua71f\\ua722-\\ua788\\ua78b-\\ua7ad\\ua7b0-\\ua7b7\\ua7f7-\\ua801\\ua803-\\ua805\\ua807-\\ua80a\\ua80c-\\ua822\\ua840-\\ua873\\ua882-\\ua8b3\\ua8f2-\\ua8f7\\ua8fb\\ua8fd\\ua90a-\\ua925\\ua930-\\ua946\\ua960-\\ua97c\\ua984-\\ua9b2\\ua9cf\\ua9e0-\\ua9e4\\ua9e6-\\ua9ef\\ua9fa-\\ua9fe\\uaa00-\\uaa28\\uaa40-\\uaa42\\uaa44-\\uaa4b\\uaa60-\\uaa76\\uaa7a\\uaa7e-\\uaaaf\\uaab1\\uaab5\\uaab6\\uaab9-\\uaabd\\uaac0\\uaac2\\uaadb-\\uaadd\\uaae0-\\uaaea\\uaaf2-\\uaaf4\\uab01-\\uab06\\uab09-\\uab0e\\uab11-\\uab16\\uab20-\\uab26\\uab28-\\uab2e\\uab30-\\uab5a\\uab5c-\\uab65\\uab70-\\uabe2\\uac00-\\ud7a3\\ud7b0-\\ud7c6\\ud7cb-\\ud7fb\\uf900-\\ufa6d\\ufa70-\\ufad9\\ufb00-\\ufb06\\ufb13-\\ufb17\\ufb1d\\ufb1f-\\ufb28\\ufb2a-\\ufb36\\ufb38-\\ufb3c\\ufb3e\\ufb40\\ufb41\\ufb43\\ufb44\\ufb46-\\ufbb1\\ufbd3-\\ufd3d\\ufd50-\\ufd8f\\ufd92-\\ufdc7\\ufdf0-\\ufdfb\\ufe70-\\ufe74\\ufe76-\\ufefc\\uff21-\\uff3a\\uff41-\\uff5a\\uff66-\\uffbe\\uffc2-\\uffc7\\uffca-\\uffcf\\uffd2-\\uffd7\\uffda-\\uffdc',
/**
* Left and right margins of comment layers used as a fallback (when a comment is placed
* somewhere where we don't know how to position the layers).
*
* @type {number}
* @memberof convenientDiscussions.g
*/
commentFallbackSideMargin: 10,
/**
* Left and right padding of thread lines.
*
* @type {number}
* @memberof convenientDiscussions.g
*/
threadLineSidePadding: 3,
/**
* Width (thickness) of comment markers and thread lines when you hover over them. Should be an
* odd number - otherwise the browser will render 1 pixel more on one side, and for each comment
* differently. See also pixelDeviationRatio that helps achieve this evenness.
*
* @type {number}
* @memberof convenientDiscussions.g
*/
commentMarkerWidth: 3,
pixelDeviationRatio,
pixelDeviationRatioFor1px: (
window.devicePixelRatio / Math.max(Math.floor(window.devicePixelRatio), 1)
),
/**
* Number of seconds between checks for new comments when the tab is not hidden.
*
* @type {number}
* @memberof convenientDiscussions.g
*/
updateCheckInterval: 15,
/**
* Number of seconds between new comments checks when the tab is hidden.
*
* @type {number}
* @memberof convenientDiscussions.g
*/
backgroundUpdateCheckInterval: 60,
/**
* Number of milliseconds in a minute.
*
* @type {number}
* @memberof convenientDiscussions.g
*/
msInMin: 1000 * 60,
/**
* Number of milliseconds in a day.
*
* @type {number}
* @memberof convenientDiscussions.g
*/
msInDay: 1000 * 60 * 60 * 24,
/**
* Popular elements that can be met in page content and don't have the `display: inline` property
* in the default browser styles.
*
* @type {string[]}
* @memberof convenientDiscussions.g
*/
popularNotInlineElements: ['BLOCKQUOTE', 'CAPTION', 'CENTER', 'DD', 'DIV', 'DL', 'DT', 'FIGURE', 'FIGCAPTION', 'FORM', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'HR', 'INPUT', 'LI', 'LINK', 'OL', 'P', 'PRE', 'SECTION', 'STYLE', 'TABLE', 'TBODY', 'TD', 'TFOOT', 'TH', 'THEAD', 'TR', 'UL'],
/**
* Popular elements that can be met in page content and do have the `display: inline` property in
* the default browser styles.
*
* @type {string[]}
* @memberof convenientDiscussions.g
*/
popularInlineElements: ['A', 'ABBR', 'B', 'BDI', 'BIG', 'BR', 'BUTTON', 'CITE', 'CODE', 'DEL', 'EM', 'FONT', 'I', 'IMG', 'INS', 'KBD', 'META', 'Q', 'S', 'SAMP', 'SMALL', 'SPAN', 'STRIKE', 'STRONG', 'SUB', 'SUP', 'TIME', 'TT', 'U', 'VAR'],
/**
* Elements with classes listed here won't be considered legit comment timestamp containers. They
* can still be parts of comments; for the way to prevent certain elements from becoming comment
* parts, see {@link module:defaultConfig.rejectNode}. This value can have a wikitext counterpart
* (though not necessarily), {@link module:defaultConfig.noSignatureTemplates}, for classes that
* are specified inside templates.
*
* When it comes to the wikitext, all lines containing these classes are ignored.
*
* @type {string[]}
* @default []
* @memberof convenientDiscussions.g
*/
noSignatureClasses: ['mw-notalk', 'cd-moveMark'],
/**
* Names of elements that shouldn't be the first or last highlightable element. CD wraps these
* elements into `<div>` containers. It allows for comment headers to be displayed correctly when
* the `reformatComments` setting is enabled.
*
* @type {string[]}
* @memberof convenientDiscussions.g
*/
badHighlightableElements: ['BLOCKQUOTE', 'DL', 'FORM', 'HR', 'OL', 'PRE', 'TABLE', 'UL'],
/**
* Classes of elements that shouldn't be highlighted. Only MediaWiki-assigned and native CD
* classes go here. Wiki-specific classes go in the configuration.
*
* @type {string[]}
* @memberof convenientDiscussions.g
*/
noHighlightClasses: [
'mw-empty-elt',
'tleft',
'tright',
'floatleft',
'floatright',
'cd-moveMark',
'cd-noHighlight',
],
/**
* Regexps for strings that should be cut out of comment beginnings (not considered parts of
* comments) when editing comments.
*
* @type {RegExp[]}
* @memberof convenientDiscussions.g
*/
badCommentBeginnings: [
/^<!--[^]*?--> *\n+/,
/^(?:----+|<hr>) *\n+/i,
/^\{\|.*?\|\} *\n+(?=[*:#])/,
],
/**
* Auxiliary property to keep the sign code: `'\~\~\~\~'`. If written as plain text, it gets
* transformed into the revision author's signature when saved. Note that the minifier
* translates `'~\~\\~'` and `'\~\~' + '\~'` into `'\~\~\~'`.
*
* @type {string}
* @memberof convenientDiscussions.g
*/
signCode: '~~'.concat('~~'),
/**
* List of tags allowed in user input. See
* {@link https://www.mediawiki.org/wiki/Help:HTML_in_wikitext#Allowed_HTML_tags},
* {@link https://en.wikipedia.org/wiki/Help:HTML_in_wikitext#Parser_and_extension_tags}.
* Deprecated tags are not included.
*
* @type {string[]}
* @memberof convenientDiscussions.g
*/
allowedTags: [
'abbr',
'b',
'bdi',
'bdo',
'blockquote',
'br',
'caption',
'cite',
'code',
'data',
'dd',
'del',
'dfn',
'div',
'dl',
'dt',
'em',
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'hr',
'i',
'ins',
'kbd',
'li',
'link',
'mark',
'meta',
'ol',
'p',
'pre',
'q',
'rp',
'rt',
'rtc',
'ruby',
's',
'samp',
'small',
'span',
'strong',
'sub',
'sup',
'table',
'td',
'th',
'time',
'tr',
'translate',
'tvar',
'u',
'ul',
'var',
'wbr',
'gallery',
'includeonly',
'noinclude',
'nowiki',
'onlyinclude',
'categorytree',
'charinsert',
'chem',
'ce',
'graph',
'hiero',
'imagemap',
'indicator',
'inputbox',
'mapframe',
'maplink',
'math',
'poem',
'ref',
'references',
'score',
'section',
'syntaxhighlight',
'templatedata',
'templatestyles',
'timeline',
],
inputPropsAffectingCoords: [
'borderBottomStyle',
'borderBottomWidth',
'borderLeftStyle',
'borderLeftWidth',
'borderRightStyle',
'borderRightWidth',
'borderTopStyle',
'borderTopWidth',
'boxSizing',
'direction',
'fontFamily',
'fontSize',
'fontSizeAdjust',
'fontStretch',
'fontStyle',
'fontVariant',
'fontWeight',
'height',
'letterSpacing',
'lineHeight',
'overflowX',
'overflowY',
'paddingBottom',
'paddingLeft',
'paddingRight',
'paddingTop',
'tabSize',
'textAlign',
'textDecoration',
'textIndent',
'textTransform',
'width',
'wordSpacing',
],
settingsOptionName: 'userjs-convenientDiscussions-settings',
localSettingsOptionName: `userjs-${localOptionsPrefix}-localSettings`,
visitsOptionName: `userjs-${localOptionsPrefix}-visits`,
subscriptionsOptionName: `userjs-${localOptionsPrefix}-${subscriptionsOptionNameEnding}`,
server,
serverName,
pageName: underlinesToSpaces(mw.config.get('wgPageName')),
pageTitle: mw.config.get('wgTitle'),
namespaceNumber: mw.config.get('wgNamespaceNumber'),
pageAction: mw.config.get('wgAction'),
// Check for mw.user.isNamed() to treat temporary accounts as unregistered (we can't save
// options for them anyway). `<unregistered>` is a workaround for anonymous users (there are
// such!).
userName: (
((!mw.user.isNamed || mw.user.isNamed()) && mw.config.get('wgUserName')) ||
'<unregistered>'
),
contentDirection: bodyClassList.contains('sitedir-rtl') ? 'rtl' : 'ltr',
userDirection: bodyClassList.contains('rtl') ? 'rtl' : 'ltr',
skin: mw.config.get('skin'),
// Quite a rough check for mobile browsers, a mix of what is advised at
// https://stackoverflow.com/a/24600597 (sends to
// https://developer.mozilla.org/en-US/docs/Browser_detection_using_the_user_agent) and
// https://stackoverflow.com/a/14301832.
isMobile: (
/Mobi|Android/i.test(navigator.userAgent) ||
typeof window.orientation !== 'undefined'
),
isDtReplyToolEnabled: bodyClassList.contains('ext-discussiontools-replytool-enabled'),
isDtNewTopicToolEnabled: bodyClassList.contains('ext-discussiontools-newtopictool-enabled'),
isDtTopicSubscriptionEnabled: bodyClassList
.contains('ext-discussiontools-topicsubscription-enabled'),
isDtVisualEnhancementsEnabled: bodyClassList
.contains('ext-discussiontools-visualenhancements-enabled'),
isParsoidUsed: $('#mw-content-text > .mw-parser-output')
.contents()
.get()
.filter((node) => node.nodeType === Node.COMMENT_NODE)
.some((c) => c.textContent.startsWith('Parsoid')),
});
if (cd.g.debug) {
window.cd = cd;
}