مدیاویکی:Gadget-Extra-Editbuttons-Functions.js

از مشروطه
پرش به ناوبری پرش به جستجو

نکته: پس از ذخیره کردن ممکن است برای دیدن تغییرات نیاز باشد که حافظهٔ نهانی مرورگر خود را پاک کنید.

  • فایرفاکس / سافاری: کلید Shift را نگه دارید و روی دکمهٔ Reload کلیک کنید، یا کلید‌های Ctrl-F5 یا Ctrl-R را با هم فشار دهید (در رایانه‌های اپل مکینتاش کلید‌های ⌘-R)
  • گوگل کروم: کلیدهای Ctrl+Shift+R را با هم فشار دهید (در رایانه‌های اپل مکینتاش کلید‌های ⌘-Shift-R)
  • اینترنت اکسپلورر: کلید Ctrl را نگه‌دارید و روی دکمهٔ Refresh کلیک کنید، یا کلید‌های Ctrl-F5 را با هم فشار دهید
  • اپرا: بروید به Menu → Settings (Opera → Preferences on a Mac) and then to Privacy & security → Clear browsing data → Cached images and files.
// <nowiki>
/*!
 * Persian Wikipedia tools
 * http://fa.wikipedia.org/wiki/MediaWiki:PersianTools.js
 * Released under the CC-by-SA 3.0 license
 * Copyright (c) 2006-2013 Persian Wikipedia
 * @author: ebraminio
 * @contributors: See http://fa.wikipedia.org/w/index.php?title=MediaWiki:Gadget-Extra-Editbuttons-Functions.js&action=history
 */
/*jslint indent: 2, regexp: true, node: true*/
(function (root) {
  'use strict';

  var persianGlyphs, persianDigits, arabicIndicDigits, arabicDigits,
    persianCharacters, persianPastVerbs, persianPresentVerbs;

  arabicDigits = '0123456789';
  arabicIndicDigits = '٠١٢٣٤٥٦٧٨٩';
  persianCharacters = 'ءآأؤإئابپةتثجحخچدذرزژسشصضطظعغفقكکگلمنهوىیي' + 'ًٌٍَُِّْ';
  persianDigits = '۰۱۲۳۴۵۶۷۸۹';

  function normalizeZwnj(text) {
    return text
      // Replace LRM، RLM characters with ZWNJ and it will remove unneeded ZWNJ at next lines
      // .replace(/(\u202A|\u202B|\u202C|\u202D|\u202E|\u200F|\¬)/g, '\u200c')
      .replace(new RegExp('([' + persianCharacters + '] *)\u200F+( *[' + persianCharacters + '])', 'g'), '$1\u200c$2')
      // Remove more than a ZWNJs
      .replace(/\u200c{2,}/g, '\u200c')
      // Convert ¬ to zwnj in Persian context
      .replace(new RegExp('([' + persianCharacters + '])¬(?=[' + persianCharacters + '])', 'g'), '$1\u200c')
      // Clean ZWNJs after characters that don't conncet to the next letter
      .replace(/([۰-۹0-9إأةؤورزژاآدذ،؛,\:«»\\\/@#$٪×\*\(\)ـ\-=\|])\u200c/g, '$1')
      // Clean ZWNJs before English characters
      .replace(/\u200c([\w])/g, '$1')
      .replace(/([\w])\u200c/g, '$1')
      // Clean ZWNJs after and before punctuation
      .replace(/\u200c([\n\s\[\]\.،«»\:\(\)\؛\؟\?\;\$\!\@\-\=\+\\\|])/g, '$1')
      .replace(/([\n\s\[\.،«»\:\(\)\؛\؟\?\;\$\!\@\-\=\+\\\|])\u200c/g, '$1')
      //Clean ZWNJs before brakets which have sapce after\before them
      .replace(/\u200c(\]\][\s\n])/g, '$1')
      .replace(/([\n\s]\[\[)\u200c/g, '$1');
  }

  persianGlyphs = {
    // these two are for visually available ZWNJ #visualZwnj
    '\u200cه': 'ﻫ',
    'ی\u200c': 'ﻰﻲ',
    'ﺃ': 'ﺄﺃ',
    'ﺁ': 'ﺁﺂ',
    'ﺇ': 'ﺇﺈ',
    'ا': 'ﺎا',
    'ب': 'ﺏﺐﺑﺒ',
    'پ': 'ﭖﭗﭘﭙ',
    'ت': 'ﺕﺖﺗﺘ',
    'ث': 'ﺙﺚﺛﺜ',
    'ج': 'ﺝﺞﺟﺠ',
    'چ': 'ﭺﭻﭼﭽ',
    'ح': 'ﺡﺢﺣﺤ',
    'خ': 'ﺥﺦﺧﺨ',
    'د': 'ﺩﺪ',
    'ذ': 'ﺫﺬ',
    'ر': 'ﺭﺮ',
    'ز': 'ﺯﺰ',
    'ژ': 'ﮊﮋ',
    'س': 'ﺱﺲﺳﺴ',
    'ش': 'ﺵﺶﺷﺸ',
    'ص': 'ﺹﺺﺻﺼ',
    'ض': 'ﺽﺾﺿﻀ',
    'ط': 'ﻁﻂﻃﻄ',
    'ظ': 'ﻅﻆﻇﻈ',
    'ع': 'ﻉﻊﻋﻌ',
    'غ': 'ﻍﻎﻏﻐ',
    'ف': 'ﻑﻒﻓﻔ',
    'ق': 'ﻕﻖﻗﻘ',
    'ک': 'ﮎﮏﮐﮑﻙﻚﻛﻜ',
    'گ': 'ﮒﮓﮔﮕ',
    'ل': 'ﻝﻞﻟﻠ',
    'م': 'ﻡﻢﻣﻤ',
    'ن': 'ﻥﻦﻧﻨ',
    'ه': 'ﻩﻪﻫﻬ',
    'هٔ': 'ﮤﮥ',
    'و': 'ﻭﻮ',
    'ﺅ': 'ﺅﺆ',
    'ی': 'ﯼﯽﯾﯿﻯﻰﻱﻲﻳﻴ',
    'ئ': 'ﺉﺊﺋﺌ',
    'لا': 'ﻼ',
    'ﻹ': 'ﻺ',
    'ﻷ': 'ﻸ',
    'ﻵ': 'ﻶ'
  };

  function toStandardPersianCharacters(text) {
    var i;
    for (i in persianGlyphs) {
      if (persianGlyphs.hasOwnProperty(i)) {
        text = text.replace(new RegExp('[' + persianGlyphs[i] + ']', 'g'), i);
      }
    }
    return normalizeZwnj(text) // needed because of #visualZwnj
      .replace(/ك/g, 'ک') // Arabic
      .replace(/ي/g, 'ی')
      .replace(/ى/g, 'ی') // Urdu
      .replace(/ۍ/g, 'ی') // Pushtu
      .replace(/ې/g, 'ی') // Uyghur
      .replace(/ہ/g, 'ه') // Convert &#x06C1; to &#x0647; ہہہہ to ههه
      .replace(/ە/g, 'ه\u200c') // Kurdish
      .replace(/ھ/g, 'ه'); // Kurdish
  }

  function toPersianDigits(text) {
    var i = 0;
    for (i = 0; i <= 9; i = i + 1) {
      text = text.replace(new RegExp('[' + arabicIndicDigits[i] + arabicDigits[i] + ']', 'g'), persianDigits[i]);
    }
    return text
      .replace(new RegExp('([' + persianDigits + ']) ?%', 'g'), '$1٪')
      .replace(new RegExp('([' + persianDigits + '])\\.(?=[' + persianDigits + '])', 'g'), '$1٫'); // persian decimal separator
  }

  function applyOrthography(text) {
    return text
      // Replace ه followed by (space|ZWNJ|lrm) follow by ی with هٔ
      .replace(/ه[\u200c\u200e\s]+ی([\s\n])/g, 'هٔ$1')
      // Replace ه followed by (space|ZWNJ|lrm|nothing) follow by ء or with هٔ
      .replace(/ه[\u200c\u200e\s]*[ءٔ]([\s\n])/g, 'هٔ$1')
      // Replace هٓ or single-character ۀ with the standard هٔ
      .replace(/(ۀ|هٓ)/g, 'هٔ')
      // Replace ه followed by ئ or ی, and then by ی, with ه\u200cای, example: خانهئی becomes خانه\u200cای
      .replace(/ه\u200c[ئی]ی/g, 'ه\u200cای')
      // Function for removing incorrect ZWNJs
      .replace(/([\u200c\u200e])([\s\n])/g, '$2')
      .replace(/([\s\n])([\u200c\u200e])/g, '$1');
  }

  /**
   * Replaces Persian characters with Arabic's ones so an Arabic sorter can sort Persian lines
   */
  function dePersian(text) {
    return text
      .replace(/ی/g, 'ي')
      .replace(/ک/g, 'ك')
      .replace(/گ/g, 'كی')
      .replace(/ژ/g, 'زی')
      .replace(/چ/g, 'جی')
      .replace(/پ/g, 'بی');
  }

  function persianSortText(text) {
    return text.split('\n').sort(function (x, y) {
      var keyX = dePersian(x),
        keyY = dePersian(y);
      if (keyX < keyY) { return -1; }
      if (keyX > keyY) { return 1; }
      return 0;
    }).join('\n');
  }

  persianPastVerbs = '(' +
    'ارزید|افتاد|افراشت|افروخت|افزود|افسرد|افشاند|افکند|انباشت|انجامید|انداخت|اندوخت|اندود|اندیشید|انگاشت|انگیخت|انگیزاند|اوباشت|ایستاد'+
    '|آراست|آراماند|آرامید|آرمید|آزرد|آزمود|آسود|آشامید|آشفت|آشوبید|آغازید|آغشت|آفرید|آکند|آگند|آلود|آمد|آمرزید|آموخت|آموزاند'+
    '|آمیخت|آهیخت|آورد|آویخت|باخت|باراند|بارید|بافت|بالید|باوراند|بایست|بخشود|بخشید|برازید|برد|برید|بست|بسود|بسیجید|بلعید'+
    '|بود|بوسید|بویید|بیخت|پاشاند|پاشید|پالود|پایید|پخت|پذیراند|پذیرفت|پراکند|پراند|پرداخت|پرستید|پرسید|پرهیزید|پروراند|پرورد|پرید'+
    '|پژمرد|پژوهید|پسندید|پلاسید|پلکید|پناهید|پنداشت|پوسید|پوشاند|پوشید|پویید|پیچاند|پیچانید|پیچید|پیراست|پیمود|پیوست|تاباند|تابید|تاخت'+
    '|تاراند|تازاند|تازید|تافت|تپاند|تپید|تراشاند|تراشید|تراوید|ترساند|ترسید|ترشید|ترکاند|ترکید|تکاند|تکانید|تنید|توانست|جَست|جُست'+
    '|جست|جنباند|جنبید|جنگید|جهاند|جهید|جوشاند|جوشید|جوید|چاپید|چایید|چپاند|چپید|چراند|چربید|چرخاند|چرخید|چرید|چسباند|چسبید'+
    '|چشاند|چشید|چکاند|چکید|چلاند|چلانید|چمید|چید|خاراند|خارید|خاست|خایید|خراشاند|خراشید|خرامید|خروشید|خرید|خزید|خست|خشکاند'+
    '|خشکید|خفت|خلید|خمید|خنداند|خندانید|خندید|خواباند|خوابانید|خوابید|خواست|خواند|خوراند|خورد|خوفید|خیساند|خیسید|داد|داشت|دانست'+
    '|درخشانید|درخشید|دروید|درید|دزدید|دمید|دواند|دوخت|دوشید|دوید|دید|دیدم|راند|ربود|رخشید|رساند|رسانید|رست|رَست|رُست'+
    '|رسید|رشت|رفت|رُفت|رقصاند|رقصید|رمید|رنجاند|رنجید|رندید|رهاند|رهانید|رهید|روبید|روفت|رویاند|رویید|ریخت|رید|ریسید'+
    '|زاد|زارید|زایید|زد|زدود|زیست|سابید|ساخت|سپارد|سپرد|سپوخت|ستاند|ستد|سترد|ستود|ستیزید|سرایید|سرشت|سرود|سرید'+
    '|سزید|سفت|سگالید|سنجید|سوخت|سود|سوزاند|شاشید|شایست|شتافت|شد|شست|شکافت|شکست|شکفت|شکیفت|شگفت|شمارد|شمرد|شناخت'+
    '|شناساند|شنید|شوراند|شورید|طپید|طلبید|طوفید|غارتید|غرید|غلتاند|غلتانید|غلتید|غلطاند|غلطانید|غلطید|غنود|فرستاد|فرسود|فرمود|فروخت'+
    '|فریفت|فشاند|فشرد|فهماند|فهمید|قاپید|قبولاند|کاست|کاشت|کاوید|کرد|کشاند|کشانید|کشت|کشید|کفت|کفید|کند|کوبید|کوچید'+
    '|کوشید|کوفت|گَزید|گُزید|گایید|گداخت|گذارد|گذاشت|گذراند|گذشت|گرازید|گرایید|گرداند|گردانید|گردید|گرفت|گروید|گریاند|گریخت|گریست'+
    '|گزارد|گزید|گسارد|گستراند|گسترد|گسست|گسیخت|گشت|گشود|گفت|گمارد|گماشت|گنجاند|گنجانید|گنجید|گندید|گوارید|گوزید|لرزاند|لرزید'+
    '|لغزاند|لغزید|لمباند|لمدنی|لمید|لندید|لنگید|لهید|لولید|لیسید|ماسید|مالاند|مالید|ماند|مانست|مرد|مکشید|مکید|مولید|مویید'+
    '|نازید|نالید|نامید|نشاند|نشست|نکوهید|نگاشت|نگریست|نمایاند|نمود|نهاد|نهفت|نواخت|نوردید|نوشاند|نوشت|نوشید|نیوشید|هراسید|هشت'+
    '|ورزید|وزاند|وزید|یارست|یازید|یافت'+
    ')';

  persianPresentVerbs = '(' +
    'ارز|افت|افراز|افروز|افزا|افزای|افسر|افشان|افکن|انبار|انباز|انجام|انداز|اندای|اندوز|اندیش|انگار|انگیز|انگیزان'+
    '|اوبار|ایست|آرا|آرام|آرامان|آرای|آزار|آزما|آزمای|آسا|آسای|آشام|آشوب|آغار|آغاز|آفرین|آکن|آگن|آلا|آلای'+
    '|آمرز|آموز|آموزان|آمیز|آهنج|آور|آویز|آی|بار|باران|باز|باش|باف|بال|باوران|بای|باید|بخش|بخشا|بخشای'+
    '|بر|بَر|بُر|براز|بساو|بسیج|بلع|بند|بو|بوس|بوی|بیز|بین|پا|پاش|پاشان|پالا|پالای|پذیر|پذیران'+
    '|پر|پراکن|پران|پرداز|پرس|پرست|پرهیز|پرور|پروران|پز|پژمر|پژوه|پسند|پلاس|پلک|پناه|پندار|پوس|پوش|پوشان'+
    '|پوی|پیچ|پیچان|پیرا|پیرای|پیما|پیمای|پیوند|تاب|تابان|تاران|تاز|تازان|تپ|تپان|تراش|تراشان|تراو|ترس|ترسان'+
    '|ترش|ترک|ترکان|تکان|تن|توان|توپ|جنب|جنبان|جنگ|جه|جهان|جو|جوش|جوشان|جوی|چاپ|چای|چپ|چپان'+
    '|چر|چران|چرب|چرخ|چرخان|چسب|چسبان|چش|چشان|چک|چکان|چل|چلان|چم|چین|خار|خاران|خای|خر|خراش'+
    '|خراشان|خرام|خروش|خز|خست|خشک|خشکان|خل|خم|خند|خندان|خواب|خوابان|خوان|خواه|خور|خوران|خوف|خیز|خیس'+
    '|خیسان|دار|در|درخش|درخشان|درو|دزد|دم|ده|دو|دوان|دوز|دوش|ران|ربا|ربای|رخش|رس|رسان'+
    '|رشت|رقص|رقصان|رم|رنج|رنجان|رند|ره|رهان|رو|روب|روی|رویان|ریز|ریس|رین|زا|زار|زای|زدا'+
    '|زدای|زن|زی|ساب|ساز|سای|سپار|سپر|سپوز|ستا|ستان|ستر|ستیز|سر|سرا|سرای|سرشت|سز|سگال|سنب'+
    '|سنج|سوز|سوزان|شاش|شای|شتاب|شکاف|شکف|شکن|شکوف|شکیب|شمار|شمر|شناس|شناسان|شنو|شو|شور|شوران|شوی'+
    '|طپ|طلب|طوف|غارت|غر|غلت|غلتان|غلط|غلطان|غنو|فرسا|فرسای|فرست|فرما|فرمای|فروش|فریب|فشار|فشان|فشر'+
    '|فهم|فهمان|قاپ|قبولان|کار|کاه|کاو|کش|کَش|کُش|کِش|کشان|کف|کن|کوب|کوچ|کوش|گا|گای|گداز'+
    '|گذار|گذر|گذران|گرا|گراز|گرای|گرد|گردان|گرو|گری|گریان|گریز|گز|گزار|گزین|گسار|گستر|گستران|گسل|گشا'+
    '|گشای|گمار|گنج|گنجان|گند|گو|گوار|گوز|گوی|گیر|لرز|لرزان|لغز|لغزان|لم|لمبان|لند|لنگ|له|لول'+
    '|لیس|ماس|مال|مان|مک|مول|موی|میر|ناز|نال|نام|نشان|نشین|نکوه|نگار|نگر|نما|نمای|نمایان|نه'+
    '|نهنب|نواز|نورد|نوش|نوشان|نویس|نیوش|هراس|هست|هل|ورز|وز|وزان|یاب|یار|یاز'+
    ')';

  function applyZwnj(text) {
    return normalizeZwnj(text)
      .replace(
        new RegExp('(^|[^' + persianCharacters + '])(می|نمی) ?' + persianPastVerbs +
          '(م|ی|یم|ید|ند|)($|[^' + persianCharacters + '])', 'g'),
        '$1$2\u200c$3$4$5'
      )
      .replace(
        new RegExp('(^|[^' + persianCharacters + '])(می|نمی) ?' + persianPresentVerbs +
          '(م|ی|د|یم|ید|ند)($|[^' + persianCharacters + '])', 'g'),
        '$1$2\u200c$3$4$5'
      )
      // ای «توان» ناقلا!
      .replace(/(\s)(می|نمی) ?توان/g, '$1$2\u200cتوان')
      // چسباندن تمام «ها»ها با فاصلهٔ مجازی
      .replace(/\sها([\]\.،\:»\)\s])/g, '\u200cها$1')
      .replace(/\sها(ی|یی|یم|یت|یش|مان|تان|شان)([\]\.،\:»\)\s])/g, '\u200cها$1$2')
      // برای حذف علامت ستاره اضافی قبل از عنوان ها
      .replace(/\n\*\s*(\=+.+?\=+\n)/g, '\n$1')
      // عضو علامت های نقل قول تکی از عنوان ها
      .replace(/(\n=+)(.*?)(?:'+)(.*?)(?:'+)(.*?)(=+\n)/g, '$1$2$3$4$5')
      // اول و آخر هم خط اگر فاصلهٔ مجازی باشد، حذف شود
      .replace(/(^\u200c|\u200c$)/mg, '')
      // شناسه ها
      // توجه: «است» تعدماً از شناسه ها حذف شده چون به عنوان فعل مستقل هم کاربرد دارد و در آن موارد باید جدا نوشته شود
      // مثال: «این یک خانه است» که است در آن باید از خانه جدا نوشته شود
      .replace(new RegExp('ه\\s+(ام|ای|ایم|اید|اند)($|[^' + persianCharacters + '\u200c])', 'g'), 'ه\u200c$1$2')
      // موارد جزئی دیگر و بی ربط به فاصلهٔ مجازی، باید منتقل شود
      .replace(/ا\sً/g, 'اً');
  }

  function punctuation(text) {
    return text
      .replace(/([،\.])([^\s\.\(\)«»\"\[\]<>\d\w\{\}\|۰-۹])/g, '$1 $2')
      .replace(/ː/g, ':') //Replace incorrect : character
      .replace(/([\(«])\s/g, '$1')
      .replace(/\s([\)»])/g, '$1')
      .replace(/٬\s/g, '، ')
      .replace(/\n\s{1,}\n/g, '\n\n')
      .replace(/(\n\*.*?)\n+(?=\n\*)/g, '$1') // Removes extra line between two items list
      .replace(/(\n#.*?)\n+(?=\n#)/g, '$1') // Removes extra line between two items list
      .replace(/([^=]) *$/mg, '$1')
      .replace(/ +\( +/g, ' (')
      .replace(new RegExp('([' + persianCharacters + ']) *\\( *(?=[' + persianCharacters + '])(?!ها\\)|ان\\))', 'g'), '$1 (')
      .replace(new RegExp('([' + persianCharacters + ']) *\\) *(?=[' + persianCharacters + '])', 'g'), '$1) ')
      // Convert , to ، if there are Persian characters on both sides of it
      .replace(new RegExp('([' + persianCharacters + ']) ?, ?(?=[' + persianCharacters + '])', 'g'), '$1، ')
      .replace(/([^ \.]) +([؟،\:؛!\.])(\s[^ \.])/g, '$1$2$3') // Remove space preceding punctuations, except for ellipses
      // Add Space After Punctuation
      .replace(new RegExp('([' + persianCharacters + ']+)([؟،؛!\\.])([' + persianCharacters + ']+)', 'g'), '$1$2 $3');
  }

  (function () {
    var tools = {
      applyOrthography: applyOrthography,
      applyZwnj: applyZwnj,
      normalizeZwnj: normalizeZwnj,
      persianSortText: persianSortText,
      punctuation: punctuation,
      toPersianDigits: toPersianDigits,
      toStandardPersianCharacters: toStandardPersianCharacters
    }, i;

    function Chain(text) {
      this.text = text;
      this.toString = this.value = function () {
        return this.text;
      };
    }

    function persianTools(text) {
      return new Chain(text);
    }

    function applyTool(tool) {
      return function () {
        this.text = tool(this.text);
        return this;
      };
    }

    for (i in tools) {
      if (tools.hasOwnProperty(i)) {
        persianTools[i] = tools[i];
        Chain.prototype[i] = applyTool(tools[i]);
      }
    }

    if (typeof module === 'object') {
      module.exports = persianTools;
    } else {
      root.persianTools = persianTools;
    }
  }());
}(this));

// Wikipedia specific extension 
/*global persianTools, persianToolsDictionary, console*/
(function (root) {
  'use strict';

  function descendingFromComparetor(x, y) {
    return x.from - y.from;
  }

  function replaceExcept(text, callback, excepts) {
    var match, result = [], i, ranges, minRange, to, min, max;
    while (text !== '') {
      ranges = [];

      for (i in excepts) {
        if (excepts.hasOwnProperty(i)) {
          // a global regex should be reset before calls
          excepts[i].lastIndex = 0;
          match = excepts[i].exec(text);
          if (match !== null) {
            ranges.push({
              from: match.index,
              to: match.index + match[0].length
            });
          }
        }
      }

      // so nothing is matched
      if (ranges.length === 0) {
        result.push(callback(text));
        break;
      }

      minRange = ranges.sort(descendingFromComparetor)[0];
      min = minRange.from;

      to = [];
      for (i in ranges) {
        if (ranges.hasOwnProperty(i)) {
          if (ranges[i].from <= minRange.to) {
            to.push(ranges[i].to);
          }
        }
      }
      max = Math.max.apply(null, to);

      result.push(callback(text.substr(0, min)));
      result.push(text.substr(min, max - min));
      // console.log('Excepted: "' + text.substr(min, max - min) + '"');
      text = text.substr(max);
    }
    return result.join('');
  }

  var patterns = {
    arabicDigitsEnglishContext: /[a-z][a-z "'\._:\|,\-\\\/\(\)\#\^\+\d><–\[\]]*\d|\d[a-z "'\._:\|,\-\\\/\(\)\#\^\+\d><–\[\]]*[a-z]\d*/gi,
    arabicTagEnclosed: /\{\{(?:عربی|شروع عربی|آغاز عربی)\}\}([\s\S]*?)\{\{(?:پایان عربی)\}\}/g,
    argumentsBlacklist: /(?:accessdate|namespace|doi|style|شابک|عرض|bibcode|isbn|pmid|arxiv)\s*\=\s*[^\|\}]*/gi,
    color: /#(?:[abcdef0-9]{8}|[abcdef0-9]{6}|[abcdef0-9]{3})/gi,
    colorAsParameter: /\=\s*(?:[abcdef0-9]{8}|[abcdef0-9]{6}|[abcdef0-9]{3})(?:[\s\|\}]|$)/gi,
    decodeUriBlacklist: /(?:%20|%5C|%5E|%60|%23|%25|%3C|%3E|%5B|%5D|%22|%09|%0A|%7B|%7C|%7D)/gi, // space, ", \t, \n, {, |, }, ... they will interfere with wiki markup
    englishDate: /\d{1,2},? [a-z]{3,} \d{2,4}/gi, // 3, May 2013
    fileNames: /(?:پرونده|File|تصویر|Image)\:.*?(?=\||\]|\n|$)/gi, // don't capture | after
    fileParameter: /\|\s*(image|تصویر)\s*\=\s*.*/g,
    ipSign: /\[\[ویژه:مشارکت\u200cها.*?\]\]/g,
    isbn: /ISBN [\d\-]*/gi,
    htmlAttributes: /(?:style|colspan|color|rowspan|cellpadding|cellspacing|height|width|size|border|thumbtime|name|perrow)\s*\=\s*(?:['\"].*?['\"]|[\da-z]+)/gi,
    htmlEntity: /&#\d+;/,
    imagePixelSize: /[\|=] *[x\d]+?(px|پیکسل)[\]\|\s]/g, // means it will capture |10px| and |10x10px|
    linksOnEnglishContext: /[a-z][\:\,\. ]*\[\[[\da-z\-\, ]*/gi,
    mathTag: /<math.*?>[\s\S]*?<\/math>/g,
    otherLanguagesInline: /\{\{(?:به .+?|به انگلیسی|انگلیسی|عربی|به عربی)[\s\S]*?\}\}/g,
    parameter: /\{\{\{\d+/gi,
    parenthesesAfterDigits: /\w\s?\([\w\s\.\-]*?\)/g,
    ref: /(?:<ref[^\/]*?>[\s\S]*?<\/ref>|<ref[^\/]*?\/>)/g, // inside <ref></ref> and <ref/>
    signatures: /\[\[(?:کاربر|User|بحث[ _]کاربر|User[_ ]talk)\:.*?\]\]/gi,
    sourceTag: /(<source.*?>[\s\S]*?<\/source>|<syntaxhighlight.*?>[\s\S]*?<\/syntaxhighlight>|<code.*?>[\s\S]*?<\/code>)/g,
    tagNames: /<\/?[a-zA-Z\d]*/g,
    templateEnglishName: /(الگو|Template):[a-z][a-z\d\-\+_]+/gi,
    templateParameterName: /\|\s*(?=[a-z_]*\d)[a-z_\d]*\s*\=/gi,
    url: /\/\/.*?(?=[\s\n\|\}\]<]|$)/gi   // بدون https?: هم ممکن است
  },
    vowels = 'ًٌٍَُِّْ',
    persianCharactersNoVowels = 'ءآأؤإئابپةتثجحخچدذرزژسشصضطظعغفقكکگلمنهوىیي',
    persianCharacters = persianCharactersNoVowels + vowels;

  function wikiConvertToPersianCharacters(text) {
    return replaceExcept(
      text,
      persianTools.toStandardPersianCharacters,
      [patterns.otherLanguagesInline, patterns.arabicTagEnclosed, patterns.fileNames, patterns.signatures, patterns.url]
    );
  }

  if (!String.prototype.trim) { // if is not available currently
    String.prototype.trim = function () {
      return this.replace(/^\s+|\s+$/g, '');
    };
  }

  function wikiPunctuation(text) {
    return replaceExcept(
      text,
      function (text) {
        text = text.replace(
          new RegExp('([' + persianCharactersNoVowels + '])ـ+([' + persianCharactersNoVowels + '])', 'g'),
          '$1$2'
        );
        return persianTools.punctuation(text);
      },
      [patterns.fileNames, patterns.url] // Other patterns should be added here also
    )
      .replace(/\n{3,}/g, '\n\n') // Cosmetic changes bot replaces
      .replace(/\[\[(رده|الگو|ویکی\u200cپدیا)\: +/g, '[[$1:')
      .replace(/[\n\s]*\{\{[•·ن](w?)\}\}\s*/g, '{{•$1}} ')
      .replace(/ *(<\/? ?br ?\/?>|\{\{بر\}\}) */g, '{{سخ}}')
      .replace(/(<\/ref>)\s+(<ref)/g, '$1$2')
      .replace(/[\s\n]+<ref(?!erences)/g, '<ref')
      .replace(/(\n?)[\s\n]+?<\/ref>/g, '$1</ref>')
      .replace(/([^=])\n+(\=.*?\=\n+)/g, '$1\n\n$2')
      .replace(/^(\={2,}) +[\:,;<>&\^#@•→←↔↑↓—–…~٫،؛ٔ]/mg, '$1') // Cleanup headers
      .replace(/[\:,;<>&\^#@•→←↔↑↓—–…~٫،؛ٔ] +(\={2,})$/mg, '$1')
      .replace(/^(\={2,}) *«(.*?)» *(\={2,})/mg, '$1 $2 $3')
      .replace(/^(\={2,}) *'+(.*?)'+ *(\={2,})/mg, '$1 $2 $3')
      .replace(/^[●⚫⬤]/mg, '*') // Wikify bullets in start of lines
      .replace(/^([*#]+)([^*#\:\s])/mg, '$1 $2') // Adds a space after the # or * for lists
      .replace(/^([*#]+) {2,}([^*#\:\s])/mg, '$1 $2') // Trim more that one space after the # or * for lists
      .replace(/^#\s*(REDIRECT|تغییر[ _]?مسیر)/gi, '#تغییرمسیر')
      .replace(/^#تغییرمسیر(?=\S)/g, '#تغییرمسیر ') // Adds a space after #REDIRECT
      .replace(/(\={2,}) *([^\=\n\r]*?) *(\={2,})/g, '$1 $2 $3') // Format headings level 2 and above
      // برای حذف فاصله های اضافی در پیوندها
      .replace(/\[\[\s*(.*?)\s*\]\]/g, '[[$1]]')
      // تبدیل به نویسه / یکی کردن فاصله های مجازی پشت سرهم
      .replace(/(\{\{فم\}\}|\&zwnj\;|\u200c+)/g, '\u200c')
      // Full stop and comma should be before citation. See en:WP:REFPUNC
      .replace(/ *((?:<ref[^\/]*?>.*?<\/ref>)+)([\.،,])?/g, '$2$1')
      .replace(/ *((?:<ref[^\/]*?\/>)+)([\.،,])/g, '$2$1')
      .trim();
  }

  function wikiUrlMinifier(text) {
    return text
      .replace(patterns.url, function (x) {
        return replaceExcept(
          x,
          function (x) {
            try {
              x = decodeURI(x);
            } catch (e) { console.error(e); }
            return x;
          },
          [patterns.decodeUriBlacklist]
        );
      })
      // Shorten Wikipedia diff URLs
      .replace(/(\/\/[\w\-]+\.wikipedia\.org\/w\/index\.php\?)title=[^&\s\]]*&(?=(?:diff=[^&\s]+&|)(?:oldid=\d+))/g, '$1')
      // Strip the http(s) prefix
      .replace(/\[(https?\:)(?=\/\/(?:[\w\-]+)\.(wikipedia|wikimedia|wikidata|wiktionary|wikisource|wikinews|wikivoyage|wikiquote)\.org\/[^\s\]]*)/g, '[');
  }

  function wikiTextDigitsToPersian(text) {
    text = replaceExcept(
      text,
      persianTools.toPersianDigits,
      [patterns.argumentsBlacklist, patterns.mathTag, patterns.imagePixelSize, patterns.fileNames, patterns.ref,
        patterns.sourceTag, patterns.url, patterns.signatures, patterns.arabicDigitsEnglishContext, patterns.htmlEntity,
        patterns.htmlAttributes, patterns.fileParameter, patterns.templateParameterName, patterns.ipSign,
        patterns.parenthesesAfterDigits, patterns.otherLanguagesInline, patterns.isbn, patterns.englishDate,
        patterns.parameter, patterns.color, patterns.templateEnglishName, patterns.linksOnEnglishContext,
        patterns.colorAsParameter, patterns.tagNames]
    );
    return text
      // Decimal point, and thousands' separator
      .replace(/([۱۲۳۴۵۶۷۸۹۰])\.([۱۲۳۴۵۶۷۸۹۰])/g, '$1٫$2')
      .replace(/([۱۲۳۴۵۶۷۸۹۰]),([۱۲۳۴۵۶۷۸۹۰])/g, '$1٬$2');
  }

  function dictationReplace(x, y, extensions, text) {
    return text.replace(
      new RegExp(
        '(^|[^' + persianCharacters + '])(\\s|\u200c|_|)(' + x + ')(\\s|_)(' + y + ')(\\s|\u200c|_|)(' +
          extensions + ')($|[^' + persianCharacters + '])',
        'g'
      ),
      '$1$2$3\u200c$5$6$7$8'
    );
  }

  // it has dependency to MediaWiki:Gadget-Extra-Editbuttons-Dictionary.js
  function dictation(text) {
    var i, dictionary = persianToolsDictionary;
    for (i in dictionary.complexes) {
      if (dictionary.complexes.hasOwnProperty(i)) {
        text = dictationReplace(
          i,
          dictionary.complexes[i],
          'ی|ها|های|هایی|هایم|هایت|هایش|هایمان|هایتان|هایشان|',
          text
        );
      }
    }
    // for last name
    text = dictationReplace(
      dictionary.personNames,
      'ی|زاده|نیا|پور|گان|فر|نژاد|یان|ی\u200cها|یها',
      'ی|',
      text
    );
    // for 'آباد's
    text = dictationReplace(
      dictionary.personNames + '|' + dictionary.addToAbad,
      'آباد',
      'زاده|نیا|پور|گان|فر|نژاد|ی|یان|ها|های|هایی|ی\u200cها|یها|',
      text
    );
    // for first names
    for (i in dictionary.firstNameComplex) {
      if (dictionary.firstNameComplex.hasOwnProperty(i)) {
        text = text.replace(
          new RegExp(
            '(^|[^' + persianCharacters + ']|\\s|_)(' + i + ')(\\s|_)(' +
              dictionary.firstNameComplex[i] + ')(\\s|_)($|[^' + persianCharacters + ']|[^' +
              persianCharacters + '])',
            'g'
          ),
          '$1$2\u200c$4$5$6'
        );
      }
    }
    // for colors
    text = dictationReplace(
      dictionary.colorsNames,
      'فام|گون',
      'زاده|نیا|پور|گان|فر|نژاد|ی|ها|های|هایی|ی\u200cها|یها|هایم|هایت|هایش|هایمان|هایتان|هایشان|',
      text
    );
    // for numbers
    text = dictationReplace(
      dictionary.persianNumbers,
      'گانه|ماهه',
      'زاده|نیا|پور|گان|فر|نژاد|ی|ها|های|هایی|هایم|هایت|هایش|هایمان|هایتان|هایشان|',
      text
    );
    // wrong dictation
    for (i in dictionary.forReplace) {
      if (dictionary.forReplace.hasOwnProperty(i)) {
        text = text.replace(
          new RegExp(
            '(^|[^' + persianCharacters + '])(\\s|\u200c|_|)(' + i + ')(\\s|\u200c|_|)($|[^' +
              persianCharacters + '])',
            'g'
          ),
          '$1$2' + dictionary.forReplace[i] + '$4$5'
        );
      }
    }
    return persianTools.normalizeZwnj(text);
  }

  function wikiDictation(text) {
    return replaceExcept(
      text,
      dictation,
      [patterns.fileNames, patterns.signatures, patterns.url]
    );
  }

  root.persianWikiTools = {
    applyZwnj: persianTools.applyZwnj,
    convertDigitsToPersian: persianTools.toStandardPersianCharacters,
    cleanUselessZwnj: persianTools.normalizeZwnj,
    nonStandard: persianTools.applyOrthography,
    dictation: dictation,
    persianSortText: persianTools.persianSortText,
    wikiConvertToPersianCharacters: wikiConvertToPersianCharacters,
    wikiDictation: wikiDictation,
    wikiPunctuation: wikiPunctuation,
    wikiUrlMinifier: wikiUrlMinifier,
    wikiTextDigitsToPersian: wikiTextDigitsToPersian
  };
}(this));