JavaScript WYSIWYG Rich Text Editor - javascript

What is the basic method of styling text inside input boxes? Can you show me the most simple example of how to change text color separately?

Very simply:
window.document.designMode = “On”;
document.execCommand(‘ForeColor’, false, ‘red’);
For more detail:
http://shakthydoss.com/javascript/html-rich-text-editor/
and then
http://shakthydoss.com/javascript/stxe-html-rich-text-editor/

There are 4 approaches you can adopt
Create a textarea. Let user modify the content. On key press or button click insertHtml to preview div. Trix uses the same approach but programmatically.
Add some textarea. Use some markdown processor which can render the the content of textarea.
Handle every movement of blinking cursor and implement something like Google docs which doesn't use execCommands. I believe quilljs also doesn't use execCommands.
You can use execCommands which are supported by almost all modern browsers. Here is the simplest example. Or check this example which uses this small code to run set of execCommands to make a rich text editor. You can simplify it more.
Example
angular.module("myApp", [])
.directive("click", function () {
return {
restrict: "A",
link: function (scope, element, attrs) {
element.bind("click", function () {
scope.$evalAsync(attrs.click);
});
}
};
})
.controller("Example", function ($scope) {
$scope.supported = function (cmd) {
var css = !!document.queryCommandSupported(cmd.cmd) ? "btn-succes" : "btn-error"
return css
};
$scope.icon = function (cmd) {
return (typeof cmd.icon !== "undefined") ? "fa fa-" + cmd.icon : "";
};
$scope.doCommand = function (cmd) {
if ($scope.supported(cmd) === "btn-error") {
alert("execCommand(“" + cmd.cmd + "”)\nis not supported in your browser");
return;
}
val = (typeof cmd.val !== "undefined") ? prompt("Value for " + cmd.cmd + "?", cmd.val) : "";
document.execCommand(cmd.cmd, false, (cmd.val || ""));
}
$scope.commands = commands;
$scope.tags = [
'Bootstrap', 'AngularJS', 'execCommand'
]
})

Related

Best lightweight non-html WYSIWYG for AngularJS

Since a few days I'm looking for a non-html (markdown, bbcode) WYSIWYG really minimalist for a kind of forum.
I just need to allow users to add links, bold italic and break line in a non html way.
But all I can found is some dirty old wysiwyg with too many functionality and without directives for angularjs or just html wysiwyg..
Do you have any suggestions ?
At this time I'm thinking that I should make it by myself.
Thank you in advance for any responses.
Have a look on this example implemented in AngularJs; You can customise and convert it into Angular module as per your need;
Example
angular.module("myApp", [])
.directive("click", function () {
return {
restrict: "A",
link: function (scope, element, attrs) {
element.bind("click", function () {
scope.$evalAsync(attrs.click);
});
}
};
})
.controller("Example", function ($scope) {
$scope.supported = function (cmd) {
var css = !!document.queryCommandSupported(cmd.cmd) ? "btn-succes" : "btn-error"
return css
};
$scope.icon = function (cmd) {
return (typeof cmd.icon !== "undefined") ? "fa fa-" + cmd.icon : "";
};
$scope.doCommand = function (cmd) {
if ($scope.supported(cmd) === "btn-error") {
alert("execCommand(“" + cmd.cmd + "”)\nis not supported in your browser");
return;
}
val = (typeof cmd.val !== "undefined") ? prompt("Value for " + cmd.cmd + "?", cmd.val) : "";
document.execCommand(cmd.cmd, false, (cmd.val || ""));
}
$scope.commands = commands;
$scope.tags = [
'Bootstrap', 'AngularJS', 'execCommand'
]
})

Google Transliterate Result not updating in the scope for use in Angular Controller

Need some help with integrating Google Transliterate with an angular project, Below is the snippet that make all the desired elements in the DOM as Transliteratable.
function za() {
google.load("elements", "1", {packages: "transliteration"});
google.setOnLoadCallback(procTA);
}
// calls the helper function for each of input as well as textarea elememnts in the page
function procTA() {
procTAHelp('textarea');
procTAHelp('input');
}
// for each element of xtype (input or textarea), it creates another attribute
// whose name is <xtype><counter>id. That way each element gets a new
// attribute name (which acts as an identifier for the transliteration process
// and a flag which check whether to enable (or not) the English <-> Hindi
// transliteration change
// if gtransx is set and is "no" then nothing is done, else it enables the transliteration
// most of the remaining code is a cut-paste from the help pages for the deprecated google transliteration api
function procTAHelp(xtype) {
var textAreaList = document.getElementsByTagName(xtype);
for(var i = 0; i < textAreaList.length; i++) {
var attrName = "gtransed";
var noTrans = "gtransx";
var taInd = i + 1;
if((textAreaList[i].getAttribute(noTrans) == null) && (textAreaList[i].getAttribute(attrName) == null)) {
var tcc;
var att = document.createAttribute(attrName);
textAreaList[i].setAttributeNode(att);
var textAreaId = xtype.concat(taInd.toString()).concat("id");
textAreaList[i].id = textAreaId;
var options = {
sourceLanguage: 'en', // destinationLanguage: ['hi','kn','ml','ta','te'],
destinationLanguage: ['hi'],
transliterationEnabled: true,
shortcutKey: 'ctrl+g'
};
tcc = new google.elements.transliteration.TransliterationControl(options);
var transIdList = [textAreaId];
tcc.makeTransliteratable(transIdList);
tcc.addEventListener(google.elements.transliteration.TransliterationControl.EventType.SERVER_UNREACHABLE, serverUnreachableHandler);
tcc.addEventListener(google.elements.transliteration.TransliterationControl.EventType.SERVER_REACHABLE, serverReachableHandler);
}
}
}
// Handler for STATE_CHANGED event which makes sure checkbox status reflects the transliteration enabled or disabled status.
function transliterateStateChangeHandler(e) {
}
// SERVER_UNREACHABLE event handler which displays the error message.
function serverUnreachableHandler(e) {
document.getElementById("errorDiv").innerHTML = "Transliteration Server unreachable";
}
// SERVER_UNREACHABLE event handler which clears the error message.
function serverReachableHandler(e) {
document.getElementById("errorDiv").innerHTML = "";
}
za();
Below is the angular snippet that reads the particular element that is being transliterated.
$scope.makePost = function() {
setTimeout(function(){
$scope.$apply();
console.log($scope.newPost.text);
}, 500);
};
The Textarea element that is being transliterated.
<textarea
ng-init="addTrnsEngine()"
ng-trim='false'
id="tweet"
class="form-control primaryPostArea"
ng-model="newPost.text"
ng-model-options="{ debounce: 2000 }"
placeholder="Say something...">
</textarea>
So once Google Transliterate does its work and updates the DOM, I am trying to refresh scope with $scope.$apply() after a time out. All the words get updated into new language in the textarea, but the last typed word doesn't update in the scope till the model encounters a new Character.
Use the contenteditable div for input instead of textarea.
The contenteditable directive is:
app.directive("contenteditable", function() {
return {
restrict: "A",
require: "ngModel",
link: function(scope, element, attrs, ngModel) {
function read() {
ngModel.$setViewValue(element.html());
}
ngModel.$render = function() {
element.html(ngModel.$viewValue || "");
};
element.bind("blur keyup change", function() {
scope.$apply(read);
});
}
};
});
Use the contenteditable directive in div tag:
<div contenteditable ng-model="text"></div>
Here is the example how to use contenteditable div using directive. This should solve your problem as it solved mine.

Object html tag data attribute not working with angularjs binding in IE11

I got any application where I need to display file from urls I got in database. Now this file can be an image and it can be a pdf. So I need to set some binding dynamically. I looked on internet and object tag looked promising but it is not working in IE11. It is working fine in Chrome and Firefox. SO that is why I am asking here for help.
I have created a directive just to make sure If we have to do any dom manipulation. Here goes my directive code.
mainApp.directive("displayFile", function () {
return {
restrict: 'AE', // only activate on element attribute
scope: {
displayFile: "=",
fileType:"="
},
link: function (scope, elem, attrs) {
scope.filePath = "";
var element = angular.element(elem);
// observe the other value and re-validate on change
scope.$watch('displayFile', function (val) {
if (val !== "") {
scope.filePath = val;
scope.type="application/"+ fileType;
//element.attr("data", scope.filePath)
}
});
},
template: '<object data="{{filePath}}" type="{{type}}">'
}
});
My html for directive
<div data-display-pdf="fileUrl" file-type="type"></div>
Attaching an image also for IE and Chrome/FF output
Above image is a comparison between IE and FF
Final cut of directive which is working on IE11, Chrome and Firefox
use it like
<div data-display-file="fileObject"></div>
where fileObject is like
$scope.fileObject = {
fileUrl: "",
type: ""
}
mainApp.directive("displayFile", function () {
var updateElem = function (element) {
return function (displayFile) {
element.empty();
var objectElem = {}
if (displayFile && displayFile.type !== "") {
if (displayFile.type === "pdf") {
objectElem = angular.element(document.createElement("object"));
objectElem.attr("data", displayFile.fileUrl);
objectElem.attr("type", "application/pdf");
}
else {
objectElem = angular.element(document.createElement("img"));
objectElem.attr("src", displayFile.fileUrl);
}
}
element.append(objectElem);
};
};
return {
restrict: "EA",
scope: {
displayFile: "="
},
link: function (scope, element) {
scope.$watch("displayFile", updateElem (element));
}
};
});

How to enable double tap on an Angular Js application

I've created an application which enables the user to double click on an item to edit. I'd like to allow the same functionality on mobile devices, meaning the user would double tap to edit the item.
What is the simplest way to implement this? I'd rather not use any additional library (I have heard of Hammer and AngularTouch but haven't used neither before) nor jQuery (in my app I completely forgone jQuery).
If there the only way to implement this is using a library, what would be lightest and easiest?
Many thanks
EDIT: Adding code
This is my controller for editing the item:
// Double click to edit products
$scope.editItem = function (item) {
item.editing = true;
};
$scope.doneEditing = function (item) {
item.editing = false;
$http.put('/api/users?id' + $scope.User.id, $scope.User);
};
$scope.cancelEditing = function (item) {
item.editing = false;
};
$scope.deleteItem = function (item) {
delete $scope.User.todos[item.id];
$http.put('/api/users?id' + $scope.User.id, $scope.User);
};
And this is the my template (Jade)
p(ng-dblclick="editItem(todo)", ng-hide="todo.editing").todo-title
span {{todo.content}}
form(ng-submit="doneEditing(todo)" ng-show="todo.editing", class="inline-editing-2")
input(type="text", class="form-control", ng-model="todo.content")
div.btn-block
button(class="btn btn-success mr-1", ng-show="todo.editing", ng-click="cancelEditing(todo)")
span(ng-click="doneEditing(todo)").fa.fa-check-circle
button(class="btn btn-warning mr-1", ng-show="todo.editing", ng-click="cancelEditing(todo)")
span(ng-click="cancelEditing(todo)").fa.fa-times-circle
So as you can see I use ng-doubleclick to fire my function. I'd need something like ng-double-tab to fire up the double tap. I've been reading more about Hammer and will use Angular Hammer for double tap but I'm not sure how it works...
You can use ios-dblclick, a directive I wrote to handle double click event on mobile browser (write it for iOS, but works on other browsers). It has no dependency and works like ng-dblclick.
It's available here on Github.
Here's an example
<div ios-dblclick="removePhoto()"></div>
Here is the code of this directive
app.directive('iosDblclick',
function () {
const DblClickInterval = 300; //milliseconds
var firstClickTime;
var waitingSecondClick = false;
return {
restrict: 'A',
link: function (scope, element, attrs) {
element.bind('click', function (e) {
if (!waitingSecondClick) {
firstClickTime = (new Date()).getTime();
waitingSecondClick = true;
setTimeout(function () {
waitingSecondClick = false;
}, DblClickInterval);
}
else {
waitingSecondClick = false;
var time = (new Date()).getTime();
if (time - firstClickTime < DblClickInterval) {
scope.$apply(attrs.iosDblclick);
}
}
});
}
};
});
You could always implement your own double tap directive. Start by looking at touchstart and touchend . Bind to these events, and check for multiple taps within some designated period of time.
As far as libraries, we've used this to handle doubletaps for mobile devices in angular
https://github.com/technoweenie/jquery.doubletap
The solution above is not working on my IOS - But I found an other Solution, which is working fine on my IPhone:
Just sharing for you:
http://jsfiddle.net/9Ymvt/3397/
fessmodule.directive('onDoubleClick', function($timeout) {
return {
restrict: 'A',
link: function($scope, $elm, $attrs) {
var clicks=0;
$elm.bind('click', function(evt) {
console.log('clicked');
clicks++;
if (clicks == 1) {
$timeout(function(){
if(clicks == 1) {
//single_click_callback.call(self, event);
} else {
$scope.$apply(function() {
console.log('clicked');
$scope.$eval($attrs.onDoubleClick)
});
}
clicks = 0;
}, 300);}
});
}
};
});

Has anyone found a way to handle code in WYSIHTML5 pleasantly?

I'm using WYSIHTML5 Bootstrap ( http://jhollingworth.github.com/bootstrap-wysihtml5 ), based on WYSIHTML5 ( https://github.com/xing/wysihtml5 ) which is absolutely fantastic at cleaning up HTML when copy pasting from websites.
I'd like to be able to handle code into the editor, and then highlight the syntax with HighlightJS.
I've created a new button and replicated the method used in wysihtml5.js to toggle bold <b> on and off, using <pre> instead:
(function(wysihtml5) {
var undef;
wysihtml5.commands.pre = {
exec: function(composer, command) {
return wysihtml5.commands.formatInline.exec(composer, command, "pre");
},
state: function(composer, command, color) {
return wysihtml5.commands.formatInline.state(composer, command, "pre");
},
value: function() {
return undef;
}
};
})(wysihtml5)
But that's not enough. The editor hides the tags when editing. I need to be able to wrap my content in both <pre>and <code>ie. <pre><code></code></pre>.
This means writing a different function than the one used by wysihtml5, and I don't know how... Could anyone help me with that?
Here's the code for the formatInline function in wysihtml5:
wysihtml5.commands.formatInline = {
exec: function(composer, command, tagName, className, classRegExp) {
var range = composer.selection.getRange();
if (!range) {
return false;
}
_getApplier(tagName, className, classRegExp).toggleRange(range);
composer.selection.setSelection(range);
},
state: function(composer, command, tagName, className, classRegExp) {
var doc = composer.doc,
aliasTagName = ALIAS_MAPPING[tagName] || tagName,
range;
// Check whether the document contains a node with the desired tagName
if (!wysihtml5.dom.hasElementWithTagName(doc, tagName) &&
!wysihtml5.dom.hasElementWithTagName(doc, aliasTagName)) {
return false;
}
// Check whether the document contains a node with the desired className
if (className && !wysihtml5.dom.hasElementWithClassName(doc, className)) {
return false;
}
range = composer.selection.getRange();
if (!range) {
return false;
}
return _getApplier(tagName, className, classRegExp).isAppliedToRange(range);
},
value: function() {
return undef;
}
};
})(wysihtml5);
Got the answer from Christopher, the developer of wysihtml5:
wysihtml5.commands.formatCode = function() {
exec: function(composer) {
var pre = this.state(composer);
if (pre) {
// caret is already within a <pre><code>...</code></pre>
composer.selection.executeAndRestore(function() {
var code = pre.querySelector("code");
wysihtml5.dom.replaceWithChildNodes(pre);
if (code) {
wysihtml5.dom.replaceWithChildNodes(pre);
}
});
} else {
// Wrap in <pre><code>...</code></pre>
var range = composer.selection.getRange(),
selectedNodes = range.extractContents(),
pre = composer.doc.createElement("pre"),
code = composer.doc.createElement("code");
pre.appendChild(code);
code.appendChild(selectedNodes);
range.insertNode(pre);
composer.selection.selectNode(pre);
}
},
state: function(composer) {
var selectedNode = composer.selection.getSelectedNode();
return wysihtml5.dom.getParentElement(selectedNode, { nodeName: "CODE" }) && wysihtml5.dom.getParentElement(selectedNode, { nodeName: "PRE" });
}
};
... and add this to your toolbar:
<a data-wysihtml5-command="formatCode">highlight code</a>
Many thanks Christopher!!
I fork today bootstrap-wysihtml5 project and add code highlighting support using Highlight.js.
You can check demo at http://evereq.github.com/bootstrap-wysihtml5 and review source code https://github.com/evereq/bootstrap-wysihtml5. It's basically almost same code as from Christopher, together with UI changes and embeded inside bootstrap version of editor itself.

Categories