2019-11-05 14:13:18 +00:00
|
|
|
/* global $:off */
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adapted from MDN polyfill (CC0)
|
|
|
|
* https://developer.mozilla.org/en-US/docs/Web/API/Element/closest
|
|
|
|
*
|
|
|
|
* @param {HTMLElement} el
|
|
|
|
* @param {string} selector
|
|
|
|
* @return {HTMLElement|null}
|
|
|
|
*/
|
|
|
|
function closest( el, selector ) {
|
|
|
|
var matches;
|
|
|
|
|
|
|
|
el = el.nodeType === Node.ELEMENT_NODE ? el : el.parentElement;
|
|
|
|
|
|
|
|
if ( Element.prototype.closest ) {
|
|
|
|
return el.closest( selector );
|
|
|
|
}
|
|
|
|
|
|
|
|
matches = Element.prototype.matches ||
|
|
|
|
Element.prototype.msMatchesSelector ||
|
|
|
|
Element.prototype.webkitMatchesSelector;
|
|
|
|
do {
|
|
|
|
if ( matches.call( el, selector ) ) {
|
|
|
|
return el;
|
|
|
|
}
|
|
|
|
el = el.parentElement || el.parentNode;
|
|
|
|
} while ( el !== null && el.nodeType === 1 );
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
Pick reply insertion point based on parser tree, not DOM tree
I don't like that I had to special-case `<p>` tags (top-level
comments) in this code. I feel like it should be possible to handle
top-level comments and replies in a generic way, but I couldn't find
a way to do it that actually worked.
Notes about changes to the behavior, based on the test cases:
* Given a top-level comment A, if there was a "list gap" in the
replies to it: previously new replies would be incorrectly added at
the location of the gap; now they are added after the last reply.
(T242822)
Example: "pl", comment at "08:23, 29 wrz 2018 (CEST)"
* Given a top-level comment A and a reply to it B that skips an
indentation level: previously new replies to A would be added with
the same indentation level as B; now they are added with the
indentation level of A plus one. (The old behavior wasn't a bug, and
this is an accidental effect of other changes, but it seems okay.)
Example: "pl", comment at "03:22, 30 wrz 2018 (CEST)"
and reply at "09:43, 30 wrz 2018 (CEST)"
* Given a top-level comment A, a reply to it B, and a following
top-level comment C that starts at the same indentation level as B:
previously new replies to A would be incorrectly added in the middle
of the comment C, due to the DOM list structure; now they are added
before C. (T241391)
(It seems that comment C was supposed to be a multi-line reply that
was wrongly indented. Unfortunately we have no way to distinguish
this case from a top-level multi-line comment that just happens to
start with a bullet list.)
Example: "pl", comments at "03:36, 24 paź 2018 (CEST)",
"08:35, 24 paź 2018 (CEST)", "17:14, 24 paź 2018 (CEST)"
* In the "en" example, there are some other changes where funnily
nested tags result in slightly different results with the new code.
They don't look important.
* In rare cases, we must split an existing list to add a reply in the
right place. (Basically add `</ul>` before the reply and `<ul>`
after, but it's a bit awkward in DOM terms.)
Example: split-list.html, comment "aaa"; also split-list2.html
(which is the result of saving the previous reply), comment "aaa"
* The modifier can no longer generate DOM that is invalid HTML, fixing
a FIXME in modifier.test.js (or at least, it doesn't happen in these
test cases any more).
Bug: T241391
Bug: T242822
Change-Id: I2a70db01e9a8916c5636bc59ea8490166966d5ec
2020-01-15 06:09:13 +00:00
|
|
|
function whitespaceParsoidHack( listItem ) {
|
2019-11-13 14:36:36 +00:00
|
|
|
// HACK: Setting data-parsoid removes the whitespace after the list item,
|
|
|
|
// which makes nested lists work.
|
|
|
|
// This is undocumented behaviour and probably very fragile.
|
|
|
|
listItem.setAttribute( 'data-parsoid', '{}' );
|
Pick reply insertion point based on parser tree, not DOM tree
I don't like that I had to special-case `<p>` tags (top-level
comments) in this code. I feel like it should be possible to handle
top-level comments and replies in a generic way, but I couldn't find
a way to do it that actually worked.
Notes about changes to the behavior, based on the test cases:
* Given a top-level comment A, if there was a "list gap" in the
replies to it: previously new replies would be incorrectly added at
the location of the gap; now they are added after the last reply.
(T242822)
Example: "pl", comment at "08:23, 29 wrz 2018 (CEST)"
* Given a top-level comment A and a reply to it B that skips an
indentation level: previously new replies to A would be added with
the same indentation level as B; now they are added with the
indentation level of A plus one. (The old behavior wasn't a bug, and
this is an accidental effect of other changes, but it seems okay.)
Example: "pl", comment at "03:22, 30 wrz 2018 (CEST)"
and reply at "09:43, 30 wrz 2018 (CEST)"
* Given a top-level comment A, a reply to it B, and a following
top-level comment C that starts at the same indentation level as B:
previously new replies to A would be incorrectly added in the middle
of the comment C, due to the DOM list structure; now they are added
before C. (T241391)
(It seems that comment C was supposed to be a multi-line reply that
was wrongly indented. Unfortunately we have no way to distinguish
this case from a top-level multi-line comment that just happens to
start with a bullet list.)
Example: "pl", comments at "03:36, 24 paź 2018 (CEST)",
"08:35, 24 paź 2018 (CEST)", "17:14, 24 paź 2018 (CEST)"
* In the "en" example, there are some other changes where funnily
nested tags result in slightly different results with the new code.
They don't look important.
* In rare cases, we must split an existing list to add a reply in the
right place. (Basically add `</ul>` before the reply and `<ul>`
after, but it's a bit awkward in DOM terms.)
Example: split-list.html, comment "aaa"; also split-list2.html
(which is the result of saving the previous reply), comment "aaa"
* The modifier can no longer generate DOM that is invalid HTML, fixing
a FIXME in modifier.test.js (or at least, it doesn't happen in these
test cases any more).
Bug: T241391
Bug: T242822
Change-Id: I2a70db01e9a8916c5636bc59ea8490166966d5ec
2020-01-15 06:09:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Given a comment, add a list item to its document's DOM tree, inside of which a reply to said
|
|
|
|
* comment can be added.
|
|
|
|
*
|
|
|
|
* The DOM tree is suitably rearranged to ensure correct indentation level of the reply (wrapper
|
|
|
|
* nodes are added, and other nodes may be moved around).
|
|
|
|
*
|
|
|
|
* @param {Object} comment Comment data returned by parser#groupThreads
|
|
|
|
* @return {HTMLElement}
|
|
|
|
*/
|
|
|
|
function addListItem( comment ) {
|
|
|
|
var
|
|
|
|
currComment, currLevel, desiredLevel,
|
|
|
|
target, parent, listType, itemType, list, item, newNode,
|
|
|
|
listTypeMap = {
|
|
|
|
li: 'ul',
|
|
|
|
dd: 'dl'
|
|
|
|
};
|
|
|
|
|
|
|
|
// 1. Start at given comment
|
|
|
|
// 2. Skip past all comments with level greater than the given
|
|
|
|
// (or in other words, all replies, and replies to replies, and so on)
|
|
|
|
// 3. Add comment with level of the given comment plus 1
|
|
|
|
|
|
|
|
currComment = comment;
|
|
|
|
while ( currComment.replies.length ) {
|
|
|
|
currComment = currComment.replies[ currComment.replies.length - 1 ];
|
|
|
|
}
|
|
|
|
|
|
|
|
desiredLevel = comment.level + 1;
|
|
|
|
currLevel = currComment.level;
|
|
|
|
target = currComment.range.endContainer;
|
|
|
|
// HACK
|
|
|
|
if ( target.nextSibling && target.nextSibling.classList.contains( 'dt-init-replylink' ) ) {
|
|
|
|
target = target.nextSibling;
|
|
|
|
}
|
|
|
|
|
|
|
|
// endContainer is probably a text node, and it may also be wrapped in some formatting.
|
|
|
|
// First, we need to find a block-level parent that we can mess with.
|
|
|
|
// If we can't find a surrounding list item or paragraph (e.g. maybe we're inside a table cell
|
|
|
|
// or something), take the parent node and hope for the best.
|
|
|
|
parent = closest( target, 'li, dd, p' ) || target.parentNode;
|
|
|
|
while ( target.parentNode !== parent ) {
|
|
|
|
target = target.parentNode;
|
|
|
|
}
|
|
|
|
// parent is a list item or paragraph (hopefully)
|
|
|
|
// target is an inline node within it
|
|
|
|
|
|
|
|
if ( currLevel < desiredLevel ) {
|
|
|
|
// Insert more lists after the target to increase nesting.
|
2019-11-13 14:36:36 +00:00
|
|
|
|
Pick reply insertion point based on parser tree, not DOM tree
I don't like that I had to special-case `<p>` tags (top-level
comments) in this code. I feel like it should be possible to handle
top-level comments and replies in a generic way, but I couldn't find
a way to do it that actually worked.
Notes about changes to the behavior, based on the test cases:
* Given a top-level comment A, if there was a "list gap" in the
replies to it: previously new replies would be incorrectly added at
the location of the gap; now they are added after the last reply.
(T242822)
Example: "pl", comment at "08:23, 29 wrz 2018 (CEST)"
* Given a top-level comment A and a reply to it B that skips an
indentation level: previously new replies to A would be added with
the same indentation level as B; now they are added with the
indentation level of A plus one. (The old behavior wasn't a bug, and
this is an accidental effect of other changes, but it seems okay.)
Example: "pl", comment at "03:22, 30 wrz 2018 (CEST)"
and reply at "09:43, 30 wrz 2018 (CEST)"
* Given a top-level comment A, a reply to it B, and a following
top-level comment C that starts at the same indentation level as B:
previously new replies to A would be incorrectly added in the middle
of the comment C, due to the DOM list structure; now they are added
before C. (T241391)
(It seems that comment C was supposed to be a multi-line reply that
was wrongly indented. Unfortunately we have no way to distinguish
this case from a top-level multi-line comment that just happens to
start with a bullet list.)
Example: "pl", comments at "03:36, 24 paź 2018 (CEST)",
"08:35, 24 paź 2018 (CEST)", "17:14, 24 paź 2018 (CEST)"
* In the "en" example, there are some other changes where funnily
nested tags result in slightly different results with the new code.
They don't look important.
* In rare cases, we must split an existing list to add a reply in the
right place. (Basically add `</ul>` before the reply and `<ul>`
after, but it's a bit awkward in DOM terms.)
Example: split-list.html, comment "aaa"; also split-list2.html
(which is the result of saving the previous reply), comment "aaa"
* The modifier can no longer generate DOM that is invalid HTML, fixing
a FIXME in modifier.test.js (or at least, it doesn't happen in these
test cases any more).
Bug: T241391
Bug: T242822
Change-Id: I2a70db01e9a8916c5636bc59ea8490166966d5ec
2020-01-15 06:09:13 +00:00
|
|
|
// If we can't insert a list directly inside this element, insert after it.
|
|
|
|
// TODO Improve this check
|
|
|
|
if ( parent.tagName.toLowerCase() === 'p' ) {
|
|
|
|
parent = parent.parentNode;
|
|
|
|
target = target.parentNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Decide on tag names for lists and items
|
|
|
|
itemType = parent.tagName.toLowerCase();
|
|
|
|
itemType = listTypeMap[ itemType ] ? itemType : 'dd';
|
|
|
|
listType = listTypeMap[ itemType ];
|
|
|
|
|
|
|
|
// Insert required number of wrappers
|
|
|
|
while ( currLevel < desiredLevel ) {
|
|
|
|
list = target.ownerDocument.createElement( listType );
|
|
|
|
item = target.ownerDocument.createElement( itemType );
|
|
|
|
whitespaceParsoidHack( item );
|
|
|
|
|
|
|
|
parent.insertBefore( list, target.nextSibling );
|
|
|
|
list.appendChild( item );
|
|
|
|
|
|
|
|
target = item;
|
|
|
|
parent = list;
|
|
|
|
currLevel++;
|
|
|
|
}
|
|
|
|
} else if ( currLevel >= desiredLevel ) {
|
|
|
|
// Split the ancestor nodes after the target to decrease nesting.
|
|
|
|
|
|
|
|
do {
|
|
|
|
// If target is the last child of its parent, no need to split it
|
|
|
|
if ( target.nextSibling ) {
|
|
|
|
// Create new identical node after the parent
|
|
|
|
newNode = parent.cloneNode( false );
|
|
|
|
parent.parentNode.insertBefore( newNode, parent.nextSibling );
|
|
|
|
|
|
|
|
// Move nodes following target to the new node
|
|
|
|
while ( target.nextSibling ) {
|
|
|
|
newNode.appendChild( target.nextSibling );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
target = parent;
|
|
|
|
parent = parent.parentNode;
|
|
|
|
|
|
|
|
// Decrease nesting level if we escaped outside of a list
|
|
|
|
if ( listTypeMap[ target.tagName.toLowerCase() ] ) {
|
|
|
|
currLevel--;
|
|
|
|
}
|
|
|
|
} while ( currLevel >= desiredLevel );
|
|
|
|
|
|
|
|
// parent is now a list, target is a list item
|
|
|
|
item = target.ownerDocument.createElement( target.tagName );
|
|
|
|
whitespaceParsoidHack( item );
|
|
|
|
parent.insertBefore( item, target.nextSibling );
|
|
|
|
}
|
|
|
|
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add another list item after the given one.
|
|
|
|
*
|
|
|
|
* @param {HTMLElement} previousItem
|
|
|
|
* @return {HTMLElement}
|
|
|
|
*/
|
|
|
|
function addSiblingListItem( previousItem ) {
|
|
|
|
var listItem = previousItem.ownerDocument.createElement( previousItem.nodeName.toLowerCase() );
|
|
|
|
whitespaceParsoidHack( listItem );
|
|
|
|
previousItem.parentNode.insertBefore( listItem, previousItem.nextSibling );
|
2019-11-05 14:13:18 +00:00
|
|
|
return listItem;
|
|
|
|
}
|
|
|
|
|
|
|
|
function createWikitextNode( wt ) {
|
|
|
|
var span = document.createElement( 'span' );
|
|
|
|
|
|
|
|
span.setAttribute( 'typeof', 'mw:Transclusion' );
|
|
|
|
span.setAttribute( 'data-mw', JSON.stringify( { parts: [ wt ] } ) );
|
|
|
|
|
|
|
|
return span;
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = {
|
2019-11-20 20:27:08 +00:00
|
|
|
closest: closest,
|
2019-11-05 14:13:18 +00:00
|
|
|
addListItem: addListItem,
|
Pick reply insertion point based on parser tree, not DOM tree
I don't like that I had to special-case `<p>` tags (top-level
comments) in this code. I feel like it should be possible to handle
top-level comments and replies in a generic way, but I couldn't find
a way to do it that actually worked.
Notes about changes to the behavior, based on the test cases:
* Given a top-level comment A, if there was a "list gap" in the
replies to it: previously new replies would be incorrectly added at
the location of the gap; now they are added after the last reply.
(T242822)
Example: "pl", comment at "08:23, 29 wrz 2018 (CEST)"
* Given a top-level comment A and a reply to it B that skips an
indentation level: previously new replies to A would be added with
the same indentation level as B; now they are added with the
indentation level of A plus one. (The old behavior wasn't a bug, and
this is an accidental effect of other changes, but it seems okay.)
Example: "pl", comment at "03:22, 30 wrz 2018 (CEST)"
and reply at "09:43, 30 wrz 2018 (CEST)"
* Given a top-level comment A, a reply to it B, and a following
top-level comment C that starts at the same indentation level as B:
previously new replies to A would be incorrectly added in the middle
of the comment C, due to the DOM list structure; now they are added
before C. (T241391)
(It seems that comment C was supposed to be a multi-line reply that
was wrongly indented. Unfortunately we have no way to distinguish
this case from a top-level multi-line comment that just happens to
start with a bullet list.)
Example: "pl", comments at "03:36, 24 paź 2018 (CEST)",
"08:35, 24 paź 2018 (CEST)", "17:14, 24 paź 2018 (CEST)"
* In the "en" example, there are some other changes where funnily
nested tags result in slightly different results with the new code.
They don't look important.
* In rare cases, we must split an existing list to add a reply in the
right place. (Basically add `</ul>` before the reply and `<ul>`
after, but it's a bit awkward in DOM terms.)
Example: split-list.html, comment "aaa"; also split-list2.html
(which is the result of saving the previous reply), comment "aaa"
* The modifier can no longer generate DOM that is invalid HTML, fixing
a FIXME in modifier.test.js (or at least, it doesn't happen in these
test cases any more).
Bug: T241391
Bug: T242822
Change-Id: I2a70db01e9a8916c5636bc59ea8490166966d5ec
2020-01-15 06:09:13 +00:00
|
|
|
addSiblingListItem: addSiblingListItem,
|
2019-11-05 14:13:18 +00:00
|
|
|
createWikitextNode: createWikitextNode
|
|
|
|
};
|