Spacing.js

A JavaScript utility for measuring the spacing between elements on webpage.

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

// ==UserScript==
// @name         Spacing.js
// @namespace    https://github.com/stevenlei
// @version      1.0.5
// @description  A JavaScript utility for measuring the spacing between elements on webpage.
// @author       Steven Lei <[email protected]>
// @run-at       document-start
// @include      *
// @license      MIT
// ==/UserScript==
'use strict';
/*!
 * Spacing.js v1.0.5
 * Copyright (c) 2021 Steven Lei
 * Released under the MIT License.
*/
/******/ (() => { // webpackBootstrap
/******/ 	"use strict";
/******/ 	// The require scope
/******/ 	var __webpack_require__ = {};
/******/ 	
/************************************************************************/
/******/ 	/* webpack/runtime/make namespace object */
/******/ 	(() => {
/******/ 		// define __esModule on exports
/******/ 		__webpack_require__.r = (exports) => {
/******/ 			if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ 				Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ 			}
/******/ 			Object.defineProperty(exports, '__esModule', { value: true });
/******/ 		};
/******/ 	})();
/******/ 	
/************************************************************************/
var __webpack_exports__ = {};
// ESM COMPAT FLAG
__webpack_require__.r(__webpack_exports__);

;// CONCATENATED MODULE: ./src/rect.ts
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

var Rect = /*#__PURE__*/function () {
  function Rect(rect) {
    _classCallCheck(this, Rect);

    this.top = rect.top;
    this.left = rect.left;
    this.width = rect.width;
    this.height = rect.height;
    this.right = rect.right;
    this.bottom = rect.bottom;
  }

  _createClass(Rect, [{
    key: "colliding",
    value: function colliding(other) {
      return !(this.top > other.bottom || this.right < other.left || this.bottom < other.top || this.left > other.right);
    }
  }, {
    key: "containing",
    value: function containing(other) {
      return this.left <= other.left && other.left < this.width && this.top <= other.top && other.top < this.height;
    }
  }, {
    key: "inside",
    value: function inside(other) {
      return other.top <= this.top && this.top <= other.bottom && other.top <= this.bottom && this.bottom <= other.bottom && other.left <= this.left && this.left <= other.right && other.left <= this.right && this.right <= other.right;
    }
  }]);

  return Rect;
}();


;// CONCATENATED MODULE: ./src/placeholder.ts
function createPlaceholderElement(type, width, height, top, left, color) {
  var placeholder = document.createElement('div');
  placeholder.classList.add("spacing-js-".concat(type, "-placeholder"));
  placeholder.style.border = "2px solid ".concat(color);
  placeholder.style.position = 'fixed';
  placeholder.style.background = 'none';
  placeholder.style.borderRadius = '2px';
  placeholder.style.padding = '0';
  placeholder.style.margin = '0';
  placeholder.style.width = "".concat(width - 2, "px");
  placeholder.style.height = "".concat(height - 2, "px");
  placeholder.style.top = "".concat(top - 1, "px");
  placeholder.style.left = "".concat(left - 1, "px");
  placeholder.style.pointerEvents = 'none';
  placeholder.style.zIndex = '9999';
  placeholder.style.boxSizing = 'content-box';
  document.body.appendChild(placeholder);
}
function clearPlaceholderElement(type) {
  var _document$querySelect;

  (_document$querySelect = document.querySelector(".spacing-js-".concat(type, "-placeholder"))) === null || _document$querySelect === void 0 ? void 0 : _document$querySelect.remove();
}
;// CONCATENATED MODULE: ./src/marker.ts
function createLine(width, height, top, left, text) {
  var border = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 'none';
  var marker = document.createElement('span');
  marker.style.backgroundColor = 'red';
  marker.style.position = 'fixed';
  marker.classList.add("spacing-js-marker");
  marker.style.width = "".concat(width, "px");
  marker.style.height = "".concat(height, "px");

  if (border === 'x') {
    marker.style.borderLeft = '1px solid rgba(255, 255, 255, .8)';
    marker.style.borderRight = '1px solid rgba(255, 255, 255, .8)';
  }

  if (border === 'y') {
    marker.style.borderTop = '1px solid rgba(255, 255, 255, .8)';
    marker.style.borderBottom = '1px solid rgba(255, 255, 255, .8)';
  }

  marker.style.pointerEvents = 'none';
  marker.style.top = "".concat(top, "px");
  marker.style.left = "".concat(left, "px");
  marker.style.zIndex = '9998';
  marker.style.boxSizing = 'content-box';
  var value = document.createElement('span');
  value.classList.add("spacing-js-value");
  value.style.backgroundColor = 'red';
  value.style.color = 'white';
  value.style.fontSize = '10px';
  value.style.display = 'inline-block';
  value.style.fontFamily = 'Helvetica, sans-serif';
  value.style.fontWeight = 'bold';
  value.style.borderRadius = '20px';
  value.style.position = 'fixed';
  value.style.width = '42px';
  value.style.lineHeight = '15px';
  value.style.height = '16px';
  value.style.textAlign = 'center';
  value.style.zIndex = '10000';
  value.style.pointerEvents = 'none';
  value.innerText = text;
  value.style.boxSizing = 'content-box';

  if (border === 'x') {
    // Prevent the badge moved outside the screen
    var topOffset = top + height / 2 - 7;

    if (topOffset > document.documentElement.clientHeight - 20) {
      topOffset = document.documentElement.clientHeight - 20;
    }

    if (topOffset < 0) {
      topOffset = 6;
    }

    value.style.top = "".concat(topOffset, "px");
    value.style.left = "".concat(left + 6, "px");
  } else if (border === 'y') {
    // Prevent the badge moved outside the screen
    var leftOffset = left + width / 2 - 20;

    if (leftOffset > document.documentElement.clientWidth - 48) {
      leftOffset = document.documentElement.clientWidth - 48;
    }

    if (leftOffset < 0) {
      leftOffset = 6;
    }

    value.style.top = "".concat(top + 6, "px");
    value.style.left = "".concat(leftOffset, "px");
  }

  document.body.appendChild(marker);
  document.body.appendChild(value);
}

function placeMark(rect1, rect2, direction, value) {
  var edgeToEdge = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;

  if (direction === 'top') {
    var width = 1;
    var height = Math.abs(rect1.top - rect2.top);
    var left = Math.floor((Math.min(rect1.right, rect2.right) + Math.max(rect1.left, rect2.left)) / 2);
    var top = Math.min(rect1.top, rect2.top);

    if (edgeToEdge) {
      if (rect1.top < rect2.top) {
        return;
      } // If not colliding


      if (rect1.right < rect2.left || rect1.left > rect2.right) {
        return;
      }

      height = Math.abs(rect2.bottom - rect1.top);
      top = Math.min(rect2.bottom, rect1.top);
    }

    createLine(width, height, top, left, value, 'x');
  } else if (direction === 'left') {
    var _width = Math.abs(rect1.left - rect2.left);

    var _height = 1;

    var _top = Math.floor((Math.min(rect1.bottom, rect2.bottom) + Math.max(rect1.top, rect2.top)) / 2);

    var _left = Math.min(rect1.left, rect2.left);

    if (edgeToEdge) {
      if (rect1.left < rect2.left) {
        return;
      } // If not overlapping


      if (rect1.bottom < rect2.top || rect1.top > rect2.bottom) {
        return;
      }

      _width = Math.abs(rect1.left - rect2.right);
      _left = Math.min(rect2.right, rect1.left);
    }

    createLine(_width, _height, _top, _left, value, 'y');
  } else if (direction === 'right') {
    var _width2 = Math.abs(rect1.right - rect2.right);

    var _height2 = 1;

    var _top2 = Math.floor((Math.min(rect1.bottom, rect2.bottom) + Math.max(rect1.top, rect2.top)) / 2);

    var _left2 = Math.min(rect1.right, rect2.right);

    if (edgeToEdge) {
      if (rect1.left > rect2.right) {
        return;
      } // If not overlapping


      if (rect1.bottom < rect2.top || rect1.top > rect2.bottom) {
        return;
      }

      _width2 = Math.abs(rect1.right - rect2.left);
    }

    createLine(_width2, _height2, _top2, _left2, value, 'y');
  } else if (direction === 'bottom') {
    var _width3 = 1;

    var _height3 = Math.abs(rect1.bottom - rect2.bottom);

    var _top3 = Math.min(rect1.bottom, rect2.bottom);

    var _left3 = Math.floor((Math.min(rect1.right, rect2.right) + Math.max(rect1.left, rect2.left)) / 2);

    if (edgeToEdge) {
      if (rect2.bottom < rect1.top) {
        return;
      } // If not overlapping


      if (rect1.right < rect2.left || rect1.left > rect2.right) {
        return;
      }

      _height3 = Math.abs(rect1.bottom - rect2.top);
    }

    createLine(_width3, _height3, _top3, _left3, value, 'x');
  }
}
function removeMarks() {
  document.querySelectorAll('.spacing-js-marker').forEach(function (element) {
    element.remove();
  });
  document.querySelectorAll('.spacing-js-value').forEach(function (element) {
    element.remove();
  });
}
;// CONCATENATED MODULE: ./src/spacing.ts



var active = false;
var selectedElement;
var targetElement;
var originalBodyOverflow;
var Spacing = {
  start: function start() {
    if (!document.body) {
      console.warn("Unable to initialise, document.body does not exist.");
      return;
    }

    window.addEventListener('keydown', function (e) {
      if (e.key === 'Alt' && !active) {
        e.preventDefault();
        active = true;
        setSelectedElement();
        preventPageScroll(true);
      }
    });
    window.addEventListener('keyup', function (e) {
      active = false;
      clearPlaceholderElement('selected');
      clearPlaceholderElement('target');
      selectedElement = null;
      targetElement = null;
      removeMarks();
      preventPageScroll(false);
    });
    window.addEventListener('mousemove', function (e) {
      setTargetElement().then(function () {
        if (selectedElement != null && targetElement != null) {
          // Do the calculation
          var selectedElementRect = selectedElement.getBoundingClientRect();
          var targetElementRect = targetElement.getBoundingClientRect();
          var selected = new Rect(selectedElementRect);
          var target = new Rect(targetElementRect);
          removeMarks();
          var top, bottom, left, right, outside;

          if (selected.containing(target) || selected.inside(target) || selected.colliding(target)) {
            console.log("containing || inside || colliding");
            top = Math.round(Math.abs(selectedElementRect.top - targetElementRect.top));
            bottom = Math.round(Math.abs(selectedElementRect.bottom - targetElementRect.bottom));
            left = Math.round(Math.abs(selectedElementRect.left - targetElementRect.left));
            right = Math.round(Math.abs(selectedElementRect.right - targetElementRect.right));
            outside = false;
          } else {
            console.log("outside");
            top = Math.round(Math.abs(selectedElementRect.top - targetElementRect.bottom));
            bottom = Math.round(Math.abs(selectedElementRect.bottom - targetElementRect.top));
            left = Math.round(Math.abs(selectedElementRect.left - targetElementRect.right));
            right = Math.round(Math.abs(selectedElementRect.right - targetElementRect.left));
            outside = true;
          }

          placeMark(selected, target, 'top', "".concat(top, "px"), outside);
          placeMark(selected, target, 'bottom', "".concat(bottom, "px"), outside);
          placeMark(selected, target, 'left', "".concat(left, "px"), outside);
          placeMark(selected, target, 'right', "".concat(right, "px"), outside);
        }
      });
    });
  }
};

function setSelectedElement() {
  var elements = document.querySelectorAll(':hover');
  var el = elements[elements.length - 1];

  if (el !== selectedElement) {
    selectedElement = el;
    clearPlaceholderElement('selected');
    var rect = selectedElement.getBoundingClientRect();
    createPlaceholderElement('selected', rect.width, rect.height, rect.top, rect.left, "red");
  }
}

function setTargetElement() {
  return new Promise(function (resolve, reject) {
    var elements = document.querySelectorAll(':hover');
    var el = elements[elements.length - 1];

    if (active && el !== selectedElement && el !== targetElement) {
      targetElement = el;
      clearPlaceholderElement('target');
      var rect = targetElement.getBoundingClientRect();
      createPlaceholderElement('target', rect.width, rect.height, rect.top, rect.left, 'blue');
      resolve();
    }
  });
}

function preventPageScroll(active) {
  if (active) {
    window.addEventListener('DOMMouseScroll', scrollingPreventDefault, false);
    window.addEventListener('wheel', scrollingPreventDefault, {
      passive: false
    });
    window.addEventListener('mousewheel', scrollingPreventDefault, {
      passive: false
    });
  } else {
    window.removeEventListener('DOMMouseScroll', scrollingPreventDefault);
    window.removeEventListener('wheel', scrollingPreventDefault);
    window.removeEventListener('mousewheel', scrollingPreventDefault);
  }
}

function scrollingPreventDefault(e) {
  e.preventDefault();
}

/* harmony default export */ const spacing = (Spacing);
;// CONCATENATED MODULE: ./src/index.ts
 // Simple, Start.

spacing.start();
window.Spacing = __webpack_exports__;
/******/ })()
;