My main problem is initializing the text/value of a code editor(CodeMirror) on my website without it affecting the way I save/send POST requests to my backend. The following is the pug code I use for the POST request:
p
form(id='form' method='POST', action='/docs/edit/'+docs._id)
textarea(name="doo" id="content" style="display: none;")=docs.content
textarea(name="foo" id="editortext" style="display: none;")
input.btn.btn-primary(type='submit' value='Save Doc')
What I'm trying to do here, is send docs.content to textarea with id "content" so that I can use that to initialize the value of my code editor and then put the content of whats in the code editor in the textarea
"editortext" once I click the submit button. Thus, the POST request would fetch me the data from both textareas, where I can then save the content of the "editortext" textarea to my database. The logic of the code editor is referenced in the same pug file to a javascript file after rollup transpilation. The following is a chunk of the pre-compiled code:
/* eslint-env browser */
import * as Y from 'yjs'
import { WebsocketProvider } from 'y-websocket'
import { CodeMirrorBinding } from 'y-codemirror'
import CodeMirror from 'codemirror'
import 'codemirror/mode/clike/clike.js'
window.addEventListener('load', () => {
const ydoc = new Y.Doc()
const provider = new WebsocketProvider(
`${location.protocol === 'http:' ? 'ws:' : 'wss:'}${location.host}`,
'codemirror',
ydoc
)
const yText = ydoc.getText('codemirror')
const editorContainer = document.createElement('div')
editorContainer.setAttribute('id', 'editor')
document.body.insertBefore(editorContainer, null)
let content = document.getElementById("content").value
const editor = CodeMirror(editorContainer, {
mode: 'text/x-java',
lineNumbers: true
})
editor.setValue(content)
document.getElementById("form").onsubmit = function(evt){
document.getElementById("editortext").value = editor.getValue();
}
Most of this code is from the yjs-codemirror demo except for the declaration of the content variable,the invocation of the setValue method, and the document.getElementById("form") block. What this code currently does is send me the right information to my database. However, I am having trouble initializing the value of the code editor when I open up the document. The setValue method doesn't work, neither does doing the following:
const editor = CodeMirror(editorContainer, {
value: content,
mode: 'text/x-java',
lineNumbers: true
})
All of the prior examples fail even if I replace the content variable with some string. The only thing that seems to work is the following:
const editor = CodeMirror(editorContainer, {
mode: 'text/x-java',
lineNumbers: true
}).setValue(content)
However, the problem with this is that for some reason, I get the following errors in the console browser:
TypeError: codeMirror is undefined (y-codemirror.js:160:4)
TypeError: editor is undefined (index.js:28:10)
For reference, the javascript that I have been showing in this question was all from the index.js file. In any case, because the editor is undefined, I can no longer set the value of my "editortext" textarea to the CodeMirror Textarea and I can't save what is written to the code editor to my database. I'm not sure as to why this would happen, I'm not sure if this is particular to the CodeMirrorBinding from yjs but any help on this would be massively appreciated.
The following is quoted from dmonad who is one of the developers of Yjs. For future reference regarding any technical questions about Yjs, you will probably get better luck asking here as there isn't a tag for Yjs yet on StackOverflow.
Hi #notemaster,
I assume that you mean you are unable to set the value of the CodeMirror editor.
The CodeMirrorBinding binds the value of the Y.Text type to a CodeMirror instance. The setValue method works, but the value of the editor is overwritten by the binding:
ytext.insert(0, 'ytext')
const editor = CodeMirror(..)
editor.setValue('my value')
editor.value() // => "my value"
new CodeMirrorBinding(editor, ytext)
editor.value() // => "ytext value"
I suggest that you set the value after it has been bound to the YText type.
Another note: There is nothing like a default value in Yjs. Initially, the Yjs document is empty until it synced with the server. So you might want to wait until the client synced with the server before setting the value.
const setDefaultVal = () => {
if (ytext.toString() === '') {
ytext.insert(0, 'my default value')
}
}
if (provider.synced) {
setDefaultVal()
} else {
provider.once('synced', setDefaultVal)
}
const editor = CodeMirror(editorContainer, {
mode: 'text/x-java',
lineNumbers: true
}).setValue(content)
I assume editor.setValue() returns undefined . This is why the binding won’t work and you can set the initial value of the editor.
Related
A pretty basic electron question, explaining it by a specific example:
in my first electron app I want to change the innerHTML of a label before sync-copying a larger file.
here's what I wrongfully thought the solution is. with this code, the label text is changed after the file was copied:
renderer.js:
const lbl = document.getElementById('statusLabel')
lbl.innerHTML = "New Description"
fs.copyFileSync(...)
However, I can "force" first updating the label by this:
renderer.js:
const lbl = document.getElementById('statusLabel')
lbl.innerHTML = "New Description"
ipcRenderer.send('copyFile')
main.js:
ipcMain.on('copyFile', () => {
ipcMain.send('copyFile'))
}
I could replace fs.copyFileSync with any other function - the point is, I only get it to work in the required order by sending the subsequent action to the renderer.
is this intended electron behaviour or did I miss something basic?
We are currently using quilljs for a project. When we try to add html through the dangerouslyPasteHTML API from the Clipboard module, the custom attributes from the paragraphs are stripped.
For example:
On applying the following code :
quill.clipboard.dangerouslyPasteHTML("<p data-id='1'>Hello</p>");
The output obtained is
<p>Hello</p>
How do you retain the attribute 'data-id' in the output?
UPDATE 1:
I have managed to retain the custom attribute 'data-id' using the following code:
var Parchment = Quill.import('parchment');
var dataId = new Parchment.Attributor.Attribute('data-id', 'data-id', {
scope: Parchment.Scope.BLOCK
});
Quill.register(dataId);
However, on creating a new line (hitting the enter key), the same data-id is appearing in the new paragraph as well. How do I ensure that the new paragraph either has a custom data-id or does not contain the 'data-id' attribute?
I am pretty late to answer this, but for anyone encountering this issue, I have fixed it in the following way:
import Quill from 'quill';
const Parchment = Quill.import('parchment');
const IDAttribute = new Parchment.Attributor.Attribute('id-attribute', 'id', {
scope: Parchment.Scope.BLOCK,
});
Quill.register(
{
'attributors/attribute/id': IDAttribute,
},
true
);
Quill.register(
{
'formats/id': IDAttribute,
},
true
);
const Block = Quill.import('blots/block');
class BlockBlot extends Block {
constructor(domNode) {
super(domNode);
domNode.setAttribute('id', '');
this.cache = {};
}
}
BlockBlot.blotName = 'block';
export default BlockBlot;
So basically we want to make a custom Blot extending from the Block blot available and use it for the Block format execution. In the constructor we can do whatever we want to do with the attribute. In my case I am removing the id attribute which was being added to new block.
I would recommend adding event handling in the textChanged method. You could check the delta and see if the 'insert' also contains an 'attributes' field that would cause it to be modified. If that happens, you can trigger an updateContents that retains through the current selection index. Then delete the length of the insert, and reinsert without the attributes.
I've been working on a custom extension for Visual Studio Code recently and am trying to show a link to the user when hovering over a specific phrase but it hasn't been working. The hover shows up but the link isn't "actionable".
My extension is registering a HoverProvider and is returning a new Hover object which contains a string with a link inside of it like so:
return new vscode.Hover({language: 'markdown', value: '[test-link](https://www.google.com)'});
I've tried switching the language between "markdown" and "HTML", but in either case, although the link was in the correct format, it was never actionable. I suspect this is because the value being fed in as a parameter to the Hover constructor is a MarkdownString which has the following note:
Note that markdown strings will be sanitized - that means html will be
escaped.
I could've sworn I've seen this feature elsewhere in VS Code like in a package.json file where a link to a repository was shown when hovering over a dependency name, but I can't seem to find a working example.
Here's an example of what the hover looks like:
Is there any way to get this feature working?
This is working for me:
let disposable1 = vscode.languages.registerHoverProvider('javascript', {
provideHover(document, position, token) {
const word = document.getText(document.getWordRangeAtPosition(position));
const searchOptions = {
query: word
};
const contents = new vscode.MarkdownString(`[test-link](https://www.google.com)`);
contents.isTrusted = true;
return new vscode.Hover(contents);
}
});
Note that new Hover() has this signature:
new Hover(contents: MarkdownString | MarkedString | Array<MarkdownString | MarkedString>, range?: Range): Hover
See Hover api reference.
You were trying to give it an object (with a language key).
With vscode v1.61 this will also work within the provider:
const contents = new vscode.MarkdownString(`[test-link](https://www.google.com)`);
contents.appendMarkdown("<a href='https://www.google.com'>test-link2</a>");
contents.supportHtml = true;
contents.isTrusted = true;
return new vscode.Hover(contents);
See https://stackoverflow.com/a/67954180/836330 for more on newly supported html tags.
Totally new to ace editor dev, to dynamically add additional rules to a mode file for syntax highlighting I'm doing an ajax call that sets a global variable that is available inside the mode file to process.
Here is the setup and initial ajax call:
var editor = ace.edit("editor");
$.ajax({
url: "json-mode-rules.php",
dataType: "json"
}).done(function(data) {
window.myModeRules=data; // ("foo","bar","etc")
editor.getSession().setMode("ace/mode/python");
});
The mode file is patched with the following:
// keywords has already been initialised as an array
// e.g. var keywords = ("and|as|assert...etc")
var extraRules=window.codebenderModeLibrary["myModeRules"].join("|");
keywords=(keywords[0]+"|"+ extraRules);
When the page is loaded initallly the ace editor gets all the keywords to syntax highlight. This works great.
The issue is that we have the rules changing when certain events occur and would like the ace editor to refresh its syntax rules.
Doing the ajax call again and calling setMode does nothing - this is due to require js not reloading the file.
I have posted an issue on GitHub without a resolution yet:
https://github.com/ajaxorg/ace/issues/1835
"If you really want to keep global variable, you can wrap everything
in a function, call that function to get updated Mode constructor, and
then call setMode(new Mode)."
I don't know how to do that and any help would be appreciated.
Anyone with techniques on how to dynamically update ace editor syntax highlighting rules?
See https://github.com/ajaxorg/ace/blob/9cbcfb35d3/lib/ace/edit_session.js#L888
setMode caches modes, unless they have options
so you can call
session.setMode({
path: "ace/mode/python",
v: Date.now()
})
to force it to create a new mode.
Another way is to do
var DynHighlightRules = function() {
// add function to change keywords
this.setKeywords = function(kwMap) {
this.keywordRule.onMatch = this.createKeywordMapper(kwMap, "identifier")
}
this.keywordRule = {
regex : "\\w+",
onMatch : function() {return "text"}
}
this.$rules = {
"start" : [
{
token: "string",
start: '"',
end: '"',
next: [{ token : "language.escape", regex : /\\[tn"\\]/}]
},
this.keywordRule
]
};
this.normalizeRules()
};
and then whenever highlight rules change do
// update keywords
editor.session.$mode.$highlightRules.setKeywords({"keyword": "foo|bar|baz"})
// force rehighlight whole document
editor.session.bgTokenizer.start(0)
see http://jsbin.com/ojijeb/445/edit
I found javascript wysiwyg editor wysiHTML5.
I'm trying to add element <a href=...> to the editor or just turn on bold programmatically.
My code is:
var editor = new wysihtml5.Editor("textarea", {
toolbar: "toolbar",
stylesheets: "css/wysihtml5.css",
parserRules: wysihtml5ParserRules
});
editor.observe("load", function() {
editor.composer.commands.exec("bold");
});
Am I doing something wrong?
Actually no, but you have to be sure that your textarea (iframe) is focused. Try to use on instead of observe. Here is a sample code that worked for me with insertHTML.
editor.on("load", function() {
editor.focus();
editor.composer.commands.exec("insertHTML","<a href=....>text</a>");
});
mateusmaso's solution gave me the following error:
NS_ERROR_FAILURE: Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsIDOMHTMLDocument.execCommand]
[Break On This Error]
Object.defineProperty(object, property, config);
So I've investigated some more and found the following solution, which (IMO) seems more ok:
var value = 'whatever you want to set';
// The jQuery reference to the textarea
var $ta = $('textarea#your-selector');
// The reference to the wysihtml5 editor - everything is contained within it
var w5ref = $ta.data('wysihtml5');
// Check if you have it
if(w5ref){
// if yes it's really an enhanced / wysihtml5ed textarea
// and use its setter
w5ref.editor.setValue(value);
} else {
// otherwise just simply populate the textarea as you normally would
$ta.html(value);
}
Source
assuming you have instantiated the editor previously using $('#textarea-id').wysihtml5()
$('#textarea-id').data("wysihtml5").editor.setValue('new content');
font