Prototype Remove HTML? - html

I have the following HTML
<div id="top-right">
<span id="top-right-name">sometexthere</span> | link
</div>
And the following prototype JS
Event.observe(window, 'load', function() {
try {
if ($$('#top-right')!=null) {
var topmenu = document.getElementById('top-right');
var value = topmenu.innerHTML;
// define an array of replacements
var replacements = [
{ from: '|', to: ' ' },
{ from: '|', to: ' ' },
{ from: '|', to: ' ' },
{ from: '|', to: ' ' }
];
for (var i = 0, replacement; i < replacements.length, replacement = replacements[i]; i++) {
// replace
value = value.replace(replacement.from, replacement.to);
}
// replace the old text with the new
topmenu.innerHTML = value;
}
}
catch(ex) {
}
});
I am trying to remove the " | " after the </span> automatically onload of the this JS - but I just cant seem to do it .
Can someone assist ?
Thanks

It seems to be a syntax error somewhere, perhaps in your loading of Prototype. The below snippet worked fine for me :)
<script type="text/javascript"
src="http://ajax.googleapis.com/ajax/libs/prototype/1.6.0.2/prototype.js"></script>
<div id="top-right">
<span id="top-right-name">sometexthere</span> | link
</div>
<script type="text/javascript">
Event.observe(window, 'load', function() {
try {
if ($$('#top-right')!=null) {
var topmenu = document.getElementById('top-right');
var value = topmenu.innerHTML;
// define an array of replacements
var replacements = [
{ from: '|', to: ' ' }
];
for (var i = 0, replacement; i < replacements.length, replacement = replacements[i]; i++) {
// replace
value = value.replace(replacement.from, replacement.to);
}
// replace the old text with the new
topmenu.innerHTML = value;
}
}
catch(ex) {
}
});
</script>
Edited to reflect problem

Related

Custom Parameters get cleared canvas to Json

I am adding svg to canvas and want to set custom Element Parameters. I shows custom parameter when we console log getActiveObject() but when we use canvas.toJSON() Element Parameter node values does not change.
var canvas = new fabric.Canvas('designcontainer'),
/* Save additional attributes in Serialization */
var ElementParameters = {
ElementType:'',
imageType:'',
top:'',
left:'',
colors:'',
isBaseTier:'',
atLevel:''
};
fabric.Object.prototype.toObject = (function (toObject) {
return function () {
return fabric.util.object.extend(toObject.call(this), {
ElementParameters:{
ElementType:'',
imageType:'',
top:'',
left:'',
colors:'',
isBaseTier:'',
atLevel:''
},
});
};
})(fabric.Object.prototype.toObject);
/* End : Save additional attributes in Serialization */
var Designer = {
addElement: function(e,p){ /* e = element, image src | p = parameters set for image */
if(p.imageType == "svg"){
if(p.ElementType == "caketier"){
var group = [];
console.log('Before ');
console.log(ElementParameters);
$.extend(ElementParameters,p);
console.log('After ');
console.log(ElementParameters);
fabric.loadSVGFromURL(e,function(objects,options){
var shape = fabric.util.groupSVGElements(objects,options);
var bound = shape.getBoundingRect();
shape.set({
left: p.left,
top: p.top,
width:bound.width+2,
height:bound.height,
angle:0,
centeredScaling:true,
ElementParameters:ElementParameters
});
if(shape.paths && baseColor.length > 0){
for(var i = 0;i<shape.paths.length;i++) shape.paths[i].setFill(baseColor[i]);
}
canvas.add(shape);
shape.setControlsVisibility(HideControls);
canvas.renderAll();
},function(item, object) {
object.set('id',item.getAttribute('id'));
group.push(object);
});
}
}
}
}
$(".tierbox").on('click',function(){
var i = $(this).find('img'),
src = i.attr('src'),
param = i.data('parameters');
Designer.addElement(src,param);
});
Now when I call JSON.stringify(json), Element Parameter node does not get overwrite with values set in shape.set() method.
Replace fabric.Object.prototype.toObject = (function (toObject) { ... } to
fabric.Object.prototype.toObject = (function (toObject) {
return function () {
return fabric.util.object.extend(toObject.call(this), {
ElementParameters:this.ElementParameters
});
};
})(fabric.Object.prototype.toObject);

Quill strips out simple html when dangerouslyPasteHTML into editor

<style>
#editor-container {
height: 375px;
}
.link {
color:blue;
}
</style>
<div id="editor-container">
This is a test
</div>
<script type="text/javascript">
var quill = new Quill('#editor-container', {
modules: {
toolbar: [
[{ header: [1, 2, false] }],
['bold', 'italic', 'underline'],
['image', 'code-block']
]
},
placeholder: 'Compose an epic...',
theme: 'bubble' // or 'bubble'
});
quill.clipboard.dangerouslyPasteHTML(5, "<span class=\"link\" data-test=\"test\">testing</span>", "silent");
</script>
MVCE - https://codepen.io/anon/pen/QMQMee
The HTML get stripped out despite being pretty harmless (this will be handled better later).
My current plan, due to the way Quill does not allow pasted html, is (As part of a click action on the mentioned person's name):
$("#tag-selectable-users-list li").on("click",
function() {
var $this = $(this);
var startIndex = $this.data("data-start-index");
var userName = $this.data("data-user-name");
var userId = $this.data("data-user-id");
var taggedUserIds = $("#hiddenTaggedUsers");
taggedUserIds.val((taggedUserIds.val()||"") + ";" + userId);
var delta = [];
if (startIndex > 0) {
//retain up to the tag start
delta.push({ retain: parseInt(startIndex) });
}
//delete the junk
delta.push({ delete: tagStatus.Total.length });
//insert the new characters
delta.push({
insert: "##" + userName,
attributes: {
color: "blue",
underline: "true"
}
});
//insert a blank space to end the span
delta.push({ insert: " " });
quill.updateContents(delta,
'api');
});
}

Angular bind-html + $sce still remove the inline style

I write simple custom filter that return string value.
The value contain html string and inline style
When binding angular remove the styles
Here is my filter
siteApp.filter('specialText', ['$filter', '$sce', function ($filter, $sce) {
return function (code, items, defaults) {
var out = defaults;
if (items && items.length) {
var arr = $filter('filter')(items, { code: code }, true);
if (arr && arr.length > 0) {
out = arr[0].value;
$sce.trustAsHtml(out);
}
}
return out;
};
}]);
And This is my html
<div ng-bind-html="'body_message' | specialText : specific_texts :''"></div>
The original text contain inline style but angular remove inline style on binding
How can I keep the inline styles
FULL EXAMPLE CODE
<body >
<div ng-app="siteApp">
<div ng-controller="ctrl">
{{'test1' | specialText : arr :'missing....' }}
<div ng-bind-html="'test1' | specialText : arr :'missing....'"></div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-sanitize/1.5.6/angular-sanitize.js"></script>
<script>
var siteApp = angular.module('siteApp', ['ngSanitize']);
siteApp.filter('specialText', ['$filter', '$sce', function ($filter, $sce) {
return function (code, items, defaults) {
var out = defaults;
if (items && items.length) {
var arr = $filter('filter')(items, { code: code }, true);
if (arr && arr.length > 0) {
out = arr[0].value;
$sce.trustAsHtml(out);
}
}
return out;
};
}]);
siteApp.controller('ctrl', ['$scope', function ($scope) {
$scope.arr = [{ "code": "test1", "rte": true, "value": "<p style=\"text-align: left;\">First Row</p>\n<h1 style=\"text-align: center;\">Second Center Row</h1>" }];
}]);
</script>
</body>
You are not using the output of $sce.trustAsHtml(out). Inside your if statement try out = $sce.trustAsHtml(out); and it should be ok.

Add search filter inside the select dropdown in AngularJS

I want to add a search filter inside a select dropdown in angularJS.
I have used ng-options to list down the options and used filter to filter out the data in the search box , but the problem is that the search box is not coming inside(or under) select dropdown. (When I click the select dropdown, it shows a search filter and below it has all the options)
Below is the code for your reference :
<div class="rowMargin">
<label class="control-label" for="entitySel">Entity:</label>
<div class="controls">
<select id="entityId" class="input-medium" type="text" name="entityId" ng-model="payment.entityId" ng-options="entityOpt for entityOpt in paymentEntityOptions">
<option value="">Select</option>
</select>
<span ng-show=" submitted && addPayment.entityId.$error.required">
<label class="error">Please provide entity Id </label>
</span>
<div ng-show="payment.entityId == \'Individual\'">
<span>
<select ng-model="payment.entity.individual" ng-options = "individual for individual in individualEntities | filter : filterEntity">
<option value="">Select Individual Entity</option>
<option>
<input type="search" placeholder="Search" ng-model="filterEntity"></input>
</option>
</select>
</span>
</div>
<div ng-show="payment.entityId == \'Group\'">
<span>
<select ng-model="payment.entity.group" ng-options = "group for group in groupEntities | filter : filterEntity">
<option value="">Select Group Entity</option>
<input type="search" placeholder="Search" ng-model="filterEntity"></input>
</select>
</span>
</div>
</div>
I have used the bootstrap button with class 'dropdown-toggle' and on click of the button I have appended an input search box as following :
<div class="dropdown pull-right makePaymentDropdownMainDiv" auto-close="outsideClick">
<button class="btn btn-default dropdown-toggle makePaymentDropdownBtn" type="button" id="individualDrop" data-toggle="dropdown">{{payment.entity}}<span class="caret pull-right"></span></button>
<span ng-show="submitted"><label class="error">Select an Individual</label></span>
<ul class="dropdown-menu makePaymentDropdownUlStyle" role="menu" aria-labelledby="individualDrop">
<input disable-auto-close type="search" ng-model="serchFilter" class="makePaymentDropdownSearchBox" placeholder="Search"></input>
<li role="presentation" ng-repeat="indi in individuals | filter: serchFilter"><a role="menuitem" ng-click="selectEntity(indi)">{{indi}}</a></li>
</ul>
</div>
Showing the 'li' using ng-repeat.
Remember to add auto-close="outsideClick" to your dropdown so that it doesn't close on filtering attempt.
Sorry, I'm rather late to the party, but to me it sounds like you need acute-select, an open source extension (MIT license) to Angular that does exactly this, without further dependencies.
They also have a demo page, which shows what it can do nicely.
you can use easy and best way to search filter inside the select dropdown in AngularJS
Working Demo : http://plnkr.co/edit/o767Mg6fQoyc7jKq77If?p=preview
(function (angular, undefined) {
'use strict';
// TODO: Move to polyfill?
if (!String.prototype.trim) {
String.prototype.trim = function () {
return this.replace(/^\s+|\s+$/g, '');
};
}
/**
* A replacement utility for internationalization very similar to sprintf.
*
* #param replace {mixed} The tokens to replace depends on type
* string: all instances of $0 will be replaced
* array: each instance of $0, $1, $2 etc. will be placed with each array item in corresponding order
* object: all attributes will be iterated through, with :key being replaced with its corresponding value
* #return string
*
* #example: 'Hello :name, how are you :day'.format({ name:'John', day:'Today' })
* #example: 'Records $0 to $1 out of $2 total'.format(['10', '20', '3000'])
* #example: '$0 agrees to all mentions $0 makes in the event that $0 hits a tree while $0 is driving drunk'.format('Bob')
*/
function format(value, replace) {
if (!value) {
return value;
}
var target = value.toString();
if (replace === undefined) {
return target;
}
if (!angular.isArray(replace) && !angular.isObject(replace)) {
return target.split('$0').join(replace);
}
var token = angular.isArray(replace) && '$' || ':';
angular.forEach(replace, function (value, key) {
target = target.split(token + key).join(value);
});
return target;
}
var module = angular.module('AxelSoft', []);
module.value('customSelectDefaults', {
displayText: 'Select...',
emptyListText: 'There are no items to display',
emptySearchResultText: 'No results match "$0"',
addText: 'Add',
searchDelay: 300
});
module.directive('customSelect', ['$parse', '$compile', '$timeout', '$q', 'customSelectDefaults', function ($parse, $compile, $timeout, $q, baseOptions) {
var CS_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/;
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, elem, attrs, controller) {
var customSelect = attrs.customSelect;
if (!customSelect) {
throw new Error('Expected custom-select attribute value.');
}
var match = customSelect.match(CS_OPTIONS_REGEXP);
if (!match) {
throw new Error("Expected expression in form of " +
"'_select_ (as _label_)? for _value_ in _collection_[ track by _id_]'" +
" but got '" + customSelect + "'.");
}
elem.addClass('dropdown custom-select');
// Ng-Options break down
var displayFn = $parse(match[2] || match[1]),
valueName = match[3],
valueFn = $parse(match[2] ? match[1] : valueName),
values = match[4],
valuesFn = $parse(values),
track = match[5],
trackByExpr = track ? " track by " + track : "",
dependsOn = attrs.csDependsOn;
var options = getOptions(),
timeoutHandle,
lastSearch = '',
focusedIndex = -1,
matchMap = {};
var itemTemplate = elem.html().trim() || '{{' + (match[2] || match[1]) + '}}',
dropdownTemplate =
'<a class="dropdown-toggle" data-toggle="dropdown" href ng-class="{ disabled: disabled }">' +
'<span>{{displayText}}</span>' +
'<b></b>' +
'</a>' +
'<div class="dropdown-menu">' +
'<div stop-propagation="click" class="custom-select-search">' +
'<input class="' + attrs.selectClass + '" type="text" autocomplete="off" ng-model="searchTerm" />' +
'</div>' +
'<ul role="menu">' +
'<li role="presentation" ng-repeat="' + valueName + ' in matches' + trackByExpr + '">' +
'<a role="menuitem" tabindex="-1" href ng-click="select(' + valueName + ')">' +
itemTemplate +
'</a>' +
'</li>' +
'<li ng-hide="matches.length" class="empty-result" stop-propagation="click">' +
'<em class="muted">' +
'<span ng-hide="searchTerm">{{emptyListText}}</span>' +
'<span class="word-break" ng-show="searchTerm">{{ format(emptySearchResultText, searchTerm) }}</span>' +
'</em>' +
'</li>' +
'</ul>' +
'<div class="custom-select-action">' +
(typeof options.onAdd === "function" ?
'<button type="button" class="btn btn-primary btn-block add-button" ng-click="add()">{{addText}}</button>' : '') +
'</div>' +
'</div>';
// Clear element contents
elem.empty();
// Create dropdown element
var dropdownElement = angular.element(dropdownTemplate),
anchorElement = dropdownElement.eq(0).dropdown(),
inputElement = dropdownElement.eq(1).find(':text'),
ulElement = dropdownElement.eq(1).find('ul');
// Create child scope for input and dropdown
var childScope = scope.$new(true);
configChildScope();
// Click event handler to set initial values and focus when the dropdown is shown
anchorElement.on('click', function (event) {
if (childScope.disabled) {
return;
}
childScope.$apply(function () {
lastSearch = '';
childScope.searchTerm = '';
});
focusedIndex = -1;
inputElement.focus();
// If filter is not async, perform search in case model changed
if (!options.async) {
getMatches('');
}
});
if (dependsOn) {
scope.$watch(dependsOn, function (newVal, oldVal) {
if (newVal !== oldVal) {
childScope.matches = [];
childScope.select(undefined);
}
});
}
// Event handler for key press (when the user types a character while focus is on the anchor element)
anchorElement.on('keypress', function (event) {
if (!(event.altKey || event.ctrlKey)) {
anchorElement.click();
}
});
// Event handler for Esc, Enter, Tab and Down keys on input search
inputElement.on('keydown', function (event) {
if (!/(13|27|40|^9$)/.test(event.keyCode)) return;
event.preventDefault();
event.stopPropagation();
switch (event.keyCode) {
case 27: // Esc
anchorElement.dropdown('toggle');
break;
case 13: // Enter
selectFromInput();
break;
case 40: // Down
focusFirst();
break;
case 9:// Tab
anchorElement.dropdown('toggle');
break;
}
});
// Event handler for Up and Down keys on dropdown menu
ulElement.on('keydown', function (event) {
if (!/(38|40)/.test(event.keyCode)) return;
event.preventDefault();
event.stopPropagation();
var items = ulElement.find('li > a');
if (!items.length) return;
if (event.keyCode == 38) focusedIndex--; // up
if (event.keyCode == 40 && focusedIndex < items.length - 1) focusedIndex++; // down
//if (!~focusedIndex) focusedIndex = 0;
if (focusedIndex >= 0) {
items.eq(focusedIndex)
.focus();
} else {
focusedIndex = -1;
inputElement.focus();
}
});
resetMatches();
// Compile template against child scope
$compile(dropdownElement)(childScope);
elem.append(dropdownElement);
// When model changes outside of the control, update the display text
controller.$render = function () {
setDisplayText();
};
// Watch for changes in the default display text
childScope.$watch(getDisplayText, setDisplayText);
childScope.$watch(function () { return elem.attr('disabled'); }, function (value) {
childScope.disabled = value;
});
childScope.$watch('searchTerm', function (newValue) {
if (timeoutHandle) {
$timeout.cancel(timeoutHandle);
}
var term = (newValue || '').trim();
timeoutHandle = $timeout(function () {
getMatches(term);
},
// If empty string, do not delay
(term && options.searchDelay) || 0);
});
// Support for autofocus
if ('autofocus' in attrs) {
anchorElement.focus();
}
var needsDisplayText;
function setDisplayText() {
var locals = { };
locals[valueName] = controller.$modelValue;
var text = displayFn(scope, locals);
if (text === undefined) {
var map = matchMap[hashKey(controller.$modelValue)];
if (map) {
text = map.label;
}
}
needsDisplayText = !text;
childScope.displayText = text || options.displayText;
}
function getOptions() {
return angular.extend({}, baseOptions, scope.$eval(attrs.customSelectOptions));
}
function getDisplayText() {
options = getOptions();
return options.displayText;
}
function focusFirst() {
var opts = ulElement.find('li > a');
if (opts.length > 0) {
focusedIndex = 0;
opts.eq(0).focus();
}
}
// Selects the first element on the list when the user presses Enter inside the search input
function selectFromInput() {
var opts = ulElement.find('li > a');
if (opts.length > 0) {
var ngRepeatItem = opts.eq(0).scope();
var item = ngRepeatItem[valueName];
childScope.$apply(function () {
childScope.select(item);
});
anchorElement.dropdown('toggle');
}
}
function getMatches(searchTerm) {
var locals = { $searchTerm: searchTerm }
$q.when(valuesFn(scope, locals)).then(function (matches) {
if (!matches) return;
if (searchTerm === inputElement.val().trim()/* && hasFocus*/) {
matchMap = {};
childScope.matches.length = 0;
for (var i = 0; i < matches.length; i++) {
locals[valueName] = matches[i];
var value = valueFn(scope, locals),
label = displayFn(scope, locals);
matchMap[hashKey(value)] = {
value: value,
label: label/*,
model: matches[i]*/
};
childScope.matches.push(matches[i]);
}
//childScope.matches = matches;
}
if (needsDisplayText) setDisplayText();
}, function() {
resetMatches();
});
}
function resetMatches() {
childScope.matches = [];
focusedIndex = -1;
};
function configChildScope() {
childScope.addText = options.addText;
childScope.emptySearchResultText = options.emptySearchResultText;
childScope.emptyListText = options.emptyListText;
childScope.select = function (item) {
var locals = {};
locals[valueName] = item;
var value = valueFn(childScope, locals);
//setDisplayText(displayFn(scope, locals));
childScope.displayText = displayFn(childScope, locals) || options.displayText;
controller.$setViewValue(value);
anchorElement.focus();
typeof options.onSelect === "function" && options.onSelect(item);
};
childScope.add = function () {
$q.when(options.onAdd(), function (item) {
if (!item) return;
var locals = {};
locals[valueName] = item;
var value = valueFn(scope, locals),
label = displayFn(scope, locals);
matchMap[hashKey(value)] = {
value: value,
label: label/*,
model: matches[i]*/
};
childScope.matches.push(item);
childScope.select(item);
});
};
childScope.format = format;
setDisplayText();
}
var current = 0;
function hashKey(obj) {
if (obj === undefined) return 'undefined';
var objType = typeof obj,
key;
if (objType == 'object' && obj !== null) {
if (typeof (key = obj.$$hashKey) == 'function') {
// must invoke on object to keep the right this
key = obj.$$hashKey();
} else if (key === undefined) {
key = obj.$$hashKey = 'cs-' + (current++);
}
} else {
key = obj;
}
return objType + ':' + key;
}
}
};
}]);
module.directive('stopPropagation', function () {
return {
restrict: 'A',
link: function (scope, elem, attrs, ctrl) {
var events = attrs['stopPropagation'];
elem.bind(events, function (event) {
event.stopPropagation();
});
}
};
});
})(angular);
<body ng-app="Demo">
<div class="container" ng-controller="DemoController">
<label>Level 1</label>
<div custom-select="g for g in nestedItemsLevel1 | filter: $searchTerm" custom-select-options="level1Options" ng-model="level1"></div>
<label>Level 2</label>
<div custom-select="g for g in nestedItemsLevel2 | filter: $searchTerm" ng-model="level2" cs-depends-on="level1"></div>
</div>
<!-- basic scripts -->
<!--[if !IE]> -->
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<!-- <![endif]-->
<!--[if IE]>
<script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
<![endif]-->
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/2.3.2/js/bootstrap.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js"></script>
<script src="js/customSelect.js"></script>
<script>
(function () {
var app = angular.module('Demo', ['AxelSoft']);
app.controller('DemoController', ['$scope', '$timeout', '$q', function ($scope, $timeout, $q) {
$scope.searchAsync = function (term) {
// No search term: return initial items
if (!term) {
return ['Item 1', 'Item 2', 'Item 3'];
}
var deferred = $q.defer();
$timeout(function () {
var result = [];
for (var i = 1; i <= 3; i++)
{
result.push(term + ' ' + i);
}
deferred.resolve(result);
}, 300);
return deferred.promise;
};
$scope.nestedItemsLevel1 = ['Item 1', 'Item 2', 'Item 3'];
$scope.level1 = $scope.nestedItemsLevel1[0];
$scope.level1Options = {
onSelect: function (item) {
var items = [];
for (var i = 1; i <= 5; i++) {
items.push(item + ': ' + 'Nested ' + i);
}
$scope.nestedItemsLevel2 = items;
}
};
$scope.nestedItemsLevel2 = [];
$scope.level1Options.onSelect($scope.nestedItemsLevel1[0]);
}]);
})();
</script>
</body>
https://docs.angularjs.org/api/ng/directive/select
There can be only one hard coded in a ngOption.

Automatic text detection on contenteditable div using angular js

what is the best way to do following using angular js
when writing text on a contenteditable div need to detect special word like ' {{FULL_NAME}} ' and covert to tag with pre-deifined constant words
example - if some one write
' His name is {{FULL_NAME}} '
should be instantly convert to ' His name is john smith '
Here is a demo plunker: http://plnkr.co/edit/GKYxXiDKv7fBeaE7rrZA?p=preview
Service:
app.factory('interpolator', function(){
var dict = { .... };
return function(str){
return (str || "").replace(/\{\{([^\}]+)\}\}/g, function(all, match){
return dict[match.trim().toLowerCase()] || all;
});
};
});
Directive:
app.directive('edit',[ 'interpolator', function(interpolator){
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
element.on('blur', function(e) {
scope.$apply(function() {
var content = interpolator(element.text());
element.text(content);
ngModel.$setViewValue(content);
});
});
ngModel.$formatters.push(interpolator);
ngModel.$render = function() {
element.text(ngModel.$viewValue);
ngModel.$setViewValue(ngModel.$viewValue);
};
}
};
}]);
Here's an example using simple DOM methods, based on other answers I've provided on Stack Overflow. It also uses Rangy to save and restore the selection while doing the substitutions so that the caret does not move.
Demo:
http://jsbin.com/zuvevocu/2
Code:
var editorEl = document.getElementById("editor");
var keyTimer = null, keyDelay = 100;
function createKeyword(matchedTextNode) {
var el = document.createElement("b");
el.style.backgroundColor = "yellow";
el.style.padding = "2px";
el.contentEditable = false;
var matchedKeyword = matchedTextNode.data.slice(1, -1); // Remove the curly brackets
matchedTextNode.data = (matchedKeyword.toLowerCase() == "name") ? "John Smith" : "REPLACEMENT FOR " + matchedKeyword;
el.appendChild(matchedTextNode);
return el;
}
function surroundInElement(el, regex, surrounderCreateFunc) {
// script and style elements are left alone
if (!/^(script|style)$/.test(el.tagName)) {
var child = el.lastChild;
while (child) {
if (child.nodeType == 1) {
surroundInElement(child, regex, surrounderCreateFunc);
} else if (child.nodeType == 3) {
surroundMatchingText(child, regex, surrounderCreateFunc);
}
child = child.previousSibling;
}
}
}
function surroundMatchingText(textNode, regex, surrounderCreateFunc) {
var parent = textNode.parentNode;
var result, surroundingNode, matchedTextNode, matchLength, matchedText;
while ( textNode && (result = regex.exec(textNode.data)) ) {
matchedTextNode = textNode.splitText(result.index);
matchedText = result[0];
matchLength = matchedText.length;
textNode = (matchedTextNode.length > matchLength) ?
matchedTextNode.splitText(matchLength) : null;
surroundingNode = surrounderCreateFunc(matchedTextNode.cloneNode(true));
parent.insertBefore(surroundingNode, matchedTextNode);
parent.removeChild(matchedTextNode);
}
}
function updateKeywords() {
var savedSelection = rangy.saveSelection();
surroundInElement(editorEl, /\{\w+\}/, createKeyword);
rangy.restoreSelection(savedSelection);
}
function keyUpHandler() {
if (keyTimer) {
window.clearTimeout(keyTimer);
}
keyTimer = window.setTimeout(function() {
updateKeywords();
keyTimer = null;
}, keyDelay);
}
editorEl.onkeyup = keyUpHandler;
Related:
https://stackoverflow.com/a/5905413/96100
https://stackoverflow.com/a/4026684/96100
https://stackoverflow.com/a/4045531/96100