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

formatic

Package Overview
Dependencies
Maintainers
3
Versions
223
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

formatic - npm Package Compare versions

Comparing version 0.2.31 to 0.2.32

163

build/lib/components/fields/pretty-text2.js

@@ -10,2 +10,16 @@ 'use strict';

var PrettyTag = React.createClass({
displayName: 'PrettyTag',
render: function render() {
var classes = cx(_.extend({}, this.props.classes, { 'pretty-part': true }));
return React.createElement(
'span',
{ className: classes, onClick: this.props.onClick },
this.props.children
);
}
});
/*

@@ -20,16 +34,2 @@ Editor for tagged text. Renders text like "hello {{firstName}}"

Initially a read-only view using a simple div is shown.
IMPLEMENTATION NOTE:
To display the tags inside CodeMirror we are using CM's
specialCharPlaceholder feature, to replace special characters with
custom DOM nodes. This feature is designed for single character
replacements, not tags like 'firstName'. So we replace each tag
with an unused character from the Unicode private use area, and
tell CM to replace that with a DOM node display the tag label with
the pill box effect.
Is this evil? Perhaps a little, but delete, undo, redo, cut, copy
and paste of the tag pill boxes just work because CM treats them as
atomic single characters, and it's not much code on our part.
*/

@@ -42,2 +42,3 @@ module.exports = React.createClass({

componentDidMount: function componentDidMount() {
console.log('------- pt2');
this.createEditor();

@@ -52,22 +53,2 @@ },

}
// If they just typed in a tag like {{firstName}} we have to replace it
if (this.state.codeMirrorMode && this.codeMirror.getValue().match(/\{\{.+\}\}/)) {
// avoid recursive update cycle
this.updatingCodeMirror = true;
// get new encoded value for CodeMirror
var cmValue = this.codeMirror.getValue();
var decodedValue = this.state.translator.decodeValue(cmValue);
var encodedValue = this.state.translator.encodeValue(decodedValue);
// Grab the cursor so we can reset it.
// The new length of the CM value will be shorter after replacing a tag like {{firstName}}
// with a single special char, so adjust cursor position accordingly.
var cursor = this.codeMirror.getCursor();
cursor.ch -= cmValue.length - encodedValue.length;
this.codeMirror.setValue(encodedValue);
this.codeMirror.setCursor(cursor);
}
},

@@ -97,7 +78,6 @@

var nextState = {
replaceChoices: replaceChoices
replaceChoices: replaceChoices,
translator: TagTranslator(replaceChoices, this.props.config.humanize)
};
this.state.translator.addChoices(replaceChoices);
if (this.state.value !== nextProps.field.value && nextProps.field.value) {

@@ -111,9 +91,13 @@ nextState.value = nextProps.field.value;

handleChoiceSelection: function handleChoiceSelection(key) {
this.setState({ isChoicesOpen: false });
var pos = this.state.selectedTagPos;
var tag = '{{' + key + '}}';
var char = this.state.translator.encodeTag(key);
if (pos) {
this.codeMirror.replaceRange(tag, { line: pos.line, ch: pos.start }, { line: pos.line, ch: pos.stop });
} else {
this.codeMirror.replaceSelection(tag, 'end');
}
this.codeMirror.focus();
// put the cursor at the end of the inserted tag.
this.codeMirror.replaceSelection(char, 'end');
this.codeMirror.focus();
this.setState({ isChoicesOpen: false, selectedTagPos: null });
},

@@ -130,7 +114,9 @@

var tabIndex = field.tabIndex;
var textBoxClasses = cx(_.extend({}, this.props.classes, { 'pretty-text-box': true }));
var textBox = this.createTextBoxNode();
var insertBtn = config.createElement('insert-button', { ref: 'toggle', onClick: this.onToggleChoices }, 'Insert...');
var onInsertClick = function onInsertClick() {
this.setState({ selectedTagPos: null });
this.onToggleChoices();
};
var insertBtn = config.createElement('insert-button', { ref: 'toggle', onClick: onInsertClick.bind(this) }, 'Insert...');

@@ -146,4 +132,3 @@ var choices = config.createElement('choices', {

// Render read-only version. We are using pure HTML via dangerouslySetInnerHTML, to avoid
// the cost of the react nodes. This is probably a premature optimization.
// Render read-only version.
var element = React.createElement(

@@ -155,3 +140,3 @@ 'div',

{ className: textBoxClasses, tabIndex: tabIndex, onFocus: this.onFocusAction, onBlur: this.onBlurAction },
textBox
React.createElement('div', { ref: 'textBox', className: 'internal-text-wrapper' })
),

@@ -185,11 +170,2 @@ insertBtn,

createTextBoxNode: function createTextBoxNode() {
if (this.state.codeMirrorMode) {
return React.createElement('div', { ref: 'textBox', className: 'internal-text-wrapper' });
} else {
var html = this.state.translator.toHtml(this.state.value);
return React.createElement('div', { ref: 'textBox', className: 'internal-text-wrapper', dangerouslySetInnerHTML: { __html: html } });
}
},
createEditor: function createEditor() {

@@ -204,10 +180,6 @@ if (this.state.codeMirrorMode) {

createCodeMirrorEditor: function createCodeMirrorEditor() {
var cmValue = this.state.translator.encodeValue(this.state.value);
var options = {
lineWrapping: true,
tabindex: this.props.tabIndex,
value: cmValue,
specialChars: this.state.translator.specialCharsRegexp,
specialCharPlaceholder: this.createTagNode,
value: this.state.value,
extraKeys: {

@@ -223,4 +195,20 @@ Tab: false

this.codeMirror.on('change', this.onCodeMirrorChange);
this.tagCodeMirror();
},
tagCodeMirror: function tagCodeMirror() {
var positions = this.state.translator.getTagPositions(this.codeMirror.getValue());
var self = this;
var tagOps = function tagOps() {
positions.forEach(function (pos) {
var node = self.createTagNode(pos);
self.codeMirror.markText({ line: pos.line, ch: pos.start }, { line: pos.line, ch: pos.stop }, { replacedWith: node, handleMouseEvents: true });
});
};
this.codeMirror.operation(tagOps);
},
onCodeMirrorChange: function onCodeMirrorChange() {

@@ -233,10 +221,41 @@ if (this.updatingCodeMirror) {

var newValue = this.state.translator.decodeValue(this.codeMirror.getValue());
var newValue = this.codeMirror.getValue();
this.onChangeValue(newValue);
this.setState({ value: newValue });
this.tagCodeMirror();
},
getTagClasses: function getTagClasses(tag) {
var choice = _.find(this.state.replaceChoices, function (c) {
return c.value === tag;
});
return choice && choice.tagClasses || {};
},
createReadonlyEditor: function createReadonlyEditor() {
var textBoxNode = this.refs.textBox.getDOMNode();
textBoxNode.innerHTML = this.state.translator.toHtml(this.state.value);
var tokens = this.state.translator.tokenize(this.state.value);
var self = this;
var nodes = tokens.map(function (part) {
if (part.type === 'tag') {
var tagClasses = self.getTagClasses(part.value);
return React.createElement(
PrettyTag,
{ classes: tagClasses },
self.state.translator.getLabel(part.value)
);
}
return React.createElement(
'span',
null,
part.value
);
});
React.render(React.createElement(
'span',
null,
nodes
), textBoxNode);
},

@@ -257,11 +276,15 @@

// Create pill box style for display inside CM. For example
// '\ue000' becomes '<span class="tag>First Name</span>'
createTagNode: function createTagNode(char) {
createTagNode: function createTagNode(pos) {
var node = document.createElement('span');
var label = this.state.translator.decodeChar(char);
var label = this.state.translator.getLabel(pos.tag);
var tagClasses = this.getTagClasses(pos.tag);
var onTagClick = function onTagClick() {
this.setState({ selectedTagPos: pos });
this.onToggleChoices();
};
React.render(React.createElement(
'span',
{ className: 'pretty-part', onClick: this.onToggleChoices },
PrettyTag,
{ classes: tagClasses, onClick: onTagClick.bind(this) },
label

@@ -268,0 +291,0 @@ ), node);

'use strict';
// Constant for first unused special use character.
// See IMPLEMENTATION NOTE in pretty-text2.js.
var FIRST_SPECIAL_CHAR = 57344;
// regexp used to grep out tags like {{firstName}}
var TAGS_REGEXP = /\{\{(.+?)\}\}/g;
function buildChoicesMap(replaceChoices) {

@@ -22,95 +15,9 @@ var choices = {};

an encoded representation suitable for use in CodeMirror.
See IMPLEMENTATION NOTE in pretty-text2.js.
*/
function TagTranslator(replaceChoices, humanize) {
var nextCharCode = FIRST_SPECIAL_CHAR;
// Map of tag to label 'firstName' --> 'First Name'
var choices = {};
var choices = buildChoicesMap(replaceChoices);
// To help translate to and from the CM representation with the special
// characters, build two maps:
// - charToTagMap: special char to tag - i.e. { '\ue000': 'firstName' }
// - tagToCharMap: tag to special char, i.e. { firstName: '\ue000' }
var charToTagMap = {};
var tagToCharMap = {};
function addChoices(choicesArray) {
choices = buildChoicesMap(choicesArray);
Object.keys(choices).sort().forEach(function (tag) {
if (tagToCharMap[tag]) {
return; // we already have this tag mapped
}
var char = String.fromCharCode(nextCharCode++);
charToTagMap[char] = tag;
tagToCharMap[tag] = char;
});
}
addChoices(replaceChoices);
return {
specialCharsRegexp: /[\ue000-\uefff]/g,
addChoices: addChoices,
/*
Convert tag to encoded character. For example
'firstName' becomes '\ue000'.
*/
encodeTag: function encodeTag(tag) {
if (!tagToCharMap[tag]) {
var char = String.fromCharCode(nextCharCode++);
tagToCharMap[tag] = char;
charToTagMap[char] = tag;
}
return tagToCharMap[tag];
},
/*
Convert text value to encoded value for CodeMirror. For example
'hello {{firstName}}' becomes 'hello \ue000'
*/
encodeValue: function encodeValue(value) {
return String(value).replace(TAGS_REGEXP, (function (m, tag) {
return this.encodeTag(tag);
}).bind(this));
},
/*
Convert encoded text used in CM to tagged text. For example
'hello \ue000' becomes 'hello {{firstName}}'
*/
decodeValue: function decodeValue(encodedValue) {
return String(encodedValue).replace(this.specialCharsRegexp, function (c) {
var tag = charToTagMap[c];
return '{{' + tag + '}}';
});
},
/*
Convert encoded character to label. For example
'\ue000' becomes 'Last Name'.
*/
decodeChar: function decodeChar(char) {
var tag = charToTagMap[char];
return this.getLabel(tag);
},
/*
Convert tagged value to HTML. For example
'hello {{firstName}}' becomes 'hello <span class="tag">First Name</span>'
*/
toHtml: function toHtml(value) {
return String(value).replace(TAGS_REGEXP, (function (m, mustache) {
var tag = mustache.replace('{{', '').replace('}}', '');
var label = this.getLabel(tag);
return '<span class="pretty-part">' + label + '</span>';
}).bind(this));
},
/*
Get label for tag. For example 'firstName' becomes 'First Name'.

@@ -127,2 +34,42 @@ Returns a humanized version of the tag if we don't have a label for the tag.

return label;
},
tokenize: function tokenize(text) {
var regexp = /(\{\{|\}\})/;
var parts = text.split(regexp);
var tokens = [];
var inTag = false;
parts.forEach(function (part) {
if (part === '{{') {
inTag = true;
} else if (part === '}}') {
inTag = false;
} else if (inTag) {
tokens.push({ type: 'tag', value: part });
} else {
tokens.push({ type: 'string', value: part });
}
});
return tokens;
},
getTagPositions: function getTagPositions(text) {
var lines = text.split('\n');
var re = /\{\{.+?\}\}/g;
var positions = [];
var m;
for (var i = 0; i < lines.length; i++) {
while ((m = re.exec(lines[i])) !== null) {
var tag = m[0].substring(2, m[0].length - 2);
positions.push({
line: i,
start: m.index,
stop: m.index + m[0].length,
tag: tag
});
}
}
return positions;
}

@@ -129,0 +76,0 @@ };

{
"name": "formatic",
"version": "0.2.31",
"version": "0.2.32",
"description": "Automatic, pluggable form generation",

@@ -5,0 +5,0 @@ "main": "./build/lib/formatic",

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

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