Code Mirror Get Current Line Number every time content changes - javascript

I am trying to use Code Mirror to create a text editor. I want to show the current line number to the user at the bottom of the display, as text editors do.
So far I have tried this:
function updateInfo(){
var lines = editor.lineCount();
document.getElementById('line-no.').innerText = lines;
editor.refresh();
}
editor.on("change", updateInfo());
The line-no. is a span where I want to display the Line Number. This works for the first Line Number but then when I go to some other line, it doesn't do anything. The console shows this error:
codemirror.js:2154 Uncaught TypeError: Cannot read property 'apply' of undefined
at codemirror.js:2154
at fireCallbacksForOps (codemirror.js:2111)
at finishOperation (codemirror.js:2125)
at endOperation (codemirror.js:3747)
at HTMLTextAreaElement.<anonymous> (codemirror.js:3884)

Update
To follow updates in the editor, register a handler on the cursorActivity event. This event is fired when there is a change in the cursor or selection. The handler is called with the instance of CodeMirror; on this you can call the getCursor method to be the an object that contains the current line number that the cursor is active on.
Note that the line number is zero-based, so you may or may not increment it by 1.
const STATUS_CURRENT_LINE = document.getElementById('line-no.');
const onCursorActivity = (instance) => {
const cursor = cm.getCursor();
STATUS_CURRENT_LINE.textContent = cursor.line + 1;
}
editor.on("cursorActivity", onCursorActivity);
Setting the current line number when there is a doc change in the editor
The error happens because the updateInfo callback is called even before you register it. So that the value registered as the callback is undefined.
editor.on('change', updateInfo()) // -> editor.on('change', undefined)
This can be resolved by registering the function.
editor.on('change', updateInfo)
However, the signature for the callback should follow what is documented.
The change callback is passed the instance of CodeMirror and a changeObject from which you can retrieve the current line.
"change" (instance: CodeMirror, changeObj: object)
Fires every time the content of the editor is changed. The changeObj is a {from, too, text, removed, origin} object containing information about the changes that occurred as the second argument. from and to are the positions (in the pre-change coordinate system) where the change started and ended (for example, it might be {ch:0, line:18} if the position is at the beginning of line #19). text is an array of strings representing the text that replaced the changed range (split by line). removed is the text that used to be between from and to, which is overwritten by this change. This event is fired before the end of an operation before the DOM updates happen.
const STATUS_CURRENT_LINE = document.getElementById('line-no.');
function updateInfo(instance, changeObj){
STATUS_CURRENT_LINE.innerText = changeObj.to.line;
}
editor.on("change", updateInfo);

Related

Getting empty array on contentControls officejs

Word.run( async (context) => {
var searchResults = context.document.body.search('moslim');
context.load(searchResults);
return context.sync().then(async function () {
if (searchResults.items.length > 0) {
// Get the first search result
var range = searchResults.items[0].getRange();
// range.clear();
// Insert a content control around the search result
var contentControl = range.insertContentControl();
// Set the tag of the content control
contentControl.tag = 'your tag';
contentControl.insertText(searchResults.items[0].text.toString(), 'Replace');
// Load the content control
context.load(contentControl);
await context.sync()
var getcontentControl = range.contentControls
context.load(getcontentControl)
await context.sync()
console.log(getcontentControl.items)
}
});
});
last console.log gives empty array but contentcontrol has been added the ms word document.
Can anyone help on this what I did wrong?
I don't think you did anything "wrong" It's just that, despite the fact that you are calling a method called insertContentControl on your Range object, office-js surrounds the range with the control (at least in the case you mention) in such a way that it is not actually added to the range.
The question is what you are actually trying to achieve and how to do that. (It wasn't obvious from the Question as it stands).
If you did the equivalent thing in VBA to your found text, it might be
myrange.ContentControls.Add wdContentControlRichText
In that case, in VBA the content control is in myrange, i.e.
myrange.ContentControls.Count
returns 1. But it wouldn't be in myrange if myrange did not "cover" any characters (i.e. myrange.Start=myrange.End )
I made a simple modification to your office-js code to select the range it found. I also commented out the code that puts text in the control
var contentControl = range.insertContentControl();
range.select();
// Set the tag of the content control
contentControl.tag = 'your tag';
//contentControl.insertText(searchResults.items[0].text.toString(), 'Replace');
If I run your example code with those changes, it's quite easy to find out where the VBA WOrd object model thinks the control and the range are:
Sub rangeandccinfo()
Debug.Print ActiveDocument.ContentControls(1).Range.Start, ActiveDocument.ContentControls(1).Range.End
'In office-js we set by using the Range. So they should really be the same (except maybe we shouldn't
'expect office-js to behave the same as the traditional object model.
Debug.Print Selection.Start, Selection.End
Debug.Print Selection.Range.ContentControls.count
Debug.Print Selection.Range.Information(wdInContentControl)
End Sub
In my experiment, the results are like this
3 9
3 9
0
True
But if I just select the same word manually and insert the CC using VBA like this:
Sub ccrange()
Dim r As Range
Debug.Print Selection.Start, Selection.End
Set r = Selection.Range
'should be the same
Debug.Print r.Start, r.End
r.ContentControls.Add wdContentControlRichText
Debug.Print r.Start, r.End
r.Select
Set r = Nothing
End Sub
then run the rangeandccinfo() Sub, I see this:
3 9
2 9
1
True
i.e. in office-js, it looks as if the range and the range of the Content Control have the same Start and End, whereas in VBA, the Range starts one character before the Range of the Content Control, and that seems to be what make the VBA count of COntent COntrols in the Range 1 rather than 0.
If you actually click at the beginning of the text in a Content Control and click the left arrow button (in a document with left-to-right text) you will see that the insertion point does not appear to move. And yet its Range does change.
So it's at least partly an implementation choice how to do this. Personally, I have found it quite difficult to work with the way VBA does it (its difficult to work out what's inside what). It's quite possible that the office-js team encountered a similar problem and just decided to do it a way that made more sense to them.

Protractor: One test passes, but the other fails

I want to check two things - whether the search functionality works on AskJeeves AND whether the search boxes stored the right string.
To check this I run three expect statements:
expect(searched_term.getText()).toEqual('Baking a Cake without Margarine');
expect(searched_term.getText()).toEqual('Baking a Pie without Butter');
expect(searched_term.getText()).toEqual(search_box.getText());
Only the second one should fail. But the first one fails as well. Why is that? Here is the error log. Also how can I count how many expect statements I have and console.log() output that?
Error Log:
browser.waitForAngularEnabled(false);
describe('Enter Search Query in Ask Jeeves', function() {
it('This will insert a query', function() {
browser.get(browser.baseUrl);
element(by.xpath('//*[#id="search-box"]'));
var search_box = element(by.name("q")).sendKeys('Baking a Cake without Margarine');
browser.pause(1500);
var button = element(by.xpath('//*[#id="sbut"]'));
button.click();
var searched_term = element(by.name("q"));
expect(searched_term.getText()).toEqual('Baking a Cake without Margarine');
expect(searched_term.getText()).toEqual('Baking a Pie without Butter');
expect(searched_term.getText()).toEqual(search_box.getText());
// This expect statement checks if the term in the following page reflects the term originally searched.
// In addition this will check functionality of the search engine.
browser.pause(1500);
});
});
The searched_term element is an input element - it does not have a "text" and the value of the input is stored in the value attribute. Replace getText() with getAttribute("value"):
expect(searched_term.getAttribute("value")).toEqual('Baking a Cake without Margarine');

Javascript: Flexibility of function parameters?

The description of Javascript function parameters on W3Schools wasn't very clear, so I just want to clarify.
From my understanding, there isn't a type restriction; the "real value" of the parameters are passed into the method. Is there a way to pass objects or elements? Or is that what is meant by "real value"?
For example:
The function displayText meant to take input text and set a display to show a new word in the given input text, going to the next word every time it's called.
function displayText() {
var text = document.getElementById("words").value;
// Since text is initialized
// every time the method is called,
// it will always start at the beginning of the text area.
// Not sure how to fix this since making `text`
// a global variable doesn't work
var list = text.split(/[ \t\n]+/);
displayNext(list, "display");
}
There is a "helper" method, displayNext, which is supposed to shift to the next word in the list and sets the display to that word.
function displayNext(list, textboxID) {
document.getElementById(textboxID).innerHTML = list.shift();
}
This isn't working as it is intended. I'm fairly sure it's because I've mucked up something with the parameters, since displayNext sets innerHTML to null. list must have not passed properly. I'm not sure how to fix this.
I'm sure there's a more efficient way to do this, but this is a good opportunity to learn how Javascript parameters actually work, so I thought I'd ask.
JSFiddle
Based on the comments in your code, it sounds like you want displayText() to display the "next" word each time. To do that, you have to create some place to store some state about which word is the next one to display. As you have it now, you create a new array every time and always display the first word.
The simplest way is to create a variable outside your function in some lasting scope where you store the word number:
var wordNum = 0;
function displayText() {
var text = document.getElementById("words").value;
var list = text.split(/\s+/);
if (list.length !== 0) {
// make sure we aren't off the end of the list
wordNum = wordNum % list.length;
displayNext(list[wordNum++], "display");
}
}
function displayNext(text, textboxID) {
document.getElementById(textboxID).innerHTML = text;
}
For a lot more info about arguments to Javascript functions and even how you can detect what arguments were passed or overload arguments, see this answer: How to overload functions in javascript? and for more info about how arguments are passed: Javascript by reference vs. by value

Assignment causing program to behave strangely

I'm making a chess app for practice and I have a function called movePiece that gets called after a user selects a square to move a piece to. Before I call it I have a line that gets the destination piece's attribute (queen, pawn, etc..) using a dictionary:
secondAttr = pieceDict[posStr][0];
Strangely, when I comment out this line my movePiece() function gets called correctly, but when I leave it in there nothing happens until I click for a third time. I'm super confused why this random line before my function call that has nothing to do with the function itself is causing such behavior.
Here's a jsFiddle:
https://jsfiddle.net/eqmbk0u1/
, the pieces png's are stored on my computer so they won't show up, but they're all in their normal starting spots and when you select one it's square turns blue. See what happens when you comment out line 67 vs it normally.
You seem to have an error in you else block, of you $('square').click() event.
} else {
$("#"+firstID).removeClass('selected');
$("#"+firstID).addClass(lastSqrColor);
destRow = parseInt($(this).attr('row'));
destCol = parseInt($(this).attr('col'));
destID = parseInt($(this).attr('id'));
secondAttr = pieceDict[posStr][0]; //<--- Swap
posStr = positions[destRow][destCol]; //<--- Swap
movePiece();
firstTouch = false;
}
When you first click on a square, you save the name of the piece into posStr. Then, on your second click, you overwrite that value with an undefined value, because there is no piece in positions[destRow][destCol]. If you swap the lines 66 and 67, the code will work, because you set the secondAttr before overwriting posStr.

JavaScript: Assigning a function to a property that will run anew each time the property is called

I'm creating an html5 JS form library. The idea is to turn elements with a class of .form-item into content editable divs, using the elements' data-attributes as instructions for the type of form item, validation, etc that should be created.
The code below creates a validation object for a single form item that checks to see if the minimum length of the field value is met. The object's properties include the DOM element it applies to (el), the minimum length for the field (minLen), the error message (msgError) that should be displayed if that minimum length is not met, and a function (submit) that returns whether the object validates, displaying the error message if it does not.
However, the submit property function always returns false. I'm pretty sure I know why, but I'm not sure of the best way to correct it. I believe the problem I'm running into has to do with the notion of a closure. The submit property checks the length of the innerHTML of the form item element and compares it to the minLen property. But I think this only happens at the moment of instantiation of the validation object, when the innerHTML.length is always 0 (because the form item, and the validation object, must be created prior to the user being able to enter anything into the field). How should I modify the below code so that the submit property function runs anew any time it is called (thereby checking the current length of the innerHTML of the field, rather than the length at the moment of instantiation)?
function formValidateMinLen(item){
var minLen = item.attr('data-minLen');
this.el = item;
this.minLen = minLen;
this.msgError = item.attr('data-error_minLen');
this.submit = function(){
if(this.el.html() >= this.minLen){
return true;
}else{
this.el.after('<div class="msgError">' + this.msgError + '</div>');
return false;
}
}
}
item = new formValidateMinLen($('#registration-form .form-item:first'));
I did come up with one solution, which is to pass the innerHTML of the form item element as an argument to the submit property function, like this:
this.submit = function(html){
if(html >= this.minLen){
...
}
item = new formValidateMinLen($('#registration-form .form-item:first'));
item.submit($('#registration-form .form-item:first').html());
However, I don't like that solution because it seems redundant to have to re-specify the DOM element in the item.submit code (since the object already contains that DOM element in its el property).
So... what should I do?
I'm an idiot. I was comparing the innerHTML to the minimum length, rather than the LENGTH of the innerHTML to the minimum length. Now it works :)

Categories

Resources