Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

font-picker

Package Overview
Dependencies
Maintainers
1
Versions
29
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

font-picker - npm Package Compare versions

Comparing version 1.2.1 to 1.3.0

1133

lib/index.js

@@ -7,680 +7,671 @@ (function (global, factory) {

function __$$styleInject(css, ref) {
if ( ref === void 0 ) ref = {};
var insertAt = ref.insertAt;
/**
* Check if font is available for the website
* Source: https://www.kirupa.com/html5/detect_whether_font_is_installed.htm
* @param fontName
* @returns {boolean}
*/
function isFontAvailable(fontName) {
// creating our in-memory Canvas element where the magic happens
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");
if (!css || typeof document === 'undefined') { return; }
// the text whose final pixel size I want to measure
var text = "abcdefghijklmnopqrstuvwxyz0123456789";
var head = document.head || document.getElementsByTagName('head')[0];
var style = document.createElement('style');
style.type = 'text/css';
// specifying the baseline font
context.font = "72px monospace";
if (insertAt === 'top') {
if (head.firstChild) {
head.insertBefore(style, head.firstChild);
} else {
head.appendChild(style);
}
} else {
head.appendChild(style);
}
// checking the size of the baseline text
var baselineSize = context.measureText(text).width;
if (style.styleSheet) {
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
}
// specifying the font whose existence we want to check
context.font = "72px '" + fontName + "', monospace";
/**
* Check if font is available for the website
* Source: https://www.kirupa.com/html5/detect_whether_font_is_installed.htm
* @param fontName
* @returns {boolean}
*/
function isFontAvailable(fontName) {
// creating our in-memory Canvas element where the magic happens
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");
// checking the size of the font we want to check
var newSize = context.measureText(text).width;
// the text whose final pixel size I want to measure
var text = "abcdefghijklmnopqrstuvwxyz0123456789";
// removing the Canvas element we created
canvas = null;
// specifying the baseline font
context.font = "72px monospace";
// checking the size of the baseline text
var baselineSize = context.measureText(text).width;
// specifying the font whose existence we want to check
context.font = "72px '" + fontName + "', monospace";
// checking the size of the font we want to check
var newSize = context.measureText(text).width;
// removing the Canvas element we created
canvas = null;
// If the size of the two text instances is the same, the font does not exist because it is being
// rendered using the default sans-serif font
if (newSize == baselineSize) {
return false;
} else {
return true;
// If the size of the two text instances is the same, the font does not exist because it is being
// rendered using the default sans-serif font
if (newSize == baselineSize) {
return false;
} else {
return true;
}
}
}
/**
* Fetch list of all fonts available on Google Fonts, sorted by popularity
*/
function fetchFontList(apiKey) {
return new Promise(function (resolve, reject) {
var url = 'https://www.googleapis.com/webfonts/v1/webfonts?sort=popularity&key=' + apiKey;
var request = new XMLHttpRequest();
request.overrideMimeType('application/json');
request.open('GET', url, true);
request.onreadystatechange = function () {
// request has completed
if (request.readyState === 4) {
// success
if (request.status === 200) {
var response = JSON.parse(request.responseText);
return resolve(response.items);
/**
* Fetch list of all fonts available on Google Fonts, sorted by popularity
*/
function fetchFontList(apiKey) {
return new Promise(function (resolve, reject) {
var url = 'https://www.googleapis.com/webfonts/v1/webfonts?sort=popularity&key=' + apiKey;
var request = new XMLHttpRequest();
request.overrideMimeType('application/json');
request.open('GET', url, true);
request.onreadystatechange = function () {
// request has completed
if (request.readyState === 4) {
// success
if (request.status === 200) {
var response = JSON.parse(request.responseText);
return resolve(response.items);
}
// error
else {
return reject(new Error('Response has status code ' + request.status));
}
}
// error
else {
return reject(new Error('Response has status code ' + request.status));
}
}
};
request.send();
});
}
/**
* Add Google Fonts stylesheet for the specified font family and variants
*/
function downloadFullFont(font, fontId, variants, onChange) {
// generate the stylesheet URL
var url = 'https://fonts.googleapis.com/css?family=';
// font name
url += font.family.replace(/ /g, '+');
// font variants
url += ':' + variants[0];
for (var i = 1; i < variants.length; i += 1) {
url += '|' + variants[i];
};
request.send();
});
}
// add the stylesheet to the document head
var link = document.createElement('link');
link.rel = 'stylesheet';
link.href = url;
link.id = 'font-full-' + fontId;
// if onChange function is specified: execute it once the stylesheet has loaded
if (onChange) {
link.onload = function () {
onChange(font);
};
/**
* Add Google Fonts stylesheet for the specified font family and variants
*/
function downloadFullFont(font, fontId, variants, onChange) {
// generate the stylesheet URL
var url = 'https://fonts.googleapis.com/css?family=';
// font name
url += font.family.replace(/ /g, '+');
// font variants
url += ':' + variants[0];
for (var i = 1; i < variants.length; i += 1) {
url += '|' + variants[i];
}
// add the stylesheet to the document head
var link = document.createElement('link');
link.rel = 'stylesheet';
link.href = url;
link.id = 'font-full-' + fontId;
// if onChange function is specified: execute it once the stylesheet has loaded
if (onChange) {
link.onload = function () {
onChange(font);
};
}
document.head.appendChild(link);
}
document.head.appendChild(link);
}
/**
* Add limited Google Fonts stylesheet for the specified font family (only containing the characters
* which are needed to write the font family name)
*/
function downloadPreviewFont(font, fontId, variants) {
// generate the stylesheet URL
var url = 'https://fonts.googleapis.com/css?family=';
// font name
url += font.family.replace(/ /g, '+');
// font variants
url += ':' + variants[0];
for (var i = 1; i < variants.length; i += 1) {
url += '|' + variants[i];
/**
* Add limited Google Fonts stylesheet for the specified font family (only containing the characters
* which are needed to write the font family name)
*/
function downloadPreviewFont(font, fontId, variants) {
// generate the stylesheet URL
var url = 'https://fonts.googleapis.com/css?family=';
// font name
url += font.family.replace(/ /g, '+');
// font variants
url += ':' + variants[0];
for (var i = 1; i < variants.length; i += 1) {
url += '|' + variants[i];
}
// characters to download: remove spaces and duplicate letters from the font name
var downloadChars = font.family;
downloadChars = downloadChars.replace(/\s+/g, '');
downloadChars = downloadChars.split('').filter(function (x, n, s) {
return s.indexOf(x) === n;
}).join('');
url += '&text=' + downloadChars;
// add the stylesheet to the document head
var link = document.createElement('link');
link.rel = 'stylesheet';
link.href = url;
link.id = 'font-preview-' + fontId;
document.head.appendChild(link);
}
// characters to download: remove spaces and duplicate letters from the font name
var downloadChars = font.family;
downloadChars = downloadChars.replace(/\s+/g, '');
downloadChars = downloadChars.split('').filter(function (x, n, s) {
return s.indexOf(x) === n;
}).join('');
url += '&text=' + downloadChars;
// add the stylesheet to the document head
var link = document.createElement('link');
link.rel = 'stylesheet';
link.href = url;
link.id = 'font-preview-' + fontId;
document.head.appendChild(link);
}
/**
* Check whether the full font needs to be downloaded and do so if necessary
*/
function checkFullFont(font, variants, onChange) {
var fontId = font.family.replace(/\s+/g, '-').toLowerCase();
/**
* Check whether the full font needs to be downloaded and do so if necessary
*/
function checkFullFont(font, variants, onChange) {
var fontId = font.family.replace(/\s+/g, '-').toLowerCase();
// if preview font is available: replace it with the full font
if (document.getElementById('font-preview-' + fontId)) {
document.getElementById('font-preview-' + fontId).outerHTML = '';
downloadFullFont(font, fontId, variants, onChange);
}
// if font is not available: download it
else if (!document.getElementById('font-full-' + fontId) && !isFontAvailable(font.family)) {
// if preview font is available: replace it with the full font
if (document.getElementById('font-preview-' + fontId)) {
document.getElementById('font-preview-' + fontId).outerHTML = '';
downloadFullFont(font, fontId, variants, onChange);
}
// if is available
else if (onChange) {
// execute onChange function if it is specified
onChange(font);
// if font is not available: download it
else if (!document.getElementById('font-full-' + fontId) && !isFontAvailable(font.family)) {
downloadFullFont(font, fontId, variants, onChange);
}
}
// if is available
else if (onChange) {
// execute onChange function if it is specified
onChange(font);
}
}
/**
* Check whether the preview font needs to be downloaded and do so if necessary
*/
function checkPreviewFont(font, variants) {
var fontId = font.family.replace(/\s+/g, '-').toLowerCase();
/**
* Check whether the preview font needs to be downloaded and do so if necessary
*/
function checkPreviewFont(font, variants) {
var fontId = font.family.replace(/\s+/g, '-').toLowerCase();
// if full font is not available: download preview font
if (!document.getElementById('font-full-' + fontId) && !isFontAvailable(font.family)) {
downloadPreviewFont(font, fontId, variants);
// if full font is not available: download preview font
if (!document.getElementById('font-full-' + fontId) && !isFontAvailable(font.family)) {
downloadPreviewFont(font, fontId, variants);
}
}
}
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
return typeof obj;
} : function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
return typeof obj;
} : function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
var classCallCheck = function (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
};
var createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
/**
* Class responsible for retrieving and filtering/sorting the font list, keeping track of the active
* font, and downloading and applying fonts and font previews
* @see FontPicker.js (same parameters)
*/
var FontHandler = function () {
/**
* Download the default font (if necessary) and apply it
*/
function FontHandler(apiKey, defaultFont, options, onChange) {
classCallCheck(this, FontHandler);
this.activeFont = defaultFont;
this.apiKey = apiKey;
this.fonts = [];
this.onChange = onChange;
this.options = options;
this.name = options.name;
this.previewIndex = 0; // list index up to which font previews have been downloaded
// make default font active and download it (if necessary)
this.activeFont = {
family: defaultFont,
variants: 'regular'
};
checkFullFont(this.activeFont, this.options.variants);
// apply default font
this.stylesheet = document.createElement('style');
this.stylesheet.rel = 'stylesheet';
this.stylesheet.type = 'text/css';
var style = '\n\t\t\t.apply-font' + this.name + ' {\n\t\t\t\tfont-family: "' + this.activeFont.family + '";\n\t\t\t}\n\t\t';
// font weight/style: split number and text in font variant parameter
var defaultVariant = this.options.variants[0].split(/(\d+)/).filter(Boolean);
// either font weight or style is specified (e.g. 'regular, '300', 'italic')
if (defaultVariant.length === 1) {
if (defaultVariant[0] === 'regular') {
style += '\n\t\t\t\t\t.apply-font' + this.name + ', #font-picker > ul > li > a {\n\t\t\t\t\t\tfont-weight: 400;\n\t\t\t\t\t\tfont-style: normal;\n\t\t\t\t\t}\n\t\t\t\t';
} else if (defaultVariant[0] === 'italic') {
style += '\n\t\t\t\t\t.apply-font' + this.name + ', #font-picker > ul > li > a {\n\t\t\t\t\t\tfont-weight: 400;\n\t\t\t\t\t\tfont-style: italic;\n\t\t\t\t\t}\n\t\t\t\t';
} else {
style += '\n\t\t\t\t\t.apply-font' + this.name + ', #font-picker > ul > li > a {\n\t\t\t\t\t\tfont-weight: ' + defaultVariant[0] + ';\n\t\t\t\t\t\tfont-style: normal;\n\t\t\t\t\t}\n\t\t\t\t';
}
}
// both font weight and style are specified
else if (defaultVariant.length === 2) {
style += '\n\t\t\t.apply-font' + this.name + ', #font-picker > ul > li > a {\n\t\t\t\tfont-weight: ' + defaultVariant[0] + ';\n\t\t\t\tfont-style: ' + defaultVariant[1] + ';\n\t\t\t}\n\t\t';
}
this.stylesheet.appendChild(document.createTextNode(style));
document.head.appendChild(this.stylesheet);
}
var classCallCheck = function (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
};
/**
* Download list of available Google Fonts and filter/sort it according as specified in the
* 'options' parameter object
*/
var createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
createClass(FontHandler, [{
key: 'init',
value: function init() {
var _this = this;
/**
* Class responsible for retrieving and filtering/sorting the font list, keeping track of the active
* font, and downloading and applying fonts and font previews
* @see FontPicker.js (same parameters)
*/
return fetchFontList(this.apiKey).then(function (fetchedList) {
var fontList = fetchedList;
var FontHandler = function () {
// 'families' parameter (only keep fonts whose names are included in the provided array)
if (_this.options.families) {
fontList = fontList.filter(function (font) {
return _this.options.families.includes(font.family);
});
}
/**
* Download the default font (if necessary) and apply it
*/
function FontHandler(apiKey, defaultFont, options, onChange) {
classCallCheck(this, FontHandler);
// 'categories' parameter (only keep fonts in categories from the provided array)
if (_this.options.categories) {
fontList = fontList.filter(function (font) {
return _this.options.categories.includes(font.category);
});
}
// 'variants' parameter (only keep fonts with at least the specified variants)
if (_this.options.variants) {
fontList = fontList.filter(function (font) {
for (var i = 0; i < _this.options.variants.length; i += 1) {
if (font.variants.indexOf(_this.options.variants[i]) === -1) {
return false;
}
}
return true;
});
}
this.activeFont = defaultFont;
this.apiKey = apiKey;
this.fonts = [];
this.onChange = onChange;
this.options = options;
this.name = options.name;
this.previewIndex = 0; // list index up to which font previews have been downloaded
// add default font to beginning of list if it is not already in it
if (fontList.filter(function (font) {
return font.family === _this.activeFont.family;
}).length === 0) {
fontList.unshift(_this.activeFont);
}
// make default font active and download it (if necessary)
this.activeFont = {
family: defaultFont,
variants: 'regular'
};
checkFullFont(this.activeFont, this.options.variants);
// 'limit' parameter (limit font list size)
if (_this.options.limit) {
fontList = fontList.slice(0, _this.options.limit);
}
// apply default font
this.stylesheet = document.createElement('style');
this.stylesheet.rel = 'stylesheet';
this.stylesheet.type = 'text/css';
var style = '\n\t\t\t.apply-font' + this.name + ' {\n\t\t\t\tfont-family: "' + this.activeFont.family + '";\n\t\t\t}\n\t\t';
// font weight/style: split number and text in font variant parameter
var defaultVariant = this.options.variants[0].split(/(\d+)/).filter(Boolean);
// either font weight or style is specified (e.g. 'regular, '300', 'italic')
if (defaultVariant.length === 1) {
if (defaultVariant[0] === 'regular') {
style += '\n\t\t\t\t\t.apply-font' + this.name + ', #font-picker > ul > li > a {\n\t\t\t\t\t\tfont-weight: 400;\n\t\t\t\t\t\tfont-style: normal;\n\t\t\t\t\t}\n\t\t\t\t';
} else if (defaultVariant[0] === 'italic') {
style += '\n\t\t\t\t\t.apply-font' + this.name + ', #font-picker > ul > li > a {\n\t\t\t\t\t\tfont-weight: 400;\n\t\t\t\t\t\tfont-style: italic;\n\t\t\t\t\t}\n\t\t\t\t';
} else {
style += '\n\t\t\t\t\t.apply-font' + this.name + ', #font-picker > ul > li > a {\n\t\t\t\t\t\tfont-weight: ' + defaultVariant[0] + ';\n\t\t\t\t\t\tfont-style: normal;\n\t\t\t\t\t}\n\t\t\t\t';
// 'sort' parameter (list is already sorted by popularity)
if (_this.options.sort === 'alphabetical') {
fontList = fontList.sort(function (fontA, fontB) {
return fontA.family.localeCompare(fontB.family);
});
}
// save modified font list
_this.fonts = fontList;
// download previews for the first 10 fonts in the list
_this.downloadPreviews(10);
});
}
}
// both font weight and style are specified
else if (defaultVariant.length === 2) {
style += '\n\t\t\t.apply-font' + this.name + ', #font-picker > ul > li > a {\n\t\t\t\tfont-weight: ' + defaultVariant[0] + ';\n\t\t\t\tfont-style: ' + defaultVariant[1] + ';\n\t\t\t}\n\t\t';
}
this.stylesheet.appendChild(document.createTextNode(style));
document.head.appendChild(this.stylesheet);
}
/**
* Download list of available Google Fonts and filter/sort it according as specified in the
* 'options' parameter object
*/
/**
* Set the font with the given font list index as the active one, download (if necessary) and
* apply it
*/
}, {
key: 'changeActiveFont',
value: function changeActiveFont(index) {
var previousFont = this.activeFont.family;
createClass(FontHandler, [{
key: 'init',
value: function init() {
var _this = this;
// change font
this.activeFont = this.fonts[index];
return fetchFontList(this.apiKey).then(function (fetchedList) {
var fontList = fetchedList;
// apply font and set fallback fonts
var fallbackFont = this.activeFont.category === 'handwriting' ? 'cursive' : this.activeFont.category;
var style = '\n\t\t\t.apply-font' + this.name + ' {\n\t\t\t\tfont-family: "' + this.activeFont.family + '", "' + previousFont + '", ' + fallbackFont + ';\n\t\t\t}\n\t\t';
this.stylesheet.replaceChild(document.createTextNode(style), this.stylesheet.childNodes[0]);
// 'families' parameter (only keep fonts whose names are included in the provided array)
if (_this.options.families) {
fontList = fontList.filter(function (font) {
return _this.options.families.includes(font.family);
});
}
// download font (if necessary)
checkFullFont(this.activeFont, this.options.variants, this.onChange);
}
// 'categories' parameter (only keep fonts in categories from the provided array)
if (_this.options.categories) {
fontList = fontList.filter(function (font) {
return _this.options.categories.includes(font.category);
});
}
/**
* Download font previews for the list entries up to the given index
*/
// 'variants' parameter (only keep fonts with at least the specified variants)
if (_this.options.variants) {
fontList = fontList.filter(function (font) {
for (var i = 0; i < _this.options.variants.length; i += 1) {
if (font.variants.indexOf(_this.options.variants[i]) === -1) {
return false;
}
}
return true;
});
}, {
key: 'downloadPreviews',
value: function downloadPreviews(downloadIndex) {
// stop at the end of the font list
var downloadIndexMax = void 0;
if (downloadIndex > this.fonts.length) {
downloadIndexMax = this.fonts.length;
} else {
downloadIndexMax = downloadIndex;
}
// add default font to beginning of list if it is not already in it
if (fontList.filter(function (font) {
return font.family === _this.activeFont.family;
}).length === 0) {
fontList.unshift(_this.activeFont);
// download the previews up to the given index and apply them to the list entries
for (var i = this.previewIndex; i < downloadIndexMax; i += 1) {
checkPreviewFont(this.fonts[i], this.options.variants);
var style = '\n\t\t\t\t.font-' + this.fonts[i].family.replace(/\s+/g, '-').toLowerCase() + ' {\n\t\t\t\t\tfont-family: "' + this.fonts[i].family + '";\n\t\t\t\t}\n\t\t\t';
this.stylesheet.appendChild(document.createTextNode(style));
}
// 'limit' parameter (limit font list size)
if (_this.options.limit) {
fontList = fontList.slice(0, _this.options.limit);
if (downloadIndexMax > this.previewIndex) {
this.previewIndex = downloadIndexMax;
}
}
}]);
return FontHandler;
}();
// 'sort' parameter (list is already sorted by popularity)
if (_this.options.sort === 'alphabetical') {
fontList = fontList.sort(function (fontA, fontB) {
return fontA.family.localeCompare(fontB.family);
});
}
function styleInject(css, ref) {
if ( ref === void 0 ) ref = {};
var insertAt = ref.insertAt;
// save modified font list
_this.fonts = fontList;
if (!css || typeof document === 'undefined') { return; }
// download previews for the first 10 fonts in the list
_this.downloadPreviews(10);
});
}
var head = document.head || document.getElementsByTagName('head')[0];
var style = document.createElement('style');
style.type = 'text/css';
/**
* Set the font with the given font list index as the active one, download (if necessary) and
* apply it
*/
if (insertAt === 'top') {
if (head.firstChild) {
head.insertBefore(style, head.firstChild);
} else {
head.appendChild(style);
}
} else {
head.appendChild(style);
}
}, {
key: 'changeActiveFont',
value: function changeActiveFont(index) {
var previousFont = this.activeFont.family;
if (style.styleSheet) {
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
}
// change font
this.activeFont = this.fonts[index];
var css = "@charset \"UTF-8\";\ndiv[id^=\"font-picker\"] {\n position: relative;\n display: inline-block;\n width: 200px;\n box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.2); }\n div[id^=\"font-picker\"] * {\n box-sizing: border-box; }\n div[id^=\"font-picker\"] p {\n margin: 0;\n padding: 0; }\n div[id^=\"font-picker\"] a {\n color: inherit;\n cursor: pointer;\n outline: none;\n text-decoration: none; }\n div[id^=\"font-picker\"] .dropdown-button {\n height: 35px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0 10px;\n background-color: #CBCBCB; }\n div[id^=\"font-picker\"] .dropdown-button:hover, div[id^=\"font-picker\"] .dropdown-button.expanded, div[id^=\"font-picker\"] .dropdown-button:focus {\n background-color: #bebebe; }\n div[id^=\"font-picker\"] .dropdown-button .dropdown-font-name {\n overflow: hidden;\n white-space: nowrap; }\n div[id^=\"font-picker\"] .dropdown-icon {\n margin-left: 10px; }\n\n@keyframes spinner {\n to {\n transform: rotate(360deg); } }\n div[id^=\"font-picker\"] .dropdown-icon.loading:before {\n content: '';\n display: block;\n height: 10px;\n width: 10px;\n border-radius: 50%;\n border: 2px solid #b2b2b2;\n border-top-color: black;\n animation: spinner 0.6s linear infinite; }\n div[id^=\"font-picker\"] .dropdown-icon.finished:before {\n content: '';\n display: block;\n height: 0;\n width: 0;\n border-left: 5px solid transparent;\n border-right: 5px solid transparent;\n border-top: 6px solid black;\n transition: transform 0.3s;\n margin: 0 2px; }\n div[id^=\"font-picker\"] .dropdown-icon.error:before {\n content: '⚠'; }\n div[id^=\"font-picker\"] .dropdown-button.expanded .dropdown-icon.finished:before {\n transform: rotate(-180deg); }\n div[id^=\"font-picker\"] ul {\n position: absolute;\n z-index: 1;\n max-height: 0;\n width: 100%;\n overflow-x: hidden;\n overflow-y: auto;\n -webkit-overflow-scrolling: touch;\n margin: 0;\n padding: 0;\n background-color: #EAEAEA;\n box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.2);\n transition: 0.3s; }\n div[id^=\"font-picker\"] ul.expanded {\n max-height: 200px; }\n div[id^=\"font-picker\"] ul li {\n height: 35px;\n list-style: none; }\n div[id^=\"font-picker\"] ul li a {\n height: 100%;\n width: 100%;\n display: flex;\n align-items: center;\n padding: 0 10px;\n white-space: nowrap; }\n div[id^=\"font-picker\"] ul li a:hover, div[id^=\"font-picker\"] ul li a:focus {\n background-color: #dddddd; }\n div[id^=\"font-picker\"] ul li a.active-font {\n background-color: #d1d1d1; }\n";
styleInject(css);
// apply font and set fallback fonts
var fallbackFont = this.activeFont.category === 'handwriting' ? 'cursive' : this.activeFont.category;
var style = '\n\t\t\t.apply-font' + this.name + ' {\n\t\t\t\tfont-family: "' + this.activeFont.family + '", "' + previousFont + '", ' + fallbackFont + ';\n\t\t\t}\n\t\t';
this.stylesheet.replaceChild(document.createTextNode(style), this.stylesheet.childNodes[0]);
/**
* Font picker interface
* @param {string} apiKey (required) - Google API key (can be generated at
* https://developers.google.com/fonts/docs/developer_api)
* @param {string} defaultFont - Font that is selected on initialization (default: 'Open Sans')
* @param {Object} options - Object with additional (optional) parameters:
* @param {string} name - If you have multiple font pickers on your site, you need to give them
* unique names (which may only consist of letters and digits). These names must also be
* appended to the font picker's ID and the .apply-font class name.
* Example: if { name: 'main' }, then use #font-picker-main and .apply-font-main
* @param {string[]} families - If only specific fonts shall appear in the list, specify their
* names in an array
* @param {string[]} categories - Array of font categories
* Possible values: 'sans-serif', 'serif', 'display', 'handwriting', 'monospace' (default:
* all categories)
* @param {string[]} variants - Array of variants which the fonts must include and which will be
* downloaded; the first variant in the array will become the default variant (and will be
* used in the font picker and the .apply-font class)
* Example: ['regular', 'italic', '700', '700italic'] (default: ['regular'])
* @param {number} limit - Maximum number of fonts to be displayed in the list (the least popular
* fonts will be omitted; default: 100)
* @param {string} sort - Sorting attribute for the font list
* Possible values: 'alphabetical' (default), 'popularity'
* @param {function} onChange - Function which is executed whenever the user changes the active
* font and its stylesheet finishes downloading
*/
// download font (if necessary)
checkFullFont(this.activeFont, this.options.variants, this.onChange);
}
var FontPicker = function () {
function FontPicker(apiKey, defaultFont) {
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var onChange = arguments[3];
classCallCheck(this, FontPicker);
/**
* Download font previews for the list entries up to the given index
*/
// parameter validation
if (!apiKey || typeof apiKey !== 'string') {
throw Error('apiKey parameter is not a string or missing');
}
if (defaultFont && typeof defaultFont !== 'string') {
throw Error('defaultFont parameter is not a string');
}
if ((typeof options === 'undefined' ? 'undefined' : _typeof(options)) !== 'object') {
throw Error('options parameter is not an object');
}
if (options.name) {
if (typeof options.name !== 'string') {
throw Error('options.name parameter is not a string');
}
if (options.name.match(/[^0-9a-z]/i)) {
throw Error('options.name may only contain letters and digits');
}
}
if (options.families && !(options.families instanceof Array)) {
throw Error('options.families parameter is not an array');
}
if (options.categories && !(options.categories instanceof Array)) {
throw Error('options.categories parameter is not an array');
}
if (options.variants && !(options.variants instanceof Array)) {
throw Error('options.variants parameter is not an array');
}
if (options.limit && typeof options.limit !== 'number') {
throw Error('options.limit parameter is not a number');
}
if (options.sort && typeof options.sort !== 'string') {
throw Error('options.sort parameter is not a string');
}
if (onChange && typeof onChange !== 'function') {
throw Error('onChange is not a function');
}
}, {
key: 'downloadPreviews',
value: function downloadPreviews(downloadIndex) {
// stop at the end of the font list
var downloadIndexMax = void 0;
if (downloadIndex > this.fonts.length) {
downloadIndexMax = this.fonts.length;
// default parameters
var newDefaultFont = defaultFont || 'Open Sans';
var newOptions = options;
if (options.name) {
this.name = '-' + options.name;
} else {
downloadIndexMax = downloadIndex;
this.name = '';
}
// download the previews up to the given index and apply them to the list entries
for (var i = this.previewIndex; i < downloadIndexMax; i += 1) {
checkPreviewFont(this.fonts[i], this.options.variants);
var style = '\n\t\t\t\t.font-' + this.fonts[i].family.replace(/\s+/g, '-').toLowerCase() + ' {\n\t\t\t\t\tfont-family: "' + this.fonts[i].family + '";\n\t\t\t\t}\n\t\t\t';
this.stylesheet.appendChild(document.createTextNode(style));
newOptions.name = this.name;
if (!options.limit) {
newOptions.limit = 100;
}
if (downloadIndexMax > this.previewIndex) {
this.previewIndex = downloadIndexMax;
if (!options.variants) {
newOptions.variants = ['regular'];
}
}
}]);
return FontHandler;
}();
if (!options.sort) {
newOptions.sort = 'alphabetical';
}
var css = "@charset \"UTF-8\";\ndiv[id^=\"font-picker\"] {\n display: inline-block;\n width: 200px;\n position: relative;\n box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.2); }\n div[id^=\"font-picker\"] * {\n box-sizing: border-box; }\n div[id^=\"font-picker\"] p {\n margin: 0;\n padding: 0; }\n div[id^=\"font-picker\"] a {\n color: inherit;\n text-decoration: none;\n cursor: pointer;\n outline: none; }\n div[id^=\"font-picker\"] .dropdown-button {\n height: 35px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0 10px;\n background-color: #CBCBCB; }\n div[id^=\"font-picker\"] .dropdown-button:hover, div[id^=\"font-picker\"] .dropdown-button.expanded, div[id^=\"font-picker\"] .dropdown-button:focus {\n background-color: #bebebe; }\n\n@keyframes spinner {\n to {\n transform: rotate(360deg); } }\n div[id^=\"font-picker\"] .dropdown-button .dropdown-icon.loading:before {\n content: '';\n display: block;\n height: 10px;\n width: 10px;\n border-radius: 50%;\n border: 2px solid #b2b2b2;\n border-top-color: black;\n animation: spinner 0.6s linear infinite; }\n div[id^=\"font-picker\"] .dropdown-button .dropdown-icon.finished:before {\n content: '▾'; }\n div[id^=\"font-picker\"] .dropdown-button .dropdown-icon.error:before {\n content: '⚠'; }\n div[id^=\"font-picker\"] ul {\n max-height: 0;\n width: 100%;\n position: absolute;\n z-index: 1;\n overflow-x: hidden;\n overflow-y: auto;\n -webkit-overflow-scrolling: touch;\n margin: 0;\n padding: 0;\n background-color: #EAEAEA;\n transition: 0.3s;\n box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.2); }\n div[id^=\"font-picker\"] ul.expanded {\n max-height: 200px; }\n div[id^=\"font-picker\"] ul li {\n height: 35px;\n list-style: none; }\n div[id^=\"font-picker\"] ul li a {\n height: 100%;\n display: flex;\n align-items: center;\n padding: 0 10px;\n white-space: nowrap;\n width: 100%; }\n div[id^=\"font-picker\"] ul li a:hover, div[id^=\"font-picker\"] ul li a:focus {\n background-color: #dddddd; }\n div[id^=\"font-picker\"] ul li a.active-font {\n background-color: #d1d1d1; }\n";
__$$styleInject(css);
// initialize FontHandler
this.fontHandler = new FontHandler(apiKey, newDefaultFont, newOptions, onChange);
/**
* Font picker interface
* @param {string} apiKey (required) - Google API key (can be generated at
* https://developers.google.com/fonts/docs/developer_api)
* @param {string} defaultFont - Font that is selected on initialization (default: 'Open Sans')
* @param {Object} options - Object with additional (optional) parameters:
* @param {string} name - If you have multiple font pickers on your site, you need to give them
* unique names (which may only consist of letters and digits). These names must also be
* appended to the font picker's ID and the .apply-font class name.
* Example: if { name: 'main' }, then use #font-picker-main and .apply-font-main
* @param {string[]} families - If only specific fonts shall appear in the list, specify their
* names in an array
* @param {string[]} categories - Array of font categories
* Possible values: 'sans-serif', 'serif', 'display', 'handwriting', 'monospace' (default:
* all categories)
* @param {string[]} variants - Array of variants which the fonts must include and which will be
* downloaded; the first variant in the array will become the default variant (and will be
* used in the font picker and the .apply-font class)
* Example: ['regular', 'italic', '700', '700italic'] (default: ['regular'])
* @param {number} limit - Maximum number of fonts to be displayed in the list (the least popular
* fonts will be omitted; default: 100)
* @param {string} sort - Sorting attribute for the font list
* Possible values: 'alphabetical' (default), 'popularity'
* @param {function} onChange - Function which is executed whenever the user changes the active
* font and its stylesheet finishes downloading
*/
var FontPicker = function () {
function FontPicker(apiKey, defaultFont) {
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var onChange = arguments[3];
classCallCheck(this, FontPicker);
// parameter validation
if (!apiKey || typeof apiKey !== 'string') {
throw Error('apiKey parameter is not a string or missing');
// function bindings
this.closeEventListener = this.closeEventListener.bind(this);
}
if (defaultFont && typeof defaultFont !== 'string') {
throw Error('defaultFont parameter is not a string');
}
if ((typeof options === 'undefined' ? 'undefined' : _typeof(options)) !== 'object') {
throw Error('options parameter is not an object');
}
if (options.name) {
if (typeof options.name !== 'string') {
throw Error('options.name parameter is not a string');
}
if (options.name.match(/[^0-9a-z]/i)) {
throw Error('options.name may only contain letters and digits');
}
}
if (options.families && !(options.families instanceof Array)) {
throw Error('options.families parameter is not an array');
}
if (options.categories && !(options.categories instanceof Array)) {
throw Error('options.categories parameter is not an array');
}
if (options.variants && !(options.variants instanceof Array)) {
throw Error('options.variants parameter is not an array');
}
if (options.limit && typeof options.limit !== 'number') {
throw Error('options.limit parameter is not a number');
}
if (options.sort && typeof options.sort !== 'string') {
throw Error('options.sort parameter is not a string');
}
if (onChange && typeof onChange !== 'function') {
throw Error('onChange is not a function');
}
// default parameters
var newDefaultFont = defaultFont || 'Open Sans';
var newOptions = options;
if (options.name) {
this.name = '-' + options.name;
} else {
this.name = '';
}
newOptions.name = this.name;
if (!options.limit) {
newOptions.limit = 100;
}
if (!options.variants) {
newOptions.variants = ['regular'];
}
if (!options.sort) {
newOptions.sort = 'alphabetical';
}
/**
* Download list of available fonts and generate the font picker UI
*/
// initialize FontHandler
this.fontHandler = new FontHandler(apiKey, newDefaultFont, newOptions, onChange);
// function bindings
this.closeEventListener = this.closeEventListener.bind(this);
}
createClass(FontPicker, [{
key: 'init',
value: function init() {
var _this = this;
/**
* Download list of available fonts and generate the font picker UI
*/
this.expanded = false;
var fontPickerDiv = document.getElementById('font-picker' + this.name);
if (!fontPickerDiv) {
throw Error('Missing div with id="font-picker' + this.name + '"');
}
createClass(FontPicker, [{
key: 'init',
value: function init() {
var _this = this;
// HTML for dropdown button (name of active font and dropdown arrow)
this.dropdownButton = document.createElement('a');
this.dropdownButton.classList.add('dropdown-button');
this.dropdownButton.role = 'button';
this.dropdownButton.tabIndex = 0;
this.dropdownButton.onclick = function () {
return _this.toggleExpanded();
};
this.dropdownButton.onkeypress = function () {
return _this.toggleExpanded();
};
fontPickerDiv.appendChild(this.dropdownButton);
// name of selected font
this.dropdownFont = document.createElement('p');
this.dropdownFont.innerHTML = this.fontHandler.activeFont.family;
this.dropdownFont.classList.add('dropdown-font-name');
this.dropdownButton.append(this.dropdownFont);
// dropdown icon (possible classes/states: 'loading', 'finished', 'error')
var dropdownIcon = document.createElement('p');
dropdownIcon.classList.add('dropdown-icon', 'loading');
this.dropdownButton.append(dropdownIcon);
this.expanded = false;
// HTML for font list
this.ul = document.createElement('ul');
var fontPickerDiv = document.getElementById('font-picker' + this.name);
if (!fontPickerDiv) {
throw Error('Missing div with id="font-picker' + this.name + '"');
}
// fetch font list, display dropdown arrow if successful
this.fontHandler.init().then(function () {
dropdownIcon.classList.remove('loading');
dropdownIcon.classList.add('finished');
// HTML for dropdown button (name of active font and dropdown arrow)
this.dropdownButton = document.createElement('a');
this.dropdownButton.classList.add('dropdown-button');
this.dropdownButton.role = 'button';
this.dropdownButton.tabIndex = 0;
this.dropdownButton.onclick = function () {
return _this.toggleExpanded();
};
this.dropdownButton.onkeypress = function () {
return _this.toggleExpanded();
};
// HTML for font list entries
_this.ul.onscroll = function () {
return _this.onScroll();
}; // download font previews on scroll
this.dropdownFont = document.createElement('p');
this.dropdownFont.innerHTML = this.fontHandler.activeFont.family;
var _loop = function _loop(i) {
var li = document.createElement('li');
var a = document.createElement('a');
this.dropdownButton.append(this.dropdownFont);
fontPickerDiv.appendChild(this.dropdownButton);
var dropdownIcon = document.createElement('p');
dropdownIcon.classList.add('dropdown-icon', 'loading');
this.dropdownButton.append(dropdownIcon);
// write font name in the corresponding font, set onclick listener
a.innerHTML = _this.fontHandler.fonts[i].family;
a.classList.add('font-' + _this.fontHandler.fonts[i].family.replace(/\s+/g, '-').toLowerCase());
a.role = 'button';
a.tabIndex = 0;
a.onclick = function () {
_this.toggleExpanded(); // collapse font list
_this.selectFont(i); // make font with index i active
};
a.onkeypress = function () {
_this.toggleExpanded(); // collapse font list
_this.selectFont(i); // make font with index i active
};
li.appendChild(a);
// HTML for font list
this.ul = document.createElement('ul');
// if active font: highlight it and save reference
if (_this.fontHandler.fonts[i].family === _this.fontHandler.activeFont.family) {
a.classList.add('active-font');
_this.activeFontA = a;
}
// fetch font list, display dropdown arrow if successful
this.fontHandler.init().then(function () {
dropdownIcon.classList.remove('loading');
dropdownIcon.classList.add('finished');
// HTML for font list entries
_this.ul.onscroll = function () {
return _this.onScroll();
}; // download font previews on scroll
var _loop = function _loop(i) {
var li = document.createElement('li');
var a = document.createElement('a');
// write font name in the corresponding font, set onclick listener
a.innerHTML = _this.fontHandler.fonts[i].family;
a.classList.add('font-' + _this.fontHandler.fonts[i].family.replace(/\s+/g, '-').toLowerCase());
a.role = 'button';
a.tabIndex = 0;
a.onclick = function () {
_this.toggleExpanded(); // collapse font list
_this.selectFont(i); // make font with index i active
_this.ul.appendChild(li);
};
a.onkeypress = function () {
_this.toggleExpanded(); // collapse font list
_this.selectFont(i); // make font with index i active
};
li.appendChild(a);
// if active font: highlight it and save reference
if (_this.fontHandler.fonts[i].family === _this.fontHandler.activeFont.family) {
a.classList.add('active-font');
_this.activeFontA = a;
for (var i = 0; i < _this.fontHandler.fonts.length; i += 1) {
_loop(i);
}
fontPickerDiv.appendChild(_this.ul);
}).catch(function (err) {
dropdownIcon.classList.remove('loading');
dropdownIcon.classList.add('error');
var errMessage = 'Error trying to fetch the list of available fonts';
console.error(errMessage);
console.error(err);
fontPickerDiv.title = errMessage;
});
}
_this.ul.appendChild(li);
};
/**
* Return the object of the currently selected font
*/
for (var i = 0; i < _this.fontHandler.fonts.length; i += 1) {
_loop(i);
}
fontPickerDiv.appendChild(_this.ul);
}).catch(function (err) {
dropdownIcon.classList.remove('loading');
dropdownIcon.classList.add('error');
var errMessage = 'Error trying to fetch the list of available fonts';
console.error(errMessage);
console.error(err);
fontPickerDiv.title = errMessage;
});
}
}, {
key: 'getActiveFont',
value: function getActiveFont() {
return this.fontHandler.activeFont;
}
/**
* Return the object of the currently selected font
*/
/**
* EventListener for closing the font picker when clicking anywhere outside it
*/
}, {
key: 'getActiveFont',
value: function getActiveFont() {
return this.fontHandler.activeFont;
}
}, {
key: 'closeEventListener',
value: function closeEventListener(e) {
var targetElement = e.target; // clicked element
/**
* EventListener for closing the font picker when clicking anywhere outside it
*/
do {
if (targetElement === document.getElementById('font-picker' + this.name)) {
// click inside font picker
return;
}
// move up the DOM
targetElement = targetElement.parentNode;
} while (targetElement);
}, {
key: 'closeEventListener',
value: function closeEventListener(e) {
var targetElement = e.target; // clicked element
// click outside font picker
this.toggleExpanded();
}
do {
if (targetElement === document.getElementById('font-picker' + this.name)) {
// click inside font picker
return;
}
// move up the DOM
targetElement = targetElement.parentNode;
} while (targetElement);
/**
* Download the font previews for all visible font entries and the five after them
*/
// click outside font picker
this.toggleExpanded();
}
}, {
key: 'onScroll',
value: function onScroll() {
var elementHeight = this.ul.scrollHeight / this.fontHandler.fonts.length;
var downloadIndex = Math.ceil((this.ul.scrollTop + this.ul.clientHeight) / elementHeight);
this.fontHandler.downloadPreviews(downloadIndex + 5);
}
/**
* Download the font previews for all visible font entries and the five after them
*/
/**
* Set the font with the given font list index as the active one and highlight it in the list
*/
}, {
key: 'onScroll',
value: function onScroll() {
var elementHeight = this.ul.scrollHeight / this.fontHandler.fonts.length;
var downloadIndex = Math.ceil((this.ul.scrollTop + this.ul.clientHeight) / elementHeight);
this.fontHandler.downloadPreviews(downloadIndex + 5);
}
}, {
key: 'selectFont',
value: function selectFont(index) {
// change font
this.fontHandler.changeActiveFont(index);
/**
* Set the font with the given font list index as the active one and highlight it in the list
*/
// write new font name in dropdown button
this.dropdownFont.innerHTML = this.fontHandler.activeFont.family;
}, {
key: 'selectFont',
value: function selectFont(index) {
// change font
this.fontHandler.changeActiveFont(index);
// highlight new active font
this.activeFontA.classList.remove('active-font');
this.activeFontA = this.ul.getElementsByTagName('li')[index].firstChild;
this.activeFontA.classList.add('active-font');
}
// write new font name in dropdown button
this.dropdownFont.innerHTML = this.fontHandler.activeFont.family;
/**
* Expand/collapse the picker's font list
*/
// highlight new active font
this.activeFontA.classList.remove('active-font');
this.activeFontA = this.ul.getElementsByTagName('li')[index].firstChild;
this.activeFontA.classList.add('active-font');
}
}, {
key: 'toggleExpanded',
value: function toggleExpanded() {
if (this.expanded) {
this.expanded = false;
this.dropdownButton.classList.remove('expanded');
this.ul.classList.remove('expanded');
document.removeEventListener('click', this.closeEventListener);
} else {
this.expanded = true;
this.dropdownButton.classList.add('expanded');
this.ul.classList.add('expanded');
document.addEventListener('click', this.closeEventListener);
}
}
}]);
return FontPicker;
}();
/**
* Expand/collapse the picker's font list
*/
}, {
key: 'toggleExpanded',
value: function toggleExpanded() {
if (this.expanded) {
this.expanded = false;
this.dropdownButton.classList.remove('expanded');
this.ul.classList.remove('expanded');
document.removeEventListener('click', this.closeEventListener);
} else {
this.expanded = true;
this.dropdownButton.classList.add('expanded');
this.ul.classList.add('expanded');
document.addEventListener('click', this.closeEventListener);
}
}
}]);
return FontPicker;
}();
return FontPicker;
})));
{
"name": "font-picker",
"version": "1.2.1",
"version": "1.3.0",
"description": "Font picker component for previewing, selecting, and downloading Google Fonts",

@@ -19,3 +19,3 @@ "keywords": [

"build": "rollup -c",
"lint": "eslint --ext .js src",
"lint": "eslint .",
"precommit": "npm run lint",

@@ -28,11 +28,11 @@ "prepush": "npm run lint"

"babel-preset-env": "^1.6.1",
"eslint": "^4.15.0",
"eslint": "^4.19.1",
"eslint-config-airbnb-base": "^12.1.0",
"eslint-plugin-import": "^2.8.0",
"eslint-plugin-import": "^2.9.0",
"husky": "^0.14.3",
"node-sass": "^4.7.2",
"postcss": "^6.0.16",
"rollup": "^0.54.0",
"node-sass": "^4.8.3",
"postcss": "^6.0.21",
"rollup": "^0.57.1",
"rollup-plugin-babel": "^3.0.3",
"rollup-plugin-postcss": "^1.2.5"
"rollup-plugin-postcss": "^1.4.0"
},

@@ -39,0 +39,0 @@ "files": [

@@ -17,3 +17,3 @@ # Font Picker

<p align="center">
<img src=".github/demo.gif" width=800 alt="Demo">
<img src=".github/demo.gif" width=700 alt="Demo">
</p>

@@ -114,2 +114,2 @@

* `npm start` to generate the library bundle using [Rollup](https://github.com/rollup/rollup) (in the `lib` directory)
* See the font picker in action by opening the [`demo/index.html`](demo/index.html) file
* See the font picker in action by opening the [`demo/index.html`](demo/index.html) file
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc