Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get and replace text inside double curly braces in CodeMirror

I want to create an input where users can write plain text only, but where I can get part of that text and replace it with some HTML.

So, for example, I want to convert a text like Lorem Ipsum {{dolor}} to Lorem Ipsum <some html with data from JS object>

I have an object with some data.

var data = {
    dolor: {
        icon: 'https://placehold.it/16x16/',
        text: 'Dolor text'
    }
}

I want to use that to get the custom text and image, so {{dolor}} should convert to something like

<span class="image">
    <img src="https://placehold.it/16x16/">
    Dolor text
</span>

enter image description here

My approach was to use CodeMirror, define a new mode and detect the curly braces, but now I don't know how to get the content between those braces and I don't know how to modify the outputted HTML (as CodeMirror only adds a cm-NameOfMode CSS class to the text).

This is what I have

var data = {
	dolor: {
		icon: 'https://placehold.it/16x16/',
		text: 'Dolor text'
	}
}

CodeMirror.defineMode( 'uoVariable', function( config, parserConfig ){
	var uoVariable = {
		token: function( stream, state ){
			var character;

			// console.log( state.inString );

			if ( stream.match( '{{' ) ){
				while ( ( character = stream.next() ) != null ){
					if ( character == '}' && stream.next() == '}' ){
						stream.eat( '}' );
						return 'uoVariable';
					}
				}
			}

			while ( stream.next() != null && ! stream.match( '{{', false ) ){}

			return null;
		}
	};

	return CodeMirror.overlayMode( CodeMirror.getMode( config, parserConfig.backdrop ), uoVariable );
});

$(document).ready(function(){

	// Do for each one
	let form__variable_cd_mirror = document.getElementById( 'form-variable__cd-text' ),
		form__variable_cd		 = document.getElementById( 'form-variable__cd' );

	let editor = CodeMirror( form__variable_cd, {
		mode: 'uoVariable',
		value: form__variable_cd_mirror.innerText,
		lineWrapping: true,
		scrollbarStyle: null,
		extraKeys: {
			Tab: function( cm ){
				return false;
			},
		},
	}).on( 'beforeChange', function( cm, changeObj ){
		// Typed new line
		var typedNewLine = changeObj.origin == '+input' && typeof changeObj.text == 'object' && changeObj.text.join('') == '';
		
		if ( typedNewLine ){
			return changeObj.cancel();
		}

		// Pasted new line
		var pastedNewLine = changeObj.origin == 'paste' && typeof changeObj.text == 'object' && changeObj.text.length > 1;
		
		if ( pastedNewLine ) {
			var newText = changeObj.text.join(' ');
			return changeObj.update( null, null, [ newText ] );
		}

		return null;
	});
});
body {
	font-size: 15px;
	font-family: -apple-system, system-ui, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
}

/* New CSS */

#form-variable__cd .CodeMirror {
	padding: 8px 12px;
	transition: all 150ms ease-in-out;
	border-radius: 3px;
	border: 1px solid #cdcdcd;
	box-shadow: 0 2px 5px 0 rgba(0,0,0,.1);
	outline: none;

	height: auto;
	font-size: 15px;
	font-family: inherit !important;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;

	margin-top: 10px;
}

#form-variable__cd .cm-uoVariable {
	background: #4f4f4f;
	color: #fff;
	padding: 2px 5px;
	border-radius: 3px;

	white-space: nowrap;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.38.0/codemirror.css" rel="stylesheet"/>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.38.0/codemirror.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.38.0/addon/mode/overlay.js"></script>

<div id="form-variable__cd-text" style="display: none">Lorem ipsum {{dolor}}</div>
<div id="form-variable__cd"></div>

So... any ideas about how can I archive this?

Thanks!

like image 571
agustin Avatar asked Jun 06 '18 21:06

agustin


Video Answer


1 Answers

I am not a user of CodeMirror, but I already worked with templates in HTML.

Here is an approach:

  1. flag all your elements that contain templates (to avoid full scan of your DOM)
  2. match all {{key}} with keys in your data object to generate HTML
  3. replace innerHTML directly in your templates, or into separate elements (if you want to regenerate from templates later)

Here is how you could do it in a simple way:

var data = {
    dolor: {
        icon: 'https://placehold.it/16x16/',
        text: 'Dolor text'
    },
    lorem: {
        text: 'Lorem text'
    }
};

function getHtmlFromData(dataitem) {
    var html = '';
    if ('icon' in dataitem)
        // Image data type
        html += '<span class="image">' +
        '<img src="' + dataitem.icon + '">' +
        dataitem.text +
        '</span>';
    else
        // Text data type
        html += '<span class="text">' +
        dataitem.text +
        '</span>';
    return html;
}

setTimeout(
    function () {
        var templates = document.getElementsByClassName("template");
        Array.prototype.forEach.call(templates, function (template) {
            for (var key in data) {
                template.innerHTML = template.innerHTML.replace(
                    new RegExp('{{'+key+'}}', 'g'),
                    getHtmlFromData(data[key])
                );
            }
        });
    },
    1000
);
<!DOCTYPE html>
<html>

  <head>
    <meta charset="utf-8" />
    <script src="index.jsx"></script>
  </head>

  <body>
    <div class="template">
      {{lorem}} Ipsum {{dolor}}
    </div>
  </body>
</html>
like image 181
RaphaMex Avatar answered Sep 22 '22 06:09

RaphaMex