Plugin Directory

Changeset 3109820


Ignore:
Timestamp:
06/29/2024 10:07:30 PM (21 months ago)
Author:
natata7
Message:

Update to version 1.2 from GitHub

Location:
mixtape
Files:
6 added
12 edited
1 copied

Legend:

Unmodified
Added
Removed
  • mixtape/tags/1.2/assets/js/mixtape-front.js

    r2998174 r3109820  
    44 */
    55(function (window) {
    6   {
    7     var unknown = "-";
    8 
    9     // screen
    10     var screenSize = "";
    11     if (screen.width) {
    12       width = screen.width ? screen.width : "";
    13       height = screen.height ? screen.height : "";
    14       screenSize += "" + width + " x " + height;
    15     }
    16 
    17     // browser
    18     var nVer = navigator.appVersion;
    19     var nAgt = navigator.userAgent;
    20     var browser = navigator.appName;
    21     var version = "" + parseFloat(navigator.appVersion);
    22     var majorVersion = parseInt(navigator.appVersion, 10);
    23     var nameOffset, verOffset, ix;
    24 
    25     // Opera
    26     if ((verOffset = nAgt.indexOf("Opera")) !== -1) {
    27       browser = "Opera";
    28       version = nAgt.substring(verOffset + 6);
    29       if ((verOffset = nAgt.indexOf("Version")) !== -1) {
    30         version = nAgt.substring(verOffset + 8);
    31       }
    32     }
    33     // Opera Next
    34     if ((verOffset = nAgt.indexOf("OPR")) !== -1) {
    35       browser = "Opera";
    36       version = nAgt.substring(verOffset + 4);
    37     }
    38     // USbrowser
    39     else if ((verOffset = nAgt.indexOf("UCBrowser")) !== -1) {
    40       browser = "UCBrowser";
    41       version = nAgt.substring(verOffset + 6);
    42       if ((verOffset = nAgt.indexOf("Version")) !== -1) {
    43         version = nAgt.substring(verOffset + 8);
    44       }
    45     }
    46     // MSIE
    47     else if ((verOffset = nAgt.indexOf("MSIE")) !== -1) {
    48       browser = "Microsoft Internet Explorer";
    49       version = nAgt.substring(verOffset + 5);
    50     }
    51     // MSE
    52     else if ((verOffset = nAgt.indexOf("Edge")) !== -1) {
    53       browser = "Edge";
    54       version = nAgt.substring(verOffset + 7);
    55     }
    56     // Chrome
    57     else if ((verOffset = nAgt.indexOf("Chrome")) !== -1) {
    58       browser = "Chrome";
    59       version = nAgt.substring(verOffset + 7);
    60     }
    61     // Safari
    62     else if ((verOffset = nAgt.indexOf("Safari")) !== -1) {
    63       browser = "Safari";
    64       version = nAgt.substring(verOffset + 7);
    65       if ((verOffset = nAgt.indexOf("Version")) !== -1) {
    66         version = nAgt.substring(verOffset + 8);
    67       }
    68     }
    69     // Firefox
    70     else if ((verOffset = nAgt.indexOf("Firefox")) !== -1) {
    71       browser = "Firefox";
    72       version = nAgt.substring(verOffset + 8);
    73     }
    74     // MSIE 11+
    75     else if (nAgt.indexOf("Trident/") !== -1) {
    76       browser = "Microsoft Internet Explorer";
    77       version = nAgt.substring(nAgt.indexOf("rv:") + 3);
    78     }
    79     // Other browsers
    80     else if (
    81       (nameOffset = nAgt.lastIndexOf(" ") + 1) <
    82       (verOffset = nAgt.lastIndexOf("/"))
    83     ) {
    84       browser = nAgt.substring(nameOffset, verOffset);
    85       version = nAgt.substring(verOffset + 1);
    86       if (browser.toLowerCase() === browser.toUpperCase()) {
    87         browser = navigator.appName;
    88       }
    89     }
    90     // trim the version string
    91     if ((ix = version.indexOf(";")) !== -1) version = version.substring(0, ix);
    92     if ((ix = version.indexOf(" ")) !== -1) version = version.substring(0, ix);
    93     if ((ix = version.indexOf(")")) !== -1) version = version.substring(0, ix);
    94 
    95     majorVersion = parseInt("" + version, 10);
    96     if (isNaN(majorVersion)) {
    97       version = "" + parseFloat(navigator.appVersion);
    98       majorVersion = parseInt(navigator.appVersion, 10);
    99     }
    100 
    101     // mobile version
    102     var mobile = /Mobile|mini|Fennec|Android|iP(ad|od|hone)/.test(nVer);
    103 
    104     // cookie
    105     var cookieEnabled = !!navigator.cookieEnabled;
    106 
    107     if (typeof navigator.cookieEnabled === "undefined" && !cookieEnabled) {
    108       document.cookie = "testcookie";
    109       cookieEnabled = document.cookie.indexOf("testcookie") !== -1;
    110     }
    111 
    112     // system
    113     var os = unknown;
    114     var clientStrings = [
    115       { s: "Windows 10", r: /(Windows 10.0|Windows NT 10.0)/ },
    116       { s: "Windows 8.1", r: /(Windows 8.1|Windows NT 6.3)/ },
    117       { s: "Windows 8", r: /(Windows 8|Windows NT 6.2)/ },
    118       { s: "Windows 7", r: /(Windows 7|Windows NT 6.1)/ },
    119       { s: "Windows Vista", r: /Windows NT 6.0/ },
    120       { s: "Windows Server 2003", r: /Windows NT 5.2/ },
    121       { s: "Windows XP", r: /(Windows NT 5.1|Windows XP)/ },
    122       { s: "Windows 2000", r: /(Windows NT 5.0|Windows 2000)/ },
    123       { s: "Windows ME", r: /(Win 9x 4.90|Windows ME)/ },
    124       { s: "Windows 98", r: /(Windows 98|Win98)/ },
    125       { s: "Windows 95", r: /(Windows 95|Win95|Windows_95)/ },
    126       { s: "Windows NT 4.0", r: /(Windows NT 4.0|WinNT4.0|WinNT|Windows NT)/ },
    127       { s: "Windows CE", r: /Windows CE/ },
    128       { s: "Windows 3.11", r: /Win16/ },
    129       { s: "Android", r: /Android/ },
    130       { s: "Open BSD", r: /OpenBSD/ },
    131       { s: "Sun OS", r: /SunOS/ },
    132       { s: "Linux", r: /(Linux|X11)/ },
    133       { s: "iOS", r: /(iPhone|iPad|iPod)/ },
    134       { s: "Mac OS X", r: /Mac OS X/ },
    135       { s: "Mac OS", r: /(MacPPC|MacIntel|Mac_PowerPC|Macintosh)/ },
    136       { s: "QNX", r: /QNX/ },
    137       { s: "UNIX", r: /UNIX/ },
    138       { s: "BeOS", r: /BeOS/ },
    139       { s: "OS/2", r: /OS\/2/ },
    140       {
    141         s: "Search Bot",
    142         r: /(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves\/Teoma|ia_archiver)/,
    143       },
    144     ];
    145     for (var id in clientStrings) {
    146       var cs = clientStrings[id];
    147       if (cs.r.test(nAgt)) {
    148         os = cs.s;
    149         break;
    150       }
    151     }
    152 
    153     if (/Windows/.test(os)) {
    154       os = "Windows";
    155     }
    156 
    157     var flashVersion = "no check";
    158     if (typeof swfobject !== "undefined") {
    159       var fv = swfobject.getFlashPlayerVersion();
    160       if (fv.major > 0) {
    161         flashVersion = fv.major + "." + fv.minor + " r" + fv.release;
    162       } else {
    163         flashVersion = unknown;
    164       }
     6  var unknown = "-";
     7
     8  var screenSize = screen.width ? `${screen.width} x ${screen.height}` : "";
     9
     10  var nVer = navigator.appVersion;
     11  var nAgt = navigator.userAgent;
     12  var browser = navigator.appName;
     13  var version = `${parseFloat(navigator.appVersion)}`;
     14  var majorVersion = parseInt(navigator.appVersion, 10);
     15
     16  var browsers = [
     17    { name: "Opera", key: "Opera", versionKey: "Version" },
     18    { name: "Opera", key: "OPR", versionKey: "Version" },
     19    { name: "UCBrowser", key: "UCBrowser", versionKey: "Version" },
     20    { name: "Microsoft Internet Explorer", key: "MSIE" },
     21    { name: "Edge", key: "Edge" },
     22    { name: "Chrome", key: "Chrome" },
     23    { name: "Safari", key: "Safari", versionKey: "Version" },
     24    { name: "Firefox", key: "Firefox" },
     25    { name: "Microsoft Internet Explorer", key: "Trident/", versionKey: "rv:" },
     26  ];
     27
     28  for (let b of browsers) {
     29    let verOffset = nAgt.indexOf(b.key);
     30    if (verOffset !== -1) {
     31      browser = b.name;
     32      version = nAgt.substring(verOffset + b.key.length + 1);
     33      if (b.versionKey) {
     34        let versionOffset = nAgt.indexOf(b.versionKey);
     35        if (versionOffset !== -1) {
     36          version = nAgt.substring(versionOffset + b.versionKey.length + 1);
     37        }
     38      }
     39      break;
    16540    }
    16641  }
     42
     43  let verEndings = [";", " ", ")"];
     44  for (let end of verEndings) {
     45    let ix = version.indexOf(end);
     46    if (ix !== -1) version = version.substring(0, ix);
     47  }
     48
     49  majorVersion = parseInt(version, 10);
     50  if (isNaN(majorVersion)) {
     51    version = `${parseFloat(navigator.appVersion)}`;
     52    majorVersion = parseInt(navigator.appVersion, 10);
     53  }
     54
     55  var mobile = /Mobile|mini|Fennec|Android|iP(ad|od|hone)/.test(nVer);
     56  var cookieEnabled =
     57    navigator.cookieEnabled ??
     58    ((document.cookie = "testcookie"),
     59    document.cookie.indexOf("testcookie") !== -1);
     60
     61  var os = unknown;
     62  var clientStrings = [
     63    { s: "Windows 10", r: /(Windows 10.0|Windows NT 10.0)/ },
     64    { s: "Windows 8.1", r: /(Windows 8.1|Windows NT 6.3)/ },
     65    { s: "Windows 8", r: /(Windows 8|Windows NT 6.2)/ },
     66    { s: "Windows 7", r: /(Windows 7|Windows NT 6.1)/ },
     67    { s: "Windows Vista", r: /Windows NT 6.0/ },
     68    { s: "Windows Server 2003", r: /Windows NT 5.2/ },
     69    { s: "Windows XP", r: /(Windows NT 5.1|Windows XP)/ },
     70    { s: "Windows 2000", r: /(Windows NT 5.0|Windows 2000)/ },
     71    { s: "Windows ME", r: /(Win 9x 4.90|Windows ME)/ },
     72    { s: "Windows 98", r: /(Windows 98|Win98)/ },
     73    { s: "Windows 95", r: /(Windows 95|Win95|Windows_95)/ },
     74    { s: "Windows NT 4.0", r: /(Windows NT 4.0|WinNT4.0|WinNT|Windows NT)/ },
     75    { s: "Windows CE", r: /Windows CE/ },
     76    { s: "Windows 3.11", r: /Win16/ },
     77    { s: "Android", r: /Android/ },
     78    { s: "Open BSD", r: /OpenBSD/ },
     79    { s: "Sun OS", r: /SunOS/ },
     80    { s: "Linux", r: /(Linux|X11)/ },
     81    { s: "iOS", r: /(iPhone|iPad|iPod)/ },
     82    { s: "Mac OS X", r: /Mac OS X/ },
     83    { s: "Mac OS", r: /(MacPPC|MacIntel|Mac_PowerPC|Macintosh)/ },
     84    { s: "QNX", r: /QNX/ },
     85    { s: "UNIX", r: /UNIX/ },
     86    { s: "BeOS", r: /BeOS/ },
     87    { s: "OS/2", r: /OS\/2/ },
     88    {
     89      s: "Search Bot",
     90      r: /(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves\/Teoma|ia_archiver)/,
     91    },
     92  ];
     93
     94  for (let cs of clientStrings) {
     95    if (cs.r.test(nAgt)) {
     96      os = cs.s;
     97      break;
     98    }
     99  }
     100
     101  if (/Windows/.test(os)) os = "Windows";
    167102
    168103  window.jscd = {
     
    174109    os: os,
    175110    cookies: cookieEnabled,
    176     flashVersion: flashVersion,
    177111  };
    178112})(window);
    179113
    180 /**
    181  * dialogFx.js v1.0.0
    182  * http://www.codrops.com
    183  *
    184  * Licensed under the MIT license.
    185  * http://www.opensource.org/licenses/mit-license.php
    186  *
    187  * Copyright 2014, Codrops
    188  * http://www.codrops.com
    189  */
    190 
    191114(function (window) {
    192115  "use strict";
    193116
    194   var support = { animations: Modernizr.cssanimations },
    195     animEndEventNames = {
    196       WebkitAnimation: "webkitAnimationEnd",
    197       OAnimation: "oAnimationEnd",
    198       msAnimation: "MSAnimationEnd",
    199       animation: "animationend",
    200     },
    201     animEndEventName = animEndEventNames[Modernizr.prefixed("animation")],
    202     onEndAnimation = function (el, callback) {
    203       var onEndCallbackFn = function (ev) {
    204         if (support.animations) {
    205           if (ev.target !== this) return;
    206           this.removeEventListener(animEndEventName, onEndCallbackFn);
     117  function checkAnimationSupport() {
     118    var animation = false,
     119      animationstring = "animation",
     120      keyframeprefix = "",
     121      domPrefixes = "Webkit Moz O ms Khtml".split(" "),
     122      pfx = "",
     123      elm = document.createElement("div");
     124
     125    if (elm.style.animationName !== undefined) {
     126      animation = true;
     127    }
     128
     129    if (animation === false) {
     130      for (var i = 0; i < domPrefixes.length; i++) {
     131        if (elm.style[domPrefixes[i] + "AnimationName"] !== undefined) {
     132          pfx = domPrefixes[i];
     133          animationstring = pfx + "Animation";
     134          keyframeprefix = "-" + pfx.toLowerCase() + "-";
     135          animation = true;
     136          break;
    207137        }
    208         if (callback && typeof callback === "function") {
    209           callback.call();
    210         }
    211       };
    212       if (support.animations) {
    213         el.addEventListener(animEndEventName, onEndCallbackFn);
    214       } else {
    215         onEndCallbackFn();
    216       }
     138      }
     139    }
     140
     141    return {
     142      supported: animation,
     143      pfx: pfx,
     144      animationstring: animationstring,
     145      keyframeprefix: keyframeprefix,
     146      eventName: animation
     147        ? pfx
     148          ? pfx + "AnimationEnd"
     149          : "animationend"
     150        : null,
    217151    };
     152  }
     153
     154  var animationSupport = checkAnimationSupport();
     155
     156  // Визначення події завершення анімації
     157  var animEndEventNames = {
     158    WebkitAnimation: "webkitAnimationEnd",
     159    OAnimation: "oAnimationEnd",
     160    msAnimation: "MSAnimationEnd",
     161    animation: "animationend",
     162  };
     163  var animEndEventName = animationSupport.eventName;
     164
     165  var onEndAnimation = function (el, callback) {
     166    var onEndCallbackFn = function (ev) {
     167      if (animationSupport.supported) {
     168        if (ev.target !== this) return;
     169        this.removeEventListener(animEndEventName, onEndCallbackFn);
     170      }
     171      if (callback && typeof callback === "function") {
     172        callback.call();
     173      }
     174    };
     175    if (animationSupport.supported) {
     176      el.addEventListener(animEndEventName, onEndCallbackFn);
     177    } else {
     178      onEndCallbackFn();
     179    }
     180  };
    218181
    219182  function extend(a, b) {
     
    300263 * mixtape
    301264 */
    302 (function ($) {
    303   // return if no args passed from backend
    304   if (!window.mixtape) {
    305     return;
    306   }
    307 
    308   var reportButton;
    309 
    310   window.mixtape = $.extend(window.mixtape, {
    311     onReady: function () {
    312       mixtape.initDialogFx();
    313 
    314       var $dialog = $(mixtape.dlg.el);
     265
     266jQuery(function ($) {
     267  const Mixtape = {
     268    reportButton: null,
     269
     270    onReady() {
     271      this.initDialogFx();
     272
     273      var $dialog = $(this.dlg.el);
    315274
    316275      $(document).on("click", ".mixtape_action", function () {
    317276        if ($(this).is("[data-action=send]")) {
    318277          var data;
    319 
    320278          if (!$dialog.data("dry-run") && (data = $dialog.data("report"))) {
    321279            if ($dialog.data("mode") === "comment") {
     
    325283            data.post_id = $(this).data("id");
    326284            data.nonce = $dialog.data("nonce");
    327             mixtape.reportSpellError(data);
     285            Mixtape.reportSpellError(data);
    328286          }
    329           mixtape.animateLetter();
    330           mixtape.hideReportButton();
     287          Mixtape.animateLetter();
     288          Mixtape.hideReportButton();
    331289        }
    332290      });
    333291
    334292      $(document).on("click", "#mixtape-close-btn", function () {
    335         mixtape.dlg.toggle();
     293        Mixtape.dlg.toggle();
    336294      });
    337295
    338296      $(document).keyup(function (ev) {
    339297        if (
     298          ev.ctrlKey &&
    340299          ev.keyCode === 13 &&
    341           ev.ctrlKey &&
    342300          ev.target.nodeName.toLowerCase() !== "textarea" &&
    343301          $("#mixtape_dialog.dialog--open").length === 0
    344302        ) {
    345           var report = mixtape.getSelectionData();
    346           if (report) {
    347             mixtape.showDialog(report);
     303          var report = Mixtape.getSelectionData();
     304          if (report) Mixtape.showDialog(report);
     305        }
     306      });
     307
     308      document.addEventListener("selectionchange", function () {
     309        if ($("#mixtape_dialog.dialog--open").length == 0) {
     310          var selection = window.getSelection().toString().trim();
     311          if (selection !== "") {
     312            Mixtape.showReportButton();
     313          } else {
     314            Mixtape.hideReportButton();
    348315          }
    349316        }
    350317      });
    351 
    352       document.addEventListener("selectionchange", function () {
    353         if ($("#mixtape_dialog.dialog--open").length === 0) {
    354           var selection = window.getSelection().toString().trim();
    355           if (selection !== "" && window.innerWidth < 1024) {
    356             mixtape.showReportButton();
    357           } else {
    358             mixtape.hideReportButton();
    359           }
    360         }
    361       });
    362     },
    363 
    364     initDialogFx: function () {
    365       mixtape.dlg = new DialogFx(document.getElementById("mixtape_dialog"), {
    366         onOpenDialog: function (dialog) {
    367           $(dialog.el).css("display", "flex");
    368         },
    369         onCloseAnimationEnd: function (dialog) {
    370           $(dialog.el).css("display", "none");
    371           mixtape.resetDialog();
    372         },
    373       });
    374     },
    375 
    376     animateLetter: function () {
    377       var dialog = $(mixtape.dlg.el),
    378         content = dialog.find(".dialog__content"),
    379         letterTop = dialog.find(".mixtape-letter-top"),
    380         letterFront = dialog.find(".mixtape-letter-front"),
    381         letterBack = dialog.find(".mixtape-letter-back"),
    382         dialogWrap = dialog.find(".dialog-wrap");
    383 
    384       content.addClass("show-letter");
    385 
    386       setTimeout(function () {
    387         var y =
    388           letterTop.offset().top -
    389           letterFront.offset().top +
    390           letterTop.outerHeight();
    391         letterTop.css({
    392           bottom: Math.floor(y),
    393           opacity: 1,
    394         });
    395         jQuery(".mixtape-letter-back-top").hide();
    396         if (content.hasClass("with-comment")) {
    397           dialogWrap.css("transform", "scaleY(0.5) scaleX(0.28)");
    398         } else {
    399           dialogWrap.css("transform", "scaleY(0.5) scaleX(0.4)");
    400         }
    401         setTimeout(function () {
    402           if (content.hasClass("with-comment")) {
    403             dialogWrap.css(
    404               "transform",
    405               "translateY(12%) scaleY(0.5) scaleX(0.4)"
    406             );
    407           } else {
    408             dialogWrap.css(
    409               "transform",
    410               "translateY(28%) scaleY(0.5) scaleX(0.45)"
    411             );
    412           }
    413           setTimeout(function () {
    414             letterTop.css("z-index", "9");
    415             letterTop.addClass("close");
    416             setTimeout(function () {
    417               dialogWrap.css({
    418                 visibility: "hidden",
    419                 opacity: "0",
    420               });
    421               letterFront.css("animation", "send-letter1 0.7s");
    422               letterBack.css("animation", "send-letter1 0.7s");
    423               letterTop.css("animation", "send-letter2 0.7s");
    424               setTimeout(function () {
    425                 mixtape.dlg.toggle();
    426               }, 400);
    427             }, 400);
    428           }, 400);
    429         }, 300);
    430       }, 400);
    431     },
    432 
    433     showDialog: function (report) {
    434       if (
    435         report.hasOwnProperty("selection") &&
    436         report.hasOwnProperty("context")
    437       ) {
    438         var $dialog = $(mixtape.dlg.el);
    439 
    440         if ($dialog.data("mode") === "notify") {
    441           mixtape.reportSpellError(report);
    442           mixtape.dlg.toggle();
    443         } else {
    444           $dialog.data("report", report);
    445           $dialog.find("#mixtape_reported_text").html(report.preview_text);
    446           mixtape.dlg.toggle();
    447         }
    448       }
    449     },
    450 
    451     resetDialog: function () {
    452       var $dialog = $(mixtape.dlg.el);
    453 
    454       if ($dialog.data("mode") != "notify") {
     318    },
     319
     320    getSelectionData() {
     321      if (!window.getSelection) return false;
     322
     323      const sel = window.getSelection();
     324      if (sel.isCollapsed) return;
     325
     326      const selChars = sel.toString();
     327      const maxContextLength = 140;
     328
     329      //if (selChars.length > maxContextLength) return;
     330
     331      const parentEl = this._getParentElement(sel);
     332      if (!parentEl) return;
     333
     334      const { context, selWithContext, initialSel, backwards, direction } =
     335        this._prepareSelection(sel, parentEl);
     336      if (!selWithContext) return;
     337
     338      const { selToFindInContext, truncatedContext, previewText } =
     339        this._getContextAndPreviewText(
     340          selWithContext,
     341          context,
     342          maxContextLength,
     343          selChars
     344        );
     345
     346      return {
     347        selection: selChars,
     348        word: selWithContext,
     349        replace_context: selToFindInContext,
     350        context: truncatedContext,
     351        preview_text: previewText,
     352        nonce: $('input[name="mixtape_nonce"]').val(),
     353      };
     354    },
     355
     356    reportSpellError(data) {
     357      data.action = "mixtape_report_error";
     358      $.ajax({
     359        type: "post",
     360        dataType: "json",
     361        url: MixtapeLocalize.ajaxurl,
     362        data: data,
     363      });
     364    },
     365
     366    resetDialog() {
     367      var $dialog = $(this.dlg.el);
     368
     369      if ($dialog.data("mode") !== "notify") {
    455370        $dialog.find("#mixtape_confirm_dialog").css("display", "");
    456371        $dialog.find("#mixtape_success_dialog").remove();
    457372      }
    458373
    459       // letter
    460374      $dialog.find(".dialog__content").removeClass("show-letter");
    461375      $dialog
     
    467381    },
    468382
    469     reportSpellError: function (data) {
    470       data.action = "mixtape_report_error";
    471       $.ajax({
    472         type: "post",
    473         dataType: "json",
    474         url: mixtape.ajaxurl,
    475         data: data,
    476       });
    477     },
    478 
    479     getSelectionData: function () {
    480       // Check for existence of window.getSelection()
    481       if (!window.getSelection) {
    482         return false;
    483       }
    484 
    485       var parentEl,
    486         sel,
     383    showDialog(report) {
     384      if (report.selection && report.context) {
     385        var $dialog = $(this.dlg.el);
     386
     387        if ($dialog.data("mode") === "notify") {
     388          this.reportSpellError(report);
     389          this.dlg.toggle();
     390        } else {
     391          $dialog.data("report", report);
     392          $dialog.find("#mixtape_reported_text").html(report.preview_text);
     393          this.dlg.toggle();
     394        }
     395      }
     396    },
     397
     398    animateLetter() {
     399      var dialog = $(this.dlg.el),
     400        content = dialog.find(".dialog__content"),
     401        letterTop = dialog.find(".mixtape-letter-top"),
     402        letterFront = dialog.find(".mixtape-letter-front"),
     403        letterBack = dialog.find(".mixtape-letter-back"),
     404        dialogWrap = dialog.find(".dialog-wrap");
     405
     406      content.addClass("show-letter");
     407
     408      setTimeout(function () {
     409        var y =
     410          letterTop.offset().top -
     411          letterFront.offset().top +
     412          letterTop.outerHeight();
     413        letterTop.css({ bottom: Math.floor(y), opacity: 1 });
     414        jQuery(".mixtape-letter-back-top").hide();
     415        var scaleX = content.hasClass("with-comment") ? 0.28 : 0.4;
     416        dialogWrap.css("transform", `scaleY(0.5) scaleX(${scaleX})`);
     417        setTimeout(function () {
     418          var translateY = content.hasClass("with-comment") ? "12%" : "28%";
     419          dialogWrap.css(
     420            "transform",
     421            `translateY(${translateY}) scaleY(0.5) scaleX(${scaleX + 0.05})`
     422          );
     423          setTimeout(function () {
     424            letterTop.css("z-index", "9").addClass("close");
     425            setTimeout(function () {
     426              dialogWrap.css({ visibility: "hidden", opacity: "0" });
     427              letterFront.add(letterBack).css("animation", "send-letter1 0.7s");
     428              letterTop.css("animation", "send-letter2 0.7s");
     429              setTimeout(function () {
     430                Mixtape.dlg.toggle();
     431              }, 400);
     432            }, 400);
     433          }, 400);
     434        }, 300);
     435      }, 400);
     436    },
     437
     438    initDialogFx() {
     439      this.dlg = new DialogFx(document.getElementById("mixtape_dialog"), {
     440        onOpenDialog: function (dialog) {
     441          $(dialog.el).css("display", "flex");
     442        },
     443        onCloseAnimationEnd: function (dialog) {
     444          $(dialog.el).css("display", "none");
     445          Mixtape.resetDialog();
     446        },
     447      });
     448    },
     449
     450    _getParentElement(sel) {
     451      if (sel.rangeCount) {
     452        let parentEl = sel.getRangeAt(0).commonAncestorContainer;
     453        while (parentEl && parentEl.nodeType !== 1) {
     454          parentEl = parentEl.parentNode;
     455        }
     456        return parentEl;
     457      }
     458      return null;
     459    },
     460
     461    _prepareSelection(sel, parentEl) {
     462      const selChars = sel.toString().trim();
     463      console.log('Initial Selection Chars:', selChars); // Логування початкового виділення
     464   
     465      if (!selChars) {
     466        return {
     467          context: '',
     468          selWithContext: '',
     469          initialSel: {},
     470          backwards: false,
     471          direction: {}
     472        };
     473      }
     474   
     475      const direction = this._determineDirection(sel);
     476      console.log('Direction:', direction); // Логування напрямку
     477   
     478      const initialSel = this._saveInitialSelection(sel);
     479      console.log('Initial Selection:', initialSel); // Логування початкового стану виділення
     480   
     481      const context = this._getContext(parentEl);
     482      console.log('Context:', context); // Логування контексту
     483   
     484      this._extendSelection(sel, direction, context, selChars);
     485   
     486      const selWithContext = sel.toString().trim();
     487      console.log('Selection with Context:', selWithContext); // Логування виділення з контекстом
     488   
     489      return {
     490        context,
     491        selWithContext,
     492        initialSel,
     493        backwards: direction.backwards,
     494        direction,
     495      };
     496    },
     497
     498    _determineDirection(sel) {
     499      const range = document.createRange();
     500      range.setStart(sel.anchorNode, sel.anchorOffset);
     501      range.setEnd(sel.focusNode, sel.focusOffset);
     502      const backwards = range.collapsed;
     503      range.detach();
     504      return {
     505        forward: backwards ? "backward" : "forward",
     506        backward: backwards ? "forward" : "backward",
     507        backwards,
     508      };
     509    },
     510
     511    _saveInitialSelection(sel) {
     512      return {
     513        focusNode: sel.focusNode,
     514        focusOffset: sel.focusOffset,
     515        anchorNode: sel.anchorNode,
     516        anchorOffset: sel.anchorOffset,
     517      };
     518    },
     519
     520    _getContext(element) {
     521      const context = element ? element.textContent.trim() : '';
     522      console.log('Element Context:', context); // Логування контексту елемента
     523      return this._stringifyContent(context);
     524    },
     525
     526    _extendSelection(sel, direction, context, selChars) {
     527      console.log('Before Extend - Selection:', sel.toString(), 'Direction:', direction); // Логування перед розширенням
     528   
     529      // Зберігаємо початкове положення виділення
     530      const initialRange = sel.getRangeAt(0).cloneRange();
     531   
     532      // Розширюємо виділення вперед
     533      sel.modify("extend", direction.forward, "character");
     534   
     535      if (!/\w/.test(selChars.charAt(0))) {
     536        sel.modify("extend", direction.forward, "character");
     537      }
     538   
     539      sel.modify("extend", direction.backward, "word");
     540   
     541      if (!/\w/.test(selChars.charAt(selChars.length - 1))) {
     542        sel.modify("extend", direction.backward, "character");
     543      }
     544   
     545      const extendedSelection = sel.toString();
     546      console.log('After Extend - Selection:', extendedSelection); // Логування після розширення
     547   
     548      // Якщо розширення виділення не дало результату, відновлюємо початкове виділення
     549      if (extendedSelection.trim() === '') {
     550        sel.removeAllRanges();
     551        sel.addRange(initialRange);
     552      }
     553    },
     554
     555    _getContextAndPreviewText(
     556      selWithContext,
     557      context,
     558      maxContextLength,
     559      selChars
     560    ) {
     561      const selPos = this._getExactSelPos(selWithContext, context);
     562      let truncatedContext = context;
     563
     564      if (context.length > maxContextLength) {
     565        truncatedContext = this._truncateContext(
     566          context,
     567          selPos,
     568          selWithContext.length,
     569          maxContextLength
     570        );
     571      }
     572
     573      const selWithContextHighlighted = selWithContext.replace(
    487574        selChars,
    488         selWord,
    489         textToHighlight,
    490         maxContextLength = 140;
    491 
    492       var stringifyContent = function (string) {
    493         return typeof string === "string"
    494           ? string
    495               .replace(/\s*(?:(?:\r\n)+|\r+|\n+)\t*/gm, "\r\n")
    496               .replace(/\s{2,}/gm, " ")
    497           : "";
     575        `<span class="mixtape_mistake_inner">${selChars}</span>`
     576      );
     577
     578      const previewText = truncatedContext.replace(
     579        selWithContext,
     580        selWithContextHighlighted
     581      );
     582      return {
     583        selToFindInContext: selWithContext,
     584        truncatedContext,
     585        previewText,
    498586      };
    499 
    500       var isSubstrUnique = function (substr, context) {
    501         if (typeof context === "undefined") {
    502           context = mixtape.contextBuffer;
    503         }
    504         if (typeof substr === "undefined") {
    505           substr = mixtape.selBuffer;
    506         }
    507         var split = context.split(substr);
    508         var count = split.length - 1;
    509         return count === 1;
    510       };
    511 
    512       var getExactSelPos = function (selection, context) {
    513         // if there is only one match, that's it
    514         if (isSubstrUnique(selWithContext, context)) {
    515           return context.indexOf(selWithContext);
    516         }
    517         // check if we can get the occurrence match from selection offsets
    518         if (!backwards) {
    519           // check anchor element
    520           if (
    521             context.substring(
    522               sel.anchorOffset,
    523               sel.anchorOffset + selection.length
    524             ) == selection
    525           ) {
    526             return sel.anchorOffset;
    527           }
    528           // check anchor parent element
    529           var parentElOffset = sel.anchorOffset;
    530           var prevEl = sel.anchorNode.previousSibling;
    531           while (prevEl !== null) {
    532             parentElOffset += prevEl.textContent.length;
    533             prevEl = prevEl.previousSibling;
    534           }
    535           if (
    536             context.substring(
    537               parentElOffset,
    538               parentElOffset + selection.length
    539             ) == selection
    540           ) {
    541             return parentElOffset;
    542           }
    543         }
    544         if (
    545           backwards &&
    546           context.substring(
    547             sel.focusOffset,
    548             sel.focusOffset + selection.length
    549           ) == selection
    550         ) {
    551           return sel.anchorOffset;
    552         }
    553         return -1;
    554       };
    555 
    556       var getExtendedSelection = function (limit, nodeExtensions) {
    557         limit = parseInt(limit) || 40;
    558         nodeExtensions = nodeExtensions || { left: "", right: "" };
    559         var i = 0,
    560           selContent,
    561           selEndNode = sel.focusNode,
    562           selEndOffset = sel.focusOffset;
    563 
    564         while (i <= limit) {
    565           if (
    566             (selContent = stringifyContent(sel.toString().trim())).length >=
    567               maxContextLength ||
    568             isSubstrUnique(selContent, context)
    569           ) {
    570             return selContent;
    571           }
    572 
    573           // only even iteration
    574           if (
    575             (i % 2 == 0 && sel.anchorOffset > 0) ||
    576             (nodeExtensions.left.length && i < limit / 2)
    577           ) {
    578             // reset
    579             if (backwards) {
    580               sel.collapseToEnd();
    581             } else {
    582               sel.collapseToStart();
    583             }
    584             sel.modify("move", direction[1], "character");
    585             sel.extend(selEndNode, selEndOffset);
    586           } else if (
    587             sel.focusOffset < sel.focusNode.length ||
    588             (nodeExtensions.right.length && i < limit / 2)
    589           ) {
    590             sel.modify("extend", direction[0], "character");
    591             if (sel.focusOffset === 1) {
    592               selEndNode = sel.focusNode;
    593               selEndOffset = sel.focusOffset;
    594             }
    595           } else if (i % 2 === 0) {
    596             break;
    597           }
    598 
    599           i++;
    600         }
    601 
    602         return stringifyContent(sel.toString().trim());
    603       };
    604 
    605       var getExtendedContext = function (context, element, method) {
    606         var contentPrepend = "",
    607           contentAppend = "",
    608           e = element,
    609           i;
    610         method = method || "textContent";
    611 
    612         for (i = 0; i < 20; i++) {
    613           if (contentPrepend || (e = e.previousSibling) === null) {
    614             break;
    615           }
    616 
    617           if ((contentPrepend = stringifyContent(e[method].trim())).length) {
    618             context = contentPrepend + context;
    619           }
    620         }
    621 
    622         // reset element
    623         e = element;
    624 
    625         for (i = 0; i < 20; i++) {
    626           if (contentAppend || (e = e.nextSibling) === null) {
    627             break;
    628           }
    629           if ((contentAppend = stringifyContent(e[method]).trim()).length) {
    630             context += contentAppend;
    631           } else if (context.slice(-1) !== " ") {
    632             context += " ";
    633           }
    634         }
    635 
    636         return {
    637           contents: context,
    638           extensions: {
    639             left: contentPrepend,
    640             right: contentAppend,
    641           },
    642         };
    643       };
    644 
    645       // check that getSelection() has a modify() method. IE has both selection APIs but no modify() method.
    646       // this works on modern browsers following standards
    647       if ((sel = window.getSelection()).modify) {
    648         // check if there is any text selected
    649         if (!sel.isCollapsed) {
    650           /**
    651            * So the first step is to get selection extended to the boundaries of words
    652            *
    653            * e.g. if the sentence is "What a wonderful life!" and selection is "rful li",
    654            * we get "wonderful life" stored in selWord variable
    655            */
    656 
    657           selChars = sel.toString();
    658 
    659           // return early if no selection to work with or if its length exceeds the limit
    660           if (!selChars || selChars.length > maxContextLength) {
    661             return;
    662           }
    663 
    664           // here we get the nearest parent node which is common for the whole selection
    665           if (sel.rangeCount) {
    666             parentEl = sel.getRangeAt(0).commonAncestorContainer.parentNode;
    667             while (parentEl.textContent == sel.toString()) {
    668               parentEl = parentEl.parentNode;
    669             }
    670           }
    671 
    672           // Detect if selection was made backwards
    673           // further logic depends on it
    674           var range = document.createRange();
    675           range.setStart(sel.anchorNode, sel.anchorOffset);
    676           range.setEnd(sel.focusNode, sel.focusOffset);
    677           var backwards = range.collapsed;
    678           range = null;
    679 
    680           // save initial selection to restore in the end
    681           var initialSel = {
    682             focusNode: sel.focusNode,
    683             focusOffset: sel.focusOffset,
    684             anchorNode: sel.anchorNode,
    685             anchorOffset: sel.anchorOffset,
    686           };
    687 
    688           // modify() works on the focus of the selection (not virtually) so we manipulate it
    689           var endNode = sel.focusNode,
    690             endOffset = sel.focusOffset;
    691 
    692           // determine second char of selection and the one before last
    693           // they will be our starting point for word boundaries detection
    694           var direction, secondChar, oneBeforeLastChar;
    695           if (backwards) {
    696             direction = ["backward", "forward"];
    697             secondChar = selChars.charAt(selChars.length - 1);
    698             oneBeforeLastChar = selChars.charAt(0);
    699           } else {
    700             direction = ["forward", "backward"];
    701             secondChar = selChars.charAt(0);
    702             oneBeforeLastChar = selChars.charAt(selChars.length - 1);
    703           }
    704 
    705           // collapse the cursor to the first char
    706           sel.collapse(sel.anchorNode, sel.anchorOffset);
    707           // move it one char forward
    708           sel.modify("move", direction[0], "character");
    709 
    710           // if the second character was a letter or digit, move cursor another step further
    711           // this way we are certain that we are in the middle of the word
    712           if (null === secondChar.match(/'[\w\d]'/)) {
    713             sel.modify("move", direction[0], "character");
    714           }
    715 
    716           // and now we can determine the beginning position of the word
    717           sel.modify("move", direction[1], "word");
    718 
    719           // then extend the selection up to the initial point
    720           // thus assure that selection starts with the beginning of the word
    721           sel.extend(endNode, endOffset);
    722 
    723           // do the same trick with the ending--extending it precisely up to the end of the word
    724           sel.modify("extend", direction[1], "character");
    725           if (null === oneBeforeLastChar.match(/'[\w\d]'/)) {
    726             sel.modify("extend", direction[1], "character");
    727           }
    728           sel.modify("extend", direction[0], "word");
    729           if (!backwards && sel.focusOffset === 1) {
    730             sel.modify("extend", "backward", "character");
    731           }
    732 
    733           // since different browser extend by "word" differently and some of them extend beyond the word
    734           // covering spaces and punctuation, we need to collapse the selection back so it ends with the word
    735           var i = 0,
    736             lengthBefore,
    737             lengthAfter;
    738           while (
    739             i < 5 &&
    740             (
    741               sel
    742                 .toString()
    743                 .slice(-1)
    744                 .match(/[\s\n\t]/) || ""
    745             ).length
    746           ) {
    747             lengthBefore = sel.toString().length;
    748             if (backwards) {
    749               endNode =
    750                 sel.anchorOffset == 0
    751                   ? sel.anchorNode.previousSibling
    752                   : sel.anchorNode;
    753               endOffset =
    754                 sel.anchorOffset == 0
    755                   ? sel.anchorNode.previousSibling.length
    756                   : sel.anchorOffset;
    757               sel.modify("move", "backward", "character");
    758               sel.extend(endNode, endOffset);
    759               backwards = false;
    760               direction = ["forward", "backward"];
    761             } else {
    762               sel.modify("extend", "backward", "character");
    763             }
    764             lengthAfter = sel.toString().length;
    765 
    766             // workaround for WebKit quirk: undo last iteration
    767             if (lengthBefore - lengthAfter > 1) {
    768               sel.modify("extend", "forward", "character");
    769               break;
    770             }
    771           }
    772 
    773           // finally, we've got a modified selection which is bound to words
    774           // save it to highlight it later
    775           selWord = stringifyContent(sel.toString().trim());
    776         }
    777       }
    778       // this one is for IE11
    779       else if ((sel = window.getSelection())) {
    780         var startOffset, startNode, endNode;
    781         selChars = sel.toString();
    782         range = document.createRange();
    783         if (range.collapsed) {
    784           startNode = sel.focusNode;
    785           endNode = sel.anchorNode;
    786           startOffset = sel.focusOffset;
    787           endOffset = sel.anchorOffset;
    788         } else {
    789           startNode = sel.anchorNode;
    790           endNode = sel.focusNode;
    791           startOffset = sel.anchorOffset;
    792           endOffset = sel.focusOffset;
    793         }
    794 
    795         while (
    796           startOffset &&
    797           !startNode.textContent
    798             .slice(startOffset - 1, startOffset)
    799             .match(/[\s\n\t]/)
    800         ) {
    801           startOffset--;
    802         }
    803         while (
    804           endOffset < endNode.length &&
    805           !endNode.textContent.slice(endOffset, endOffset + 1).match(/[\s\n\t]/)
    806         ) {
    807           endOffset++;
    808         }
    809 
    810         // here we get the nearest parent node which is common for the whole selection
    811         if (sel.rangeCount) {
    812           parentEl = sel.getRangeAt(0).commonAncestorContainer.parentNode;
    813           while (parentEl.textContent == sel.toString()) {
    814             parentEl = parentEl.parentNode;
    815           }
    816         }
    817 
    818         selWord = stringifyContent(sel.toString().trim());
    819 
    820         // this logic is for IE<10
    821         // } else if ((sel = document.selection) && sel.type != "Control") {
    822         //     var textRange = sel.createRange();
    823         //
    824         //      if (!textRange || textRange.text.length > maxContextLength) {
    825         //      return;
    826         //      }
    827         //
    828         //      if (textRange.text) {
    829         //      selChars = textRange.text;
    830         //      textRange.expand("word");
    831         //      // Move the end back to not include the word's trailing space(s), if necessary
    832         //      while (/\s$/.test(textRange.text)) {
    833         //      textRange.moveEnd("character", -1);
    834         //      }
    835         //      selWord = textRange.text;
    836         //      parentEl = textRange.parentNode;
    837         //      }
    838       }
    839 
    840       if (typeof parentEl == "undefined") {
    841         return;
    842       }
    843 
    844       var selToFindInContext,
    845         contextsToCheck = {
    846           // different browsers implement different methods, we try them by turn
    847           textContent: parentEl.textContent,
    848           innerText: parentEl.innerText,
    849         };
    850 
    851       textToHighlight = selWord;
    852 
    853       for (var method in contextsToCheck) {
    854         if (
    855           contextsToCheck.hasOwnProperty(method) &&
    856           typeof contextsToCheck[method] != "undefined"
    857         ) {
    858           // start with counting selected word occurrences in context
    859           var scope = { selection: "word", context: "initial" };
    860           var context = stringifyContent(contextsToCheck[method].trim());
    861           var selWithContext = stringifyContent(sel.toString().trim());
    862           mixtape.contextBuffer = context;
    863           mixtape.selBuffer = selWithContext;
    864           var selPos; // this is what we are going to find
    865           var selExactMatch = false;
    866 
    867           if ((selPos = getExactSelPos(selWithContext, context)) != -1) {
    868             selExactMatch = true;
    869             selToFindInContext = selWithContext;
    870             break;
    871           }
    872 
    873           // if there is more than one occurrence, extend the selection
    874           selWithContext = getExtendedSelection(40);
    875           scope.selection = "word extended";
    876 
    877           if ((selPos = getExactSelPos(selWithContext, context)) != -1) {
    878             selExactMatch = true;
    879             selToFindInContext = selWithContext;
    880             break;
    881           }
    882 
    883           // if still have duplicates, extend the context and selection, and try again
    884           var initialContext = context;
    885           var extContext = getExtendedContext(context, parentEl, method);
    886           context = extContext.contents;
    887           selWithContext = getExtendedSelection(40, extContext.extensions);
    888           scope.context = "extended";
    889 
    890           if ((selPos = getExactSelPos(selWithContext, context)) != -1) {
    891             selExactMatch = true;
    892             selToFindInContext = selWithContext;
    893             break;
    894           }
    895 
    896           // skip to next context getting method and start over, or exit
    897           if (!selWithContext) {
    898             continue;
    899           }
    900 
    901           if (
    902             isSubstrUnique(selWord, selWithContext) ||
    903             selWord == selChars.trim()
    904           ) {
    905             context = selWithContext;
    906             selWithContext = selWord;
    907             textToHighlight = selWord;
    908             scope.selection = "word";
    909             scope.context = "extended";
    910           } else {
    911             context = selWord;
    912             selWithContext = selChars.trim();
    913             textToHighlight = selChars.trim();
    914             scope.selection = "initial";
    915             scope.context = "word";
    916           }
    917 
    918           selPos = context.indexOf(selWithContext);
    919 
    920           if (selPos !== -1) {
    921             selToFindInContext = selWithContext;
    922           } else if ((selPos = context.indexOf(selWord)) !== -1) {
    923             selToFindInContext = selWord;
    924           } else if ((selPos = context.indexOf(selChars)) !== -1) {
    925             selToFindInContext = selChars;
    926           } else {
    927             continue;
    928           }
    929           break;
    930         }
    931       }
    932 
    933       if (selToFindInContext) {
    934         sel.removeAllRanges();
    935       } else {
    936         mixtape.restoreInitSelection(sel, initialSel);
    937         return;
    938       }
    939 
    940       if (scope.context === "extended") {
    941         context =
    942           extContext.extensions.left +
    943           initialContext +
    944           " " +
    945           extContext.extensions.right;
    946       }
    947 
    948       var contExcerptStartPos,
    949         contExcerptEndPos,
    950         selPosInContext,
    951         highlightedChars,
    952         previewText;
    953       maxContextLength = Math.min(context.length, maxContextLength);
    954 
    955       var truncatedContext = context;
    956 
    957       if (context.length > maxContextLength) {
    958         if (selPos + selToFindInContext.length / 2 < maxContextLength / 2) {
    959           selPosInContext = "beginning";
    960           contExcerptStartPos = 0;
    961           contExcerptEndPos = Math.max(
    962             selPos + selToFindInContext.length,
    963             context.indexOf(" ", maxContextLength - 10)
    964           );
    965         } else if (
    966           selPos + selToFindInContext.length / 2 >
    967           context.length - maxContextLength / 2
    968         ) {
    969           selPosInContext = "end";
    970           contExcerptStartPos = Math.min(
    971             selPos,
    972             context.indexOf(" ", context.length - maxContextLength + 10)
    973           );
    974           contExcerptEndPos = context.length;
    975         } else {
    976           selPosInContext = "middle";
    977           var centerPos = selPos + Math.round(selToFindInContext.length / 2);
    978           contExcerptStartPos = Math.min(
    979             selPos,
    980             context.indexOf(" ", centerPos - maxContextLength / 2 - 10)
    981           );
    982           contExcerptEndPos = Math.max(
    983             selPos + selToFindInContext.length,
    984             context.indexOf(" ", centerPos + maxContextLength / 2 - 10)
    985           );
    986         }
    987 
    988         truncatedContext = context
    989           .substring(contExcerptStartPos, contExcerptEndPos)
    990           .trim();
    991 
    992         if (
    993           selPosInContext !== "beginning" &&
    994           context.charAt(contExcerptStartPos - 1) !== "."
    995         ) {
    996           truncatedContext = "... " + truncatedContext;
    997         }
    998         if (
    999           selPosInContext !== "end" &&
    1000           context.charAt(contExcerptStartPos + contExcerptEndPos - 1) !== "."
    1001         ) {
    1002           truncatedContext = truncatedContext + " ...";
    1003         }
    1004       }
    1005 
    1006       if (isSubstrUnique(selChars, textToHighlight)) {
    1007         highlightedChars = textToHighlight.replace(
    1008           selChars,
    1009           '<span class="mixtape_mistake_inner">' + selChars + "</span>"
    1010         );
    1011       } else {
    1012         highlightedChars =
    1013           '<strong class="mixtape_mistake_inner">' +
    1014           textToHighlight +
    1015           "</strong>";
    1016       }
    1017 
    1018       var selWithContextHighlighted = selToFindInContext.replace(
    1019         textToHighlight,
    1020         '<span class="mixtape_mistake_outer">' + highlightedChars + "</span>"
     587    },
     588
     589    _getExactSelPos(selection, context) {
     590      return context.indexOf(selection);
     591    },
     592
     593    _truncateContext(context, selPos, selLength, maxContextLength) {
     594      let start = Math.max(0, selPos - Math.floor(maxContextLength / 2));
     595      let end = Math.min(context.length, start + maxContextLength);
     596
     597      if (start > 0) {
     598        start = context.lastIndexOf(" ", start) + 1;
     599      }
     600      if (end < context.length) {
     601        end = context.indexOf(" ", end);
     602      }
     603
     604      return (
     605        (start > 0 ? "..." : "") +
     606        context.slice(start, end) +
     607        (end < context.length ? "..." : "")
    1021608      );
    1022 
    1023       if (selExactMatch && truncatedContext === context) {
    1024         previewText =
    1025           truncatedContext.substring(0, selPos) +
    1026             selWithContextHighlighted +
    1027             truncatedContext.substring(selPos + selWithContext.length) ||
    1028           selWithContextHighlighted;
    1029       } else {
    1030         previewText =
    1031           truncatedContext.replace(selWithContext, selWithContextHighlighted) ||
    1032           selWithContextHighlighted;
    1033       }
    1034 
    1035       return {
    1036         selection: selChars,
    1037         word: selWord,
    1038         replace_context: selToFindInContext,
    1039         context: truncatedContext,
    1040         preview_text: previewText,
    1041         nonce: $('input[name="mixtape_nonce"]').val(),
    1042         // post_id: mixtape.getPostId()
    1043       };
    1044     },
    1045 
    1046     restoreInitSelection: function (sel, initialSel) {
     609    },
     610
     611    _stringifyContent(string) {
     612      return typeof string === "string"
     613        ? string.replace(/\s+/g, " ").trim()
     614        : "";
     615    },
     616
     617    restoreInitSelection(sel, initialSel) {
    1047618      sel.collapse(initialSel.anchorNode, initialSel.anchorOffset);
    1048619      sel.extend(initialSel.focusNode, initialSel.focusOffset);
    1049620    },
    1050621
    1051     showReportButton: function () {
    1052       if (!reportButton) {
    1053         reportButton = $("<button>")
    1054           .text(mixtape.reportError)
     622    showReportButton() {
     623      if (!this.reportButton) {
     624        this.reportButton = $("<button>")
     625          .text(MixtapeLocalize.reportError)
    1055626          .addClass("mixtape-report-button")
    1056           .on("click", function () {
    1057             const report = mixtape.getSelectionData();
     627          .on("click", () => {
     628            const report = Mixtape.getSelectionData();
    1058629            if (report) {
    1059               mixtape.showDialog(report);
     630              this.showDialog(report);
     631            } else {
     632              console.error("No report generated");
    1060633            }
    1061634          });
    1062635
    1063         $("body").append(reportButton);
     636        $("body").append(this.reportButton);
    1064637      }
    1065638
    1066639      const selection = window.getSelection();
     640      if (selection.rangeCount === 0) {
     641        console.error("No selection range found");
     642        return;
     643      }
     644
    1067645      const range = selection.getRangeAt(0);
    1068646      const rect = range.getBoundingClientRect();
    1069 
    1070       const buttonHeight = reportButton.outerHeight();
    1071 
     647      const buttonHeight = this.reportButton.outerHeight();
    1072648      const topPosition = rect.top + window.scrollY - 20 - buttonHeight;
    1073649
    1074       reportButton.css({
    1075         top: topPosition + "px",
    1076         left: rect.left + window.scrollX + "px",
     650      this.reportButton.css({
     651        top: `${topPosition}px`,
     652        left: `${rect.left + window.scrollX}px`,
    1077653        position: "absolute",
    1078654      });
    1079655    },
    1080656
    1081     hideReportButton: function () {
    1082       if (reportButton) {
    1083         reportButton.remove();
    1084         reportButton = null;
    1085       }
    1086     },
     657    hideReportButton() {
     658      if (this.reportButton) {
     659        this.reportButton.remove();
     660        this.reportButton = null;
     661      }
     662    },
     663
     664    init() {
     665      this.onReady();
     666    },
     667  };
     668
     669  $(document).ready(function () {
     670    Mixtape.init();
    1087671  });
    1088 
    1089   $(document).ready(mixtape.onReady);
    1090 })(jQuery);
     672});
  • mixtape/tags/1.2/package-lock.json

    r2994803 r3109820  
    99      "version": "1.0.0",
    1010      "devDependencies": {
     11        "@playwright/test": "^1.45.0",
     12        "@types/node": "^20.14.9",
    1113        "gulp": "^4.0.2",
    1214        "gulp-phpcs": "^3.1.0",
    1315        "gulp-wp-pot": "^2.5.0"
     16      }
     17    },
     18    "node_modules/@playwright/test": {
     19      "version": "1.45.0",
     20      "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.45.0.tgz",
     21      "integrity": "sha512-TVYsfMlGAaxeUllNkywbwek67Ncf8FRGn8ZlRdO291OL3NjG9oMbfVhyP82HQF0CZLMrYsvesqoUekxdWuF9Qw==",
     22      "dev": true,
     23      "dependencies": {
     24        "playwright": "1.45.0"
     25      },
     26      "bin": {
     27        "playwright": "cli.js"
     28      },
     29      "engines": {
     30        "node": ">=18"
     31      }
     32    },
     33    "node_modules/@types/node": {
     34      "version": "20.14.9",
     35      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.9.tgz",
     36      "integrity": "sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==",
     37      "dev": true,
     38      "dependencies": {
     39        "undici-types": "~5.26.4"
    1440      }
    1541    },
     
    28592885      }
    28602886    },
     2887    "node_modules/playwright": {
     2888      "version": "1.45.0",
     2889      "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.45.0.tgz",
     2890      "integrity": "sha512-4z3ac3plDfYzGB6r0Q3LF8POPR20Z8D0aXcxbJvmfMgSSq1hkcgvFRXJk9rUq5H/MJ0Ktal869hhOdI/zUTeLA==",
     2891      "dev": true,
     2892      "dependencies": {
     2893        "playwright-core": "1.45.0"
     2894      },
     2895      "bin": {
     2896        "playwright": "cli.js"
     2897      },
     2898      "engines": {
     2899        "node": ">=18"
     2900      },
     2901      "optionalDependencies": {
     2902        "fsevents": "2.3.2"
     2903      }
     2904    },
     2905    "node_modules/playwright-core": {
     2906      "version": "1.45.0",
     2907      "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.0.tgz",
     2908      "integrity": "sha512-lZmHlFQ0VYSpAs43dRq1/nJ9G/6SiTI7VPqidld9TDefL9tX87bTKExWZZUF5PeRyqtXqd8fQi2qmfIedkwsNQ==",
     2909      "dev": true,
     2910      "bin": {
     2911        "playwright-core": "cli.js"
     2912      },
     2913      "engines": {
     2914        "node": ">=18"
     2915      }
     2916    },
     2917    "node_modules/playwright/node_modules/fsevents": {
     2918      "version": "2.3.2",
     2919      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
     2920      "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
     2921      "dev": true,
     2922      "hasInstallScript": true,
     2923      "optional": true,
     2924      "os": [
     2925        "darwin"
     2926      ],
     2927      "engines": {
     2928        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
     2929      }
     2930    },
    28612931    "node_modules/plugin-error": {
    28622932      "version": "1.0.1",
     
    38203890        "node": ">= 0.10"
    38213891      }
     3892    },
     3893    "node_modules/undici-types": {
     3894      "version": "5.26.5",
     3895      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
     3896      "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
     3897      "dev": true
    38223898    },
    38233899    "node_modules/union-value": {
     
    41414217  },
    41424218  "dependencies": {
     4219    "@playwright/test": {
     4220      "version": "1.45.0",
     4221      "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.45.0.tgz",
     4222      "integrity": "sha512-TVYsfMlGAaxeUllNkywbwek67Ncf8FRGn8ZlRdO291OL3NjG9oMbfVhyP82HQF0CZLMrYsvesqoUekxdWuF9Qw==",
     4223      "dev": true,
     4224      "requires": {
     4225        "playwright": "1.45.0"
     4226      }
     4227    },
     4228    "@types/node": {
     4229      "version": "20.14.9",
     4230      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.9.tgz",
     4231      "integrity": "sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==",
     4232      "dev": true,
     4233      "requires": {
     4234        "undici-types": "~5.26.4"
     4235      }
     4236    },
    41434237    "acorn": {
    41444238      "version": "8.10.0",
     
    64026496      }
    64036497    },
     6498    "playwright": {
     6499      "version": "1.45.0",
     6500      "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.45.0.tgz",
     6501      "integrity": "sha512-4z3ac3plDfYzGB6r0Q3LF8POPR20Z8D0aXcxbJvmfMgSSq1hkcgvFRXJk9rUq5H/MJ0Ktal869hhOdI/zUTeLA==",
     6502      "dev": true,
     6503      "requires": {
     6504        "fsevents": "2.3.2",
     6505        "playwright-core": "1.45.0"
     6506      },
     6507      "dependencies": {
     6508        "fsevents": {
     6509          "version": "2.3.2",
     6510          "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
     6511          "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
     6512          "dev": true,
     6513          "optional": true
     6514        }
     6515      }
     6516    },
     6517    "playwright-core": {
     6518      "version": "1.45.0",
     6519      "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.0.tgz",
     6520      "integrity": "sha512-lZmHlFQ0VYSpAs43dRq1/nJ9G/6SiTI7VPqidld9TDefL9tX87bTKExWZZUF5PeRyqtXqd8fQi2qmfIedkwsNQ==",
     6521      "dev": true
     6522    },
    64046523    "plugin-error": {
    64056524      "version": "1.0.1",
     
    71867305      "dev": true
    71877306    },
     7307    "undici-types": {
     7308      "version": "5.26.5",
     7309      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
     7310      "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
     7311      "dev": true
     7312    },
    71887313    "union-value": {
    71897314      "version": "1.0.1",
  • mixtape/tags/1.2/package.json

    r2994803 r3109820  
    44  "scripts": {
    55    "make-pot": "gulp make-pot",
    6     "phpcs": "gulp phpcs"
     6    "phpcs": "gulp phpcs",
     7    "test": "npx playwright test --ui"
    78  },
    89  "devDependencies": {
     10    "@playwright/test": "^1.45.0",
     11    "@types/node": "^20.14.9",
    912    "gulp": "^4.0.2",
    1013    "gulp-phpcs": "^3.1.0",
  • mixtape/tags/1.2/readme.md

    r2998174 r3109820  
    77**License:** GPLv2 or later 
    88**Contributors:** natata7 
    9 **Stable tag:** 1.
     9**Stable tag:** 1.2
    1010**License URI:** <http://www.gnu.org/licenses/gpl-2.0.html>
    1111
     
    33331. Go to Settings > Mixtape and set options.
    3434
     35### 1.2 ###
     36
     37* Fix: sending reports on front on some cases.
     38
    3539### 1.1 ###
    3640
  • mixtape/tags/1.2/readme.txt

    r2998174 r3109820  
    55Tested up to: 6.4.1
    66Requires PHP: 7.4
    7 Stable tag: 1.1
     7Stable tag: 1.2
    88Contributors: natata7
    99License: GPLv2 or later
     
    28282. Activate and follow the settings link in the notice you will see at the top. Tick desired checkboxes, save, and that's it!
    2929
     30== 1.2 ==
     31
     32* Fix: sending reports on front on some cases.
     33
    3034== 1.1 ==
    3135
  • mixtape/tags/1.2/src/class-mixtape-abstract.php

    r2998174 r3109820  
    301301
    302302        // modernizer
    303         wp_enqueue_script( 'modernizr', plugins_url( 'assets/js/modernizr.custom.js', self::$plugin_path ), array( 'jquery' ), self::$version, true );
     303        //wp_enqueue_script( 'modernizr', plugins_url( 'assets/js/modernizr.custom.js', self::$plugin_path ), array( 'jquery' ), self::$version, true );
    304304
    305305        // frontend script (combined)
     
    309309            array(
    310310                'jquery',
    311                 'modernizr',
     311                //'modernizr',
    312312            ),
    313313            filemtime( plugin_dir_path( self::$plugin_path ) . '/assets/js/mixtape-front.js' ),
     
    318318            'reportError' => __( 'Report an error', 'mixtape' ),
    319319        );
    320         wp_localize_script( 'mixtape-front', 'mixtape', $localized_data );
     320        wp_localize_script( 'mixtape-front', 'MixtapeLocalize', $localized_data );
    321321    }
    322322
  • mixtape/trunk/assets/js/mixtape-front.js

    r2998174 r3109820  
    44 */
    55(function (window) {
    6   {
    7     var unknown = "-";
    8 
    9     // screen
    10     var screenSize = "";
    11     if (screen.width) {
    12       width = screen.width ? screen.width : "";
    13       height = screen.height ? screen.height : "";
    14       screenSize += "" + width + " x " + height;
    15     }
    16 
    17     // browser
    18     var nVer = navigator.appVersion;
    19     var nAgt = navigator.userAgent;
    20     var browser = navigator.appName;
    21     var version = "" + parseFloat(navigator.appVersion);
    22     var majorVersion = parseInt(navigator.appVersion, 10);
    23     var nameOffset, verOffset, ix;
    24 
    25     // Opera
    26     if ((verOffset = nAgt.indexOf("Opera")) !== -1) {
    27       browser = "Opera";
    28       version = nAgt.substring(verOffset + 6);
    29       if ((verOffset = nAgt.indexOf("Version")) !== -1) {
    30         version = nAgt.substring(verOffset + 8);
    31       }
    32     }
    33     // Opera Next
    34     if ((verOffset = nAgt.indexOf("OPR")) !== -1) {
    35       browser = "Opera";
    36       version = nAgt.substring(verOffset + 4);
    37     }
    38     // USbrowser
    39     else if ((verOffset = nAgt.indexOf("UCBrowser")) !== -1) {
    40       browser = "UCBrowser";
    41       version = nAgt.substring(verOffset + 6);
    42       if ((verOffset = nAgt.indexOf("Version")) !== -1) {
    43         version = nAgt.substring(verOffset + 8);
    44       }
    45     }
    46     // MSIE
    47     else if ((verOffset = nAgt.indexOf("MSIE")) !== -1) {
    48       browser = "Microsoft Internet Explorer";
    49       version = nAgt.substring(verOffset + 5);
    50     }
    51     // MSE
    52     else if ((verOffset = nAgt.indexOf("Edge")) !== -1) {
    53       browser = "Edge";
    54       version = nAgt.substring(verOffset + 7);
    55     }
    56     // Chrome
    57     else if ((verOffset = nAgt.indexOf("Chrome")) !== -1) {
    58       browser = "Chrome";
    59       version = nAgt.substring(verOffset + 7);
    60     }
    61     // Safari
    62     else if ((verOffset = nAgt.indexOf("Safari")) !== -1) {
    63       browser = "Safari";
    64       version = nAgt.substring(verOffset + 7);
    65       if ((verOffset = nAgt.indexOf("Version")) !== -1) {
    66         version = nAgt.substring(verOffset + 8);
    67       }
    68     }
    69     // Firefox
    70     else if ((verOffset = nAgt.indexOf("Firefox")) !== -1) {
    71       browser = "Firefox";
    72       version = nAgt.substring(verOffset + 8);
    73     }
    74     // MSIE 11+
    75     else if (nAgt.indexOf("Trident/") !== -1) {
    76       browser = "Microsoft Internet Explorer";
    77       version = nAgt.substring(nAgt.indexOf("rv:") + 3);
    78     }
    79     // Other browsers
    80     else if (
    81       (nameOffset = nAgt.lastIndexOf(" ") + 1) <
    82       (verOffset = nAgt.lastIndexOf("/"))
    83     ) {
    84       browser = nAgt.substring(nameOffset, verOffset);
    85       version = nAgt.substring(verOffset + 1);
    86       if (browser.toLowerCase() === browser.toUpperCase()) {
    87         browser = navigator.appName;
    88       }
    89     }
    90     // trim the version string
    91     if ((ix = version.indexOf(";")) !== -1) version = version.substring(0, ix);
    92     if ((ix = version.indexOf(" ")) !== -1) version = version.substring(0, ix);
    93     if ((ix = version.indexOf(")")) !== -1) version = version.substring(0, ix);
    94 
    95     majorVersion = parseInt("" + version, 10);
    96     if (isNaN(majorVersion)) {
    97       version = "" + parseFloat(navigator.appVersion);
    98       majorVersion = parseInt(navigator.appVersion, 10);
    99     }
    100 
    101     // mobile version
    102     var mobile = /Mobile|mini|Fennec|Android|iP(ad|od|hone)/.test(nVer);
    103 
    104     // cookie
    105     var cookieEnabled = !!navigator.cookieEnabled;
    106 
    107     if (typeof navigator.cookieEnabled === "undefined" && !cookieEnabled) {
    108       document.cookie = "testcookie";
    109       cookieEnabled = document.cookie.indexOf("testcookie") !== -1;
    110     }
    111 
    112     // system
    113     var os = unknown;
    114     var clientStrings = [
    115       { s: "Windows 10", r: /(Windows 10.0|Windows NT 10.0)/ },
    116       { s: "Windows 8.1", r: /(Windows 8.1|Windows NT 6.3)/ },
    117       { s: "Windows 8", r: /(Windows 8|Windows NT 6.2)/ },
    118       { s: "Windows 7", r: /(Windows 7|Windows NT 6.1)/ },
    119       { s: "Windows Vista", r: /Windows NT 6.0/ },
    120       { s: "Windows Server 2003", r: /Windows NT 5.2/ },
    121       { s: "Windows XP", r: /(Windows NT 5.1|Windows XP)/ },
    122       { s: "Windows 2000", r: /(Windows NT 5.0|Windows 2000)/ },
    123       { s: "Windows ME", r: /(Win 9x 4.90|Windows ME)/ },
    124       { s: "Windows 98", r: /(Windows 98|Win98)/ },
    125       { s: "Windows 95", r: /(Windows 95|Win95|Windows_95)/ },
    126       { s: "Windows NT 4.0", r: /(Windows NT 4.0|WinNT4.0|WinNT|Windows NT)/ },
    127       { s: "Windows CE", r: /Windows CE/ },
    128       { s: "Windows 3.11", r: /Win16/ },
    129       { s: "Android", r: /Android/ },
    130       { s: "Open BSD", r: /OpenBSD/ },
    131       { s: "Sun OS", r: /SunOS/ },
    132       { s: "Linux", r: /(Linux|X11)/ },
    133       { s: "iOS", r: /(iPhone|iPad|iPod)/ },
    134       { s: "Mac OS X", r: /Mac OS X/ },
    135       { s: "Mac OS", r: /(MacPPC|MacIntel|Mac_PowerPC|Macintosh)/ },
    136       { s: "QNX", r: /QNX/ },
    137       { s: "UNIX", r: /UNIX/ },
    138       { s: "BeOS", r: /BeOS/ },
    139       { s: "OS/2", r: /OS\/2/ },
    140       {
    141         s: "Search Bot",
    142         r: /(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves\/Teoma|ia_archiver)/,
    143       },
    144     ];
    145     for (var id in clientStrings) {
    146       var cs = clientStrings[id];
    147       if (cs.r.test(nAgt)) {
    148         os = cs.s;
    149         break;
    150       }
    151     }
    152 
    153     if (/Windows/.test(os)) {
    154       os = "Windows";
    155     }
    156 
    157     var flashVersion = "no check";
    158     if (typeof swfobject !== "undefined") {
    159       var fv = swfobject.getFlashPlayerVersion();
    160       if (fv.major > 0) {
    161         flashVersion = fv.major + "." + fv.minor + " r" + fv.release;
    162       } else {
    163         flashVersion = unknown;
    164       }
     6  var unknown = "-";
     7
     8  var screenSize = screen.width ? `${screen.width} x ${screen.height}` : "";
     9
     10  var nVer = navigator.appVersion;
     11  var nAgt = navigator.userAgent;
     12  var browser = navigator.appName;
     13  var version = `${parseFloat(navigator.appVersion)}`;
     14  var majorVersion = parseInt(navigator.appVersion, 10);
     15
     16  var browsers = [
     17    { name: "Opera", key: "Opera", versionKey: "Version" },
     18    { name: "Opera", key: "OPR", versionKey: "Version" },
     19    { name: "UCBrowser", key: "UCBrowser", versionKey: "Version" },
     20    { name: "Microsoft Internet Explorer", key: "MSIE" },
     21    { name: "Edge", key: "Edge" },
     22    { name: "Chrome", key: "Chrome" },
     23    { name: "Safari", key: "Safari", versionKey: "Version" },
     24    { name: "Firefox", key: "Firefox" },
     25    { name: "Microsoft Internet Explorer", key: "Trident/", versionKey: "rv:" },
     26  ];
     27
     28  for (let b of browsers) {
     29    let verOffset = nAgt.indexOf(b.key);
     30    if (verOffset !== -1) {
     31      browser = b.name;
     32      version = nAgt.substring(verOffset + b.key.length + 1);
     33      if (b.versionKey) {
     34        let versionOffset = nAgt.indexOf(b.versionKey);
     35        if (versionOffset !== -1) {
     36          version = nAgt.substring(versionOffset + b.versionKey.length + 1);
     37        }
     38      }
     39      break;
    16540    }
    16641  }
     42
     43  let verEndings = [";", " ", ")"];
     44  for (let end of verEndings) {
     45    let ix = version.indexOf(end);
     46    if (ix !== -1) version = version.substring(0, ix);
     47  }
     48
     49  majorVersion = parseInt(version, 10);
     50  if (isNaN(majorVersion)) {
     51    version = `${parseFloat(navigator.appVersion)}`;
     52    majorVersion = parseInt(navigator.appVersion, 10);
     53  }
     54
     55  var mobile = /Mobile|mini|Fennec|Android|iP(ad|od|hone)/.test(nVer);
     56  var cookieEnabled =
     57    navigator.cookieEnabled ??
     58    ((document.cookie = "testcookie"),
     59    document.cookie.indexOf("testcookie") !== -1);
     60
     61  var os = unknown;
     62  var clientStrings = [
     63    { s: "Windows 10", r: /(Windows 10.0|Windows NT 10.0)/ },
     64    { s: "Windows 8.1", r: /(Windows 8.1|Windows NT 6.3)/ },
     65    { s: "Windows 8", r: /(Windows 8|Windows NT 6.2)/ },
     66    { s: "Windows 7", r: /(Windows 7|Windows NT 6.1)/ },
     67    { s: "Windows Vista", r: /Windows NT 6.0/ },
     68    { s: "Windows Server 2003", r: /Windows NT 5.2/ },
     69    { s: "Windows XP", r: /(Windows NT 5.1|Windows XP)/ },
     70    { s: "Windows 2000", r: /(Windows NT 5.0|Windows 2000)/ },
     71    { s: "Windows ME", r: /(Win 9x 4.90|Windows ME)/ },
     72    { s: "Windows 98", r: /(Windows 98|Win98)/ },
     73    { s: "Windows 95", r: /(Windows 95|Win95|Windows_95)/ },
     74    { s: "Windows NT 4.0", r: /(Windows NT 4.0|WinNT4.0|WinNT|Windows NT)/ },
     75    { s: "Windows CE", r: /Windows CE/ },
     76    { s: "Windows 3.11", r: /Win16/ },
     77    { s: "Android", r: /Android/ },
     78    { s: "Open BSD", r: /OpenBSD/ },
     79    { s: "Sun OS", r: /SunOS/ },
     80    { s: "Linux", r: /(Linux|X11)/ },
     81    { s: "iOS", r: /(iPhone|iPad|iPod)/ },
     82    { s: "Mac OS X", r: /Mac OS X/ },
     83    { s: "Mac OS", r: /(MacPPC|MacIntel|Mac_PowerPC|Macintosh)/ },
     84    { s: "QNX", r: /QNX/ },
     85    { s: "UNIX", r: /UNIX/ },
     86    { s: "BeOS", r: /BeOS/ },
     87    { s: "OS/2", r: /OS\/2/ },
     88    {
     89      s: "Search Bot",
     90      r: /(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves\/Teoma|ia_archiver)/,
     91    },
     92  ];
     93
     94  for (let cs of clientStrings) {
     95    if (cs.r.test(nAgt)) {
     96      os = cs.s;
     97      break;
     98    }
     99  }
     100
     101  if (/Windows/.test(os)) os = "Windows";
    167102
    168103  window.jscd = {
     
    174109    os: os,
    175110    cookies: cookieEnabled,
    176     flashVersion: flashVersion,
    177111  };
    178112})(window);
    179113
    180 /**
    181  * dialogFx.js v1.0.0
    182  * http://www.codrops.com
    183  *
    184  * Licensed under the MIT license.
    185  * http://www.opensource.org/licenses/mit-license.php
    186  *
    187  * Copyright 2014, Codrops
    188  * http://www.codrops.com
    189  */
    190 
    191114(function (window) {
    192115  "use strict";
    193116
    194   var support = { animations: Modernizr.cssanimations },
    195     animEndEventNames = {
    196       WebkitAnimation: "webkitAnimationEnd",
    197       OAnimation: "oAnimationEnd",
    198       msAnimation: "MSAnimationEnd",
    199       animation: "animationend",
    200     },
    201     animEndEventName = animEndEventNames[Modernizr.prefixed("animation")],
    202     onEndAnimation = function (el, callback) {
    203       var onEndCallbackFn = function (ev) {
    204         if (support.animations) {
    205           if (ev.target !== this) return;
    206           this.removeEventListener(animEndEventName, onEndCallbackFn);
     117  function checkAnimationSupport() {
     118    var animation = false,
     119      animationstring = "animation",
     120      keyframeprefix = "",
     121      domPrefixes = "Webkit Moz O ms Khtml".split(" "),
     122      pfx = "",
     123      elm = document.createElement("div");
     124
     125    if (elm.style.animationName !== undefined) {
     126      animation = true;
     127    }
     128
     129    if (animation === false) {
     130      for (var i = 0; i < domPrefixes.length; i++) {
     131        if (elm.style[domPrefixes[i] + "AnimationName"] !== undefined) {
     132          pfx = domPrefixes[i];
     133          animationstring = pfx + "Animation";
     134          keyframeprefix = "-" + pfx.toLowerCase() + "-";
     135          animation = true;
     136          break;
    207137        }
    208         if (callback && typeof callback === "function") {
    209           callback.call();
    210         }
    211       };
    212       if (support.animations) {
    213         el.addEventListener(animEndEventName, onEndCallbackFn);
    214       } else {
    215         onEndCallbackFn();
    216       }
     138      }
     139    }
     140
     141    return {
     142      supported: animation,
     143      pfx: pfx,
     144      animationstring: animationstring,
     145      keyframeprefix: keyframeprefix,
     146      eventName: animation
     147        ? pfx
     148          ? pfx + "AnimationEnd"
     149          : "animationend"
     150        : null,
    217151    };
     152  }
     153
     154  var animationSupport = checkAnimationSupport();
     155
     156  // Визначення події завершення анімації
     157  var animEndEventNames = {
     158    WebkitAnimation: "webkitAnimationEnd",
     159    OAnimation: "oAnimationEnd",
     160    msAnimation: "MSAnimationEnd",
     161    animation: "animationend",
     162  };
     163  var animEndEventName = animationSupport.eventName;
     164
     165  var onEndAnimation = function (el, callback) {
     166    var onEndCallbackFn = function (ev) {
     167      if (animationSupport.supported) {
     168        if (ev.target !== this) return;
     169        this.removeEventListener(animEndEventName, onEndCallbackFn);
     170      }
     171      if (callback && typeof callback === "function") {
     172        callback.call();
     173      }
     174    };
     175    if (animationSupport.supported) {
     176      el.addEventListener(animEndEventName, onEndCallbackFn);
     177    } else {
     178      onEndCallbackFn();
     179    }
     180  };
    218181
    219182  function extend(a, b) {
     
    300263 * mixtape
    301264 */
    302 (function ($) {
    303   // return if no args passed from backend
    304   if (!window.mixtape) {
    305     return;
    306   }
    307 
    308   var reportButton;
    309 
    310   window.mixtape = $.extend(window.mixtape, {
    311     onReady: function () {
    312       mixtape.initDialogFx();
    313 
    314       var $dialog = $(mixtape.dlg.el);
     265
     266jQuery(function ($) {
     267  const Mixtape = {
     268    reportButton: null,
     269
     270    onReady() {
     271      this.initDialogFx();
     272
     273      var $dialog = $(this.dlg.el);
    315274
    316275      $(document).on("click", ".mixtape_action", function () {
    317276        if ($(this).is("[data-action=send]")) {
    318277          var data;
    319 
    320278          if (!$dialog.data("dry-run") && (data = $dialog.data("report"))) {
    321279            if ($dialog.data("mode") === "comment") {
     
    325283            data.post_id = $(this).data("id");
    326284            data.nonce = $dialog.data("nonce");
    327             mixtape.reportSpellError(data);
     285            Mixtape.reportSpellError(data);
    328286          }
    329           mixtape.animateLetter();
    330           mixtape.hideReportButton();
     287          Mixtape.animateLetter();
     288          Mixtape.hideReportButton();
    331289        }
    332290      });
    333291
    334292      $(document).on("click", "#mixtape-close-btn", function () {
    335         mixtape.dlg.toggle();
     293        Mixtape.dlg.toggle();
    336294      });
    337295
    338296      $(document).keyup(function (ev) {
    339297        if (
     298          ev.ctrlKey &&
    340299          ev.keyCode === 13 &&
    341           ev.ctrlKey &&
    342300          ev.target.nodeName.toLowerCase() !== "textarea" &&
    343301          $("#mixtape_dialog.dialog--open").length === 0
    344302        ) {
    345           var report = mixtape.getSelectionData();
    346           if (report) {
    347             mixtape.showDialog(report);
     303          var report = Mixtape.getSelectionData();
     304          if (report) Mixtape.showDialog(report);
     305        }
     306      });
     307
     308      document.addEventListener("selectionchange", function () {
     309        if ($("#mixtape_dialog.dialog--open").length == 0) {
     310          var selection = window.getSelection().toString().trim();
     311          if (selection !== "") {
     312            Mixtape.showReportButton();
     313          } else {
     314            Mixtape.hideReportButton();
    348315          }
    349316        }
    350317      });
    351 
    352       document.addEventListener("selectionchange", function () {
    353         if ($("#mixtape_dialog.dialog--open").length === 0) {
    354           var selection = window.getSelection().toString().trim();
    355           if (selection !== "" && window.innerWidth < 1024) {
    356             mixtape.showReportButton();
    357           } else {
    358             mixtape.hideReportButton();
    359           }
    360         }
    361       });
    362     },
    363 
    364     initDialogFx: function () {
    365       mixtape.dlg = new DialogFx(document.getElementById("mixtape_dialog"), {
    366         onOpenDialog: function (dialog) {
    367           $(dialog.el).css("display", "flex");
    368         },
    369         onCloseAnimationEnd: function (dialog) {
    370           $(dialog.el).css("display", "none");
    371           mixtape.resetDialog();
    372         },
    373       });
    374     },
    375 
    376     animateLetter: function () {
    377       var dialog = $(mixtape.dlg.el),
    378         content = dialog.find(".dialog__content"),
    379         letterTop = dialog.find(".mixtape-letter-top"),
    380         letterFront = dialog.find(".mixtape-letter-front"),
    381         letterBack = dialog.find(".mixtape-letter-back"),
    382         dialogWrap = dialog.find(".dialog-wrap");
    383 
    384       content.addClass("show-letter");
    385 
    386       setTimeout(function () {
    387         var y =
    388           letterTop.offset().top -
    389           letterFront.offset().top +
    390           letterTop.outerHeight();
    391         letterTop.css({
    392           bottom: Math.floor(y),
    393           opacity: 1,
    394         });
    395         jQuery(".mixtape-letter-back-top").hide();
    396         if (content.hasClass("with-comment")) {
    397           dialogWrap.css("transform", "scaleY(0.5) scaleX(0.28)");
    398         } else {
    399           dialogWrap.css("transform", "scaleY(0.5) scaleX(0.4)");
    400         }
    401         setTimeout(function () {
    402           if (content.hasClass("with-comment")) {
    403             dialogWrap.css(
    404               "transform",
    405               "translateY(12%) scaleY(0.5) scaleX(0.4)"
    406             );
    407           } else {
    408             dialogWrap.css(
    409               "transform",
    410               "translateY(28%) scaleY(0.5) scaleX(0.45)"
    411             );
    412           }
    413           setTimeout(function () {
    414             letterTop.css("z-index", "9");
    415             letterTop.addClass("close");
    416             setTimeout(function () {
    417               dialogWrap.css({
    418                 visibility: "hidden",
    419                 opacity: "0",
    420               });
    421               letterFront.css("animation", "send-letter1 0.7s");
    422               letterBack.css("animation", "send-letter1 0.7s");
    423               letterTop.css("animation", "send-letter2 0.7s");
    424               setTimeout(function () {
    425                 mixtape.dlg.toggle();
    426               }, 400);
    427             }, 400);
    428           }, 400);
    429         }, 300);
    430       }, 400);
    431     },
    432 
    433     showDialog: function (report) {
    434       if (
    435         report.hasOwnProperty("selection") &&
    436         report.hasOwnProperty("context")
    437       ) {
    438         var $dialog = $(mixtape.dlg.el);
    439 
    440         if ($dialog.data("mode") === "notify") {
    441           mixtape.reportSpellError(report);
    442           mixtape.dlg.toggle();
    443         } else {
    444           $dialog.data("report", report);
    445           $dialog.find("#mixtape_reported_text").html(report.preview_text);
    446           mixtape.dlg.toggle();
    447         }
    448       }
    449     },
    450 
    451     resetDialog: function () {
    452       var $dialog = $(mixtape.dlg.el);
    453 
    454       if ($dialog.data("mode") != "notify") {
     318    },
     319
     320    getSelectionData() {
     321      if (!window.getSelection) return false;
     322
     323      const sel = window.getSelection();
     324      if (sel.isCollapsed) return;
     325
     326      const selChars = sel.toString();
     327      const maxContextLength = 140;
     328
     329      //if (selChars.length > maxContextLength) return;
     330
     331      const parentEl = this._getParentElement(sel);
     332      if (!parentEl) return;
     333
     334      const { context, selWithContext, initialSel, backwards, direction } =
     335        this._prepareSelection(sel, parentEl);
     336      if (!selWithContext) return;
     337
     338      const { selToFindInContext, truncatedContext, previewText } =
     339        this._getContextAndPreviewText(
     340          selWithContext,
     341          context,
     342          maxContextLength,
     343          selChars
     344        );
     345
     346      return {
     347        selection: selChars,
     348        word: selWithContext,
     349        replace_context: selToFindInContext,
     350        context: truncatedContext,
     351        preview_text: previewText,
     352        nonce: $('input[name="mixtape_nonce"]').val(),
     353      };
     354    },
     355
     356    reportSpellError(data) {
     357      data.action = "mixtape_report_error";
     358      $.ajax({
     359        type: "post",
     360        dataType: "json",
     361        url: MixtapeLocalize.ajaxurl,
     362        data: data,
     363      });
     364    },
     365
     366    resetDialog() {
     367      var $dialog = $(this.dlg.el);
     368
     369      if ($dialog.data("mode") !== "notify") {
    455370        $dialog.find("#mixtape_confirm_dialog").css("display", "");
    456371        $dialog.find("#mixtape_success_dialog").remove();
    457372      }
    458373
    459       // letter
    460374      $dialog.find(".dialog__content").removeClass("show-letter");
    461375      $dialog
     
    467381    },
    468382
    469     reportSpellError: function (data) {
    470       data.action = "mixtape_report_error";
    471       $.ajax({
    472         type: "post",
    473         dataType: "json",
    474         url: mixtape.ajaxurl,
    475         data: data,
    476       });
    477     },
    478 
    479     getSelectionData: function () {
    480       // Check for existence of window.getSelection()
    481       if (!window.getSelection) {
    482         return false;
    483       }
    484 
    485       var parentEl,
    486         sel,
     383    showDialog(report) {
     384      if (report.selection && report.context) {
     385        var $dialog = $(this.dlg.el);
     386
     387        if ($dialog.data("mode") === "notify") {
     388          this.reportSpellError(report);
     389          this.dlg.toggle();
     390        } else {
     391          $dialog.data("report", report);
     392          $dialog.find("#mixtape_reported_text").html(report.preview_text);
     393          this.dlg.toggle();
     394        }
     395      }
     396    },
     397
     398    animateLetter() {
     399      var dialog = $(this.dlg.el),
     400        content = dialog.find(".dialog__content"),
     401        letterTop = dialog.find(".mixtape-letter-top"),
     402        letterFront = dialog.find(".mixtape-letter-front"),
     403        letterBack = dialog.find(".mixtape-letter-back"),
     404        dialogWrap = dialog.find(".dialog-wrap");
     405
     406      content.addClass("show-letter");
     407
     408      setTimeout(function () {
     409        var y =
     410          letterTop.offset().top -
     411          letterFront.offset().top +
     412          letterTop.outerHeight();
     413        letterTop.css({ bottom: Math.floor(y), opacity: 1 });
     414        jQuery(".mixtape-letter-back-top").hide();
     415        var scaleX = content.hasClass("with-comment") ? 0.28 : 0.4;
     416        dialogWrap.css("transform", `scaleY(0.5) scaleX(${scaleX})`);
     417        setTimeout(function () {
     418          var translateY = content.hasClass("with-comment") ? "12%" : "28%";
     419          dialogWrap.css(
     420            "transform",
     421            `translateY(${translateY}) scaleY(0.5) scaleX(${scaleX + 0.05})`
     422          );
     423          setTimeout(function () {
     424            letterTop.css("z-index", "9").addClass("close");
     425            setTimeout(function () {
     426              dialogWrap.css({ visibility: "hidden", opacity: "0" });
     427              letterFront.add(letterBack).css("animation", "send-letter1 0.7s");
     428              letterTop.css("animation", "send-letter2 0.7s");
     429              setTimeout(function () {
     430                Mixtape.dlg.toggle();
     431              }, 400);
     432            }, 400);
     433          }, 400);
     434        }, 300);
     435      }, 400);
     436    },
     437
     438    initDialogFx() {
     439      this.dlg = new DialogFx(document.getElementById("mixtape_dialog"), {
     440        onOpenDialog: function (dialog) {
     441          $(dialog.el).css("display", "flex");
     442        },
     443        onCloseAnimationEnd: function (dialog) {
     444          $(dialog.el).css("display", "none");
     445          Mixtape.resetDialog();
     446        },
     447      });
     448    },
     449
     450    _getParentElement(sel) {
     451      if (sel.rangeCount) {
     452        let parentEl = sel.getRangeAt(0).commonAncestorContainer;
     453        while (parentEl && parentEl.nodeType !== 1) {
     454          parentEl = parentEl.parentNode;
     455        }
     456        return parentEl;
     457      }
     458      return null;
     459    },
     460
     461    _prepareSelection(sel, parentEl) {
     462      const selChars = sel.toString().trim();
     463      console.log('Initial Selection Chars:', selChars); // Логування початкового виділення
     464   
     465      if (!selChars) {
     466        return {
     467          context: '',
     468          selWithContext: '',
     469          initialSel: {},
     470          backwards: false,
     471          direction: {}
     472        };
     473      }
     474   
     475      const direction = this._determineDirection(sel);
     476      console.log('Direction:', direction); // Логування напрямку
     477   
     478      const initialSel = this._saveInitialSelection(sel);
     479      console.log('Initial Selection:', initialSel); // Логування початкового стану виділення
     480   
     481      const context = this._getContext(parentEl);
     482      console.log('Context:', context); // Логування контексту
     483   
     484      this._extendSelection(sel, direction, context, selChars);
     485   
     486      const selWithContext = sel.toString().trim();
     487      console.log('Selection with Context:', selWithContext); // Логування виділення з контекстом
     488   
     489      return {
     490        context,
     491        selWithContext,
     492        initialSel,
     493        backwards: direction.backwards,
     494        direction,
     495      };
     496    },
     497
     498    _determineDirection(sel) {
     499      const range = document.createRange();
     500      range.setStart(sel.anchorNode, sel.anchorOffset);
     501      range.setEnd(sel.focusNode, sel.focusOffset);
     502      const backwards = range.collapsed;
     503      range.detach();
     504      return {
     505        forward: backwards ? "backward" : "forward",
     506        backward: backwards ? "forward" : "backward",
     507        backwards,
     508      };
     509    },
     510
     511    _saveInitialSelection(sel) {
     512      return {
     513        focusNode: sel.focusNode,
     514        focusOffset: sel.focusOffset,
     515        anchorNode: sel.anchorNode,
     516        anchorOffset: sel.anchorOffset,
     517      };
     518    },
     519
     520    _getContext(element) {
     521      const context = element ? element.textContent.trim() : '';
     522      console.log('Element Context:', context); // Логування контексту елемента
     523      return this._stringifyContent(context);
     524    },
     525
     526    _extendSelection(sel, direction, context, selChars) {
     527      console.log('Before Extend - Selection:', sel.toString(), 'Direction:', direction); // Логування перед розширенням
     528   
     529      // Зберігаємо початкове положення виділення
     530      const initialRange = sel.getRangeAt(0).cloneRange();
     531   
     532      // Розширюємо виділення вперед
     533      sel.modify("extend", direction.forward, "character");
     534   
     535      if (!/\w/.test(selChars.charAt(0))) {
     536        sel.modify("extend", direction.forward, "character");
     537      }
     538   
     539      sel.modify("extend", direction.backward, "word");
     540   
     541      if (!/\w/.test(selChars.charAt(selChars.length - 1))) {
     542        sel.modify("extend", direction.backward, "character");
     543      }
     544   
     545      const extendedSelection = sel.toString();
     546      console.log('After Extend - Selection:', extendedSelection); // Логування після розширення
     547   
     548      // Якщо розширення виділення не дало результату, відновлюємо початкове виділення
     549      if (extendedSelection.trim() === '') {
     550        sel.removeAllRanges();
     551        sel.addRange(initialRange);
     552      }
     553    },
     554
     555    _getContextAndPreviewText(
     556      selWithContext,
     557      context,
     558      maxContextLength,
     559      selChars
     560    ) {
     561      const selPos = this._getExactSelPos(selWithContext, context);
     562      let truncatedContext = context;
     563
     564      if (context.length > maxContextLength) {
     565        truncatedContext = this._truncateContext(
     566          context,
     567          selPos,
     568          selWithContext.length,
     569          maxContextLength
     570        );
     571      }
     572
     573      const selWithContextHighlighted = selWithContext.replace(
    487574        selChars,
    488         selWord,
    489         textToHighlight,
    490         maxContextLength = 140;
    491 
    492       var stringifyContent = function (string) {
    493         return typeof string === "string"
    494           ? string
    495               .replace(/\s*(?:(?:\r\n)+|\r+|\n+)\t*/gm, "\r\n")
    496               .replace(/\s{2,}/gm, " ")
    497           : "";
     575        `<span class="mixtape_mistake_inner">${selChars}</span>`
     576      );
     577
     578      const previewText = truncatedContext.replace(
     579        selWithContext,
     580        selWithContextHighlighted
     581      );
     582      return {
     583        selToFindInContext: selWithContext,
     584        truncatedContext,
     585        previewText,
    498586      };
    499 
    500       var isSubstrUnique = function (substr, context) {
    501         if (typeof context === "undefined") {
    502           context = mixtape.contextBuffer;
    503         }
    504         if (typeof substr === "undefined") {
    505           substr = mixtape.selBuffer;
    506         }
    507         var split = context.split(substr);
    508         var count = split.length - 1;
    509         return count === 1;
    510       };
    511 
    512       var getExactSelPos = function (selection, context) {
    513         // if there is only one match, that's it
    514         if (isSubstrUnique(selWithContext, context)) {
    515           return context.indexOf(selWithContext);
    516         }
    517         // check if we can get the occurrence match from selection offsets
    518         if (!backwards) {
    519           // check anchor element
    520           if (
    521             context.substring(
    522               sel.anchorOffset,
    523               sel.anchorOffset + selection.length
    524             ) == selection
    525           ) {
    526             return sel.anchorOffset;
    527           }
    528           // check anchor parent element
    529           var parentElOffset = sel.anchorOffset;
    530           var prevEl = sel.anchorNode.previousSibling;
    531           while (prevEl !== null) {
    532             parentElOffset += prevEl.textContent.length;
    533             prevEl = prevEl.previousSibling;
    534           }
    535           if (
    536             context.substring(
    537               parentElOffset,
    538               parentElOffset + selection.length
    539             ) == selection
    540           ) {
    541             return parentElOffset;
    542           }
    543         }
    544         if (
    545           backwards &&
    546           context.substring(
    547             sel.focusOffset,
    548             sel.focusOffset + selection.length
    549           ) == selection
    550         ) {
    551           return sel.anchorOffset;
    552         }
    553         return -1;
    554       };
    555 
    556       var getExtendedSelection = function (limit, nodeExtensions) {
    557         limit = parseInt(limit) || 40;
    558         nodeExtensions = nodeExtensions || { left: "", right: "" };
    559         var i = 0,
    560           selContent,
    561           selEndNode = sel.focusNode,
    562           selEndOffset = sel.focusOffset;
    563 
    564         while (i <= limit) {
    565           if (
    566             (selContent = stringifyContent(sel.toString().trim())).length >=
    567               maxContextLength ||
    568             isSubstrUnique(selContent, context)
    569           ) {
    570             return selContent;
    571           }
    572 
    573           // only even iteration
    574           if (
    575             (i % 2 == 0 && sel.anchorOffset > 0) ||
    576             (nodeExtensions.left.length && i < limit / 2)
    577           ) {
    578             // reset
    579             if (backwards) {
    580               sel.collapseToEnd();
    581             } else {
    582               sel.collapseToStart();
    583             }
    584             sel.modify("move", direction[1], "character");
    585             sel.extend(selEndNode, selEndOffset);
    586           } else if (
    587             sel.focusOffset < sel.focusNode.length ||
    588             (nodeExtensions.right.length && i < limit / 2)
    589           ) {
    590             sel.modify("extend", direction[0], "character");
    591             if (sel.focusOffset === 1) {
    592               selEndNode = sel.focusNode;
    593               selEndOffset = sel.focusOffset;
    594             }
    595           } else if (i % 2 === 0) {
    596             break;
    597           }
    598 
    599           i++;
    600         }
    601 
    602         return stringifyContent(sel.toString().trim());
    603       };
    604 
    605       var getExtendedContext = function (context, element, method) {
    606         var contentPrepend = "",
    607           contentAppend = "",
    608           e = element,
    609           i;
    610         method = method || "textContent";
    611 
    612         for (i = 0; i < 20; i++) {
    613           if (contentPrepend || (e = e.previousSibling) === null) {
    614             break;
    615           }
    616 
    617           if ((contentPrepend = stringifyContent(e[method].trim())).length) {
    618             context = contentPrepend + context;
    619           }
    620         }
    621 
    622         // reset element
    623         e = element;
    624 
    625         for (i = 0; i < 20; i++) {
    626           if (contentAppend || (e = e.nextSibling) === null) {
    627             break;
    628           }
    629           if ((contentAppend = stringifyContent(e[method]).trim()).length) {
    630             context += contentAppend;
    631           } else if (context.slice(-1) !== " ") {
    632             context += " ";
    633           }
    634         }
    635 
    636         return {
    637           contents: context,
    638           extensions: {
    639             left: contentPrepend,
    640             right: contentAppend,
    641           },
    642         };
    643       };
    644 
    645       // check that getSelection() has a modify() method. IE has both selection APIs but no modify() method.
    646       // this works on modern browsers following standards
    647       if ((sel = window.getSelection()).modify) {
    648         // check if there is any text selected
    649         if (!sel.isCollapsed) {
    650           /**
    651            * So the first step is to get selection extended to the boundaries of words
    652            *
    653            * e.g. if the sentence is "What a wonderful life!" and selection is "rful li",
    654            * we get "wonderful life" stored in selWord variable
    655            */
    656 
    657           selChars = sel.toString();
    658 
    659           // return early if no selection to work with or if its length exceeds the limit
    660           if (!selChars || selChars.length > maxContextLength) {
    661             return;
    662           }
    663 
    664           // here we get the nearest parent node which is common for the whole selection
    665           if (sel.rangeCount) {
    666             parentEl = sel.getRangeAt(0).commonAncestorContainer.parentNode;
    667             while (parentEl.textContent == sel.toString()) {
    668               parentEl = parentEl.parentNode;
    669             }
    670           }
    671 
    672           // Detect if selection was made backwards
    673           // further logic depends on it
    674           var range = document.createRange();
    675           range.setStart(sel.anchorNode, sel.anchorOffset);
    676           range.setEnd(sel.focusNode, sel.focusOffset);
    677           var backwards = range.collapsed;
    678           range = null;
    679 
    680           // save initial selection to restore in the end
    681           var initialSel = {
    682             focusNode: sel.focusNode,
    683             focusOffset: sel.focusOffset,
    684             anchorNode: sel.anchorNode,
    685             anchorOffset: sel.anchorOffset,
    686           };
    687 
    688           // modify() works on the focus of the selection (not virtually) so we manipulate it
    689           var endNode = sel.focusNode,
    690             endOffset = sel.focusOffset;
    691 
    692           // determine second char of selection and the one before last
    693           // they will be our starting point for word boundaries detection
    694           var direction, secondChar, oneBeforeLastChar;
    695           if (backwards) {
    696             direction = ["backward", "forward"];
    697             secondChar = selChars.charAt(selChars.length - 1);
    698             oneBeforeLastChar = selChars.charAt(0);
    699           } else {
    700             direction = ["forward", "backward"];
    701             secondChar = selChars.charAt(0);
    702             oneBeforeLastChar = selChars.charAt(selChars.length - 1);
    703           }
    704 
    705           // collapse the cursor to the first char
    706           sel.collapse(sel.anchorNode, sel.anchorOffset);
    707           // move it one char forward
    708           sel.modify("move", direction[0], "character");
    709 
    710           // if the second character was a letter or digit, move cursor another step further
    711           // this way we are certain that we are in the middle of the word
    712           if (null === secondChar.match(/'[\w\d]'/)) {
    713             sel.modify("move", direction[0], "character");
    714           }
    715 
    716           // and now we can determine the beginning position of the word
    717           sel.modify("move", direction[1], "word");
    718 
    719           // then extend the selection up to the initial point
    720           // thus assure that selection starts with the beginning of the word
    721           sel.extend(endNode, endOffset);
    722 
    723           // do the same trick with the ending--extending it precisely up to the end of the word
    724           sel.modify("extend", direction[1], "character");
    725           if (null === oneBeforeLastChar.match(/'[\w\d]'/)) {
    726             sel.modify("extend", direction[1], "character");
    727           }
    728           sel.modify("extend", direction[0], "word");
    729           if (!backwards && sel.focusOffset === 1) {
    730             sel.modify("extend", "backward", "character");
    731           }
    732 
    733           // since different browser extend by "word" differently and some of them extend beyond the word
    734           // covering spaces and punctuation, we need to collapse the selection back so it ends with the word
    735           var i = 0,
    736             lengthBefore,
    737             lengthAfter;
    738           while (
    739             i < 5 &&
    740             (
    741               sel
    742                 .toString()
    743                 .slice(-1)
    744                 .match(/[\s\n\t]/) || ""
    745             ).length
    746           ) {
    747             lengthBefore = sel.toString().length;
    748             if (backwards) {
    749               endNode =
    750                 sel.anchorOffset == 0
    751                   ? sel.anchorNode.previousSibling
    752                   : sel.anchorNode;
    753               endOffset =
    754                 sel.anchorOffset == 0
    755                   ? sel.anchorNode.previousSibling.length
    756                   : sel.anchorOffset;
    757               sel.modify("move", "backward", "character");
    758               sel.extend(endNode, endOffset);
    759               backwards = false;
    760               direction = ["forward", "backward"];
    761             } else {
    762               sel.modify("extend", "backward", "character");
    763             }
    764             lengthAfter = sel.toString().length;
    765 
    766             // workaround for WebKit quirk: undo last iteration
    767             if (lengthBefore - lengthAfter > 1) {
    768               sel.modify("extend", "forward", "character");
    769               break;
    770             }
    771           }
    772 
    773           // finally, we've got a modified selection which is bound to words
    774           // save it to highlight it later
    775           selWord = stringifyContent(sel.toString().trim());
    776         }
    777       }
    778       // this one is for IE11
    779       else if ((sel = window.getSelection())) {
    780         var startOffset, startNode, endNode;
    781         selChars = sel.toString();
    782         range = document.createRange();
    783         if (range.collapsed) {
    784           startNode = sel.focusNode;
    785           endNode = sel.anchorNode;
    786           startOffset = sel.focusOffset;
    787           endOffset = sel.anchorOffset;
    788         } else {
    789           startNode = sel.anchorNode;
    790           endNode = sel.focusNode;
    791           startOffset = sel.anchorOffset;
    792           endOffset = sel.focusOffset;
    793         }
    794 
    795         while (
    796           startOffset &&
    797           !startNode.textContent
    798             .slice(startOffset - 1, startOffset)
    799             .match(/[\s\n\t]/)
    800         ) {
    801           startOffset--;
    802         }
    803         while (
    804           endOffset < endNode.length &&
    805           !endNode.textContent.slice(endOffset, endOffset + 1).match(/[\s\n\t]/)
    806         ) {
    807           endOffset++;
    808         }
    809 
    810         // here we get the nearest parent node which is common for the whole selection
    811         if (sel.rangeCount) {
    812           parentEl = sel.getRangeAt(0).commonAncestorContainer.parentNode;
    813           while (parentEl.textContent == sel.toString()) {
    814             parentEl = parentEl.parentNode;
    815           }
    816         }
    817 
    818         selWord = stringifyContent(sel.toString().trim());
    819 
    820         // this logic is for IE<10
    821         // } else if ((sel = document.selection) && sel.type != "Control") {
    822         //     var textRange = sel.createRange();
    823         //
    824         //      if (!textRange || textRange.text.length > maxContextLength) {
    825         //      return;
    826         //      }
    827         //
    828         //      if (textRange.text) {
    829         //      selChars = textRange.text;
    830         //      textRange.expand("word");
    831         //      // Move the end back to not include the word's trailing space(s), if necessary
    832         //      while (/\s$/.test(textRange.text)) {
    833         //      textRange.moveEnd("character", -1);
    834         //      }
    835         //      selWord = textRange.text;
    836         //      parentEl = textRange.parentNode;
    837         //      }
    838       }
    839 
    840       if (typeof parentEl == "undefined") {
    841         return;
    842       }
    843 
    844       var selToFindInContext,
    845         contextsToCheck = {
    846           // different browsers implement different methods, we try them by turn
    847           textContent: parentEl.textContent,
    848           innerText: parentEl.innerText,
    849         };
    850 
    851       textToHighlight = selWord;
    852 
    853       for (var method in contextsToCheck) {
    854         if (
    855           contextsToCheck.hasOwnProperty(method) &&
    856           typeof contextsToCheck[method] != "undefined"
    857         ) {
    858           // start with counting selected word occurrences in context
    859           var scope = { selection: "word", context: "initial" };
    860           var context = stringifyContent(contextsToCheck[method].trim());
    861           var selWithContext = stringifyContent(sel.toString().trim());
    862           mixtape.contextBuffer = context;
    863           mixtape.selBuffer = selWithContext;
    864           var selPos; // this is what we are going to find
    865           var selExactMatch = false;
    866 
    867           if ((selPos = getExactSelPos(selWithContext, context)) != -1) {
    868             selExactMatch = true;
    869             selToFindInContext = selWithContext;
    870             break;
    871           }
    872 
    873           // if there is more than one occurrence, extend the selection
    874           selWithContext = getExtendedSelection(40);
    875           scope.selection = "word extended";
    876 
    877           if ((selPos = getExactSelPos(selWithContext, context)) != -1) {
    878             selExactMatch = true;
    879             selToFindInContext = selWithContext;
    880             break;
    881           }
    882 
    883           // if still have duplicates, extend the context and selection, and try again
    884           var initialContext = context;
    885           var extContext = getExtendedContext(context, parentEl, method);
    886           context = extContext.contents;
    887           selWithContext = getExtendedSelection(40, extContext.extensions);
    888           scope.context = "extended";
    889 
    890           if ((selPos = getExactSelPos(selWithContext, context)) != -1) {
    891             selExactMatch = true;
    892             selToFindInContext = selWithContext;
    893             break;
    894           }
    895 
    896           // skip to next context getting method and start over, or exit
    897           if (!selWithContext) {
    898             continue;
    899           }
    900 
    901           if (
    902             isSubstrUnique(selWord, selWithContext) ||
    903             selWord == selChars.trim()
    904           ) {
    905             context = selWithContext;
    906             selWithContext = selWord;
    907             textToHighlight = selWord;
    908             scope.selection = "word";
    909             scope.context = "extended";
    910           } else {
    911             context = selWord;
    912             selWithContext = selChars.trim();
    913             textToHighlight = selChars.trim();
    914             scope.selection = "initial";
    915             scope.context = "word";
    916           }
    917 
    918           selPos = context.indexOf(selWithContext);
    919 
    920           if (selPos !== -1) {
    921             selToFindInContext = selWithContext;
    922           } else if ((selPos = context.indexOf(selWord)) !== -1) {
    923             selToFindInContext = selWord;
    924           } else if ((selPos = context.indexOf(selChars)) !== -1) {
    925             selToFindInContext = selChars;
    926           } else {
    927             continue;
    928           }
    929           break;
    930         }
    931       }
    932 
    933       if (selToFindInContext) {
    934         sel.removeAllRanges();
    935       } else {
    936         mixtape.restoreInitSelection(sel, initialSel);
    937         return;
    938       }
    939 
    940       if (scope.context === "extended") {
    941         context =
    942           extContext.extensions.left +
    943           initialContext +
    944           " " +
    945           extContext.extensions.right;
    946       }
    947 
    948       var contExcerptStartPos,
    949         contExcerptEndPos,
    950         selPosInContext,
    951         highlightedChars,
    952         previewText;
    953       maxContextLength = Math.min(context.length, maxContextLength);
    954 
    955       var truncatedContext = context;
    956 
    957       if (context.length > maxContextLength) {
    958         if (selPos + selToFindInContext.length / 2 < maxContextLength / 2) {
    959           selPosInContext = "beginning";
    960           contExcerptStartPos = 0;
    961           contExcerptEndPos = Math.max(
    962             selPos + selToFindInContext.length,
    963             context.indexOf(" ", maxContextLength - 10)
    964           );
    965         } else if (
    966           selPos + selToFindInContext.length / 2 >
    967           context.length - maxContextLength / 2
    968         ) {
    969           selPosInContext = "end";
    970           contExcerptStartPos = Math.min(
    971             selPos,
    972             context.indexOf(" ", context.length - maxContextLength + 10)
    973           );
    974           contExcerptEndPos = context.length;
    975         } else {
    976           selPosInContext = "middle";
    977           var centerPos = selPos + Math.round(selToFindInContext.length / 2);
    978           contExcerptStartPos = Math.min(
    979             selPos,
    980             context.indexOf(" ", centerPos - maxContextLength / 2 - 10)
    981           );
    982           contExcerptEndPos = Math.max(
    983             selPos + selToFindInContext.length,
    984             context.indexOf(" ", centerPos + maxContextLength / 2 - 10)
    985           );
    986         }
    987 
    988         truncatedContext = context
    989           .substring(contExcerptStartPos, contExcerptEndPos)
    990           .trim();
    991 
    992         if (
    993           selPosInContext !== "beginning" &&
    994           context.charAt(contExcerptStartPos - 1) !== "."
    995         ) {
    996           truncatedContext = "... " + truncatedContext;
    997         }
    998         if (
    999           selPosInContext !== "end" &&
    1000           context.charAt(contExcerptStartPos + contExcerptEndPos - 1) !== "."
    1001         ) {
    1002           truncatedContext = truncatedContext + " ...";
    1003         }
    1004       }
    1005 
    1006       if (isSubstrUnique(selChars, textToHighlight)) {
    1007         highlightedChars = textToHighlight.replace(
    1008           selChars,
    1009           '<span class="mixtape_mistake_inner">' + selChars + "</span>"
    1010         );
    1011       } else {
    1012         highlightedChars =
    1013           '<strong class="mixtape_mistake_inner">' +
    1014           textToHighlight +
    1015           "</strong>";
    1016       }
    1017 
    1018       var selWithContextHighlighted = selToFindInContext.replace(
    1019         textToHighlight,
    1020         '<span class="mixtape_mistake_outer">' + highlightedChars + "</span>"
     587    },
     588
     589    _getExactSelPos(selection, context) {
     590      return context.indexOf(selection);
     591    },
     592
     593    _truncateContext(context, selPos, selLength, maxContextLength) {
     594      let start = Math.max(0, selPos - Math.floor(maxContextLength / 2));
     595      let end = Math.min(context.length, start + maxContextLength);
     596
     597      if (start > 0) {
     598        start = context.lastIndexOf(" ", start) + 1;
     599      }
     600      if (end < context.length) {
     601        end = context.indexOf(" ", end);
     602      }
     603
     604      return (
     605        (start > 0 ? "..." : "") +
     606        context.slice(start, end) +
     607        (end < context.length ? "..." : "")
    1021608      );
    1022 
    1023       if (selExactMatch && truncatedContext === context) {
    1024         previewText =
    1025           truncatedContext.substring(0, selPos) +
    1026             selWithContextHighlighted +
    1027             truncatedContext.substring(selPos + selWithContext.length) ||
    1028           selWithContextHighlighted;
    1029       } else {
    1030         previewText =
    1031           truncatedContext.replace(selWithContext, selWithContextHighlighted) ||
    1032           selWithContextHighlighted;
    1033       }
    1034 
    1035       return {
    1036         selection: selChars,
    1037         word: selWord,
    1038         replace_context: selToFindInContext,
    1039         context: truncatedContext,
    1040         preview_text: previewText,
    1041         nonce: $('input[name="mixtape_nonce"]').val(),
    1042         // post_id: mixtape.getPostId()
    1043       };
    1044     },
    1045 
    1046     restoreInitSelection: function (sel, initialSel) {
     609    },
     610
     611    _stringifyContent(string) {
     612      return typeof string === "string"
     613        ? string.replace(/\s+/g, " ").trim()
     614        : "";
     615    },
     616
     617    restoreInitSelection(sel, initialSel) {
    1047618      sel.collapse(initialSel.anchorNode, initialSel.anchorOffset);
    1048619      sel.extend(initialSel.focusNode, initialSel.focusOffset);
    1049620    },
    1050621
    1051     showReportButton: function () {
    1052       if (!reportButton) {
    1053         reportButton = $("<button>")
    1054           .text(mixtape.reportError)
     622    showReportButton() {
     623      if (!this.reportButton) {
     624        this.reportButton = $("<button>")
     625          .text(MixtapeLocalize.reportError)
    1055626          .addClass("mixtape-report-button")
    1056           .on("click", function () {
    1057             const report = mixtape.getSelectionData();
     627          .on("click", () => {
     628            const report = Mixtape.getSelectionData();
    1058629            if (report) {
    1059               mixtape.showDialog(report);
     630              this.showDialog(report);
     631            } else {
     632              console.error("No report generated");
    1060633            }
    1061634          });
    1062635
    1063         $("body").append(reportButton);
     636        $("body").append(this.reportButton);
    1064637      }
    1065638
    1066639      const selection = window.getSelection();
     640      if (selection.rangeCount === 0) {
     641        console.error("No selection range found");
     642        return;
     643      }
     644
    1067645      const range = selection.getRangeAt(0);
    1068646      const rect = range.getBoundingClientRect();
    1069 
    1070       const buttonHeight = reportButton.outerHeight();
    1071 
     647      const buttonHeight = this.reportButton.outerHeight();
    1072648      const topPosition = rect.top + window.scrollY - 20 - buttonHeight;
    1073649
    1074       reportButton.css({
    1075         top: topPosition + "px",
    1076         left: rect.left + window.scrollX + "px",
     650      this.reportButton.css({
     651        top: `${topPosition}px`,
     652        left: `${rect.left + window.scrollX}px`,
    1077653        position: "absolute",
    1078654      });
    1079655    },
    1080656
    1081     hideReportButton: function () {
    1082       if (reportButton) {
    1083         reportButton.remove();
    1084         reportButton = null;
    1085       }
    1086     },
     657    hideReportButton() {
     658      if (this.reportButton) {
     659        this.reportButton.remove();
     660        this.reportButton = null;
     661      }
     662    },
     663
     664    init() {
     665      this.onReady();
     666    },
     667  };
     668
     669  $(document).ready(function () {
     670    Mixtape.init();
    1087671  });
    1088 
    1089   $(document).ready(mixtape.onReady);
    1090 })(jQuery);
     672});
  • mixtape/trunk/package-lock.json

    r2994803 r3109820  
    99      "version": "1.0.0",
    1010      "devDependencies": {
     11        "@playwright/test": "^1.45.0",
     12        "@types/node": "^20.14.9",
    1113        "gulp": "^4.0.2",
    1214        "gulp-phpcs": "^3.1.0",
    1315        "gulp-wp-pot": "^2.5.0"
     16      }
     17    },
     18    "node_modules/@playwright/test": {
     19      "version": "1.45.0",
     20      "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.45.0.tgz",
     21      "integrity": "sha512-TVYsfMlGAaxeUllNkywbwek67Ncf8FRGn8ZlRdO291OL3NjG9oMbfVhyP82HQF0CZLMrYsvesqoUekxdWuF9Qw==",
     22      "dev": true,
     23      "dependencies": {
     24        "playwright": "1.45.0"
     25      },
     26      "bin": {
     27        "playwright": "cli.js"
     28      },
     29      "engines": {
     30        "node": ">=18"
     31      }
     32    },
     33    "node_modules/@types/node": {
     34      "version": "20.14.9",
     35      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.9.tgz",
     36      "integrity": "sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==",
     37      "dev": true,
     38      "dependencies": {
     39        "undici-types": "~5.26.4"
    1440      }
    1541    },
     
    28592885      }
    28602886    },
     2887    "node_modules/playwright": {
     2888      "version": "1.45.0",
     2889      "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.45.0.tgz",
     2890      "integrity": "sha512-4z3ac3plDfYzGB6r0Q3LF8POPR20Z8D0aXcxbJvmfMgSSq1hkcgvFRXJk9rUq5H/MJ0Ktal869hhOdI/zUTeLA==",
     2891      "dev": true,
     2892      "dependencies": {
     2893        "playwright-core": "1.45.0"
     2894      },
     2895      "bin": {
     2896        "playwright": "cli.js"
     2897      },
     2898      "engines": {
     2899        "node": ">=18"
     2900      },
     2901      "optionalDependencies": {
     2902        "fsevents": "2.3.2"
     2903      }
     2904    },
     2905    "node_modules/playwright-core": {
     2906      "version": "1.45.0",
     2907      "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.0.tgz",
     2908      "integrity": "sha512-lZmHlFQ0VYSpAs43dRq1/nJ9G/6SiTI7VPqidld9TDefL9tX87bTKExWZZUF5PeRyqtXqd8fQi2qmfIedkwsNQ==",
     2909      "dev": true,
     2910      "bin": {
     2911        "playwright-core": "cli.js"
     2912      },
     2913      "engines": {
     2914        "node": ">=18"
     2915      }
     2916    },
     2917    "node_modules/playwright/node_modules/fsevents": {
     2918      "version": "2.3.2",
     2919      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
     2920      "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
     2921      "dev": true,
     2922      "hasInstallScript": true,
     2923      "optional": true,
     2924      "os": [
     2925        "darwin"
     2926      ],
     2927      "engines": {
     2928        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
     2929      }
     2930    },
    28612931    "node_modules/plugin-error": {
    28622932      "version": "1.0.1",
     
    38203890        "node": ">= 0.10"
    38213891      }
     3892    },
     3893    "node_modules/undici-types": {
     3894      "version": "5.26.5",
     3895      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
     3896      "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
     3897      "dev": true
    38223898    },
    38233899    "node_modules/union-value": {
     
    41414217  },
    41424218  "dependencies": {
     4219    "@playwright/test": {
     4220      "version": "1.45.0",
     4221      "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.45.0.tgz",
     4222      "integrity": "sha512-TVYsfMlGAaxeUllNkywbwek67Ncf8FRGn8ZlRdO291OL3NjG9oMbfVhyP82HQF0CZLMrYsvesqoUekxdWuF9Qw==",
     4223      "dev": true,
     4224      "requires": {
     4225        "playwright": "1.45.0"
     4226      }
     4227    },
     4228    "@types/node": {
     4229      "version": "20.14.9",
     4230      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.9.tgz",
     4231      "integrity": "sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==",
     4232      "dev": true,
     4233      "requires": {
     4234        "undici-types": "~5.26.4"
     4235      }
     4236    },
    41434237    "acorn": {
    41444238      "version": "8.10.0",
     
    64026496      }
    64036497    },
     6498    "playwright": {
     6499      "version": "1.45.0",
     6500      "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.45.0.tgz",
     6501      "integrity": "sha512-4z3ac3plDfYzGB6r0Q3LF8POPR20Z8D0aXcxbJvmfMgSSq1hkcgvFRXJk9rUq5H/MJ0Ktal869hhOdI/zUTeLA==",
     6502      "dev": true,
     6503      "requires": {
     6504        "fsevents": "2.3.2",
     6505        "playwright-core": "1.45.0"
     6506      },
     6507      "dependencies": {
     6508        "fsevents": {
     6509          "version": "2.3.2",
     6510          "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
     6511          "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
     6512          "dev": true,
     6513          "optional": true
     6514        }
     6515      }
     6516    },
     6517    "playwright-core": {
     6518      "version": "1.45.0",
     6519      "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.0.tgz",
     6520      "integrity": "sha512-lZmHlFQ0VYSpAs43dRq1/nJ9G/6SiTI7VPqidld9TDefL9tX87bTKExWZZUF5PeRyqtXqd8fQi2qmfIedkwsNQ==",
     6521      "dev": true
     6522    },
    64046523    "plugin-error": {
    64056524      "version": "1.0.1",
     
    71867305      "dev": true
    71877306    },
     7307    "undici-types": {
     7308      "version": "5.26.5",
     7309      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
     7310      "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
     7311      "dev": true
     7312    },
    71887313    "union-value": {
    71897314      "version": "1.0.1",
  • mixtape/trunk/package.json

    r2994803 r3109820  
    44  "scripts": {
    55    "make-pot": "gulp make-pot",
    6     "phpcs": "gulp phpcs"
     6    "phpcs": "gulp phpcs",
     7    "test": "npx playwright test --ui"
    78  },
    89  "devDependencies": {
     10    "@playwright/test": "^1.45.0",
     11    "@types/node": "^20.14.9",
    912    "gulp": "^4.0.2",
    1013    "gulp-phpcs": "^3.1.0",
  • mixtape/trunk/readme.md

    r2998174 r3109820  
    77**License:** GPLv2 or later 
    88**Contributors:** natata7 
    9 **Stable tag:** 1.
     9**Stable tag:** 1.2
    1010**License URI:** <http://www.gnu.org/licenses/gpl-2.0.html>
    1111
     
    33331. Go to Settings > Mixtape and set options.
    3434
     35### 1.2 ###
     36
     37* Fix: sending reports on front on some cases.
     38
    3539### 1.1 ###
    3640
  • mixtape/trunk/readme.txt

    r2998174 r3109820  
    55Tested up to: 6.4.1
    66Requires PHP: 7.4
    7 Stable tag: 1.1
     7Stable tag: 1.2
    88Contributors: natata7
    99License: GPLv2 or later
     
    28282. Activate and follow the settings link in the notice you will see at the top. Tick desired checkboxes, save, and that's it!
    2929
     30== 1.2 ==
     31
     32* Fix: sending reports on front on some cases.
     33
    3034== 1.1 ==
    3135
  • mixtape/trunk/src/class-mixtape-abstract.php

    r2998174 r3109820  
    301301
    302302        // modernizer
    303         wp_enqueue_script( 'modernizr', plugins_url( 'assets/js/modernizr.custom.js', self::$plugin_path ), array( 'jquery' ), self::$version, true );
     303        //wp_enqueue_script( 'modernizr', plugins_url( 'assets/js/modernizr.custom.js', self::$plugin_path ), array( 'jquery' ), self::$version, true );
    304304
    305305        // frontend script (combined)
     
    309309            array(
    310310                'jquery',
    311                 'modernizr',
     311                //'modernizr',
    312312            ),
    313313            filemtime( plugin_dir_path( self::$plugin_path ) . '/assets/js/mixtape-front.js' ),
     
    318318            'reportError' => __( 'Report an error', 'mixtape' ),
    319319        );
    320         wp_localize_script( 'mixtape-front', 'mixtape', $localized_data );
     320        wp_localize_script( 'mixtape-front', 'MixtapeLocalize', $localized_data );
    321321    }
    322322
Note: See TracChangeset for help on using the changeset viewer.