Spacing.js

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

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

// ==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__;
/******/ })()
;