From Wikipedia, the free encyclopedia
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.

// Forked from [[User:Gary/comments in local time.js]]

/**

 * COMMENTS IN LOCAL TIME

 *

 * Description:

 * Changes [[Coordinated Universal Time|UTC]]-based times and dates,

 * such as those used in signatures, to be relative to local time.

 *

 * Documentation:

 * [[Wikipedia:Comments in Local Time]]

 */

mw.hook('wikipage.content').add(($content) => {

  /**

   * Given a number, add a leading zero if necessary, so that the final number

   * has two characters.

   *

   * @param {number} number Number

   * @returns {string} The number with a leading zero, if necessary.

   */

  function addLeadingZero(number) {

    const numberArg = number;



    if (numberArg < 10) {

      return `0${numberArg}`;

    }



    return numberArg;

  }



  function convertMonthToNumber(month) {

    return new Date(`${month} 1, 2001`).getMonth();

  }



  function getDates(time) {

    const [, oldHour, oldMinute, oldDay, oldMonth, oldYear = time;



    // Today

    const today = new Date();



    // Yesterday

    const yesterday = new Date();



    yesterday.setDate(yesterday.getDate() - 1);



    // Tomorrow

    const tomorrow = new Date();



    tomorrow.setDate(tomorrow.getDate() + 1);



    // Set the date entered.

    const newTime = new Date();



    newTime.setUTCFullYear(oldYear, convertMonthToNumber(oldMonth), oldDay);

    newTime.setUTCHours(oldHour);

    newTime.setUTCMinutes(oldMinute);



    return { time: newTime, today, tomorrow, yesterday };

  }



  /**

   * Determine whether to use the singular or plural word, and use that.

   *

   * @param {string} term Original term

   * @param {number} count Count of items

   * @param {string} plural Pluralized term

   * @returns {string} The word to use

   */

  function pluralize(term, count, plural = null) {

    let pluralArg = plural;



    // No unique pluralized word is found, so just use a general one.

    if (!pluralArg) {

      pluralArg = `${term}s`;

    }



    // There's only one item, so just use the singular word.

    if (count === 1) {

      return term;

    }



    // There are multiple items, so use the plural word.

    return pluralArg;

  }



  class CommentsInLocalTime {

    constructor() {

      this.language = '';

      this.LocalComments = {};



      /**

       * Settings

       */

      this.settings();



      this.language = this.setDefaultSetting(

        'language',

        this.LocalComments.language

      );



      // These values are also reflected in the documentation:

      // /info/en/?search=Wikipedia:Comments_in_Local_Time#Default_settings

      this.setDefaultSetting({

        dateDifference: true,

        dateFormat: 'dmy',

        dayOfWeek: true,

        dropDays: 0,

        dropMonths: 0,

        timeFirst: true,

        twentyFourHours: false,

      });

    }



    adjustTime(originalTimestamp, search) {

      const { time, today, tomorrow, yesterday } = getDates(

        originalTimestamp.match(search)

      );



      // A string matching the date pattern was found, but it cannot be

      // converted to a Date object. Return it with no changes made.

      if (Number.isNaN(time)) {

        return originalTimestamp, ''];

      }



      const date = this.determineDateText({

        time,

        today,

        tomorrow,

        yesterday,

      });



      const { ampm, hour } = this.getHour(time);

      const minute = addLeadingZero(time.getMinutes());

      const finalTime = `${hour}:${minute}${ampm}`;

      let returnDate;



      // Determine the time offset.

      const utcValue = (-1 * time.getTimezoneOffset()) / 60;

      const utcOffset =

        utcValue >= 0 ? `+${utcValue}` : `−${Math.abs(utcValue.toFixed(1))}`;



      if (this.LocalComments.timeFirst) {

        returnDate = `${finalTime}, ${date} (UTC${utcOffset})`;

      } else {

        returnDate = `${date}, ${finalTime} (UTC${utcOffset})`;

      }



      return { returnDate, time };

    }



    convertNumberToMonth(number) {

      return 

        this.language.January,

        this.language.February,

        this.language.March,

        this.language.April,

        this.language.May,

        this.language.June,

        this.language.July,

        this.language.August,

        this.language.September,

        this.language.October,

        this.language.November,

        this.language.December,

      ][number];

    }



    createDateText({ day, month, time, today, year }) {

      // Calculate day of week

      const dayNames = 

        this.language.Sunday,

        this.language.Monday,

        this.language.Tuesday,

        this.language.Wednesday,

        this.language.Thursday,

        this.language.Friday,

        this.language.Saturday,

      ];

      const dayOfTheWeek = dayNamestime.getDay()];

      let descriptiveDifference = '';

      let last = '';



      // Create a relative descriptive difference

      if (this.LocalComments.dateDifference) {

        ({ descriptiveDifference, last } = this.createRelativeDate(

          today,

          time

        ));

      }



      const monthName = this.convertNumberToMonth(time.getMonth());



      // Format the date according to user preferences

      let formattedDate = '';



      switch (this.LocalComments.dateFormat.toLowerCase()) {

        case 'dmy':

          formattedDate = `${day} ${monthName} ${year}`;



          break;

        case 'mdy':

          formattedDate = `${monthName} ${day}, ${year}`;



          break;

        default:

          formattedDate = `${year}-${month}-${addLeadingZero(day)}`;

      }



      let formattedDayOfTheWeek = '';



      if (this.LocalComments.dayOfWeek) {

        formattedDayOfTheWeek = `, ${last}${dayOfTheWeek}`;

      }



      return formattedDate + formattedDayOfTheWeek + descriptiveDifference;

    }



    /**

     * Create relative date data.

     *

     * @param {Date} today Today

     * @param {Date} time The timestamp from a comment

     * @returns {Object.<string, *>} Relative date data

     */

    createRelativeDate(today, time) {

      /**

       * The time difference from today, in milliseconds.

       *

       * @type {number}

       */

      const millisecondsAgo = today.getTime() - time.getTime();



      /**

       * The number of days ago, that we will display. It's not necessarily the

       * total days ago.

       *

       * @type {number}

       */

      let daysAgo = Math.abs(Math.round(millisecondsAgo / 1000 / 60 / 60 / 24));

      const { differenceWord, last } = this.relativeText({

        daysAgo,

        millisecondsAgo,

      });



      // This method of computing the years and months is not exact. However,

      // it's better than the previous method that used 1 January + delta days.

      // That was usually quite off because it mapped the second delta month to

      // February, which has only 28 days. This method is usually not more than

      // one day off, except perhaps over very distant dates.



      /**

       * The number of months ago, that we will display. It's not necessarily

       * the total months ago.

       *

       * @type {number}

       */

      let monthsAgo = Math.floor((daysAgo / 365) * 12);



      /**

       * The total amount of time ago, in months.

       *

       * @type {number}

       */

      const totalMonthsAgo = monthsAgo;



      /**

       * The number of years ago that we will display. It's not necessarily the

       * total years ago.

       *

       * @type {number}

       */

      let yearsAgo = Math.floor(totalMonthsAgo / 12);



      if (totalMonthsAgo < this.LocalComments.dropMonths) {

        yearsAgo = 0;

      } else if (this.LocalComments.dropMonths > 0) {

        monthsAgo = 0;

      } else {

        monthsAgo -= yearsAgo * 12;

      }



      if (daysAgo < this.LocalComments.dropDays) {

        monthsAgo = 0;

        yearsAgo = 0;

      } else if (this.LocalComments.dropDays > 0 && totalMonthsAgo >= 1) {

        daysAgo = 0;

      } else {

        daysAgo -= Math.floor((totalMonthsAgo * 365) / 12);

      }



      const descriptiveParts = [];



      // There is years text to add.

      if (yearsAgo > 0) {

        descriptiveParts.push(

          `${yearsAgo} ${pluralize(

            this.language.year,

            yearsAgo,

            this.language.years

          )}`

        );

      }



      // There is months text to add.

      if (monthsAgo > 0) {

        descriptiveParts.push(

          `${monthsAgo} ${pluralize(

            this.language.month,

            monthsAgo,

            this.language.months

          )}`

        );

      }



      // There is days text to add.

      if (daysAgo > 0) {

        descriptiveParts.push(

          `${daysAgo} ${pluralize(

            this.language.day,

            daysAgo,

            this.language.days

          )}`

        );

      }



      return {

        descriptiveDifference: ` (${descriptiveParts.join(

          ', '

        )} ${differenceWord})`,

        last,

      };

    }



    determineDateText({ time, today, tomorrow, yesterday }) {

      // Set the date bits to output.

      const year = time.getFullYear();

      const month = addLeadingZero(time.getMonth() + 1);

      const day = time.getDate();



      // Return 'today' or 'yesterday' if that is the case

      if (

        year === today.getFullYear() &&

        month === addLeadingZero(today.getMonth() + 1) &&

        day === today.getDate()

      ) {

        return this.language.Today;

      }



      if (

        year === yesterday.getFullYear() &&

        month === addLeadingZero(yesterday.getMonth() + 1) &&

        day === yesterday.getDate()

      ) {

        return this.language.Yesterday;

      }



      if (

        year === tomorrow.getFullYear() &&

        month === addLeadingZero(tomorrow.getMonth() + 1) &&

        day === tomorrow.getDate()

      ) {

        return this.language.Tomorrow;

      }



      return this.createDateText({ day, month, time, today, year });

    }



    getHour(time) {

      let ampm;

      let hour = parseInt(time.getHours(), 10);



      if (this.LocalComments.twentyFourHours) {

        ampm = '';

        hour = addLeadingZero(hour);

      } else {

        // Output am or pm depending on the date.

        ampm = hour <= 11 ? ' am' : ' pm';



        if (hour > 12) {

          hour -= 12;

        } else if (hour === 0) {

          hour = 12;

        }

      }



      return { ampm, hour };

    }



    relativeText({ daysAgo, millisecondsAgo }) {

      let differenceWord = '';

      let last = '';



      // The date is in the past.

      if (millisecondsAgo >= 0) {

        differenceWord = this.language.ago;



        if (daysAgo <= 7) {

          last = `${this.language.last} `;

        }



        // The date is in the future.

      } else {

        differenceWord = this.language'from now'];



        if (daysAgo <= 7) {

          last = `${this.language.this} `;

        }

      }



      return { differenceWord, last };

    }



    replaceText(node, search) {

      if (!node) {

        return;

      }



      // Check if this is a text node.

      if (node.nodeType === 3) {

        let parent = node.parentNode;



        const parentNodeName = parent.nodeName;



        if (['CODE', 'PRE'].includes(parentNodeName)) {

          return;

        }



        const value = node.nodeValue;

        const matches = value.match(search);



        // Stick with manipulating the DOM directly rather than using jQuery.

        // I've got more than a 100% speed improvement afterward.

        if (matches) {

          // Only act on the first timestamp we found in this node. This is

          // really a temporary fix for the situation in which there are two or

          // more timestamps in the same node.

          const match = matches;

          const position = value.search(search);

          const stringLength = match.toString().length;

          const beforeMatch = value.substring(0, position);

          const afterMatch = value.substring(position + stringLength);

          const { returnDate, time } = this.adjustTime(

            match.toString(),

            search

          );

          const timestamp = time ? time.getTime() : '';



          // Is the "timestamp" attribute used for microformats?

          const span = document.createElement('span');



          span.className = 'localcomments';

          span.style.fontSize = '95%';

          span.style.whiteSpace = 'nowrap';

          span.setAttribute('timestamp', timestamp);

          span.title = match;

          span.append(document.createTextNode(returnDate));



          parent = node.parentNode;

          parent.replaceChild(span, node);



          const before = document.createElement('span');



          before.className = 'before-localcomments';

          before.append(document.createTextNode(beforeMatch));



          const after = document.createElement('span');



          after.className = 'after-localcomments';

          after.append(document.createTextNode(afterMatch));



          parent.insertBefore(before, span);

          parent.insertBefore(after, span.nextSibling);

        }

      } else {

        const children = [];

        let child;



        child = node.childNodes;



        while (child) {

          children.push(child);

          child = child.nextSibling;

        }



        // Loop through children and run this func on it again, recursively.

        children.forEach((child2) => {

          this.replaceText(child2, search);

        });

      }

    }



    run() {

      if (

        '', 'MediaWiki', 'Special'].includes(

          mw.config.get('wgCanonicalNamespace')

        )

      ) {

        return;

      }



      // Check for disabled URLs.

      const isDisabledUrl = 'action=history'].some((disabledUrl) =>

        document.location.href.includes(disabledUrl)

      );



      if (isDisabledUrl) {

        return;

      }

      

      // If this content already contains localcomments class, it was

      // already worked on.

      if ($content0].querySelector('.localcomments')) {

        return;

      }



      this.replaceText(

        $content0],

        /(\d{1,2}):(\d{2}), (\d{1,2}) ([A-Z][a-z]+) (\d{4}) \(UTC\)/

      );

    }



    setDefaultSetting(...args) {

      // There are no arguments.

      if (args.length === 0) {

        return false;

      }



      // The first arg is an object, so just set that data directly onto the

      // settings object. like {setting 1: true, setting 2: false}

      if (typeof args0 === 'object') {

        const settings = args;



        // Loop through each setting.

        Object.keys(settings).forEach((name) => {

          const value = settingsname];



          if (typeof this.LocalCommentsname === 'undefined') {

            this.LocalCommentsname = value;

          }

        });



        return settings;

      }



      // The first arg is a string, so use the first arg as the settings key,

      // and the second arg as the value to set it to.

      const name, setting = args;



      if (typeof this.LocalCommentsname === 'undefined') {

        this.LocalCommentsname = setting;

      }



      return this.LocalCommentsname];

    }



    /**

     * Set the script's settings.

     *

     * @returns {undefined}

     */

    settings() {

      // The user has set custom settings, so use those.

      if (window.LocalComments) {

        this.LocalComments = window.LocalComments;

      }



      /**

       * Language

       *

       * LOCALIZING THIS SCRIPT

       * To localize this script, change the terms below,

       * to the RIGHT of the colons, to the correct term used in that language.

       *

       * For example, in the French language,

       *

       * 'Today' : 'Today',

       *

       * would be

       *

       * 'Today' : "Aujourd'hui",

       */

      this.LocalComments.language = {

        // Relative terms

        Today: 'Today',

        Yesterday: 'Yesterday',

        Tomorrow: 'Tomorrow',

        last: 'last',

        this: 'this',



        // Days of the week

        Sunday: 'Sunday',

        Monday: 'Monday',

        Tuesday: 'Tuesday',

        Wednesday: 'Wednesday',

        Thursday: 'Thursday',

        Friday: 'Friday',

        Saturday: 'Saturday',



        // Months of the year

        January: 'January',

        February: 'February',

        March: 'March',

        April: 'April',

        May: 'May',

        June: 'June',

        July: 'July',

        August: 'August',

        September: 'September',

        October: 'October',

        November: 'November',

        December: 'December',



        // Difference words

        ago: 'ago',

        'from now': 'from now',



        // Date phrases

        year: 'year',

        years: 'years',

        month: 'month',

        months: 'months',

        day: 'day',

        days: 'days',

      };

    }

  }



  new CommentsInLocalTime().run();

});
From Wikipedia, the free encyclopedia
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.

// Forked from [[User:Gary/comments in local time.js]]

/**

 * COMMENTS IN LOCAL TIME

 *

 * Description:

 * Changes [[Coordinated Universal Time|UTC]]-based times and dates,

 * such as those used in signatures, to be relative to local time.

 *

 * Documentation:

 * [[Wikipedia:Comments in Local Time]]

 */

mw.hook('wikipage.content').add(($content) => {

  /**

   * Given a number, add a leading zero if necessary, so that the final number

   * has two characters.

   *

   * @param {number} number Number

   * @returns {string} The number with a leading zero, if necessary.

   */

  function addLeadingZero(number) {

    const numberArg = number;



    if (numberArg < 10) {

      return `0${numberArg}`;

    }



    return numberArg;

  }



  function convertMonthToNumber(month) {

    return new Date(`${month} 1, 2001`).getMonth();

  }



  function getDates(time) {

    const [, oldHour, oldMinute, oldDay, oldMonth, oldYear = time;



    // Today

    const today = new Date();



    // Yesterday

    const yesterday = new Date();



    yesterday.setDate(yesterday.getDate() - 1);



    // Tomorrow

    const tomorrow = new Date();



    tomorrow.setDate(tomorrow.getDate() + 1);



    // Set the date entered.

    const newTime = new Date();



    newTime.setUTCFullYear(oldYear, convertMonthToNumber(oldMonth), oldDay);

    newTime.setUTCHours(oldHour);

    newTime.setUTCMinutes(oldMinute);



    return { time: newTime, today, tomorrow, yesterday };

  }



  /**

   * Determine whether to use the singular or plural word, and use that.

   *

   * @param {string} term Original term

   * @param {number} count Count of items

   * @param {string} plural Pluralized term

   * @returns {string} The word to use

   */

  function pluralize(term, count, plural = null) {

    let pluralArg = plural;



    // No unique pluralized word is found, so just use a general one.

    if (!pluralArg) {

      pluralArg = `${term}s`;

    }



    // There's only one item, so just use the singular word.

    if (count === 1) {

      return term;

    }



    // There are multiple items, so use the plural word.

    return pluralArg;

  }



  class CommentsInLocalTime {

    constructor() {

      this.language = '';

      this.LocalComments = {};



      /**

       * Settings

       */

      this.settings();



      this.language = this.setDefaultSetting(

        'language',

        this.LocalComments.language

      );



      // These values are also reflected in the documentation:

      // /info/en/?search=Wikipedia:Comments_in_Local_Time#Default_settings

      this.setDefaultSetting({

        dateDifference: true,

        dateFormat: 'dmy',

        dayOfWeek: true,

        dropDays: 0,

        dropMonths: 0,

        timeFirst: true,

        twentyFourHours: false,

      });

    }



    adjustTime(originalTimestamp, search) {

      const { time, today, tomorrow, yesterday } = getDates(

        originalTimestamp.match(search)

      );



      // A string matching the date pattern was found, but it cannot be

      // converted to a Date object. Return it with no changes made.

      if (Number.isNaN(time)) {

        return originalTimestamp, ''];

      }



      const date = this.determineDateText({

        time,

        today,

        tomorrow,

        yesterday,

      });



      const { ampm, hour } = this.getHour(time);

      const minute = addLeadingZero(time.getMinutes());

      const finalTime = `${hour}:${minute}${ampm}`;

      let returnDate;



      // Determine the time offset.

      const utcValue = (-1 * time.getTimezoneOffset()) / 60;

      const utcOffset =

        utcValue >= 0 ? `+${utcValue}` : `−${Math.abs(utcValue.toFixed(1))}`;



      if (this.LocalComments.timeFirst) {

        returnDate = `${finalTime}, ${date} (UTC${utcOffset})`;

      } else {

        returnDate = `${date}, ${finalTime} (UTC${utcOffset})`;

      }



      return { returnDate, time };

    }



    convertNumberToMonth(number) {

      return 

        this.language.January,

        this.language.February,

        this.language.March,

        this.language.April,

        this.language.May,

        this.language.June,

        this.language.July,

        this.language.August,

        this.language.September,

        this.language.October,

        this.language.November,

        this.language.December,

      ][number];

    }



    createDateText({ day, month, time, today, year }) {

      // Calculate day of week

      const dayNames = 

        this.language.Sunday,

        this.language.Monday,

        this.language.Tuesday,

        this.language.Wednesday,

        this.language.Thursday,

        this.language.Friday,

        this.language.Saturday,

      ];

      const dayOfTheWeek = dayNamestime.getDay()];

      let descriptiveDifference = '';

      let last = '';



      // Create a relative descriptive difference

      if (this.LocalComments.dateDifference) {

        ({ descriptiveDifference, last } = this.createRelativeDate(

          today,

          time

        ));

      }



      const monthName = this.convertNumberToMonth(time.getMonth());



      // Format the date according to user preferences

      let formattedDate = '';



      switch (this.LocalComments.dateFormat.toLowerCase()) {

        case 'dmy':

          formattedDate = `${day} ${monthName} ${year}`;



          break;

        case 'mdy':

          formattedDate = `${monthName} ${day}, ${year}`;



          break;

        default:

          formattedDate = `${year}-${month}-${addLeadingZero(day)}`;

      }



      let formattedDayOfTheWeek = '';



      if (this.LocalComments.dayOfWeek) {

        formattedDayOfTheWeek = `, ${last}${dayOfTheWeek}`;

      }



      return formattedDate + formattedDayOfTheWeek + descriptiveDifference;

    }



    /**

     * Create relative date data.

     *

     * @param {Date} today Today

     * @param {Date} time The timestamp from a comment

     * @returns {Object.<string, *>} Relative date data

     */

    createRelativeDate(today, time) {

      /**

       * The time difference from today, in milliseconds.

       *

       * @type {number}

       */

      const millisecondsAgo = today.getTime() - time.getTime();



      /**

       * The number of days ago, that we will display. It's not necessarily the

       * total days ago.

       *

       * @type {number}

       */

      let daysAgo = Math.abs(Math.round(millisecondsAgo / 1000 / 60 / 60 / 24));

      const { differenceWord, last } = this.relativeText({

        daysAgo,

        millisecondsAgo,

      });



      // This method of computing the years and months is not exact. However,

      // it's better than the previous method that used 1 January + delta days.

      // That was usually quite off because it mapped the second delta month to

      // February, which has only 28 days. This method is usually not more than

      // one day off, except perhaps over very distant dates.



      /**

       * The number of months ago, that we will display. It's not necessarily

       * the total months ago.

       *

       * @type {number}

       */

      let monthsAgo = Math.floor((daysAgo / 365) * 12);



      /**

       * The total amount of time ago, in months.

       *

       * @type {number}

       */

      const totalMonthsAgo = monthsAgo;



      /**

       * The number of years ago that we will display. It's not necessarily the

       * total years ago.

       *

       * @type {number}

       */

      let yearsAgo = Math.floor(totalMonthsAgo / 12);



      if (totalMonthsAgo < this.LocalComments.dropMonths) {

        yearsAgo = 0;

      } else if (this.LocalComments.dropMonths > 0) {

        monthsAgo = 0;

      } else {

        monthsAgo -= yearsAgo * 12;

      }



      if (daysAgo < this.LocalComments.dropDays) {

        monthsAgo = 0;

        yearsAgo = 0;

      } else if (this.LocalComments.dropDays > 0 && totalMonthsAgo >= 1) {

        daysAgo = 0;

      } else {

        daysAgo -= Math.floor((totalMonthsAgo * 365) / 12);

      }



      const descriptiveParts = [];



      // There is years text to add.

      if (yearsAgo > 0) {

        descriptiveParts.push(

          `${yearsAgo} ${pluralize(

            this.language.year,

            yearsAgo,

            this.language.years

          )}`

        );

      }



      // There is months text to add.

      if (monthsAgo > 0) {

        descriptiveParts.push(

          `${monthsAgo} ${pluralize(

            this.language.month,

            monthsAgo,

            this.language.months

          )}`

        );

      }



      // There is days text to add.

      if (daysAgo > 0) {

        descriptiveParts.push(

          `${daysAgo} ${pluralize(

            this.language.day,

            daysAgo,

            this.language.days

          )}`

        );

      }



      return {

        descriptiveDifference: ` (${descriptiveParts.join(

          ', '

        )} ${differenceWord})`,

        last,

      };

    }



    determineDateText({ time, today, tomorrow, yesterday }) {

      // Set the date bits to output.

      const year = time.getFullYear();

      const month = addLeadingZero(time.getMonth() + 1);

      const day = time.getDate();



      // Return 'today' or 'yesterday' if that is the case

      if (

        year === today.getFullYear() &&

        month === addLeadingZero(today.getMonth() + 1) &&

        day === today.getDate()

      ) {

        return this.language.Today;

      }



      if (

        year === yesterday.getFullYear() &&

        month === addLeadingZero(yesterday.getMonth() + 1) &&

        day === yesterday.getDate()

      ) {

        return this.language.Yesterday;

      }



      if (

        year === tomorrow.getFullYear() &&

        month === addLeadingZero(tomorrow.getMonth() + 1) &&

        day === tomorrow.getDate()

      ) {

        return this.language.Tomorrow;

      }



      return this.createDateText({ day, month, time, today, year });

    }



    getHour(time) {

      let ampm;

      let hour = parseInt(time.getHours(), 10);



      if (this.LocalComments.twentyFourHours) {

        ampm = '';

        hour = addLeadingZero(hour);

      } else {

        // Output am or pm depending on the date.

        ampm = hour <= 11 ? ' am' : ' pm';



        if (hour > 12) {

          hour -= 12;

        } else if (hour === 0) {

          hour = 12;

        }

      }



      return { ampm, hour };

    }



    relativeText({ daysAgo, millisecondsAgo }) {

      let differenceWord = '';

      let last = '';



      // The date is in the past.

      if (millisecondsAgo >= 0) {

        differenceWord = this.language.ago;



        if (daysAgo <= 7) {

          last = `${this.language.last} `;

        }



        // The date is in the future.

      } else {

        differenceWord = this.language'from now'];



        if (daysAgo <= 7) {

          last = `${this.language.this} `;

        }

      }



      return { differenceWord, last };

    }



    replaceText(node, search) {

      if (!node) {

        return;

      }



      // Check if this is a text node.

      if (node.nodeType === 3) {

        let parent = node.parentNode;



        const parentNodeName = parent.nodeName;



        if (['CODE', 'PRE'].includes(parentNodeName)) {

          return;

        }



        const value = node.nodeValue;

        const matches = value.match(search);



        // Stick with manipulating the DOM directly rather than using jQuery.

        // I've got more than a 100% speed improvement afterward.

        if (matches) {

          // Only act on the first timestamp we found in this node. This is

          // really a temporary fix for the situation in which there are two or

          // more timestamps in the same node.

          const match = matches;

          const position = value.search(search);

          const stringLength = match.toString().length;

          const beforeMatch = value.substring(0, position);

          const afterMatch = value.substring(position + stringLength);

          const { returnDate, time } = this.adjustTime(

            match.toString(),

            search

          );

          const timestamp = time ? time.getTime() : '';



          // Is the "timestamp" attribute used for microformats?

          const span = document.createElement('span');



          span.className = 'localcomments';

          span.style.fontSize = '95%';

          span.style.whiteSpace = 'nowrap';

          span.setAttribute('timestamp', timestamp);

          span.title = match;

          span.append(document.createTextNode(returnDate));



          parent = node.parentNode;

          parent.replaceChild(span, node);



          const before = document.createElement('span');



          before.className = 'before-localcomments';

          before.append(document.createTextNode(beforeMatch));



          const after = document.createElement('span');



          after.className = 'after-localcomments';

          after.append(document.createTextNode(afterMatch));



          parent.insertBefore(before, span);

          parent.insertBefore(after, span.nextSibling);

        }

      } else {

        const children = [];

        let child;



        child = node.childNodes;



        while (child) {

          children.push(child);

          child = child.nextSibling;

        }



        // Loop through children and run this func on it again, recursively.

        children.forEach((child2) => {

          this.replaceText(child2, search);

        });

      }

    }



    run() {

      if (

        '', 'MediaWiki', 'Special'].includes(

          mw.config.get('wgCanonicalNamespace')

        )

      ) {

        return;

      }



      // Check for disabled URLs.

      const isDisabledUrl = 'action=history'].some((disabledUrl) =>

        document.location.href.includes(disabledUrl)

      );



      if (isDisabledUrl) {

        return;

      }

      

      // If this content already contains localcomments class, it was

      // already worked on.

      if ($content0].querySelector('.localcomments')) {

        return;

      }



      this.replaceText(

        $content0],

        /(\d{1,2}):(\d{2}), (\d{1,2}) ([A-Z][a-z]+) (\d{4}) \(UTC\)/

      );

    }



    setDefaultSetting(...args) {

      // There are no arguments.

      if (args.length === 0) {

        return false;

      }



      // The first arg is an object, so just set that data directly onto the

      // settings object. like {setting 1: true, setting 2: false}

      if (typeof args0 === 'object') {

        const settings = args;



        // Loop through each setting.

        Object.keys(settings).forEach((name) => {

          const value = settingsname];



          if (typeof this.LocalCommentsname === 'undefined') {

            this.LocalCommentsname = value;

          }

        });



        return settings;

      }



      // The first arg is a string, so use the first arg as the settings key,

      // and the second arg as the value to set it to.

      const name, setting = args;



      if (typeof this.LocalCommentsname === 'undefined') {

        this.LocalCommentsname = setting;

      }



      return this.LocalCommentsname];

    }



    /**

     * Set the script's settings.

     *

     * @returns {undefined}

     */

    settings() {

      // The user has set custom settings, so use those.

      if (window.LocalComments) {

        this.LocalComments = window.LocalComments;

      }



      /**

       * Language

       *

       * LOCALIZING THIS SCRIPT

       * To localize this script, change the terms below,

       * to the RIGHT of the colons, to the correct term used in that language.

       *

       * For example, in the French language,

       *

       * 'Today' : 'Today',

       *

       * would be

       *

       * 'Today' : "Aujourd'hui",

       */

      this.LocalComments.language = {

        // Relative terms

        Today: 'Today',

        Yesterday: 'Yesterday',

        Tomorrow: 'Tomorrow',

        last: 'last',

        this: 'this',



        // Days of the week

        Sunday: 'Sunday',

        Monday: 'Monday',

        Tuesday: 'Tuesday',

        Wednesday: 'Wednesday',

        Thursday: 'Thursday',

        Friday: 'Friday',

        Saturday: 'Saturday',



        // Months of the year

        January: 'January',

        February: 'February',

        March: 'March',

        April: 'April',

        May: 'May',

        June: 'June',

        July: 'July',

        August: 'August',

        September: 'September',

        October: 'October',

        November: 'November',

        December: 'December',



        // Difference words

        ago: 'ago',

        'from now': 'from now',



        // Date phrases

        year: 'year',

        years: 'years',

        month: 'month',

        months: 'months',

        day: 'day',

        days: 'days',

      };

    }

  }



  new CommentsInLocalTime().run();

});

Videos

Youtube | Vimeo | Bing

Websites

Google | Yahoo | Bing

Encyclopedia

Google | Yahoo | Bing

Facebook