From 7b11e7dd801b6bb9b0f67a0f3a1b42ec044d36cf Mon Sep 17 00:00:00 2001 From: smith120bh <42363318+smith120bh@users.noreply.github.com> Date: Tue, 22 Jul 2025 16:12:28 +1000 Subject: [PATCH 01/13] MVP of parametric SVG system - vibe coded --- src/css/property-validation.css | 129 +++++++++++ src/index.html | 12 +- src/js/Keyboard.js | 2 +- src/js/Panel.js | 9 +- src/js/Parameters.js | 196 +++++++++++++++++ src/js/PropertyValidation.js | 365 ++++++++++++++++++++++++++++++++ src/js/dao.js | 9 + src/js/editor.js | 186 ++++++++++++++++ src/js/lib/jquery-draginput.js | 9 + src/js/modals.js | 307 +++++++++++++++++++++++++++ src/js/start.js | 11 +- src/js/state.js | 1 + src/js/svgcanvas.js | 2 + 13 files changed, 1231 insertions(+), 7 deletions(-) create mode 100644 src/css/property-validation.css create mode 100644 src/js/Parameters.js create mode 100644 src/js/PropertyValidation.js diff --git a/src/css/property-validation.css b/src/css/property-validation.css new file mode 100644 index 00000000..0e7f1295 --- /dev/null +++ b/src/css/property-validation.css @@ -0,0 +1,129 @@ +/* Property Input Validation Styles */ + +/* Valid parameter reference (e.g., @width) */ +.draginput .param-reference { + background-color: #e7f3ff !important; + border-color: #007cba !important; + color: #005a8b !important; + font-weight: 500; + font-size: 10px !important; + /* Maintain proper draginput layout - preserve original padding */ + padding: 30px 0 16px !important; + text-align: center !important; + /* Ensure text doesn't overflow into label area */ + overflow: hidden !important; + text-overflow: ellipsis !important; + /* Preserve draginput positioning */ + position: relative !important; + z-index: 2 !important; + width: 100% !important; + height: 24px !important; +} + +/* Make labels visible on parameter reference fields - use class-based approach */ +.draginput.has-parameter span, +.draginput.has-parameter span.icon_label, +.draginput .param-reference + span, +.draginput .param-reference + span.icon_label, +.draginput .param-reference ~ span, +.draginput .param-reference ~ span.icon_label { + color: #333 !important; + font-weight: 600 !important; + /* Ensure label stays in correct position */ + position: absolute !important; + top: 5px !important; + left: 5px !important; + z-index: 10 !important; + white-space: nowrap !important; + display: block !important; + visibility: visible !important; + opacity: 1 !important; + font-size: 12px !important; + background: none !important; + border: none !important; + pointer-events: none !important; +} + +/* Valid value (number or valid parameter reference) */ +.draginput .param-valid { + border-color: #28a745 !important; +} + +/* Invalid value */ +.draginput .param-invalid { + background-color: #fff5f5 !important; + border-color: #dc3545 !important; + color: #dc3545 !important; + font-size: 11px !important; + /* Maintain proper draginput layout */ + padding: 30px 0 16px !important; + text-align: center !important; + position: relative !important; + z-index: 2 !important; + width: 100% !important; + height: 24px !important; +} + +/* Make labels visible on invalid fields too */ +.draginput .param-invalid + span, +.draginput .param-invalid + span.icon_label, +.draginput .param-invalid ~ span, +.draginput .param-invalid ~ span.icon_label { + color: #333 !important; + font-weight: 600 !important; + /* Ensure label stays in correct position */ + position: absolute !important; + top: 5px !important; + left: 5px !important; + z-index: 10 !important; + white-space: nowrap !important; + display: block !important; + visibility: visible !important; + opacity: 1 !important; + font-size: 12px !important; + background: none !important; + border: none !important; + pointer-events: none !important; +} + +/* Parameter autocomplete suggestions */ +datalist option { + background: #f8f9fa; + color: #007cba; + font-family: monospace; +} + +/* Focus states for parameter inputs */ +input.param-reference:focus { + box-shadow: 0 0 0 2px rgba(0, 124, 186, 0.25); +} + +input.param-invalid:focus { + box-shadow: 0 0 0 2px rgba(220, 53, 69, 0.25); +} + +/* Placeholder styling for parameter hints */ +input[data-attr]::placeholder { + color: #666; + font-style: italic; +} + +/* Tooltip for invalid parameter references */ +input.param-invalid { + position: relative; +} + +input.param-invalid:hover::after { + content: "Invalid parameter reference or number format"; + position: absolute; + top: 100%; + left: 0; + background: #dc3545; + color: white; + padding: 4px 8px; + border-radius: 3px; + font-size: 12px; + white-space: nowrap; + z-index: 1000; + margin-top: 2px; +} \ No newline at end of file diff --git a/src/index.html b/src/index.html index d71ea870..57da29ac 100644 --- a/src/index.html +++ b/src/index.html @@ -35,7 +35,8 @@ - + + @@ -87,6 +88,7 @@ + @@ -96,6 +98,8 @@
+ +
@@ -152,12 +156,12 @@

Canvas

@@ -802,6 +806,8 @@

Stroke

+ + diff --git a/src/js/Keyboard.js b/src/js/Keyboard.js index d1075d2b..7f75ef0b 100644 --- a/src/js/Keyboard.js +++ b/src/js/Keyboard.js @@ -85,7 +85,7 @@ MD.Keyboard = function(){ document.addEventListener("keyup", function(e){ if ($("#color_picker").is(":visible")) return e; const canvasMode = state.get("canvasMode"); - const key = e.key.toLowerCase(); + const key = e.key ? e.key.toLowerCase() : ''; const keys = { "alt": ()=> $("#workarea").removeClass("out"), " ": ()=> editor.pan.stopPan(), diff --git a/src/js/Panel.js b/src/js/Panel.js index 38c247df..b4efbaa3 100644 --- a/src/js/Panel.js +++ b/src/js/Panel.js @@ -350,7 +350,7 @@ MD.Panel = function(){ if (!elem && !multiselected) { menu_items.disableContextMenuItems('#delete,#cut,#copy,#ungroup,#move_front,#move_up,#move_down,#move_back'); - $('.menu_item', '#edit_menu').addClass('disabled'); + $('.menu_item.action_selected', '#edit_menu').addClass('disabled'); } $('.menu_item', '#object_menu').toggleClass('disabled', !elem && !multiselected); @@ -371,6 +371,13 @@ MD.Panel = function(){ // Enable regular menu options $("#cmenu_canvas").enableContextMenuItems('#delete,#cut,#copy,#move_front,#move_up,#move_down,#move_back'); } + + // Restore parameter references for the selected element, or clear validation if none selected + if (editor.propertyValidation) { + setTimeout(() => { + editor.propertyValidation.restoreParameterReferences(); + }, 10); + } } $('#cur_context_panel').delegate('a', 'click', function() { diff --git a/src/js/Parameters.js b/src/js/Parameters.js new file mode 100644 index 00000000..d48fb966 --- /dev/null +++ b/src/js/Parameters.js @@ -0,0 +1,196 @@ +MD.Parameters = function(){ + const _self = this; + + // Parameter name validation regex: must start with letter or underscore, + // followed by letters, numbers, or underscores + const PARAM_NAME_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]*$/; + + // Parameter types supported + const PARAM_TYPES = { + number: { label: 'Number', defaultValue: 0 }, + text: { label: 'Text', defaultValue: '' }, + color: { label: 'Color', defaultValue: '#000000' }, + boolean: { label: 'Boolean', defaultValue: false } + }; + + // Get current parameters from state + function getParameters() { + return state.get('canvasParameters') || {}; + } + + // Save parameters to state + function saveParameters(params) { + state.set('canvasParameters', params); + } + + // Validate parameter name + function isValidParameterName(name) { + if (!name || typeof name !== 'string') return false; + return PARAM_NAME_REGEX.test(name); + } + + // Check if parameter name already exists + function parameterExists(name, excludeId = null) { + const params = getParameters(); + return Object.keys(params).some(id => + id !== excludeId && params[id].name === name + ); + } + + // Generate unique parameter ID + function generateParameterId() { + return 'param_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); + } + + // Add new parameter + function addParameter(name, type, defaultValue, description = '') { + if (!isValidParameterName(name)) { + throw new Error('Invalid parameter name. Must start with letter or underscore, followed by letters, numbers, or underscores.'); + } + + if (parameterExists(name)) { + throw new Error('Parameter name already exists.'); + } + + if (!PARAM_TYPES[type]) { + throw new Error('Invalid parameter type.'); + } + + const params = getParameters(); + const id = generateParameterId(); + + params[id] = { + name: name, + type: type, + defaultValue: defaultValue, + description: description + }; + + saveParameters(params); + return id; + } + + // Update existing parameter + function updateParameter(id, name, type, defaultValue, description = '') { + if (!isValidParameterName(name)) { + throw new Error('Invalid parameter name. Must start with letter or underscore, followed by letters, numbers, or underscores.'); + } + + if (parameterExists(name, id)) { + throw new Error('Parameter name already exists.'); + } + + if (!PARAM_TYPES[type]) { + throw new Error('Invalid parameter type.'); + } + + const params = getParameters(); + if (!params[id]) { + throw new Error('Parameter not found.'); + } + + params[id] = { + name: name, + type: type, + defaultValue: defaultValue, + description: description + }; + + saveParameters(params); + } + + // Delete parameter + function deleteParameter(id) { + const params = getParameters(); + if (!params[id]) { + throw new Error('Parameter not found.'); + } + + delete params[id]; + saveParameters(params); + } + + // Get parameter by ID + function getParameter(id) { + const params = getParameters(); + return params[id] || null; + } + + // Get parameter by name + function getParameterByName(name) { + const params = getParameters(); + const id = Object.keys(params).find(id => params[id].name === name); + return id ? params[id] : null; + } + + // Get all parameter names (for autocomplete) + function getParameterNames() { + const params = getParameters(); + return Object.values(params).map(param => param.name); + } + + // Resolve parameter reference (e.g., "@width" -> actual value) + function resolveParameterValue(value) { + if (typeof value !== 'string' || !value.startsWith('@')) { + return value; + } + + const paramName = value.substring(1); + const param = getParameterByName(paramName); + + if (!param) { + console.warn(`Parameter @${paramName} not found, using original value`); + return value; + } + + // Ensure proper type conversion + const resolvedValue = param.defaultValue; + if (param.type === 'number') { + const num = parseFloat(resolvedValue); + return isNaN(num) ? 0 : num; + } + + return resolvedValue; + } + + // Check if a value is a parameter reference + function isParameterReference(value) { + return typeof value === 'string' && value.startsWith('@') && value.length > 1; + } + + // Validate parameter value for its type + function validateParameterValue(value, type) { + switch (type) { + case 'number': + const num = parseFloat(value); + return !isNaN(num) ? num : PARAM_TYPES.number.defaultValue; + case 'text': + return String(value); + case 'color': + // Basic color validation - should be hex color + if (typeof value === 'string' && /^#[0-9A-F]{6}$/i.test(value)) { + return value; + } + return PARAM_TYPES.color.defaultValue; + case 'boolean': + return Boolean(value); + default: + return value; + } + } + + // Public API + this.getParameters = getParameters; + this.addParameter = addParameter; + this.updateParameter = updateParameter; + this.deleteParameter = deleteParameter; + this.getParameter = getParameter; + this.getParameterByName = getParameterByName; + this.getParameterNames = getParameterNames; + this.resolveParameterValue = resolveParameterValue; + this.isParameterReference = isParameterReference; + this.validateParameterValue = validateParameterValue; + this.isValidParameterName = isValidParameterName; + this.parameterExists = parameterExists; + this.PARAM_TYPES = PARAM_TYPES; +}; \ No newline at end of file diff --git a/src/js/PropertyValidation.js b/src/js/PropertyValidation.js new file mode 100644 index 00000000..1b9e09fd --- /dev/null +++ b/src/js/PropertyValidation.js @@ -0,0 +1,365 @@ +MD.PropertyValidation = function() { + const _self = this; + + // Pattern to match parameter references (@paramName) + const PARAM_REFERENCE_PATTERN = /^@[a-zA-Z_][a-zA-Z0-9_]*$/; + + // Pattern to match numeric values (including decimals and negatives) + const NUMERIC_PATTERN = /^-?[0-9]+(\.[0-9]+)?$/; + + // Validate if a value is either a number or a valid parameter reference + function isValidPropertyValue(value) { + if (typeof value !== 'string') return false; + + value = value.trim(); + if (value === '') return false; + + // Check if it's a number + if (NUMERIC_PATTERN.test(value)) return true; + + // Check if it's a valid parameter reference + if (PARAM_REFERENCE_PATTERN.test(value)) { + const paramName = value.substring(1); + return editor.parametersManager.getParameterByName(paramName) !== null; + } + + return false; + } + + // Clear all validation classes from an input + function clearInputValidation(input) { + input.classList.remove('param-valid', 'param-invalid', 'param-reference'); + input.removeAttribute('data-original-param'); + + // Remove has-parameter class from parent draginput + const draginputParent = input.closest('.draginput'); + if (draginputParent) { + draginputParent.classList.remove('has-parameter'); + } + } + + // Add visual feedback to input field based on validation + function updateInputValidation(input) { + let value = input.value.trim(); + + // Handle parameter display format: "@width (200)" - extract just the parameter reference + const paramMatch = value.match(/^(@[a-zA-Z_][a-zA-Z0-9_]*)\s*\([^)]*\)$/); + if (paramMatch) { + value = paramMatch[1]; // Get just the parameter reference part + } + + const isValid = value === '' || isValidPropertyValue(value); + + // Remove existing validation classes + input.classList.remove('param-valid', 'param-invalid', 'param-reference'); + + if (value === '') { + // Empty is neutral - make sure no parameter data remains + input.removeAttribute('data-original-param'); + return; + } + + if (isValid) { + input.classList.add('param-valid'); + + // Add special styling for parameter references + if (PARAM_REFERENCE_PATTERN.test(value)) { + input.classList.add('param-reference'); + } + } else { + input.classList.add('param-invalid'); + } + } + + // Enhanced change handler for dragInput callbacks + function createEnhancedCallback(originalCallback) { + return function(attr, value, completed) { + // Validate the value before processing + if (typeof value === 'string' && value.trim() !== '') { + if (!isValidPropertyValue(value)) { + console.warn(`Invalid property value: ${value}. Expected number or @paramName.`); + return; // Don't apply invalid values + } + } + + // Call the original callback + return originalCallback.call(this, attr, value, completed); + }; + } + + // Initialize validation for all property inputs + function initializeValidation() { + // Find all property input fields + const propertyInputs = document.querySelectorAll('.attr_changer, input[data-attr]'); + + propertyInputs.forEach(input => { + // Update pattern to accept parameter references + if (input.hasAttribute('pattern')) { + input.pattern = '([0-9]+(\.[0-9]+)?|@[a-zA-Z_][a-zA-Z0-9_]*)'; + } else { + input.setAttribute('pattern', '([0-9]+(\.[0-9]+)?|@[a-zA-Z_][a-zA-Z0-9_]*)'); + } + + // Add real-time validation + input.addEventListener('input', () => updateInputValidation(input)); + input.addEventListener('blur', () => updateInputValidation(input)); + input.addEventListener('change', () => updateInputValidation(input)); + + // Handle parameter references on blur/change/enter + input.addEventListener('blur', function() { + handleParameterInput(this); + }); + input.addEventListener('change', function() { + handleParameterInput(this); + }); + + // Handle Enter key for parameter references + input.addEventListener('keydown', function(e) { + if (e.key === 'Enter' && isValidParameterReference(this.value)) { + e.preventDefault(); + handleParameterInput(this); + this.blur(); + } + }); + + // Initial validation + updateInputValidation(input); + }); + + // Add autocomplete support for parameter names + addParameterAutocomplete(); + } + + // Check if a value is a valid parameter reference + function isValidParameterReference(value) { + // Handle parameter display format: "@width (200)" - extract just the parameter reference + const paramMatch = value.match(/^(@[a-zA-Z_][a-zA-Z0-9_]*)\s*\([^)]*\)$/); + if (paramMatch) { + value = paramMatch[1]; // Get just the parameter reference part + } + + if (!PARAM_REFERENCE_PATTERN.test(value)) return false; + const paramName = value.substring(1); + return editor.parametersManager.getParameterByName(paramName) !== null; + } + + // Handle parameter input changes + function handleParameterInput(input) { + let value = input.value.trim(); + + // Handle parameter display format: "@width (200)" - extract just the parameter reference + const paramMatch = value.match(/^(@[a-zA-Z_][a-zA-Z0-9_]*)\s*\([^)]*\)$/); + if (paramMatch) { + value = paramMatch[1]; // Get just the parameter reference part + } + + // If this is not a parameter reference, make sure we clear any parameter data + if (!PARAM_REFERENCE_PATTERN.test(value)) { + const attr = input.getAttribute('data-attr'); + if (attr) { + // Clear parameter data from selected elements + const selectedElements = svgCanvas.getSelectedElems(); + selectedElements.forEach(elem => { + if (elem) { + elem.removeAttribute(`data-param-${attr}`); + } + }); + } + // Clear parameter display data + input.removeAttribute('data-original-param'); + return; + } + + // Validate the parameter exists + if (!isValidParameterReference(value)) return; + + // Get the attribute name and resolve the parameter + const attr = input.getAttribute('data-attr'); + if (!attr) return; + + const resolvedValue = editor.parametersManager.resolveParameterValue(value); + + // Store parameter reference on selected elements for persistence + storeParameterReference(attr, value); + + // Update input display to show parameter and resolved value + updateParameterDisplay(input, value, resolvedValue); + + // Call the appropriate change handler directly with resolved value + if (typeof editor.changeAttribute === 'function') { + editor.changeAttribute(attr, resolvedValue, true); + } + } + + // Store parameter reference on SVG elements for persistence + function storeParameterReference(attr, paramRef) { + const selectedElements = svgCanvas.getSelectedElems(); + selectedElements.forEach(elem => { + if (elem) { + elem.setAttribute(`data-param-${attr}`, paramRef); + } + }); + } + + // Update input display to show both parameter and resolved value + function updateParameterDisplay(input, paramRef, resolvedValue) { + const displayValue = `${paramRef} (${resolvedValue})`; + input.value = displayValue; + + // Store original parameter reference for later use + input.setAttribute('data-original-param', paramRef); + + // Add class to parent draginput to help with label styling + const draginputParent = input.closest('.draginput'); + if (draginputParent) { + draginputParent.classList.add('has-parameter'); + } + } + + // Clear all parameter validation from inputs + function clearAllInputValidation() { + const propertyInputs = document.querySelectorAll('.attr_changer, input[data-attr]'); + propertyInputs.forEach(input => { + clearInputValidation(input); + }); + } + + // Restore parameter references when elements are selected + function restoreParameterReferences() { + const selectedElements = svgCanvas.getSelectedElems(); + if (!selectedElements.length || !selectedElements[0]) { + // No element selected, clear all validation styling + clearAllInputValidation(); + return; + } + + const elem = selectedElements[0]; + + // Find all property inputs and check for stored parameter references + const propertyInputs = document.querySelectorAll('.attr_changer, input[data-attr]'); + propertyInputs.forEach(input => { + const attr = input.getAttribute('data-attr'); + if (!attr) return; + + // Clear any existing validation first + clearInputValidation(input); + + const paramRef = elem.getAttribute(`data-param-${attr}`); + if (paramRef && PARAM_REFERENCE_PATTERN.test(paramRef)) { + const resolvedValue = editor.parametersManager.resolveParameterValue(paramRef); + updateParameterDisplay(input, paramRef, resolvedValue); + updateInputValidation(input); + } + }); + } + + // Add autocomplete functionality for parameter names + function addParameterAutocomplete() { + // Create a datalist element for parameter suggestions + let datalist = document.getElementById('parameter-suggestions'); + if (!datalist) { + datalist = document.createElement('datalist'); + datalist.id = 'parameter-suggestions'; + document.body.appendChild(datalist); + } + + // Update the datalist with current parameters + function updateParameterSuggestions() { + if (!editor.parametersManager) return; + const paramNames = editor.parametersManager.getParameterNames(); + datalist.innerHTML = paramNames.map(name => ``).join(''); + } + + // Add autocomplete to property inputs + const propertyInputs = document.querySelectorAll('.attr_changer, input[data-attr]'); + propertyInputs.forEach(input => { + input.setAttribute('list', 'parameter-suggestions'); + + // Update suggestions when input gets focus + input.addEventListener('focus', updateParameterSuggestions); + + // Also trigger on input for @ character + input.addEventListener('input', function(e) { + if (e.target.value.includes('@')) { + updateParameterSuggestions(); + } + }); + }); + + // Update suggestions when parameters change + updateParameterSuggestions(); + } + + // Flag to prevent infinite loops during parameter reset + let isResettingParameters = false; + + // Reset elements with parameters back to their parameter values after manual changes + function resetParameterizedAttributes(elements) { + if (isResettingParameters) { + return; // Prevent infinite loops + } + + if (!Array.isArray(elements)) { + elements = [elements]; + } + + isResettingParameters = true; + + try { + elements.forEach(elem => { + if (!elem) return; + + // Find all parameter attributes on this element + const attributes = elem.attributes; + for (let i = 0; i < attributes.length; i++) { + const attr = attributes[i]; + if (attr.name.startsWith('data-param-')) { + const attrName = attr.name.substring('data-param-'.length); + const paramRef = attr.value; + + if (PARAM_REFERENCE_PATTERN.test(paramRef)) { + const resolvedValue = editor.parametersManager.resolveParameterValue(paramRef); + + // Apply the parameter value to the actual SVG attribute + elem.setAttribute(attrName, resolvedValue); + + // Update the corresponding input field if it exists + const input = document.querySelector(`input[data-attr="${attrName}"]`); + if (input) { + updateParameterDisplay(input, paramRef, resolvedValue); + updateInputValidation(input); + } + } + } + } + }); + } finally { + isResettingParameters = false; + } + } + + // Check if element has any parameter references + function hasParameterReferences(elem) { + if (!elem || !elem.attributes) return false; + + for (let i = 0; i < elem.attributes.length; i++) { + if (elem.attributes[i].name.startsWith('data-param-')) { + return true; + } + } + return false; + } + + // Public API + this.isValidPropertyValue = isValidPropertyValue; + this.updateInputValidation = updateInputValidation; + this.createEnhancedCallback = createEnhancedCallback; + this.initializeValidation = initializeValidation; + this.addParameterAutocomplete = addParameterAutocomplete; + this.restoreParameterReferences = restoreParameterReferences; + this.clearAllInputValidation = clearAllInputValidation; + this.resetParameterizedAttributes = resetParameterizedAttributes; + this.hasParameterReferences = hasParameterReferences; + this.PARAM_REFERENCE_PATTERN = PARAM_REFERENCE_PATTERN; + this.NUMERIC_PATTERN = NUMERIC_PATTERN; +}; \ No newline at end of file diff --git a/src/js/dao.js b/src/js/dao.js index bfdb2f98..80b1a468 100644 --- a/src/js/dao.js +++ b/src/js/dao.js @@ -146,6 +146,15 @@ const dao = [ private: true, save: true, }, + // parametric SVG parameters + { + name: "canvasParameters", + label: "Canvas Parameters", + type: "object", + default: {}, + private: true, + save: true, + }, ]; diff --git a/src/js/editor.js b/src/js/editor.js index 7af66550..de0ba979 100644 --- a/src/js/editor.js +++ b/src/js/editor.js @@ -196,6 +196,18 @@ MD.Editor = function(){ elems: elems }); + // Reset any parametrized attributes back to their parameter values after element changes + if (editor.propertyValidation && elems && elems.length > 0) { + // Use setTimeout to ensure DOM updates are complete + setTimeout(() => { + const elementsWithParams = elems.filter(elem => + elem && editor.propertyValidation.hasParameterReferences(elem) + ); + if (elementsWithParams.length > 0) { + editor.propertyValidation.resetParameterizedAttributes(elementsWithParams); + } + }, 50); + } if (!svgCanvas.getContext()) { saveCanvas(); @@ -203,6 +215,7 @@ MD.Editor = function(){ } function changeAttribute(attr, value, completed) { + // Parameter resolution handled by PropertyValidation if (attr === "opacity") value *= 0.01; if (completed) { svgCanvas.changeSelectedAttribute(attr, value); @@ -300,6 +313,169 @@ MD.Editor = function(){ }}); }; + function exportJS() { + try { + console.log('Starting JavaScript export...'); + + // Get the SVG content + const svgString = svgCanvas.getSvgString(); + console.log('Got SVG string:', svgString.length, 'characters'); + + // Create the JavaScript function + const jsFunction = generateParametricJS(svgString); + console.log('Generated JS function:', jsFunction.length, 'characters'); + + // Create and download the JS file + const title = state.get("canvasTitle") || "parametric-svg"; + const filename = title.replace(/[^a-z0-9]/gi, '_').toLowerCase(); + const blob = new Blob([jsFunction], { type: "application/javascript;charset=utf-8" }); + + console.log('Downloading file:', `${filename}.js`); + + // Use the saveAs function from filesaver.js + if (typeof saveAs !== 'undefined') { + saveAs(blob, `${filename}.js`); + } else { + // Fallback download method + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = `${filename}.js`; + document.body.appendChild(a); + a.click(); + setTimeout(() => { + document.body.removeChild(a); + URL.revokeObjectURL(url); + }, 0); + } + + console.log('JavaScript export completed successfully'); + } catch (error) { + console.error('Error during JavaScript export:', error); + alert('Error exporting JavaScript file: ' + error.message); + } + } + + function generateParametricJS(svgString) { + // Parse the SVG to find parametric elements + const parser = new DOMParser(); + const svgDoc = parser.parseFromString(svgString, 'image/svg+xml'); + + // Collect all parameters and their default values + const parametersObj = editor.parametersManager.getParameters() || {}; + console.log('Parameters object:', parametersObj); + const paramMap = {}; + + // Convert parameters object to map by parameter name + Object.values(parametersObj).forEach(param => { + if (param && param.name) { + paramMap[param.name] = param; + } + }); + console.log('Parameter map:', paramMap); + + // Find all elements with data-param-* attributes and replace their values + const parameterizedElements = svgDoc.querySelectorAll('[class*="data-param-"], [data-param-width], [data-param-height], [data-param-x], [data-param-y], [data-param-cx], [data-param-cy], [data-param-rx], [data-param-ry], [data-param-r]'); + + // Actually, let's find ALL elements and check their attributes + const allElements = svgDoc.querySelectorAll('*'); + allElements.forEach(elem => { + for (let i = 0; i < elem.attributes.length; i++) { + const attr = elem.attributes[i]; + if (attr.name.startsWith('data-param-')) { + const attrName = attr.name.substring('data-param-'.length); + const paramRef = attr.value; + const paramName = paramRef.substring(1); // Remove the @ symbol + + if (paramMap[paramName]) { + // Replace the actual attribute value with template literal + elem.setAttribute(attrName, `\${${paramName}}`); + } + } + } + }); + + // Clean up data-param-* attributes from the final output + allElements.forEach(elem => { + const attributesToRemove = []; + for (let i = 0; i < elem.attributes.length; i++) { + const attr = elem.attributes[i]; + if (attr.name.startsWith('data-param-')) { + attributesToRemove.push(attr.name); + } + } + attributesToRemove.forEach(attrName => { + elem.removeAttribute(attrName); + }); + }); + + // Get the modified SVG string + const serializer = new XMLSerializer(); + let modifiedSvgString = serializer.serializeToString(svgDoc.documentElement); + + // Generate parameter list and default values + const paramNames = Object.keys(paramMap); + const paramDefaults = paramNames.map(name => { + const param = paramMap[name]; + let defaultValue = param.defaultValue; + + // Format default value based on type + if (param.type === 'text' || param.type === 'color') { + defaultValue = `"${defaultValue}"`; + } else if (param.type === 'boolean') { + defaultValue = defaultValue === 'true' || defaultValue === true ? 'true' : 'false'; + } + + return defaultValue; + }); + + // Escape the SVG string for template literal + const escapedSvg = modifiedSvgString + .replace(/\\/g, '\\\\') // Escape backslashes first + .replace(/`/g, '\\`') // Escape backticks + .replace(/\$(?!{)/g, '\\$'); // Escape $ that aren't part of ${...} + + // Generate the function + const hasParams = paramNames.length > 0; + const paramComment = hasParams + ? paramNames.map(name => ` * @param {${paramMap[name].type}} ${name} - Default: ${paramMap[name].defaultValue}`).join('\n') + : ' * No parameters defined'; + + const functionParams = hasParams ? `{${paramNames.join(', ')}} = {}` : ''; + const defaultAssignments = hasParams + ? paramNames.map((name, i) => ` const ${name}_val = ${name} !== undefined ? ${name} : ${paramDefaults[i]};`).join('\n') + : ''; + const variableDeclaration = hasParams + ? ` const ${paramNames.join(', ')} = ${paramNames.map(name => `${name}_val`).join(', ')};` + : ''; + + const functionBody = `/** + * Parametric SVG Generator + * Generated by Method Draw + * + * Parameters: +${paramComment} + */ +function generateSVG(${functionParams}) { +${defaultAssignments} +${variableDeclaration} + + return \`${escapedSvg}\`; +} + +// Export for different module systems +if (typeof module !== 'undefined' && module.exports) { + module.exports = generateSVG; +} else if (typeof define === 'function' && define.amd) { + define([], function() { return generateSVG; }); +} else if (typeof window !== 'undefined') { + window.generateSVG = generateSVG; +} +`; + + return functionBody; + } + function saveCanvas(){ state.set("canvasContent", svgCanvas.getSvgString()); } @@ -350,6 +526,14 @@ MD.Editor = function(){ editor.modal.source.open(); } + function parameters(){ + // Refresh the parameters list before opening + if (editor.modal.parameters.renderParametersList) { + editor.modal.parameters.renderParametersList(); + } + editor.modal.parameters.open(); + } + function loadFromUrl(url, cb){ if(!cb) cb = function(){/*noop*/}; $.ajax({ @@ -406,6 +590,8 @@ MD.Editor = function(){ this.shortcuts = shortcuts; this.donate = donate; this.source = source; + this.parameters = parameters; + this.exportJS = exportJS; this.saveCanvas = saveCanvas; this.loadFromUrl = loadFromUrl; diff --git a/src/js/lib/jquery-draginput.js b/src/js/lib/jquery-draginput.js index 4921bf5a..c6b05d2d 100644 --- a/src/js/lib/jquery-draginput.js +++ b/src/js/lib/jquery-draginput.js @@ -46,6 +46,13 @@ $.fn.dragInput = function(cfg){ this.adjustValue = function(i, completed = false){ var v; i = parseFloat(i); + + // Check for parameter references - PropertyValidation handles this more reliably + if (typeof this.value === 'string' && this.value.trim().startsWith('@')) { + // Let PropertyValidation handle parameter resolution + return; + } + if(isNaN(this.value)) { v = this.dragCfg.reset; } else if($.isFunction(this.dragCfg.stepfunc)) { @@ -138,6 +145,8 @@ $.fn.dragInput = function(cfg){ }) + + .bind("dblclick taphold", function(e) { this.removeAttribute("readonly", "readonly"); this.focus(); diff --git a/src/js/modals.js b/src/js/modals.js index a066d3aa..55671059 100644 --- a/src/js/modals.js +++ b/src/js/modals.js @@ -81,5 +81,312 @@ editor.modal = { js: function(el){ el.children[0].classList.add("modal-item-wide"); } + }), + parameters: new MD.Modal({ + html: ` +

Parameters

+
+
+ + + + + + + + + + + + +
NameTypeDefault ValueActions
+
+
+ + +
+
+ + + `, + js: function(el){ + el.children[0].classList.add("modal-item-wide"); + + const modal = this; + const parametersContainer = el.querySelector('#parameters-container'); + const parameterForm = el.querySelector('#parameter-form'); + const parametersTable = el.querySelector('#parameters-table'); + const parametersEmpty = el.querySelector('#parameters-empty'); + const parametersTBody = el.querySelector('#parameters-tbody'); + const addParameterBtn = el.querySelector('#add-parameter-btn'); + const parametersCancelBtn = el.querySelector('#parameters-cancel-btn'); + const parameterFormTitle = el.querySelector('#parameter-form-title'); + const parameterFormElement = el.querySelector('#parameter-form-element'); + const parameterFormCancel = el.querySelector('#parameter-form-cancel'); + + const paramNameInput = el.querySelector('#param-name'); + const paramTypeSelect = el.querySelector('#param-type'); + const paramDefaultInput = el.querySelector('#param-default'); + const paramDescriptionInput = el.querySelector('#param-description'); + + let editingParameterId = null; + + // Update default value input based on parameter type + function updateDefaultValueInput() { + const type = paramTypeSelect.value; + let currentElement = el.querySelector('#param-default'); + + const baseStyle = 'width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; position: relative; z-index: 1001; pointer-events: auto;'; + + if (type === 'boolean') { + // Replace input with select for boolean + if (currentElement.tagName === 'INPUT') { + const select = document.createElement('select'); + select.id = 'param-default'; + select.style.cssText = baseStyle; + select.innerHTML = ''; + currentElement.parentNode.replaceChild(select, currentElement); + } + } else { + // Replace select with input for non-boolean types, or just update if already input + if (currentElement.tagName === 'SELECT') { + const input = document.createElement('input'); + input.id = 'param-default'; + input.style.cssText = baseStyle; + currentElement.parentNode.replaceChild(input, currentElement); + currentElement = input; + } + + // Set input properties based on type + switch (type) { + case 'number': + currentElement.type = 'number'; + currentElement.placeholder = 'e.g., 100'; + break; + case 'text': + currentElement.type = 'text'; + currentElement.placeholder = 'e.g., Hello World'; + break; + case 'color': + currentElement.type = 'color'; + currentElement.placeholder = ''; + break; + default: + currentElement.type = 'text'; + } + } + + // Re-apply click handling after updating the input + ensureDefaultInputClickable(); + } + + // Render parameters list + function renderParametersList() { + const parameters = editor.parametersManager.getParameters(); + const paramIds = Object.keys(parameters); + + if (paramIds.length === 0) { + parametersTable.style.display = 'none'; + parametersEmpty.style.display = 'block'; + } else { + parametersTable.style.display = 'table'; + parametersEmpty.style.display = 'none'; + + parametersTBody.innerHTML = ''; + paramIds.forEach(id => { + const param = parameters[id]; + const row = document.createElement('tr'); + row.style.borderBottom = '1px solid #eee'; + + row.innerHTML = + '@' + param.name + '' + + '' + editor.parametersManager.PARAM_TYPES[param.type].label + '' + + '' + param.defaultValue + '' + + '' + + '' + + '' + + ''; + + parametersTBody.appendChild(row); + }); + + // Add event listeners for edit/delete buttons + el.querySelectorAll('.edit-param-btn').forEach(btn => { + btn.addEventListener('click', function() { + editParameter(this.dataset.id); + }); + }); + + el.querySelectorAll('.delete-param-btn').forEach(btn => { + btn.addEventListener('click', function() { + deleteParameter(this.dataset.id); + }); + }); + } + } + + // Store the render function on the modal object so it can be called later + modal.renderParametersList = renderParametersList; + + // Show parameter form + function showParameterForm(isEdit = false, parameterId = null) { + parametersContainer.style.display = 'none'; + parameterForm.style.display = 'block'; + parameterFormTitle.textContent = isEdit ? 'Edit Parameter' : 'Add Parameter'; + editingParameterId = parameterId; + + if (isEdit && parameterId) { + const param = editor.parametersManager.getParameter(parameterId); + if (param) { + paramNameInput.value = param.name; + paramTypeSelect.value = param.type; + updateDefaultValueInput(); + + // Set default value after updating input type + setTimeout(() => { + const defaultInput = el.querySelector('#param-default'); + defaultInput.value = param.defaultValue; + }, 0); + + paramDescriptionInput.value = param.description || ''; + } + } else { + parameterFormElement.reset(); + updateDefaultValueInput(); + } + + paramNameInput.focus(); + } + + // Hide parameter form + function hideParameterForm() { + parametersContainer.style.display = 'block'; + parameterForm.style.display = 'none'; + editingParameterId = null; + parameterFormElement.reset(); + } + + // Edit parameter + function editParameter(id) { + showParameterForm(true, id); + } + + // Delete parameter + function deleteParameter(id) { + const param = editor.parametersManager.getParameter(id); + if (param && confirm("Are you sure you want to delete parameter \"@" + param.name + "\"?")) { + try { + editor.parametersManager.deleteParameter(id); + renderParametersList(); + + // Update property validation autocomplete + if (editor.propertyValidation) { + editor.propertyValidation.addParameterAutocomplete(); + } + } catch (error) { + alert('Error deleting parameter: ' + error.message); + } + } + } + + // Event listeners + addParameterBtn.addEventListener('click', () => showParameterForm()); + parametersCancelBtn.addEventListener('click', () => editor.modal.parameters.close()); + parameterFormCancel.addEventListener('click', hideParameterForm); + paramTypeSelect.addEventListener('change', updateDefaultValueInput); + + // Ensure input fields are clickable + paramNameInput.addEventListener('mousedown', function(e) { + e.stopPropagation(); + this.focus(); + }); + + // Handle dynamic default input field + function ensureDefaultInputClickable() { + const defaultInput = el.querySelector('#param-default'); + if (defaultInput) { + defaultInput.addEventListener('mousedown', function(e) { + e.stopPropagation(); + this.focus(); + }); + } + } + ensureDefaultInputClickable(); + + parameterFormElement.addEventListener('submit', function(e) { + e.preventDefault(); + + const name = paramNameInput.value.trim(); + const type = paramTypeSelect.value; + const defaultValue = el.querySelector('#param-default').value; + const description = paramDescriptionInput.value.trim(); + + if (!name) { + alert('Parameter name is required.'); + return; + } + + try { + if (editingParameterId) { + editor.parametersManager.updateParameter(editingParameterId, name, type, defaultValue, description); + } else { + editor.parametersManager.addParameter(name, type, defaultValue, description); + } + + hideParameterForm(); + renderParametersList(); + + // Update property validation autocomplete + if (editor.propertyValidation) { + editor.propertyValidation.addParameterAutocomplete(); + } + } catch (error) { + alert('Error saving parameter: ' + error.message); + } + }); + } }) }; \ No newline at end of file diff --git a/src/js/start.js b/src/js/start.js index 73949893..23794b3b 100644 --- a/src/js/start.js +++ b/src/js/start.js @@ -18,7 +18,9 @@ editor.import = new MD.Import(); editor.contextMenu = new MD.ContextMenu(); editor.darkmode = new MD.Darkmode(); editor.title = new MD.Title(); - +editor.parametersManager = new MD.Parameters(); +editor.propertyValidation = new MD.PropertyValidation(); + // bind the selected event to our function that handles updates to the UI svgCanvas.bind("selected", editor.selectedChanged); svgCanvas.bind("transition", editor.elementTransition); @@ -61,4 +63,9 @@ state.set("canvasTitle", svgCanvas.getDocumentTitle()); //editor.paintBox.canvas.setPaint(state.get("canvasBackground")); document.body.classList.remove("loading"); -document.getElementById("svgcanvas").removeAttribute("title"); \ No newline at end of file +document.getElementById("svgcanvas").removeAttribute("title"); + +// Initialize property validation for parameter references +setTimeout(() => { + editor.propertyValidation.initializeValidation(); +}, 100); \ No newline at end of file diff --git a/src/js/state.js b/src/js/state.js index e4cfd82a..2b99ab84 100644 --- a/src/js/state.js +++ b/src/js/state.js @@ -32,6 +32,7 @@ function State(){ this.canvasFill = (paint) => { /* noop */ } this.canvasStroke = (paint) => { /* noop */ } this.canvasBackground = (paint) => { /* noop */ } + this.canvasParameters = (params) => { /* noop */ } this.darkmode = (isDark) => { editor.darkmode.set(isDark) } this.clean = (warn = true) => { diff --git a/src/js/svgcanvas.js b/src/js/svgcanvas.js index 3513703c..c4d166a8 100644 --- a/src/js/svgcanvas.js +++ b/src/js/svgcanvas.js @@ -3099,6 +3099,8 @@ var getMouseTarget = this.getMouseTarget = function(evt) { selectorManager.requestSelector(selectedElements[i]).resize(); } } + + } // no change in position/size, so maybe we should move to pathedit From 8b95845e2db7b265aa9d78b35b2dbcc6f8e85974 Mon Sep 17 00:00:00 2001 From: smith120bh <42363318+smith120bh@users.noreply.github.com> Date: Wed, 23 Jul 2025 12:55:11 +1000 Subject: [PATCH 02/13] Fix syntax error --- src/js/method-draw.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/js/method-draw.js b/src/js/method-draw.js index 9e3b63a9..3038bcd0 100644 --- a/src/js/method-draw.js +++ b/src/js/method-draw.js @@ -1,4 +1,4 @@ -window.methodDraw = function() { +window.methodDraw = (function($) { var Editor = {}; var is_ready = false; var curConfig = { @@ -2070,7 +2070,6 @@ window.methodDraw = function() { } }; - }; var callbacks = []; @@ -2151,7 +2150,7 @@ window.methodDraw = function() { }; return Editor; -}(jQuery); +})(jQuery); // Run init once DOM is loaded $(methodDraw.init); \ No newline at end of file From 724cd56ac4efc39b5077efe2efc3f9ac8e022a49 Mon Sep 17 00:00:00 2001 From: smith120bh <42363318+smith120bh@users.noreply.github.com> Date: Wed, 23 Jul 2025 14:49:45 +1000 Subject: [PATCH 03/13] Add Parametric Clone Grids, for repeating elements --- src/index.html | 2 + src/js/ContextMenu.js | 155 ++++++++++++++++++---------- src/js/Menu.js | 3 + src/js/Panel.js | 20 ++++ src/js/Parameters.js | 6 +- src/js/editor.js | 169 +++++++++++++++++++++++++++++- src/js/method-draw.js | 55 +--------- src/js/modals.js | 232 ++++++++++++++++++++++++++++++++++++++++-- src/js/svgcanvas.js | 207 +++++++++++++++++++++++++++++++++++++ 9 files changed, 728 insertions(+), 121 deletions(-) diff --git a/src/index.html b/src/index.html index 57da29ac..0d2c6e77 100644 --- a/src/index.html +++ b/src/index.html @@ -118,6 +118,7 @@
+
@@ -740,6 +741,7 @@

Stroke

  • Delete
  • Group⌘G
  • Ungroup⌘⇧G
  • +
  • Define Parametric Clone...
  • Bring to Front⌘⇧↑
  • Bring Forward⌘↑
  • Send Backward⌘↓
  • diff --git a/src/js/ContextMenu.js b/src/js/ContextMenu.js index 332eb196..26fa50e4 100644 --- a/src/js/ContextMenu.js +++ b/src/js/ContextMenu.js @@ -1,56 +1,105 @@ MD.ContextMenu = function(){ + // Set up the context menu $("#workarea").contextMenu({ - menu: 'cmenu_canvas', - inSpeed: 0 - }, - function(action, el, pos) { - switch ( action ) { - case 'delete': - editor.deleteSelected(); + menu: 'cmenu_canvas', + inSpeed: 0 + }, + function(action, el, pos) { + switch ( action ) { + case 'delete': + svgCanvas.deleteSelectedElements(); + break; + case 'cut': + // Flash edit menu and call cut + var edit_menu_title = $('#edit_menu').prev(); + edit_menu_title.css({'background': 'white', 'color': 'black'}); + setTimeout(() => edit_menu_title.removeAttr('style'), 200); + svgCanvas.cutSelectedElements(); + break; + case 'copy': + // Flash edit menu and call copy + var edit_menu_title = $('#edit_menu').prev(); + edit_menu_title.css({'background': 'white', 'color': 'black'}); + setTimeout(() => edit_menu_title.removeAttr('style'), 200); + svgCanvas.copySelectedElements(); + break; + case 'paste': + // Flash edit menu and call paste with proper positioning + var edit_menu_title = $('#edit_menu').prev(); + edit_menu_title.css({'background': 'white', 'color': 'black'}); + setTimeout(() => edit_menu_title.removeAttr('style'), 200); + var zoom = svgCanvas.getZoom(); + var workarea = $("#workarea"); + var x = (workarea[0].scrollLeft + workarea.width()/2)/zoom - svgCanvas.contentW; + var y = (workarea[0].scrollTop + workarea.height()/2)/zoom - svgCanvas.contentH; + svgCanvas.pasteElements('point', x, y); + break; + case 'paste_in_place': + // Flash edit menu and call paste in place + var edit_menu_title = $('#edit_menu').prev(); + edit_menu_title.css({'background': 'white', 'color': 'black'}); + setTimeout(() => edit_menu_title.removeAttr('style'), 200); + svgCanvas.pasteElements('in_place'); + break; + case 'group': + svgCanvas.groupSelectedElements(); + break; + case 'ungroup': + svgCanvas.ungroupSelectedElement(); + break; + case 'move_front': + // Flash object menu and call move front + var object_menu_title = $('#object_menu').prev(); + object_menu_title.css({'background': 'white', 'color': 'black'}); + setTimeout(() => object_menu_title.removeAttr('style'), 200); + svgCanvas.moveToTopSelectedElement(); + break; + case 'move_up': + // Flash object menu and call move up + var object_menu_title = $('#object_menu').prev(); + object_menu_title.css({'background': 'white', 'color': 'black'}); + setTimeout(() => object_menu_title.removeAttr('style'), 200); + svgCanvas.moveUpDownSelected('Up'); + break; + case 'move_down': + // Flash object menu and call move down + var object_menu_title = $('#object_menu').prev(); + object_menu_title.css({'background': 'white', 'color': 'black'}); + setTimeout(() => object_menu_title.removeAttr('style'), 200); + svgCanvas.moveUpDownSelected('Down'); + break; + case 'move_back': + // Flash object menu and call move back + var object_menu_title = $('#object_menu').prev(); + object_menu_title.css({'background': 'white', 'color': 'black'}); + setTimeout(() => object_menu_title.removeAttr('style'), 200); + svgCanvas.moveToBottomSelectedElement(); + break; + case 'parametric_clone': + // Check if we're editing an existing parametric clone + var selectedElements = svgCanvas.getSelectedElems(); + var isParametricClone = selectedElements[0] && selectedElements[0].getAttribute('data-parametric-clone') === 'true'; + + if (isParametricClone) { + editor.editParametricClone(selectedElements[0]); + } else { + editor.modal.parametricClone.open(); + } + break; + default: + if(svgedit.contextmenu && svgedit.contextmenu.hasCustomHandler(action)){ + svgedit.contextmenu.getCustomHandler(action).call(); + } break; - case 'cut': - editor.cutSelected(); - break; - case 'copy': - editor.copySelected(); - break; - case 'paste': - editor.pasteSelected(); - break; - case 'paste_in_place': - svgCanvas.pasteElements('in_place'); - break; - case 'group': - editor.groupSelected(); - break; - case 'ungroup': - editor.ungroupSelected(); - break; - case 'move_front': - editor.moveToTopSelected(); - break; - case 'move_up': - editor.moveUpSelected(); - break; - case 'move_down': - editor.moveDownSelected(); - break; - case 'move_back': - editor.moveToBottomSelected(); - break; - default: - if(svgedit.contextmenu && svgedit.contextmenu.hasCustomHandler(action)){ - svgedit.contextmenu.getCustomHandler(action).call(); - } - break; - } - - }); - - $('.contextMenu li').mousedown(function(ev) { - ev.preventDefault(); - }) - - $('#cmenu_canvas li').disableContextMenu(); - $("#cmenu_canvas").enableContextMenuItems('#delete,#cut,#copy'); -} \ No newline at end of file + } + + }); + + $('.contextMenu li').mousedown(function(ev) { + ev.preventDefault(); + }) + + $('#cmenu_canvas li').disableContextMenu(); + var canv_menu = $("#cmenu_canvas"); + canv_menu.enableContextMenuItems('#delete,#cut,#copy'); +}; \ No newline at end of file diff --git a/src/js/Menu.js b/src/js/Menu.js index 7104cb39..4c494a59 100644 --- a/src/js/Menu.js +++ b/src/js/Menu.js @@ -8,6 +8,9 @@ MD.Menu = function(){ $('#tool_topath').on("click", editor.convertToPath); $('#tool_group').on("click", editor.groupSelected); $('#tool_ungroup').on("click", editor.ungroupSelected); + $('#tool_parametric_clone').on("click", function() { + editor.modal.parametricClone.open(); + }); if (window.location.host === "editor.method.ac") { $('#modal_donate').show(); $('#sponsors').show(); diff --git a/src/js/Panel.js b/src/js/Panel.js index b4efbaa3..6ac5a6c2 100644 --- a/src/js/Panel.js +++ b/src/js/Panel.js @@ -348,6 +348,23 @@ MD.Panel = function(){ .disableContextMenuItems('#ungroup'); } + // Handle parametric clone menu items + const isParametricClone = elem && elem.getAttribute('data-parametric-clone') === 'true'; + + if (isParametricClone) { + // Change menu text to "Edit Parametric Clone..." for parametric clones + $('#cmenu_canvas a[href="#parametric_clone"]').text('Edit Parametric Clone...'); + menu_items.enableContextMenuItems('#parametric_clone'); + } else if (elem && !multiselected) { + // Show "Define Parametric Clone..." for non-parametric elements + $('#cmenu_canvas a[href="#parametric_clone"]').text('Define Parametric Clone...'); + menu_items.enableContextMenuItems('#parametric_clone'); + } else { + // Disable for no selection or multiselection + $('#cmenu_canvas a[href="#parametric_clone"]').text('Define Parametric Clone...'); + menu_items.disableContextMenuItems('#parametric_clone'); + } + if (!elem && !multiselected) { menu_items.disableContextMenuItems('#delete,#cut,#copy,#ungroup,#move_front,#move_up,#move_down,#move_back'); $('.menu_item.action_selected', '#edit_menu').addClass('disabled'); @@ -355,6 +372,9 @@ MD.Panel = function(){ $('.menu_item', '#object_menu').toggleClass('disabled', !elem && !multiselected); + // Also ensure Object menu parametric clone item follows the same logic + $('#tool_parametric_clone').toggleClass('disabled', !elem || multiselected); + // update history buttons setTimeout(function(){ $('#tool_paste').toggleClass( 'disabled', !svgCanvas.clipBoard.length > 0); diff --git a/src/js/Parameters.js b/src/js/Parameters.js index d48fb966..cf271c3d 100644 --- a/src/js/Parameters.js +++ b/src/js/Parameters.js @@ -10,7 +10,11 @@ MD.Parameters = function(){ number: { label: 'Number', defaultValue: 0 }, text: { label: 'Text', defaultValue: '' }, color: { label: 'Color', defaultValue: '#000000' }, - boolean: { label: 'Boolean', defaultValue: false } + boolean: { label: 'Boolean', defaultValue: false }, + grid_cols: { label: 'Grid Columns', defaultValue: 3 }, + grid_rows: { label: 'Grid Rows', defaultValue: 2 }, + grid_spacing_x: { label: 'Horizontal Spacing', defaultValue: 50 }, + grid_spacing_y: { label: 'Vertical Spacing', defaultValue: 50 } }; // Get current parameters from state diff --git a/src/js/editor.js b/src/js/editor.js index de0ba979..d451e939 100644 --- a/src/js/editor.js +++ b/src/js/editor.js @@ -395,12 +395,75 @@ MD.Editor = function(){ } }); - // Clean up data-param-* attributes from the final output + // Handle parametric clone groups + const cloneGroups = svgDoc.querySelectorAll('[data-parametric-clone="true"]'); + const cloneHelperFunctions = []; + + cloneGroups.forEach((cloneGroup, index) => { + const colsParam = cloneGroup.getAttribute('data-cols-param'); + const rowsParam = cloneGroup.getAttribute('data-rows-param'); + const spacingXParam = cloneGroup.getAttribute('data-spacing-x-param'); + const spacingYParam = cloneGroup.getAttribute('data-spacing-y-param'); + + // Find template group + const templateGroup = cloneGroup.querySelector('[data-template="true"]'); + if (templateGroup) { + // Get template elements as string + const templateElements = Array.from(templateGroup.children).map(child => { + return new XMLSerializer().serializeToString(child); + }).join(''); + + // Generate helper function name + const funcName = `generateCloneGrid_${index}`; + + // Create helper function + const helperFunction = ` + function ${funcName}() { + const cols = ${colsParam}; + const rows = ${rowsParam}; + const spacingX = ${spacingXParam}; + const spacingY = ${spacingYParam}; + + let elements = ''; + for (let row = 0; row < rows; row++) { + for (let col = 0; col < cols; col++) { + const offsetX = col * spacingX; + const offsetY = row * spacingY; + + if (offsetX === 0 && offsetY === 0) { + // Original position - just add template elements + elements += \`${templateElements.replace(/`/g, '\\`')}\`; + } else { + // Clone position - wrap in transform group + elements += \`${templateElements.replace(/`/g, '\\`')}\`; + } + } + } + return elements; + }`; + + cloneHelperFunctions.push(helperFunction); + + // Replace the clone group with a placeholder that calls the helper function + const placeholder = svgDoc.createElement('g'); + placeholder.setAttribute('id', cloneGroup.id); + placeholder.innerHTML = `\${${funcName}()}`; + cloneGroup.parentNode.replaceChild(placeholder, cloneGroup); + } + }); + + // Clean up data-param-* attributes and parametric clone attributes from the final output allElements.forEach(elem => { const attributesToRemove = []; for (let i = 0; i < elem.attributes.length; i++) { const attr = elem.attributes[i]; - if (attr.name.startsWith('data-param-')) { + if (attr.name.startsWith('data-param-') || + attr.name.startsWith('data-parametric-') || + attr.name.startsWith('data-cols-') || + attr.name.startsWith('data-rows-') || + attr.name.startsWith('data-spacing-') || + attr.name === 'data-template' || + attr.name === 'data-clone-instance') { attributesToRemove.push(attr.name); } } @@ -446,7 +509,7 @@ MD.Editor = function(){ ? paramNames.map((name, i) => ` const ${name}_val = ${name} !== undefined ? ${name} : ${paramDefaults[i]};`).join('\n') : ''; const variableDeclaration = hasParams - ? ` const ${paramNames.join(', ')} = ${paramNames.map(name => `${name}_val`).join(', ')};` + ? paramNames.map(name => ` const ${name} = ${name}_val;`).join('\n') : ''; const functionBody = `/** @@ -459,6 +522,7 @@ ${paramComment} function generateSVG(${functionParams}) { ${defaultAssignments} ${variableDeclaration} +${cloneHelperFunctions.join('')} return \`${escapedSvg}\`; } @@ -503,6 +567,103 @@ if (typeof module !== 'undefined' && module.exports) { } } + function createParametricClone(cols, rows, spacingX, spacingY) { + // Check if we have selected elements + const selectedElements = svgCanvas.getSelectedElems(); + if (!selectedElements || selectedElements.length === 0 || !selectedElements[0]) { + alert('Please select one or more elements to create a parametric clone.'); + return; + } + + // Generate unique parameter names based on timestamp + const timestamp = Date.now(); + const colsParamName = `clone_cols_${timestamp}`; + const rowsParamName = `clone_rows_${timestamp}`; + const spacingXParamName = `clone_spacing_x_${timestamp}`; + const spacingYParamName = `clone_spacing_y_${timestamp}`; + + try { + // Add the grid parameters to the parameter system + editor.parametersManager.addParameter(colsParamName, 'grid_cols', cols, 'Number of columns in the grid'); + editor.parametersManager.addParameter(rowsParamName, 'grid_rows', rows, 'Number of rows in the grid'); + editor.parametersManager.addParameter(spacingXParamName, 'grid_spacing_x', spacingX, 'Horizontal spacing between elements'); + editor.parametersManager.addParameter(spacingYParamName, 'grid_spacing_y', spacingY, 'Vertical spacing between elements'); + + // Create the parametric clone group using SVG canvas + const cloneGroupId = svgCanvas.createParametricCloneGroup( + selectedElements, + colsParamName, + rowsParamName, + spacingXParamName, + spacingYParamName + ); + + if (cloneGroupId) { + // Select the new group + const cloneGroup = svgedit.utilities.getElem(cloneGroupId); + if (cloneGroup) { + svgCanvas.clearSelection(); + svgCanvas.addToSelection([cloneGroup]); + } + + saveCanvas(); + alert(`Parametric clone created with ${cols}×${rows} grid pattern.`); + } + } catch (error) { + alert('Error creating parametric clone: ' + error.message); + } + } + + function editParametricClone(cloneGroup) { + if (!cloneGroup || cloneGroup.getAttribute('data-parametric-clone') !== 'true') { + alert('Selected element is not a parametric clone.'); + return; + } + + // Get parameter names from the clone group + const colsParam = cloneGroup.getAttribute('data-cols-param'); + const rowsParam = cloneGroup.getAttribute('data-rows-param'); + const spacingXParam = cloneGroup.getAttribute('data-spacing-x-param'); + const spacingYParam = cloneGroup.getAttribute('data-spacing-y-param'); + + if (!colsParam || !rowsParam || !spacingXParam || !spacingYParam) { + alert('Parametric clone data is corrupted.'); + return; + } + + // Get current parameter values + const colsParamObj = editor.parametersManager.getParameterByName(colsParam); + const rowsParamObj = editor.parametersManager.getParameterByName(rowsParam); + const spacingXParamObj = editor.parametersManager.getParameterByName(spacingXParam); + const spacingYParamObj = editor.parametersManager.getParameterByName(spacingYParam); + + if (!colsParamObj || !rowsParamObj || !spacingXParamObj || !spacingYParamObj) { + alert('Could not find associated parameters for this parametric clone.'); + return; + } + + // Set up the modal with current values + const modal = editor.modal.parametricClone; + modal.open(); + + // Pre-populate the form with current values + setTimeout(() => { + const modalEl = modal.el; + modalEl.querySelector('#clone-cols').value = colsParamObj.defaultValue; + modalEl.querySelector('#clone-rows').value = rowsParamObj.defaultValue; + modalEl.querySelector('#clone-spacing-x').value = spacingXParamObj.defaultValue; + modalEl.querySelector('#clone-spacing-y').value = spacingYParamObj.defaultValue; + + // Store the parameter names for updating + modalEl.setAttribute('data-editing-clone', 'true'); + modalEl.setAttribute('data-cols-param', colsParam); + modalEl.setAttribute('data-rows-param', rowsParam); + modalEl.setAttribute('data-spacing-x-param', spacingXParam); + modalEl.setAttribute('data-spacing-y-param', spacingYParam); + modalEl.setAttribute('data-clone-group-id', cloneGroup.id); + }, 50); + } + function about(){ editor.modal.about.open(); } @@ -559,6 +720,8 @@ if (typeof module !== 'undefined' && module.exports) { this.changeAttribute = changeAttribute; this.contextChanged = contextChanged; this.elementTransition = elementTransition; + this.createParametricClone = createParametricClone; + this.editParametricClone = editParametricClone; this.switchPaint = switchPaint; this.focusPaint = focusPaint; this.save = save; diff --git a/src/js/method-draw.js b/src/js/method-draw.js index 3038bcd0..36ab2634 100644 --- a/src/js/method-draw.js +++ b/src/js/method-draw.js @@ -1722,60 +1722,7 @@ window.methodDraw = (function($) { // Set default zoom $('#zoom').val(svgCanvas.getZoom() * 100); - $("#workarea").contextMenu({ - menu: 'cmenu_canvas', - inSpeed: 0 - }, - function(action, el, pos) { - switch ( action ) { - case 'delete': - deleteSelected(); - break; - case 'cut': - cutSelected(); - break; - case 'copy': - copySelected(); - break; - case 'paste': - svgCanvas.pasteElements(); - break; - case 'paste_in_place': - svgCanvas.pasteElements('in_place'); - break; - case 'group': - svgCanvas.groupSelectedElements(); - break; - case 'ungroup': - svgCanvas.ungroupSelectedElement(); - break; - case 'move_front': - moveToTopSelected(); - break; - case 'move_up': - moveUpDownSelected('Up'); - break; - case 'move_down': - moveUpDownSelected('Down'); - break; - case 'move_back': - moveToBottomSelected(); - break; - default: - if(svgedit.contextmenu && svgedit.contextmenu.hasCustomHandler(action)){ - svgedit.contextmenu.getCustomHandler(action).call(); - } - break; - } - - }); - - $('.contextMenu li').mousedown(function(ev) { - ev.preventDefault(); - }) - - $('#cmenu_canvas li').disableContextMenu(); - canv_menu.enableContextMenuItems('#delete,#cut,#copy'); + Editor.openPrep = function(func) { $('#main_menu').hide(); diff --git a/src/js/modals.js b/src/js/modals.js index 55671059..b16bddae 100644 --- a/src/js/modals.js +++ b/src/js/modals.js @@ -234,25 +234,99 @@ editor.modal = { parametersEmpty.style.display = 'none'; parametersTBody.innerHTML = ''; + + // Group parameters by parametric clone groups + const cloneGroups = {}; + const regularParams = []; + paramIds.forEach(id => { const param = parameters[id]; + // Check if this is a parametric clone parameter by looking for the timestamp pattern + const timestampMatch = param.name.match(/^clone_(cols|rows|spacing_x|spacing_y)_(\d+)$/); + if (timestampMatch) { + const timestamp = timestampMatch[2]; + if (!cloneGroups[timestamp]) { + cloneGroups[timestamp] = { + cols: null, + rows: null, + spacing_x: null, + spacing_y: null + }; + } + cloneGroups[timestamp][timestampMatch[1]] = { id, param }; + } else { + regularParams.push({ id, param }); + } + }); + + // Render regular parameters first + regularParams.forEach(({ id, param }) => { const row = document.createElement('tr'); row.style.borderBottom = '1px solid #eee'; - row.innerHTML = - '@' + param.name + '' + - '' + editor.parametersManager.PARAM_TYPES[param.type].label + '' + - '' + param.defaultValue + '' + - '' + - '' + - '' + - ''; + row.innerHTML = + '@' + param.name + '' + + '' + editor.parametersManager.PARAM_TYPES[param.type].label + '' + + '' + param.defaultValue + '' + + '' + + '' + + '' + + ''; parametersTBody.appendChild(row); }); + // Add separator if we have both regular params and clone groups + if (regularParams.length > 0 && Object.keys(cloneGroups).length > 0) { + const separatorRow = document.createElement('tr'); + separatorRow.innerHTML = 'Parametric Clone Groups'; + parametersTBody.appendChild(separatorRow); + } + + // Render parametric clone groups + Object.keys(cloneGroups).forEach(timestamp => { + const group = cloneGroups[timestamp]; + + // Create group header + const headerRow = document.createElement('tr'); + headerRow.style.background = '#f5f5f5'; + headerRow.innerHTML = 'Clone Group ' + timestamp + ''; + parametersTBody.appendChild(headerRow); + + // Render group parameters in order: cols, rows, spacing_x, spacing_y + ['cols', 'rows', 'spacing_x', 'spacing_y'].forEach(type => { + if (group[type]) { + const { id, param } = group[type]; + const row = document.createElement('tr'); + row.style.borderBottom = '1px solid #eee'; + row.style.paddingLeft = '16px'; + + // Friendly names for grid parameters + const friendlyNames = { + cols: 'Columns', + rows: 'Rows', + spacing_x: 'Horizontal Spacing', + spacing_y: 'Vertical Spacing' + }; + + row.innerHTML = + '└ ' + friendlyNames[type] + '' + + '' + editor.parametersManager.PARAM_TYPES[param.type].label + '' + + '' + param.defaultValue + '' + + '' + + '' + + '' + + ''; + + parametersTBody.appendChild(row); + } + }); + }); + // Add event listeners for edit/delete buttons el.querySelectorAll('.edit-param-btn').forEach(btn => { btn.addEventListener('click', function() { @@ -388,5 +462,143 @@ editor.modal = { } }); } + }), + + parametricClone: new MD.Modal({ + html: ` +

    Define Parametric Clone

    +

    + Create a parametric grid of cloned elements with configurable spacing and dimensions. +

    + +
    +
    +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    +
    + +
    + + +
    +
    + `, + + js: function(el) { + // Handle cancel button + el.querySelector('#parametric-clone-cancel').addEventListener('click', function() { + editor.modal.parametricClone.close(); + }); + + // Handle create button + el.querySelector('#parametric-clone-create').addEventListener('click', function() { + const cols = parseInt(el.querySelector('#clone-cols').value) || 3; + const rows = parseInt(el.querySelector('#clone-rows').value) || 2; + const spacingX = parseInt(el.querySelector('#clone-spacing-x').value) || 50; + const spacingY = parseInt(el.querySelector('#clone-spacing-y').value) || 50; + + const isEditing = el.getAttribute('data-editing-clone') === 'true'; + + if (isEditing) { + // Update existing parametric clone + const colsParam = el.getAttribute('data-cols-param'); + const rowsParam = el.getAttribute('data-rows-param'); + const spacingXParam = el.getAttribute('data-spacing-x-param'); + const spacingYParam = el.getAttribute('data-spacing-y-param'); + const cloneGroupId = el.getAttribute('data-clone-group-id'); + + try { + // Update parameter values + const parametersManager = editor.parametersManager; + const colsParamObj = parametersManager.getParameterByName(colsParam); + const rowsParamObj = parametersManager.getParameterByName(rowsParam); + const spacingXParamObj = parametersManager.getParameterByName(spacingXParam); + const spacingYParamObj = parametersManager.getParameterByName(spacingYParam); + + if (colsParamObj && rowsParamObj && spacingXParamObj && spacingYParamObj) { + // Get parameter IDs from the parameters object + const parameters = parametersManager.getParameters(); + let colsId, rowsId, spacingXId, spacingYId; + + Object.keys(parameters).forEach(id => { + const param = parameters[id]; + if (param.name === colsParam) colsId = id; + if (param.name === rowsParam) rowsId = id; + if (param.name === spacingXParam) spacingXId = id; + if (param.name === spacingYParam) spacingYId = id; + }); + + if (colsId) parametersManager.updateParameter(colsId, colsParam, 'grid_cols', cols, colsParamObj.description); + if (rowsId) parametersManager.updateParameter(rowsId, rowsParam, 'grid_rows', rows, rowsParamObj.description); + if (spacingXId) parametersManager.updateParameter(spacingXId, spacingXParam, 'grid_spacing_x', spacingX, spacingXParamObj.description); + if (spacingYId) parametersManager.updateParameter(spacingYId, spacingYParam, 'grid_spacing_y', spacingY, spacingYParamObj.description); + + // Regenerate the clone group + if (typeof svgCanvas.updateParametricCloneGroup === 'function') { + svgCanvas.updateParametricCloneGroup(cloneGroupId); + } + + alert('Parametric clone updated successfully.'); + } + } catch (error) { + alert('Error updating parametric clone: ' + error.message); + } + + // Clear editing state + el.removeAttribute('data-editing-clone'); + el.removeAttribute('data-cols-param'); + el.removeAttribute('data-rows-param'); + el.removeAttribute('data-spacing-x-param'); + el.removeAttribute('data-spacing-y-param'); + el.removeAttribute('data-clone-group-id'); + } else { + // Create new parametric clone + if (typeof editor.createParametricClone === 'function') { + editor.createParametricClone(cols, rows, spacingX, spacingY); + } + } + + editor.modal.parametricClone.close(); + }); + + // Ensure input fields are clickable (fix for modal z-index issues) + el.querySelector('#clone-cols').addEventListener('mousedown', function(e) { + e.stopPropagation(); + this.focus(); + }); + el.querySelector('#clone-rows').addEventListener('mousedown', function(e) { + e.stopPropagation(); + this.focus(); + }); + el.querySelector('#clone-spacing-x').addEventListener('mousedown', function(e) { + e.stopPropagation(); + this.focus(); + }); + el.querySelector('#clone-spacing-y').addEventListener('mousedown', function(e) { + e.stopPropagation(); + this.focus(); + }); + } }) }; \ No newline at end of file diff --git a/src/js/svgcanvas.js b/src/js/svgcanvas.js index c4d166a8..1f49dcd5 100644 --- a/src/js/svgcanvas.js +++ b/src/js/svgcanvas.js @@ -8124,6 +8124,213 @@ this.groupSelectedElements = function(type) { selectOnly([g], true); }; +// Function: createParametricCloneGroup +// Creates a parametric clone group with grid layout +this.createParametricCloneGroup = function(templateElements, colsParamName, rowsParamName, spacingXParamName, spacingYParamName) { + if (!templateElements || templateElements.length === 0) { + return null; + } + + // Get the parameter values + const cols = editor.parametersManager.resolveParameterValue('@' + colsParamName); + const rows = editor.parametersManager.resolveParameterValue('@' + rowsParamName); + const spacingX = editor.parametersManager.resolveParameterValue('@' + spacingXParamName); + const spacingY = editor.parametersManager.resolveParameterValue('@' + spacingYParamName); + + const batchCmd = new BatchCommand("Create Parametric Clone"); + + // Create the main parametric clone group + const cloneGroupId = getNextId(); + const cloneGroup = addSvgElementFromJson({ + "element": "g", + "attr": { + "id": cloneGroupId, + "data-parametric-clone": "true", + "data-cols-param": colsParamName, + "data-rows-param": rowsParamName, + "data-spacing-x-param": spacingXParamName, + "data-spacing-y-param": spacingYParamName + } + }); + + // Create template group to hold the original elements + const templateGroupId = getNextId(); + const templateGroup = addSvgElementFromJson({ + "element": "g", + "attr": { + "id": templateGroupId, + "data-template": "true" + } + }); + + cloneGroup.appendChild(templateGroup); + + // Move template elements to template group and get their bounding box + let templateBBox = null; + templateElements.forEach(elem => { + if (elem) { + const oldParent = elem.parentNode; + const oldNextSibling = elem.nextSibling; + templateGroup.appendChild(elem); + batchCmd.addSubCommand(new MoveElementCommand(elem, oldNextSibling, oldParent)); + + // Calculate bounding box of template elements + try { + const elemBBox = getStrokedBBox([elem]); + if (elemBBox) { + if (!templateBBox) { + templateBBox = { + x: elemBBox.x, + y: elemBBox.y, + width: elemBBox.width, + height: elemBBox.height + }; + } else { + const x2 = Math.max(templateBBox.x + templateBBox.width, elemBBox.x + elemBBox.width); + const y2 = Math.max(templateBBox.y + templateBBox.height, elemBBox.y + elemBBox.height); + templateBBox.x = Math.min(templateBBox.x, elemBBox.x); + templateBBox.y = Math.min(templateBBox.y, elemBBox.y); + templateBBox.width = x2 - templateBBox.x; + templateBBox.height = y2 - templateBBox.y; + } + } + } catch (e) { + console.warn('Could not get bounding box for element:', elem); + } + } + }); + + // If we couldn't get a bounding box, use a default offset + if (!templateBBox) { + templateBBox = { x: 0, y: 0, width: 50, height: 50 }; + } + + // Generate grid of clones + for (let row = 0; row < rows; row++) { + for (let col = 0; col < cols; col++) { + // Skip the first position (0,0) since that's where the template is + if (row === 0 && col === 0) continue; + + const cloneInstanceGroup = addSvgElementFromJson({ + "element": "g", + "attr": { + "id": getNextId(), + "data-clone-instance": "true", + "data-row": row, + "data-col": col + } + }); + + // Calculate position offset + const offsetX = col * spacingX; + const offsetY = row * spacingY; + + // Set transform for positioning + if (offsetX !== 0 || offsetY !== 0) { + cloneInstanceGroup.setAttribute("transform", `translate(${offsetX},${offsetY})`); + } + + // Clone all template elements + templateElements.forEach(elem => { + if (elem) { + const clone = copyElem(elem); + cloneInstanceGroup.appendChild(clone); + } + }); + + cloneGroup.appendChild(cloneInstanceGroup); + } + } + + batchCmd.addSubCommand(new InsertElementCommand(cloneGroup)); + addCommandToHistory(batchCmd); + + call("changed", [cloneGroup]); + return cloneGroupId; +}; + +// Function: updateParametricCloneGroup +// Updates an existing parametric clone group with new parameter values +this.updateParametricCloneGroup = function(cloneGroupId) { + const cloneGroup = getElem(cloneGroupId); + if (!cloneGroup || cloneGroup.getAttribute('data-parametric-clone') !== 'true') { + return false; + } + + // Get parameter names + const colsParamName = cloneGroup.getAttribute('data-cols-param'); + const rowsParamName = cloneGroup.getAttribute('data-rows-param'); + const spacingXParamName = cloneGroup.getAttribute('data-spacing-x-param'); + const spacingYParamName = cloneGroup.getAttribute('data-spacing-y-param'); + + // Get updated parameter values + const cols = editor.parametersManager.resolveParameterValue('@' + colsParamName); + const rows = editor.parametersManager.resolveParameterValue('@' + rowsParamName); + const spacingX = editor.parametersManager.resolveParameterValue('@' + spacingXParamName); + const spacingY = editor.parametersManager.resolveParameterValue('@' + spacingYParamName); + + const batchCmd = new BatchCommand("Update Parametric Clone"); + + // Find template group + const templateGroup = cloneGroup.querySelector('[data-template="true"]'); + if (!templateGroup) { + console.error('Template group not found in parametric clone'); + return false; + } + + // Get template elements + const templateElements = Array.from(templateGroup.children); + + // Remove all existing clone instances + const cloneInstances = cloneGroup.querySelectorAll('[data-clone-instance="true"]'); + cloneInstances.forEach(instance => { + batchCmd.addSubCommand(new RemoveElementCommand(instance, instance.nextSibling, instance.parentNode)); + instance.parentNode.removeChild(instance); + }); + + // Generate new grid of clones + for (let row = 0; row < rows; row++) { + for (let col = 0; col < cols; col++) { + // Skip the first position (0,0) since that's where the template is + if (row === 0 && col === 0) continue; + + const cloneInstanceGroup = addSvgElementFromJson({ + "element": "g", + "attr": { + "id": getNextId(), + "data-clone-instance": "true", + "data-row": row, + "data-col": col + } + }); + + // Calculate position offset + const offsetX = col * spacingX; + const offsetY = row * spacingY; + + // Set transform for positioning + if (offsetX !== 0 || offsetY !== 0) { + cloneInstanceGroup.setAttribute("transform", `translate(${offsetX},${offsetY})`); + } + + // Clone all template elements + templateElements.forEach(elem => { + if (elem) { + const clone = copyElem(elem); + cloneInstanceGroup.appendChild(clone); + } + }); + + cloneGroup.appendChild(cloneInstanceGroup); + batchCmd.addSubCommand(new InsertElementCommand(cloneInstanceGroup)); + } + } + + addCommandToHistory(batchCmd); + call("changed", [cloneGroup]); + return true; +}; + // Function: pushGroupProperties // Pushes all appropriate parent group properties down to its children, then From 3458d210d2083707356b6941ebff6360e1007164 Mon Sep 17 00:00:00 2001 From: smith120bh <42363318+smith120bh@users.noreply.github.com> Date: Wed, 23 Jul 2025 15:24:37 +1000 Subject: [PATCH 04/13] Add equation parameter type --- src/js/Parameters.js | 115 +++++++++++++++++++++++++++++++++-- src/js/PropertyValidation.js | 15 ++++- src/js/editor.js | 57 +++++++++++++---- src/js/modals.js | 5 ++ 4 files changed, 174 insertions(+), 18 deletions(-) diff --git a/src/js/Parameters.js b/src/js/Parameters.js index cf271c3d..df05103e 100644 --- a/src/js/Parameters.js +++ b/src/js/Parameters.js @@ -11,6 +11,7 @@ MD.Parameters = function(){ text: { label: 'Text', defaultValue: '' }, color: { label: 'Color', defaultValue: '#000000' }, boolean: { label: 'Boolean', defaultValue: false }, + equation: { label: 'Equation', defaultValue: '0' }, grid_cols: { label: 'Grid Columns', defaultValue: 3 }, grid_rows: { label: 'Grid Rows', defaultValue: 2 }, grid_spacing_x: { label: 'Horizontal Spacing', defaultValue: 50 }, @@ -133,6 +134,79 @@ MD.Parameters = function(){ return Object.values(params).map(param => param.name); } + // Evaluate equation with parameter references + function evaluateEquation(equation, visited = new Set()) { + if (typeof equation !== 'string') { + return equation; + } + + // Replace parameter references with their values + let processedEquation = equation; + const paramReferenceRegex = /@([a-zA-Z_][a-zA-Z0-9_]*)/g; + let match; + + while ((match = paramReferenceRegex.exec(equation)) !== null) { + const paramName = match[1]; + const fullMatch = match[0]; // e.g., "@width" + + // Check for circular dependencies + if (visited.has(paramName)) { + console.error(`Circular dependency detected involving parameter: ${paramName}`); + return 0; + } + + const param = getParameterByName(paramName); + if (!param) { + console.warn(`Parameter @${paramName} not found in equation, treating as 0`); + processedEquation = processedEquation.replace(fullMatch, '0'); + continue; + } + + let resolvedValue; + if (param.type === 'equation') { + // Recursively resolve equation parameters + const newVisited = new Set(visited); + newVisited.add(paramName); + resolvedValue = evaluateEquation(param.defaultValue, newVisited); + } else { + resolvedValue = resolveParameterValueDirect(param); + } + + // Replace the parameter reference with its resolved value + processedEquation = processedEquation.replace(fullMatch, resolvedValue); + } + + // Evaluate the mathematical expression + try { + // Basic safety check - only allow numbers, operators, parentheses, and whitespace + if (!/^[0-9+\-*/.() \t]+$/.test(processedEquation)) { + throw new Error('Invalid characters in equation'); + } + + // Use Function constructor for safe evaluation (better than eval) + const result = new Function('return ' + processedEquation)(); + + if (typeof result !== 'number' || isNaN(result)) { + throw new Error('Equation did not evaluate to a number'); + } + + return result; + } catch (error) { + console.error(`Error evaluating equation "${equation}": ${error.message}`); + return 0; + } + } + + // Helper function to resolve parameter value directly (without equation evaluation) + function resolveParameterValueDirect(param) { + const resolvedValue = param.defaultValue; + if (param.type === 'number') { + const num = parseFloat(resolvedValue); + return isNaN(num) ? 0 : num; + } + return resolvedValue; + } + // Resolve parameter reference (e.g., "@width" -> actual value) function resolveParameterValue(value) { if (typeof value !== 'string' || !value.startsWith('@')) { @@ -147,14 +221,13 @@ MD.Parameters = function(){ return value; } - // Ensure proper type conversion - const resolvedValue = param.defaultValue; - if (param.type === 'number') { - const num = parseFloat(resolvedValue); - return isNaN(num) ? 0 : num; + // Handle equation type parameters + if (param.type === 'equation') { + return evaluateEquation(param.defaultValue); } - return resolvedValue; + // Ensure proper type conversion for other types + return resolveParameterValueDirect(param); } // Check if a value is a parameter reference @@ -162,6 +235,29 @@ MD.Parameters = function(){ return typeof value === 'string' && value.startsWith('@') && value.length > 1; } + // Validate equation syntax + function validateEquationSyntax(equation) { + if (typeof equation !== 'string') { + return false; + } + + // Basic syntax validation - check for valid characters only + if (!/^[0-9+\-*/.()@ \t_a-zA-Z]+$/.test(equation)) { + return false; + } + + // Check for valid parameter references + const paramReferenceRegex = /@([a-zA-Z_][a-zA-Z0-9_]*)/g; + let match; + while ((match = paramReferenceRegex.exec(equation)) !== null) { + const paramName = match[1]; + // Allow parameter references even if they don't exist yet (for forward references) + // The actual resolution will handle missing parameters gracefully + } + + return true; + } + // Validate parameter value for its type function validateParameterValue(value, type) { switch (type) { @@ -178,6 +274,11 @@ MD.Parameters = function(){ return PARAM_TYPES.color.defaultValue; case 'boolean': return Boolean(value); + case 'equation': + if (validateEquationSyntax(value)) { + return String(value); + } + return PARAM_TYPES.equation.defaultValue; default: return value; } @@ -194,6 +295,8 @@ MD.Parameters = function(){ this.resolveParameterValue = resolveParameterValue; this.isParameterReference = isParameterReference; this.validateParameterValue = validateParameterValue; + this.validateEquationSyntax = validateEquationSyntax; + this.evaluateEquation = evaluateEquation; this.isValidParameterName = isValidParameterName; this.parameterExists = parameterExists; this.PARAM_TYPES = PARAM_TYPES; diff --git a/src/js/PropertyValidation.js b/src/js/PropertyValidation.js index 1b9e09fd..66eb670e 100644 --- a/src/js/PropertyValidation.js +++ b/src/js/PropertyValidation.js @@ -20,7 +20,20 @@ MD.PropertyValidation = function() { // Check if it's a valid parameter reference if (PARAM_REFERENCE_PATTERN.test(value)) { const paramName = value.substring(1); - return editor.parametersManager.getParameterByName(paramName) !== null; + const param = editor.parametersManager.getParameterByName(paramName); + if (!param) return false; + + // For equation parameters, validate that they can be resolved to a number + if (param.type === 'equation') { + try { + const resolvedValue = editor.parametersManager.resolveParameterValue(value); + return typeof resolvedValue === 'number' && !isNaN(resolvedValue); + } catch (error) { + return false; + } + } + + return true; } return false; diff --git a/src/js/editor.js b/src/js/editor.js index d451e939..00b2fb15 100644 --- a/src/js/editor.js +++ b/src/js/editor.js @@ -476,9 +476,21 @@ MD.Editor = function(){ const serializer = new XMLSerializer(); let modifiedSvgString = serializer.serializeToString(svgDoc.documentElement); - // Generate parameter list and default values - const paramNames = Object.keys(paramMap); - const paramDefaults = paramNames.map(name => { + // Separate input parameters from equation parameters + const inputParams = []; + const equationParams = []; + + Object.keys(paramMap).forEach(name => { + const param = paramMap[name]; + if (param.type === 'equation') { + equationParams.push(name); + } else { + inputParams.push(name); + } + }); + + // Generate parameter list and default values for input parameters only + const paramDefaults = inputParams.map(name => { const param = paramMap[name]; let defaultValue = param.defaultValue; @@ -498,18 +510,40 @@ MD.Editor = function(){ .replace(/`/g, '\\`') // Escape backticks .replace(/\$(?!{)/g, '\\$'); // Escape $ that aren't part of ${...} + // Generate equation calculation code + const equationCalculations = equationParams.map(name => { + const param = paramMap[name]; + const equation = param.defaultValue; + + // Replace parameter references in equation with variable names + let processedEquation = equation; + const paramReferenceRegex = /@([a-zA-Z_][a-zA-Z0-9_]*)/g; + processedEquation = processedEquation.replace(paramReferenceRegex, '$1'); + + return ` const ${name} = (${processedEquation});`; + }).join('\n'); + // Generate the function - const hasParams = paramNames.length > 0; - const paramComment = hasParams - ? paramNames.map(name => ` * @param {${paramMap[name].type}} ${name} - Default: ${paramMap[name].defaultValue}`).join('\n') + const hasInputParams = inputParams.length > 0; + const hasEquationParams = equationParams.length > 0; + const hasAnyParams = hasInputParams || hasEquationParams; + + const inputParamComment = hasInputParams + ? inputParams.map(name => ` * @param {${paramMap[name].type}} ${name} - Default: ${paramMap[name].defaultValue}`).join('\n') + : ''; + const equationParamComment = hasEquationParams + ? equationParams.map(name => ` * @calculated {number} ${name} - Equation: ${paramMap[name].defaultValue}`).join('\n') + : ''; + const paramComment = hasAnyParams + ? [inputParamComment, equationParamComment].filter(c => c).join('\n') : ' * No parameters defined'; - const functionParams = hasParams ? `{${paramNames.join(', ')}} = {}` : ''; - const defaultAssignments = hasParams - ? paramNames.map((name, i) => ` const ${name}_val = ${name} !== undefined ? ${name} : ${paramDefaults[i]};`).join('\n') + const functionParams = hasInputParams ? `{${inputParams.join(', ')}} = {}` : ''; + const defaultAssignments = hasInputParams + ? inputParams.map((name, i) => ` const ${name}_val = ${name} !== undefined ? ${name} : ${paramDefaults[i]};`).join('\n') : ''; - const variableDeclaration = hasParams - ? paramNames.map(name => ` const ${name} = ${name}_val;`).join('\n') + const variableDeclaration = hasInputParams + ? inputParams.map(name => ` const ${name} = ${name}_val;`).join('\n') : ''; const functionBody = `/** @@ -522,6 +556,7 @@ ${paramComment} function generateSVG(${functionParams}) { ${defaultAssignments} ${variableDeclaration} +${equationCalculations ? '\n' + equationCalculations : ''} ${cloneHelperFunctions.join('')} return \`${escapedSvg}\`; diff --git a/src/js/modals.js b/src/js/modals.js index b16bddae..cbecda6d 100644 --- a/src/js/modals.js +++ b/src/js/modals.js @@ -127,6 +127,7 @@ editor.modal = { + @@ -212,6 +213,10 @@ editor.modal = { currentElement.type = 'color'; currentElement.placeholder = ''; break; + case 'equation': + currentElement.type = 'text'; + currentElement.placeholder = 'e.g., 2 * @width + 10'; + break; default: currentElement.type = 'text'; } From e63f58f9b83bbffaa8aaba8d9354b990e1d40104 Mon Sep 17 00:00:00 2001 From: smith120bh <42363318+smith120bh@users.noreply.github.com> Date: Wed, 23 Jul 2025 16:31:03 +1000 Subject: [PATCH 05/13] Make the output compatible with our existing custom diagram widget! --- src/js/editor.js | 52 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/src/js/editor.js b/src/js/editor.js index 00b2fb15..b6992c93 100644 --- a/src/js/editor.js +++ b/src/js/editor.js @@ -445,7 +445,7 @@ MD.Editor = function(){ cloneHelperFunctions.push(helperFunction); // Replace the clone group with a placeholder that calls the helper function - const placeholder = svgDoc.createElement('g'); + const placeholder = svgDoc.createElementNS('http://www.w3.org/2000/svg', 'g'); placeholder.setAttribute('id', cloneGroup.id); placeholder.innerHTML = `\${${funcName}()}`; cloneGroup.parentNode.replaceChild(placeholder, cloneGroup); @@ -538,14 +538,40 @@ MD.Editor = function(){ ? [inputParamComment, equationParamComment].filter(c => c).join('\n') : ' * No parameters defined'; - const functionParams = hasInputParams ? `{${inputParams.join(', ')}} = {}` : ''; - const defaultAssignments = hasInputParams - ? inputParams.map((name, i) => ` const ${name}_val = ${name} !== undefined ? ${name} : ${paramDefaults[i]};`).join('\n') + const functionParams = 'params = {}'; + const parameterAssignments = hasInputParams + ? inputParams.map((name, i) => ` const ${name} = params.${name} ?? ${paramDefaults[i]};`).join('\n') : ''; - const variableDeclaration = hasInputParams - ? inputParams.map(name => ` const ${name} = ${name}_val;`).join('\n') + + // Generate parameter types array for export + const typeMapping = { + 'number': 'Number', + 'text': 'String', + 'color': 'String', + 'boolean': 'Boolean', + 'equation': 'Number', + 'grid_cols': 'Number', + 'grid_rows': 'Number', + 'grid_spacing_x': 'Number', + 'grid_spacing_y': 'Number' + }; + + const paramsTypesArray = hasInputParams + ? inputParams.map(name => { + const param = paramMap[name]; + const jsType = typeMapping[param.type] || 'String'; + return ` { key: "${name}", type: "${jsType}" }`; + }).join(',\n') : ''; + const paramsTypesExport = hasInputParams + ? `function params() { + return [\n${paramsTypesArray}\n]; +}` + : `function params() { + return []; +}`; + const functionBody = `/** * Parametric SVG Generator * Generated by Method Draw @@ -553,22 +579,24 @@ MD.Editor = function(){ * Parameters: ${paramComment} */ -function generateSVG(${functionParams}) { -${defaultAssignments} -${variableDeclaration} +function render(${functionParams}) { +${parameterAssignments} ${equationCalculations ? '\n' + equationCalculations : ''} ${cloneHelperFunctions.join('')} return \`${escapedSvg}\`; } +${paramsTypesExport} + // Export for different module systems if (typeof module !== 'undefined' && module.exports) { - module.exports = generateSVG; + module.exports = render, params; } else if (typeof define === 'function' && define.amd) { - define([], function() { return generateSVG; }); + define([], function() { return render, params; }); } else if (typeof window !== 'undefined') { - window.generateSVG = generateSVG; + window.render = render; + window.params = params; } `; From cadbc55f8624f7afde1946bb4ae1f4f95eee2173 Mon Sep 17 00:00:00 2001 From: smith120bh <42363318+smith120bh@users.noreply.github.com> Date: Wed, 23 Jul 2025 16:34:02 +1000 Subject: [PATCH 06/13] Update package-lock.json --- package-lock.json | 3502 ++++++++++++++++++++++++++++----------------- 1 file changed, 2155 insertions(+), 1347 deletions(-) diff --git a/package-lock.json b/package-lock.json index 43ce7b16..a4c134c2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,268 +1,354 @@ { "name": "method-draw", "version": "0.0.1", - "lockfileVersion": 1, + "lockfileVersion": 3, "requires": true, - "dependencies": { - "@types/expect": { + "packages": { + "": { + "name": "method-draw", + "version": "0.0.1", + "license": "MIT", + "devDependencies": { + "gulp": "^4.0.2", + "gulp-cache-bust": "^1.4.1", + "gulp-concat": "^2.6.1", + "gulp-minify": "^3.1.0", + "gulp-replace": "^1.1.3", + "gulp-useref": "^4.0.1" + } + }, + "node_modules/@types/expect": { "version": "1.20.4", "resolved": "https://registry.npmjs.org/@types/expect/-/expect-1.20.4.tgz", "integrity": "sha512-Q5Vn3yjTDyCMV50TB6VRIbQNxSE4OmZR86VSbGaNpfUolm0iePBB4KdEEHmxoY5sT2+2DIvXW0rvMDP2nHZ4Mg==", "dev": true }, - "@types/node": { + "node_modules/@types/node": { "version": "14.14.45", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.45.tgz", "integrity": "sha512-DssMqTV9UnnoxDWu959sDLZzfvqCF0qDNRjaWeYSui9xkFe61kKo4l1TWNTQONpuXEm+gLMRvdlzvNHBamzmEw==", "dev": true }, - "@types/vinyl": { + "node_modules/@types/vinyl": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.4.tgz", "integrity": "sha512-2o6a2ixaVI2EbwBPg1QYLGQoHK56p/8X/sGfKbFC8N6sY9lfjsMf/GprtkQkSya0D4uRiutRZ2BWj7k3JvLsAQ==", "dev": true, - "requires": { + "dependencies": { "@types/expect": "^1.20.4", "@types/node": "*" } }, - "ansi-colors": { + "node_modules/ansi-colors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", "dev": true, - "requires": { + "dependencies": { "ansi-wrap": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "ansi-cyan": { + "node_modules/ansi-cyan": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz", "integrity": "sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM=", "dev": true, - "requires": { + "dependencies": { "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "ansi-gray": { + "node_modules/ansi-gray": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", "dev": true, - "requires": { + "dependencies": { "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "ansi-red": { + "node_modules/ansi-red": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz", "integrity": "sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw=", "dev": true, - "requires": { + "dependencies": { "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "ansi-regex": { + "node_modules/ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "ansi-wrap": { + "node_modules/ansi-wrap": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "anymatch": { + "node_modules/anymatch": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", "dev": true, - "requires": { + "dependencies": { "micromatch": "^3.1.4", "normalize-path": "^2.1.1" - }, + } + }, + "node_modules/anymatch/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "append-buffer": { + "node_modules/append-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", "integrity": "sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE=", "dev": true, - "requires": { + "dependencies": { "buffer-equal": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "archy": { + "node_modules/archy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", "dev": true }, - "arr-diff": { + "node_modules/arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "arr-filter": { + "node_modules/arr-filter": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz", "integrity": "sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4=", "dev": true, - "requires": { + "dependencies": { "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "arr-flatten": { + "node_modules/arr-flatten": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "arr-map": { + "node_modules/arr-map": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz", "integrity": "sha1-Onc0X/wc814qkYJWAfnljy4kysQ=", "dev": true, - "requires": { + "dependencies": { "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "arr-union": { + "node_modules/arr-union": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "array-each": { + "node_modules/array-each": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "array-initial": { + "node_modules/array-initial": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz", "integrity": "sha1-L6dLJnOTccOUe9enrcc74zSz15U=", "dev": true, - "requires": { + "dependencies": { "array-slice": "^1.0.0", "is-number": "^4.0.0" }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", - "dev": true - } + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-initial/node_modules/is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "array-last": { + "node_modules/array-last": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz", "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==", "dev": true, - "requires": { + "dependencies": { "is-number": "^4.0.0" }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", - "dev": true - } + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-last/node_modules/is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "array-slice": { + "node_modules/array-slice": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "array-sort": { + "node_modules/array-sort": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz", "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==", "dev": true, - "requires": { + "dependencies": { "default-compare": "^1.0.0", "get-value": "^2.0.6", "kind-of": "^5.0.2" }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-sort/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "array-unique": { + "node_modules/array-unique": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "assign-symbols": { + "node_modules/assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "async-done": { + "node_modules/async-done": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz", "integrity": "sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw==", "dev": true, - "requires": { + "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.2", "process-nextick-args": "^2.0.0", "stream-exhaust": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" } }, - "async-each": { + "node_modules/async-each": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", "dev": true }, - "async-settle": { + "node_modules/async-settle": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz", "integrity": "sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs=", "dev": true, - "requires": { + "dependencies": { "async-done": "^1.2.2" + }, + "engines": { + "node": ">= 0.10" } }, - "atob": { + "node_modules/atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true + "dev": true, + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } }, - "bach": { + "node_modules/bach": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", "integrity": "sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA=", "dev": true, - "requires": { + "dependencies": { "arr-filter": "^1.1.1", "arr-flatten": "^1.0.1", "arr-map": "^2.0.0", @@ -272,20 +358,23 @@ "async-done": "^1.2.2", "async-settle": "^1.0.0", "now-and-later": "^2.0.0" + }, + "engines": { + "node": ">= 0.10" } }, - "balanced-match": { + "node_modules/balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, - "base": { + "node_modules/base": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", "dev": true, - "requires": { + "dependencies": { "cache-base": "^1.0.1", "class-utils": "^0.3.5", "component-emitter": "^1.2.1", @@ -294,91 +383,115 @@ "mixin-deep": "^1.2.0", "pascalcase": "^0.1.1" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "deprecated": "Please upgrade to v1.0.1", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "deprecated": "Please upgrade to v1.0.1", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "binary-extensions": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/binary-extensions": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "binaryextensions": { + "node_modules/binaryextensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-2.3.0.tgz", "integrity": "sha512-nAihlQsYGyc5Bwq6+EsubvANYGExeJKHDO3RjnvwU042fawQTQfM3Kxn7IHUXQOz4bzfwsGYYHGSvXyW4zOGLg==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.8" + }, + "funding": { + "url": "https://bevry.me/fund" + } }, - "bindings": { + "node_modules/bindings": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", "dev": true, "optional": true, - "requires": { + "dependencies": { "file-uri-to-path": "1.0.0" } }, - "boolbase": { + "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", "dev": true }, - "brace-expansion": { + "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "requires": { + "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "braces": { + "node_modules/braces": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, - "requires": { + "dependencies": { "arr-flatten": "^1.1.0", "array-unique": "^0.3.2", "extend-shallow": "^2.0.1", @@ -390,36 +503,43 @@ "split-string": "^3.0.2", "to-regex": "^3.0.1" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "buffer-equal": { + "node_modules/buffer-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.4.0" + } }, - "buffer-from": { + "node_modules/buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", "dev": true }, - "cache-base": { + "node_modules/cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", "dev": true, - "requires": { + "dependencies": { "collection-visit": "^1.0.0", "component-emitter": "^1.2.1", "get-value": "^2.0.6", @@ -429,36 +549,48 @@ "to-object-path": "^0.3.0", "union-value": "^1.0.0", "unset-value": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "cachebust": { + "node_modules/cachebust": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/cachebust/-/cachebust-2.4.1.tgz", "integrity": "sha512-Y2O58hqbFeeD09qyC3o9nPp9qHPC4KvRnZkiXI+Eaxs15IIA/3mqGrTrE8AWcThgADrwtQ+97yKbc2c/CypaAQ==", "dev": true, - "requires": { + "dependencies": { "cheerio": "^0.22.0", "md5": "^2.0.0" + }, + "bin": { + "cachebust": "cli.js" } }, - "camelcase": { + "node_modules/camelcase": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "charenc": { + "node_modules/charenc": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=", - "dev": true + "dev": true, + "engines": { + "node": "*" + } }, - "cheerio": { + "node_modules/cheerio": { "version": "0.22.0", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", "integrity": "sha1-qbqoYKP5tZWmuBsahocxIe06Jp4=", "dev": true, - "requires": { + "dependencies": { "css-select": "~1.2.0", "dom-serializer": "~0.1.0", "entities": "~1.1.1", @@ -475,18 +607,20 @@ "lodash.reduce": "^4.4.0", "lodash.reject": "^4.4.0", "lodash.some": "^4.4.0" + }, + "engines": { + "node": ">= 0.6" } }, - "chokidar": { + "node_modules/chokidar": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", "dev": true, - "requires": { + "dependencies": { "anymatch": "^2.0.0", "async-each": "^1.0.1", "braces": "^2.3.2", - "fsevents": "^1.2.7", "glob-parent": "^3.1.0", "inherits": "^2.0.3", "is-binary-path": "^1.0.0", @@ -495,459 +629,531 @@ "path-is-absolute": "^1.0.0", "readdirp": "^2.2.1", "upath": "^1.1.1" + }, + "optionalDependencies": { + "fsevents": "^1.2.7" } }, - "class-utils": { + "node_modules/class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", "dev": true, - "requires": { + "dependencies": { "arr-union": "^3.1.0", "define-property": "^0.2.5", "isobject": "^3.0.0", "static-extend": "^0.1.1" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "cliui": { + "node_modules/cliui": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", "dev": true, - "requires": { + "dependencies": { "string-width": "^1.0.1", "strip-ansi": "^3.0.1", "wrap-ansi": "^2.0.0" } }, - "clone": { + "node_modules/clone": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.8" + } }, - "clone-buffer": { + "node_modules/clone-buffer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.10" + } }, - "clone-stats": { + "node_modules/clone-stats": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", "dev": true }, - "cloneable-readable": { + "node_modules/cloneable-readable": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", "dev": true, - "requires": { + "dependencies": { "inherits": "^2.0.1", "process-nextick-args": "^2.0.0", "readable-stream": "^2.3.5" } }, - "code-point-at": { + "node_modules/code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "collection-map": { + "node_modules/collection-map": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz", "integrity": "sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw=", "dev": true, - "requires": { + "dependencies": { "arr-map": "^2.0.2", "for-own": "^1.0.0", "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "collection-visit": { + "node_modules/collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", "dev": true, - "requires": { + "dependencies": { "map-visit": "^1.0.0", "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "color-support": { + "node_modules/color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "dev": true + "dev": true, + "bin": { + "color-support": "bin.js" + } }, - "commander": { + "node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, - "component-emitter": { + "node_modules/component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", "dev": true }, - "concat-map": { + "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "concat-stream": { + "node_modules/concat-stream": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", "dev": true, - "requires": { + "engines": [ + "node >= 0.8" + ], + "dependencies": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^2.2.2", "typedarray": "^0.0.6" } }, - "concat-with-sourcemaps": { + "node_modules/concat-with-sourcemaps": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz", "integrity": "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==", "dev": true, - "requires": { - "source-map": "^0.6.1" - }, "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } + "source-map": "^0.6.1" + } + }, + "node_modules/concat-with-sourcemaps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "convert-source-map": { + "node_modules/convert-source-map": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", "dev": true, - "requires": { + "dependencies": { "safe-buffer": "~5.1.1" } }, - "copy-descriptor": { + "node_modules/copy-descriptor": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "copy-props": { + "node_modules/copy-props": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.4.tgz", "integrity": "sha512-7cjuUME+p+S3HZlbllgsn2CDwS+5eCCX16qBgNC4jgSTf49qR1VKy/Zhl400m0IQXl/bPGEVqncgUUMjrr4s8A==", "dev": true, - "requires": { + "dependencies": { "each-props": "^1.3.0", "is-plain-object": "^2.0.1" } }, - "core-util-is": { + "node_modules/core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, - "crypt": { + "node_modules/crypt": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=", - "dev": true + "dev": true, + "engines": { + "node": "*" + } }, - "css-select": { + "node_modules/css-select": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", "dev": true, - "requires": { + "dependencies": { "boolbase": "~1.0.0", "css-what": "2.1", "domutils": "1.5.1", "nth-check": "~1.0.1" } }, - "css-what": { + "node_modules/css-what": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", - "dev": true + "dev": true, + "engines": { + "node": "*" + } }, - "d": { + "node_modules/d": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", "dev": true, - "requires": { + "dependencies": { "es5-ext": "^0.10.50", "type": "^1.0.1" } }, - "debug": { + "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, - "requires": { + "dependencies": { "ms": "2.0.0" } }, - "decamelize": { + "node_modules/decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "decode-uri-component": { + "node_modules/decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10" + } }, - "default-compare": { + "node_modules/default-compare": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==", "dev": true, - "requires": { + "dependencies": { "kind-of": "^5.0.2" }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-compare/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "default-resolution": { + "node_modules/default-resolution": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/default-resolution/-/default-resolution-2.0.0.tgz", "integrity": "sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ=", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.10" + } }, - "define-properties": { + "node_modules/define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", "dev": true, - "requires": { + "dependencies": { "object-keys": "^1.0.12" + }, + "engines": { + "node": ">= 0.4" } }, - "define-property": { + "node_modules/define-property": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", "dev": true, - "requires": { + "dependencies": { "is-descriptor": "^1.0.2", "isobject": "^3.0.1" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "deprecated": "Please upgrade to v1.0.1", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "deprecated": "Please upgrade to v1.0.1", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "detect-file": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-file": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "dom-serializer": { + "node_modules/dom-serializer": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", "dev": true, - "requires": { + "dependencies": { "domelementtype": "^1.3.0", "entities": "^1.1.1" } }, - "domelementtype": { + "node_modules/domelementtype": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", "dev": true }, - "domhandler": { + "node_modules/domhandler": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", "dev": true, - "requires": { + "dependencies": { "domelementtype": "1" } }, - "domutils": { + "node_modules/domutils": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", "dev": true, - "requires": { + "dependencies": { "dom-serializer": "0", "domelementtype": "1" } }, - "duplexer": { + "node_modules/duplexer": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", "dev": true }, - "duplexify": { + "node_modules/duplexify": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", "dev": true, - "requires": { + "dependencies": { "end-of-stream": "^1.0.0", "inherits": "^2.0.1", "readable-stream": "^2.0.0", "stream-shift": "^1.0.0" } }, - "each-props": { + "node_modules/each-props": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz", "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==", "dev": true, - "requires": { + "dependencies": { "is-plain-object": "^2.0.1", "object.defaults": "^1.1.0" } }, - "end-of-stream": { + "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dev": true, - "requires": { + "dependencies": { "once": "^1.4.0" } }, - "entities": { + "node_modules/entities": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", "dev": true }, - "error-ex": { + "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, - "requires": { + "dependencies": { "is-arrayish": "^0.2.1" } }, - "es5-ext": { + "node_modules/es5-ext": { "version": "0.10.53", "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", "dev": true, - "requires": { + "dependencies": { "es6-iterator": "~2.0.3", "es6-symbol": "~3.1.3", "next-tick": "~1.0.0" } }, - "es6-iterator": { + "node_modules/es6-iterator": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", "dev": true, - "requires": { + "dependencies": { "d": "1", "es5-ext": "^0.10.35", "es6-symbol": "^3.1.1" } }, - "es6-symbol": { + "node_modules/es6-symbol": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", "dev": true, - "requires": { + "dependencies": { "d": "^1.0.1", "ext": "^1.1.2" } }, - "es6-weak-map": { + "node_modules/es6-weak-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", "dev": true, - "requires": { + "dependencies": { "d": "1", "es5-ext": "^0.10.46", "es6-iterator": "^2.0.3", "es6-symbol": "^3.1.1" } }, - "escape-string-regexp": { + "node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.8.0" + } }, - "event-stream": { + "node_modules/event-stream": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-4.0.1.tgz", "integrity": "sha512-qACXdu/9VHPBzcyhdOWR5/IahhGMf0roTeZJfzz077GwylcDd90yOHLouhmv7GJ5XzPi6ekaQWd8AvPP2nOvpA==", "dev": true, - "requires": { + "dependencies": { "duplexer": "^0.1.1", "from": "^0.1.7", "map-stream": "0.0.7", @@ -957,12 +1163,12 @@ "through": "^2.3.8" } }, - "expand-brackets": { + "node_modules/expand-brackets": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, - "requires": { + "dependencies": { "debug": "^2.3.3", "define-property": "^0.2.5", "extend-shallow": "^2.0.1", @@ -971,86 +1177,98 @@ "snapdragon": "^0.8.1", "to-regex": "^3.0.1" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "expand-tilde": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-tilde": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", "dev": true, - "requires": { + "dependencies": { "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "ext": { + "node_modules/ext": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", "dev": true, - "requires": { - "type": "^2.0.0" - }, "dependencies": { - "type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", - "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==", - "dev": true - } + "type": "^2.0.0" } }, - "extend": { + "node_modules/ext/node_modules/type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", + "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==", + "dev": true + }, + "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, - "extend-shallow": { + "node_modules/extend-shallow": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", "dev": true, - "requires": { + "dependencies": { "assign-symbols": "^1.0.0", "is-extendable": "^1.0.1" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extend-shallow/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" } }, - "extglob": { + "node_modules/extglob": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": true, - "requires": { + "dependencies": { "array-unique": "^0.3.2", "define-property": "^1.0.0", "expand-brackets": "^2.1.4", @@ -1060,277 +1278,345 @@ "snapdragon": "^0.8.1", "to-regex": "^3.0.1" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "deprecated": "Please upgrade to v1.0.1", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "deprecated": "Please upgrade to v1.0.1", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "fancy-log": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fancy-log": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", "dev": true, - "requires": { + "dependencies": { "ansi-gray": "^0.1.1", "color-support": "^1.1.3", "parse-node-version": "^1.0.0", "time-stamp": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" } }, - "fast-levenshtein": { + "node_modules/fast-levenshtein": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz", "integrity": "sha1-5qdUzI8V5YmHqpy9J69m/W9OWvk=", "dev": true }, - "file-uri-to-path": { + "node_modules/file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", "dev": true, "optional": true }, - "fill-range": { + "node_modules/fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, - "requires": { + "dependencies": { "extend-shallow": "^2.0.1", "is-number": "^3.0.0", "repeat-string": "^1.6.1", "to-regex-range": "^2.1.0" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "find-up": { + "node_modules/find-up": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, - "requires": { + "dependencies": { "path-exists": "^2.0.0", "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "findup-sync": { + "node_modules/findup-sync": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", "dev": true, - "requires": { + "dependencies": { "detect-file": "^1.0.0", "is-glob": "^4.0.0", "micromatch": "^3.0.4", "resolve-dir": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" } }, - "fined": { + "node_modules/fined": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", "dev": true, - "requires": { + "dependencies": { "expand-tilde": "^2.0.2", "is-plain-object": "^2.0.3", "object.defaults": "^1.1.0", "object.pick": "^1.2.0", "parse-filepath": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" } }, - "flagged-respawn": { + "node_modules/flagged-respawn": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.10" + } }, - "flush-write-stream": { + "node_modules/flush-write-stream": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", "dev": true, - "requires": { + "dependencies": { "inherits": "^2.0.3", "readable-stream": "^2.3.6" } }, - "for-in": { + "node_modules/for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "for-own": { + "node_modules/for-own": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", "dev": true, - "requires": { + "dependencies": { "for-in": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "fork-stream": { + "node_modules/fork-stream": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/fork-stream/-/fork-stream-0.0.4.tgz", "integrity": "sha1-24Sfznf2cIpfjzhq5TOgkHtUrnA=", "dev": true }, - "fragment-cache": { + "node_modules/fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", "dev": true, - "requires": { + "dependencies": { "map-cache": "^0.2.2" + }, + "engines": { + "node": ">=0.10.0" } }, - "from": { + "node_modules/from": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", "dev": true }, - "fs-mkdirp-stream": { + "node_modules/fs-mkdirp-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz", "integrity": "sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes=", "dev": true, - "requires": { + "dependencies": { "graceful-fs": "^4.1.11", "through2": "^2.0.3" + }, + "engines": { + "node": ">= 0.10" } }, - "fs.realpath": { + "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, - "fsevents": { + "node_modules/fsevents": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "deprecated": "Upgrade to fsevents v2 to mitigate potential security issues", "dev": true, + "hasInstallScript": true, "optional": true, - "requires": { + "os": [ + "darwin" + ], + "dependencies": { "bindings": "^1.5.0", "nan": "^2.12.1" + }, + "engines": { + "node": ">= 4.0" } }, - "function-bind": { + "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, - "get-caller-file": { + "node_modules/get-caller-file": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", "dev": true }, - "get-value": { + "node_modules/get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "glob": { + "node_modules/glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, - "requires": { + "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "glob-parent": { + "node_modules/glob-parent": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", "dev": true, - "requires": { + "dependencies": { "is-glob": "^3.1.0", "path-dirname": "^1.0.0" - }, + } + }, + "node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "glob-stream": { + "node_modules/glob-stream": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz", "integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=", "dev": true, - "requires": { + "dependencies": { "extend": "^3.0.0", "glob": "^7.1.1", "glob-parent": "^3.1.0", @@ -1341,14 +1627,17 @@ "remove-trailing-separator": "^1.0.1", "to-absolute-glob": "^2.0.0", "unique-stream": "^2.0.2" + }, + "engines": { + "node": ">= 0.10" } }, - "glob-watcher": { + "node_modules/glob-watcher": { "version": "5.0.5", "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.5.tgz", "integrity": "sha512-zOZgGGEHPklZNjZQaZ9f41i7F2YwE+tS5ZHrDhbBCk3stwahn5vQxnFmBJZHoYdusR6R1bLSXeGUy/BhctwKzw==", "dev": true, - "requires": { + "dependencies": { "anymatch": "^2.0.0", "async-done": "^1.2.0", "chokidar": "^2.0.0", @@ -1356,93 +1645,83 @@ "just-debounce": "^1.0.0", "normalize-path": "^3.0.0", "object.defaults": "^1.1.0" + }, + "engines": { + "node": ">= 0.10" } }, - "global-modules": { + "node_modules/global-modules": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", "dev": true, - "requires": { + "dependencies": { "global-prefix": "^1.0.1", "is-windows": "^1.0.1", "resolve-dir": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "global-prefix": { + "node_modules/global-prefix": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", "dev": true, - "requires": { + "dependencies": { "expand-tilde": "^2.0.2", "homedir-polyfill": "^1.0.1", "ini": "^1.3.4", "is-windows": "^1.0.1", "which": "^1.2.14" + }, + "engines": { + "node": ">=0.10.0" } }, - "glogg": { + "node_modules/glogg": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==", "dev": true, - "requires": { + "dependencies": { "sparkles": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" } }, - "graceful-fs": { + "node_modules/graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", "dev": true }, - "gulp": { + "node_modules/gulp": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz", "integrity": "sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==", "dev": true, - "requires": { + "dependencies": { "glob-watcher": "^5.0.3", "gulp-cli": "^2.2.0", "undertaker": "^1.2.1", "vinyl-fs": "^3.0.0" }, - "dependencies": { - "gulp-cli": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.3.0.tgz", - "integrity": "sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A==", - "dev": true, - "requires": { - "ansi-colors": "^1.0.1", - "archy": "^1.0.0", - "array-sort": "^1.0.0", - "color-support": "^1.1.3", - "concat-stream": "^1.6.0", - "copy-props": "^2.0.1", - "fancy-log": "^1.3.2", - "gulplog": "^1.0.0", - "interpret": "^1.4.0", - "isobject": "^3.0.1", - "liftoff": "^3.1.0", - "matchdep": "^2.0.0", - "mute-stdout": "^1.0.0", - "pretty-hrtime": "^1.0.0", - "replace-homedir": "^1.0.0", - "semver-greatest-satisfied-range": "^1.1.0", - "v8flags": "^3.2.0", - "yargs": "^7.1.0" - } - } - } - }, - "gulp-cache-bust": { + "bin": { + "gulp": "bin/gulp.js" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/gulp-cache-bust": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/gulp-cache-bust/-/gulp-cache-bust-1.4.1.tgz", "integrity": "sha512-ksYCu06p2hpUDilgtv/jMB9lxBLg/v9F0xEAYbndgS/xWqamHpIpGRPAKj76VRXJpDTHgEqVPcd2rCofj4TERg==", "dev": true, - "requires": { + "dependencies": { "ansi-colors": "^1.0.1", "cachebust": "2.4.1", "fancy-log": "^1.3.2", @@ -1453,229 +1732,269 @@ "through2": "2.0.1", "vinyl": "^2.1.0" }, + "engines": { + "node": ">=4.4.5", + "npm": ">=2.15.1" + } + }, + "node_modules/gulp-cache-bust/node_modules/arr-diff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", + "dev": true, "dependencies": { - "arr-diff": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", - "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", - "dev": true, - "requires": { - "arr-flatten": "^1.0.1", - "array-slice": "^0.2.3" - } - }, - "arr-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", - "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=", - "dev": true - }, - "array-slice": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", - "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", - "dev": true - }, - "extend-shallow": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", - "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", - "dev": true, - "requires": { - "kind-of": "^1.1.0" - } - }, - "kind-of": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", - "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", - "dev": true - }, - "map-stream": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", - "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", - "dev": true - }, - "plugin-error": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", - "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", - "dev": true, - "requires": { - "ansi-cyan": "^0.1.1", - "ansi-red": "^0.1.1", - "arr-diff": "^1.0.1", - "arr-union": "^2.0.1", - "extend-shallow": "^1.1.2" - } - }, - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", - "dev": true - }, - "readable-stream": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", - "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "string_decoder": "~0.10.x", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - }, - "through2": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.1.tgz", - "integrity": "sha1-OE51MU1J8y3hLuu4E2uOtrXVnak=", - "dev": true, - "requires": { - "readable-stream": "~2.0.0", - "xtend": "~4.0.0" - } - } - } - }, - "gulp-concat": { + "arr-flatten": "^1.0.1", + "array-slice": "^0.2.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-cache-bust/node_modules/arr-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", + "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-cache-bust/node_modules/array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-cache-bust/node_modules/extend-shallow": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", + "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", + "dev": true, + "dependencies": { + "kind-of": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-cache-bust/node_modules/kind-of": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-cache-bust/node_modules/map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", + "dev": true + }, + "node_modules/gulp-cache-bust/node_modules/plugin-error": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", + "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", + "dev": true, + "dependencies": { + "ansi-cyan": "^0.1.1", + "ansi-red": "^0.1.1", + "arr-diff": "^1.0.1", + "arr-union": "^2.0.1", + "extend-shallow": "^1.1.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-cache-bust/node_modules/process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "dev": true + }, + "node_modules/gulp-cache-bust/node_modules/readable-stream": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~0.10.x", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/gulp-cache-bust/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "node_modules/gulp-cache-bust/node_modules/through2": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.1.tgz", + "integrity": "sha1-OE51MU1J8y3hLuu4E2uOtrXVnak=", + "dev": true, + "dependencies": { + "readable-stream": "~2.0.0", + "xtend": "~4.0.0" + } + }, + "node_modules/gulp-concat": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/gulp-concat/-/gulp-concat-2.6.1.tgz", "integrity": "sha1-Yz0WyV2IUEYorQJmVmPO5aR5M1M=", "dev": true, - "requires": { + "dependencies": { "concat-with-sourcemaps": "^1.0.0", "through2": "^2.0.0", "vinyl": "^2.0.0" + }, + "engines": { + "node": ">= 0.10" } }, - "gulp-if": { + "node_modules/gulp-if": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/gulp-if/-/gulp-if-3.0.0.tgz", "integrity": "sha512-fCUEngzNiEZEK2YuPm+sdMpO6ukb8+/qzbGfJBXyNOXz85bCG7yBI+pPSl+N90d7gnLvMsarthsAImx0qy7BAw==", "dev": true, - "requires": { + "dependencies": { "gulp-match": "^1.1.0", "ternary-stream": "^3.0.0", "through2": "^3.0.1" - }, + } + }, + "node_modules/gulp-if/node_modules/through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "dev": true, "dependencies": { - "through2": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", - "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", - "dev": true, - "requires": { - "inherits": "^2.0.4", - "readable-stream": "2 || 3" - } - } + "inherits": "^2.0.4", + "readable-stream": "2 || 3" } }, - "gulp-match": { + "node_modules/gulp-match": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/gulp-match/-/gulp-match-1.1.0.tgz", "integrity": "sha512-DlyVxa1Gj24DitY2OjEsS+X6tDpretuxD6wTfhXE/Rw2hweqc1f6D/XtsJmoiCwLWfXgR87W9ozEityPCVzGtQ==", "dev": true, - "requires": { + "dependencies": { "minimatch": "^3.0.3" } }, - "gulp-minify": { + "node_modules/gulp-minify": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/gulp-minify/-/gulp-minify-3.1.0.tgz", "integrity": "sha512-ixF41aYg+NQikI8hpoHdEclYcQkbGdXQu1CBdHaU7Epg8H6e8d2jWXw1+rBPgYwl/XpKgjHj7NI6gkhoSNSSAg==", "dev": true, - "requires": { + "dependencies": { "ansi-colors": "^1.0.1", "minimatch": "^3.0.2", "plugin-error": "^0.1.2", "terser": "^3.7.6", "through2": "^2.0.3", "vinyl": "^2.1.0" + } + }, + "node_modules/gulp-minify/node_modules/arr-diff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", + "dev": true, + "dependencies": { + "arr-flatten": "^1.0.1", + "array-slice": "^0.2.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-minify/node_modules/arr-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", + "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-minify/node_modules/array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-minify/node_modules/extend-shallow": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", + "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", + "dev": true, + "dependencies": { + "kind-of": "^1.1.0" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-minify/node_modules/kind-of": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-minify/node_modules/plugin-error": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", + "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", + "dev": true, "dependencies": { - "arr-diff": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", - "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", - "dev": true, - "requires": { - "arr-flatten": "^1.0.1", - "array-slice": "^0.2.3" - } - }, - "arr-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", - "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=", - "dev": true - }, - "array-slice": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", - "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", - "dev": true - }, - "extend-shallow": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", - "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", - "dev": true, - "requires": { - "kind-of": "^1.1.0" - } - }, - "kind-of": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", - "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", - "dev": true - }, - "plugin-error": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", - "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", - "dev": true, - "requires": { - "ansi-cyan": "^0.1.1", - "ansi-red": "^0.1.1", - "arr-diff": "^1.0.1", - "arr-union": "^2.0.1", - "extend-shallow": "^1.1.2" - } - } - } - }, - "gulp-replace": { + "ansi-cyan": "^0.1.1", + "ansi-red": "^0.1.1", + "arr-diff": "^1.0.1", + "arr-union": "^2.0.1", + "extend-shallow": "^1.1.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-replace": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/gulp-replace/-/gulp-replace-1.1.3.tgz", "integrity": "sha512-HcPHpWY4XdF8zxYkDODHnG2+7a3nD/Y8Mfu3aBgMiCFDW3X2GiOKXllsAmILcxe3KZT2BXoN18WrpEFm48KfLQ==", "dev": true, - "requires": { + "dependencies": { "@types/node": "^14.14.41", "@types/vinyl": "^2.0.4", "istextorbinary": "^3.0.0", "replacestream": "^4.0.3", "yargs-parser": ">=5.0.0-security.0" + }, + "engines": { + "node": ">=10" } }, - "gulp-useref": { + "node_modules/gulp-useref": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/gulp-useref/-/gulp-useref-4.0.1.tgz", "integrity": "sha512-+FjOGvvlCiUTbIjDQ0CAz8sQC3zkq3n1FqG6HR3njvcKQ9rNPIsHhcE5nUah4IHthKOHMPsnUBtE71nZp+dHAg==", "dev": true, - "requires": { + "dependencies": { "event-stream": "^4.0.1", "extend": "^3.0.2", "glob": "^7.1.6", @@ -1686,440 +2005,584 @@ "useref": "^1.4.3", "vinyl-fs": "^3.0.3" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-useref/node_modules/through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "dev": true, "dependencies": { - "through2": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", - "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", - "dev": true, - "requires": { - "inherits": "^2.0.4", - "readable-stream": "2 || 3" - } - } + "inherits": "^2.0.4", + "readable-stream": "2 || 3" } }, - "gulplog": { + "node_modules/gulp/node_modules/gulp-cli": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.3.0.tgz", + "integrity": "sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A==", + "dev": true, + "dependencies": { + "ansi-colors": "^1.0.1", + "archy": "^1.0.0", + "array-sort": "^1.0.0", + "color-support": "^1.1.3", + "concat-stream": "^1.6.0", + "copy-props": "^2.0.1", + "fancy-log": "^1.3.2", + "gulplog": "^1.0.0", + "interpret": "^1.4.0", + "isobject": "^3.0.1", + "liftoff": "^3.1.0", + "matchdep": "^2.0.0", + "mute-stdout": "^1.0.0", + "pretty-hrtime": "^1.0.0", + "replace-homedir": "^1.0.0", + "semver-greatest-satisfied-range": "^1.1.0", + "v8flags": "^3.2.0", + "yargs": "^7.1.0" + }, + "bin": { + "gulp": "bin/gulp.js" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/gulplog": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", "dev": true, - "requires": { + "dependencies": { "glogg": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" } }, - "has": { + "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, - "requires": { + "dependencies": { "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" } }, - "has-symbols": { + "node_modules/has-symbols": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "has-value": { + "node_modules/has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", "dev": true, - "requires": { + "dependencies": { "get-value": "^2.0.6", "has-values": "^1.0.0", "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "has-values": { + "node_modules/has-values": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", "dev": true, - "requires": { + "dependencies": { "is-number": "^3.0.0", "kind-of": "^4.0.0" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, "dependencies": { - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" } }, - "homedir-polyfill": { + "node_modules/homedir-polyfill": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", "dev": true, - "requires": { + "dependencies": { "parse-passwd": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "hosted-git-info": { + "node_modules/hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, - "htmlparser2": { + "node_modules/htmlparser2": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", "dev": true, - "requires": { + "dependencies": { "domelementtype": "^1.3.1", "domhandler": "^2.3.0", "domutils": "^1.5.1", "entities": "^1.1.1", "inherits": "^2.0.1", "readable-stream": "^3.1.1" - }, + } + }, + "node_modules/htmlparser2/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" } }, - "inflight": { + "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, - "requires": { + "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, - "inherits": { + "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "ini": { + "node_modules/ini": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, - "interpret": { + "node_modules/interpret": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.10" + } }, - "invert-kv": { + "node_modules/invert-kv": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "is-absolute": { + "node_modules/is-absolute": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", "dev": true, - "requires": { + "dependencies": { "is-relative": "^1.0.0", "is-windows": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "is-accessor-descriptor": { + "node_modules/is-accessor-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "deprecated": "Please upgrade to v0.1.7", "dev": true, - "requires": { + "dependencies": { "kind-of": "^3.0.2" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" } }, - "is-arrayish": { + "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, - "is-binary-path": { + "node_modules/is-binary-path": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", "dev": true, - "requires": { + "dependencies": { "binary-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "is-buffer": { + "node_modules/is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, - "is-core-module": { + "node_modules/is-core-module": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", "dev": true, - "requires": { + "dependencies": { "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "is-data-descriptor": { + "node_modules/is-data-descriptor": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "deprecated": "Please upgrade to v0.1.5", "dev": true, - "requires": { + "dependencies": { "kind-of": "^3.0.2" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" } }, - "is-descriptor": { + "node_modules/is-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, - "requires": { + "dependencies": { "is-accessor-descriptor": "^0.1.6", "is-data-descriptor": "^0.1.4", "kind-of": "^5.0.0" }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-descriptor/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "is-extendable": { + "node_modules/is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "is-extglob": { + "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "is-fullwidth-code-point": { + "node_modules/is-fullwidth-code-point": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, - "requires": { + "dependencies": { "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "is-glob": { + "node_modules/is-glob": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", "dev": true, - "requires": { + "dependencies": { "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "is-negated-glob": { + "node_modules/is-negated-glob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "is-number": { + "node_modules/is-number": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, - "requires": { + "dependencies": { "kind-of": "^3.0.2" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" } }, - "is-plain-object": { + "node_modules/is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, - "requires": { + "dependencies": { "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "is-relative": { + "node_modules/is-relative": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", "dev": true, - "requires": { + "dependencies": { "is-unc-path": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "is-unc-path": { + "node_modules/is-unc-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", "dev": true, - "requires": { + "dependencies": { "unc-path-regex": "^0.1.2" + }, + "engines": { + "node": ">=0.10.0" } }, - "is-utf8": { + "node_modules/is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", "dev": true }, - "is-valid-glob": { + "node_modules/is-valid-glob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz", "integrity": "sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "is-windows": { + "node_modules/is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "isarray": { + "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, - "isexe": { + "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, - "isobject": { + "node_modules/isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "istextorbinary": { + "node_modules/istextorbinary": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-3.3.0.tgz", "integrity": "sha512-Tvq1W6NAcZeJ8op+Hq7tdZ434rqnMx4CCZ7H0ff83uEloDvVbqAwaMTZcafKGJT0VHkYzuXUiCY4hlXQg6WfoQ==", "dev": true, - "requires": { + "dependencies": { "binaryextensions": "^2.2.0", "textextensions": "^3.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://bevry.me/fund" } }, - "json-stable-stringify-without-jsonify": { + "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, - "just-debounce": { + "node_modules/just-debounce": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.0.0.tgz", "integrity": "sha1-h/zPrv/AtozRnVX2cilD+SnqNeo=", "dev": true }, - "kind-of": { + "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "last-run": { + "node_modules/last-run": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz", "integrity": "sha1-RblpQsF7HHnHchmCWbqUO+v4yls=", "dev": true, - "requires": { + "dependencies": { "default-resolution": "^2.0.0", "es6-weak-map": "^2.0.1" + }, + "engines": { + "node": ">= 0.10" } }, - "lazystream": { + "node_modules/lazystream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", "dev": true, - "requires": { + "dependencies": { "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" } }, - "lcid": { + "node_modules/lcid": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", "dev": true, - "requires": { + "dependencies": { "invert-kv": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "lead": { + "node_modules/lead": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz", "integrity": "sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI=", "dev": true, - "requires": { + "dependencies": { "flush-write-stream": "^1.0.2" + }, + "engines": { + "node": ">= 0.10" } }, - "liftoff": { + "node_modules/liftoff": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz", "integrity": "sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog==", "dev": true, - "requires": { + "dependencies": { "extend": "^3.0.0", "findup-sync": "^3.0.0", "fined": "^1.0.1", @@ -2128,181 +2591,204 @@ "object.map": "^1.0.0", "rechoir": "^0.6.2", "resolve": "^1.1.7" + }, + "engines": { + "node": ">= 0.8" } }, - "load-json-file": { + "node_modules/load-json-file": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, - "requires": { + "dependencies": { "graceful-fs": "^4.1.2", "parse-json": "^2.2.0", "pify": "^2.0.0", "pinkie-promise": "^2.0.0", "strip-bom": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "lodash.assignin": { + "node_modules/lodash.assignin": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", "integrity": "sha1-uo31+4QesKPoBEIysOJjqNxqKKI=", "dev": true }, - "lodash.bind": { + "node_modules/lodash.bind": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz", "integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU=", "dev": true }, - "lodash.defaults": { + "node_modules/lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=", "dev": true }, - "lodash.filter": { + "node_modules/lodash.filter": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", "integrity": "sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4=", "dev": true }, - "lodash.flatten": { + "node_modules/lodash.flatten": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", "dev": true }, - "lodash.foreach": { + "node_modules/lodash.foreach": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", "integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=", "dev": true }, - "lodash.map": { + "node_modules/lodash.map": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", "integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=", "dev": true }, - "lodash.merge": { + "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "lodash.pick": { + "node_modules/lodash.pick": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=", + "deprecated": "This package is deprecated. Use destructuring assignment syntax instead.", "dev": true }, - "lodash.reduce": { + "node_modules/lodash.reduce": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", "integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs=", "dev": true }, - "lodash.reject": { + "node_modules/lodash.reject": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz", "integrity": "sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU=", "dev": true }, - "lodash.some": { + "node_modules/lodash.some": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", "integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=", "dev": true }, - "make-iterator": { + "node_modules/make-iterator": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", "dev": true, - "requires": { + "dependencies": { "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" } }, - "map-cache": { + "node_modules/map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "map-stream": { + "node_modules/map-stream": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=", "dev": true }, - "map-visit": { + "node_modules/map-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", "dev": true, - "requires": { + "dependencies": { "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "matchdep": { + "node_modules/matchdep": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", "integrity": "sha1-xvNINKDY28OzfCfui7yyfHd1WC4=", "dev": true, - "requires": { + "dependencies": { "findup-sync": "^2.0.0", "micromatch": "^3.0.4", "resolve": "^1.4.0", "stack-trace": "0.0.10" }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/matchdep/node_modules/findup-sync": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", + "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", + "dev": true, + "dependencies": { + "detect-file": "^1.0.0", + "is-glob": "^3.1.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/matchdep/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, "dependencies": { - "findup-sync": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", - "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", - "dev": true, - "requires": { - "detect-file": "^1.0.0", - "is-glob": "^3.1.0", - "micromatch": "^3.0.4", - "resolve-dir": "^1.0.1" - } - }, - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "md5": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/md5": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", "integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=", "dev": true, - "requires": { + "dependencies": { "charenc": "~0.0.1", "crypt": "~0.0.1", "is-buffer": "~1.1.1" } }, - "merge-stream": { + "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, - "micromatch": { + "node_modules/micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": true, - "requires": { + "dependencies": { "arr-diff": "^4.0.0", "array-unique": "^0.3.2", "braces": "^2.3.1", @@ -2316,78 +2802,94 @@ "regex-not": "^1.0.0", "snapdragon": "^0.8.1", "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" } }, - "minimatch": { + "node_modules/minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, - "requires": { + "dependencies": { "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "minimist": { + "node_modules/minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, - "mixin-deep": { + "node_modules/mixin-deep": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, - "requires": { + "dependencies": { "for-in": "^1.0.2", "is-extendable": "^1.0.1" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mixin-deep/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" } }, - "mkdirp": { + "node_modules/mkdirp": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "dev": true, - "requires": { + "dependencies": { "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" } }, - "ms": { + "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, - "mute-stdout": { + "node_modules/mute-stdout": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz", "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.10" + } }, - "nan": { + "node_modules/nan": { "version": "2.14.2", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", "dev": true, "optional": true }, - "nanomatch": { + "node_modules/nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", "dev": true, - "requires": { + "dependencies": { "arr-diff": "^4.0.0", "array-unique": "^0.3.2", "define-property": "^2.0.2", @@ -2399,393 +2901,496 @@ "regex-not": "^1.0.0", "snapdragon": "^0.8.1", "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "next-tick": { + "node_modules/next-tick": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "dev": true }, - "normalize-package-data": { + "node_modules/normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "dev": true, - "requires": { + "dependencies": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", "semver": "2 || 3 || 4 || 5", "validate-npm-package-license": "^3.0.1" } }, - "normalize-path": { + "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "now-and-later": { + "node_modules/now-and-later": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz", "integrity": "sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==", "dev": true, - "requires": { + "dependencies": { "once": "^1.3.2" + }, + "engines": { + "node": ">= 0.10" } }, - "nth-check": { + "node_modules/nth-check": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", "dev": true, - "requires": { + "dependencies": { "boolbase": "~1.0.0" } }, - "number-is-nan": { + "node_modules/number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "object-assign": { + "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "object-copy": { + "node_modules/object-copy": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", "dev": true, - "requires": { + "dependencies": { "copy-descriptor": "^0.1.0", "define-property": "^0.2.5", "kind-of": "^3.0.3" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "object-keys": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.4" + } }, - "object-visit": { + "node_modules/object-visit": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", "dev": true, - "requires": { + "dependencies": { "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "object.assign": { + "node_modules/object.assign": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", "dev": true, - "requires": { + "dependencies": { "define-properties": "^1.1.2", "function-bind": "^1.1.1", "has-symbols": "^1.0.0", "object-keys": "^1.0.11" + }, + "engines": { + "node": ">= 0.4" } }, - "object.defaults": { + "node_modules/object.defaults": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", "dev": true, - "requires": { + "dependencies": { "array-each": "^1.0.1", "array-slice": "^1.0.0", "for-own": "^1.0.0", "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "object.map": { + "node_modules/object.map": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", "dev": true, - "requires": { + "dependencies": { "for-own": "^1.0.0", "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "object.pick": { + "node_modules/object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", "dev": true, - "requires": { + "dependencies": { "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "object.reduce": { + "node_modules/object.reduce": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object.reduce/-/object.reduce-1.0.1.tgz", "integrity": "sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60=", "dev": true, - "requires": { + "dependencies": { "for-own": "^1.0.0", "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "once": { + "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, - "requires": { + "dependencies": { "wrappy": "1" } }, - "ordered-read-streams": { + "node_modules/ordered-read-streams": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=", "dev": true, - "requires": { + "dependencies": { "readable-stream": "^2.0.1" } }, - "os-locale": { + "node_modules/os-locale": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "dev": true, - "requires": { + "dependencies": { "lcid": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "os-tmpdir": { + "node_modules/os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "parse-filepath": { + "node_modules/parse-filepath": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", "dev": true, - "requires": { + "dependencies": { "is-absolute": "^1.0.0", "map-cache": "^0.2.0", "path-root": "^0.1.1" + }, + "engines": { + "node": ">=0.8" } }, - "parse-json": { + "node_modules/parse-json": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "dev": true, - "requires": { + "dependencies": { "error-ex": "^1.2.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "parse-node-version": { + "node_modules/parse-node-version": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.10" + } }, - "parse-passwd": { + "node_modules/parse-passwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "pascalcase": { + "node_modules/pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "path-dirname": { + "node_modules/path-dirname": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", "dev": true }, - "path-exists": { + "node_modules/path-exists": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, - "requires": { + "dependencies": { "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "path-is-absolute": { + "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "path-parse": { + "node_modules/path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, - "path-root": { + "node_modules/path-root": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", "dev": true, - "requires": { + "dependencies": { "path-root-regex": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "path-root-regex": { + "node_modules/path-root-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "path-type": { + "node_modules/path-type": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, - "requires": { + "dependencies": { "graceful-fs": "^4.1.2", "pify": "^2.0.0", "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "pause-stream": { + "node_modules/pause-stream": { "version": "0.0.11", "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", "dev": true, - "requires": { + "dependencies": { "through": "~2.3" } }, - "pify": { + "node_modules/pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "pinkie": { + "node_modules/pinkie": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "pinkie-promise": { + "node_modules/pinkie-promise": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", "dev": true, - "requires": { + "dependencies": { "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "plugin-error": { + "node_modules/plugin-error": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", "dev": true, - "requires": { + "dependencies": { "ansi-colors": "^1.0.1", "arr-diff": "^4.0.0", "arr-union": "^3.1.0", "extend-shallow": "^3.0.2" + }, + "engines": { + "node": ">= 0.10" } }, - "posix-character-classes": { + "node_modules/posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "pretty-hrtime": { + "node_modules/pretty-hrtime": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.8" + } }, - "process-nextick-args": { + "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, - "pump": { + "node_modules/pump": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", "dev": true, - "requires": { + "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, - "pumpify": { + "node_modules/pumpify": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", "dev": true, - "requires": { + "dependencies": { "duplexify": "^3.6.0", "inherits": "^2.0.3", "pump": "^2.0.0" } }, - "read-pkg": { + "node_modules/read-pkg": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, - "requires": { + "dependencies": { "load-json-file": "^1.0.0", "normalize-package-data": "^2.3.2", "path-type": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "read-pkg-up": { + "node_modules/read-pkg-up": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, - "requires": { + "dependencies": { "find-up": "^1.0.0", "read-pkg": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "readable-stream": { + "node_modules/readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, - "requires": { + "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", @@ -2795,221 +3400,274 @@ "util-deprecate": "~1.0.1" } }, - "readdirp": { + "node_modules/readdirp": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", "dev": true, - "requires": { + "dependencies": { "graceful-fs": "^4.1.11", "micromatch": "^3.1.10", "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10" } }, - "rechoir": { + "node_modules/rechoir": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", "dev": true, - "requires": { + "dependencies": { "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" } }, - "regex-not": { + "node_modules/regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", "dev": true, - "requires": { + "dependencies": { "extend-shallow": "^3.0.2", "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "remove-bom-buffer": { + "node_modules/remove-bom-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", "dev": true, - "requires": { + "dependencies": { "is-buffer": "^1.1.5", "is-utf8": "^0.2.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "remove-bom-stream": { + "node_modules/remove-bom-stream": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz", "integrity": "sha1-BfGlk/FuQuH7kOv1nejlaVJflSM=", "dev": true, - "requires": { + "dependencies": { "remove-bom-buffer": "^3.0.0", "safe-buffer": "^5.1.0", "through2": "^2.0.3" + }, + "engines": { + "node": ">= 0.10" } }, - "remove-trailing-separator": { + "node_modules/remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", "dev": true }, - "repeat-element": { + "node_modules/repeat-element": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "repeat-string": { + "node_modules/repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10" + } }, - "replace-ext": { + "node_modules/replace-ext": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.10" + } }, - "replace-homedir": { + "node_modules/replace-homedir": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-1.0.0.tgz", "integrity": "sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw=", "dev": true, - "requires": { + "dependencies": { "homedir-polyfill": "^1.0.1", "is-absolute": "^1.0.0", "remove-trailing-separator": "^1.1.0" + }, + "engines": { + "node": ">= 0.10" } }, - "replacestream": { + "node_modules/replacestream": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/replacestream/-/replacestream-4.0.3.tgz", "integrity": "sha512-AC0FiLS352pBBiZhd4VXB1Ab/lh0lEgpP+GGvZqbQh8a5cmXVoTe5EX/YeTFArnp4SRGTHh1qCHu9lGs1qG8sA==", "dev": true, - "requires": { + "dependencies": { "escape-string-regexp": "^1.0.3", "object-assign": "^4.0.1", "readable-stream": "^2.0.2" } }, - "require-directory": { + "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "require-main-filename": { + "node_modules/require-main-filename": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", "dev": true }, - "resolve": { + "node_modules/resolve": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", "dev": true, - "requires": { + "dependencies": { "is-core-module": "^2.1.0", "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "resolve-dir": { + "node_modules/resolve-dir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", "dev": true, - "requires": { + "dependencies": { "expand-tilde": "^2.0.0", "global-modules": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "resolve-options": { + "node_modules/resolve-options": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz", "integrity": "sha1-MrueOcBtZzONyTeMDW1gdFZq0TE=", "dev": true, - "requires": { + "dependencies": { "value-or-function": "^3.0.0" + }, + "engines": { + "node": ">= 0.10" } }, - "resolve-url": { + "node_modules/resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "deprecated": "https://github.com/lydell/resolve-url#deprecated", "dev": true }, - "ret": { + "node_modules/ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.12" + } }, - "safe-buffer": { + "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, - "safe-regex": { + "node_modules/safe-regex": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, - "requires": { + "dependencies": { "ret": "~0.1.10" } }, - "semver": { + "node_modules/semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true + "dev": true, + "bin": { + "semver": "bin/semver" + } }, - "semver-greatest-satisfied-range": { + "node_modules/semver-greatest-satisfied-range": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz", "integrity": "sha1-E+jCZYq5aRywzXEJMkAoDTb3els=", "dev": true, - "requires": { + "dependencies": { "sver-compat": "^1.5.0" + }, + "engines": { + "node": ">= 0.10" } }, - "set-blocking": { + "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, - "set-value": { + "node_modules/set-value": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", "dev": true, - "requires": { + "dependencies": { "extend-shallow": "^2.0.1", "is-extendable": "^0.1.1", "is-plain-object": "^2.0.3", "split-string": "^3.0.1" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "snapdragon": { + "node_modules/snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", "dev": true, - "requires": { + "dependencies": { "base": "^0.11.1", "debug": "^2.2.0", "define-property": "^0.2.5", @@ -3019,110 +3677,140 @@ "source-map-resolve": "^0.5.0", "use": "^3.1.0" }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "snapdragon-node": { + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", "dev": true, - "requires": { + "dependencies": { "define-property": "^1.0.0", "isobject": "^3.0.0", "snapdragon-util": "^3.0.1" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "deprecated": "Please upgrade to v1.0.1", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "deprecated": "Please upgrade to v1.0.1", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "snapdragon-util": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", "dev": true, - "requires": { + "dependencies": { "kind-of": "^3.2.0" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "source-map": { + "node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "source-map-resolve": { + "node_modules/source-map-resolve": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", "dev": true, - "requires": { + "dependencies": { "atob": "^2.1.2", "decode-uri-component": "^0.2.0", "resolve-url": "^0.2.1", @@ -3130,386 +3818,452 @@ "urix": "^0.1.0" } }, - "source-map-support": { + "node_modules/source-map-support": { "version": "0.5.19", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", "dev": true, - "requires": { + "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } } }, - "source-map-url": { + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-url": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "deprecated": "See https://github.com/lydell/source-map-url#deprecated", "dev": true }, - "sparkles": { + "node_modules/sparkles": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.10" + } }, - "spdx-correct": { + "node_modules/spdx-correct": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", "dev": true, - "requires": { + "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" } }, - "spdx-exceptions": { + "node_modules/spdx-exceptions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", "dev": true }, - "spdx-expression-parse": { + "node_modules/spdx-expression-parse": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "dev": true, - "requires": { + "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, - "spdx-license-ids": { + "node_modules/spdx-license-ids": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==", "dev": true }, - "split": { + "node_modules/split": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", "dev": true, - "requires": { + "dependencies": { "through": "2" + }, + "engines": { + "node": "*" } }, - "split-string": { + "node_modules/split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", "dev": true, - "requires": { + "dependencies": { "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "stack-trace": { + "node_modules/stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", - "dev": true + "dev": true, + "engines": { + "node": "*" + } }, - "static-extend": { + "node_modules/static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", "dev": true, - "requires": { + "dependencies": { "define-property": "^0.2.5", "object-copy": "^0.1.0" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "stream-combiner": { + "node_modules/stream-combiner": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", "integrity": "sha1-rsjLrBd7Vrb0+kec7YwZEs7lKFg=", "dev": true, - "requires": { + "dependencies": { "duplexer": "~0.1.1", "through": "~2.3.4" } }, - "stream-exhaust": { + "node_modules/stream-exhaust": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==", "dev": true }, - "stream-shift": { + "node_modules/stream-shift": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", "dev": true }, - "string-width": { + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, - "requires": { + "dependencies": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { + "node_modules/strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, - "requires": { + "dependencies": { "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "strip-bom": { + "node_modules/strip-bom": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, - "requires": { + "dependencies": { "is-utf8": "^0.2.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "sver-compat": { + "node_modules/sver-compat": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/sver-compat/-/sver-compat-1.5.0.tgz", "integrity": "sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg=", "dev": true, - "requires": { + "dependencies": { "es6-iterator": "^2.0.1", "es6-symbol": "^3.1.1" } }, - "temp-write": { + "node_modules/temp-write": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/temp-write/-/temp-write-2.1.0.tgz", "integrity": "sha1-WYkJGODvCdVIqqNC9L00CdhATpY=", "dev": true, - "requires": { + "dependencies": { "graceful-fs": "^4.1.2", "mkdirp": "^0.5.0", "os-tmpdir": "^1.0.0", "pify": "^2.2.0", "pinkie-promise": "^2.0.0", "uuid": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "ternary-stream": { + "node_modules/ternary-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ternary-stream/-/ternary-stream-3.0.0.tgz", "integrity": "sha512-oIzdi+UL/JdktkT+7KU5tSIQjj8pbShj3OASuvDEhm0NT5lppsm7aXWAmAq4/QMaBIyfuEcNLbAQA+HpaISobQ==", "dev": true, - "requires": { + "dependencies": { "duplexify": "^4.1.1", "fork-stream": "^0.0.4", "merge-stream": "^2.0.0", "through2": "^3.0.1" + } + }, + "node_modules/ternary-stream/node_modules/duplexify": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz", + "integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" + } + }, + "node_modules/ternary-stream/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ternary-stream/node_modules/through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "dev": true, "dependencies": { - "duplexify": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz", - "integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==", - "dev": true, - "requires": { - "end-of-stream": "^1.4.1", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1", - "stream-shift": "^1.0.0" - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "through2": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", - "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", - "dev": true, - "requires": { - "inherits": "^2.0.4", - "readable-stream": "2 || 3" - } - } - } - }, - "terser": { + "inherits": "^2.0.4", + "readable-stream": "2 || 3" + } + }, + "node_modules/terser": { "version": "3.17.0", "resolved": "https://registry.npmjs.org/terser/-/terser-3.17.0.tgz", "integrity": "sha512-/FQzzPJmCpjAH9Xvk2paiWrFq+5M6aVOf+2KRbwhByISDX/EujxsK+BAvrhb6H+2rtrLCHK9N01wO014vrIwVQ==", "dev": true, - "requires": { + "dependencies": { "commander": "^2.19.0", "source-map": "~0.6.1", "source-map-support": "~0.5.10" }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } + "bin": { + "terser": "bin/uglifyjs" + }, + "engines": { + "node": ">=6.0.0" } }, - "textextensions": { + "node_modules/terser/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/textextensions": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-3.3.0.tgz", "integrity": "sha512-mk82dS8eRABNbeVJrEiN5/UMSCliINAuz8mkUwH4SwslkNP//gbEzlWNS5au0z5Dpx40SQxzqZevZkn+WYJ9Dw==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://bevry.me/fund" + } }, - "through": { + "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, - "through2": { + "node_modules/through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, - "requires": { + "dependencies": { "readable-stream": "~2.3.6", "xtend": "~4.0.1" } }, - "through2-filter": { + "node_modules/through2-filter": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", "dev": true, - "requires": { + "dependencies": { "through2": "~2.0.0", "xtend": "~4.0.0" } }, - "time-stamp": { + "node_modules/time-stamp": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "to-absolute-glob": { + "node_modules/to-absolute-glob": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=", "dev": true, - "requires": { + "dependencies": { "is-absolute": "^1.0.0", "is-negated-glob": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "to-object-path": { + "node_modules/to-object-path": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", "dev": true, - "requires": { + "dependencies": { "kind-of": "^3.0.2" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-object-path/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" } }, - "to-regex": { + "node_modules/to-regex": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", "dev": true, - "requires": { + "dependencies": { "define-property": "^2.0.2", "extend-shallow": "^3.0.2", "regex-not": "^1.0.2", "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "to-regex-range": { + "node_modules/to-regex-range": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", "dev": true, - "requires": { + "dependencies": { "is-number": "^3.0.0", "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "to-through": { + "node_modules/to-through": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz", "integrity": "sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY=", "dev": true, - "requires": { + "dependencies": { "through2": "^2.0.3" + }, + "engines": { + "node": ">= 0.10" } }, - "type": { + "node_modules/type": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", "dev": true }, - "typedarray": { + "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "dev": true }, - "unc-path-regex": { + "node_modules/unc-path-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "undertaker": { + "node_modules/undertaker": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.3.0.tgz", "integrity": "sha512-/RXwi5m/Mu3H6IHQGww3GNt1PNXlbeCuclF2QYR14L/2CHPz3DFZkvB5hZ0N/QUkiXWCACML2jXViIQEQc2MLg==", "dev": true, - "requires": { + "dependencies": { "arr-flatten": "^1.0.1", "arr-map": "^2.0.0", "bach": "^1.0.0", @@ -3520,157 +4274,195 @@ "object.defaults": "^1.0.0", "object.reduce": "^1.0.0", "undertaker-registry": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" } }, - "undertaker-registry": { + "node_modules/undertaker-registry": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz", "integrity": "sha1-XkvaMI5KiirlhPm5pDWaSZglzFA=", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.10" + } }, - "union-value": { + "node_modules/union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", "dev": true, - "requires": { + "dependencies": { "arr-union": "^3.1.0", "get-value": "^2.0.6", "is-extendable": "^0.1.1", "set-value": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "unique-stream": { + "node_modules/unique-stream": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", "dev": true, - "requires": { + "dependencies": { "json-stable-stringify-without-jsonify": "^1.0.1", "through2-filter": "^3.0.0" } }, - "unset-value": { + "node_modules/unset-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", "dev": true, - "requires": { + "dependencies": { "has-value": "^0.3.1", "isobject": "^3.0.0" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "dependencies": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true - } - } - }, - "upath": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/upath": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", - "dev": true + "dev": true, + "engines": { + "node": ">=4", + "yarn": "*" + } }, - "urix": { + "node_modules/urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "deprecated": "Please see https://github.com/lydell/urix#deprecated", "dev": true }, - "use": { + "node_modules/use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "useref": { + "node_modules/useref": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/useref/-/useref-1.4.3.tgz", "integrity": "sha512-hX0qTEaDdzO3WfFl2KHQnSdaPSk8KqRaQ/nXXjBPrXzanew+12U/gm/Jt7zR978wtKMryOMXGoF0+qhYL2oseQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=4" + } }, - "util-deprecate": { + "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, - "uuid": { + "node_modules/uuid": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", "dev": true }, - "v8flags": { + "node_modules/v8flags": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz", "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==", "dev": true, - "requires": { + "dependencies": { "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" } }, - "validate-npm-package-license": { + "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, - "requires": { + "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" } }, - "value-or-function": { + "node_modules/value-or-function": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz", "integrity": "sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.10" + } }, - "vinyl": { + "node_modules/vinyl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", "dev": true, - "requires": { + "dependencies": { "clone": "^2.1.1", "clone-buffer": "^1.0.0", "clone-stats": "^1.0.0", "cloneable-readable": "^1.0.0", "remove-trailing-separator": "^1.0.1", "replace-ext": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" } }, - "vinyl-fs": { + "node_modules/vinyl-fs": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz", "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==", "dev": true, - "requires": { + "dependencies": { "fs-mkdirp-stream": "^1.0.0", "glob-stream": "^6.1.0", "graceful-fs": "^4.0.0", @@ -3688,14 +4480,17 @@ "value-or-function": "^3.0.0", "vinyl": "^2.0.0", "vinyl-sourcemap": "^1.1.0" + }, + "engines": { + "node": ">= 0.10" } }, - "vinyl-sourcemap": { + "node_modules/vinyl-sourcemap": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz", "integrity": "sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY=", "dev": true, - "requires": { + "dependencies": { "append-buffer": "^1.0.2", "convert-source-map": "^1.5.0", "graceful-fs": "^4.1.6", @@ -3704,67 +4499,80 @@ "remove-bom-buffer": "^3.0.0", "vinyl": "^2.0.0" }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vinyl-sourcemap/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "which": { + "node_modules/which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, - "requires": { + "dependencies": { "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" } }, - "which-module": { + "node_modules/which-module": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", "dev": true }, - "wrap-ansi": { + "node_modules/wrap-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, - "requires": { + "dependencies": { "string-width": "^1.0.1", "strip-ansi": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "wrappy": { + "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, - "xtend": { + "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.4" + } }, - "y18n": { + "node_modules/y18n": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", "dev": true }, - "yargs": { + "node_modules/yargs": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.1.tgz", "integrity": "sha512-huO4Fr1f9PmiJJdll5kwoS2e4GqzGSsMT3PPMpOwoVkOK8ckqAewMTZyA6LXVQWflleb/Z8oPBEvNsMft0XE+g==", "dev": true, - "requires": { + "dependencies": { "camelcase": "^3.0.0", "cliui": "^3.2.0", "decamelize": "^1.1.1", @@ -3780,12 +4588,12 @@ "yargs-parser": "5.0.0-security.0" } }, - "yargs-parser": { + "node_modules/yargs-parser": { "version": "5.0.0-security.0", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0-security.0.tgz", "integrity": "sha512-T69y4Ps64LNesYxeYGYPvfoMTt/7y1XtfpIslUeK4um+9Hu7hlGoRtaDLvdXb7+/tfq4opVa2HRY5xGip022rQ==", "dev": true, - "requires": { + "dependencies": { "camelcase": "^3.0.0", "object.assign": "^4.1.0" } From f4aee564def106d6f1e6dabbe15b4579c20403bc Mon Sep 17 00:00:00 2001 From: smith120bh <42363318+smith120bh@users.noreply.github.com> Date: Wed, 23 Jul 2025 17:21:04 +1000 Subject: [PATCH 07/13] Fix parameters list being empty after refresh --- src/js/editor.js | 10 +++- src/js/modals.js | 143 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 149 insertions(+), 4 deletions(-) diff --git a/src/js/editor.js b/src/js/editor.js index b6992c93..09cb545d 100644 --- a/src/js/editor.js +++ b/src/js/editor.js @@ -751,11 +751,17 @@ if (typeof module !== 'undefined' && module.exports) { } function parameters(){ - // Refresh the parameters list before opening + if (!editor.modal || !editor.modal.parameters) { + console.error('editor.modal.parameters is not available'); + return; + } + + editor.modal.parameters.open(); + + // Ensure the parameters list is rendered when modal opens if (editor.modal.parameters.renderParametersList) { editor.modal.parameters.renderParametersList(); } - editor.modal.parameters.open(); } function loadFromUrl(url, cb){ diff --git a/src/js/modals.js b/src/js/modals.js index cbecda6d..4c192c7c 100644 --- a/src/js/modals.js +++ b/src/js/modals.js @@ -347,8 +347,7 @@ editor.modal = { } } - // Store the render function on the modal object so it can be called later - modal.renderParametersList = renderParametersList; + // Note: renderParametersList will be attached to the modal instance after construction // Show parameter form function showParameterForm(isEdit = false, parameterId = null) { @@ -606,4 +605,144 @@ editor.modal = { }); } }) +}; + +// Attach renderParametersList function to the actual modal instance after construction +editor.modal.parameters.renderParametersList = function() { + const parametersContainer = editor.modal.parameters.el.querySelector('#parameters-container'); + const parametersTable = editor.modal.parameters.el.querySelector('#parameters-table'); + const parametersEmpty = editor.modal.parameters.el.querySelector('#parameters-empty'); + const parametersTBody = editor.modal.parameters.el.querySelector('#parameters-tbody'); + + const parameters = editor.parametersManager.getParameters(); + const paramIds = Object.keys(parameters); + + if (paramIds.length === 0) { + parametersTable.style.display = 'none'; + parametersEmpty.style.display = 'block'; + } else { + parametersTable.style.display = 'table'; + parametersEmpty.style.display = 'none'; + + parametersTBody.innerHTML = ''; + + // Group parameters by parametric clone groups + const cloneGroups = {}; + const regularParams = []; + + paramIds.forEach(id => { + const param = parameters[id]; + // Check if this is a parametric clone parameter by looking for the timestamp pattern + const timestampMatch = param.name.match(/^clone_(cols|rows|spacing_x|spacing_y)_(\d+)$/); + if (timestampMatch) { + const timestamp = timestampMatch[2]; + if (!cloneGroups[timestamp]) { + cloneGroups[timestamp] = { + cols: null, + rows: null, + spacing_x: null, + spacing_y: null + }; + } + cloneGroups[timestamp][timestampMatch[1]] = { id, param }; + } else { + regularParams.push({ id, param }); + } + }); + + // Render regular parameters first + regularParams.forEach(({ id, param }) => { + const row = document.createElement('tr'); + row.style.borderBottom = '1px solid #eee'; + + row.innerHTML = + '@' + param.name + '' + + '' + editor.parametersManager.PARAM_TYPES[param.type].label + '' + + '' + param.defaultValue + '' + + '' + + '' + + '' + + ''; + + parametersTBody.appendChild(row); + }); + + // Add separator if we have both regular params and clone groups + if (regularParams.length > 0 && Object.keys(cloneGroups).length > 0) { + const separatorRow = document.createElement('tr'); + separatorRow.innerHTML = 'Parametric Clone Groups'; + parametersTBody.appendChild(separatorRow); + } + + // Render parametric clone groups + Object.keys(cloneGroups).forEach(timestamp => { + const group = cloneGroups[timestamp]; + + // Create group header + const headerRow = document.createElement('tr'); + headerRow.style.background = '#f5f5f5'; + headerRow.innerHTML = 'Clone Group ' + timestamp + ''; + parametersTBody.appendChild(headerRow); + + // Render group parameters in order: cols, rows, spacing_x, spacing_y + ['cols', 'rows', 'spacing_x', 'spacing_y'].forEach(type => { + if (group[type]) { + const { id, param } = group[type]; + const row = document.createElement('tr'); + row.style.borderBottom = '1px solid #eee'; + row.style.paddingLeft = '16px'; + + // Friendly names for grid parameters + const friendlyNames = { + cols: 'Columns', + rows: 'Rows', + spacing_x: 'Horizontal Spacing', + spacing_y: 'Vertical Spacing' + }; + + row.innerHTML = + '└ ' + friendlyNames[type] + '' + + '' + editor.parametersManager.PARAM_TYPES[param.type].label + '' + + '' + param.defaultValue + '' + + '' + + '' + + '' + + ''; + + parametersTBody.appendChild(row); + } + }); + }); + + // Add event listeners for edit/delete buttons (simplified for now) + editor.modal.parameters.el.querySelectorAll('.edit-param-btn').forEach(btn => { + btn.addEventListener('click', function() { + // TODO: Implement edit functionality + }); + }); + + editor.modal.parameters.el.querySelectorAll('.delete-param-btn').forEach(btn => { + btn.addEventListener('click', function() { + const id = this.dataset.id; + const param = editor.parametersManager.getParameter(id); + if (param && confirm("Are you sure you want to delete parameter \"@" + param.name + "\"?")) { + try { + editor.parametersManager.deleteParameter(id); + editor.modal.parameters.renderParametersList(); // Re-render after deletion + + // Update property validation autocomplete + if (editor.propertyValidation) { + editor.propertyValidation.addParameterAutocomplete(); + } + } catch (error) { + alert('Error deleting parameter: ' + error.message); + } + } + }); + }); + } }; \ No newline at end of file From 168b76f8e1ff399e4dbd807ea5d5bbdc19ddd1ca Mon Sep 17 00:00:00 2001 From: smith120bh <42363318+smith120bh@users.noreply.github.com> Date: Thu, 24 Jul 2025 10:30:41 +1000 Subject: [PATCH 08/13] Change parametric clone inputs to objects, improve behaviour --- src/js/ContextMenu.js | 9 ++ src/js/Menu.js | 9 ++ src/js/Parameters.js | 1 + src/js/editor.js | 96 ++++++------- src/js/modals.js | 323 ++++++++++++++++++++++++++++++++++++------ src/js/svgcanvas.js | 52 ++++--- 6 files changed, 379 insertions(+), 111 deletions(-) diff --git a/src/js/ContextMenu.js b/src/js/ContextMenu.js index 26fa50e4..5138423b 100644 --- a/src/js/ContextMenu.js +++ b/src/js/ContextMenu.js @@ -83,6 +83,15 @@ MD.ContextMenu = function(){ if (isParametricClone) { editor.editParametricClone(selectedElements[0]); } else { + // Clear any flags when opening from context menu + setTimeout(() => { + editor.modal.parametricClone.el.removeAttribute('data-opened-from-params'); + // Clear the name field for new clones + const nameInput = editor.modal.parametricClone.el.querySelector('#clone-name'); + if (nameInput) { + nameInput.value = ''; + } + }, 50); editor.modal.parametricClone.open(); } break; diff --git a/src/js/Menu.js b/src/js/Menu.js index 4c494a59..76db10f4 100644 --- a/src/js/Menu.js +++ b/src/js/Menu.js @@ -9,6 +9,15 @@ MD.Menu = function(){ $('#tool_group').on("click", editor.groupSelected); $('#tool_ungroup').on("click", editor.ungroupSelected); $('#tool_parametric_clone').on("click", function() { + // Clear any flags when opening from menu + setTimeout(() => { + editor.modal.parametricClone.el.removeAttribute('data-opened-from-params'); + // Clear the name field for new clones + const nameInput = editor.modal.parametricClone.el.querySelector('#clone-name'); + if (nameInput) { + nameInput.value = ''; + } + }, 50); editor.modal.parametricClone.open(); }); if (window.location.host === "editor.method.ac") { diff --git a/src/js/Parameters.js b/src/js/Parameters.js index df05103e..c035e43b 100644 --- a/src/js/Parameters.js +++ b/src/js/Parameters.js @@ -12,6 +12,7 @@ MD.Parameters = function(){ color: { label: 'Color', defaultValue: '#000000' }, boolean: { label: 'Boolean', defaultValue: false }, equation: { label: 'Equation', defaultValue: '0' }, + clone_config: { label: 'Clone Configuration', defaultValue: { num_cols: 3, num_rows: 2, spacing_x: 50, spacing_y: 50 } }, grid_cols: { label: 'Grid Columns', defaultValue: 3 }, grid_rows: { label: 'Grid Rows', defaultValue: 2 }, grid_spacing_x: { label: 'Horizontal Spacing', defaultValue: 50 }, diff --git a/src/js/editor.js b/src/js/editor.js index 09cb545d..0026c9cf 100644 --- a/src/js/editor.js +++ b/src/js/editor.js @@ -400,14 +400,11 @@ MD.Editor = function(){ const cloneHelperFunctions = []; cloneGroups.forEach((cloneGroup, index) => { - const colsParam = cloneGroup.getAttribute('data-cols-param'); - const rowsParam = cloneGroup.getAttribute('data-rows-param'); - const spacingXParam = cloneGroup.getAttribute('data-spacing-x-param'); - const spacingYParam = cloneGroup.getAttribute('data-spacing-y-param'); + const cloneParam = cloneGroup.getAttribute('data-clone-param'); // Find template group const templateGroup = cloneGroup.querySelector('[data-template="true"]'); - if (templateGroup) { + if (templateGroup && cloneParam) { // Get template elements as string const templateElements = Array.from(templateGroup.children).map(child => { return new XMLSerializer().serializeToString(child); @@ -419,10 +416,11 @@ MD.Editor = function(){ // Create helper function const helperFunction = ` function ${funcName}() { - const cols = ${colsParam}; - const rows = ${rowsParam}; - const spacingX = ${spacingXParam}; - const spacingY = ${spacingYParam}; + const config = ${cloneParam}; + const cols = config.num_cols; + const rows = config.num_rows; + const spacingX = config.spacing_x; + const spacingY = config.spacing_y; let elements = ''; for (let row = 0; row < rows; row++) { @@ -499,6 +497,9 @@ MD.Editor = function(){ defaultValue = `"${defaultValue}"`; } else if (param.type === 'boolean') { defaultValue = defaultValue === 'true' || defaultValue === true ? 'true' : 'false'; + } else if (param.type === 'clone_config' && typeof defaultValue === 'object') { + // Serialize clone config objects properly + defaultValue = JSON.stringify(defaultValue); } return defaultValue; @@ -529,7 +530,7 @@ MD.Editor = function(){ const hasAnyParams = hasInputParams || hasEquationParams; const inputParamComment = hasInputParams - ? inputParams.map(name => ` * @param {${paramMap[name].type}} ${name} - Default: ${paramMap[name].defaultValue}`).join('\n') + ? inputParams.map((name, i) => ` * @param {${paramMap[name].type}} ${name} - Default: ${paramDefaults[i]}`).join('\n') : ''; const equationParamComment = hasEquationParams ? equationParams.map(name => ` * @calculated {number} ${name} - Equation: ${paramMap[name].defaultValue}`).join('\n') @@ -550,6 +551,7 @@ MD.Editor = function(){ 'color': 'String', 'boolean': 'Boolean', 'equation': 'Number', + 'clone_config': 'Object', 'grid_cols': 'Number', 'grid_rows': 'Number', 'grid_spacing_x': 'Number', @@ -630,7 +632,7 @@ if (typeof module !== 'undefined' && module.exports) { } } - function createParametricClone(cols, rows, spacingX, spacingY) { + function createParametricClone(cloneName, cols, rows, spacingX, spacingY) { // Check if we have selected elements const selectedElements = svgCanvas.getSelectedElems(); if (!selectedElements || selectedElements.length === 0 || !selectedElements[0]) { @@ -638,27 +640,22 @@ if (typeof module !== 'undefined' && module.exports) { return; } - // Generate unique parameter names based on timestamp - const timestamp = Date.now(); - const colsParamName = `clone_cols_${timestamp}`; - const rowsParamName = `clone_rows_${timestamp}`; - const spacingXParamName = `clone_spacing_x_${timestamp}`; - const spacingYParamName = `clone_spacing_y_${timestamp}`; - try { - // Add the grid parameters to the parameter system - editor.parametersManager.addParameter(colsParamName, 'grid_cols', cols, 'Number of columns in the grid'); - editor.parametersManager.addParameter(rowsParamName, 'grid_rows', rows, 'Number of rows in the grid'); - editor.parametersManager.addParameter(spacingXParamName, 'grid_spacing_x', spacingX, 'Horizontal spacing between elements'); - editor.parametersManager.addParameter(spacingYParamName, 'grid_spacing_y', spacingY, 'Vertical spacing between elements'); + // Create the clone configuration object + const cloneConfig = { + num_cols: cols, + num_rows: rows, + spacing_x: spacingX, + spacing_y: spacingY + }; + + // Add the clone configuration parameter to the parameter system + editor.parametersManager.addParameter(cloneName, 'clone_config', cloneConfig, `Configuration for ${cloneName} clone group`); // Create the parametric clone group using SVG canvas const cloneGroupId = svgCanvas.createParametricCloneGroup( selectedElements, - colsParamName, - rowsParamName, - spacingXParamName, - spacingYParamName + cloneName ); if (cloneGroupId) { @@ -670,7 +667,7 @@ if (typeof module !== 'undefined' && module.exports) { } saveCanvas(); - alert(`Parametric clone created with ${cols}×${rows} grid pattern.`); + alert(`Parametric clone "${cloneName}" created with ${cols}×${rows} grid pattern.`); } } catch (error) { alert('Error creating parametric clone: ' + error.message); @@ -683,28 +680,29 @@ if (typeof module !== 'undefined' && module.exports) { return; } - // Get parameter names from the clone group - const colsParam = cloneGroup.getAttribute('data-cols-param'); - const rowsParam = cloneGroup.getAttribute('data-rows-param'); - const spacingXParam = cloneGroup.getAttribute('data-spacing-x-param'); - const spacingYParam = cloneGroup.getAttribute('data-spacing-y-param'); + // Get parameter name from the clone group + const cloneParam = cloneGroup.getAttribute('data-clone-param'); - if (!colsParam || !rowsParam || !spacingXParam || !spacingYParam) { + if (!cloneParam) { alert('Parametric clone data is corrupted.'); return; } - // Get current parameter values - const colsParamObj = editor.parametersManager.getParameterByName(colsParam); - const rowsParamObj = editor.parametersManager.getParameterByName(rowsParam); - const spacingXParamObj = editor.parametersManager.getParameterByName(spacingXParam); - const spacingYParamObj = editor.parametersManager.getParameterByName(spacingYParam); + // Get current parameter value + const cloneParamObj = editor.parametersManager.getParameterByName(cloneParam); - if (!colsParamObj || !rowsParamObj || !spacingXParamObj || !spacingYParamObj) { - alert('Could not find associated parameters for this parametric clone.'); + if (!cloneParamObj || cloneParamObj.type !== 'clone_config') { + alert('Could not find associated clone configuration parameter for this parametric clone.'); return; } + // Extract values from the configuration object + const config = cloneParamObj.defaultValue; + const cols = config.num_cols || 3; + const rows = config.num_rows || 2; + const spacingX = config.spacing_x || 50; + const spacingY = config.spacing_y || 50; + // Set up the modal with current values const modal = editor.modal.parametricClone; modal.open(); @@ -712,17 +710,15 @@ if (typeof module !== 'undefined' && module.exports) { // Pre-populate the form with current values setTimeout(() => { const modalEl = modal.el; - modalEl.querySelector('#clone-cols').value = colsParamObj.defaultValue; - modalEl.querySelector('#clone-rows').value = rowsParamObj.defaultValue; - modalEl.querySelector('#clone-spacing-x').value = spacingXParamObj.defaultValue; - modalEl.querySelector('#clone-spacing-y').value = spacingYParamObj.defaultValue; + modalEl.querySelector('#clone-name').value = cloneParam; + modalEl.querySelector('#clone-cols').value = cols; + modalEl.querySelector('#clone-rows').value = rows; + modalEl.querySelector('#clone-spacing-x').value = spacingX; + modalEl.querySelector('#clone-spacing-y').value = spacingY; - // Store the parameter names for updating + // Store the parameter name and clone group ID for updating modalEl.setAttribute('data-editing-clone', 'true'); - modalEl.setAttribute('data-cols-param', colsParam); - modalEl.setAttribute('data-rows-param', rowsParam); - modalEl.setAttribute('data-spacing-x-param', spacingXParam); - modalEl.setAttribute('data-spacing-y-param', spacingYParam); + modalEl.setAttribute('data-clone-param', cloneParam); modalEl.setAttribute('data-clone-group-id', cloneGroup.id); }, 50); } diff --git a/src/js/modals.js b/src/js/modals.js index 4c192c7c..9a535df3 100644 --- a/src/js/modals.js +++ b/src/js/modals.js @@ -104,7 +104,7 @@ editor.modal = {
    - +
    @@ -145,7 +145,7 @@ editor.modal = {
    + style="background: #666; color: white; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer; margin-right: 10px;">Close
    @@ -272,7 +272,7 @@ editor.modal = { row.innerHTML = '@' + param.name + '' + '' + editor.parametersManager.PARAM_TYPES[param.type].label + '' + - '' + param.defaultValue + '' + + '' + formatParameterValue(param) + '' + '' + '' + @@ -319,7 +319,7 @@ editor.modal = { row.innerHTML = '└ ' + friendlyNames[type] + '' + '' + editor.parametersManager.PARAM_TYPES[param.type].label + '' + - '' + param.defaultValue + '' + + '' + formatParameterValue(param) + '' + '' + '' + @@ -389,7 +389,38 @@ editor.modal = { // Edit parameter function editParameter(id) { - showParameterForm(true, id); + const param = editor.parametersManager.getParameter(id); + + // If this is a clone configuration parameter, open the parametric clone modal instead + if (param && param.type === 'clone_config') { + // Find the clone group that uses this parameter + const cloneGroups = document.querySelectorAll('[data-parametric-clone="true"]'); + let targetCloneGroup = null; + + cloneGroups.forEach(group => { + if (group.getAttribute('data-clone-param') === param.name) { + targetCloneGroup = group; + } + }); + + if (targetCloneGroup) { + // Close the parameters modal + editor.modal.parameters.close(); + + // Mark that we're opening from parameters manager + setTimeout(() => { + editor.modal.parametricClone.el.setAttribute('data-opened-from-params', 'true'); + }, 50); + + // Open the parametric clone modal for editing + editor.editParametricClone(targetCloneGroup); + } else { + alert('Could not find the clone group associated with this parameter.'); + } + } else { + // Regular parameter editing + showParameterForm(true, id); + } } // Delete parameter @@ -448,8 +479,13 @@ editor.modal = { } try { + // Check for manual edit ID first, then fall back to regular editingParameterId + const manualEditId = el.getAttribute('data-manual-edit-id'); if (editingParameterId) { editor.parametersManager.updateParameter(editingParameterId, name, type, defaultValue, description); + } else if (manualEditId) { + editor.parametersManager.updateParameter(manualEditId, name, type, defaultValue, description); + el.removeAttribute('data-manual-edit-id'); // Clean up } else { editor.parametersManager.addParameter(name, type, defaultValue, description); } @@ -465,6 +501,10 @@ editor.modal = { alert('Error saving parameter: ' + error.message); } }); + + // Expose functions for use by renderParametersList + this.editParameter = editParameter; + this.showParameterForm = showParameterForm; } }), @@ -476,6 +516,12 @@ editor.modal = {

    +
    + + +
    +
    @@ -504,7 +550,7 @@ editor.modal = {
    - +
    `, @@ -512,58 +558,88 @@ editor.modal = { js: function(el) { // Handle cancel button el.querySelector('#parametric-clone-cancel').addEventListener('click', function() { + const openedFromParams = el.getAttribute('data-opened-from-params') === 'true'; editor.modal.parametricClone.close(); + + // If opened from Parameters Manager, reopen it + if (openedFromParams) { + setTimeout(() => { + editor.modal.parameters.open(); + }, 100); + } }); // Handle create button el.querySelector('#parametric-clone-create').addEventListener('click', function() { + const cloneName = el.querySelector('#clone-name').value.trim(); const cols = parseInt(el.querySelector('#clone-cols').value) || 3; const rows = parseInt(el.querySelector('#clone-rows').value) || 2; const spacingX = parseInt(el.querySelector('#clone-spacing-x').value) || 50; const spacingY = parseInt(el.querySelector('#clone-spacing-y').value) || 50; + // Validate clone name + if (!cloneName) { + alert('Please enter a name for the clone group.'); + return; + } + + // Check if name is valid parameter name + if (!editor.parametersManager.isValidParameterName(cloneName)) { + alert('Invalid parameter name. Use only letters, numbers, and underscores. Cannot start with a number.'); + return; + } + const isEditing = el.getAttribute('data-editing-clone') === 'true'; if (isEditing) { // Update existing parametric clone - const colsParam = el.getAttribute('data-cols-param'); - const rowsParam = el.getAttribute('data-rows-param'); - const spacingXParam = el.getAttribute('data-spacing-x-param'); - const spacingYParam = el.getAttribute('data-spacing-y-param'); + const cloneParam = el.getAttribute('data-clone-param'); const cloneGroupId = el.getAttribute('data-clone-group-id'); try { - // Update parameter values const parametersManager = editor.parametersManager; - const colsParamObj = parametersManager.getParameterByName(colsParam); - const rowsParamObj = parametersManager.getParameterByName(rowsParam); - const spacingXParamObj = parametersManager.getParameterByName(spacingXParam); - const spacingYParamObj = parametersManager.getParameterByName(spacingYParam); + const cloneParamObj = parametersManager.getParameterByName(cloneParam); - if (colsParamObj && rowsParamObj && spacingXParamObj && spacingYParamObj) { - // Get parameter IDs from the parameters object + if (cloneParamObj) { + // Check if renaming to a different name that already exists + if (cloneName !== cloneParam && parametersManager.parameterExists(cloneName)) { + const existingParam = parametersManager.getParameterByName(cloneName); + if (existingParam && existingParam.type === 'clone_config') { + alert(`A parametric clone with the name "${cloneName}" already exists. Please choose a different name.`); + } else { + alert(`A parameter with the name "${cloneName}" already exists. Please choose a different name.`); + } + return; + } + + // Get parameter ID const parameters = parametersManager.getParameters(); - let colsId, rowsId, spacingXId, spacingYId; + let cloneParamId; Object.keys(parameters).forEach(id => { const param = parameters[id]; - if (param.name === colsParam) colsId = id; - if (param.name === rowsParam) rowsId = id; - if (param.name === spacingXParam) spacingXId = id; - if (param.name === spacingYParam) spacingYId = id; + if (param.name === cloneParam) cloneParamId = id; }); - if (colsId) parametersManager.updateParameter(colsId, colsParam, 'grid_cols', cols, colsParamObj.description); - if (rowsId) parametersManager.updateParameter(rowsId, rowsParam, 'grid_rows', rows, rowsParamObj.description); - if (spacingXId) parametersManager.updateParameter(spacingXId, spacingXParam, 'grid_spacing_x', spacingX, spacingXParamObj.description); - if (spacingYId) parametersManager.updateParameter(spacingYId, spacingYParam, 'grid_spacing_y', spacingY, spacingYParamObj.description); - - // Regenerate the clone group - if (typeof svgCanvas.updateParametricCloneGroup === 'function') { - svgCanvas.updateParametricCloneGroup(cloneGroupId); + if (cloneParamId) { + const newConfig = { num_cols: cols, num_rows: rows, spacing_x: spacingX, spacing_y: spacingY }; + parametersManager.updateParameter(cloneParamId, cloneName, 'clone_config', newConfig, cloneParamObj.description); + + // Update the clone group's data attribute if name changed + if (cloneName !== cloneParam) { + const cloneGroup = svgedit.utilities.getElem(cloneGroupId); + if (cloneGroup) { + cloneGroup.setAttribute('data-clone-param', cloneName); + } + } + + // Regenerate the clone group + if (typeof svgCanvas.updateParametricCloneGroup === 'function') { + svgCanvas.updateParametricCloneGroup(cloneGroupId); + } + + alert('Parametric clone updated successfully.'); } - - alert('Parametric clone updated successfully.'); } } catch (error) { alert('Error updating parametric clone: ' + error.message); @@ -571,22 +647,50 @@ editor.modal = { // Clear editing state el.removeAttribute('data-editing-clone'); - el.removeAttribute('data-cols-param'); - el.removeAttribute('data-rows-param'); - el.removeAttribute('data-spacing-x-param'); - el.removeAttribute('data-spacing-y-param'); + el.removeAttribute('data-clone-param'); el.removeAttribute('data-clone-group-id'); } else { + // Check if parameter name already exists + if (editor.parametersManager.parameterExists(cloneName)) { + const existingParam = editor.parametersManager.getParameterByName(cloneName); + if (existingParam && existingParam.type === 'clone_config') { + alert(`A parametric clone with the name "${cloneName}" already exists. Please choose a different name.`); + } else { + alert(`A parameter with the name "${cloneName}" already exists. Please choose a different name.`); + } + return; + } + + // Double-check: look for existing clone groups with this name + const existingCloneGroups = document.querySelectorAll('[data-parametric-clone="true"]'); + let nameAlreadyUsed = false; + existingCloneGroups.forEach(group => { + if (group.getAttribute('data-clone-param') === cloneName) { + nameAlreadyUsed = true; + } + }); + + if (nameAlreadyUsed) { + alert(`A parametric clone with the name "${cloneName}" already exists in the canvas. Please choose a different name.`); + return; + } + // Create new parametric clone if (typeof editor.createParametricClone === 'function') { - editor.createParametricClone(cols, rows, spacingX, spacingY); + editor.createParametricClone(cloneName, cols, rows, spacingX, spacingY); } } + // Clear the flag before closing + el.removeAttribute('data-opened-from-params'); editor.modal.parametricClone.close(); }); // Ensure input fields are clickable (fix for modal z-index issues) + el.querySelector('#clone-name').addEventListener('mousedown', function(e) { + e.stopPropagation(); + this.focus(); + }); el.querySelector('#clone-cols').addEventListener('mousedown', function(e) { e.stopPropagation(); this.focus(); @@ -607,6 +711,143 @@ editor.modal = { }) }; +// Helper function to format parameter display values +function formatParameterValue(param) { + if (param.type === 'clone_config' && typeof param.defaultValue === 'object') { + const config = param.defaultValue; + return `${config.num_cols || 1}×${config.num_rows || 1} grid\n${config.spacing_x || 0} × ${config.spacing_y || 0} spacing`; + } + return param.defaultValue; +} + +// Global editParameter function for parameters modal +function editParameterGlobal(id) { + const param = editor.parametersManager.getParameter(id); + + // If this is a clone configuration parameter, open the parametric clone modal instead + if (param && param.type === 'clone_config') { + // Find the clone group that uses this parameter + const cloneGroups = document.querySelectorAll('[data-parametric-clone="true"]'); + let targetCloneGroup = null; + + cloneGroups.forEach(group => { + if (group.getAttribute('data-clone-param') === param.name) { + targetCloneGroup = group; + } + }); + + if (targetCloneGroup) { + // Close the parameters modal + editor.modal.parameters.close(); + + // Mark that we're opening from parameters manager + setTimeout(() => { + editor.modal.parametricClone.el.setAttribute('data-opened-from-params', 'true'); + }, 50); + + // Open the parametric clone modal for editing + editor.editParametricClone(targetCloneGroup); + } else { + alert('Could not find the clone group associated with this parameter.'); + } + } else { + // Regular parameter editing - try multiple approaches + if (editor.modal.parameters.showParameterForm) { + editor.modal.parameters.showParameterForm(true, id); + } else if (editor.modal.parameters.editParameter) { + editor.modal.parameters.editParameter(id); + } else { + // Use manual parameter form approach + if (showParameterFormManually(param, id)) { + console.log('Successfully opened parameter form manually'); + } else { + alert('Error: Cannot edit parameter. Please try closing and reopening the Parameters Manager.'); + } + } + } +} + +// Simple function to manually show parameter form for editing +function showParameterFormManually(param, id) { + const modal = editor.modal.parameters; + const parametersContainer = modal.el.querySelector('#parameters-container'); + const parameterForm = modal.el.querySelector('#parameter-form'); + const parameterFormTitle = modal.el.querySelector('#parameter-form-title'); + const paramNameInput = modal.el.querySelector('#param-name'); + const paramTypeSelect = modal.el.querySelector('#param-type'); + const paramDescriptionInput = modal.el.querySelector('#param-description'); + + if (!parameterForm || !parametersContainer) return false; + + // Hide parameters list and show form (same as Add Parameter behavior) + parametersContainer.style.display = 'none'; + parameterForm.style.display = 'block'; + parameterFormTitle.textContent = 'Edit Parameter'; + paramNameInput.value = param.name || ''; + paramTypeSelect.value = param.type || 'text'; + paramDescriptionInput.value = param.description || ''; + + // Handle default value input based on type - use the same approach as the original function + const updateDefaultValueInput = () => { + const type = param.type; + let currentElement = modal.el.querySelector('#param-default'); + + if (!currentElement) return; // Exit if no default input field found + + const baseStyle = 'width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; position: relative; z-index: 1001; pointer-events: auto;'; + + if (type === 'boolean') { + // Replace input with select for boolean + if (currentElement.tagName !== 'SELECT') { + const select = document.createElement('select'); + select.id = 'param-default'; + select.style.cssText = baseStyle; + select.innerHTML = ''; + select.value = param.defaultValue === true || param.defaultValue === 'true' ? 'true' : 'false'; + currentElement.parentNode.replaceChild(select, currentElement); + } + } else { + // Replace select with input for non-boolean types, or just update if already input + if (currentElement.tagName === 'SELECT') { + const input = document.createElement('input'); + input.id = 'param-default'; + input.style.cssText = baseStyle; + currentElement.parentNode.replaceChild(input, currentElement); + currentElement = input; + } + + // Set input properties based on type + switch (type) { + case 'number': + currentElement.type = 'number'; + currentElement.value = param.defaultValue || ''; + break; + case 'color': + currentElement.type = 'color'; + currentElement.value = param.defaultValue || '#000000'; + break; + default: + currentElement.type = 'text'; + currentElement.value = param.defaultValue || ''; + break; + } + } + + // Make it clickable + currentElement.addEventListener('mousedown', function(e) { + e.stopPropagation(); + this.focus(); + }); + }; + + updateDefaultValueInput(); + + // Store the editing parameter ID for form submission + modal.el.setAttribute('data-manual-edit-id', id); + + return true; +} + // Attach renderParametersList function to the actual modal instance after construction editor.modal.parameters.renderParametersList = function() { const parametersContainer = editor.modal.parameters.el.querySelector('#parameters-container'); @@ -658,7 +899,7 @@ editor.modal.parameters.renderParametersList = function() { row.innerHTML = '@' + param.name + '' + '' + editor.parametersManager.PARAM_TYPES[param.type].label + '' + - '' + param.defaultValue + '' + + '' + formatParameterValue(param) + '' + '' + '' + @@ -705,7 +946,7 @@ editor.modal.parameters.renderParametersList = function() { row.innerHTML = '└ ' + friendlyNames[type] + '' + '' + editor.parametersManager.PARAM_TYPES[param.type].label + '' + - '' + param.defaultValue + '' + + '' + formatParameterValue(param) + '' + '' + '' + @@ -718,10 +959,10 @@ editor.modal.parameters.renderParametersList = function() { }); }); - // Add event listeners for edit/delete buttons (simplified for now) + // Add event listeners for edit/delete buttons editor.modal.parameters.el.querySelectorAll('.edit-param-btn').forEach(btn => { btn.addEventListener('click', function() { - // TODO: Implement edit functionality + editParameterGlobal(this.dataset.id); }); }); diff --git a/src/js/svgcanvas.js b/src/js/svgcanvas.js index 1f49dcd5..5099f14c 100644 --- a/src/js/svgcanvas.js +++ b/src/js/svgcanvas.js @@ -8126,16 +8126,23 @@ this.groupSelectedElements = function(type) { // Function: createParametricCloneGroup // Creates a parametric clone group with grid layout -this.createParametricCloneGroup = function(templateElements, colsParamName, rowsParamName, spacingXParamName, spacingYParamName) { +this.createParametricCloneGroup = function(templateElements, cloneParamName) { if (!templateElements || templateElements.length === 0) { return null; } - // Get the parameter values - const cols = editor.parametersManager.resolveParameterValue('@' + colsParamName); - const rows = editor.parametersManager.resolveParameterValue('@' + rowsParamName); - const spacingX = editor.parametersManager.resolveParameterValue('@' + spacingXParamName); - const spacingY = editor.parametersManager.resolveParameterValue('@' + spacingYParamName); + // Get the clone configuration parameter + const cloneConfig = editor.parametersManager.resolveParameterValue('@' + cloneParamName); + if (!cloneConfig || typeof cloneConfig !== 'object') { + console.error('Invalid clone configuration parameter:', cloneParamName); + return null; + } + + // Extract values from the configuration object + const cols = cloneConfig.num_cols || 3; + const rows = cloneConfig.num_rows || 2; + const spacingX = cloneConfig.spacing_x || 50; + const spacingY = cloneConfig.spacing_y || 50; const batchCmd = new BatchCommand("Create Parametric Clone"); @@ -8146,10 +8153,7 @@ this.createParametricCloneGroup = function(templateElements, colsParamName, rows "attr": { "id": cloneGroupId, "data-parametric-clone": "true", - "data-cols-param": colsParamName, - "data-rows-param": rowsParamName, - "data-spacing-x-param": spacingXParamName, - "data-spacing-y-param": spacingYParamName + "data-clone-param": cloneParamName } }); @@ -8257,17 +8261,25 @@ this.updateParametricCloneGroup = function(cloneGroupId) { return false; } - // Get parameter names - const colsParamName = cloneGroup.getAttribute('data-cols-param'); - const rowsParamName = cloneGroup.getAttribute('data-rows-param'); - const spacingXParamName = cloneGroup.getAttribute('data-spacing-x-param'); - const spacingYParamName = cloneGroup.getAttribute('data-spacing-y-param'); + // Get the clone parameter name + const cloneParamName = cloneGroup.getAttribute('data-clone-param'); + if (!cloneParamName) { + console.error('Clone parameter name not found in parametric clone group'); + return false; + } + + // Get the clone configuration parameter + const cloneConfig = editor.parametersManager.resolveParameterValue('@' + cloneParamName); + if (!cloneConfig || typeof cloneConfig !== 'object') { + console.error('Invalid clone configuration parameter:', cloneParamName); + return false; + } - // Get updated parameter values - const cols = editor.parametersManager.resolveParameterValue('@' + colsParamName); - const rows = editor.parametersManager.resolveParameterValue('@' + rowsParamName); - const spacingX = editor.parametersManager.resolveParameterValue('@' + spacingXParamName); - const spacingY = editor.parametersManager.resolveParameterValue('@' + spacingYParamName); + // Extract values from the configuration object + const cols = cloneConfig.num_cols || 3; + const rows = cloneConfig.num_rows || 2; + const spacingX = cloneConfig.spacing_x || 50; + const spacingY = cloneConfig.spacing_y || 50; const batchCmd = new BatchCommand("Update Parametric Clone"); From 93a5cba8db3675b1995d051e470fda6065b9316d Mon Sep 17 00:00:00 2001 From: smith120bh <42363318+smith120bh@users.noreply.github.com> Date: Thu, 24 Jul 2025 11:41:38 +1000 Subject: [PATCH 09/13] Add ability to create a new parameter directly from autocomplete menu --- src/css/property-validation.css | 64 +++++ src/js/PropertyValidation.js | 447 ++++++++++++++++++++++++++++++-- src/js/modals.js | 56 +++- 3 files changed, 545 insertions(+), 22 deletions(-) diff --git a/src/css/property-validation.css b/src/css/property-validation.css index 0e7f1295..87693a87 100644 --- a/src/css/property-validation.css +++ b/src/css/property-validation.css @@ -93,6 +93,70 @@ datalist option { font-family: monospace; } +/* Custom parameter autocomplete dropdown */ +.parameter-autocomplete-dropdown { + position: absolute; + background: white; + border: 1px solid #ddd; + border-radius: 4px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + max-height: 200px; + overflow-y: auto; + z-index: 1000; + min-width: 150px; + font-family: monospace; + font-size: 12px; + display: none; +} + +/* Force hide any lingering dropdown elements */ +.parameter-autocomplete-dropdown[style*="display: none"] { + display: none !important; + visibility: hidden !important; + opacity: 0 !important; + pointer-events: none !important; +} + +#parameter-autocomplete-dropdown[style*="display: none"] { + display: none !important; + visibility: hidden !important; + opacity: 0 !important; + pointer-events: none !important; +} + +.parameter-autocomplete-item { + padding: 8px 12px; + cursor: pointer; + border-bottom: 1px solid #f0f0f0; + color: #007cba; + background: #f8f9fa; +} + +.parameter-autocomplete-item:hover, +.parameter-autocomplete-item.highlighted { + background: #007cba; + color: white; +} + +.parameter-autocomplete-item:last-child { + border-bottom: none; +} + +.parameter-autocomplete-create { + padding: 8px 12px; + cursor: pointer; + color: #28a745; + background: #f8fff9; + border-top: 1px solid #ddd; + font-style: italic; +} + +.parameter-autocomplete-create:hover, +.parameter-autocomplete-create.highlighted { + background: #28a745; + color: white; +} + /* Focus states for parameter inputs */ input.param-reference:focus { box-shadow: 0 0 0 2px rgba(0, 124, 186, 0.25); diff --git a/src/js/PropertyValidation.js b/src/js/PropertyValidation.js index 66eb670e..9f1fb19f 100644 --- a/src/js/PropertyValidation.js +++ b/src/js/PropertyValidation.js @@ -1,6 +1,9 @@ MD.PropertyValidation = function() { const _self = this; + // Global flag to prevent dropdown recreation during parameter creation + let isCreatingParameterFromAutocomplete = false; + // Pattern to match parameter references (@paramName) const PARAM_REFERENCE_PATTERN = /^@[a-zA-Z_][a-zA-Z0-9_]*$/; @@ -53,6 +56,7 @@ MD.PropertyValidation = function() { // Add visual feedback to input field based on validation function updateInputValidation(input) { + if (!input || !input.value) return; // Add null check let value = input.value.trim(); // Handle parameter display format: "@width (200)" - extract just the parameter reference @@ -268,39 +272,441 @@ MD.PropertyValidation = function() { // Add autocomplete functionality for parameter names function addParameterAutocomplete() { - // Create a datalist element for parameter suggestions - let datalist = document.getElementById('parameter-suggestions'); - if (!datalist) { - datalist = document.createElement('datalist'); - datalist.id = 'parameter-suggestions'; - document.body.appendChild(datalist); + // If we're in the middle of autocomplete parameter creation, skip recreation + if (isCreatingParameterFromAutocomplete) { + return; + } + + // Remove any existing dropdown elements first + const allPossibleDropdowns = document.querySelectorAll( + '#parameter-autocomplete-dropdown, ' + + '.parameter-autocomplete-dropdown, ' + + '[class*="parameter-autocomplete"], ' + + '[id*="parameter-autocomplete"]' + ); + allPossibleDropdowns.forEach((el) => { + el.style.display = 'none !important'; + el.style.visibility = 'hidden !important'; + el.style.opacity = '0 !important'; + el.innerHTML = ''; + if (el.parentNode) { + el.parentNode.removeChild(el); + } + }); + + // Remove old datalist if it exists + const oldDatalist = document.getElementById('parameter-suggestions'); + if (oldDatalist) { + oldDatalist.remove(); + } + + // Create custom dropdown element + let dropdown = document.getElementById('parameter-autocomplete-dropdown'); + if (!dropdown) { + dropdown = document.createElement('div'); + dropdown.id = 'parameter-autocomplete-dropdown'; + dropdown.className = 'parameter-autocomplete-dropdown'; + document.body.appendChild(dropdown); } - // Update the datalist with current parameters - function updateParameterSuggestions() { + // Always ensure dropdown is clean and hidden when initializing + dropdown.style.display = 'none'; + dropdown.style.visibility = 'hidden'; + dropdown.innerHTML = ''; + + let currentInput = null; + let highlightedIndex = -1; + let currentParams = []; + let currentTypedText = ''; + + // Store autocomplete context for parameter creation + let autocompleteContext = { + input: null, + inputSelector: '', + inputAttr: '', + parameterName: '', + active: false + }; + + // Show dropdown with parameter suggestions + function showDropdown(input, typedText) { if (!editor.parametersManager) return; + + // Ensure dropdown is back in DOM if it was removed + if (!dropdown.parentNode) { + document.body.appendChild(dropdown); + } + + currentInput = input; + currentTypedText = typedText; + + // Store context for potential parameter creation + const inputAttr = input.getAttribute('data-attr') || ''; + const inputId = input.id || ''; + const inputSelector = inputId ? `#${inputId}` : `input[data-attr="${inputAttr}"]`; + + autocompleteContext = { + input: input, + inputSelector: inputSelector, + inputAttr: inputAttr, + parameterName: typedText, + active: false // Will be set to true if create option is clicked + }; + + // Get current parameters const paramNames = editor.parametersManager.getParameterNames(); - datalist.innerHTML = paramNames.map(name => ``).join(''); + + // Filter parameters based on typed text (without @) + const filterText = typedText.toLowerCase(); + currentParams = paramNames.filter(name => + name.toLowerCase().includes(filterText) || filterText === '' + ); + + // Build dropdown content + let html = ''; + + // Add existing parameters + currentParams.forEach((name, index) => { + html += `
    @${name}
    `; + }); + + // Add "Create parameter" option if we have typed text + if (typedText.length > 0) { + const createIndex = currentParams.length; + html += `
    Create '@${typedText}'...
    `; + } + + dropdown.innerHTML = html; + + // Position dropdown below the input + const inputRect = input.getBoundingClientRect(); + dropdown.style.left = inputRect.left + 'px'; + dropdown.style.top = (inputRect.bottom + 2) + 'px'; + dropdown.style.display = 'block'; + dropdown.style.visibility = 'visible'; + + // Reset highlighted index + highlightedIndex = -1; + + // Add click handlers to dropdown items + dropdown.querySelectorAll('.parameter-autocomplete-item, .parameter-autocomplete-create').forEach(item => { + item.addEventListener('click', function(e) { + e.preventDefault(); + e.stopPropagation(); + selectItem(parseInt(this.getAttribute('data-index'))); + }); + }); + } + + // Hide dropdown + function hideDropdown() { + // Find ALL dropdown elements that might exist + const allDropdowns = document.querySelectorAll('#parameter-autocomplete-dropdown, .parameter-autocomplete-dropdown'); + + // Hide all of them + allDropdowns.forEach((dd) => { + dd.style.display = 'none'; + dd.style.visibility = 'hidden'; + dd.innerHTML = ''; + if (dd.parentNode) { + dd.parentNode.removeChild(dd); + } + }); + + // Hide our specific dropdown too + dropdown.style.display = 'none'; + dropdown.style.visibility = 'hidden'; + dropdown.innerHTML = ''; + + // Also temporarily remove from DOM to ensure it's really gone + if (dropdown.parentNode) { + dropdown.parentNode.removeChild(dropdown); + } + + currentInput = null; + highlightedIndex = -1; + currentParams = []; + } + + // Highlight item by index + function highlightItem(index) { + // Remove previous highlights + dropdown.querySelectorAll('.highlighted').forEach(item => { + item.classList.remove('highlighted'); + }); + + // Highlight new item + const items = dropdown.querySelectorAll('.parameter-autocomplete-item, .parameter-autocomplete-create'); + if (index >= 0 && index < items.length) { + items[index].classList.add('highlighted'); + highlightedIndex = index; + } else { + highlightedIndex = -1; + } + } + + // Select item by index + function selectItem(index) { + const items = dropdown.querySelectorAll('.parameter-autocomplete-item, .parameter-autocomplete-create'); + if (index < 0 || index >= items.length) return; + + const item = items[index]; + const isCreateOption = item.hasAttribute('data-create'); + + if (isCreateOption) { + // Set global flag to prevent dropdown recreation + isCreatingParameterFromAutocomplete = true; + + // Activate the context for parameter creation BEFORE hiding dropdown + autocompleteContext.parameterName = currentTypedText; + autocompleteContext.active = true; + + // Hide dropdown completely + hideDropdown(); + + // Nuclear option: destroy ALL possible dropdown elements + setTimeout(() => { + const allPossibleDropdowns = document.querySelectorAll( + '#parameter-autocomplete-dropdown, ' + + '.parameter-autocomplete-dropdown, ' + + '[class*="parameter-autocomplete"], ' + + '[id*="parameter-autocomplete"]' + ); + allPossibleDropdowns.forEach(el => { + el.style.display = 'none !important'; + el.style.visibility = 'hidden !important'; + el.style.opacity = '0 !important'; + el.innerHTML = ''; + if (el.parentNode) { + el.parentNode.removeChild(el); + } + }); + }, 1); + + // Try multiple approaches to open the modal + if (editor.modal && editor.modal.parameters) { + editor.modal.parameters.open(); + + // Small delay to ensure modal is ready + setTimeout(() => { + // Try to access the showParameterForm function + if (editor.modal.parameters.showParameterForm) { + editor.modal.parameters.showParameterForm(false, null); + + // Pre-fill the name field + setTimeout(() => { + const nameInput = editor.modal.parameters.el.querySelector('#param-name'); + if (nameInput) { + nameInput.value = currentTypedText; + nameInput.focus(); + } + }, 50); + } else { + // Fallback 1: Try clicking the Add Parameter button + const addParameterBtn = editor.modal.parameters.el.querySelector('#add-parameter-btn'); + if (addParameterBtn) { + addParameterBtn.click(); + + // Pre-fill the name field after button click + setTimeout(() => { + const nameInput = editor.modal.parameters.el.querySelector('#param-name'); + if (nameInput) { + nameInput.value = currentTypedText; + nameInput.focus(); + } + }, 50); + } else { + // Fallback 2: Manual approach - directly manipulate the modal DOM + const parametersContainer = editor.modal.parameters.el.querySelector('#parameters-container'); + const parameterForm = editor.modal.parameters.el.querySelector('#parameter-form'); + const parameterFormTitle = editor.modal.parameters.el.querySelector('#parameter-form-title'); + const paramNameInput = editor.modal.parameters.el.querySelector('#param-name'); + const parameterFormElement = editor.modal.parameters.el.querySelector('#parameter-form-element'); + + if (parametersContainer && parameterForm && paramNameInput) { + // Hide parameters list and show form + parametersContainer.style.display = 'none'; + parameterForm.style.display = 'block'; + parameterFormTitle.textContent = 'Add Parameter'; + + // Reset form and set name + parameterFormElement.reset(); + paramNameInput.value = currentTypedText; + paramNameInput.focus(); + } + } + } + }, 100); + } + } else { + // Select existing parameter + const paramValue = item.getAttribute('data-value'); + if (currentInput && paramValue) { + currentInput.value = paramValue; + hideDropdown(); + + // Trigger input validation and parameter handling + if (currentInput && currentInput.value) { + updateInputValidation(currentInput); + handleParameterInput(currentInput); + } + } + } + } + + // Handle keyboard navigation + function handleKeydown(e) { + if (dropdown.style.display === 'none') return; + + const items = dropdown.querySelectorAll('.parameter-autocomplete-item, .parameter-autocomplete-create'); + const itemCount = items.length; + + switch (e.key) { + case 'ArrowDown': + e.preventDefault(); + highlightItem((highlightedIndex + 1) % itemCount); + break; + + case 'ArrowUp': + e.preventDefault(); + highlightItem(highlightedIndex <= 0 ? itemCount - 1 : highlightedIndex - 1); + break; + + case 'Enter': + e.preventDefault(); + if (highlightedIndex >= 0) { + selectItem(highlightedIndex); + } + break; + + case 'Escape': + e.preventDefault(); + hideDropdown(); + break; + } } // Add autocomplete to property inputs const propertyInputs = document.querySelectorAll('.attr_changer, input[data-attr]'); propertyInputs.forEach(input => { - input.setAttribute('list', 'parameter-suggestions'); + // Remove old list attribute if it exists + input.removeAttribute('list'); - // Update suggestions when input gets focus - input.addEventListener('focus', updateParameterSuggestions); - - // Also trigger on input for @ character + // Handle input changes input.addEventListener('input', function(e) { - if (e.target.value.includes('@')) { - updateParameterSuggestions(); + const value = e.target.value; + const atIndex = value.lastIndexOf('@'); + + if (atIndex !== -1) { + // Extract text after the last @ + const afterAt = value.substring(atIndex + 1); + + // Only show dropdown if @ is at the start or preceded by non-alphanumeric + const beforeAt = atIndex > 0 ? value.charAt(atIndex - 1) : ' '; + if (atIndex === 0 || !/[a-zA-Z0-9_]/.test(beforeAt)) { + showDropdown(this, afterAt); + } + } else { + hideDropdown(); } }); + + // Handle keyboard events + input.addEventListener('keydown', handleKeydown); + + // Hide dropdown when input loses focus (with small delay to allow clicks) + input.addEventListener('blur', function() { + setTimeout(() => { + if (!dropdown.matches(':hover')) { + hideDropdown(); + } + }, 150); + }); + }); + + // Hide dropdown when clicking outside + document.addEventListener('click', function(e) { + if (!dropdown.contains(e.target) && currentInput !== e.target) { + hideDropdown(); + } }); - // Update suggestions when parameters change - updateParameterSuggestions(); + // Function to handle parameter creation from autocomplete + function handleParameterCreated(parameterName) { + + if (autocompleteContext.active) { + // Clear context if empty parameter name (error case) or if parameter matches + if (!parameterName || parameterName === autocompleteContext.parameterName) { + if (parameterName) { + // Try to re-find the input field using multiple methods + let targetInput = autocompleteContext.input; + + // If the original reference is stale, try to find it again + if (!targetInput || !document.body.contains(targetInput)) { + // Try by selector first + if (autocompleteContext.inputSelector) { + targetInput = document.querySelector(autocompleteContext.inputSelector); + } + + // Fallback: find by data-attr + if (!targetInput && autocompleteContext.inputAttr) { + targetInput = document.querySelector(`input[data-attr="${autocompleteContext.inputAttr}"]`); + } + } + + if (targetInput) { + // Delay field update to happen after modal closes + setTimeout(() => { + // Update the original input with the new parameter reference + const paramRef = '@' + parameterName; + targetInput.value = paramRef; + + // Trigger validation and parameter handling immediately + updateInputValidation(targetInput); + handleParameterInput(targetInput); + + // Trigger change event to ensure all handlers are called + const changeEvent = new Event('change', { bubbles: true }); + targetInput.dispatchEvent(changeEvent); + + // Also trigger input event + const inputEvent = new Event('input', { bubbles: true }); + targetInput.dispatchEvent(inputEvent); + + // Hide any dropdown that might have been recreated + hideDropdown(); + }, 50); + } + } + + // Store whether this was from autocomplete before clearing + const wasFromAutocomplete = autocompleteContext.active && parameterName === autocompleteContext.parameterName; + + // Clear context and global flag + autocompleteContext = { + input: null, + inputSelector: '', + inputAttr: '', + parameterName: '', + active: false + }; + + // Clear global flag after a delay to ensure all initialization calls are blocked + setTimeout(() => { + isCreatingParameterFromAutocomplete = false; + }, 500); + + // Return true if this was triggered from autocomplete (so modal should close) + return wasFromAutocomplete; + } + } + + // Not from autocomplete, return false (normal behavior) + return false; + }; + + // Expose the handleParameterCreated function globally + _self.handleParameterCreated = handleParameterCreated; } // Flag to prevent infinite loops during parameter reset @@ -373,6 +779,11 @@ MD.PropertyValidation = function() { this.clearAllInputValidation = clearAllInputValidation; this.resetParameterizedAttributes = resetParameterizedAttributes; this.hasParameterReferences = hasParameterReferences; + this.hideDropdown = function() { + if (typeof hideDropdown === 'function') { + hideDropdown(); + } + }; this.PARAM_REFERENCE_PATTERN = PARAM_REFERENCE_PATTERN; this.NUMERIC_PATTERN = NUMERIC_PATTERN; }; \ No newline at end of file diff --git a/src/js/modals.js b/src/js/modals.js index 9a535df3..8257b4ea 100644 --- a/src/js/modals.js +++ b/src/js/modals.js @@ -490,21 +490,69 @@ editor.modal = { editor.parametersManager.addParameter(name, type, defaultValue, description); } - hideParameterForm(); - renderParametersList(); + // Handle autocomplete parameter creation (if triggered from autocomplete) + let shouldCloseModal = false; + if (editor.propertyValidation && editor.propertyValidation.handleParameterCreated) { + shouldCloseModal = editor.propertyValidation.handleParameterCreated(name); + } + + if (shouldCloseModal) { + // Close the entire modal if this was triggered from autocomplete + // Use a small delay to ensure the callback completes first + setTimeout(() => { + // Reset modal state before closing + editingParameterId = null; + parametersContainer.style.display = 'block'; + parameterForm.style.display = 'none'; + parameterFormElement.reset(); + + editor.modal.parameters.close(); + + // Hide any autocomplete dropdown after modal closes + if (editor.propertyValidation && editor.propertyValidation.hideDropdown) { + setTimeout(() => { + editor.propertyValidation.hideDropdown(); + }, 50); + } + }, 10); + } else { + // Normal parameter manager flow + hideParameterForm(); + renderParametersList(); + } - // Update property validation autocomplete - if (editor.propertyValidation) { + // Update property validation autocomplete (but not if this was from autocomplete) + if (editor.propertyValidation && !shouldCloseModal) { editor.propertyValidation.addParameterAutocomplete(); } } catch (error) { alert('Error saving parameter: ' + error.message); + + // Clear autocomplete context on error too + if (editor.propertyValidation && editor.propertyValidation.handleParameterCreated) { + editor.propertyValidation.handleParameterCreated(''); // Empty name clears context + } } }); // Expose functions for use by renderParametersList this.editParameter = editParameter; this.showParameterForm = showParameterForm; + + // Reset modal to default state when opened + this.open = function() { + // Call the original open method + MD.Modal.prototype.open.call(this); + + // Reset to show parameters list + editingParameterId = null; + parametersContainer.style.display = 'block'; + parameterForm.style.display = 'none'; + parameterFormElement.reset(); + + // Render the parameters list + renderParametersList(); + }; } }), From 65b8e8dcf3264a07977bf72fe3db81d2363da731 Mon Sep 17 00:00:00 2001 From: smith120bh <42363318+smith120bh@users.noreply.github.com> Date: Thu, 24 Jul 2025 11:56:03 +1000 Subject: [PATCH 10/13] Fix bug where keyboard actions lost focus --- src/js/Modal.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/js/Modal.js b/src/js/Modal.js index 452d5969..235a60b3 100644 --- a/src/js/Modal.js +++ b/src/js/Modal.js @@ -23,6 +23,12 @@ MD.Modal = function(config){ } function close(){ + // Blur any focused elements within the modal to restore keyboard shortcuts + const focusedElement = el.querySelector(':focus'); + if (focusedElement) { + focusedElement.blur(); + } + el.classList.add("hidden"); } From 59f6b2c039c9b470e4ebf9716d189504be6637b7 Mon Sep 17 00:00:00 2001 From: smith120bh <42363318+smith120bh@users.noreply.github.com> Date: Thu, 24 Jul 2025 12:01:59 +1000 Subject: [PATCH 11/13] Block literal 'width' and 'height' parameter names. --- src/js/Parameters.js | 11 +++++++++-- src/js/modals.js | 6 +++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/js/Parameters.js b/src/js/Parameters.js index c035e43b..df11f0df 100644 --- a/src/js/Parameters.js +++ b/src/js/Parameters.js @@ -5,6 +5,9 @@ MD.Parameters = function(){ // followed by letters, numbers, or underscores const PARAM_NAME_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]*$/; + // Reserved parameter names that cannot be used (conflict with platform variables) + const RESERVED_NAMES = ['width', 'height']; + // Parameter types supported const PARAM_TYPES = { number: { label: 'Number', defaultValue: 0 }, @@ -32,6 +35,10 @@ MD.Parameters = function(){ // Validate parameter name function isValidParameterName(name) { if (!name || typeof name !== 'string') return false; + + // Check if name is reserved + if (RESERVED_NAMES.includes(name.toLowerCase())) return false; + return PARAM_NAME_REGEX.test(name); } @@ -51,7 +58,7 @@ MD.Parameters = function(){ // Add new parameter function addParameter(name, type, defaultValue, description = '') { if (!isValidParameterName(name)) { - throw new Error('Invalid parameter name. Must start with letter or underscore, followed by letters, numbers, or underscores.'); + throw new Error('Invalid parameter name. Must start with letter or underscore, followed by letters, numbers, or underscores. Cannot use reserved names: width, height.'); } if (parameterExists(name)) { @@ -79,7 +86,7 @@ MD.Parameters = function(){ // Update existing parameter function updateParameter(id, name, type, defaultValue, description = '') { if (!isValidParameterName(name)) { - throw new Error('Invalid parameter name. Must start with letter or underscore, followed by letters, numbers, or underscores.'); + throw new Error('Invalid parameter name. Must start with letter or underscore, followed by letters, numbers, or underscores. Cannot use reserved names: width, height.'); } if (parameterExists(name, id)) { diff --git a/src/js/modals.js b/src/js/modals.js index 8257b4ea..ec7bd061 100644 --- a/src/js/modals.js +++ b/src/js/modals.js @@ -115,9 +115,9 @@ editor.modal = {
    - - Must start with letter or underscore, followed by letters, numbers, or underscores + Must start with letter or underscore, followed by letters, numbers, or underscores. Cannot use: width, height
    @@ -633,7 +633,7 @@ editor.modal = { // Check if name is valid parameter name if (!editor.parametersManager.isValidParameterName(cloneName)) { - alert('Invalid parameter name. Use only letters, numbers, and underscores. Cannot start with a number.'); + alert('Invalid parameter name. Use only letters, numbers, and underscores. Cannot start with a number. Cannot use reserved names: width, height.'); return; } From d5cc625cae48b98ff138f27dc47ade09bb25a025 Mon Sep 17 00:00:00 2001 From: smith120bh <42363318+smith120bh@users.noreply.github.com> Date: Thu, 24 Jul 2025 12:37:06 +1000 Subject: [PATCH 12/13] Add ability to save/restore our parameters (in enhanced SVG format) --- src/js/sanitize.js | 1 + src/js/svgcanvas.js | 61 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/src/js/sanitize.js b/src/js/sanitize.js index 3e5777a2..5fcb06c0 100644 --- a/src/js/sanitize.js +++ b/src/js/sanitize.js @@ -34,6 +34,7 @@ nsMap_[xlinkns] = 'xlink'; nsMap_[xmlns] = 'xml'; nsMap_[xmlnsns] = 'xmlns'; nsMap_[se_ns] = 'se'; +nsMap_["http://methoddraw.com/parametric/1.0"] = 'md'; nsMap_[htmlns] = 'xhtml'; nsMap_[mathns] = 'mathml'; diff --git a/src/js/svgcanvas.js b/src/js/svgcanvas.js index 5099f14c..57d4f982 100644 --- a/src/js/svgcanvas.js +++ b/src/js/svgcanvas.js @@ -38,6 +38,7 @@ var svgns = "http://www.w3.org/2000/svg", xmlns = "http://www.w3.org/XML/1998/namespace", xmlnsns = "http://www.w3.org/2000/xmlns/", // see http://www.w3.org/TR/REC-xml-names/#xmlReserved se_ns = "http://svg-edit.googlecode.com", + md_ns = "http://methoddraw.com/parametric/1.0", // Method Draw parametric namespace htmlns = "http://www.w3.org/1999/xhtml", mathns = "http://www.w3.org/1998/Math/MathML"; @@ -5187,6 +5188,14 @@ this.svgToString = function(elem, indent) { }); }); + // Add Method Draw parametric namespace if we have parameters + if (typeof editor !== 'undefined' && editor.parametersManager) { + var parameters = editor.parametersManager.getParameters(); + if (parameters && Object.keys(parameters).length > 0) { + out.push(' xmlns:md="' + md_ns + '"'); + } + } + var i = attrs.length; var attr_names = ['width','height','xmlns','x','y','viewBox','id','overflow']; while (i--) { @@ -5254,6 +5263,26 @@ this.svgToString = function(elem, indent) { indent++; var bOneLine = false; + // Add parametric metadata for root element + if(elem.id === 'svgcontent' && typeof editor !== 'undefined' && editor.parametersManager) { + var parameters = editor.parametersManager.getParameters(); + if (parameters && Object.keys(parameters).length > 0) { + out.push("\n"); + for (var j=0; j"); + out.push("\n"); + for (var j=0; j<=indent; j++) out.push(" "); + out.push(""); + out.push(""); + out.push(""); + out.push("\n"); + for (var j=0; j"); + } + } + for (var i=0; i 0) { + try { + var parametersJson = metadataElem.text().trim(); + if (parametersJson) { + var parameters = JSON.parse(parametersJson); + if (typeof editor !== 'undefined' && editor.parametersManager && parameters) { + // Clear existing parameters and restore from SVG + state.set('canvasParameters', parameters); + console.log('Restored parametric data:', parameters); + + // Trigger parameter UI refresh if parameters modal is available + if (editor.modal && editor.modal.parameters && editor.modal.parameters.renderParametersList) { + setTimeout(function() { + editor.modal.parameters.renderParametersList(); + }, 100); + } + + // Restore parameter references on elements after a short delay to ensure DOM is ready + setTimeout(function() { + if (editor.propertyValidation && editor.propertyValidation.restoreParameterReferences) { + editor.propertyValidation.restoreParameterReferences(); + } + }, 200); + } + } + } catch (e) { + console.warn('Failed to parse parametric metadata:', e); + } + } + svgcontent.querySelectorAll('textPath').forEach(function(el){ const href = svgCanvas.getHref(el); if (!href) return; From 2252b32ea70a7fde7addf6cfe37c4371a0cab8e0 Mon Sep 17 00:00:00 2001 From: smith120bh <42363318+smith120bh@users.noreply.github.com> Date: Fri, 25 Jul 2025 14:41:08 +1000 Subject: [PATCH 13/13] Bug - clear parameters when creating a new doc --- src/js/editor.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/js/editor.js b/src/js/editor.js index 0026c9cf..6e88a175 100644 --- a/src/js/editor.js +++ b/src/js/editor.js @@ -20,6 +20,15 @@ MD.Editor = function(){ editor.panel.updateContextPanel(); editor.paintBox.fill.prep(); editor.paintBox.stroke.prep(); + + // Clear all parameters when creating a new document + state.set("canvasParameters", {}); + + // Clear parameter validation from inputs if available + if (editor.propertyValidation && editor.propertyValidation.clearAllInputValidation) { + editor.propertyValidation.clearAllInputValidation(); + } + svgCanvas.runExtensions('onNewDocument'); }); } @@ -470,6 +479,12 @@ MD.Editor = function(){ }); }); + // Remove metadata tags that contain parametric data + const metadataElements = svgDoc.querySelectorAll('metadata'); + metadataElements.forEach(metadata => { + metadata.parentNode.removeChild(metadata); + }); + // Get the modified SVG string const serializer = new XMLSerializer(); let modifiedSvgString = serializer.serializeToString(svgDoc.documentElement);