User:Newslinger/Notifier.js

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.
// <nowiki>
// Notifier (beta)

// Notifies other talk pages of a discussion on the current page
// Please provide feedback at [[User talk:Newslinger/Notifier]]

$(function () {
  var api, log, pageName, texts, windowManager
  var dialogReady = false

  var talkNamespaceRegex = /(Talk|User talk|Wikipedia|Wikipedia talk|File talk|MediaWiki talk|Template talk|Help talk|Category talk|Portal talk|Draft talk|TimedText talk|Module talk):/

  function init () {
    mw.loader.using('mediawiki.util', function () {
      pageName = mw.config.get('wgPageName').replace(/_/g, ' ')

      if (pageName === 'Wikipedia:Reliable sources/Noticeboard' && $('body.skin-vector').length) {
        markHeadings()
      } else if (pageName.search(talkNamespaceRegex) === 0) {
        var markLink = mw.util.addPortletLink('p-cactions', '#', 'Notifier', 'ca-notifier',
          'Notify other talk pages of a discussion on this page')
        $(markLink).click(function (event) {
          event.preventDefault()
          markHeadings()
        })
      }
    })
  }

  function initDialog () {
    OO.inheritClass(Dialog, OO.ui.ProcessDialog)

    Dialog.static.name = 'notifierDialog'
    Dialog.static.title = 'Notifier'
    Dialog.static.actions = [
      {
        action: 'notify',
        label: 'Notify',
        flags: ['primary', 'progressive']
      }, {
        label: 'Cancel',
        flags: 'safe'
      }
    ]

    Dialog.prototype.initialize = function () {
      Dialog.super.prototype.initialize.call(this)

      this.$notifyingLabel = $('<div>Notifying of discussion:</div>')
      this.$discussionName = $('<div style="font-weight: bold" />')
      this.$info = $('<p />')
      this.$info.append(this.$notifyingLabel, this.$discussionName)

      this.targetPages = new OO.ui.MultilineTextInputWidget({rows: 5})
      this.targetPagesField = new OO.ui.FieldLayout(this.targetPages, {
        label: 'Pages to notify',
        align: 'top',
        help: 'One page per line (maximum 90 pages). Only pages in talk and project namespaces are allowed. Subject to the rate limit on your account (90 edits/minute if autoconfirmed, and 8 edits/minute if not). Edits exceeding the limit will fail.',
        helpInline: true
      })
      this.targetPages.on('change', (function (value) {
        var pageCount = value.trim().split('\n').length
        var countLabel = ''

        if (pageCount > 90) {
          countLabel = ' (' + pageCount + ' pages; exceeds maximum of 90)'
        } else if (value) {
          countLabel = ' (' + pageCount + ' page' + (pageCount > 1 ? 's' : '') + ')'
        }

        this.targetPagesField.setLabel('Pages to notify' + countLabel)
      }).bind(this))

      this.messageTitle = new OO.ui.TextInputWidget({})
      this.messageTitleField = new OO.ui.FieldLayout(this.messageTitle, {
        label: 'Message title',
        align: 'top'
      })
      
      this.message = new OO.ui.MultilineTextInputWidget({rows: 5})
      this.messageField = new OO.ui.FieldLayout(this.message, {
        label: 'Message',
        align: 'top',
        help: 'Signature not needed. Your signature will be added if not present at the end of the message.',
        helpInline: true
      })

      this.log = new OO.ui.MultilineTextInputWidget({
        readOnly: true,
        rows: 5,
        value: log
      })
      this.logField = new OO.ui.FieldLayout(this.log, {
        label: 'Log',
        align: 'top'
      })

      this.notificationTemplate = new OO.ui.TextInputWidget({readOnly: true})
      this.notificationTemplateField = new OO.ui.FieldLayout(this.notificationTemplate, {
        label: 'Notification template',
        align: 'top',
        help: 'After notifying, paste this template into the end of the original discussion.',
        helpInline: true
      })

      this.panel = new OO.ui.PanelLayout({
        padded: true,
        expanded: false
      })

      this.panel.$element.append(this.$info, this.targetPagesField.$element, this.messageTitleField.$element,
        this.messageField.$element, this.logField.$element, this.notificationTemplateField.$element)
      this.$body.append(this.panel.$element)
    }

    Dialog.prototype.getSetupProcess = function (data) {
      data = data || {}
      return Dialog.super.prototype.getSetupProcess.call(this, data)
        .next(function () {
          this.$discussionName.text(pageName + ' § ' + texts[data.id])
          this.message.setValue('{{slink|' + pageName + '|' + texts[data.id] + '}}')
        }, this)
    }

    Dialog.prototype.getActionProcess = function (action) {
      if (action === 'notify') {
        sendMessages(this)
      }
      return Dialog.super.prototype.getActionProcess.call(this, action)
    }

    Dialog.prototype.getBodyHeight = function () {
      return this.panel.$element.outerHeight(true)
    }

    windowManager = new OO.ui.WindowManager()
    $(document.body).append(windowManager.$element)

    dialogReady = true
  }

  function Dialog (config) {
    Dialog.super.call(this, config)
  }

  function markHeadings () {
    texts = {}
    var $headings = $('#mw-content-text h2:not(.toctitle h2)')

    $headings.each(function (index) {
      var $headline = $(this).children('.mw-headline')
      var id = $headline.attr('id')
      var text = $headline.contents().not($headline.children('.mw-headline-number')).text().trim()

      createLink($(this), id, text)
      texts[id] = text
    })
  }

  function createLink ($heading, id, text) {
    var $link = $('<a href="#">notify</a>')
    var $leftBracket = $('<span class="mw-editsection-bracket">[</span>')
    var $rightBracket = $('<span class="mw-editsection-bracket">]</span>')
    var $editSection = $('<span class="mw-editsection" />')
    
    $link.click(function (event) {
      event.preventDefault()
      promptNotify(id)
    })
    
    $editSection.append($leftBracket, $link, $rightBracket)
    $heading.append($editSection)

    return $link
  }

  function promptNotify (id) {
    mw.loader.using(['oojs', 'oojs-ui'], function () {
      if (!dialogReady) {
        initDialog()
      }

      log = 'Ready to notify'

      var dialog = new Dialog()
      windowManager.addWindows([dialog])
      windowManager.openWindow(dialog, {id: id})
    })
  }

  function sendMessages (dialog) {
    api = api || new mw.Api()

    var targetPages = dialog.targetPages.getValue().trim().split('\n').map(function (targetPage) {
      return targetPage.trim()
    })
    var messageTitle = dialog.messageTitle.getValue().trim()
    var message = dialog.message.getValue().trim()
    
    if (message.slice(-4) !== '~~' + '~~') {
      message += ' ~~' + '~~'
    }

    var notificationTemplate = '{{subst' + ':notified|' + targetPages.join('|') + '}}'
    dialog.notificationTemplate.setValue(notificationTemplate)

    var tasks = []

    $.each(targetPages, function(index, targetPage) {
      addLog(dialog, 'Notifying: ' + targetPage)

      if (targetPage.search(talkNamespaceRegex) != 0) {
        addLog(dialog, 'Failed to notify: ' + targetPage)
        addLog(dialog, 'Error: Pages outside of talk and project namespaces are not allowed.')

        return
      }

      var task = api.newSection(targetPage, messageTitle, message, { redirect: true })
        .then(function () {
          addLog(dialog, 'Successfully notified: ' + targetPage)
        }, function(error) {
          addLog(dialog, 'Failed to notify: ' + targetPage)
          addLog(dialog, 'Error: ' + error)
        })

      tasks.push(task)
    })

    $.when.apply(null, tasks).then(function () {
      addLog(dialog, 'Finished!')
    })
  }

  function addLog (dialog, message) {
    log += '\n' + message
    dialog.log.setValue(log)
  }

  init()
})
// </nowiki>