getState
- Gets the state that has the name pName.
+ Gets the state that has the name pName. The LAST defined state that has the passed name will be returned.
- The state to remove from this icon. pName should be not be used in tandem with this method of removing.
+ The icon data that is used to build this icon.
+
+
-
-
-
pName
-
-
-
-
-string
-
-
-
-
-
- The name of the state to remove. pState must be undefined to use this method for removing.
-
-
getIcon
- Gets the icon that has the name pName.
+ Gets the icon that has the name pName. The LAST defined icon that has the passed name will be returned.
@@ -598,7 +594,7 @@
* @private
* @type {number}
*/
- delay = 100;
+ delay;
/**
* The data URL of the sprite in this frame.
* @private
@@ -71,7 +71,6 @@
frame.mjs
parent;
/**
* The vyi this frame belongs to.
- *
* @private
* @type {VYI}
*/
@@ -88,24 +87,53 @@
frame.mjs
* @param {Icon} pParentIcon - The icon that created this frame.
* @private
*/
- constructor(pFrameData, pParentIcon) {
- this.vyi = pParentIcon.vyi;
- this.parent = pParentIcon;
+ constructor(pFrameData) {
this.parse(pFrameData);
}
/**
- * parses through the icon data and adds data to this frame.
+ * Parses through the icon data and adds data to this frame.
* @param {Array} pFrameData - The frame data that is used to build this frame.
* @private
*/
parse(pFrameData) {
+ if (!pFrameData) return;
// Loop through frame data and build frame.
const dataURL = pFrameData[0];
- const frameDelay = pFrameData[1] ? pFrameData[1] : this.parent.getDelay();
- // Set the data url
+ const frameDelay = pFrameData[1]
+ ? pFrameData[1]
+ : this.parent
+ ? this.parent.getDelay()
+ : null;
+
this.setDataURL(dataURL);
- // Set the frame delay
- this.setDelay(frameDelay);
+ // A frame delay may not be passed. So we await for a parent to default this frame's delay to.
+ if (frameDelay) {
+ this.setDelay(frameDelay);
+ }
+ }
+ /**
+ * Sets the parent for this frame.
+ * @private
+ * @param {Icon} pParent - The parent icon of this frame.
+ */
+ setParent(pParent) {
+ if (!pParent || this.parent) return;
+ if (pParent instanceof Icon) {
+ this.parent = pParent;
+ this.vyi = pParent.vyi;
+ // If no delay is found, get it from the parent.
+ if (!this.getDelay()) {
+ this.setDelay(this.parent.getDelay());
+ }
+ }
+ }
+ /**
+ * Removes the parent and vyi from this frame.
+ * @private
+ */
+ removeParent() {
+ this.parent = null;
+ this.vyi = null;
}
/**
* Sets the delay of this frame in ms.
@@ -113,12 +141,10 @@
getDataURL() {
return this.dataURL;
}
+ /**
+ * Gets the width of the frame.
+ * @returns {number} The width of the frame.
+ */
+ getWidth() {
+ if (!this.parent) return;
+ return this.parent.width;
+ }
+ /**
+ * Gets the height of the frame.
+ * @returns {number} The height of the frame.
+ */
+ getHeight() {
+ if (!this.parent) return;
+ return this.parent.height;
+ }
+ /**
+ * Gets the width and height of this frame and returns it.
+ * @returns {Object} An object with the width and height of this frame.
+ */
+ getSize() {
+ if (!this.parent) return;
+ return { width: this.parent.width, height: this.parent.height };
+ }
/**
* Gets the vyi this frame belongs to.
- *
* @returns {VYI} The vyi this frame belongs to.
*/
getVyi() {
@@ -161,7 +208,6 @@
frame.mjs
}
/**
* Gets the icon this frame belongs to.
- *
* @returns {Icon} The icon this frame belongs to.
*/
getParent() {
@@ -173,14 +219,10 @@
frame.mjs
* @returns {Array} An array of data related to this frame in the proper vyi format.
*/
export() {
- const frameData = [];
- // frame dataURL
- frameData[0] = this.getDataURL();
- // We do not have to store the delay if it is the default value of 100. This will save data.
+ const frameData = [this.getDataURL()];
const delayIsDefault = this.getDelay() === Frame.defaultDelay;
if (!delayIsDefault) {
- // frame delay
- frameData[1] = this.getDelay();
+ frameData[1] = this.getDelay() || Frame.defaultDelay;
}
return frameData;
}
@@ -196,7 +238,7 @@
*/
export class Icon {
/**
- * An array of Icon's that are state of this icon.
+ * A map of icons that are state of this icon.
* @private
- * @type {Array}
+ * @type {Map}
*/
- states = [];
+ states = new Map();
/**
- * An arary of Frame's that are the frames of this icon.
+ * A map of frames that are the frames of this icon.
* @private
- * @type {Array}
+ * @type {Map}
*/
- frames = [];
+ frames = new Map();
/**
* The width of this icon. All states and frames of this icon must match this size.
* @private
@@ -102,62 +102,82 @@
icon.mjs
*/
vyi;
/**
- * A random unique ID attached to each icon to distinguish them from others in the event another icon shares the same name.
+ * A random unique Id attached to each icon to distinguish them from others in the event another icon shares the same name.
*
* @private
* @type {string}
*/
id;
+ /**
+ * An set of used Ids to prevent collusion between duplicate named icons.
+ *
+ * @private
+ * @type {Set}
+ */
+ static reservedIds = new Set();
/**
* Generates a UUID (Universally Unique Identifier) version 4.
*
* @private
- * @param {VYI} pVYI - The vyi that will reserve this ID.
* @returns {string} The generated UUID.
*/
- static generateID(pVYI) {
- const genID = () => {
+ static generateId() {
+ const genId = () => {
return Math.floor(Math.random() * 0xFFFFFFFF).toString(16).padStart(8, '0');
}
- // Generate a random number in the range of 0 to 0xFFFFFFFF (8 hex digits) and convert to hex
- let id = genID();
- while (pVYI.reservedIDs.includes(id)) {
- id = genID();
+
+ let id = genId();
+ while (this.reservedIds.has(id)) {
+ id = genId();
}
- pVYI.reservedIDs.push(id);
+ this.reservedIds.add(id);
return id;
}
/**
* Creates this icon instance.
- * @param {Object} pIconData - The icon data that is used to build this icon.
- * @param {VYI} pVYI - The vyi this icon | state belongs to.
+ * @param {Array} pIconData - The icon data that is used to build this icon.
* @private
*/
- constructor(pIconData, pVYI) {
- this.vyi = pVYI;
- this.assignID(pVYI);
+ constructor(pIconData) {
this.parse(pIconData);
+ this.assignId();
}
/**
- * Assigns an ID to this icon.
+ * Sets the vyi of this icon.
*
- * @param {VYI} pVYI - The vyi that holds this ID.
+ * @private
+ * @param {VYI} pVyi - The vyi that owns this icon.
+ */
+ setVyi(pVyi) {
+ if (!pVyi) return;
+
+ if (pVyi instanceof VYI) {
+ this.vyi = pVyi;
+ }
+ }
+ /**
+ * Removes the vyi from this icon.
+ * @private
+ */
+ removeVyi() {
+ this.vyi = null;
+ }
+ /**
+ * Assigns an Id to this icon.
* @private
*/
- assignID(pVYI) {
- this.id = Icon.generateID(pVYI);
+ assignId() {
+ this.id = Icon.generateId();
}
/**
* Gets the id of this icon.
- *
* @returns {string} The id of this icon.
*/
- getID() {
+ getId() {
return this.id;
}
/**
* Gets the vyi this icon belongs to.
- *
* @returns {VYI} The vyi this icon belongs to.
*/
getVyi() {
@@ -165,19 +185,19 @@
icon.mjs
}
/**
* Gets the icon this state belongs to. If this icon is not a state, it will return undefined.
- *
* @returns {Icon|undefined} The icon this state belongs to.
*/
getParent() {
return this.parent;
}
/**
- * parses through the icon data and adds data to this icon.
- * @param {Object} pIconData - The icon data that is used to build this icon.
- * @private
+ * Parses through the icon data and adds data to this icon.
+ * @param {Array} pIconData - The icon data that is used to build this icon.
*/
parse(pIconData) {
- // Loop through pIconData and create this icon
+ if (!pIconData) return;
+
+ // Loop through the icon data and create this icon
const iconName = pIconData[0];
const iconWidth = pIconData[1];
const iconHeight = pIconData[2];
@@ -236,13 +256,27 @@
icon.mjs
* @returns {self} This icon instance.
*/
setSize(pWidth, pHeight) {
- if (typeof(pWidth) === 'number') {
+ if (typeof pWidth === 'number') {
this.width = pWidth;
}
- if (typeof(pHeight) === 'number') {
+ if (typeof pHeight === 'number') {
this.height = pHeight;
}
}
+ /**
+ * Gets the width of the icon.
+ * @returns {number} The width of the icon.
+ */
+ getWidth() {
+ return this.width;
+ }
+ /**
+ * Gets the height of the icon.
+ * @returns {number} The height of the icon.
+ */
+ getHeight() {
+ return this.height;
+ }
/**
* Gets the width and height of this icon and returns it.
* @returns {Object} An object with the width and height of this icon.
@@ -256,12 +290,10 @@
* @returns {self} This icon instance.
*/
rename(pName) {
- if (pName || pName === '') {
- if (typeof(pName) === 'string') {
- this.name = pName;
- } else {
- VYI.logger.prefix('VYI-Module').error('Invalid type for pName!');
- }
+ if (typeof pName === 'string') {
+ this.name = pName;
+ } else {
+ VYI.logger.prefix('Vyi-module').error('Invalid type for pName!');
}
return this;
}
@@ -322,111 +350,70 @@
icon.mjs
* @returns {self} This icon instance.
*/
setAllFrameDelays(pDelay) {
- if (pDelay) {
- if (typeof(pDelay) === 'number') {
- this.setDelay(pDelay);
- this.frames.forEach((pFrame) => {
- pFrame.setDelay(pDelay);
- });
- } else {
- VYI.logger.prefix('VYI-Module').error('Invalid type for pDelay!');
- }
+ if (typeof pDelay === 'number') {
+ this.setDelay(pDelay);
+ this.getFrames().forEach((pFrame) => pFrame.setDelay(pDelay));
+ } else {
+ VYI.logger.prefix('Vyi-module').error('Invalid type for pDelay!');
}
return this;
}
/**
- * Adds this icon data as a state. A state is also an icon.
- * @param {Object} pIconData - The data used to create this state icon.
- * @returns {Icon|undefined} The state that was added or undefined.
+ * Adds a new frame to this icon.
+ * @param {Frame|Array} pFrameData - The frame data to give this frame.
+ * @returns {Frame|undefined} The frame that was added or undefined.
*/
- addState(pIconData) {
- if (pIconData instanceof Object) {
- const state = new Icon(pIconData, this.vyi);
- state.parent = this;
- this.states.push(state);
- return state;
+ addFrame(pFrameData) {
+ if (!pFrameData) {
+ VYI.logger.prefix('Vyi-module').error('No frame data passed!');
+ return;
}
- }
- /**
- * Removes the state passed or the state with the name pName.
- * @param {Icon} pState - The state to remove from this icon. pName should be not be used in tandem with this method of removing.
- * @param {string} pName - The name of the state to remove. pState must be undefined to use this method for removing.
- * @returns {self} This icon instance.
- */
- removeState(pState, pName) {
- // The index used to remove this frame.
- let index;
- // Remove via reference to state.
- if (this.states.includes(pState)) {
- index = this.states.indexOf(pState);
- // Remove via reference to name
- } else if (typeof(pName) === 'string') {
- const state = this.getState(pName);
- if (state) {
- index = this.states.indexOf(state);
- }
- } else {
- VYI.logger.prefix('VYI-Module').error('Failed to remove state!');
- return this;
+
+ if (!(pFrameData instanceof Frame) && !Array.isArray(pFrameData)) {
+ VYI.logger.prefix('Vyi-module').error('Invalid frame data type passed!');
+ return;
}
- if (typeof(index) === 'number') {
- // Remove the state
- this.states.splice(index, 1);
+
+ // Create the icon instance
+ const frame = pFrameData instanceof Frame
+ ? pFrameData
+ : new Frame(pFrameData);
+
+ if (frame.getWidth() !== frame.getWidth() || frame.getHeight() !== frame.getHeight()) {
+ VYI.logger.prefix('Vyi-module').error('Frame dimensions do not match parent!');
+ return;
}
- return this;
+
+ frame.setParent(this);
+ // We store this frame under its index in the Map 0-1
+ this.frames.set(this.frames.size, frame);
+ this.indexFrames();
+
+ return frame;
}
/**
- * Adds a new frame to this icon.
- * @param {Array} pFrameData - The frame data to give this frame.
- * @returns {Frame|undefined} The frame that was added or undefined.
+ * Removes the frame passed.
+ * @param {Frame} pFrame - The frame to remove from this icon.
+ * @returns {self} This icon instance.
*/
- addFrame(pFrameData) {
- if (pFrameData) {
- if (Array.isArray(pFrameData)) {
- const frame = new Frame(pFrameData, this);
- // Add the frame to the frames array.
- this.frames.push(frame);
- // Re-index frames after a change
+ removeFrame(pFrame) {
+ if (!pFrame) return;
+ if (pFrame instanceof Frame) {
+ if (this.frames.delete(pFrame.index)) {
+ pFrame.removeParent();
this.indexFrames();
- return frame;
- } else {
- VYI.logger.prefix('VYI-Module').error('Invalid frame data passed!');
}
- } else {
- VYI.logger.prefix('VYI-Module').error('No frame data passed!');
}
+ return this;
}
/**
- * Removes the frame passed or the frame that exists at pIndex.
- * @param {Frame} pFrame - The frame to remove from this icon. pIndex should be not be used in tandem with this method of removing.
- * @param {number} pIndex - The index of the frame to remove. pFrame must be undefined to use this method for removing.
+ * Removes the frame via it's index.
+ * @param {number} pIndex - The index of the frame to remove.
* @returns {self} This icon instance.
*/
- removeFrame(pFrame, pIndex) {
- // The index used to remove this frame.
- let index;
- // Remove via reference to frame.
- if (this.frames.includes(pFrame)) {
- index = this.frames.indexOf(pFrame);
- // Remove via index passed.
- } else if (pIndex || pIndex === 0) {
- if (typeof(pIndex) === 'number') {
- const frame = this.getFrame(pIndex);
- if (frame) {
- index = pIndex;
- }
- } else {
- VYI.logger.prefix('VYI-Module').error('Invalid pIndex type!');
- }
- } else {
- VYI.logger.prefix('VYI-Module').error('Failed to remove frame!');
- }
- if (typeof(index) === 'number') {
- // Remove the frame
- this.frames.splice(index, 1);
- // Re-index frames after a change
- this.indexFrames();
- }
+ removeFrameByIndex(pIndex) {
+ const frame = this.getFrame(pIndex);
+ this.removeFrame(frame);
return this;
}
/**
@@ -434,8 +421,7 @@
* Reorders the frame in the animation. The index of the passed frame will be swapped with the frame at pIndex.
* The "first" frame of the animation is technically this icon's dataURL. So if you are aiming to change the order of this icon and convert it into a frame.
* pCurrentIndex must be set to -1 to match this icon.
+ *
* @param {number} pCurrentIndex - The current index of the frame.
* @param {number} pIndex - The index the frame will be moving to.
* @returns {self} This icon instance.
*/
reorderFrame(pCurrentIndex, pIndex) {
- if (typeof(pCurrentIndex) === 'number' && typeof(pIndex) === 'number') {
- let frameAtIndex;
- let currentFrame;
- // We check if the current index is -1, if it is then it means we want to treat this icon as a frame. As the icon data and delay of this icon serves
- // as the frame 0.
- if (pCurrentIndex === -1) {
- currentFrame = this;
- // Otherwise if the index passed can be found in the frames array, then we use that frame.
- } else if (this.frames[pCurrentIndex]) {
- currentFrame = this.frames[pCurrentIndex];
- }
-
- // We get the frame at the specified index.
- if (this.frames[pIndex]) {
- frameAtIndex = this.frames[pIndex];
- }
+ if (typeof pCurrentIndex === 'number' && typeof pIndex === 'number') {
+ let frameAtIndex = this.getFrame(pIndex);
+ let currentFrame = pCurrentIndex === -1
+ ? this
+ : this.getFrame(pCurrentIndex)
// If both frames can be found, we can swap their data.
if (currentFrame && frameAtIndex) {
@@ -477,14 +453,15 @@
icon.mjs
// Swap data from frame
currentFrame.setDataURL(frameAtIndexDataURL);
currentFrame.setDelay(frameAtIndexDelay);
+
// Swap data to frame
frameAtIndex.setDataURL(currentFrameDataURL);
frameAtIndex.setDelay(currentFrameDelay);
} else {
- VYI.logger.prefix('VYI-Module').error('There was no frame found at pCurrentIndex, or there was no frame found at pIndex!');
+ VYI.logger.prefix('Vyi-module').error('There was no frame found at pCurrentIndex, or there was no frame found at pIndex!');
}
} else {
- VYI.logger.prefix('VYI-Module').error('Invalid type used!');
+ VYI.logger.prefix('Vyi-module').error('Invalid type used!');
}
return this;
}
@@ -496,18 +473,14 @@
icon.mjs
* @returns {Frame|undefined} The frame found at pIndex.
*/
getFrame(pIndex) {
- if (typeof(pIndex) === 'number') {
- return this.frames[pIndex];
- } else {
- VYI.logger.prefix('VYI-Module').error('Invalid type used!');
- }
+ return this.frames.get(pIndex);
}
/**
* Returns an array of all the frames this icons has.
* @returns {Array} An array of frames this icon has.
*/
getFrames() {
- return [ ...this.frames ];
+ return Array.from(this.frames.values());
}
/**
* Gets all the frames belonging to this icon.
@@ -515,36 +488,130 @@
icon.mjs
* @returns {Array} An array containing the frame data of all frames.
*/
getFramesData() {
- const frameDataArray = [];
- this.frames.forEach((pFrame) => {
- frameDataArray.push(pFrame.export());
- });
+ const frameDataArray = this.getFrames().map((pFrame) => pFrame.export());
return frameDataArray;
}
/**
- * Gets the state that has the name pName.
+ * Adds this icon data as a state. A state is also an icon.
+ * @param {Array} pIconData - The data used to create this state icon.
+ * @returns {Icon|undefined} The state that was added or undefined.
+ */
+ addState(pIconData) {
+ if (!pIconData) {
+ VYI.logger.prefix('Vyi-module').error('No icon data passed!');
+ return;
+ }
+
+ if (!(pIconData instanceof Icon) && !Array.isArray(pIconData)) {
+ VYI.logger.prefix('Vyi-module').error('Invalid icon data type passed!');
+ return;
+ }
+
+ // Create the icon instance
+ const state = pIconData instanceof Icon
+ ? pIconData
+ : new Icon(pIconData);
+
+ if (state.getWidth() !== this.getWidth() || state.getHeight() !== this.getHeight()) {
+ VYI.logger.prefix('Vyi-module').error('State dimensions do not match parent!');
+ return;
+ }
+
+ state.setParent(this);
+ state.setVyi(this.vyi);
+ this.states.set(state.id, state);
+
+ return state;
+ }
+ /**
+ * Sets the parent for this state.
+ * @private
+ * @param {Icon} pParent - The parent icon of this state.
+ */
+ setParent(pParent) {
+ if (!pParent || this.parent) return;
+ if (pParent instanceof Icon) {
+ this.parent = pParent;
+ }
+ }
+ /**
+ * Removes the parent and vyi from this state.
+ * @private
+ */
+ removeParent() {
+ if (this.parent) {
+ this.parent = null;
+ this.vyi = null;
+ }
+ }
+ /**
+ * Removes the state passed.
+ * @param {Icon} pState - The state to remove from this icon.
+ * @returns {self} This icon instance.
+ */
+ removeState(pState) {
+ if (pState instanceof Icon) {
+ if (this.icons.delete(pState.id)) {;
+ pState.removeParent();
+ }
+ }
+ return this;
+ }
+ /**
+ * Removes the state via it's name. The LAST defined icon that has the passed name will be removed. As names are not unique.
+ * @param {string} pName - The name to use to find the state.
+ * @returns {self} This icon instance.
+ */
+ removeStateByName(pName) {
+ const state = this.getState(pName);
+ this.removeState(state);
+ return this;
+ }
+ /**
+ * Removes the state via it's id.
+ * @param {string} pName - The id to use to find the state.
+ * @returns {self} This icon instance.
+ */
+ removeStateById(pId) {
+ const state = this.getStateById(pId);
+ this.removeState(state);
+ return this;
+ }
+ /**
+ * Gets the state that has the name pName. The LAST defined state that has the passed name will be returned.
* @param {string} pName - The name of the state to get.
* @returns {Icon} The state that has the name of pName.
*/
getState(pName) {
- if (typeof(pName) === 'string') {
- for (let i = this.states.length - 1; i >= 0; i--) {
- const icon = this.states[i];
+ if (typeof pName === 'string') {
+ const states = this.getStates();
+ for (let i = states.length - 1; i >= 0; i--) {
+ const state = states[i];
// If the icon has the same name, return that icon
- if (icon.getName() === pName) {
- return icon;
+ if (state.getName() === pName) {
+ return state;
}
}
} else {
- VYI.logger.prefix('VYI-module').error('Invalid name type used!');
+ VYI.logger.prefix('Vyi-module').error('Invalid name type used!');
}
}
+ /**
+ * Gets the state by the id provided.
+ * @private
+ * @param {string} pId - The id of the state.
+ * @returns {Icon} The state that has the id that was passed.
+ */
+ getStateById(pId) {
+ if (!pId) return;
+ return this.states.get(pId);
+ }
/**
* Returns an array of all the states this icons has.
* @returns {Array} An array of states this icon has.
*/
getStates() {
- return [ ...this.states ];
+ return Array.from(this.states.values());
}
/**
* Gets all the states belonging to this icon.
@@ -552,11 +619,7 @@
icon.mjs
* @returns {Array} An array containing the state data of all frames.
*/
getStatesData() {
- const stateDataArray = [];
- // Loop state array to export relevant information.
- this.states.forEach((pState) => {
- stateDataArray.push(pState.exportAsState());
- });
+ const stateDataArray = this.getStates().map((pState) => pState.exportAsState());
return stateDataArray;
}
/**
@@ -573,12 +636,7 @@
// frame array
iconData[5] = this.getFramesData();
- // this is actually an optional data entry into the vyi, only used if states actually exist on this icon.
- // this will save data
- if (this.states.length) {
- // states array
+ // This is actually an optional data entry into the vyi, only used if states actually exist on this icon.
+ if (this.states.size > 0) {
iconData[6] = this.getStatesData();
}
return iconData;
@@ -623,7 +677,7 @@
import { Logger } from './vendor/logger.mjs';
import { Icon } from './icon.mjs';
+import { Frame } from './frame.mjs';
import pako from './vendor/pako.esm.mjs';
/**
* @public
*/
-export class VYI {
+class VYI {
/**
* The version of the module.
*/
@@ -57,11 +58,11 @@
vyi.mjs
*/
static logger = new Logger();
/**
- * An array of icons that belong to this VYI
+ * A map of icons that belong to this VYI
* @private
- * @type {Array}
+ * @type {Map}
*/
- icons = [];
+ icons = new Map();
/**
* The name of this vyi.
* @private
@@ -73,20 +74,13 @@
vyi.mjs
* @private
* @type {number}
*/
- formatVersion;
- /**
- * An array of used IDs to prevent collusion between duplicate named icons.
- *
- * @private
- * @type {Array}
- */
- reservedIDs = [];
+ formatVersion = 1;
/**
* Initializes this module with the information from the VYI passed.
* @param {Object} pVyiData - A JSON / Javascript object containing the vyi information.this.ogger
*/
constructor(pVyiData) {
- VYI.logger.registerType('VYI-Module', '#ff6600');
+ VYI.logger.registerType('Vyi-module', '#ff6600');
this.parse(pVyiData);
}
/**
@@ -104,31 +98,28 @@
vyi.mjs
try {
let vyi;
- if (typeof(pVyiData) === 'string') {
- const isNodeEnv = typeof(window) === 'undefined';
+ if (typeof pVyiData === 'string') {
+ const isNodeEnv = typeof window === 'undefined';
vyi = isNodeEnv
? await this.readFileAndGetVYI(pVyiData)
: await this.fetchAndParseJSON(pVyiData);
- } else if (typeof(pVyiData) === 'object' && !Array.isArray(pVyiData) && !(pVyiData instanceof ArrayBuffer || pVyiData instanceof Uint8Array)) {
+ } else if (pVyiData instanceof VYI) {
+ vyi = pVyiData.export();
+ } else if (pVyiData instanceof Object && !Array.isArray(pVyiData) && !(pVyiData instanceof ArrayBuffer || pVyiData instanceof Uint8Array)) {
vyi = pVyiData;
} else if (pVyiData instanceof ArrayBuffer || pVyiData instanceof Uint8Array) {
vyi = await this.handleBinaryData(pVyiData);
} else {
- throw new Error('Invalid input type provided.');
+ throw new Error('Error processing: Invalid input type provided.');
}
- // If there is valid vyi data, process it
- if (vyi) {
- this.processVyiData(vyi);
- }
+ this.processVyiData(vyi);
} catch (pError) {
- VYI.logger.prefix('VYI-module').error(`Error processing VYI data: ${pError.message}`);
- throw pError; // Rethrow the error for further handling
+ VYI.logger.prefix('Vyi-module').error(`${pError.message}`);
}
}
/**
* Reads a file and returns the vyi from it.
- *
* @private
* @param {string} - The URL to read the data from.
* @returns {Promise<Object>} - The vyi from the file.
@@ -140,7 +131,6 @@
vyi.mjs
}
/**
* Fetches data from a URL and parses it as JSON.
- *
* @private
* @param {string} pURL - The URL to fetch the data from.
* @returns {Promise<Object>} - A promise that resolves to the parsed JSON data.
@@ -156,7 +146,6 @@
vyi.mjs
}
/**
* Handles binary data (ArrayBuffer or Uint8Array). Attempts to inflate or decode it.
- *
* @private
* @param {ArrayBuffer|Uint8Array} pBinaryData - The binary data to process.
* @returns {Object|string|null} - The parsed JSON object or decoded string, or null if it fails.
@@ -176,7 +165,6 @@
vyi.mjs
}
/**
* Processes the parsed VYI data and adds icons to the VYI module instance.
- *
* @private
* @param {Object} pVyi - The parsed VYI data.
*/
@@ -189,104 +177,114 @@
vyi.mjs
this.addIcon(pIconData, this);
});
} else {
- VYI.logger.prefix('VYI-module').error('Invalid .vyi file! Cannot parse icons.');
+ VYI.logger.prefix('Vyi-module').error('Invalid .vyi file! Cannot parse icons.');
}
}
/**
* Adds an icon to this VYI.
- * @param {Object} pIconData - The icon data to use.
+ * @param {Icon|Array} pIconData - The icon data to use.
* @returns {Icon|undefined} - The Icon added or undefined.
*/
addIcon(pIconData) {
- if (pIconData) {
- if (pIconData instanceof Object) {
- const icon = new Icon(pIconData, this);
- // Add the icon to the icons array.
- this.icons.push(icon);
- return icon;
- } else {
- VYI.logger.prefix('VYI-module').error('Invalid icon data type passed!');
- }
- } else {
- VYI.logger.prefix('VYI-module').error('No icon data passed!');
+ if (!pIconData) {
+ VYI.logger.prefix('Vyi-module').error('No icon data passed!');
+ return;
}
+
+ if (!(pIconData instanceof Icon) && !Array.isArray(pIconData)) {
+ VYI.logger.prefix('Vyi-module').error('Invalid icon data type passed!');
+ return;
+ }
+
+ // Create the icon instance
+ const icon = pIconData instanceof Icon
+ ? pIconData
+ : new Icon(pIconData);
+
+ icon.setVyi(this);
+ this.icons.set(icon.id, icon);
+ return icon;
}
/**
- * Removes the icon passed or the icon with the name pName.
- * @param {Icon} pIcon - The state to remove from this icon. pName should be not be used in tandem with this method of removing.
- * @param {string} pName - The name of the icon to remove. pIcon must be undefined to use this method for removing.
+ * Removes the icon passed.
+ * @param {Icon} pIcon - The icon to remove from this vyi.
*/
- removeIcon(pIcon, pName) {
- const icon = pIcon || this.getIcon(pName);
- if (icon) {
- if (this.icons.includes(icon)) {
- this.icons.splice(this.icons.indexOf(icon), 1);
+ removeIcon(pIcon) {
+ if (!pIcon) return;
+ if (pIcon instanceof Icon) {
+ if (this.icons.delete(pIcon.id)) {;
+ pIcon.removeVyi();
}
}
}
+ /**
+ * Removes the icon via it's name. The LAST defined icon that has the passed name will be removed. As names are not unique.
+ * @param {string} pName - The name to use to find the icon.
+ */
+ removeIconByName(pName) {
+ const icon = this.getIcon(pName);
+ this.removeIcon(icon);
+ }
+ /**
+ * Removes the icon via it's id.
+ * @param {string} pName - The id to use to find the icon.
+ */
+ removeIconById(pId) {
+ const icon = this.getIconById(pId);
+ this.removeIcon(icon);
+ }
/**
* Returns all the icon names in this vyi.
* @returns {Array} An array of icon names in this vyi.
*/
getIconNames() {
- // Array to store the icon names.
- const iconNames = [];
- this.icons.forEach((pIcon) => {
- iconNames.push(pIcon.name);
- });
+ const iconNames = this.getIcons().map((pIcon) => pIcon.name);
return iconNames;
}
/**
- * Gets the icon that has the name pName.
+ * Gets the icon that has the name pName. The LAST defined icon that has the passed name will be returned.
* @param {string} pName - The name of the icon to get.
* @returns {Icon|undefined} The icon that has the name pName or undefined.
*/
getIcon(pName) {
- if (typeof(pName) === 'string') {
- for (let i = this.icons.length - 1; i >= 0; i--) {
- const icon = this.icons[i];
+ if (typeof pName === 'string') {
+ const icons = this.getIcons();
+ for (let i = icons.length - 1; i >= 0; i--) {
+ const icon = icons[i];
// If the icon has the same name, return that icon
if (icon.getName() === pName) {
return icon;
}
}
} else {
- VYI.logger.prefix('VYI-module').error('Invalid name type used!');
+ VYI.logger.prefix('Vyi-module').error('Invalid name type used!');
}
}
/**
* Gets an icon by the id provided.
- *
- * @private
- * @param {string} pID - The id of the icon.
+ * @param {string} pId - The id of the icon.
* @returns {Icon} The icon that has the id that was passed.
*/
- getIconByID(pID) {
- if (!pID) return;
- for (const icon of this.icons) {
- for (const state of icon.states) {
- if (state.id === pID) return state;
- }
- if (icon.id === pID) return icon;
- }
+ getIconById(pId) {
+ if (!pId) return;
+ return this.icons.get(pId);
}
/**
* Gets all the icons in this vyi.
* @returns {Array<Icon>}
*/
getIcons() {
- return [...this.icons];
+ return Array.from(this.icons.values());
}
/**
* Renames the vyi.
- *
* @param {string} pName - The name to give this vyi.
*/
rename(pName) {
- if (typeof(pName) === 'string') {
+ if (typeof pName === 'string') {
this.name = pName;
} else {
- VYI.logger.prefix('VYI-module').error('Invalid name type used!');
+ VYI.logger.prefix('Vyi-module').error('Invalid name type used!');
}
}
/**
@@ -302,17 +300,13 @@
vyi.mjs
*/
export() {
const vyi = {};
- // Set version
vyi.v = this.formatVersion;
- // Set the icons array
- vyi.i = [];
- this.icons.forEach((pIcon) => {
- // Push the icon data to the vyi export object.
- vyi.i.push(pIcon.export());
- });
+ vyi.i = this.getIcons().map((pIcon) => pIcon.export());
return vyi;
}
-}
diff --git a/package.json b/package.json
index f5bf818..2540ce9 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "vyi",
- "version": "1.4.0",
+ "version": "1.5.0",
"description": "A lightweight module to read / manage .vyi files created in the Vylocity Game Engine.",
"main": "src/vyi.mjs",
"scripts": {
diff --git a/src/frame.mjs b/src/frame.mjs
index 977fe83..bbd5a3f 100644
--- a/src/frame.mjs
+++ b/src/frame.mjs
@@ -1,4 +1,4 @@
-import { VYI } from './vyi.mjs';
+import { VYI, Icon } from './vyi.mjs';
/**
* @public
@@ -9,7 +9,7 @@ export class Frame {
* @private
* @type {number}
*/
- delay = 100;
+ delay;
/**
* The data URL of the sprite in this frame.
* @private
@@ -30,7 +30,6 @@ export class Frame {
parent;
/**
* The vyi this frame belongs to.
- *
* @private
* @type {VYI}
*/
@@ -47,24 +46,53 @@ export class Frame {
* @param {Icon} pParentIcon - The icon that created this frame.
* @private
*/
- constructor(pFrameData, pParentIcon) {
- this.vyi = pParentIcon.vyi;
- this.parent = pParentIcon;
+ constructor(pFrameData) {
this.parse(pFrameData);
}
/**
- * parses through the icon data and adds data to this frame.
+ * Parses through the icon data and adds data to this frame.
* @param {Array} pFrameData - The frame data that is used to build this frame.
* @private
*/
parse(pFrameData) {
+ if (!pFrameData) return;
// Loop through frame data and build frame.
const dataURL = pFrameData[0];
- const frameDelay = pFrameData[1] ? pFrameData[1] : this.parent.getDelay();
- // Set the data url
+ const frameDelay = pFrameData[1]
+ ? pFrameData[1]
+ : this.parent
+ ? this.parent.getDelay()
+ : null;
+
this.setDataURL(dataURL);
- // Set the frame delay
- this.setDelay(frameDelay);
+ // A frame delay may not be passed. So we await for a parent to default this frame's delay to.
+ if (frameDelay) {
+ this.setDelay(frameDelay);
+ }
+ }
+ /**
+ * Sets the parent for this frame.
+ * @private
+ * @param {Icon} pParent - The parent icon of this frame.
+ */
+ setParent(pParent) {
+ if (!pParent || this.parent) return;
+ if (pParent instanceof Icon) {
+ this.parent = pParent;
+ this.vyi = pParent.vyi;
+ // If no delay is found, get it from the parent.
+ if (!this.getDelay()) {
+ this.setDelay(this.parent.getDelay());
+ }
+ }
+ }
+ /**
+ * Removes the parent and vyi from this frame.
+ * @private
+ */
+ removeParent() {
+ this.parent = null;
+ this.vyi = null;
}
/**
* Sets the delay of this frame in ms.
@@ -72,12 +100,10 @@ export class Frame {
* @returns {self} This frame instance.
*/
setDelay(pDelay) {
- if (pDelay) {
- if (typeof(pDelay) === 'number') {
- this.delay = pDelay;
- } else {
- VYI.logger.prefix('VYI-module').error('Invalid delay type!');
- }
+ if (typeof pDelay === 'number') {
+ this.delay = pDelay;
+ } else {
+ VYI.logger.prefix('Vyi-module').error('Invalid delay type!');
}
return this;
}
@@ -94,12 +120,10 @@ export class Frame {
* @returns {self} This frame instance.
*/
setDataURL(pDataURL) {
- if (pDataURL) {
- if (typeof(pDataURL) === 'string') {
- this.dataURL = pDataURL;
- } else {
- VYI.logger.prefix('VYI-module').error('Invalid data url type!');
- }
+ if (typeof pDataURL === 'string') {
+ this.dataURL = pDataURL;
+ } else {
+ VYI.logger.prefix('Vyi-module').error('Invalid data url type!');
}
return this;
}
@@ -110,9 +134,32 @@ export class Frame {
getDataURL() {
return this.dataURL;
}
+ /**
+ * Gets the width of the frame.
+ * @returns {number} The width of the frame.
+ */
+ getWidth() {
+ if (!this.parent) return;
+ return this.parent.width;
+ }
+ /**
+ * Gets the height of the frame.
+ * @returns {number} The height of the frame.
+ */
+ getHeight() {
+ if (!this.parent) return;
+ return this.parent.height;
+ }
+ /**
+ * Gets the width and height of this frame and returns it.
+ * @returns {Object} An object with the width and height of this frame.
+ */
+ getSize() {
+ if (!this.parent) return;
+ return { width: this.parent.width, height: this.parent.height };
+ }
/**
* Gets the vyi this frame belongs to.
- *
* @returns {VYI} The vyi this frame belongs to.
*/
getVyi() {
@@ -120,7 +167,6 @@ export class Frame {
}
/**
* Gets the icon this frame belongs to.
- *
* @returns {Icon} The icon this frame belongs to.
*/
getParent() {
@@ -132,14 +178,10 @@ export class Frame {
* @returns {Array} An array of data related to this frame in the proper vyi format.
*/
export() {
- const frameData = [];
- // frame dataURL
- frameData[0] = this.getDataURL();
- // We do not have to store the delay if it is the default value of 100. This will save data.
+ const frameData = [this.getDataURL()];
const delayIsDefault = this.getDelay() === Frame.defaultDelay;
if (!delayIsDefault) {
- // frame delay
- frameData[1] = this.getDelay();
+ frameData[1] = this.getDelay() || Frame.defaultDelay;
}
return frameData;
}
diff --git a/src/icon.mjs b/src/icon.mjs
index 4c3d603..4c9e8c7 100644
--- a/src/icon.mjs
+++ b/src/icon.mjs
@@ -6,17 +6,17 @@ import { Frame } from './frame.mjs';
*/
export class Icon {
/**
- * An array of Icon's that are state of this icon.
+ * A map of icons that are state of this icon.
* @private
- * @type {Array}
+ * @type {Map}
*/
- states = [];
+ states = new Map();
/**
- * An arary of Frame's that are the frames of this icon.
+ * A map of frames that are the frames of this icon.
* @private
- * @type {Array}
+ * @type {Map}
*/
- frames = [];
+ frames = new Map();
/**
* The width of this icon. All states and frames of this icon must match this size.
* @private
@@ -61,62 +61,82 @@ export class Icon {
*/
vyi;
/**
- * A random unique ID attached to each icon to distinguish them from others in the event another icon shares the same name.
+ * A random unique Id attached to each icon to distinguish them from others in the event another icon shares the same name.
*
* @private
* @type {string}
*/
id;
+ /**
+ * An set of used Ids to prevent collusion between duplicate named icons.
+ *
+ * @private
+ * @type {Set}
+ */
+ static reservedIds = new Set();
/**
* Generates a UUID (Universally Unique Identifier) version 4.
*
* @private
- * @param {VYI} pVYI - The vyi that will reserve this ID.
* @returns {string} The generated UUID.
*/
- static generateID(pVYI) {
- const genID = () => {
+ static generateId() {
+ const genId = () => {
return Math.floor(Math.random() * 0xFFFFFFFF).toString(16).padStart(8, '0');
}
- // Generate a random number in the range of 0 to 0xFFFFFFFF (8 hex digits) and convert to hex
- let id = genID();
- while (pVYI.reservedIDs.includes(id)) {
- id = genID();
+
+ let id = genId();
+ while (this.reservedIds.has(id)) {
+ id = genId();
}
- pVYI.reservedIDs.push(id);
+ this.reservedIds.add(id);
return id;
}
/**
* Creates this icon instance.
- * @param {Object} pIconData - The icon data that is used to build this icon.
- * @param {VYI} pVYI - The vyi this icon | state belongs to.
+ * @param {Array} pIconData - The icon data that is used to build this icon.
* @private
*/
- constructor(pIconData, pVYI) {
- this.vyi = pVYI;
- this.assignID(pVYI);
+ constructor(pIconData) {
this.parse(pIconData);
+ this.assignId();
}
/**
- * Assigns an ID to this icon.
+ * Sets the vyi of this icon.
*
- * @param {VYI} pVYI - The vyi that holds this ID.
+ * @private
+ * @param {VYI} pVyi - The vyi that owns this icon.
+ */
+ setVyi(pVyi) {
+ if (!pVyi) return;
+
+ if (pVyi instanceof VYI) {
+ this.vyi = pVyi;
+ }
+ }
+ /**
+ * Removes the vyi from this icon.
+ * @private
+ */
+ removeVyi() {
+ this.vyi = null;
+ }
+ /**
+ * Assigns an Id to this icon.
* @private
*/
- assignID(pVYI) {
- this.id = Icon.generateID(pVYI);
+ assignId() {
+ this.id = Icon.generateId();
}
/**
* Gets the id of this icon.
- *
* @returns {string} The id of this icon.
*/
- getID() {
+ getId() {
return this.id;
}
/**
* Gets the vyi this icon belongs to.
- *
* @returns {VYI} The vyi this icon belongs to.
*/
getVyi() {
@@ -124,19 +144,19 @@ export class Icon {
}
/**
* Gets the icon this state belongs to. If this icon is not a state, it will return undefined.
- *
* @returns {Icon|undefined} The icon this state belongs to.
*/
getParent() {
return this.parent;
}
/**
- * parses through the icon data and adds data to this icon.
- * @param {Object} pIconData - The icon data that is used to build this icon.
- * @private
+ * Parses through the icon data and adds data to this icon.
+ * @param {Array} pIconData - The icon data that is used to build this icon.
*/
parse(pIconData) {
- // Loop through pIconData and create this icon
+ if (!pIconData) return;
+
+ // Loop through the icon data and create this icon
const iconName = pIconData[0];
const iconWidth = pIconData[1];
const iconHeight = pIconData[2];
@@ -195,13 +215,27 @@ export class Icon {
* @returns {self} This icon instance.
*/
setSize(pWidth, pHeight) {
- if (typeof(pWidth) === 'number') {
+ if (typeof pWidth === 'number') {
this.width = pWidth;
}
- if (typeof(pHeight) === 'number') {
+ if (typeof pHeight === 'number') {
this.height = pHeight;
}
}
+ /**
+ * Gets the width of the icon.
+ * @returns {number} The width of the icon.
+ */
+ getWidth() {
+ return this.width;
+ }
+ /**
+ * Gets the height of the icon.
+ * @returns {number} The height of the icon.
+ */
+ getHeight() {
+ return this.height;
+ }
/**
* Gets the width and height of this icon and returns it.
* @returns {Object} An object with the width and height of this icon.
@@ -215,12 +249,10 @@ export class Icon {
* @returns {self} This icon instance.
*/
setDataURL(pDataURL) {
- if (pDataURL) {
- if (typeof(pDataURL) === 'string') {
- this.dataURL = pDataURL;
- } else {
- VYI.logger.prefix('VYI-module').error('Invalid data url type!');
- }
+ if (typeof pDataURL === 'string') {
+ this.dataURL = pDataURL;
+ } else {
+ VYI.logger.prefix('Vyi-module').error('Invalid data url type!');
}
return this;
}
@@ -237,12 +269,10 @@ export class Icon {
* @returns {self} This icon instance.
*/
setDelay(pDelay) {
- if (pDelay) {
- if (typeof(pDelay) === 'number') {
- this.delay = pDelay;
- } else {
- VYI.logger.prefix('VYI-module').error('Invalid delay type!');
- }
+ if (typeof pDelay === 'number') {
+ this.delay = pDelay;
+ } else {
+ VYI.logger.prefix('Vyi-module').error('Invalid delay type!');
}
return this;
}
@@ -259,12 +289,10 @@ export class Icon {
* @returns {self} This icon instance.
*/
rename(pName) {
- if (pName || pName === '') {
- if (typeof(pName) === 'string') {
- this.name = pName;
- } else {
- VYI.logger.prefix('VYI-Module').error('Invalid type for pName!');
- }
+ if (typeof pName === 'string') {
+ this.name = pName;
+ } else {
+ VYI.logger.prefix('Vyi-module').error('Invalid type for pName!');
}
return this;
}
@@ -281,111 +309,70 @@ export class Icon {
* @returns {self} This icon instance.
*/
setAllFrameDelays(pDelay) {
- if (pDelay) {
- if (typeof(pDelay) === 'number') {
- this.setDelay(pDelay);
- this.frames.forEach((pFrame) => {
- pFrame.setDelay(pDelay);
- });
- } else {
- VYI.logger.prefix('VYI-Module').error('Invalid type for pDelay!');
- }
+ if (typeof pDelay === 'number') {
+ this.setDelay(pDelay);
+ this.getFrames().forEach((pFrame) => pFrame.setDelay(pDelay));
+ } else {
+ VYI.logger.prefix('Vyi-module').error('Invalid type for pDelay!');
}
return this;
}
/**
- * Adds this icon data as a state. A state is also an icon.
- * @param {Object} pIconData - The data used to create this state icon.
- * @returns {Icon|undefined} The state that was added or undefined.
+ * Adds a new frame to this icon.
+ * @param {Frame|Array} pFrameData - The frame data to give this frame.
+ * @returns {Frame|undefined} The frame that was added or undefined.
*/
- addState(pIconData) {
- if (pIconData instanceof Object) {
- const state = new Icon(pIconData, this.vyi);
- state.parent = this;
- this.states.push(state);
- return state;
+ addFrame(pFrameData) {
+ if (!pFrameData) {
+ VYI.logger.prefix('Vyi-module').error('No frame data passed!');
+ return;
}
- }
- /**
- * Removes the state passed or the state with the name pName.
- * @param {Icon} pState - The state to remove from this icon. pName should be not be used in tandem with this method of removing.
- * @param {string} pName - The name of the state to remove. pState must be undefined to use this method for removing.
- * @returns {self} This icon instance.
- */
- removeState(pState, pName) {
- // The index used to remove this frame.
- let index;
- // Remove via reference to state.
- if (this.states.includes(pState)) {
- index = this.states.indexOf(pState);
- // Remove via reference to name
- } else if (typeof(pName) === 'string') {
- const state = this.getState(pName);
- if (state) {
- index = this.states.indexOf(state);
- }
- } else {
- VYI.logger.prefix('VYI-Module').error('Failed to remove state!');
- return this;
+
+ if (!(pFrameData instanceof Frame) && !Array.isArray(pFrameData)) {
+ VYI.logger.prefix('Vyi-module').error('Invalid frame data type passed!');
+ return;
}
- if (typeof(index) === 'number') {
- // Remove the state
- this.states.splice(index, 1);
+
+ // Create the icon instance
+ const frame = pFrameData instanceof Frame
+ ? pFrameData
+ : new Frame(pFrameData);
+
+ if (frame.getWidth() !== frame.getWidth() || frame.getHeight() !== frame.getHeight()) {
+ VYI.logger.prefix('Vyi-module').error('Frame dimensions do not match parent!');
+ return;
}
- return this;
+
+ frame.setParent(this);
+ // We store this frame under its index in the Map 0-1
+ this.frames.set(this.frames.size, frame);
+ this.indexFrames();
+
+ return frame;
}
/**
- * Adds a new frame to this icon.
- * @param {Array} pFrameData - The frame data to give this frame.
- * @returns {Frame|undefined} The frame that was added or undefined.
+ * Removes the frame passed.
+ * @param {Frame} pFrame - The frame to remove from this icon.
+ * @returns {self} This icon instance.
*/
- addFrame(pFrameData) {
- if (pFrameData) {
- if (Array.isArray(pFrameData)) {
- const frame = new Frame(pFrameData, this);
- // Add the frame to the frames array.
- this.frames.push(frame);
- // Re-index frames after a change
+ removeFrame(pFrame) {
+ if (!pFrame) return;
+ if (pFrame instanceof Frame) {
+ if (this.frames.delete(pFrame.index)) {
+ pFrame.removeParent();
this.indexFrames();
- return frame;
- } else {
- VYI.logger.prefix('VYI-Module').error('Invalid frame data passed!');
}
- } else {
- VYI.logger.prefix('VYI-Module').error('No frame data passed!');
}
+ return this;
}
/**
- * Removes the frame passed or the frame that exists at pIndex.
- * @param {Frame} pFrame - The frame to remove from this icon. pIndex should be not be used in tandem with this method of removing.
- * @param {number} pIndex - The index of the frame to remove. pFrame must be undefined to use this method for removing.
+ * Removes the frame via it's index.
+ * @param {number} pIndex - The index of the frame to remove.
* @returns {self} This icon instance.
*/
- removeFrame(pFrame, pIndex) {
- // The index used to remove this frame.
- let index;
- // Remove via reference to frame.
- if (this.frames.includes(pFrame)) {
- index = this.frames.indexOf(pFrame);
- // Remove via index passed.
- } else if (pIndex || pIndex === 0) {
- if (typeof(pIndex) === 'number') {
- const frame = this.getFrame(pIndex);
- if (frame) {
- index = pIndex;
- }
- } else {
- VYI.logger.prefix('VYI-Module').error('Invalid pIndex type!');
- }
- } else {
- VYI.logger.prefix('VYI-Module').error('Failed to remove frame!');
- }
- if (typeof(index) === 'number') {
- // Remove the frame
- this.frames.splice(index, 1);
- // Re-index frames after a change
- this.indexFrames();
- }
+ removeFrameByIndex(pIndex) {
+ const frame = this.getFrame(pIndex);
+ this.removeFrame(frame);
return this;
}
/**
@@ -393,8 +380,7 @@ export class Icon {
* @private
*/
indexFrames() {
- // Reorder the frames after removing.
- this.frames.forEach((pFrame, pIndex) => {
+ this.getFrames().forEach((pFrame, pIndex) => {
pFrame.index = pIndex;
});
}
@@ -402,27 +388,17 @@ export class Icon {
* Reorders the frame in the animation. The index of the passed frame will be swapped with the frame at pIndex.
* The "first" frame of the animation is technically this icon's dataURL. So if you are aiming to change the order of this icon and convert it into a frame.
* pCurrentIndex must be set to -1 to match this icon.
+ *
* @param {number} pCurrentIndex - The current index of the frame.
* @param {number} pIndex - The index the frame will be moving to.
* @returns {self} This icon instance.
*/
reorderFrame(pCurrentIndex, pIndex) {
- if (typeof(pCurrentIndex) === 'number' && typeof(pIndex) === 'number') {
- let frameAtIndex;
- let currentFrame;
- // We check if the current index is -1, if it is then it means we want to treat this icon as a frame. As the icon data and delay of this icon serves
- // as the frame 0.
- if (pCurrentIndex === -1) {
- currentFrame = this;
- // Otherwise if the index passed can be found in the frames array, then we use that frame.
- } else if (this.frames[pCurrentIndex]) {
- currentFrame = this.frames[pCurrentIndex];
- }
-
- // We get the frame at the specified index.
- if (this.frames[pIndex]) {
- frameAtIndex = this.frames[pIndex];
- }
+ if (typeof pCurrentIndex === 'number' && typeof pIndex === 'number') {
+ let frameAtIndex = this.getFrame(pIndex);
+ let currentFrame = pCurrentIndex === -1
+ ? this
+ : this.getFrame(pCurrentIndex)
// If both frames can be found, we can swap their data.
if (currentFrame && frameAtIndex) {
@@ -436,14 +412,15 @@ export class Icon {
// Swap data from frame
currentFrame.setDataURL(frameAtIndexDataURL);
currentFrame.setDelay(frameAtIndexDelay);
+
// Swap data to frame
frameAtIndex.setDataURL(currentFrameDataURL);
frameAtIndex.setDelay(currentFrameDelay);
} else {
- VYI.logger.prefix('VYI-Module').error('There was no frame found at pCurrentIndex, or there was no frame found at pIndex!');
+ VYI.logger.prefix('Vyi-module').error('There was no frame found at pCurrentIndex, or there was no frame found at pIndex!');
}
} else {
- VYI.logger.prefix('VYI-Module').error('Invalid type used!');
+ VYI.logger.prefix('Vyi-module').error('Invalid type used!');
}
return this;
}
@@ -455,18 +432,14 @@ export class Icon {
* @returns {Frame|undefined} The frame found at pIndex.
*/
getFrame(pIndex) {
- if (typeof(pIndex) === 'number') {
- return this.frames[pIndex];
- } else {
- VYI.logger.prefix('VYI-Module').error('Invalid type used!');
- }
+ return this.frames.get(pIndex);
}
/**
* Returns an array of all the frames this icons has.
* @returns {Array} An array of frames this icon has.
*/
getFrames() {
- return [ ...this.frames ];
+ return Array.from(this.frames.values());
}
/**
* Gets all the frames belonging to this icon.
@@ -474,36 +447,130 @@ export class Icon {
* @returns {Array} An array containing the frame data of all frames.
*/
getFramesData() {
- const frameDataArray = [];
- this.frames.forEach((pFrame) => {
- frameDataArray.push(pFrame.export());
- });
+ const frameDataArray = this.getFrames().map((pFrame) => pFrame.export());
return frameDataArray;
}
/**
- * Gets the state that has the name pName.
+ * Adds this icon data as a state. A state is also an icon.
+ * @param {Array} pIconData - The data used to create this state icon.
+ * @returns {Icon|undefined} The state that was added or undefined.
+ */
+ addState(pIconData) {
+ if (!pIconData) {
+ VYI.logger.prefix('Vyi-module').error('No icon data passed!');
+ return;
+ }
+
+ if (!(pIconData instanceof Icon) && !Array.isArray(pIconData)) {
+ VYI.logger.prefix('Vyi-module').error('Invalid icon data type passed!');
+ return;
+ }
+
+ // Create the icon instance
+ const state = pIconData instanceof Icon
+ ? pIconData
+ : new Icon(pIconData);
+
+ if (state.getWidth() !== this.getWidth() || state.getHeight() !== this.getHeight()) {
+ VYI.logger.prefix('Vyi-module').error('State dimensions do not match parent!');
+ return;
+ }
+
+ state.setParent(this);
+ state.setVyi(this.vyi);
+ this.states.set(state.id, state);
+
+ return state;
+ }
+ /**
+ * Sets the parent for this state.
+ * @private
+ * @param {Icon} pParent - The parent icon of this state.
+ */
+ setParent(pParent) {
+ if (!pParent || this.parent) return;
+ if (pParent instanceof Icon) {
+ this.parent = pParent;
+ }
+ }
+ /**
+ * Removes the parent and vyi from this state.
+ * @private
+ */
+ removeParent() {
+ if (this.parent) {
+ this.parent = null;
+ this.vyi = null;
+ }
+ }
+ /**
+ * Removes the state passed.
+ * @param {Icon} pState - The state to remove from this icon.
+ * @returns {self} This icon instance.
+ */
+ removeState(pState) {
+ if (pState instanceof Icon) {
+ if (this.icons.delete(pState.id)) {;
+ pState.removeParent();
+ }
+ }
+ return this;
+ }
+ /**
+ * Removes the state via it's name. The LAST defined icon that has the passed name will be removed. As names are not unique.
+ * @param {string} pName - The name to use to find the state.
+ * @returns {self} This icon instance.
+ */
+ removeStateByName(pName) {
+ const state = this.getState(pName);
+ this.removeState(state);
+ return this;
+ }
+ /**
+ * Removes the state via it's id.
+ * @param {string} pName - The id to use to find the state.
+ * @returns {self} This icon instance.
+ */
+ removeStateById(pId) {
+ const state = this.getStateById(pId);
+ this.removeState(state);
+ return this;
+ }
+ /**
+ * Gets the state that has the name pName. The LAST defined state that has the passed name will be returned.
* @param {string} pName - The name of the state to get.
* @returns {Icon} The state that has the name of pName.
*/
getState(pName) {
- if (typeof(pName) === 'string') {
- for (let i = this.states.length - 1; i >= 0; i--) {
- const icon = this.states[i];
+ if (typeof pName === 'string') {
+ const states = this.getStates();
+ for (let i = states.length - 1; i >= 0; i--) {
+ const state = states[i];
// If the icon has the same name, return that icon
- if (icon.getName() === pName) {
- return icon;
+ if (state.getName() === pName) {
+ return state;
}
}
} else {
- VYI.logger.prefix('VYI-module').error('Invalid name type used!');
+ VYI.logger.prefix('Vyi-module').error('Invalid name type used!');
}
}
+ /**
+ * Gets the state by the id provided.
+ * @private
+ * @param {string} pId - The id of the state.
+ * @returns {Icon} The state that has the id that was passed.
+ */
+ getStateById(pId) {
+ if (!pId) return;
+ return this.states.get(pId);
+ }
/**
* Returns an array of all the states this icons has.
* @returns {Array} An array of states this icon has.
*/
getStates() {
- return [ ...this.states ];
+ return Array.from(this.states.values());
}
/**
* Gets all the states belonging to this icon.
@@ -511,11 +578,7 @@ export class Icon {
* @returns {Array} An array containing the state data of all frames.
*/
getStatesData() {
- const stateDataArray = [];
- // Loop state array to export relevant information.
- this.states.forEach((pState) => {
- stateDataArray.push(pState.exportAsState());
- });
+ const stateDataArray = this.getStates().map((pState) => pState.exportAsState());
return stateDataArray;
}
/**
@@ -532,12 +595,7 @@ export class Icon {
// state frame delay
stateData[2] = this.getDelay();
// state frame array
- stateData[3] = [];
-
- // Loop frame array to export relevant information.
- this.frames.forEach((pFrame) => {
- stateData[3].push(pFrame.export());
- });
+ stateData[3] = this.getFrames().map((pFrame) => pFrame.export());
return stateData;
}
/**
@@ -549,12 +607,10 @@ export class Icon {
const iconData = [];
// icon name
iconData[0] = this.getName();
- // Get the size of this icon.
- const size = this.getSize();
// icon width
- iconData[1] = size.width;
+ iconData[1] = this.getWidth();
// icon height
- iconData[2] = size.height;
+ iconData[2] = this.getHeight();
// frame delay
iconData[3] = this.getDelay();
// icon DataURL
@@ -562,10 +618,8 @@ export class Icon {
// frame array
iconData[5] = this.getFramesData();
- // this is actually an optional data entry into the vyi, only used if states actually exist on this icon.
- // this will save data
- if (this.states.length) {
- // states array
+ // This is actually an optional data entry into the vyi, only used if states actually exist on this icon.
+ if (this.states.size > 0) {
iconData[6] = this.getStatesData();
}
return iconData;
diff --git a/src/vyi.mjs b/src/vyi.mjs
index 4d71d51..22b5b37 100644
--- a/src/vyi.mjs
+++ b/src/vyi.mjs
@@ -1,11 +1,12 @@
import { Logger } from './vendor/logger.mjs';
import { Icon } from './icon.mjs';
+import { Frame } from './frame.mjs';
import pako from './vendor/pako.esm.mjs';
/**
* @public
*/
-export class VYI {
+class VYI {
/**
* The version of the module.
*/
@@ -16,11 +17,11 @@ export class VYI {
*/
static logger = new Logger();
/**
- * An array of icons that belong to this VYI
+ * A map of icons that belong to this VYI
* @private
- * @type {Array}
+ * @type {Map}
*/
- icons = [];
+ icons = new Map();
/**
* The name of this vyi.
* @private
@@ -32,20 +33,13 @@ export class VYI {
* @private
* @type {number}
*/
- formatVersion;
- /**
- * An array of used IDs to prevent collusion between duplicate named icons.
- *
- * @private
- * @type {Array}
- */
- reservedIDs = [];
+ formatVersion = 1;
/**
* Initializes this module with the information from the VYI passed.
* @param {Object} pVyiData - A JSON / Javascript object containing the vyi information.this.ogger
*/
constructor(pVyiData) {
- VYI.logger.registerType('VYI-Module', '#ff6600');
+ VYI.logger.registerType('Vyi-module', '#ff6600');
this.parse(pVyiData);
}
/**
@@ -63,31 +57,28 @@ export class VYI {
try {
let vyi;
- if (typeof(pVyiData) === 'string') {
- const isNodeEnv = typeof(window) === 'undefined';
+ if (typeof pVyiData === 'string') {
+ const isNodeEnv = typeof window === 'undefined';
vyi = isNodeEnv
? await this.readFileAndGetVYI(pVyiData)
: await this.fetchAndParseJSON(pVyiData);
- } else if (typeof(pVyiData) === 'object' && !Array.isArray(pVyiData) && !(pVyiData instanceof ArrayBuffer || pVyiData instanceof Uint8Array)) {
+ } else if (pVyiData instanceof VYI) {
+ vyi = pVyiData.export();
+ } else if (pVyiData instanceof Object && !Array.isArray(pVyiData) && !(pVyiData instanceof ArrayBuffer || pVyiData instanceof Uint8Array)) {
vyi = pVyiData;
} else if (pVyiData instanceof ArrayBuffer || pVyiData instanceof Uint8Array) {
vyi = await this.handleBinaryData(pVyiData);
} else {
- throw new Error('Invalid input type provided.');
+ throw new Error('Error processing: Invalid input type provided.');
}
- // If there is valid vyi data, process it
- if (vyi) {
- this.processVyiData(vyi);
- }
+ this.processVyiData(vyi);
} catch (pError) {
- VYI.logger.prefix('VYI-module').error(`Error processing VYI data: ${pError.message}`);
- throw pError; // Rethrow the error for further handling
+ VYI.logger.prefix('Vyi-module').error(`${pError.message}`);
}
}
/**
* Reads a file and returns the vyi from it.
- *
* @private
* @param {string} - The URL to read the data from.
* @returns {Promise