CodeMirror: make atomic range of token - javascript

I'm implementing CodeMirror to use as an editor for special files that require some syntax highlighting. I wrote my own parser for it, but now I face the following problem: there is a specific kind of token that I would always like to mark as an atomic range (with doc.markText).
I would have thought that there would exist some event handler for when tokens have been parsed, containing {line, ch} objects for its start and end positions. Reading through the docs, this does not seem to exist, so I would write my own, but the problem is that there seems to be no way to get any kind of position data whatsoever related to the parser.
What would be the best way to go about this? There are really crude ways like registering a change handler or iterating over the whole contents every few seconds, but of course this should be avoided.

I've forked the CodeMirror github repo and made an event that fires when a token gets parsed.
The syntax is this:
"tokenParsed" (instance: CodeMirror, start: {ch, line}, end: {ch, line}, style: String, text: String)
And then I handle it as follows:
myCodeMirror.on("tokenParsed", function(instance, start, end, style, text) {
if(!instance.findMarksAt(end).length) { //check if the mark doesn't exist yet
if(style && style.indexOf("param") > -1) {
instance.markText(start, end, {atomic: true});
}
}
});
If anyone wants this, see my repository.

Related

pdftron copy wrong text

I want to use pdftron and all things work perfect but when i copy text from pdf some characters convert to blank square and question mark, any idea?
here is my pdf.
As you can see below:
I wrote this code:
WebViewer({
path: '/assets/plugins/pdftron',
initialDoc: '/practical.pdf',
fullAPI: true,
disableLogs: true
}, document.getElementById('pdf')).then((instance) => {
// PDFNet is only available with full API enabled
const { PDFNet, docViewer } = instance;
let Feature = instance.Feature;
instance.disableFeatures([Feature.NotesPanel]);
docViewer.on('documentLoaded', () => {
// call methods relating to the loaded document
});
instance.textPopup.add({
type: 'actionButton',
img: '/language.svg',
onClick: () => {
const quads = docViewer.getSelectedTextQuads(docViewer.getCurrentPage());
const text = docViewer.getSelectedText();
$("#out-pdf").html(text);
console.log(quads);
},
});
});
Document does seem to cause incorrect extraction. Extraction is not defined by PDF specification so every viewer handles cases little differently. I your case there is a probably a malformed or incomplete font or unicode map included in the document. We've added multiple fixes to our core components and with those fixes extraction happens correctly. Unfortunately current release of WebViewer does not include these fixes yet. We cannot give exact time schedule when fixes will be land to the WebViewer, but should be at least part of our next major release. For now I would try to see if you can recreate the document and see if that helps. Most of the documents we see and test have no problem with extraction.
Could you create ticket through our support https://www.pdftron.com/form/request/ and attach the document that this happens to the ticket, so I can take a closer look on and get issue resolved faster.

Using modular design pattern in Javascript with DOM selections

I have been following the Modular Design Pattern for quite some time now and find it extremely useful as it helps in the well maintenance of code & separation of blocks into modules.
Regular usage of the module structure with jQuery has led to most of my applications/code following the below structure:
(function() {
var chat = {
websocket: new WebSocket("ws://echo.websocket.org/"),
that: this,
init: function() {
this.scrollToBottom();
this.bindEvents();
this.webSocketHandlers();
},
bindEvents: function() {
this.toggleChat();
this.filterPeople();
this.compose();
},
elements: {
indicator: $(".indicator"),
statusText: $(".status-text"),
chatHeadNames: $(".people li .name"),
filterInput: $("#filter-input"),
msgInput: $("#msg-input"),
sendBtn: $(".send")
},
...
...
...
filterPeople: function() {
var that = this;
this.elements.chatHeadNames.each(function() {
$(this).attr('data-search-term', $(this).text().toLowerCase());
});
},
...
...
};
chat.init();
})();
What I would like to know is whether referencing all my elements via jQuery as part of a single variable chat.elements is a good practice?
One part of me tells that it indeed is a good way to reference all your selectors at once and cache them in variables so that multiple usages of the same element can be done with the cached variables (instead of multiple DOM selections).
Another part of me tells that this might be an anti-pattern and specific elements should be selected and cached locally when required.
I have used similar structures throughout and have got mixed responses about the code, but nothing solid. Any help would be appreciated. Thanks!
Caching the selectors is a good thing. Hanging on to them is a good idea. It improves performance over repeatedly querying the DOM for the same thing. The code you have above looks very similar to BackboneJS and MarionetteJS code.
I do have some warnings for you though:
This pattern could cause memory leaks. Consider the case where you destory a subview, but you keep a reference to something that selected it. This is called a dangling pointer. The view won't really disappear. All bindings will remain. Events will continue to fire behind the scenes.
You will eventually run into a bug where you decided to re-render part of your screen. Cleaning up all your bindings is then required and you need to remember to delete and selectors. If you don't do this you will almost certainly run into issues where you wonder why an event is indeed firing but nothing appears to happen on screen.... (this will be because its happening off screen, to the element that you tried to delete, that still exists... sorta).
The current way you are querying for elements causes searches against the entire page. Take a look at https://api.jquery.com/find/. If you cache one selector and then perform searches within that selector it may gain you a little performance bump.
I think, If the chat module has selectors only for its children, then it's a good pattern. Like:
<div id="chat-module">
<div class="indicator">...</div>
<div class="status-text">...<div>
...
</div>
<script src="and your chat module.js"></script>
// your chat module selecting .indicator:
// $('#chat-module.indicator')
Also, add a shut-down function to your module. So, when you remove it from the view (as in a single-page-app), you can nullify your selectors and detaching event handlers, like: delete this.elements.indicator and event detaching code.
There are also other/better patterns for this, like, when a user types something, you fire an event, and catch that event in your module. To separate UI and the code.

selectively using different range sliders using Algolia instantsearch.js

With Algolia Instantsearch.js I'm looking to selectively use different rangeSliders depending on the value of a passed country_code parameter in the query string.
If e.g: the country_code is 'FR' I want to use a total_area_meters range slider, and use another range slider that uses euros. For the 'UK' I want to use total_area_ft and another range slider that uses pounds.
Though I'm waiting (and hoping) for https://github.com/algolia/instantsearch.js/issues/753 to be implemented, I've been told by Algolia support personnel that it's potentially possible to use the helper library: https://github.com/algolia/algoliasearch-helper-js to get this working.
I have e.g: experimented with selectively enabling disjunctive facets using:
search.addWidget({
init: function (opts) {
// opts.helper contains the underlying algoliasearchHelper
if(detected_locale === 'fr'){
opts.helper.setQueryParameter('disjunctiveFacets', [
'total_area_meters','price_eur'
]);
//...
Though doing this is not enough: among other issues, there are still slider widgets on the page e.g: requiring 'total_area_feet, and 'price_gbp' which raise javascript errors - I need to disable them somehow, and also there are quite obtrusive urls that need to be selectively removed.
e.g: If my disjunctive facet is: total_area_meters: I get a url like this:
&nR[total_area_meters][>=][0]=135&nR[total_area_meters][<=][0]=770 - which needs to be removed when I have a country_code where feet are used, instead.
So my questions about this are:
Is there a way to selectively enable/disable instantsearch.js rangeslider widgets programmatically via js? (or can I somehow programmatically set their values/ reset them so they have no query parameters?)
If not covered in the solution above, are there any pre-built functions for me to clean up the rangeslider url parameters? Thank you!
With help from Tim Carry at Algolia, I was able to get a work-around working. It's not perfect, but in a nutshell, I first added all my sliders to the page. I then selectively hid them using:
.hideClass {
display: none;
}
$sliders[i].addClass('hideClass');
// or
$sliders[i].removeClass('hideClass');
This performs much better than:
$sliders[i].show()
//and
$sliders[i].hide()
Depending on the country_code in the url.
I found that it's (unfortunately) necessary to re-issue hiding/showing commands on each render call (not just on init()) e.g:
search.addWidget( {
render: function(opts) {
show_hide_sliders();
}
});
It's possible to remove all url parameters for a slider using:
helper.removeNumericRefinement(slider_attribute[i]);
which is only issued if there is a country_code change.
And it pretty-much seems to function (though caveat: only limited testing so far). Hope this is helpful to someone.
EDIT:
To respond to the comments, a note: in this case I got a handle on the helper by using opts.helper (but I probably might have also succeeded using search.helper, too.). e.g:
search.addWidget( {
render: function(opts) {
show_hide_sliders();
opts.helper. ... // add your helper methods...
}
});

PyBossa loading and presenting tasks

I am trying to set up a project on CrowdCrafting.org by using the PyBOSSA framework.
I followed their tutorial for project development.
The first parts seemed very clear to me, creating the project and adding the tasks worked fine.
Then I built my own HTML webpage to present the task to the users. Now the next step would be to load the tasks from the project, present them to the users, and save their answers.
Unfortunately, I don't understand how to do this.
I will try to formulate some questions to make you understand my problem:
How can I try this out? The only way seems to be by updating the code and then running pbs update_project
Where can I find documentation for PyBossa.js? I just saw (in the tutorial and on other pages) that there are some functions like pybossa.taskLoaded(function(task, deferred){}); and pybossa.presentTask(function(task, deferred){});. But I don't know how they work and what else there is. This page looks like it would contain some documentation, but it doesn't (broken links or empty index).
How do I use the library? I want to a) load a task, b) present it to the user, c) show the user his progress, and, d) send the answer. So I think I'll have to call 4 different functions. But I don't know how.
Looking at the example project's code, I don't understand what this stuff about loading disqus is. I think disqus is a forum software, but I am not sure about that and I don't know what this has to do with my project (or theirs).
As far as I understand, the essential parts of the JS-library are:
pybossa.taskLoaded(function(task, deferred) {
if ( !$.isEmptyObject(task) ) {
deferred.resolve(task);
}
else {
deferred.resolve(task);
}
});
pybossa.presentTask(function(task, deferred) {
if ( !$.isEmptyObject(task) ) {
// choose a container within your html to load the data into (depends on your layout and on the way you created the tasks)
$("#someID").html(task.info.someName);
// by clickin "next_button" save answer and load next task
$("#next_button").click( function () {
// save answer into variable here
var answer = $("#someOtherID").val();
if (typeof answer != 'undefined') {
pybossa.saveTask(task.id, answer).done(function() {
deferred.resolve();
});
}
});
}
else {
$("#someID").html("There are no more tasks to complete. Thanks for participating in ... ");
}
});
pybossa.run('<short name>');
I will try to answer your points one by one:
You can either run pbs update project or go to the project page >
tasks > task presenter and edit the code there.
I believe this link works, and there you should find the
information you want.
So, once you've created the project and added the tasks and the
presenter (the HTML you've built) you should include the Javascript
code inside the presenter itself. You actually only need to write
those two functions: pybossa.taskLoaded(function(task,
deferred){}); and pybossa.presentTask(function(task, deferred){});
Within the first one you'll have to write what you want to happen
once the task has been loaded but before you're ready to present it
to the user (e.g. load additional data associated to the tasks,
other than the task itself, like images from external sites). Once
this is done, you must call deferred.resolve(), which is the way
to tell pybossa.js that we are done with the load of the task
(either if it has been successful or some error has happened).
After that, you must write the callback for the second one
(pybossa.presentTask) where you set up everything for your task,
like the event handlers for the button answer submission and here is
where you should put the logic of the user completing the task
itself, and where you should then call pybossa.saveTask(). Again,
you should in the end call deferred.resolve() to tell pybossa.js
that the user is done with this task and present the next one. I
would recommend you to do in inside the callback for
pybossa.saveTask(task).done(callbackFunc()), so you make sure you
go on to the next task once the current one has correctly been
saved.
You can forget about that discuss code. These are only templates
provided, in which there is included some code to allow people
comment about the tasks. For that, Disquss is used, but it is up to
you whether you want to use it or not, so you can safely remove this
code.

Event handling in Dojo

Taking Jeff Atwood's advice, I decided to use a JavaScript library for the very basic to-do list application I'm writing. I picked the Dojo toolkit, version 1.1.1. At first, all was fine: the drag-and-drop code I wrote worked first time, you can drag tasks on-screen to change their order of precedence, and each drag-and-drop operation calls an event handler that sends an AJAX call to the server to let it know that order has been changed.
Then I went to add in the email tracking functionality. Standard stuff: new incoming emails have a unique ID number attached to their subject line, all subsequent emails about that problem can be tracked by simply leaving that ID number in the subject when you reply. So, we have a list of open tasks, each with their own ID number, and each of those tasks has a time-ordered list of associated emails. I wanted the text of those emails to be available to the user as they were looking at their list of tasks, so I made each task box a Dijit "Tree" control - top level contains the task description, branches contain email dates, and a single "leaf" off of each of those branches contains the email text.
First problem: I wanted the tree view to be fully-collapsed by default. After searching Google quite extensively, I found a number of solutions, all of which seemed to be valid for previous versions of Dojo but not the one I was using. I eventually figured out that the best solution would seem to be to have a event handler called when the Tree control had loaded that simply collapsed each branch/leaf. Unfortunately, even though the Tree control had been instantiated and its "startup" event handler called, the branches and leaves still hadn't loaded (the data was still being loaded via an AJAX call). So, I modified the system so that all email text and Tree structure is added server-side. This means the whole fully-populated Tree control is available when its startup event handler is called.
So, the startup event handler fully collapses the tree. Next, I couldn't find a "proper" way to have nice formatted text for the email leaves. I can put the email text in the leaf just fine, but any HTML gets escaped out and shows up in the web page. Cue more rummaging around Dojo's documentation (tends to be out of date, with code and examples for pre-1.0 versions) and Google. I eventually came up with the solution of getting JavaScript to go and read the SPAN element that's inside each leaf node and un-escape the escaped HTML code in it's innerHTML. I figured I'd put code to do this in with the fully-collapse-the-tree code, in the Tree control's startup event handler.
However... it turns out that the SPAN element isn't actually created until the user clicks on the expando (the little "+" symbol in a tree view you click to expand a node). Okay, fair enough - I'll add the re-formatting code to the onExpand() event handler, or whatever it's called. Which doesn't seem to exist. I've searched to documentation, I've searched Google... I'm quite possibly mis-understanding Dojo's "publish/subscribe" event handling system, but I think that mainly because there doesn't seem to be any comprehensive documentation for it anywhere (like, where do I find out what events I can subscribe to?).
So, in the end, the best solution I can come up with is to add an onClick event handler (not a "Dojo" event, but a plain JavaScript event that Dojo knows nothing about) to the expando node of each Tree branch that re-formats the HTML inside the SPAN element of each leaf. Except... when that is called, the SPAN element still doesn't exist (sometimes - other times it's been cached, just to further confuse you). Therefore, I have the event handler set up a timer that periodically calls a function that checks to see if the relevant SPAN element has turned up yet before then re-formatting it.
// An event handler called whenever a "email title" tree node is expanded.
function formatTreeNode(nodeID) {
if (dijit.byId(nodeID).getChildren().length != 0) {
clearInterval(nodeUpdateIntervalID);
messageBody = dijit.byId(nodeID).getChildren()[0].labelNode.innerHTML
if (messageBody.indexOf("<b>Message text:</b>") == -1) {
messageBody = messageBody.replace(/>/g, ">");
messageBody = messageBody.replace(/</g, "<");
messageBody = messageBody.replace(/&/g, "&");
dijit.byId(nodeID).getChildren()[0].labelNode.innerHTML = "<b>Message text:</b><div style=\"font-family:courier\">"+messageBody+"</div>";
}
}
}
// An event handler called when a tree node has been set up - we changed the default fully-expanded to fully-collapsed.
function setupTree(theTree) {
dijit.byId("tree-"+theTree).rootNode.collapse();
messageNode = dijit.byId("tree-"+theTree).rootNode.getChildren();
for (pl = 0; pl < messageNode.length; pl++) {
messageNode[pl].collapse();
messageNode[pl].expandoNode.onclick = eval("nodeUpdateIntervalID = setInterval(\"formatTreeNode('"+messageNode[pl].id+"')\",200); formatTreeNode('"+messageNode[pl].id+"');");
}
}
The above has the feel of a truly horrible hack, and I feel sure I must have taken a wrong turn somewhere early on in my thought process. Can someone please tell me:
The correct way to go about putting nicely-formatted text inside a Dojo/Dijit Tree control.
The correct way to handle Dojo events, like where I can figure out what events are available for me to subscribe to.
A better JavaScript library to use (can I do what I want to with JQuery and avoid the all-around-the-houses approach seen above?).
PS: If you're naming a software project, give thought to its name's uniqueness in Google - I'm sure searching for "Dojo" documentation in Google would be easier without all the martial arts results getting in the way.
PPS: Firefox spellchecker knows how to spell "Atwood", correcting me when I put two 'T's instead of one. Is Jeff just that famous now?
I assume that you followed the dijit.Tree and dojo.data in Dojo 1.1 tutorial which directed you to pass the data to the tree control using a data store. That had me banging my head of a brick wall for a while.
Its not really a great approach and the alternative is not really well documented. You need to create a use model instead. I have included an example below of a tree model that I created for displaying the structure of an LDAP directory.
You will find the default implementation of the model in your dojo distribution at ./dijit/_tree/model.js. The comments should help you understand the functions supported by the model.
The IDirectoryService class the code below are stubs for server-side Java POJOs generated by Direct Web Remoting (DWR). I highly recommend DWR if you going to be doing a lot of client-server interaction.
dojo.declare("LDAPDirectoryTreeModel", [ dijit.tree.model ], {
getRoot : function(onItem) {
IDirectoryService.getRoots( function(roots) {
onItem(roots[0])
});
},
mayHaveChildren : function(item) {
return true;
},
getChildren : function(parentItem, onComplete) {
IDirectoryService.getChildrenImpl(parentItem, onComplete);
},
getIdentity : function(item) {
return item.dn;
},
getLabel : function(item) {
return item.rdn;
}
});
And here is an extract from the my JSP page where I created the model and used it to populate the tree control.
<div
dojoType="LDAPDirectoryTreeModel"
jsid="treeModel"
id="treeModel">
</div>
<div
jsid="tree"
id="tree"
dojoType="dijit.Tree" model="treeModel"
labelAttr="name"
label="${directory.host}:${directory.port}">
</div>

Categories

Resources