Divide the string into sections and provide an object as output

I encountered a roadblock while working on a tool.

My task involves accessing the stylesheet object of a website and specifically retrieving the "CSSFontFaceRule". I managed to accomplish this, but the output in my returned object is a lengthy string. I aim to break down the string into an object format. I have also prepared a fiddle for reference: http://jsfiddle.net/9eoytc6v/1/

This represents my current situation:

@font-face {font-family: "Test-Book";
src: 
    url("https://fontserver.xyz/test.eot?#iefix") format("embedded-opentype"),
    url("https://fontserver.xyz/test.woff2") format("woff2"),
    url("https://fontserver.xyz/test.woff") format("woff"),
    url("https://fontserver.xyz/test.ttf") format("truetype"),
    url("https://fontserver.xyz/test.svg#Test-Book") format("svg");
}
let fonts = {};

function getFontPairs(obj) {
  let object = obj || {},
    stylesheet = document.styleSheets,
    rule = null,
    length = stylesheet.length,
    j;
  while (0 <= --length) {
    rule = stylesheet[length].rules || stylesheet[length].cssRules || [];
    j = rule.length;
    while (0 <= --j) {
      if (rule[j].constructor.name === "CSSFontFaceRule") {
        let sourcesString = rule[j].style.src;
        let re = /\s*(?:,|$)\s*/;
        let sources = sourcesString.split(re);
        let final = [];

        sources.forEach(function(element){
        let reg = /[ ,]+/;
        let srcArray = element.split(reg);
        srcArray = srcArray.reverse();
        final.push(srcArray);
        });

       object[rule[j].style.fontFamily] = final;


      }
    }
  }
  return object;
}

getFontPairs(fonts);
console.log(fonts);

I attempted to use arrays but the outcome is somewhat disorganized: Current array solution

My goal is to achieve a structure similar to this: Expected object solution

As I am not proficient with RegEx at the moment, I want to remove the url("") and the format("") as well. Any assistance provided would be greatly appreciated. Perhaps someone can offer a more efficient version of my code.

Answer №1

To extract the URL and format from each source string, a simple regex can be used to match what is inside double or single quotes. For example, the following regex can be used to match the URL:

/url\(.(.+?).\)/

This regex means to match anything between url(. and .). The parentheses are escaped so that the regex does not perceive them as a group. The . (any character) matches the quotes (double or single). The (.+?) part represents the URL we are interested in, utilizing the non-greedy mode with the ? so it matches the fewest characters possible. This prevents matching beyond the URL into the format section, avoiding issues like:

https://fontserver.xyz/test.eot?#iefix") format("embedded-opentype
.

To create an object, the sources can be reduced into one using the following method:

let sourcesObj = sources.reduce((acc, source) => {
  let format = source.match(/format\(.(.+?).\)/)[1],
      url = source.match(/url\(.(.+?).\)/)[1];

  acc[format] = { format, url };
  return acc;
}, {});

Full Code:

// JavaScript Function to Get Font Pairs
function getFontPairs() {
  let object = {},
    stylesheet = document.styleSheets,
    rule = null,
    length = stylesheet.length,
    j;

  while (0 <= --length) {
    rule = stylesheet[length].rules || stylesheet[length].cssRules || [];
    j = rule.length;

    while (0 <= --j) {
      if (rule[j].constructor.name === "CSSFontFaceRule") {
        let sourcesString = rule[j].style.src;
        let sources = sourcesString.split(/\s*,\s*/);

        let sourcesObj = sources.reduce((acc, source) => {
          let format = source.match(/format\(.(.+?).\))/[1],
              url = source.match(/url\(.(.+?).\)/)[1];
          
          acc[format] = { format, url };
          return acc;

        }, {});

        object[rule[j].style.fontFamily] = sourcesObj;
      }
    }
  }

  return object;
}

// Execute the function and display results
let fonts = getFontPairs();
console.log(fonts);

Note: An alternative regex for URL and format could be:

/url(\s*["'](.+?)["']\s*)/

This regex accounts for cases with extra spaces like url( "..." ) and ensures it looks for either double (") or single (') quotes. Similar adjustments could be made for the format regex.


EDIT:

If a source lacks a field, ensure the format and URL regexes do indeed find a match before accessing the group [1]:

let sourcesObj = sources.reduce((acc, source) => {
  let format = source.match(/format\(.(.+?).\)/),
      url = source.match(/url\(.(.+?).\)/);

  if(format && url) {
    acc[format[1]] = { format: format[1], url: url[1] };
  }

  return acc;

}, {}); 

EDIT 2:

In cases where the format is missing, replace it with "Unknown":

let sourcesObj = sources.reduce((acc, source) => {
  let format = source.match(/format\(.(.+?).\)/),
      url = source.match(/url\(.(.+?).\)/)[1];

  format = format ? format[1] : "Unknown";

  acc = { format, url };

  return acc;

}, {}); 

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What drawbacks come with developing an Express.js application using TypeScript?

Curious about the potential drawbacks of using TypeScript to write Express.js applications or APIs instead of JavaScript. ...

Adjust the jQuery.animate function based on the specific value

When the button with the class name btn is clicked, the element with the class name img-box will transition with animation. I want to adjust the animation based on the current position of the img-box. After the first click on the button, the element mo ...

Functions for abbreviating and combining strings in Javascript

Looking for help to simplify and shorten a Javascript function: $scope.doRefresh = function (){ if($scope.bulletpointPopular){ ArticleService.popular().then(function(data){ $scope.articles = data; }) .finally(function() { ...

What causes Font Awesome 5 icons to vanish when changing the font-family?

I am facing an issue with the code I have below. It changes the font style successfully, but it also causes the icon to disappear. Can anyone provide tips on how to ensure that the icon remains visible while changing the font style? <link rel="styles ...

Guide to logging out from a website using AngularJS

I have developed a login page using AngularJS, where I input static username and password. Upon submitting the form, it redirects to a welcome screen. Now, I am looking to implement a logout feature that will transition from the welcome page back to the lo ...

Send a personalized array from Javascript to a Spring Rest Service using Ajax

I am faced with the task of creating a POST request using AJAX in jQuery. This involves passing an array of objects, which I can achieve with the following code: var actor = new Array(); for(var i = 1; i <= incr; i++) { actor.push({ "name": ...

Creating an Array of Objects in JAVA - A Step by Step Guide

My task is to read a CSV file and store its data into an array of Objects, but I mistakenly created an ArrayList instead. Now, I need help figuring out how to fix this error as my attempts have been fruitless. Below is the code I used to read the CSV file ...

Step-by-step guide for adding an icon to the corner of a Material UI button

Is there a way to position an icon in the corner of a Material UI button in React? Currently, I have the icon next to the title but I would like to move it to the lower right corner of the button. Any suggestions on how to achieve this? Thank you! export ...

Unable to access the response body of a POST request from an external API within Firebase Cloud Functions

I am encountering an issue with my cloud function in which it makes an http POST request to the LinkedIn API for retrieving an access token. The main problem is that I am unable to retrieve the `body` of the response as it always turns out to be `undefined ...

What methods are available to load sections of a complex SVG shape asynchronously?

I'm currently in the process of creating a website with a geographic map built using SVG, pulling data from OpenStreetMap. Due to its large size and potential for transformations such as zooming and moving, only a portion of it will be visible on the ...

Utilizing malloc for dynamic allocation of multi-dimensional arrays with varying row sizes

My current C code looks like this : int *a; size_t size = 2000*sizeof(int); a = malloc(size); and it's functioning properly. However, if I switch to the following : char **b = malloc(2000*sizeof *b); where each element of b has a different length. ...

Converting Typescript objects containing arrays into a single array

Looking for some assistance with this problem :) I am trying to convert the object into an array with the following expected result: result = [ { id: 'test-1', message: 'test#1.1' }, { id: 'test-1', mess ...

Issue with Checkbox Functionality Between Parent and Child Components in React.js

In the main component, I have four checkboxes. My goal is to display a value from a function in the child component based on whether each checkbox is checked or not. export default class App extends Component { constructor(props) { super(props); ...

ReactJS - Unable to access property of undefined object

I encountered a TypeError when rendering, stating that my object is not defined. Despite thinking I defined it before using it. Here is an example of ArticleDetails.js that I am referencing: import React, {Component} from 'react'; class Arti ...

In a Twitter Bootstrap environment, the button cursor transforms into an I-beam when activated

After utilizing twitter bootstrap to construct a small blog site and integrating a third-party file upload application into the project, I encountered an issue. The CSS files in the file uploader style the "upload button," but when my cursor hovers over it ...

Calculate the combined sum and alter the values of three input fields

There are three text boxes (testbox, testbox2, testbox3) that receive values from an input field, radio button selection, and checkbox. The correct values are displayed in testbox, testbox2, testbox3. Additionally, the sum of testbox, testbox2, testbox3 n ...

Trigger a function on q-select change using onChange event (Quasar Framework)

I'm trying to get the function to run when I change the selected value, but it's not working. How can I solve this issue? Below is the code I am using: <q-select v-model="single" :options="['def', 'abc', ...

HTML image identifier and canvas elements

Greetings! I have a question that I've been struggling to find an answer for on Google. I'm attempting to draw an image on a canvas and initially used the "new" constructor ( ballPic = new Image(); ballPic.src = "ball.png" ) which worked fine. Ho ...

Combining and sorting cursor data from multiple collections based on date

Can you combine two or more different collections and arrange them by createdAt? For example, suppose we have data like this: CollectionA.insert({ createdAt: new Date(), field1: value1, field2: value3, field3: value2, ...

Node.js is throwing a TypeError: Unable to access the property 'url' of null, yet the application is functioning properly

UPDATE: It appears to be checking for /:slug even when navigating to a different route, which may be causing the issue. I'm currently working on building a URL Shortener using Nodejs, Expressjs, MongoDB, and EJS. Despite my application functioning s ...