How to calculate unsaved changes in Code Mirror - javascript

I use a CodeMirror for displaying code on the HTML page. I have a version of the code which was saved in BD and when a user edits it in CodeMirror, I want to highlight unsaved changes like Visual Studio Code does:
I know that I can use method: codeMirror.removeLineClass(line, 'gutter', 'my_class'); to add a border that shows that changes were done at the specific line. My problem is in the calculation of the changed lines. I tried to use diff and diff2html packages and calculates changes like this:
var diff = Diff.createTwoFilesPatch('some name', 'some name', cm.state.savedText, cm.getValue());
var diffInfo = Diff2Html.getJsonFromDiff(diff, options);
This approach gives me a diff and I can get changed lines from it:
But this solution has a performance problem - it works slowly if a text contains more than 40 lines, and I will have large texts in CodeMirror.
I also tried to use history (the structure that codeMirror.getDoc().getHistory() returns) and highlight lines that are stored in 'changes' array, but it works only for new lines which were added. This approach doesn't work if the user removes the line.
What is the right way of calculation for such changes?
I was thinking of using the change event and tracking a specific change("+delete",
"+input"), then collect changed lines in special array. But this solution looks painful because in this case, I need to update this array manually if lines were added/deleted in the next iteration.
Is there a simpler way of how to do it?

After some research I've realized that even for big texts (~12 000 lines) approach with calculating the diff still works:
var diff = Diff.createTwoFilesPatch('some name', 'some name', cm.state.savedText, cm.getValue());
var diffInfo = Diff2Html.getJsonFromDiff(diff, options)
It takes ~30ms to calculate the diff for big text, and 5-6ms for small ones (~100lines). So for now it seems the most simple way for highlighting updated lines.

Related

Why is my reducer behaving differently between the first filter and subsequent filters applied in dc.js?

I'm working on a data visualization that has an odd little bug:
It's a little tricky to see, but essentially, when I click on a point in the line chart, that point corresponds to a specific issue of a magazine. The choropleth updates to reflect geodata for that issue, but, critically, the geodata is for a sampled period that corresponds to the issue. Essentially, the choropleth will look the same for any issue between January-June or July-December of a given year.
As you can see, I have a key called Sampled Issue Date (for Geodata), and the value should be the date of the issue for which the geodata is based on (basically, they would get geographical distribution for one specific issue and call it representative of ALL data in a six month period.) Yet, when I initially click on an issue, I'm always getting the last sampled date in my data. All of the geodata is correct, and, annoyingly, all subsequent clicks display the correct information. So it's only that first click (after refreshing the page OR clearing an issue) that I have a problem.
Honestly, my code is a nightmare right now because I'm focused on debugging, but you can see my reducer for the remove function on GitHub which is also copy/pasted below:
// Reducer function for raw geodata
function geoReducerAdd(p, v) {
// console.log(p.sampled_issue_date, v.sampled_issue_date, state.periodEnding, state.periodStart)
++p.count
p.sampled_mail_subscriptions += v.sampled_mail_subscriptions
p.sampled_single_copy_sales += v.sampled_single_copy_sales
p.sampled_total_sales += v.sampled_total_sales
p.state_population = v.state_population // only valid for population viz
p.sampled_issue_date = v.sampled_issue_date
return p
}
function geoReducerRemove(p, v) {
const currDate = new Date(v.sampled_issue_date)
// if(currDate.getFullYear() === 1921) {
// console.log(currDate)
// }
currDate <= state.periodEnding && currDate >= state.periodStart ? console.log(v.sampled_issue_date, p.sampled_issue_date) : null
const dateToRender = currDate <= state.periodEnding && currDate >= state.periodStart ? v.sampled_issue_date : p.sampled_issue_date
--p.count
p.sampled_mail_subscriptions -= v.sampled_mail_subscriptions
p.sampled_single_copy_sales -= v.sampled_single_copy_sales
p.sampled_total_sales -= v.sampled_total_sales
p.state_population = v.state_population // only valid for population viz
p.sampled_issue_date = dateToRender
return p
}
// generic georeducer
function geoReducerDefault() {
return {
count: 0,
sampled_mail_subscriptions: 0,
sampled_single_copy_sales: 0,
sampled_total_sales: 0,
state_population: 0,
sampled_issue_date: ""
}
}
The problem could be somewhere else, but I don't think it's a crossfilter issue (I'm not running into the "two groups from the same dimension" problem for sure) and adding additional logic to the add reducer makes things even less predictable (understandably - I don't ever really need to render the sample date for all values anyway.) The point of this is that I'm completely lost about where the flaw in my logic is, and I'd love some help!
EDIT: Note that the reducers are for the reduce method on a dc.js dimension, not the native javascript reducer! :D
Two crossfilters! Always fun to see that... but it can be tricky because nothing in dc.js directly supports that, except for the chart registry. You're on your own for filtering between different chart groups, and it can be tricky to map between data sets with different time resolutions and so on.
The problem
As I understand your app, when a date is selected in the line chart, the choropleth and accompanying text should have exactly one row from the geodata dataset selected per state.
The essential problem is that Crossfilter is not great at telling you which rows are in any given bin. So even though there's just one row selected, you don't know what it is!
This is the same problem that makes minimum, maximum, and median reductions surprisingly complicated. You often end up building new data structures to capture what crossfilter throws away in the name of efficiency.
A general solution
I'll go with a general solution that's more that you need, but can be helpful in similar situations. The only alternative that I know is to go completely outside crossfilter and look in the original dataset. That's fine too, and maybe more efficient. But it can be buggy and it's nice to work within the system.
So let's keep track of which dates we've seen per bin. When we start out, every bin will have all the dates. Once a date is selected, there will be only one date (but not exactly the one that was selected, because of your two-crossfilter setup).
Instead of the sampled_issue_date stuff, we'll keep track of an object called date_counts now:
// Reducer function for raw geodata
function geoReducerAdd(p, v) {
// ...
const canonDate = new Date(v.sampled_issue_date).getTime()
p.date_counts[canonDate] = (p.date_counts[canonDate] || 0) + 1
return p
}
function geoReducerRemove(p, v) {
// ...
const canonDate = new Date(v.sampled_issue_date).getTime()
if(!--p.date_counts[canonDate])
delete p.date_counts[canonDate]
return p
}
// generic georeducer
function geoReducerDefault() {
return {
// ...
date_counts: {}
}
}
What does it do?
Line by line
const canonDate = new Date(v.sampled_issue_date).getTime()
Maybe this is paranoid, but this canonicalizes the input dates by converting them to the number of milliseconds since 1970. I'm sure you'd be safe using the string dates directly, but who knows there could be a space or a zero or something.
You can't index an object with a date object, you have to convert it to an integer.
p.date_counts[canonDate] = (p.date_counts[canonDate] || 0) + 1
When we add a row, we'll check if we currently have a count for the row's date. If so, we'll use the count we have. Otherwise we'll default to zero. Then we'll add one.
if(!--p.date_counts[canonDate])
delete p.date_counts[canonDate]
When we remove a row, we know that we have a count for the date for that row (because crossfilter won't tell us it's removing the row unless it was added earlier). So we can go ahead and decrement the count. Then if it hits zero we can remove the entry.
Like I said, it's overkill. In your case, the count will only go to 1 and then drop to 0. But it's not much more expensive to this rather than just keep
Rendering the side panel
When we render the side panel, there should only be one date left in date_counts for that selected item.
console.assert(Object.keys(date_counts).length === 1) // only one entry
console.assert(Object.entries(date_counts)[0][1] === 1) // with count 1
document.getElementById('geo-issue-date').textContent = new Date(+Object.keys(date_counts)[0]).format('mmm dd, yyyy')
Usability notes
From a usability perspective, I would recommend not to filter(null) on mouseleave, or if you really want to, then put it on a timeout which gets cancelled when you see a mouseenter. One should be able to "scrub" over the line chart and see the changes over time in the choropleth without accidentally switching back to the unfiltered colors.
I also noticed (and filed) an issue because I noticed that dots to the right of the mouse pointer are shown, making them difficult to click. The reason is that the dots are overlapping, so only a little sliver of a crescent is hoverable. At least with my trackpad, the click causes the pointer to travel leftward. (I can see the date go back a week in the tooltip and then return.) It's not as much of a problem when you're zoomed in.

How to inspect variable data flow in Javascript?

Is there a way to inspect which variables (and lines of code) contribute to a value in Javascript? For example if I would inspect the parameter input in this code
let x=5;
let y=x+3;
function a(input) {
// analyzing input in this scope/context,
// i would like to see that it is a combination of
// x from line 1 and a constant '3' from line 2.
inspectDataFlow(input);
}
a(y);
I would like to get as output a data structure that would be something like this:
input (line 8)
y (line 2)
x (line 1)
"3" (line 2)
Purpose and goal
My goal for this would be to have a tool where you could see/change the value of a Javascript variable and it would automatically adjust the variables that the value originates from.
For example, you could have a function to draw shapes. And when you adjust visually the shapes that were drawn, the original variables for color, shape positions etc would update accordingly.
Something a bit like this but for editing arbitrary code, even after a user has modified the code lines:
http://yining1023.github.io/p5PlayGround/
Another idea I was thinking was to visualize how a variable has been composed: which lines it is a combination of and how they affect the result.
Potential approach
One approach for this I'm thinking about is to add instrumentation to the code with Esprima/Acorn that is then called on runtime. The instrumentation would keep track on which variables have been called on which lines (and scopes) and how they relate to each other, a bit like this:
http://alltom.com/pages/instrumenting-javascript/
I wonder if this would work and if there is a framework one could use for this? Or if one would have to do the instrumentation from scratch?
Related themes
This could be related to data flow analysis or use-define chains, but I'm not sure, since I don't know much about compilers.
https://en.wikipedia.org/wiki/Use-define_chain
My first idea was that this could be done using static analysis with something like Esprima/Acorn, but I'm not sure if that is the right way, or if this could be done with some custom Javascript interpreter instead.
http://tobyho.com/2013/12/02/fun-with-esprima/

How to truncate complex html by lines

I am trying to truncate complex html by lines in order to be able to display a show more link after a certain number of lines has been reached. I found a great library trunk8.js which truncates by lines..but if falls short when it comes to truncating complex html. So for my particular case I overrode the truncate function so that I can handle complex the using another truncation function which gracefully leaves complex html tags intact. Truncation will work great with html but I am stuck on how to accurately calculate where to put show more more based on the number of lines
As seen in the image above I am trying to truncate to 7 lines but if the user input contains white spaces shown in yellow my calculations will be wrong because I am not accounting for the white spaces. My initial line of thought was that if I can calculate the length of the spaces in yellow for each line and convert it to characters, I can add this offset to the maximum number number of characters, then I can know where to put approximately the show more link. Is this the best approach to this problem and if not ,any suggestions to make my life easier.
This is a plunker of what I have tried so far and I am stuck in my truncateHTML() function in the trunk8.js where I am only now truncating based on the maximum length of the string.
Eureka!! After a couple of google searches and heavy debugging sprints, I stumbled upon a library truncate.js which I customized the truncatedNestednode() function for my needs.
element.appendChild(child);
/** Customized--here **/
var clonedNode = {};
if ($clipNode.length) {
if ($.inArray(element.tagName.toLowerCase(), BLOCK_TAGS) >= 0) {
// Certain elements like <li> should not be appended to.
$element.after($clipNode);
}
else
{ //Removed the commented line to put showmore next to the last line of text
$element.prev().append($clipNode);
//$element.append($clipNode);
}
}
In case someone faced this problem in the past, I have posted my revised plunker here

Simple Collision Detection in Javascript / Jquery?

I am working on a portion of a project that I am trying to detect when certain divs hit each other. In the code that I made, that doesn't work, I basically say take the first div's left amount, compare it to the other div's left amount, if they are within a certain amount it triggers an alert. If I get that much to work I am going to implant a way to say that if the distance between the two divs is 0 then it will run a certain function. I am afraid the scope of this project is too big for me, even though I am basically at the last part, because I have spent hours researching a simple way to add collision detection, but everything I find looks like rocket science to me, that is why I tried to create my own way below. So in summary, what I want to know is why my collision detection code doesn't work, how I can make it work if possible, and if not possible what is the next best option that I should use.
//Collision
function collision(){
var tri = $('#triangle');
var enemyPos = $('.object1').css('left');
var minHit = enemyPos - 32.5;
var maxHit = enemyPos + 32.5;
var triLoc = tri.css('left');
if(triLoc > minHit && triLoc < maxHit){
alert('hit');
}
}
collision();
}
}
full code: https://jsfiddle.net/kc59vzpy/
If the code you have above is definitely where the problem is, then you need to look at the enemyPos variable. Getting the left position also adds px, so enemyPos is 100px or something like that. When you add 32.5, you get 100px32.5 and when you subtract you get NaN, neither of which you want.
Before you add or subtract, use enemyPos = parseInt($('.object1').css('left')); to turn it into an actual number.

Line wrapping in javascript/appcelerator

How can i determine the number of lines in a <textarea> based on line wrapping ?
( There are no new line characters to be detected. )
Basically, I need a way to programmatically determine the average width of characters in the text area, so I can determine where it wraps (and determine the number of lines in this <textarea>).
This is for an application using Appcelerator
SimpleCoder, a SO member, developed a plugin that tackles this same issue. You can find a description of it as well as a link to the plugin at the article below:
Textarea Line Count
Usage:
var result = $.countLines("#yourTextArea");

Categories