I'm going to be dynamically generating a JSON file which is then passed to SCEditor as the emoticons object; this data will come from the database, so essentially it should be safe, but one can never be 100% sure.
This is how it is being called:
// Create var to store emoticons
var emoticons = false;
$.getJSON('../../images/emoticons/default/emoticons.json')
.done(function(response) {
emoticons = response;
console.log(emoticons);
})
.always(function() {
// always initialize sceditor
$(".sceditor").sceditor({
// Other options.....
plugins: "bbcode",
emoticons: emoticons,
});
});
An example of the JSON file would look like:
{
"dropdown": {
":)": "smile.png",
":angel:": "angel.png",
":angry:": "angry.png",
"8-)": "cool.png",
":'(": "cwy.png",
}
}
So the emoticon code and filename are pulled from the database. Is there anything I need to do here other than escape double quotes? Whilst this data will be coming from the database, it's possible the codes/filenames will be provided by the user.
When I store them in the database I will be stripping tags with PHP's strip_tags function.
I wanted to avoid turning the data into html entities as it doesn't seem to play nice with the editor as it doesn't turn the emoticons into smileys within the editor if you say set the code as :") - it will literally output in the editor as :") rather than show the smiley.
Edit: To see an example of how the code is used check out the SCEditor demo. Only difference is the demo uses the default codes provided within the JS file itself and mine will be provided via a JSON file passed as an option.
What is are my best options here?
Related
I am setting up Quill to use as a rich text editor in a blog project. I have the editor working correctly and can store user posts in mongoDB, retrieve them later, and display them.
My code for doing this involves grabbing the delta from quill, stringifying it, then encoding this as a URI. This is what is stored in my db. These actions take place upon hitting my post button but before submitting the form. I am also storing the plain text for other purposes.
function parseQuill(){
document.getElementById("body").value = encodeURIComponent(JSON.stringify(quill.getContents()));
document.getElementById("bodyText").value = quill.getText();
document.getElementById("compose-form").submit();
}
When accessing a blog post, the delta is retrieved from the db and converted back into html for viewing.
This takes place on the backend. The decoded HTML is sent to my post page and rendered with ejs.
app.get("/posts/:postID", function(req, res){
User.findOne({name: "user1"}, function(err, foundUser){
let posts = foundUser.posts;
posts.forEach(function(post){
if (post._id == req.params.postID){
const decoded = JSON.parse(decodeURIComponent(post.content));
const converter = new QuillDeltaToHtmlConverter(decoded.ops);
const decodedHTML = converter.convert();
console.log(decodedHTML);
post.decoded_HTML = decodedHTML;
res.render("post", {post: post});
}
}
);
});
});
This works for all of the default formats that quill offers.
I have been following along with the Quill guide "Cloning medium with parchment" and have attempted to implement the custom divider blot. My code is identical to what is happening there minus the jQuery. My horizontal rule appears in the text editor and behaves as expected.
My issue arises when saving the delta and attempting to convert it back to html. The delta.ops for a post with a horizontal rule looks something like this.
{"ops":[
{"insert":"Some text followed by a horizontal rule.\n"},
{"insert":{"divider":true}},
{"insert":"Some more text.\n"}]}"
The line representing the horizontal rule is the second insert statement of "divider": true. After storing this as a URI component and decoding it back to HTML, the HTML looks like this:
<p>Some text followed by a horizontal rule.<br/>Some more text.</p>
The horizontal rule tag is nowhere to be found. How can I get this to be interpreted correctly and show up?
If I produce a hidden Quill editor container on my post page, I can load in the pure delta and extract the html with quill.root.innerHTML. This produces HTML that contains the HR. I was hoping to avoid inserting the hidden quill container, if at all possible.
I was an idiot and missed an important step in the html decoding process. Leaving this question up with this answer (which solves the problem) incase anyone else stumbles upon a moment of mental ineptitude.
I was using the package quill-delta-to-html to convert my delta's back to usable html. Of course this package doesn't know how to register custom blots. You have to do that manually before converting.
const dividerOp = {
insert: {
type: "divider",
value: true
},
attributes: {
renderAsBlock: true
}
}
converter.renderCustomWith(function(dividerOp){
if (dividerOp.insert.type === "divider"){
return "<hr>"
} else {
console.log("custom blot convert error");
}
});
const decodedHTML = converter.convert();
Quill was doing everything right on it's end. I had a lapse in memory and forgot I was relying on an external package to handle my delta to html conversions. Adding this customBlot render solves the issue.
I've been using fast-csv as my converter library for a while now. A problem emerged when a client actually attempted to upload a csv files that actually contained ';' as a delimiter instead of the default ','. The NPM documentation explicitly says that all methods should accept an 'option' (don't understand why not an object) to actually switch these flags. Of course I can always go into the source js file and change the delimiter manually, but I'd really like to understand this documentation since its all part of my growing as a developer, but still I can't manage to grasp it on how to actually use these options (delimiter) when parsing it on my code. If none of you guys can understand it either, maybe you have some recommendations regarding csv parsers on javascript? Maybe a manual script would be more versatile and useful?
Documentation sample from (fast-csv on npm):
All methods accept the following options
objectMode=true: Ensure that data events have an object emitted rather than the stringified version set to false to have a stringified buffer.
headers=false: Set to true if you expect the first line of your CSV to contain headers, alternatly you can specify an array of headers to use. You can also specify a sparse array to omit some of the columns.
ignoreEmpty=false: If you wish to ignore empty rows.
discardUnmappedColumns=false: If you want to discard columns that do not map to a header.
strictColumnHandling=false: If you want to consider empty lines/lines with too few fields as errors - Only to be used with headers=true
renameHeaders=false: If you want the first line of the file to be removed and replaced by the one provided in the headers option - Only to be used with headers=[String]
delimiter=',': If your data uses an alternate delimiter such as ; or \t.
Also, here is a sample code of how it works, and also how I use it (with pipe):
var stream = fs.createReadStream("my.csv");
var csvStream = csv()
.on("data", function(data){
console.log(data);
})
.on("end", function(){
console.log("done");
});
stream.pipe(csvStream);
//or
var csvStream = csv
.parse()
.on("data", function(data){
console.log(data);
})
.on("end", function(){
console.log("done");
});
stream.pipe(csvStream);
PS: I have tried it asking it elsewhere (where the package is published), but had no replies.
The NPM documentation explicitly says that all methods should accept
an 'option' (don't understand why not an object) to actually switch
these flags
The quoted text basically means that all methods accept a so-called options object as their last parameter. You can specify an alternate delimiter by setting the corresponding field in that object.
but I'd really like to understand this documentation since its all
part of my growing as a developer
I strongly recommend looking at the tests whenever you feel something's not clearly explained in the docs. There's actually a test case for the exact scenario you're describing:
it.should("support semicolon delimiters", function (next) {
var actual = [];
csv
.fromPath(path.resolve(__dirname, "./assets/test16.txt"), {headers: true, delimiter: ";"})
.on("data", function (data) {
actual.push(data);
})
.on("error", next)
.on("end", function (count) {
assert.deepEqual(actual, expected14);
assert.equal(count, actual.length);
next();
});
});
Situation
I am trying to load multiple xml files (located on server) without the need to declare the name of the files hard coded. For this I am trying to use the d3.queue library https://github.com/d3/d3-queue.
I have implemented the xml to force layout to my own needs (https://bl.ocks.org/mbostock/1080941), but there is one crucial flaw namely I need to manually type in the name of the xml file that I want to load...
Reproduce
Given (adjusted example from http://learnjsdata.com/read_data.html) :
queue()
.defer(d3.xml, "/mappings/Customer.hbm.xml")
.defer(d3.xml, "/mappings/Actor.hbm.xml")
.await(analyze);
function analyze(error, Customer, Actor) {
if(error) { console.log(error); }
// do stuff with Customer data, do stuff with Actor data
}
And given my implementation of the processing of an xml:
d3.xml("mappings/Customer.hbm.xml","application/xml", function(error,xml){
if (error) throw error;
// do stuff with the data retrieved from Customer.hbm.xml
});
Question
How do I combine above two snippets in such a way that I dont have to write the locations of the xml hard coded and pass all the parameters to the analyze function? Any nudge in the right direction would be much appreciated.
In psuedocode I have tried to code something like the following (but I cant get it to work):
function to get all names of the xmls from the mappings folder (probably with node.js fs.readdir or fs.readdirSync methods, but I am unsure of how that would work exactly)
for each xml .defer(d3.xml, nameofxml)
pass all the found names as parameters to the analyze function
In Java I would have chosen to do this with a var...args but I dont know how to do it in JS.
There's really two parts to this question:
How do I get a list of server-side files to client-side JavaScript?
Short answer is you don't without having a server-side api that can return that list. Depending on what backend you are using, you write a method that returns a JSON array of the files in your target directory. You call this first, get the response and then process them all with queue:
d3.json('/get/list/of/xml/files', function(error, fileArray){
var q = d3.queue();
fileArray.forEach(function(d){
q = q.defer(d3.xml, d);
});
q.await(analyze);
});
How do a process a variable number of arguments in JavaScript?
This is actually very well supported in JavaScript.
function analyze(error) {
if(error) { console.log(error); }
// skip 0 it's error variable
for (i = 1; i < arguments.length; i++) {
var xml = arguments[i];
...
}
}
I am using couchbase API in java
View view = client.getView("dev_1", "view1");
Query query = new Query();
query.setIncludeDocs(true);
query.setKey(this.Key);
ViewResponse res=client.query(view, query);
for(ViewRow row: res)
{
// Print out some infos about the document
a=a+" "+row.getKey()+" : "+row.getValue()+"<br/>";
}
return a;
and the java script view in couchbase
function (doc,meta) {
emit(meta.id,doc);
}
So, when I remove the statement query.setkey(this.Key) it works returns me all the tables, what am I missing here .. How can I change the function to refect only the table name mentioned in the key
Change the map function like this:
function (doc,meta) {
emit(doc.table,null);
}
it is good practice not to emit the entire document like:
emit(doc.table, doc)
NB: This is surprisingly important:
i have tried using setKey("key") so many times from Java projects and setting the key using CouchBase Console 3.0.1's Filter Result dialog, but nothing get returned.
One day, i used setInclusiveEnd and it worked. i checked the setInclusiveEnd checkbox in CouchBase Console 3.0.1's Filter Result dialog and i got json output.
query.setKey("whatEverKey");
query.setInclusiveEnd(true);
i hope this will be helpful to others having the same issue. if anyone finds another way out, please feel free to add a comment about it.
i don't know why their documentation does not specify this.
EXTRA
If your json is derived from an entity class in a Java Project, make sure to include an if statement to test the json field for the entity class name to enclose you emit statement. This will avoid the key being emitted as null:
if(doc._class == "path.to.Entity") {
emit(doc.table, null);
}
I've used the webOS Ares tool to create a relatively simple App. It displays an image and underneath the image are two labels. One is static, and the other label should be updated with new information by tapping the image.
When I tap the image, I wish to obtain a JSON object via a URL (http://jonathanstark.com/card/api/latest). The typcial JSON that is returned looks like this:
{"balance":{"amount":"0","amount_formatted":"$0.00","balance_id":"28087","created_at":"2011-08-09T12:17:02-0700","message":"My balance is $0.00 as of Aug 9th at 3:17pm EDT (America\/New_York)"}}
I want to parse the JSON's "amount_formatted" field and assign the result to the dynamic label (called cardBalance in main-chrome.js). I know that the JSON should return a single object, per the API.
If that goes well, I will create an additional label and convert/assign the "created_at" field to an additional label, but I want to walk before I run.
I'm having some trouble using AJAX to get the JSON, parse the JSON, and assign a string to one of the labels.
After I get this working, I plan to see if I can load this result on the application's load instead of first requiring the user to tap.
So far, this is my code in the main-assistant.js file. jCard is the image.
Code:
function MainAssistant(argFromPusher) {}
MainAssistant.prototype = {
setup: function() {
Ares.setupSceneAssistant(this);
},
cleanup: function() {
Ares.cleanupSceneAssistant(this);
},
giveCoffeeTap: function(inSender, event) {
window.location = "http://jonathanstark.com/card/#give-a-coffee";
},
jcardImageTap: function(inSender, event) {
//get "amount_formatted" in JSON from http://jonathanstark.com/card/api/latest
//and assign it to the "updatedBalance" label.
// I need to use Ajax.Request here.
Mojo.Log.info("Requesting latest card balance from Jonathan's Card");
var balanceRequest = new Ajax.Request("http://jonathanstark.com/card/api/latest", {
method: 'get',
evalJSON: 'false',
onSuccess: this.balanceRequestSuccess.bind(this),
onFailure: this.balanceRequestFailure.bind(this)
});
//After I can get the balance working, also get "created_at", parse it, and reformat it in the local time prefs.
},
//Test
balanceRequestSuccess: function(balanceResponse) {
//Chrome says that the page is returning X-JSON.
balanceJSON = balanceResponse.headerJSON;
var balanceAmtFromWeb = balanceJSON.getElementsByTagName("amount_formatted");
Mojo.Log.info(balanceAmtFromWeb[0]);
//The label I wish to update is named "updatedBalance" in main-chrome.js
updatedBalance.label = balanceAmtFromWeb[0];
},
balanceRequestFailure: function(balanceResponse) {
Mojo.Log.info("Failed to get the card balance: " + balanceResponse.getAllHeaders());
Mojo.Log.info(balanceResponse.responseText);
Mojo.Controller.errorDialog("Failed to load the latest card balance.");
},
//End test
btnGiveCoffeeTap: function(inSender, event) {
window.location = "http://jonathanstark.com/card/#give-a-coffee";
}
};
Here is a screenshot of the application running in the Chrome browser:
In the browser, I get some additional errors that weren't present in the Ares log viewer:
XMLHttpRequest cannot load http://jonathanstark.com/card/api/latest. Origin https://ares.palm.com is not allowed by Access-Control-Allow-Origin.
and
Refused to get unsafe header "X-JSON"
Any assistance is appreciated.
Ajax is the right tool for the job. Since webOS comes packaged with the Prototype library, try using it's Ajax.Request function to do the job. To see some examples of it, you can check out the source code to a webOS app I wrote, Plogger, that accesses Blogger on webOS using Ajax calls. In particular, the source for my post-list-assistant is probably the cleanest to look at to get the idea.
Ajax is pretty much the way you want to get data, even if it sometimes feels like overkill, since it's one of the few ways you can get asynchronous behavior in JavaScript. Otherwise you'd end up with code that hangs the interface while waiting on a response from a server (JavaScript is single threaded).