Draft.js applying styles to many individual characters - javascript

I have been trying to tailor Draft.js into a sortof IDE with custom syntax highlighting, I am aware Prism exists including Prism for Draft.js but it lacks the features I require. I am not using Draft.js in the manner that is expected but it feels like it is something it should be able to handle.
In the Draft.js page Complex Inline Styles under Model they show an array of styles and while it is labelled as a "Model" and "In essence", it feels like that it may be possible to do something like that.
I have initially gone the route laid out in this thread Draftjs apply inline styling to text programatically however potentially every character is going to have a different style to the next and it isn't practical to do this for every character potentially on every key press. But generating an array of styles would be really easy to manage.
The documentation for Draft.js feels incomplete and I keep coming to dead ends whichever route I go, such as that there's no OrderedSet page. I know I'm meant to show code that I've tried but it's mostly that SO thread I've tried but moved it into an onChange function and made it work with onChange as well as toggling styles, I included it but I'm pretty sure I'd be told off for not providing anything.
I'm completely stumped and a little frustrated, there's probably a method there I'm looking for but I just can't find it.
const onChange = e => {
const selectionState = e.getSelection();
const newSelection = selectionState.merge({
anchorOffset: 0,
focusOffset: 1,
}); //Selects the first character of the line that was edited
const editorStateWithNewSelection = EditorState.forceSelection(e, newSelection);
const editorStateWithStyles = RichUtils.toggleInlineStyle(editorStateWithNewSelection,'red'); //There's no method to even just set a style, only toggle
const editorStateWithStylesAndPreviousSelection = EditorState.forceSelection(
editorStateWithStyles,
selectionState,
);
setEditorState(editorStateWithStylesAndPreviousSelection);
}

Related

How to cut off function from global scope

I have an idea for a game where people can type in some simple instructions for their character like player.goLeft() or player.attackInFront() and for that I have people type their code into a text box and then I parse it into eval(). This works well but it also allows people to change their own character object by typing things like player.health = Infinity; or something similar. I have a list of functions I want to allow people to use, but I am unsure how to restrict it to only use them.
I understand that the whole point of not letting people use eval is to avoid accidental cross-site scripting but I am unsure on how else to do this. If you have a suggestion please leave a comment about that.
I asked some people around on what to do and most suggested somehow changing scope(which is something I was not able to figure out) or to add some odd parameter to each function in my code that would be required to be a specific string to execute any function, but that seems hacky and since I am making the game in browser with p5js it would be easy to just inspect element and see what the password is.
basically every character has variable called "instruction" which is just a string of javascript. Then every frame of the game I execute it by doing eval(playerList[i].instruction);
tl;dr, how can I only allow specific function to be executed and not allow any others?
EDIT: I forgot to mention that I also am planning to provide player with information so that people can made code that would adapt to the situation. For example there will be parameter called vision that has vision.front and vision.left etc. These variables would just say if there is an enemy, wall, flower, etc around them in a grid. Some people suggested that I just replace some functions with key words but then it compromises the idea of using if statements and making it act differently.
EDIT 2: Sorry for lack of code in this post, but because of the way I am making it, half of the logic is written on server side and half of it works on client side. It will be a little large and to be completely honest I am not sure how readable my code is, still so far I am getting great help and I am very thankful for it. Thank you to everybody who is answering
Do NOT use eval() to execute arbitrary user input as code! There's no way to allow your code to run a function but prevent eval() from doing the same.
Instead, what you should do is make a map of commands the player can use, mapping them to functions. That way, you run the function based on the map lookup, but if it's not in the map, it can't be run. You can even allow arguments by splitting the string at spaces and spreading the array over the function parameters. Something like this:
const instructions = {
goLeft: player.goLeft.bind(player),
goRight: player.goRight.bind(player),
attackInFront: player.attackInFront.bind(player)
};
function processInstruction(instruction_string) {
const pieces = instruction_string.split(' ');
const command = pieces[0];
const args = pieces.slice(1);
if (instructions[command]) {
instructions[command](...args);
} else {
// Notify the user their command is not recognized.
}
};
With that, the player can enter things like goLeft 5 6 and it will call player.goLeft(5,6), but if they try to enter otherFunction 20 40 it will just say it's unrecognized, since otherFunction isn't in the map.
This issue sounds similar to the SQL Injection problem. I suggest you use a similar solution. Create an abstraction layer between the users input and your execution, similar to using parameters with stored procedures.
Let the users type keywords such as 'ATTACK FRONT', then pass that input to a function which parses the string, looks for keywords, then passes back 'player.attackInFront()' to be evaluated.
With this approach you simplify the syntax for the users, and limit the possible actions to those you allow.
I hope this isn't too vague. Good luck!
From your edit, it sounds like you're looking for an object-oriented approach to players. I'm not sure of your existing implementation needs, but it would look like this.
function Player() {
this.vision = {
left: '',
// and so on
}
}
Player.prototype.updateVisibilities = function() {
// to modify the values of this.visibility for each player
}
Player.prototype.moveLeft = function() {
}
Don't give the user an arbitrary interface (such as an input textfield that uses eval) to modify their attributes. Make a UI layer to control this logic. Things like buttons, inputs which explicitly run functions/methods that operate on the player. It shouldn't be up to the player as to what attributes they should have.

Javascript detect when style.display is used regardless of style change

Take the following html:
<div id="somediv" style="display:none;"></div>
<script>
document.getElementById("somediv").style.display = 'none';
</script>
somediv is already hidden, some javascript runs, effectively doing nothing. I need code that detects when style.display has been used in javascript, regardless of if style was changed or not.
I've tried MutationObserver:
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutationRecord) {
alert(mutationRecord.target.id);
});
});
observer.observe(document.getElementById("somediv"), { attributes : true, attributeFilter : ['style'] });
The above only triggers if there was a style change. I need it to trigger regardless if there was a style change or not.
So I did come up with an answer. The way it works, is you grab every script tag, replace .style.display with your own function, and finally replace the DOM (which is the real trick):
//loop through <script> tags
$('script').each(function(){
var scripthtml = $(this).html();
if (scripthtml.indexOf('style.display') != -1){
scripthtml = scripthtml.replace(/.style.display = 'none'/g, ".customdisplay('none')");
scripthtml = scripthtml.replace(/.style.display = "none"/g, '.customdisplay("none")');
scripthtml = scripthtml.replace(/.style.display ='none'/g, ".customdisplay('none')");
scripthtml = scripthtml.replace(/.style.display ="none"/g, '.customdisplay("none")');
scripthtml = scripthtml.replace(/.style.display='none'/g, ".customdisplay('none')");
scripthtml = scripthtml.replace(/.style.display="none"/g, '.customdisplay("none")');
$(this).replaceWith('<script>' + scripthtml + '</script>');
}
});
Now here is my .style.display replacement function:
HTMLElement.prototype.customdisplay = function(showhide){
//insert whatever code you want to execute
this.style.display = showhide;
alert('Success! .style.display has been detected!');
};
.replaceWith is what actually changes the DOM. The only thing this script doesn't do, is it isn't able to look through included javascript files. Thank you all for your comments <3.
UPDATE:
When using replaceWith to add the script tag, ipad/iphone/ipod will execute the script tag a second time. to prevent this double execution, you need to do this:
$(this).replaceWith('<script>if (1==0){' + scripthtml + '}</script>');
Your functions will be valid, but anything outside of the function will not be executed.
I don't think you fully realise what you are asking for, but here is a short description of closest options I am aware of:
https://blog.sessionstack.com/how-javascript-works-tracking-changes-in-the-dom-using-mutationobserver-86adc7446401
Basically, MutationObserver is the major improvement over past here that is closest to what you want and supported in modern browsers. But the whole point of all this is it listens to changes.
You are basically asking to detect even non-changes. I don't see you getting out of this problem other than:
Writing a wrapper for the ways you use to change this in code and then instead of calling the change call the wrapper. Simple, easy, requires refactoring all the calls in code.
Overwriting the actual functions that make a change. This saves you the refactor but you are playing with fire here. Rewriting a well-known function on a global level means a PERMANENT source of problems to you and all developers who work on the project.
Polling - in short calling over and over on some element to check properties. Not only it detects changes with a lag of 0 to the polling interval it also uses resources and if you want to monitor everything you will have to write a recursion that descends through the current DOM from the top to each node and check it. You are gonna kill the performance with this. How hard I can't tell but I suspect either polling interval will be long thus increasing the detection lag or your performance will dive down like a gray falcon.
I have 1 big question for you:
What led you to the state where you need this?
You basically want the ability for a program to detect when it is using a specific part of itself (be that as it is, a core part, one implemented by the browser).
This sounds similar to request: hey whenever I change or not change a value in any variable in my code I should be able to react to it.
I am not trying to sound like Naggin Nancy here but instead encourage you to question train of thought that led to this being something you need and figure out whether you want to sink further time into this, because I don't think you are getting that what you desire easily and I suspect it came to be due to poor design decisions in the past.

Problems with a custom self-closing tag in CKEditor

I have a plugin for inserting tag <cut /> in text.
It works fine, result is expectable, but in editor window <cut /> transforms into <cut></cut>, wraps the paragraphs below and hampers further edits.
GIF - http://gyazo.com/dd7c36ba7cb7bc7cb00186cfb83e5fbc
Any ideas how to fix it?
CKEDITOR.plugins.add('pagecut', {
lang: 'de,en,ru',
onLoad: function(){
var css = ('display:block;clear:both;width:100%;border-top:#999 1px dotted;padding:0;height:1px;cursor:default;');
var cssBefore = (
'content:"";' +
'background: url(' + CKEDITOR.getUrl( this.path + 'images/image.png' ) + ') no-repeat right center;' +
'height:14px;width:25px;position:relative;display:block;top:-8px;float:right;'
);
CKEDITOR.addCss( 'cut{' + css + '} cut:before{' + cssBefore + '}' );
},
init: function(editor) {
CKEDITOR.dtd['cut'] = {};
CKEDITOR.dtd.$empty['cut'] = 1;
CKEDITOR.dtd.$nonEditable['cut'] = 1;
CKEDITOR.dtd.$object['cut'] = 1;
editor.addCommand('insertPagecut', {
exec: function(editor) {
var element = CKEDITOR.dom.element.createFromHtml('<cut />');
editor.insertElement(element);
}
});
editor.ui.addButton('Pagecut', {
label: editor.lang.pagecut.toolbar,
command: 'insertPagecut',
icon: this.path + 'images/icon.png',
toolbar: 'links'
});
}
});
Uh, I'm sure that I explained this thoroughly in some question, but I can't find it, so here goes another explanation :D.
There are two important facts that one must understand before trying to edit non-HTML tags in CKEditor:
CKEditor is an HTML editor.
Of course custom tags start to be more and more popular in HTML. You can also say that XML is some kind of generalisation of HTML (although not precisely, because it has other rules), so if CKEditor handles HTML why doesn't it handle other tags equally well. Well - the answer is simple - because HTML tags have a meaning and CKEditor knows it. But it does not know meaning of your custom tags. And the meaning of tags (what a list is, that it has items, that they are block elements, etc.) is crucial to implement editing algorithms.
Fair enough, you could say. But why wasn't CKEditor's configuration (e.g. the CKEDITOR.dtd object) generalised so meaning of every possible tag can be configured? Because every generalisation increases complexity and HTML editing is already complex enough.
So why does the CKEDITOR.dtd object exist at all? Because some components of CKEditor are configurable to some extent. The DTD has the biggest impact on CKEditor's HTML parser (which is used mostly during data processing) so this is the most configurable component. Other algorithms, like the enter key handling, backspace/delete, lists editing (which is a very complex task) are only slightly configurable and there is no guarantee given that they will work with your custom tags.
The editing happens in the browsers and is partially handled by the browsers.
This fact is important because it means that browsers' capabilities are affecting CKEditor's limits too. Browser must be able to parse and render your tags (fortunately, this part works rather well in modern browsers - IE8 is the last one with a huge problems) and must be able to edit it. This means - render caret, handle selection, handle backspace, enter, etc. Since browsers are not easily extensible and their implementations of contentEditable are highly inconsistent, incompatible and buggy, from release to release CKEditor overrides more and more of their native behaviours. Not all yet (actually - it will never override all, because that could be disastrous for certain reasons), but a significant amount. For instance, all the enter key behaviour is custom, on Webkit and Blink CKEditor handles backspace and delete in many scenarios due to still unresolved bugs (1 and 2), it implements its own undo system, intercepts pasted and dropped content and perform custom HTML insertion (I remember that when we implemented it this closed a huge number of tickets), etc., etc.
One of the greatest efforts to ensure consistent, configurable and powerful editing experience is the widgets system. It is full of hacks inside, but it exposes a clean and pretty powerful API to the developer and a very consistent behaviour to the end user. It allows to implement a "special rich content units that are groups of elements which are treated as a single entity inside the editor". So the widgets system has the power to encapsulate part of your content and isolate it from the browsers.
After this short introduction I can finally answer your question. You need to implement your <cut> tag as a widget. You already configured your DTD pretty well (you only forgot to set in what elements the <cut> element can exist and whether it is more like a block or inline element), so the parser will accept it and handle as an empty tag. Now you need to wrap it with a widget in order to isolate it so it does not break the editing experience. That should do the trick.

Reset Key Bindings - Ace Editor

OK, I'm doing a few experiments with Ace editor and I'm trying to control what keys/shortcuts have been bound.
The best way I could think of would be to totally eliminate/reset all keybinding, and re-set only the ones I need.
How is that doable?
Any ideas?
P.S. I've had a look into the the whole documentation (and the list of "default" Key Bindings) but I'm still not sure. I've also played with the JS console and editor.keyBinding.getKeyboardHandler() but I still cannot find any suitable way/method/property to unset a specific keybinding.
Trying this approach (Ace Editor - Change CTRL+H keybinding), I tried editor.commands.findnext={} in order - for instance - to eliminate the Cmd+G binding to the "Find Next" command. However, the shortcut is still there... :S
To remove all commands do
var allCommands = editor.commands.byName;
editor.commands.removeCommands(allCommands)
// now you can add commands as you wish
allCommands.findnext.bindKey = {win: "Ctrl-H", mac: "Cmd-H"}
editor.commands.addCommand(allCommands.findnext)
But if you want to only remove particular binding, call
editor.commands.bindKey("Cmd-G", null)

Adding Javascript variables to HTML elements

So, I have some code that should do four things:
remove the ".mp4" extension from every title
change my video category
put the same description in all of the videos
put the same keywords in all of the videos
Note: All of this would be done on the YouTube upload page. I'm using Greasemonkey in Mozilla Firefox.
I wrote this, but my question is: how do I change the HTML title in the actual HTML page to the new title (which is a Javascript variable)?
This is my code:
function remove_mp4()
{
var title = document.getElementsByName("title").value;
var new_title = title.replace(title.match(".mp4"), "");
}
function add_description()
{
var description = document.getElementsByName("description").value;
var new_description = "Subscribe."
}
function add_keywords()
{
var keywords = document.getElementsByName("keywords").value;
var new_keywords = prompt("Enter keywords.", "");
}
function change_category()
{
var category = document.getElementsByName("category").value;
var new_category = "<option value="27">Education</option>"
}
remove_mp4();
add_description();
add_keywords();
change_category();
Note: If you see any mistakes in the JavaScript code, please let me know.
Note 2: If you wonder why I stored the current HTML values in variables, that's because I think I will have to use them in order to replace HTML values (I may be wrong).
A lot of things have been covered already, but still i would like to remind you that if you are looking for cross browser compatibility innerHTML won't be enough, as you may need innerText too or textContent to tackle some old versions of IE or even using some other way to modify the content of an element.
As a side note innerHTML is considered from a great majority of people as deprecated though some others still use it. (i'm not here to debate about is it good or not to use it but this is just a little remark for you to checkabout)
Regarding remarks, i would suggest minimizing the number of functions you create by creating some more generic versions for editing or adding purposes, eg you could do the following :
/*
* #param $affectedElements the collection of elements to be changed
* #param $attribute here means the attribute to be added to each of those elements
* #param $attributeValue the value of that attribute
*/
function add($affectedElements, $attribute, $attributeValue){
for(int i=0; i<$affectedElements.length; i++){
($affectedElements[i]).setAttribute($attribute, $attributeValue);
}
}
If you use a global function to do the work for you, not only your coce is gonna be easier to maintain but also you'll avoid fetching for elements in the DOM many many times, which will considerably make your script run faster. For example, in your previous code you fetch the DOM for a set of specific elements before you can add a value to them, in other words everytime your function is executed you'll have to go through the whole DOM to retrieve your elements, while if you just fetch your elements once then store in a var and just pass them to a function that's focusing on adding or changing only, you're clearly avoiding some repetitive tasks to be done.
Concerning the last function i think code is still incomplete, but i would suggest you use the built in methods for manipulating HTMLOption stuff, if i remember well, using plain JavaScript you'll find yourself typing this :
var category = document.getElem.... . options[put-index-here];
//JavaScript also lets you create <option> elements with the Option() constructor
Anyway, my point is that you would better use JavaScript's available methods to do the work instead of relying on innerHTML fpr anything you may need, i know innerHTML is the simplest and fastest way to get your work done, but if i can say it's like if you built a whole HTML page using and tags only instead of using various semantic tags that would help make everything clearer.
As a last point for future use, if you're interested by jQuery, this will give you a different way to manipulate your DOM through CSS selectors in a much more advanced way than plain JavaScript can do.
you can check out this link too :
replacement for innerHTML
I assume that your question is only about the title changing, and not about the rest; also, I assume you mean changing all elements in the document that have "title" as name attribute, and not the document title.
In that case, you could indeed use document.getElementsByName("title").
To handle the name="title" elements, you could do:
titleElems=document.getElementsByName("title");
for(i=0;i<titleElems.length;i++){
titleInner=titleElems[i].innerHTML;
titleElems[i].innerHTML=titleInner.replace(titleInner.match(".mp4"), "");
}
For the name="description" element, use this: (assuming there's only one name="description" element on the page, or you want the first one)
document.getElementsByName("description")[0].value="Subscribe.";
I wasn't really sure about the keywords (I haven't got a YouTube page in front of me right now), so this assumes it's a text field/area just like the description:
document.getElementsByName("keywords")[0].value=prompt("Please enter keywords:","");
Again, based on your question which just sets the .value of the category thingy:
document.getElementsByName("description")[0].value="<option value='27'>Education</option>";
At the last one, though, note that I changed the "27" into '27': you can't put double quotes inside a double-quoted string assuming they're handled just like any other character :)
Did this help a little more? :)
Sry, but your question is not quite clear. What exactly is your HTML title that you are referring to?
If it's an element that you wish to modify, use this :
element.setAttribute('title', 'new-title-here');
If you want to modify the window title (shown in the browser tab), you can do the following :
document.title = "the new title";
You've reading elements from .value property, so you should write back it too:
document.getElementsByName("title").value = new_title
If you are refering to changing text content in an element called title try using innerHTML
var title = document.getElementsByName("title").value;
document.getElementsByName("title").innerHTML = title.replace(title.match(".mp4"), "");
source: https://developer.mozilla.org/en-US/docs/DOM/element.innerHTML
The <title> element is an invisible one, it is only displayed indirectly - in the window or tab title. This means that you want to change whatever is displayed in the window/tab title and not the HTML code itself. You can do this by changing the document.title property:
function remove_mp4()
{
document.title = document.title.replace(title.match(".mp4"), "");
}

Categories