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

clamp-js

Package Overview
Dependencies
Maintainers
1
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

clamp-js - npm Package Compare versions

Comparing version 0.6.3 to 0.7.0

406

clamp.js
/*!
* Clamp.js 0.6.2-bw
* Clamp.js 0.7.0
*

@@ -9,212 +9,264 @@ * Copyright 2011-2013, Joseph Schmitt http://joe.sh

(function(){
/**
* Clamps a text node.
* @param {HTMLElement} element. Element containing the text node to clamp.
* @param {Object} options. Options to pass to the clamper.
*/
function clamp(element, options) {
options = options || {};
(function(root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define([], factory);
} else if (typeof exports === 'object') {
// Node, CommonJS-like
module.exports = factory();
} else {
// Browser globals
root.$clamp = factory();
}
}(this, function() {
/**
* Clamps a text node.
* @param {HTMLElement} element. Element containing the text node to clamp.
* @param {Object} options. Options to pass to the clamper.
*/
function clamp(element, options) {
options = options || {};
var self = this,
win = window,
opt = {
clamp: options.clamp || 2,
useNativeClamp: typeof(options.useNativeClamp) != 'undefined' ? options.useNativeClamp : true,
truncationChar: options.truncationChar || '…',
removeTrailingChars: options.removeTrailingChars || ',.;:!?-'
},
var self = this,
win = window,
opt = {
clamp: options.clamp || 2,
useNativeClamp: typeof(options.useNativeClamp) != 'undefined' ? options.useNativeClamp : true,
splitOnChars: options.splitOnChars || ['.', '-', '–', '—', ' '], //Split on sentences (periods), hypens, en-dashes, em-dashes, and words (spaces).
animate: options.animate || false,
truncationChar: options.truncationChar || '…',
truncationHTML: options.truncationHTML
},
sty = element.style,
originalText = element.innerHTML,
sty = element.style,
originalText = element.innerHTML,
supportsNativeClamp = typeof(element.style.webkitLineClamp) != 'undefined',
clampValue = opt.clamp,
isCSSValue = clampValue.indexOf && (clampValue.indexOf('px') > -1 || clampValue.indexOf('em') > -1);
supportsNativeClamp = typeof(element.style.webkitLineClamp) != 'undefined',
clampValue = opt.clamp,
isCSSValue = clampValue.indexOf && (clampValue.indexOf('px') > -1 || clampValue.indexOf('em') > -1),
truncationHTMLContainer;
// UTILITY FUNCTIONS __________________________________________________________
if (opt.truncationHTML) {
truncationHTMLContainer = document.createElement('span');
truncationHTMLContainer.innerHTML = opt.truncationHTML;
}
/**
* Return the current style for an element.
* @param {HTMLElement} elem The element to compute.
* @param {string} prop The style property.
* @returns {number}
*/
function computeStyle(elem, prop) {
var getComputedStyle;
if (win.getComputedStyle) {
getComputedStyle = win.getComputedStyle;
} else {
getComputedStyle = function (el, pseudo) {
this.el = el;
this.getPropertyValue = function (prop) {
var re = /(\-([a-z]){1})/g;
if (prop == 'float') prop = 'styleFloat';
if (re.test(prop)) {
prop = prop.replace(re, function () {
return arguments[2].toUpperCase();
});
}
return el.currentStyle && el.currentStyle[prop] ? el.currentStyle[prop] : null;
};
return this;
};
}
return getComputedStyle(elem, null).getPropertyValue(prop);
}
// UTILITY FUNCTIONS __________________________________________________________
/**
* Returns the maximum number of lines of text that should be rendered based
* on the current height of the element and the line-height of the text.
*/
function getMaxLines(height) {
var availHeight = height || getElemHeight(element),
lineHeight = getLineHeight(element);
return Math.max(Math.floor(availHeight/lineHeight), 0);
}
/**
* Returns the maximum height a given element should have based on the line-
* height of the text and the given clamp value.
*/
function getMaxHeight(clmp) {
var lineHeight = getLineHeight(element);
return lineHeight * clmp;
}
/**
* Returns the line-height of an element as an integer.
*/
function getLineHeight(elem) {
var lh = computeStyle(elem, 'line-height');
if (lh == 'normal') {
// Normal line heights vary from browser to browser. The spec recommends
// a value between 1.0 and 1.2 of the font size. Using 1.1 to split the diff.
lh = parseInt(computeStyle(elem, 'font-size'), 10) * 1.2;
/**
* Return the current style for an element.
* @param {HTMLElement} elem The element to compute.
* @param {string} prop The style property.
* @returns {number}
*/
function computeStyle(elem, prop) {
if (!win.getComputedStyle) {
win.getComputedStyle = function(el, pseudo) {
this.el = el;
this.getPropertyValue = function(prop) {
var re = /(\-([a-z]){1})/g;
if (prop == 'float') prop = 'styleFloat';
if (re.test(prop)) {
prop = prop.replace(re, function() {
return arguments[2].toUpperCase();
});
}
return parseInt(lh, 10);
}
return el.currentStyle && el.currentStyle[prop] ? el.currentStyle[prop] : null;
};
return this;
};
}
/**
* Returns the height of an element as an integer (max of scroll/offset/client).
* Note: inline elements return 0 for scrollHeight and clientHeight
*/
function getElemHeight(elem) {
return Math.max(elem.scrollHeight, elem.offsetHeight, elem.clientHeight);
}
return win.getComputedStyle(elem, null).getPropertyValue(prop);
}
/**
* Gets an element's last text node. This will remove empty elements from the end.
*/
function getLastTextNode(elem) {
// if we have children, search inside the last one
if (elem.lastChild) {
return getLastTextNode(elem.lastChild);
}
/**
* Returns the maximum number of lines of text that should be rendered based
* on the current height of the element and the line-height of the text.
*/
function getMaxLines(height) {
var availHeight = height || element.clientHeight,
lineHeight = getLineHeight(element);
//we don't have children, and this is the root => we can't find anything here
if (elem == element) {
return null;
}
return Math.max(Math.floor(availHeight / lineHeight), 0);
}
// we don't have children, but this is not a text node, or it is empty => remove it and try again
if (elem.nodeType !== 3 || !elem.nodeValue.trim() || elem.nodeValue == opt.truncationChar) {
var parent = elem.parentNode;
parent.removeChild(elem);
return getLastTextNode(parent);
}
/**
* Returns the maximum height a given element should have based on the line-
* height of the text and the given clamp value.
*/
function getMaxHeight(clmp) {
var lineHeight = getLineHeight(element);
return lineHeight * clmp;
}
//we found a child of type text with actual content
return elem;
}
/**
* Returns the line-height of an element as an integer.
*/
function getLineHeight(elem) {
var lh = computeStyle(elem, 'line-height');
if (lh == 'normal') {
// Normal line heights vary from browser to browser. The spec recommends
// a value between 1.0 and 1.2 of the font size. Using 1.1 to split the diff.
lh = parseInt(computeStyle(elem, 'font-size')) * 1.2;
}
return parseInt(lh);
}
// MEAT AND POTATOES (MMMM, POTATOES...) ______________________________________
var splitOnChars = opt.splitOnChars.slice(0),
splitChar = splitOnChars[0],
chunks,
lastChunk;
// MEAT AND POTATOES (MMMM, POTATOES...) ______________________________________
/**
* Gets an element's last child. That may be another node or a node's contents.
*/
function getLastChild(elem) {
//Current element has children, need to go deeper and get last child as a text node
if (elem.lastChild.children && elem.lastChild.children.length > 0) {
return getLastChild(Array.prototype.slice.call(elem.children).pop());
}
//This is the absolute last child, a text node, but something's wrong with it. Remove it and keep trying
else if (!elem.lastChild || !elem.lastChild.nodeValue || elem.lastChild.nodeValue === '' || elem.lastChild.nodeValue == opt.truncationChar) {
elem.lastChild.parentNode.removeChild(elem.lastChild);
return getLastChild(element);
}
//This is the last child we want, return it
else {
return elem.lastChild;
}
}
/**
* Removes one character at a time from the text until its width or
* height is beneath the passed-in max param.
*/
function truncate(target, maxHeight) {
if (!maxHeight) {
return;
}
/**
* Does a binary search over the words in the elements text until it finds the last one that fits in the maximum height
*/
function truncate(target, maxHeight) {
if (!target || !maxHeight) {return;}
/**
* Resets global variables.
*/
function reset() {
splitOnChars = opt.splitOnChars.slice(0);
splitChar = splitOnChars[0];
chunks = null;
lastChunk = null;
}
var original = target.nodeValue.replace(opt.truncationChar, '');
var nodeValue = target.nodeValue.replace(opt.truncationChar, '');
var words = original.split(' ');
//Grab the next chunks
if (!chunks) {
//If there are more characters to try, grab the next one
if (splitOnChars.length > 0) {
splitChar = splitOnChars.shift();
}
//No characters to chunk by. Go character-by-character
else {
splitChar = '';
}
var start=0, end = words.length-1, mid=-1, m;
chunks = nodeValue.split(splitChar);
}
while (start <= end && end > 0) {
m = Math.floor((start+end)/2);
if (m == mid) {
break;
}
mid= m;
//If there are chunks left to remove, remove the last one and see if
// the nodeValue fits.
if (chunks.length > 1) {
// console.log('chunks', chunks);
lastChunk = chunks.pop();
// console.log('lastChunk', lastChunk);
applyEllipsis(target, chunks.join(splitChar));
}
//No more chunks can be removed using this character
else {
chunks = null;
}
applyEllipsis(target, words.slice(0, mid+1).join(' '));
//Insert the custom HTML before the truncation character
if (truncationHTMLContainer) {
target.nodeValue = target.nodeValue.replace(opt.truncationChar, '');
element.innerHTML = target.nodeValue + ' ' + truncationHTMLContainer.innerHTML + opt.truncationChar;
}
height = getElemHeight(element);
//Search produced valid chunks
if (chunks) {
//It fits
if (element.clientHeight <= maxHeight) {
//There's still more characters to try splitting on, not quite done yet
if (splitOnChars.length >= 0 && splitChar !== '') {
applyEllipsis(target, chunks.join(splitChar) + splitChar + lastChunk);
chunks = null;
}
//Finished!
else {
return element.innerHTML;
}
}
}
//No valid chunks produced
else {
//No valid chunks even when splitting by letter, time to move
//on to the next node
if (splitChar === '') {
applyEllipsis(target, '');
target = getLastChild(element);
if (height <= maxHeight) {
start = mid;
} else {
end = mid;
}
}
if (height > maxHeight) {
target.parentNode.removeChild(target);
truncate(getLastTextNode(element), maxHeight);
}
reset();
}
}
function applyEllipsis(elem, str) {
while (str.length && opt.removeTrailingChars.indexOf(str[str.length-1]) != -1) {
str = str.substring(0, str.length -1);
}
//If you get here it means still too big, let's keep truncating
if (opt.animate) {
setTimeout(function() {
truncate(target, maxHeight);
}, opt.animate === true ? 10 : opt.animate);
} else {
return truncate(target, maxHeight);
}
}
elem.nodeValue = str + opt.truncationChar;
}
function applyEllipsis(elem, str) {
elem.nodeValue = str + opt.truncationChar;
}
// CONSTRUCTOR ________________________________________________________________
// CONSTRUCTOR ________________________________________________________________
if (clampValue == 'auto') {
clampValue = getMaxLines();
}
else if (isCSSValue) {
clampValue = getMaxLines(parseInt(clampValue, 10));
}
if (clampValue == 'auto') {
clampValue = getMaxLines();
} else if (isCSSValue) {
clampValue = getMaxLines(parseInt(clampValue));
}
var clampedText;
if (supportsNativeClamp && opt.useNativeClamp) {
sty.overflow = 'hidden';
sty.textOverflow = 'ellipsis';
sty.webkitBoxOrient = 'vertical';
sty.display = '-webkit-box';
sty.webkitLineClamp = clampValue;
var clampedText;
if (supportsNativeClamp && opt.useNativeClamp) {
sty.overflow = 'hidden';
sty.textOverflow = 'ellipsis';
sty.webkitBoxOrient = 'vertical';
sty.display = '-webkit-box';
sty.webkitLineClamp = clampValue;
if (isCSSValue) {
sty.height = opt.clamp + 'px';
}
}
else {
var height = getMaxHeight(clampValue);
if (height < getElemHeight(element)) {
truncate(getLastTextNode(element), height);
clampedText = element.innerHTML;
}
}
return {
'original': originalText,
'clamped': clampedText
};
if (isCSSValue) {
sty.height = opt.clamp + 'px';
}
} else {
var height = getMaxHeight(clampValue);
if (height <= element.clientHeight) {
clampedText = truncate(getLastChild(element), height);
}
}
window.$clamp = clamp;
})();
return {
'original': originalText,
'clamped': clampedText
};
}
return clamp;
}));
{
"name": "clamp-js",
"version": "0.6.3",
"version": "0.7.0",
"license": "WTFPL",
"description": "Clamps (ie. cuts off) an HTML element's content by adding ellipsis to it if the content inside is too long.",
"author": {
"name": "Javier López",
"email": "xavi160@gmail.com"
},
"keywords": [
"clamp",
"ellipsis",
"multiline",
"browser"
],
"main": "clamp.js",
"devDependencies": {}
"repository": {
"type": "git",
"url": "https://github.com/xavi160/Clamp.js.git"
}
}

Sorry, the diff of this file is not supported yet

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