WYSIHTML5 editor does not focus the editable area while inserting unordered list in Chrome - wysihtml5

In WYSIHTML5 editor, when we add unordered list, it does not focus on the editable area in the Google Chrome browser. However, it works fine for other options such as bold, italic, etc. I have searched, but have been unable to find a solution for this bug.

In WYSIHTML5 js, search line wysihtml5.commands.insertUnorderedList and further look for
composer.commands.exec("formatBlock", "div", tempClassName);
tempElement = doc.querySelector("." + tempClassName);
isEmpty = tempElement.innerHTML === "" || tempElement.innerHTML === wysihtml5.INVISIBLE_SPACE;
composer.selection.executeAndRestoreSimple(function() {
list = wysihtml5.dom.convertToList(tempElement, "ul");
});
if (isEmpty) {
composer.selection.selectNode(list.querySelector("li"));
}
and comment or remove the condition part like :
composer.commands.exec("formatBlock", "div", tempClassName);
tempElement = doc.querySelector("." + tempClassName);
isEmpty = tempElement.innerHTML === "" || tempElement.innerHTML === wysihtml5.INVISIBLE_SPACE;
composer.selection.executeAndRestoreSimple(function() {
list = wysihtml5.dom.convertToList(tempElement, "ul");
});
//if (isEmpty) {
composer.selection.selectNode(list.querySelector("li"));
//}
It worked for me coz the condition isEmpty = tempElement.innerHTML === "" || tempElement.innerHTML === wysihtml5.INVISIBLE_SPACE; can never become true as we have inserted unordered list and editable area is not blank after that. Good luck..!!

Related

How can we validate just the mandatory fields in a form in SAP UI5?

I am trying to create a form which has some mandatory fields that requires validation on form submission.
Could anyone suggest me the best possible way to do that in SAP UI5? The mandatory fields are in greater number, thus i don't want to check all fields separately by their ID.
You can do this in two scenarios. While entering a value, or when submitting the form as in your question.
CheckRequired: function(oEvent) {
var aInputs = [this.getView().byId(oEvent.getSource().getId())];
var sError = false;
jQuery.each(aInputs, function(i, input) {
if (!input.getValue() || input.getValue().length < 1) {
input.setValueState("Error");
input.focus();
sError = true;
} else {
input.setValueState("None");
}
});
return sError;
},
This function is to be used with the onLiveChange property. It checks if the control is filled with at least one character.
If you would like to check everything when you press submit. you could use a function like this with your form:
_onSubmitCheck: function() {
var oForm = this.getView().byId("form").getContent();
var sError = false;
oForm.forEach(function(Field) {
if (typeof Field.getValue === "function") {
if (!Field.getValue() || Field.getValue().length < 1) {
Field.setValueState("Error");
sError = true;
}
else {
Field.setValueState("None");
}
}
});
return sError;
},
It will loop over your form controls to check if the getValue() method exists as part of the control. If that returns yes, it wil check if it has a value of at least 1 character.
There are kind of two ways.
add
"sap.ui5": {
...
"handleValidation": true,
to your manifest.json and type & constraints to your inputs
<Input type="Text" value="{path: 'NoFioriValidationsInDefault', type: 'sap.ui.model.type.String', constraints: { minLength:2 }}" valueLiveUpdate="true" enabled="{= ${editView>/nfvid/enabled} && ${editView>/creating}}" visible="true" width="auto" valueHelpOnly="false" maxLength="0" id="inp_cond_nfvid" required="{editView>/nfvid/required}"/>
This gives just visual feedback to the user, if you need the status in your controller you can either iterate over all the inputs and check them by hand, or use https://github.com/qualiture/ui5-validator
Just by calling
var validator = new Validator();
validator.validate(this.byId("form1"));
if (!validator.isValid()){
//do something additional to drawing red borders? message box?
return;
}
in your controller, the view will mark missing required inputs with the ValueState.ERROR (red borders) and tell you if all inputs inside the supplied control are valid.
I am doing it the old-school way. The input fields do get the required=true property and then I loop over all controls found with this property:
// store view ID to compare with control IDs later
var viewId = this.getView().getId();
jQuery('input[required=required]').each(function () {
// control has wrapper with no id, therefore we need to remove the "-inner" end
var oControl = sap.ui.getCore().byId(this.id.replace(/-inner/g,''));
// CAUTION: as OpenUI5 keeps all loaded views in DOM, ensure that the controls found belong to the current view
if (oControl.getId().startsWith(viewId) && (oControl instanceof sap.m.Input || oControl instanceof sap.m.DatePicker)) {
var val = oControl.getValue();
if (!val) {
oControl.setValueState(sap.ui.core.ValueState.Error);
oControl.openValueStateMessage();
bError = true;
return false;
} else {
oControl.setValueState(sap.ui.core.ValueState.None);
oControl.closeValueStateMessage();
}
}
});
HTH,
Anton

How to force cursive display in ckeditor while typing

<!DOCTYPE html>
<html>
<head>
<script src="http://cdn.ckeditor.com/4.6.2/standard/ckeditor.js"></script>
</head>
<body>
<textarea name="editorUrdu"></textarea>
<script>
CKEDITOR.plugins.addExternal( 'easykeymap', '/ckeditor/plugins/easykeymap', 'plugin.js' );
CKEDITOR.replace( 'editorUrdu',{
extraPlugins: 'easykeymap',
contentsLangDirection: 'rtl'
});
</script>
</body>
</html>
/**
* This work is mine, and yours. You can modify it as you wish.
* #Author: Roni Saha<roni.cse#gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
CKEDITOR.plugins.add('easykeymap',
{
requires : ['wysiwygarea'],
init: function (editor) {
//var keyMaps = CKEDITOR.tools.extend({}, editor.config.easykeymaps || {}, true);
//Not using keymap specification from config variables, but using internally defined ones from param below
function isRegisteredKeystroke(code) {
if(typeof editor.keystrokeHandler.keystrokes[code] != 'undefined') {
console.warn("the keystroke : " + code + " is being attached to another event")
return true;
}
return false;
}
var sm1 = []; var en1 = [];
sm1[192]="؏"; en1[192]="~";
sm1[49]="۱"; en1[49]="1";
sm1[50]="۲"; en1[50]="2";
sm1[51]="۳"; en1[51]="3";
sm1[52]="۴"; en1[52]="4";
sm1[53]="۵"; en1[53]="5";
sm1[54]="۶"; en1[54]="6";
sm1[55]="۷"; en1[55]="7";
sm1[56]="۸"; en1[56]="8";
sm1[57]="۹"; en1[57]="9";
sm1[48]="۰"; en1[48]="0";
sm1[189]="-"; en1[189]="-";
sm1[187]="="; en1[187]="=";
//sm1[8]=""; en1[8]="";//BACKSPACE
var sm2 = []; var en2 = [];
sm2[9]=""; en2[9]="";//TAB
sm2[81]="ق"; en2[81]="Q";
sm2[87]="و"; en2[87]="W";
sm2[69]="ع"; en2[69]="E";
sm2[82]="ر"; en2[82]="R";
sm2[84]="ت"; en2[84]="T";
sm2[89]="ے"; en2[89]="Y";
sm2[85]="ء"; en2[85]="U";
sm2[73]="ی"; en2[73]="I";
sm2[79]="ہ"; en2[79]="O";
sm2[80]="پ"; en2[80]="P";
sm2[219]="'"; en2[219]="{";
sm2[221]="ﷲ"; en2[221]="}";
sm2[220]="÷"; en2[220]="\\";
var sm3 = []; var en3 = [];
//sm3[20]=""; en3[20]="";//CAPSLOCK
sm3[65]="ا"; en3[65]="A";
sm3[83]="س"; en3[83]="S";
sm3[68]="د"; en3[68]="D";
sm3[70]="ف"; en3[70]="F";
sm3[71]="گ"; en3[71]="G";
sm3[72]="ح"; en3[72]="H";
sm3[74]="ج"; en3[74]="J";
sm3[74]="ک"; en3[75]="K";
sm3[76]="ل"; en3[76]="L";
sm3[186]="؛"; en3[186]=":";
sm3[222]=","; en3[222]="\"";
sm3[13]=""; en3[13]="";//ENTER
var sm4 = []; var en4 = [];
//sm4[16]=""; en4[16]="";//SHIFT
sm4[90]="ز"; en4[90]="Z";
sm4[88]="ش"; en4[88]="X";
sm4[67]="چ"; en4[67]="C";
sm4[86]="ط"; en4[86]="V";
sm4[66]="ب"; en4[66]="B";
sm4[78]="ن"; en4[78]="N";
sm4[77]="م"; en4[77]="M";
sm4[188]="،"; en4[188]="<";
sm4[190]="۔"; en4[190]=">";
sm4[191]="/"; en4[191]="?";
sm4[16]=""; en4[16]="";//SHIFT
var keyBoard = {};
keyBoard.Row1 = { "sm" : sm1, "en" : en1 };
keyBoard.Row2 = { "sm" : sm2, "en" : en2 };
keyBoard.Row3 = { "sm" : sm3, "en" : en3 };
keyBoard.Row4 = { "sm" : sm4, "en" : en4 };
function getMappedCharacter(code) {
console.info(code);
if (typeof keyBoard.Row1.sm[code] != 'undefined')
return keyBoard.Row1.sm[code]
else if (typeof keyBoard.Row2.sm[code] != 'undefined')
return keyBoard.Row2.sm[code]
else if (typeof keyBoard.Row3.sm[code] != 'undefined')
return keyBoard.Row3.sm[code]
else if (typeof keyBoard.Row4.sm[code] != 'undefined')
return keyBoard.Row4.sm[code]
else
return false;
}
editor.on( 'key', function( event ) {
var t = event.target;
var mappedCode = getMappedCharacter(event.data.keyCode);
if (mappedCode !== false && !isRegisteredKeystroke(event.data.keyCode)) {
event.cancel();
if(typeof mappedCode == 'function') {
return mappedCode.call(editor, editor);
}
editor.insertText(mappedCode);
}
} );
}
});
I'm using ckeditor for allowing my users to input Urdu text. Like Arabic, Urdu is cursive, and uses distinct ligatures when joined.
I'm using the
editor.on( 'key', function( event ))
event to intercept the
event.data.keyCode
and replace it using
editor.insertText()
function.
So, e.g. if the user types L and A, I replace it with the Urdu
ا (U+0627)
and
ل (U+0644).
Now, after being typed, they both appear as distinct characters, separate from each other. I can press space, or enter, and they both remain as they are. But I would like them to be replaced with their proper equivalent ligature ﻻ which is FEFB in this unicode chart
I see that ckeditor automatically correct this if I switch to Source view. There, inside the <p> block, it shows the separate, disjointed letter's already replace with proper cursive ligature. And it keeps it that way when I switch back from Source view. But whatever is causing this to happen, how can I trigger that to happen while typing?
Attaching images also.
After typing
After going to source view
After returning from source view
When ever you type L and A , editor.insertText() is just append it as two separate stings, instead of combining into one.
<p>"ل"
"ا"
<br>
</p>
that why its not producing desired output.
Added these two line
var $pTag = editor.getSelection().getRanges()[0].startContainer.$; // accessing the p tag
$pTag.innerText = $pTag.innerText+mappedCode; // modifing the inner text
replacing
editor.insertText(mappedCode); // in editor.on( 'key', function( event )
will output as "لا"
above fix has some issues to deal with like linebeak(new line)
Updated
replace the below snippet
var $pTag = editor.getSelection().getRanges()[0].startContainer.$;
var innerText =$pTag.innerText; // accessing the p tag data
$pTag.innerText = ""; // removing the existing data
editor.insertHtml(innerText+mappedCode); // concat with new string
with
editor.insertText(mappedCode); // in editor.on( 'key', function( event )
Example: codepen

CodeMirror custom mode - how to apply styles on keywords?

I'm trying to write my own CodeMirror mode as documented here.
My objective is to change the color of specific keywords. For example, any "aaa" word needs to be red and any "bbb" word needs to be blue. Any other words need to have the default color.
This is my unsuccessful attempt (see jsfiddle). How to make this work?
HTML:
<textarea rows="4" cols="30" id="cm" name="cm">aaa bbb ccc</textarea>
CSS:
.style1 { color: red; }
.style2 { color: blue; }
Javascript:
CodeMirror.defineMode("mymode", function() {
return {
token: function(stream,state) {
if (stream.match("aaa") ) {
console.log("aaa found");
while ((ch = stream.next()) != null)
if (ch == " " && stream.next() == " ") break;
return "style1";
}
else if (stream.match("bbb") ) {
console.log("bbb found");
while ((ch = stream.next()) != null)
if (ch == " " && stream.next() == " ") break;
return "style2";
}
else
return null;
}
};
});
var editor = CodeMirror.fromTextArea(document.getElementById('cm'), {
mode: "mymode",
lineNumbers: true
});
You had two problems.
CodeMirror prefixed cm- to classes used to style tokens. The styles in your CSS have to account for that.
You were skipping the rest of the line after finding "aaa" or "bbb", althrough your description of your goal sounds like you didn't want to do that.
I've fixed both in the jsfiddle. You may also want to only match full words (currently fooaaabar also has aaa highlighted). To do that, first have your tokenizer read a whole word (stream.eatWhile(/\w/)), and then see if the resulting word is one of the words you are looking for (stream.current() == "aaa").

AngularJS: Move to next form input element after successful validation

I have written a custom directive for validation of my form fields. When certain criteria are met (i.e. it is dirty and valid), I want to set the focus automatically to the next input element. This is a requirement from my users, such that they can move through the forms most efficiently.
The simplified directive looks like this:
directive('custom', ['$parse', function($parse) {
return {
restrict: 'A',
require: ['ngModel', '^ngController'],
link: function(scope, element, attrs, ctrls) {
var model=ctrls[0], form=ctrls[1];
scope.next = function(){
return model.$valid
}
scope.$watch(scope.next, function(newValue, oldValue){
if (newValue && model.$dirty){
???
}
})
Now my question is: how can I identify
- the next input element (which is the next sibling) or possibly via the tabindex
- and focus on it
without using Jquery?
For me, it is currently not clear, how to get to the next input element from the available "scope" or "element" attributes without Jquery; and JQlite does nothave a "focus" method. Basically, I need a working substitute for ??? in my code.
Any help is highly appreciated. Thanks
Juergen
You can use [0] to get the underlying input element (which has a focus() function) from the angular/jqLite object (which doesn't).
app.directive('custom', ['$parse', function($parse) {
return {
restrict: 'A',
require: ['ngModel'],
link: function(scope, element, attrs, ctrls) {
var model=ctrls[0], form=ctrls[1];
scope.next = function(){
return model.$valid;
}
scope.$watch(scope.next, function(newValue, oldValue){
if (newValue && model.$dirty)
{
var nextinput = element.next('input');
if (nextinput.length === 1)
{
nextinput[0].focus();
}
}
})
}
}
}])
http://jsfiddle.net/Y2XLA/
element.next().focus() might not work if you have a complex form and input are nested into different divs.
I ended writing this directive (here I move the focus on Enter, but can be adapted to whatever event):
.directive('enterToTab', function($timeout) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var procAttr = 'data-ett-processed';
$timeout(function() { // Use $timeout to run the directive when the DOM is fully rendered
var formElements = element[0].querySelectorAll('input:not([' + procAttr + '="true"]), select:not([' + procAttr + '="true"]), textarea:not([' + procAttr + '="true"])');
// Run through all elements in form
var formElementsLength = formElements.length;
for (var i = 0; i < formElementsLength; i++) { // Add tabindex attribute
formElements[i].setAttribute('tabindex', i + 1);
// Go to next element on Enter key press
formElements[i].addEventListener('keypress', function(event) {
if (event.keyCode === 13) { // Enter
// Prevent Angular from validating all the fields and submitting
if (event.target.tagName !== 'TEXTAREA') { // Not on textarea, otherwise not possible to add new line
event.stopPropagation();
event.preventDefault();
}
var nextIndex = parseInt(event.target.getAttribute('tabindex')) + 1;
// Move focus to next element
// TODO: find next visible element
var nextElem = element[0].querySelector('[tabIndex="' + (nextIndex) + '"]');
if (nextElem) {
nextElem.focus();
}
}
});
formElements[i].setAttribute(procAttr, true); // Add attribute to prevent adding 2 listeners on same element
}
});
}
};
});
Event should be in HTML component (keypress) = "keyFocus($event)"
Method shoulb be like .ts file.
keyFocus(input1){
input1.srcElement.nextElementSibling.focus();
}
AngularJS already contains a light version of jQuery so you can as well use it...
http://docs.angularjs.org/api/angular.element
You could try something like this:
element.next().focus()

Distinguishing between the blog title and the post title when creating a custom theme for Tumblr?

Long time lurker, first time asker.
I am currently writing a custom theme for Tumblr blogs to embed a widget after every post, regardless of their type. This widget requires the post's title, and if there is none, then it takes the blog's title.
According to Tumblr, {Title} refers to the blog title. However, if we have a Text post or a Chat post, {Title} refers to the post title.
Here is my code:
var title;
if ('{PostType}' === 'text' || '{PostType}' === 'chat')
title = '{Title}';
else if ('{PostType}' === 'photo' || '{PostType}' === 'photoset' || '{PostType}' === 'audio' || '{PostType}' === 'video')
title = '{PlaintextCaption}';
else if ('{PostType}' === 'quote')
title = '{PlaintextQuote}';
else if ('{PostType}' === 'link')
title = '{PlaintextName}';
else if ('{PostType}' === 'answer')
title = '{PlaintextQuestion}';
if (title === '')
title = '{Title}';
If I have a Photo post with no caption for example, then title will be correctly set to the blog title. But if I have a Text post with no title, then title will be set to [empty string] instead of the blog title.
So my question is: how can I get the blog title when I am inside of a Text or Chat post?
What you can do is set the blog title as a variable before you enter into your {block:Posts}. So in your <head>, you can do this:
var blogTitle = '{Title}';
Then modify your code like this:
var title;
if ('{PostType}' === 'text' || '{PostType}' === 'chat')
title = blogTitle;
else if ('{PostType}' === 'photo' || '{PostType}' === 'photoset' || '{PostType}' === 'audio' || '{PostType}' === 'video')
title = '{PlaintextCaption}';
else if ('{PostType}' === 'quote')
title = '{PlaintextQuote}';
else if ('{PostType}' === 'link')
title = '{PlaintextName}';
else if ('{PostType}' === 'answer')
title = '{PlaintextQuestion}';
if (title === '')
title = '{Title}';
Also, be careful, as {PostType} will never == 'photoset'. Photoset post types always come out as 'photo'. What I usually do to counter that when making class names is this:
class="type-{PostType}{block:Photoset}set{/block:Photoset}
...and the output will be class="type-photoset".