Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
/* <nowiki> */
$(() => {
  const config = {
    name: '[[User:DevSpenpai/format|format.js]]',
    version: 0.0,
    debug: false
  };

  const summary = `Formatted article with ${config.name} [BETA]`;
  const scriptUrl = mw.config.get('wgScriptPath') + '/api.php';

  mw.loader.using('mediawiki.util', () => {
    $(document).ready(() => {
      mw.util.addPortletLink('p-cactions', 'javascript:void(0)', 'format', 'ca-de-format', 'Format articles.');
      $('#ca-de-format').on('click', () => {
        format();
      });
    });
  });


  const punc = (match) => {
    match = match.trim();
    if (match.startsWith("?")) return "?";
    if (match.startsWith("!")) return "!";
    return ".";
  };

  function format() {
    const name = mw.config.get('wgPageName');
    const page = fetchPage(name);
    const thingsChanged = [];
    /'"/gi.test(page) ? thingsChanged.push("quote within quote formatting") : undefined;
    // !/{{reflist}}|==.*(references|citations|footnotes|sources).*==/gi.test(page) ? thingsChanged.push("add reference section") : undefined;
    !/<ref>|{{.*cite.*}}/gi.test(page) && !/{{.*(unreferenced|more citations needed|p2|primary sources).*}}/gi.test(page) && !/{{reflist}}|==.*(references|citations|footnotes|sources).*==/gi.test(page) ? thingsChanged.push("add {{unreferenced}} tag") : (page.split(/{{.*cite.*}}|<ref>/gi).length === 2 && !/{{.*(unreferenced|more citations needed|p2|primary sources).*}}/gi.test(page)) && page.length >= 1200 ? thingsChanged.push("added more ref needed tag") : undefined;
    /[\t\f ]+\.(?=(?:[^"]*"[^"]*")*[^"]*$)/gim.test(page) ? thingsChanged.push("fix spacing before period") : undefined;
    /((\.|\?)[\t\f ]*)((\.|\?)[\t\f ]*)+(?=(?:[^"]*"[^"]*")*[^"]*$)/gi.test(page) ? thingsChanged.push("remove duplicate punctuation") : undefined;
    /(\.|\?)[\t\f ]+(<ref>|{{.*cite.*}})(?=(?:[^"]*"[^"]*")*[^"]*$)/gi.test(page) ? thingsChanged.push("remove excess spacing from inline references") : undefined;
	/(#|№|n\.?º)[\t\f ]*[0-9]/gi.test(page) ? thingsChanged.push("number notation formatting") : undefined;
	/U\.S\.?(?=(?:[^"]*"[^"]*")*[^"]*$)/g.test(page) ? thingsChanged.push("properify United States abbreviation") : undefined;
	/(in )?the year( of)? [0-9]{4,}(?=(?:[^"]*"[^"]*")*[^"]*$)/gi.test(page) ? thingsChanged.push("fix redundant date text") : undefined;
	/\(\s+.+\)|\(.+\s+\)/gim.test(page) ? thingsChanged.push("Remove excess spacing between parentheses") : undefined;

	// const dotMatches = page.match(/[^\s\.]{1,250}\.[^\s\.]{1,250}(?=(?:[^"]*"[^"]*")*[^"]*$)/gim);
	// if (dotMatches.length > 0) {
	// 	(dotMatches.filter((m) => !/https?:\/\/|\.com|\.org|\.gov/gi.test(m))).length > 0 ? thingsChanged.push("add space between period and start of new sentance") : undefined;
	// }
	
	if (!thingsChanged.join("").trim()) return window.alert("🎉 Nothing to format! Everything looks good.");
    const approval = window.confirm(`Do you want to auto-format this article?\nThis will: ${thingsChanged.join(", ").replace(/^[A-Za-z]/gim, (match) => {
    	return match.toUpperCase();
    })}`);
    if (!approval) return;

    let newPage = page
      .replace(/'"/gi, "{{' \"}}") //https://en.wiki.x.io/wiki/Wikipedia:Manual_of_Style#For%20a%20quotation%20within%20a%20quotation
      .replace(/[\t\f ]*\.(?![0-9])(?=(?:[^"]*"[^"]*")*[^"]*$)/gim, ".")
      .replace(/U\.S\.?(?=(?:[^"]*"[^"]*")*[^"]*$)/g, "US")
      .replace(/\(\s+.+\)|\(.+\s+\)/gim, (match) => {
      	match = match.replace(/^\(|\)$/gi, "").trim();
      	return `(${match})`;
      })
      .replace(/(in )?the year( of)? [0-9]{4,}(?=(?:[^"]*"[^"]*")*[^"]*$)/gi, (match) => {
		return (match.toLowerCase().includes("in")) ? match.replace(/the year( of)?/, "").trim().replace(/\s\s+/gi, " ") : match.replace(/[^0-9]/gim, "");
	  })
      .replace(/(#|№|n\.?º)[\t\f ]*[0-9]/gi, (match) => {
      	return `No. ${match.replace(/[^0-9]/gi, "")}`;
      })
      //.replace(/\.\s*\.(?=(?:[^"]*"[^"]*")*[^"]*$)/gim, (match) => {
      //	return ".";
      //})
      .replace(/(\.|\?)[\t\f ]+(<ref>|{{.*cite.*}})(?=(?:[^"]*"[^"]*")*[^"]*$)/gi, (match) => {
        const p = punc(match);
        return `${p}${match.replace(/\s/g, "").replace(punc, "")}`;
      })
      //.replace(/[^\s\.]{1,250}\.[^\s\.]{1,250}(?=(?:[^"]*"[^"]*")*[^"]*$)/gim, (match) => {
      //	if (/https?:\/\/|\.com|\.org|\.gov/gi.test(match)) return match;
      //	match = match.split("");
      //	const i = match.indexOf(".") + 1;
      //	match.splice(1, 0, " ");
      //	return match.join(" ");
      //})
      .replace(/((\.|\?)[\t\f ]*)((\.|\?)[\t\f ]*)+(?=(?:[^"]*"[^"]*")*[^"]*$)/gi, punc);

    //refs
    if (!/{{.*cite.*}}|<ref>/gi.test(newPage) && !/{{.*(unreferenced|more citations needed|p2|primary sources).*}}/gi.test(newPage) && !/{{reflist}}|==.*(references|citations|footnotes|sources|bibliography).*==/gi.test(newPage)) {
      newPage = "{{Unreferenced|date=\{\{subst:CURRENTMONTHNAME\}\} \{\{subst:CURRENTYEAR\}\}}}" + "\n" + newPage;
    } else if (newPage.split(/{{.*cite.*}}|<ref>/gi).length === 2 && !/{{.*(unreferenced|more citations needed|p2|primary sources).*}}/gi.test(newPage)) {
      newPage = "{{More citations needed|date=\{\{subst:CURRENTMONTHNAME\}\} \{\{subst:CURRENTYEAR\}\}}}" + newPage;
    }
    
    setNew(name, newPage);
  }

  function fetchPage(name) {
    const pageToGet = {
      action: 'query',
      titles: name,
      prop: 'revisions',
      rvprop: 'content',
      format: 'json',
      formatversion: 2
    };
    let result;
    $.ajax({
      url: scriptUrl,
      type: 'get',
      data: pageToGet,
      dataType: 'json',
      async: false,
      success: (page) => {
        result = page.query.pages["0"].revisions["0"].content;
      }
    });
    return result;
  }

  function setNew(page, new_content) {
    const req = {
      action: 'edit',
      title: page,
      text: new_content,
      summary: summary,
      token: mw.user.tokens.get('csrfToken')
    };
    $.when(
      $.post(scriptUrl, req, function (response) {})
    ).done(() => {
      alert("Applied changes!");
      location.reload();
      //window.location = '//en.wiki.x.io/w/index.php?title=' + encodeURIComponent(mw.config.get( 'wgTitle')) + '&diff=cur&oldid=prev';
    });
  }

  //util
  function findLastIndex(array, predicate, fromIndex) {
    const length = array == null ? 0 : array.length
    if (!length) {
      return -1
    }
    let index = length - 1
    if (fromIndex !== undefined) {
      index = toInteger(fromIndex)
      index = fromIndex < 0 ?
        Math.max(length + index, 0) :
        Math.min(index, length - 1)
    }
    return baseFindIndex(array, predicate, index, true)
  }

  function toInteger(value) {
    const result = toFinite(value)
    const remainder = result % 1

    return remainder ? result - remainder : result
  }

  function baseFindIndex(array, predicate, fromIndex, fromRight) {
    const {
      length
    } = array
    let index = fromIndex + (fromRight ? 1 : -1)

    while ((fromRight ? index-- : ++index < length)) {
      if (predicate(array[index], index, array)) {
        return index
      }
    }
    return -1
  }
});
/* </nowiki> */