How to add the placeholder in tinymce v4.0 - angularjs

Hi is there a way to add a placeholder for tinymce v4.0 ? I need html 5 placeholder implementation in tinymce but it is not there by default.
<textarea id="Text" placeholder="Create your prompt here." ui-tinymce="$ctrl.tinymceOptions" ng-model="$ctrl.tmcModel"></textarea>

Assuming one is using tinyMCE 4, you could add a placeholder upon init, and then remove it on focus. Remember TinyMCE uses an iframe.
Needs to be polished for being more efficient, but here is a quick approach:
tinymce.init({
//here all the rest of the options
//xxxxx
//Add the placeholder
setup: function (editor) {
editor.on('init', function(){
if (tinymce.get('Text').getContent() == ''){
tinymce.get('Text').setContent("<p id='#imThePlaceholder'>Your nice text here!</p>");
}
},
//and remove it on focus
editor.on('focus',function(){
$('iframe').contents().find('#imThePlaceholder').remove();
}),
})

This worked for me.
var iframe = document.getElementsByTagName('iframe')[0];
iframe.style.resize = 'vertical';

Related

Convert Quill Delta to HTML

How do I convert Deltas to pure HTML? I'm using Quill as a rich text editor, but I'm not sure how I would display the existing Deltas in a HTML context. Creating multiple Quill instances wouldn't be reasonable, but I couldn't come up with anything better yet.
I did my research, and I didn't find any way to do this.
Not very elegant, but this is how I had to do it.
function quillGetHTML(inputDelta) {
var tempCont = document.createElement("div");
(new Quill(tempCont)).setContents(inputDelta);
return tempCont.getElementsByClassName("ql-editor")[0].innerHTML;
}
Obviously this needs quill.js.
I guess you want the HTML inside it. Its fairly simple.
quill.root.innerHTML
If I've understood you correctly, there's a quill thread of discussion here, with the key information you're after.
I've quoted what should be of most value to you below:
Quill has always used Deltas as a more consistent and easier to use (no parsing)
data structure. There's no reason for Quill to reimplement DOM APIs in
addition to this. quill.root.innerHTML or document.querySelector(".ql-editor").innerHTML works just fine (quill.container.firstChild.innerHTML is a bit more brittle as it depends on child ordering) and the previous getHTML implementation did little more than this.
Simple, solution is here:
https://www.scalablepath.com/blog/using-quill-js-build-wysiwyg-editor-website/
The main code is:
console.log(quill.root.innerHTML);
This is a very common confusion when it comes to Quilljs. The thing is you should NOT retrieve your html just to display it. You should render and display your Quill container just the same way you do when it is an editor. This is one of the major advantages to Quilljs and the ONLY thing you need to do is:
$conf.readOnly = true;
This will remove the toolbar and make the content not editable.
I have accomplished it in the backend using php.
My input is json encoded delta and my output is the html string.
here is the code , if it is of any help to you.This function is still to handle lists though and some other formats but you can always extend those in operate function.
function formatAnswer($answer){
$formattedAnswer = '';
$answer = json_decode($answer,true);
foreach($answer['ops'] as $key=>$element){
if(empty($element['insert']['image'])){
$result = $element['insert'];
if(!empty($element['attributes'])){
foreach($element['attributes'] as $key=>$attribute){
$result = operate($result,$key,$attribute);
}
}
}else{
$image = $element['insert']['image'];
// if you are getting the image as url
if(strpos($image,'http://') !== false || strpos($image,'https://') !== false){
$result = "<img src='".$image."' />";
}else{
//if the image is uploaded
//saving the image somewhere and replacing it with its url
$imageUrl = getImageUrl($image);
$result = "<img src='".$imageUrl."' />";
}
}
$formattedAnswer = $formattedAnswer.$result;
}
return nl2br($formattedAnswer);
}
function operate($text,$ops,$attribute){
$operatedText = null;
switch($ops){
case 'bold':
$operatedText = '<strong>'.$text.'</strong>';
break;
case 'italic':
$operatedText = '<i>'.$text.'</i>';
break;
case 'strike':
$operatedText = '<s>'.$text.'</s>';
break;
case 'underline':
$operatedText = '<u>'.$text.'</u>';
break;
case 'link':
$operatedText = ''.$text.'';
break;
default:
$operatedText = $text;
}
return $operatedText;
}
Here's a full function using quill.root.innerHTML, as the others didn't quite cover the complete usage of it:
function quillGetHTML(inputDelta) {
var tempQuill=new Quill(document.createElement("div"));
tempQuill.setContents(inputDelta);
return tempQuill.root.innerHTML;
}
This is just a slight different variation of km6 's answer.
For Quill version 1.3.6, just use:
quill.root.innerHTML;
Try it online: https://jsfiddle.net/Imabot/86dtuhap/
Detailed explaination on my blog
This link if you have to post the Quill HTML content in a form
quill.root.innerHTML on the quill object works perfectly.
$scope.setTerm = function (form) {
var contents = JSON.stringify(quill.root.innerHTML)
$("#note").val(contents)
$scope.main.submitFrm(form)
}
I put together a node package to convert html or plain text to and from a Quill Delta.
My team used it to update our data model to include both Quill's Delta and HTML. This allows us to render on the client without an instance of Quill.
See node-quill-converter.
It features the following functions:
- convertTextToDelta
- convertHtmlToDelta
- convertDeltaToHtml
Behind the scenes it uses an instance of JSDOM. This may make it best suited for migration scripts as performance has not been tested in a typical app request lifecycle.
Try
console.log ( $('.ql-editor').html() );
Here is how I did it, for you Express folks. It seems to have worked very well in conjunction with express-sanitizer.
app.js
import expressSanitizer from 'express-sanitizer'
app.use(expressSanitizer())
app.post('/route', async (req, res) => {
const title = req.body.article.title
const content = req.sanitize(req.body.article.content)
// Do stuff with content
})
new.ejs
<head>
<link href="https://cdn.quilljs.com/1.3.2/quill.snow.css" rel="stylesheet">
</head>
...
<form action="/route" method="POST">
<input type="text" name="article[title]" placeholder="Enter Title">
<div id="editor"></div>
<input type="submit" onclick="return quillContents()" />
</form>
...
<script src="https://cdn.quilljs.com/1.3.2/quill.js"></script>
<script>
const quill = new Quill('#editor', {
theme: 'snow'
})
const quillContents = () => {
const form = document.forms[0]
const editor = document.createElement('input')
editor.type = 'hidden'
editor.name = 'article[content]'
editor.value = document.querySelector('.ql-editor').innerHTML
form.appendChild(editor)
return form.submit()
}
</script>
express-sanitizer (https://www.npmjs.com/package/express-sanitizer)
document.forms (https://developer.mozilla.org/en-US/docs/Web/API/Document/forms)
My view only has one form, so I used document.forms[0], but if you have multiple or may extend your view in the future to have multiple forms, check out the MDN reference.
What we are doing here is creating a hidden form input that we assign the contents of the Quill Div, and then we bootleg the form submit and pass it through our function to finish it off.
Now, to test it, make a post with <script>alert()</script> in it, and you won't have to worry about injection exploits.
That's all there is to it.
Here is a proper way to do it.
var QuillDeltaToHtmlConverter = require('quill-delta-to-html').QuillDeltaToHtmlConverter;
// TypeScript / ES6:
// import { QuillDeltaToHtmlConverter } from 'quill-delta-to-html';
var deltaOps = [
{insert: "Hello\n"},
{insert: "This is colorful", attributes: {color: '#f00'}}
];
var cfg = {};
var converter = new QuillDeltaToHtmlConverter(deltaOps, cfg);
var html = converter.convert();
Refer https://github.com/nozer/quill-delta-to-html
For a jQuery-style solution that allows getting and setting the Quill value I am doing the following:
Quill.prototype.val = function(newVal) {
if (newVal) {
this.container.querySelector('.ql-editor').innerHTML = newVal;
} else {
return this.container.querySelector('.ql-editor').innerHTML;
}
};
let editor = new Quill( ... );
//set the value
editor.val('<h3>My new editor value</h3>');
//get the value
let theValue = editor.val();
quill-render looks like it's what you want. From the docs:
var render = require('quill-render');
render([
{
"attributes": {
"bold": true
},
"insert": "Hi mom"
}
]);
// => '<b>Hi mom</b>'
If you want to render quill using nodejs, there is a package quite simple based on jsdom, usefull to render backside (only one file & last update 18 days from now) render quill delta to html string on server
Just use this clean library to convert from delta from/to text/html
node-quill-converter
example:
const { convertDeltaToHtml } = require('node-quill-converter');
let html = convertDeltaToHtml(delta);
console.log(html) ; // '<p>hello, <strong>world</strong></p>'

how to toggle medium editor option on click using angularjs

I am trying to toggle the medium editor option (disableEditing) on button click. On the click the value for the medium editor option is changed but the medium editor does not use 'updated' value.
AngularJS Controller
angular.module('myApp').controller('MyCtrl',
function MyCtrl($scope) {
$scope.isDisableEdit = false;
});
Html Template
<div ng-app='myApp' ng-controller="MyCtrl">
<span class='position-left' medium-editor ng-model='editModel' bind-options="{'disableEditing': isDisableEdit, 'placeholder': {'text': 'type here'}}"></span>
<button class='position-right' ng-click='isDisableEdit = !isDisableEdit'>
Click to Toggle Editing
</button>
<span class='position-right'>
toggle value - {{isDisableEdit}}
</span>
</div>
I have created a jsfiddle demo.
I think initialising medium editor on 'click' could solve the issue, but i am not sure how to do that either.
using thijsw angular medium editor and yabwe medium editor
For this specific use case, you could try just disabling/enabling the editor when the button is clicked:
var editor = new MediumEditor(iElement);
function onClick(event) {
if (editor.isActive) {
editor.destroy();
} else {
editor.setup();
}
}
In the above example, the onClick function is a handler for that toggle button you defined.
If you're just trying to enable/disable the user's ability to edit, I think those helpers should work for you.
MediumEditor does not currently support changing configuration options on an already existing instance. So, if you were actually trying to change a value for a MediumEditor option (ie disableEditing) you would need to .destroy() the previous instance, and create a new instance of the editor:
var editor = new MediumEditor(iElement),
editingAllowed = true;
function onClick(event) {
editor.destroy();
if (editingAllowed) {
editor = new MediumEditor(iElement, { disableEditing: true });
} else {
editor = new MediumEditor(iElement);
}
editingAllowed = !editingAllowed;
}
Once instantiated, you can use .setup() and .destroy() helper methods to tear-down and re-initialize the editor respectively. However, you cannot pass new options unless you create a new instance of the editor itself.
One last note, you were calling the init() method above. This method is not officially supported or documented and it may be going away in future releases, so I would definitely avoid calling that method if you can.
Or you could just use this dirty hack : duplicate the medium-editor element (one with disableEditing enabled, the other with disableEditing disabled), and show only one at a time with ng-show / ng-hide :)
<span ng-show='isDisableEdit' class='position-left' medium-editor ng-model='editModel' bind-options="{'disableEditing': true ,'disableReturn': isDisableEdit, 'placeholder': {'text': 'type here'}}"></span>
<span ng-hide='isDisableEdit' class='position-left' medium-editor ng-model='editModel' bind-options="{'disableEditing':false ,'disableReturn': isDisableEdit, 'placeholder': {'text': 'type here'}}"></span>
You can see jsfiddle.

Could we add our menu items in Gitkit Starter kit "Sign In cum User Info " ( #navbar )?

Could we add our menu items in Starter kit Gitkit NavBar ?
There are two list items in the drop down : Manage Account and Sign Out.
Is it possible to add a third option with a URL link ( say like Update Profile ) ?
The html for the #navbar gets loaded through Javascript code of Gitkit.
I use GAE Python.
Possible solutions which I could think of are :
After my webpage loads completely, I could add my own <li> items to the list of dropdown menu provided by #navbar.
Or
Write a custom "Sign In cum User Info " ( #navbar ) widget.
Is there a better and simpler approach ?
MY REQUEST TO GITKIT TEAM FOR ENHANCEMENT
It would be great if we could provide our custom menu items along with their URL links as options to below JS code which loads #navbar :
<script type=text/javascript>
window.google.identitytoolkit.signInButton(
'#navbar', // accepts any CSS selector
{
widgetUrl: "http://www.mywebsite.com/oauth2callback",
signOutUrl: "/",
// Example of possible solution ( my suggestion ):
custom_menu_item__1__name : "item_1", // My Custom Menu Item 1
custom_menu_item__1__link : "http://site/link_url_1",
::
custom_menu_item__n__name : "item_1", // My Custom Menu Item n
custom_menu_item__n__link : "http://site/link_url_1",
}
);
</script>
UPDATE
Temporary Fix = I have added the needed menu options using jquery temporarily. Code snippet provided below to help anyone with similar needs till official solution arrives :
On page load,
custom_menu_add_job_id = setInterval(function(){
add_custom_menu();
}, 5000);
function add_custom_menu(){
if ($("#navbar").find(".gitkit-user-card-menu").length){
$(".gitkit-user-card-menu").append($("<li class='gitkit-user-card-menuitem' id='smh_user_profile' tabindex='0'> <img src='/images/person_32x32.png' class='user_profile_menu_icon' > Profile </li>")
.click(function(){
window.location.href = window.location.protocol + "//" + window.location.host + "/user/";
})
);
clearInterval(custom_menu_add_job_id);
}
}
If you want, you could check it live at ShowMyHall.
Customized menu items are now supported in Google Identity Toolkit javascript widget. Examples:
window.google.identitytoolkit.signInButton(
'#navbar', // accepts any CSS selector
{
widgetUrl: "...",
dropDownMenu: [
{
'label': 'Check Configuration',
'url': '/config'
},
{
'label': 'Sign out',
'handler': function() {google.identitytoolkit.signOut();}
},
{
'label': 'Manage Account',
'handler': function() {google.identitytoolkit.manageAccount();}
},
]
};
Until this feature arrives, I also implemented a similar temporary fix that you outlined at the end of your question. I got around using a timer as follows (note that my gitkit is using the div login):
$(window).load(function() {
$("#login").hover(function() {
add_custom_menu_items();
})
});
function add_custom_menu_items(){
if ($("#login").find(".gitkit-user-card-menu").length == 1){
if ($("#my_data_link").length == 0) {
$(".gitkit-user-card-menu li:eq(0)").after($('<li class="gitkit-user-card-menuitem" id="my_data_link" tabindex="0">My data</li>'));
}
}
}
Basically when you hover over the div it adds the menu item, but only if it hasn't already been added.
The navbar drop down menu does not support images but if you really need that, here's a hacky way to do it in jquery:
var config = {...}; // your config which includes the custom drop down menu.
// Render button on load. (now supported)
window.onload = function() {
window.google.identitytoolkit.signInButton(
'#navbar', // accepts any CSS selector
config);
// This will modify the html content of the first option in drop down menu.
// Make menu dom changes.
jQuery('#navbar li:first-child').html('<img src="img.png">My Label');
}

Typeahead Value in Bootstrap UI

I'm working on an app using AngularJS and Bootstrap UI. I've been fumbling my way through using the Typeahead control in Bootstrap UI.
Here's my Plunker
My challenge is I want the user to have the option of choosing an item, but not required to do so. For instance, right now, if you type Test in the text field and press "Enter", Test will be replaced with Alpha. However, I really want to use Test. The only time I want the text to be replaced is when someone chooses the item from the drop down list. My markup looks like the following:
<input type="text" class="form-control" placeholder="Search..."
ng-model="query"
typeahead="result as result.name for result in getResults($viewValue)"
typeahead-template-url="result.html" />
How do I give the user the option of choosing an item, but allow them to still enter their own text?
The issue is that both Enter and Tab confirm the selection of the currently highlighted item and Typeahead automatically selects an item as soon as you start to type.
If you want, you can click off the control to lose focus or hit Esc to exit out of typeahead, but those might be difficult to communicate to your users.
There's an open request in Bootstrap Ui to not auto select / highlight the first item
One solution is to populate the first item with the contents of the query thus far, so tabbing or entering will only confirm selection of the current query:
JavaScript:
angular.module('plunker', ['ui.bootstrap'])
.filter('concat', function() {
return function(input, viewValue) {
if (input && input.length) {
if (input.indexOf(viewValue) === -1) {
input.unshift(viewValue);
}
return input;
} else {
return [];
}};})
HTML:
<input type="text"
ng-model="selected"
typeahead="state for state in states | filter:$viewValue | limitTo:8 | concat:$viewValue"
class="form-control">
Demo in Plunker
I came across this same situation and found no good answers so I implemented it myself in ui-bootstrap Here is the relevant answer. This is probably not the best route to take, but it does get the job done. It makes the first result in the typeahead to be what you're currently typing, so if you tab or enter off of it, it's selected -- you must arrow-down or select another option to get it.
Here is the modified ui-bootstrap-tpls.js file
I added a mustMouseDownToMatch property/attribute to the directive, like:
<input type="text" ng-model="selected" typeahead="item for item in typeaheadOptions | filter:$viewValue" typeahead-arrow-down-to-match="true">
And the javascript:
var mustArrowDownToMatch = originalScope.$eval(attrs.typeaheadArrowDownToMatch) ? originalScope.$eval(attrs.typeaheadArrowDownToMatch) : false;
I also added this function which will put the current text into the first item of the typeahead list, and make it the selected item:
var setFirstResultToViewValue = function (inputValue) {
scope.matches.splice(0, 0, {
id: 0,
label: inputValue,
model: inputValue
});
}
And that is called in the getMatchesAsync call in the typeahead directive:
var getMatchesAsync = function(inputValue) {
// do stuff
$q.when(parserResult.source(originalScope, locals)).then(function(matches) {
// do stuff
if (matches.length > 0) {
// do stuff
}
if (mustArrowDownToMatch) {
setFirstResultToViewValue(inputValue);
scope.activeIdx = 0;
setTypeaheadPosition();
}
// do stuff
};

How to set ime-mode in element input angular?

I read at http://docs.angularjs.org/api/ng.directive:input
<input
ng-model="{string}"
[name="{string}"]
[required]
[ng-required="{boolean}"]
[ng-minlength="{number}"]
[ng-maxlength="{number}"]
[ng-pattern="{string}"]
[ng-change="{string}"]>
</input>
How can I set ime-mode in this code ?
You are able to add another attributes to input (it won't cause any errors), so simply add, for example, style="ime-mode: disabled" (or add class attribute and set ime-mode in your css - it's better way in my opinion)
ime-mode is not supported in Chrome, Safari, or Opera. Read more about ime-mode and its compatibility here: https://developer.mozilla.org/en-US/docs/Web/CSS/ime-mode
If you do want to use it in supported versions of IE and Firefox, here are two methods:
Use a class:
<input class="ime">
CSS:
.ime {
ime-mode: disables;
}
Inline style (not recommended):
Also note that in your markup is not valid. input tags are self closing and do not need </input>. Also, with angular, you probably don't need the name and required attributes. You markup should probably look like this:
<input
ng-model="myModel"
ng-required="required"
ng-minlength="minLength"
ng-maxlength="maxLength"
ng-pattern="regex"
ng-change="myChangeFunction()"
>
to data-bind in these values with Angular:
app.controller('myCtrl', function($scope) {
$scope.myModel = 'input value here';
$scope.required = true;
$scope.minLength = 5;
$scope.maxLength = 20;
$scope.regex = '/put your regex here/';
$scope.myChangeFunction() {
//code to run when value of input is changed
};
});

Resources