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

scribe-plugin-span-style

Package Overview
Dependencies
Maintainers
1
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

scribe-plugin-span-style - npm Package Compare versions

Comparing version 0.1.7 to 0.2.0

2

package.json
{
"name": "scribe-plugin-span-style",
"version": "0.1.7",
"version": "0.2.0",
"description": "A Scribe Plugin for adding inline styles through <span> elements",

@@ -5,0 +5,0 @@ "main": "src/scribe-plugin-span-style.js",

# scribe-plugin-span-style
A Scribe Plugin for adding inline styles through &lt;span> elements
A Scribe Plugin for adding inline styles through `<span>` elements.
*The description `<span>` is probably not accurate anymore. From 0.2.0 on, the plugin will now use any available tags it can find, that includes `<b>`,`<i>`,`<strong>`,`<em>`,`<li>`,`etc`. Only if it is not able to use one of those will it create a `<span>` to wrap the selection into.*
**Example:**

@@ -21,6 +23,30 @@ ```javascript

// ...............
let command = scribe.getCommand('fontSize');
let originalRange = scribe.selection.range;
// Some more code that causes the selection to lose focus...
command.execute({
value: '24px',
range: originalRange
});
```
**Requirements**
- Scribe should always put `<p>` or `<div>` tags as the root nodes of your lines
**Known Issues / Todo**
- Multi-Line selections do not get styled properly
- Safari does not wrap the text into the spans correctly, scribe itself does not support Safari, so need to think about a workaround
- Heavily nested `<span>` nodes sometimes do not get unwrapped properly
- Untested performance on a lot of text ("logical" algorithm might be a bit slow for that)
- Untested in older browsers
**Changelog**
*v0.2.0*
- Complete code makeover
- Multi-line selections are supported mostly reliable now (even cross browser)
- Implemented "logical" algorithm to keep the nesting as clean as possible
- Supports the passing of an object as value now (`{value: '12px', range: originalRange}`) to support input elements that might cause losing the selection before execution of the command

@@ -9,52 +9,203 @@ 'use strict';

var clearChildStyles = function(root) {
if (typeof root === 'undefined' || typeof root.childNodes === 'undefined') return;
if (typeof root === 'undefined' || typeof root.childNodes === 'undefined' || typeof root.nodeType === 'undefined' || root.nodeType === 3) return;
for (var i in root.childNodes) {
var child = root.childNodes[i];
clearChildStyles(root.childNodes[i]);
}
clearChildStyles(child.childNodes);
root.style[styleName] = '';
if (root.nodeName === 'SPAN') {
if (!root.getAttribute('style')) {
scribe.node.unwrap(root.parentNode, root);
}
}
};
if (child.nodeName === 'SPAN') {
child.style[styleName] = '';
if (!child.getAttribute('style')) {
scribe.node.unwrap(root, child);
}
var nextNode = function(node) {
if (node.hasChildNodes()) {
return node.firstChild;
}
else {
while (node && !node.nextSibling) {
node = node.parentNode;
}
if (!node) {
return null;
}
return node.nextSibling;
}
};
var getRangeSelectedNodes = function(range) {
var node = range.startContainer;
var endNode = range.endContainer;
if (node === endNode) {
return [node];
}
var rangeNodes = [];
while (node && node !== endNode) {
rangeNodes.push(node = nextNode(node));
}
node = range.startContainer;
while (node && node !== range.commonAncestorContainer) {
rangeNodes.unshift(node);
node = node.parentNode;
}
return rangeNodes;
};
var getLogicalCombinations = function(fragments) {
var p = [];
for (var i=0;i<fragments.length;i++) {
var f = fragments[i];
p.push(f);
for (var x = i+1;x<fragments.length;x++) {
f += fragments[x];
p.push(f);
}
}
return p;
};
var styleLogicalFragments = function(root, fragments, value) {
if (typeof root === 'undefined' || root.nodeType === 3) return;
if (fragments.indexOf(root.textContent) > -1) {
clearChildStyles(root);
root.style[styleName] = value;
}
else if (root.childNodes) {
for (var i in root.childNodes) {
styleLogicalFragments(root.childNodes[i], fragments, value);
}
}
};
var normalizeNodes = function(root) {
if (typeof root === 'undefined' || root.nodeType === 3 || typeof root.nodeType === 'undefined') return;
if (root.childNodes) {
for (var i in root.childNodes) {
normalizeNodes(root.childNodes[i]);
}
}
root.normalize();
};
var createSubRange = function(node, start, end) {
var subRange = document.createRange();
subRange.setStart(node, start);
subRange.setEnd(node, end);
return subRange;
};
spanStyleCommand.execute = function(value) {
var selection = new scribe.api.Selection();
var range = selection.range;
if (typeof value.range !== 'undefined') {
// Sometimes a click on the firing element (button etc.) causes the selection to lose focus
// If needed a range from the original selection can be passed alongside the value
range = value.range;
value = value.value;
}
if (range.collapsed) {
// Trying to style a collapsed range makes no sense
return;
}
scribe.transactionManager.run(function() {
var selection = new scribe.api.Selection();
var range = selection.range;
var nodes = getRangeSelectedNodes(range);
// Get Range Text & Current Node Text
var selectedHtmlDocumentFragment = range.extractContents();
var tDiv = document.createElement('div');
tDiv.appendChild(selectedHtmlDocumentFragment.cloneNode(true));
var rangeText = tDiv.innerText;
var nodeText = selection.selection.focusNode.textContent;
if (nodes.length === 1) {
var node = nodes[0];
var parent = node.parentNode;
if (node.nodeType === 3) {
if (parent.textContent === range.startContainer.textContent.substr(range.startOffset,range.endOffset)) {
clearChildStyles(parent);
parent.style[styleName] = value;
}
else {
var span = document.createElement('span');
span.style[styleName] = value;
// Determine if we need a new node
var isNewNode = true;
if (nodeText === rangeText) {
isNewNode = (selection.selection.focusNode.parentElement.nodeName === 'SPAN') ? false : true;
range.surroundContents(span);
}
}
}
else {
var startOffset = range.startOffset;
var startContainer = range.startContainer;
var endOffset = range.endOffset;
var endContainer = range.endContainer;
// Create / Get SPAN
var span = (!isNewNode) ? selection.selection.focusNode.parentElement : document.createElement('span');
span.appendChild(selectedHtmlDocumentFragment);
if (isNewNode) {
range.insertNode(span);
range.selectNode(span);
var fragments = [];
for (var i in nodes) {
var node = nodes[i];
if (node.nodeType === 3) {
if (node === range.startContainer) {
fragments.push(node.textContent.substr(range.startOffset));
}
else if (node === range.endContainer) {
fragments.push(node.textContent.substr(0,range.endOffset));
}
else {
fragments.push(node.textContent);
}
}
}
fragments = getLogicalCombinations(fragments);
if (scribe.el.childNodes) {
for (var i in scribe.el.childNodes) {
var rootNode = scribe.el.childNodes[i];
styleLogicalFragments(rootNode, fragments, value);
}
}
if (startOffset > 0) {
var parent = startContainer.parentNode;
if (parent.innerText === startContainer.textContent.substr(startOffset)) {
clearChildStyles(parent);
parent.style[styleName] = value;
}
else {
var subRange = createSubRange(startContainer, startOffset, startContainer.textContent.length);
var span = document.createElement('span');
span.style[styleName] = value;
subRange.surroundContents(span);
}
}
if (endOffset < endContainer.textContent.length) {
var parent = endContainer.parentNode;
if (parent.innerText === endContainer.textContent.substr(0,endOffset)) {
clearChildStyles(parent);
parent.style[styleName] = value;
}
else {
var subRange = createSubRange(endContainer, 0, endOffset);
var span = document.createElement('span');
span.style[styleName] = value;
subRange.surroundContents(span);
}
}
}
// Clear Setting for children
clearChildStyles(span);
// Apply new Font-Size
span.style[styleName] = value;
// Re-apply the range
selection.selection.removeAllRanges();
selection.selection.addRange(range);
if (scribe.el.childNodes) {
normalizeNodes(scribe.el);
}
});

@@ -65,6 +216,5 @@ };

var selection = new scribe.api.Selection();
var that = this;
return !!selection.getContaining(function(node) {
if (node.style) {
return (node.nodeName === that.nodeName && node.style[styleName] !== '');
return (node.style[styleName] !== '');
}

@@ -71,0 +221,0 @@ return false;

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