I am using a javascript library that generates annotations within an ePub. I am hoping to use HTML5 localStorage for storing the annotation data.
Is there a way to have an endpoint url associated with localStorage?
In the hook that invokes the library there is a variable for 'server', which is currently set to a url for the local server, port 5000.
I am hoping to replace that with some pointer to localStorage, but I'm not sure what endpoint would be involved. Seems like the only method for adding in information would be the method "localStorage.setItem", which in turn needs a variable name as an argument, so it wouldn't be appropriate as an endpoint.
The unadulterated hook, before I customized it is in this github repo,
My current version after editing, (which isn't functioning) is as follows:
EPUBJS.Hooks.register("beforeChapterDisplay").annotate = function(callback, chapter){
var chap = chapter.bodyEl,
server = localStorage;
files = [
EPUBJS.filePath + "annotator-full.js"
];
console.log(files);//show the pathways defined above
EPUBJS.core.load(files, chapter.doc.head);
$(chapter.doc.body).annotator().annotator('setupPlugins', {}, {
Filter:false,
Store: {
annotationData: {
'uri': chapter.path
},
loadFromSearch: {
'limit': 100,
'uri': chapter.path
}
}
});
Util.mousePosition = function(e, offsetEl) {
var offset;
offset = $(offsetEl).position();
return {
top: e.pageY,
left: e.pageX
};
};
devAnnotator = new Annotator(chapter.doc.body)
// .addPlugin('Auth', {
// tokenUrl: 'http://annotateit.org/api/token',//'http://localhost:5001/api/token'
// })
// .addPlugin('Unsupported')
// .addPlugin('AnnotateItPermissions')
// .addPlugin('Store', {
// prefix: 'http://annotateit.org/api',//'http://localhost:5000',
// loadFromSearch: {
// uri: chapter.currentChapter.href
// },
// annotationData: {
// uri: chapter.currentChapter.href
// }
// });
// devAnnotator.plugins.Auth.withToken(function (tok) {
// console.log(tok);
// })
// EPUBJS.core.addCss("../libs/annotator/css/annotator.css", false, chapter.doc.head);
if(callback) callback();
function annotate(){
// EPUBJS.core.addCss("css/annotator.css", false, chapter.doc.head);
var s = document.createElement("script");
s.type = 'text/javascript';
console.log(jQuery);
var a = "jQuery.migrateTrace = false;";
a += "console.log(document.getElementById('c001p0002').getBoundingClientRect());";
a += "var content = $('body').annotator().annotator('setupPlugins', {}, {Filter:false});";
//-- Use Local Server:
a += "var content = $('body').annotator(),";
a += " server = '" + server + "';";
a += " path = '" + chapter.path + "';";
a += " content.annotator('addPlugin', 'Store', {";
// The endpoint of the store on your server.
a += " prefix: server,";
// Attach the uri of the current page to all annotations to allow search.
a += " annotationData: {";
a += " 'uri': path";
a += " }";
// This will perform a search action rather than read when the plugin
// loads. Will request the last 20 annotations for the current url.
// eg. /store/endpoint/search?limit=20&uri=http://this/document/only
a += ","
a += " loadFromSearch: {";
a += " 'limit': 20,";
a += " 'uri': path";
a += " }";
a += "});";
s.innerHTML = a;
chapter.doc.body.appendChild(s);
if(callback) callback();
}
}
Just to close the loop on this, solution:
The file isn't an actual ePub, it is a "shell" for ingesting an ePub with ePub.js and then displaying it in a custom way...
Related
I'm experiencing some weird behavior in my code that I don't quite understand. I call a function, and inside that function there is another (anonymous) callback function it skips over and it goes to the end of the containing function, runs those lines, and then goes back into the callback function and runs those lines... Anybody have some insight, what am I doing wrong? Is it doing this because the "relatedQuery" method isn't complete yet so it hasn't hit the callback function before it runs the rest of the containing function's lines? That's the only thing I can think of, but I'm also not very skilled at JS. I've added some console.log statements that will tell you the order in which lines are being hit.
//Call the mgmtPopupContent function
mgmtTractPopupBox.setContent(mgmtPopupContent);
function mgmtPopupContent(feature) {
for (var attrb in feature.attributes) {
if (attrb == "HabitatManagement.DBO.MgmtTracts.OBJECTID") {
var OID = feature.attributes[attrb];
}
}
var relatedQuery = new RelationshipQuery();
relatedQuery.outFields = ["*"];
relatedQuery.relationshipId = 0;
relatedQuery.objectIds = [OID];
//Get data year that the map view is set to and set the definition expression on the table
viewYear = dom.byId("data-year").value;
relatedQuery.definitionExpression = "YearTreated = " + viewYear;
//Create table header that will go inside popup
var content = '<table id="mgmtPopupTable1"><tr><th>Veg Mgmt Practice</th><th>Herbicide</th><th>Month</th><th>Year</th>\
<th>Implemented By</th><th>Funded By</th><th>Farm Bill Code</th></tr>';
console.log("PRINTS FIRST");
//Do query and get the attributes of each related record for the popup
queryableMgmtTractFL.queryRelatedFeatures(relatedQuery, function (relatedRecords) {
console.log("PRINTS THIRD");
var fset = relatedRecords[OID].features;
fset.forEach(function (feature) {
var vegPractice = vegPName(feature.attributes.VegMgmtPractice);
var herbicide = herbName(feature.attributes.Herbicide);
var monthTreated = monthName(feature.attributes.MonthTreated);
var yearTreated = feature.attributes.YearTreated;
var impBy = impName(feature.attributes.ImplementedBy);
var fundBy = fundName(feature.attributes.FundedBy);
var fbc = feature.attributes.FarmBillCode;
if (fundBy == "CRP" || fundBy == "CRP - CREP") {
fbc = crpName(fbc);
}
else if (fundBy == "EQIP" || fundBy == "EQIP - RCPP") {
fbc = eqipName(fbc);
}
else {
fbc = "Not applicable";
}
row = '<tr><td>' + vegPractice + '</td><td>' + herbicide + '</td><td>' + monthTreated + '</td><td>' + yearTreated +
'</td><td>' + impBy + '</td><td>' + fundBy + '</td><td>' + fbc + '</td></tr>';
content = content + row;
});
content = content + '</table>';
});
console.log("PRINTS SECOND");
return content;
}
As mentioned in my comment, you have to wait for the queries to finish before you can render the content. So something like:
let content = '<table id="mgmtPopupTable1"><tr><th>Veg Mgmt Practice</th><th>Herbicide</th><th>Month</th><th>Year</th>\
<th>Implemented By</th><th>Funded By</th><th>Farm Bill Code</th></tr>';
const render_popup = function( content ) {
document.querySelector( '#myPopup' ).innerHTML = content;
};
// Render only the headers to begin with.
render_popup( content );
queryableMgmtTractFL.queryRelatedFeatures(relatedQuery, function (relatedRecords) {
var fset = relatedRecords[OID].features;
fset.forEach(function (feature) {
...
});
// Rerender the popup, now headers And content.
render_popup( content );
});
This code is for internal, offline, single user use, IE only. The code looks at a folder, and lists all files including those in subfolders. It sorts through the data based on some date fields and datelastmodified. It also uses and if to throw out thumbs.db entries. All of the data is put into a table.
My issue is that this script can take a long time to get the data. I would like to add a progress bar but the progress bar cant update while the script is running. After some research it looks like SetTimeOut can allow the page elements to be updated as the script runs, therefore allowing the progress bar to work and looking overall cleaner. However I can not figure out of to implement SetTimeOut into my existing code.
<script type="text/javascript">
var fso = new ActiveXObject("Scripting.FileSystemObject");
function ShowFolderFileList(folderspec) {
var beginningdate = new Date(startdate.value);
var finishdate = new Date(enddate.value);
var s = "";
var f = fso.GetFolder(folderspec);
var subfolders = new Enumerator(f.SubFolders);
for (subfolders.moveFirst(); !subfolders.atEnd(); subfolders.moveNext()) {
s += ShowFolderFileList(subfolders.item().path);
}
// display all file path names.
var fc = new Enumerator(f.files);
for (i = 0; !fc.atEnd(); fc.moveNext()) {
if (fc.item().name != "Thumbs.db") {
var dateModified = fc.item().DatelastModified;
if (dateModified >= beginningdate && dateModified <= finishdate) {
Date.prototype.toDateString = function () {
return [this.getMonth() + 1, '/', this.getDate(), '/', this.getFullYear()].join('');
}
var dateModifiedClean = (new Date(fc.item().DatelastModified).toDateString());
s += "<table border=0 width=100% cellspacing=0><tr " + ((i % 2) ? "" : "bgcolor=#EBF1DE") + "><td width=75%><font class=find><b>" + fc.item().ParentFolder.name + "</b>" + " - " + fc.item().name + "</font></td><td width=25% align=right><font class=find>" + dateModifiedClean + "</font></td></tr>";
i++;
}
}
}
var results = s + "</table>";
return results;
}
function listFiles() {
outPut.innerHTML = ShowFolderFileList('*Path to scan*');
}
</script>
outPut is the ID of a div tag where the results table is displayed. A button calls the listfiles function.
I'm trying to scrape a number of pages that have a standard format. I've been able to use Phantomjs to successfully scrape a single page, but when I try to iterate over multiple ones, the asynchronous processing makes things hang up. What's the proper way to tell Casper/Phantom to wait?
var page = require('webpage').create();
var fs = require('fs');
page.onConsoleMessage = function(msg) {
phantom.outputEncoding = "utf-8";
console.log(msg);
};
// this overwrites the previous output file
f = fs.open("lat_long.txt", "w");
f.write("--");
f.close();
// this is the unique identifier for the locations. For now, I just have three datapoints
var EPAID = ["KYD980501076","ME8170022018", "MEN000103584"];
/// this code will be used to loop through the different locations. For now, set to look at only one.
for (q= 0; q < 1; q++) {
var processing = false;
//we construct the target url
var url = "http://iaspub.epa.gov/enviro/efsystemquery.cerclis?fac_search=site_epa_id&fac_value=" + EPAID[0] + "&fac_search_type=Beginning+With&postal_code=&location_address=&add_search_type=Beginning+With&city_name=&county_name=&state_code=&program_search=1&report=2&page_no=1&output_sql_switch=TRUE&database_type=CERCLIS" ;
page.open(url);
page.onLoadFinished = function(status) {
if ( status === "success" ) {
page.includeJs("http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js", function() {
var str = page.evaluate(function() {
$value = [];
$Object = $(".result tr");
for (i =0 ; i < 10; i++) {
$value.push($Object.find('td').html(),$Object.find('td').next().next().html() );
$Object = $Object.next();
}
$string = "{ EPAID: "+ $value[0] + ", " +
"Name: "+ $value[1] + ", " +
"City: "+ $value[4] + ", " +
"State: "+ $value[6] + ", " +
"ZipCode: "+ $value[8] + ", " +
"Latitude: "+ $value[14] + ", " +
"Longitude: "+ $value[16] + " }" ;
return $string;
});
f = fs.open("lat_long.txt", "a");
f.write(str);
f.close();
processing = true;
console.log("writing to file");
phantom.exit();
});
}
// right here it should delay until the previous page is completed
// while (!processing) {
// setTimeout(function(){ console.log("waiting....");},1000);
// }
};
}
console.log("finished all pages");
If you switched to using casperJS, it is as simple as changing your page.open() into page.thenOpen(). (This CasperJS - How to open up all links in an array of links question looks very similar to yours?)
If you wanted to stick with PhantomJS you need to start the next page load in the onSuccess callback of the previous load. This is tedious, and needs care to avoid large memory usage. (I did it once or twice, but now simply use CasperJS.)
An alternative approach is to create the page object inside the loop. However that is not quite answering your question, as then they will run in parallel. But you could use setTimeout to stagger each once to avoid a burst of activity if you have hundreds of URLs!
Here is the code that ultimately works (using the timeout approach since I wasn't able to get the success callback to work better).
With casperjs installed, I named this file "process.js" and was able to run it from the command line as "casperjs process.js"
var page = require('webpage').create();
var fs = require('fs');
page.onConsoleMessage = function(msg) {
phantom.outputEncoding = "utf-8";
console.log(msg);
};
// this overwrites the previous output f
// this is the unique identifier for the locations.
var EPAID = ["NED981713837",... , "FLD049985302", "NJD986643153"];
f = fs.open("lat_long.txt", "w");
f.write("-<>-");
f.close();
var count = 0;
var target = 1400;
var written = [];
function yourFunction(){
if (count < target) {
process(count);
count++;
setTimeout(yourFunction, 5000);
} else {
console.log("exiting");
phantom.exit();
return;
}
}
function process(counter){
var processing = false;
console.log("Beginning record #" + counter);
//we construct the target url
var url = "http://iaspub.epa.gov/enviro/efsystemquery.cerclis?fac_search=site_epa_id&fac_value=" + EPAID[counter] + "&fac_search_type=Beginning+With&postal_code=&location_address=&add_search_type=Beginning+With&city_name=&county_name=&state_code=&program_search=1&report=2&page_no=1&output_sql_switch=TRUE&database_type=CERCLIS" ;
page.open(url);
page.onLoadFinished = function(status) {
if ( status === "success" ) {
page.includeJs("http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js", function() {
var str = page.evaluate(function() {
$value = [];
$Object = $(".result tr");
for (i =0 ; i < 10; i++) {
$value.push($Object.find('td').html(),$Object.find('td').next().next().html() );
$Object = $Object.next();
}
$string = "{ \"EPAID\": \""+ $value[0] + "\", " +
"\"Name\": \""+ $value[1] + "\", " +
"\"City\": \""+ $value[4] + "\", " +
"\"State\": \""+ $value[6] + "\", " +
"\"ZipCode\": \""+ $value[8] + "\", " +
"\"Latitude\": "+ $value[14] + ", " +
"\"Longitude\": "+ $value[16] + " }," ;
return $string;
});
if (written[counter] === undefined) {
f = fs.open("lat_long.txt", "a");
f.write(str);
f.close();
written[counter] = true;
console.log("Writing to file #"+ counter);
}
});
}
};
}
console.log("Start...");
yourFunction();
This title may not be 100% accurate with regards to my question, and I apologize beforehand if that's the case. Here's my problem, I found this cool applet that allows you to paste an image from your clipboard without the need for an upload. However, my site does not use explicitly written HTML, so I don't know how to insert this applet. For example, here is my code for an Upload button, the kind you would use normally to input an image:
getUploadControl: function(data) {
var uploadData = data.uploadData || {},
resultHandler = generateCallbackHandler({
success: data.onSuccess,
error: data.onError,
busyMethod: noop
}),
eventHandler = function(e) {
var jsonResponse = parseJSON(e.XMLHttpRequest.responseText);
resultHandler(jsonResponse);
};
return extend(true, {
type: "upload",
options: {
async: {
saveUrl: window.CaledonianAPIWebServiceRoot + "WriteFile.aspx"
},
multiple: false,
upload: function(e) {
var ud = copyNestedProperties({}, uploadData);
api.addAuthToData(ud);
e.data = ud;
},
success: eventHandler,
error: eventHandler
}
}, data);
},
I would like to make a similar button like this using the SUPA Applet. Here is a demo of the applet.
I have downloaded the source code for Supa.js, here it is, not very long.
function Supa() {
this.ping = function (supaApplet) {
try {
// IE will throw an exception if you try to access the method in a
// scalar context, i.e. if( supaApplet.pasteFromClipboard ) ...
return supaApplet.ping();
} catch (e) {
return false;
}
};
this.ajax_post = function (actionUrl, bytes, fieldname_filename, filename, params) {
// some constants for the request body
//FIXME: make sure boundaryString is not part of bytes or the form values
var boundaryString = 'AaB03x' + parseInt(Math.random() * 9999999, 10),
boundary = '--' + boundaryString,
cr = '\r\n',
body,
i,
isAsync,
xrequest;
// sanity checks
if (!fieldname_filename || fieldname_filename === "") {
throw "Developer Error: fieldname_filename not set or empty";
}
if (!filename || filename === "") {
throw "Filename required";
}
// build request body
body = '';
body += boundary + cr;
if (isArray(params)) {
for (i = 0; i < params.length; i += 1) {
body += "Content-disposition: form-data; name=\"" + escape(params[i].name) + "\";" + cr;
body += cr;
body += encodeURI(params[i].value) + cr;
body += boundary + cr;
}
}
// add the screenshot as a file
body += "Content-Disposition: form-data; name=\"" + escape(fieldname_filename) + "\"; filename=\"" + encodeURI(filename) + "\"" + cr;
body += "Content-Type: application/octet-stream" + cr;
body += "Content-Transfer-Encoding: base64" + cr;
body += cr;
body += bytes + cr;
// last boundary, no extra cr here!
body += boundary + "--" + cr;
// finally, the Ajax request
isAsync = false;
xrequest = new XMLHttpRequest();
xrequest.open("POST", actionUrl, isAsync);
// set request headers
xrequest.setRequestHeader("Content-Type", "multipart/form-data; charset=UTF-8; boundary=" + boundaryString);
xrequest.send(body);
return xrequest.responseText;
};
}
function supa() {
return new Supa();
}
My question is how do I make a similar widget to use this library for the pasting process? The paste() method can be found in the source HTML for the demo I referenced earlier, it's not in Supa.js, the main file. Plus, I'm not using HTML, so I might have to combine all that code into one? Anyway, any help is appreciated, to get me going on the right track.
I'm writing a script that stores database backup files to multiple locations. In addition, the data is stored on a NAS, which is normally accessible. If this is not the case, the error message is to be intercepted.
I tried it this way:
wshshell = WScript.CreateObject("WScript.Shell");
fso = WScript.CreateObject("Scripting.FileSystemObject");
SetupPath = WScript.arguments(0);
if (fso.FileExists(SetupPath))
{
//Load setup JSON
SetupFile = fso.OpenTextFile(SetupPath, 1, true, 0);
eval(SetupFile.ReadAll());
// Parse setup configuration from JSON
...
BackupPath = Setup["BackupPath"];
FilesForBackup = Setup["FilesForBackup"];
NASBackupPath = Setup["NASBackupPath"];
...
CreateNewBackupFiles();
Log("Script was successfully executed.");
}
else
{
Log("Script was not successfully executed.");
}
/* Creates a new compressed database backup */
function CreateNewBackupFiles()
{
tempfile = fso.GetTempName();
file = fso.GetFile("C:\\Program\\7-Zip\\7z.exe");
rarexe = file.ShortPath;
script = rarexe + " a -r " + tempfile + " " + FilesForBackup;
wshshell.Run(script, 0, true);
fso.CopyFile(tempfile, BackupPath + PrefixBackupFile + "backup.7z");
//Save to NAS
if(NASBackupPath!="")
{
try
{
fso.CopyFile(tempfile, NASBackupPath);
}
catch(err)
{
Log("Path \"" + NASBackupPath + "\" not available.");
}
}
fso.DeleteFile(tempfile, true); //delete tempfile
}
/* Saves a log in "Debug.Log" */
function Log(msg)
{
fs = WScript.CreateObject("Scripting.FileSystemObject");
a = fs.OpenTextFile("Debug.log", 8, true, 0);
line = "[" + CurrentTime()+ "] ";
line += "[" + WScript.ScriptName + "] ";
line += "[" + msg + "]";
a.WriteLine(line);
}
Nevertheless, errors continue to be issued. I´m using the Script on Windows XP SP3. Is there a possibility to catch this error with javascript?
thanks
I have found the error. The error came from the "Log". First, not every time a new "File System object" must be created. It can be simply used the object "fso". In addition, the open text file must be closed at the end of the function. Here is the correction:
function Log(msg)
{
a = fso.OpenTextFile("Debug.log", 8, true, 0);
line = "[" + CurrentTime()+ "] ";
line += "[" + WScript.ScriptName + "] ";
line += "[" + msg + "]";
a.WriteLine(line);
a.Close();
}