139 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			139 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
'use strict';
 | 
						|
 | 
						|
/**
 | 
						|
 * @typedef {import('../lib/types').PathDataItem} PathDataItem
 | 
						|
 */
 | 
						|
 | 
						|
const { visitSkip, detachNodeFromParent } = require('../lib/xast.js');
 | 
						|
const { parsePathData } = require('../lib/path.js');
 | 
						|
const { intersects } = require('./_path.js');
 | 
						|
 | 
						|
exports.type = 'visitor';
 | 
						|
exports.name = 'removeOffCanvasPaths';
 | 
						|
exports.active = false;
 | 
						|
exports.description =
 | 
						|
  'removes elements that are drawn outside of the viewbox (disabled by default)';
 | 
						|
 | 
						|
/**
 | 
						|
 * Remove elements that are drawn outside of the viewbox.
 | 
						|
 *
 | 
						|
 * @author JoshyPHP
 | 
						|
 *
 | 
						|
 * @type {import('../lib/types').Plugin<void>}
 | 
						|
 */
 | 
						|
exports.fn = () => {
 | 
						|
  /**
 | 
						|
   * @type {null | {
 | 
						|
   *   top: number,
 | 
						|
   *   right: number,
 | 
						|
   *   bottom: number,
 | 
						|
   *   left: number,
 | 
						|
   *   width: number,
 | 
						|
   *   height: number
 | 
						|
   * }}
 | 
						|
   */
 | 
						|
  let viewBoxData = null;
 | 
						|
 | 
						|
  return {
 | 
						|
    element: {
 | 
						|
      enter: (node, parentNode) => {
 | 
						|
        if (node.name === 'svg' && parentNode.type === 'root') {
 | 
						|
          let viewBox = '';
 | 
						|
          // find viewbox
 | 
						|
          if (node.attributes.viewBox != null) {
 | 
						|
            // remove commas and plus signs, normalize and trim whitespace
 | 
						|
            viewBox = node.attributes.viewBox;
 | 
						|
          } else if (
 | 
						|
            node.attributes.height != null &&
 | 
						|
            node.attributes.width != null
 | 
						|
          ) {
 | 
						|
            viewBox = `0 0 ${node.attributes.width} ${node.attributes.height}`;
 | 
						|
          }
 | 
						|
 | 
						|
          // parse viewbox
 | 
						|
          // remove commas and plus signs, normalize and trim whitespace
 | 
						|
          viewBox = viewBox
 | 
						|
            .replace(/[,+]|px/g, ' ')
 | 
						|
            .replace(/\s+/g, ' ')
 | 
						|
            .replace(/^\s*|\s*$/g, '');
 | 
						|
          // ensure that the dimensions are 4 values separated by space
 | 
						|
          const m =
 | 
						|
            /^(-?\d*\.?\d+) (-?\d*\.?\d+) (\d*\.?\d+) (\d*\.?\d+)$/.exec(
 | 
						|
              viewBox
 | 
						|
            );
 | 
						|
          if (m == null) {
 | 
						|
            return;
 | 
						|
          }
 | 
						|
          const left = Number.parseFloat(m[1]);
 | 
						|
          const top = Number.parseFloat(m[2]);
 | 
						|
          const width = Number.parseFloat(m[3]);
 | 
						|
          const height = Number.parseFloat(m[4]);
 | 
						|
 | 
						|
          // store the viewBox boundaries
 | 
						|
          viewBoxData = {
 | 
						|
            left,
 | 
						|
            top,
 | 
						|
            right: left + width,
 | 
						|
            bottom: top + height,
 | 
						|
            width,
 | 
						|
            height,
 | 
						|
          };
 | 
						|
        }
 | 
						|
 | 
						|
        // consider that any item with a transform attribute is visible
 | 
						|
        if (node.attributes.transform != null) {
 | 
						|
          return visitSkip;
 | 
						|
        }
 | 
						|
 | 
						|
        if (
 | 
						|
          node.name === 'path' &&
 | 
						|
          node.attributes.d != null &&
 | 
						|
          viewBoxData != null
 | 
						|
        ) {
 | 
						|
          const pathData = parsePathData(node.attributes.d);
 | 
						|
 | 
						|
          // consider that a M command within the viewBox is visible
 | 
						|
          let visible = false;
 | 
						|
          for (const pathDataItem of pathData) {
 | 
						|
            if (pathDataItem.command === 'M') {
 | 
						|
              const [x, y] = pathDataItem.args;
 | 
						|
              if (
 | 
						|
                x >= viewBoxData.left &&
 | 
						|
                x <= viewBoxData.right &&
 | 
						|
                y >= viewBoxData.top &&
 | 
						|
                y <= viewBoxData.bottom
 | 
						|
              ) {
 | 
						|
                visible = true;
 | 
						|
              }
 | 
						|
            }
 | 
						|
          }
 | 
						|
          if (visible) {
 | 
						|
            return;
 | 
						|
          }
 | 
						|
 | 
						|
          if (pathData.length === 2) {
 | 
						|
            // close the path too short for intersects()
 | 
						|
            pathData.push({ command: 'z', args: [] });
 | 
						|
          }
 | 
						|
 | 
						|
          const { left, top, width, height } = viewBoxData;
 | 
						|
          /**
 | 
						|
           * @type {Array<PathDataItem>}
 | 
						|
           */
 | 
						|
          const viewBoxPathData = [
 | 
						|
            { command: 'M', args: [left, top] },
 | 
						|
            { command: 'h', args: [width] },
 | 
						|
            { command: 'v', args: [height] },
 | 
						|
            { command: 'H', args: [left] },
 | 
						|
            { command: 'z', args: [] },
 | 
						|
          ];
 | 
						|
 | 
						|
          if (intersects(viewBoxPathData, pathData) === false) {
 | 
						|
            detachNodeFromParent(node, parentNode);
 | 
						|
          }
 | 
						|
        }
 | 
						|
      },
 | 
						|
    },
 | 
						|
  };
 | 
						|
};
 |