Highlight.js custom language definition - javascript

I'm trying to create language definition for highlight.js.
But it doesn't work. I've got an example.
In this example i'm trying to create custom "aaa" language, which is the same as JSON. registerLanguage function receives same function as a default JSON highlight function (from highlight.js sources).
hljs.listLanguages() shows, that language is registered.
After, i'm calling hljs.highlightBlock(block).
<code class="aaa"> forces to use custom "aaa" language, and in this case hljs.highlightBlock(block) doesn't changes content.
$(document).ready(function() {
// registering aaa language (JSON alias)
// code from https://github.com/isagalaev/highlight.js/blob/master/src/languages/json.js
hljs.registerLanguage("aaa", function(hljs) {
var LITERALS = {
literal: 'true false null'
};
var TYPES = [
hljs.QUOTE_STRING_MODE,
hljs.C_NUMBER_MODE
];
var VALUE_CONTAINER = {
className: 'value',
end: ',',
endsWithParent: true,
excludeEnd: true,
contains: TYPES,
keywords: LITERALS
};
var OBJECT = {
begin: '{',
end: '}',
contains: [{
className: 'attribute',
begin: '\\s*"',
end: '"\\s*:\\s*',
excludeBegin: true,
excludeEnd: true,
contains: [hljs.BACKSLASH_ESCAPE],
illegal: '\\n',
starts: VALUE_CONTAINER
}],
illegal: '\\S'
};
var ARRAY = {
begin: '\\[',
end: '\\]',
contains: [hljs.inherit(VALUE_CONTAINER, {
className: null
})], // inherit is also a workaround for a bug that makes shared modes with endsWithParent compile only the ending of one of the parents
illegal: '\\S'
};
TYPES.splice(TYPES.length, 0, OBJECT, ARRAY);
return {
contains: TYPES,
keywords: LITERALS,
illegal: '\\S'
};
});
console.log(hljs.listLanguages()); // aaa in the list
$('pre code').each(function(i, block) {
hljs.highlightBlock(block);
});
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.4/styles/tomorrow.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.4/highlight.min.js"></script>
<pre><code class="aaa">
{"menu": {
"id": "file",
"value": "File",
"popup": {
"menuitem": [
{"value": "New", "onclick": "CreateNewDoc()"},
{"value": "Open", "onclick": "OpenDoc()"},
{"value": "Close", "onclick": "CloseDoc()"}
]
}
}}
</code></pre>

Highlight.js have a minification/packing process during build that renames the fields of the language definition objects. This means that the objects they describe in http://highlightjs.readthedocs.org/en/latest/language-guide.html won't work with the distributed library. Only languages defined before build time will work this way.
You could look in the minified highlightjs file for usages of registerLanguage to try and reverse engineer the names of fields.

Related

jsonschema not throwing error on missing required property

I have the following schema:
const LIST_EVENTS = {
"id": "/listEvents",
"type": "object",
"properties": {
"filter": {
"$ref": "/MarketFilter",
"required": true
},
"locale": {
"type": "string"
}
}
}
From debugging, I can see that the object being sent to the validation is:
{
marketFilter: {
eventTypeIds: [ '1' ],
marketStartTime: {
from: '2018-12-15T00:00:00+00:00',
to: '2018-12-15T23:59:59+00:00'
}
}
}
marketFilter does not match the name of filter in the schema. To my understanding, seeing as this is a required property, this should have been flagged in the errors array of the validation result but it is not. This is my validation result:
ValidatorResult {
instance:
{ marketFilter: { eventTypeIds: [Array], marketStartTime: [Object] } },
schema:
{ id: '/listEvents',
type: 'object',
properties: { filter: [Object], locale: [Object] } },
propertyPath: 'instance',
errors: [],
throwError: undefined,
disableFormat: false }
I thought that it was possible that it did not mind about the naming convention so I removed the property altogether and still, an error is not logged with this being the validation result:
ValidatorResult {
instance: {},
schema:
{ id: '/listEvents',
type: 'object',
properties: { filter: [Object], locale: [Object] } },
propertyPath: 'instance',
errors: [],
throwError: undefined,
disableFormat: false }
I have many schemas and they are all added via .addSchema method
You have two issues with your schema. The main issue is that your required keyword is ignored because it is next to $ref. When an object with a $ref keyword is encountered where a schema is expected, it's treated as a JSON Reference only. It's not treated as a schema. A JSON Reference only has semantics for the $ref keyword. Everything else is ignored. You could fix your problem by isolating the $ref in you schema.
"filter": {
"allOf": [{ "$ref": "/MarketFilter" }],
"required": true
}
The other problem is the use of the boolean form of the required keyword. This usage of the required keyword was removed from the JSON Schema specification years ago. Unless you are specifically writing JSON Schemas against the draft-03 specification (unlikely, it's long out of date), you should be using the array form of required. Some older implementations allow you to use both forms, but that's not a good idea. You should be targeting a single specification and not mix keywords from two different versions of specification.
{
"id": "/listEvents",
"type": "object",
"properties": {
"filter": {
"$ref": "/MarketFilter"
},
"locale": {
"type": "string"
}
},
"required": ["filter"]
}
For now, I have found a work-around that is also described in the docs. I have added the required array property to the schema and added filter to it. This now threw an error.
However, the documentation states that the required property on the property itself should work the same. Is this potentially an issue with the package or is there different behaviours if the property is a reference?

storing content from a Quill form in the database

I've been trying a couple of rich text editors, and Quill seems to be the best tool to implement into my projects. I'm using Node/Express and MongoDB/Mongoose. In my 'newPost route', I have a form that just takes a title and the main content. Since I'm just testing it out, the title field is outside of the Quill editor. Here the error I'm getting:
This is after I hit "submit". The console shows not only the title but also the main content that has a code-block with syntax highlight which is exactly what I wanted. But it's not being added to the database.
There's also this message Use of Mutation Events is deprecated. Use MutationObserver instead., and it seems I'm required to change Quill's source code myself to fix this.
Getting the main content of the post on the console kind of gave me a feeling that I'm halfway of getting this to work.
Is there a not-so-hard way to get this to work? Thanks!!
js
var quill;
var metaData = [];
hljs.configure({ // optionally configure hljs
languages: ['javascript', 'ruby', 'python']
});
hljs.initHighlighting();
$(document).ready(function() {
var toolbarOptions = [['blockquote', 'code-block'],
["bold", "italic", "underline", "strike"], // toggled buttons
//['blockquote'],
[{ list: "ordered" }, { list: "bullet" }],
[{ script: "sub" }, { script: "super" }], // superscript/subscript
[{ indent: "-1" }, { indent: "+1" }], // outdent/indent
[{ direction: "rtl" }], // text direction
[{ size: ["small", false, "large", "huge"] }], // custom dropdown
[{ header: [1, 2, 3, 4, 5, 6, false] }],
[{ color: [] }, { background: [] }], // dropdown with defaults from theme
[{ font: [] }],
[{ align: [] }],
["clean"] // remove formatting button
];
quill = new Quill("#snow-container", {
placeholder: "Compose an epic...",
modules: {
syntax: true,
toolbar: toolbarOptions
},
theme: "snow"
});
var oldDelta = {"ops":[{"attributes":{"color":"#000000"},"insert":"This is sample text."}]};
quill.setContents(oldDelta);
});
var form = document.querySelector('form');
form.onsubmit = function() {
// Populate hidden form on submit
var formContent = document.querySelector('input[name=content]');
formContent.value = JSON.stringify(quill.getContents());
console.log("Submitted", $(form).serialize(), $(form).serializeArray());
// No back end to actually submit to!
//alert('Open the console to see the submit data!')
return false;
};

How to read a multidimensionnal javascript object?

First of all, here is my Javascript object :
var languages = {
languages: [
{ name: "French", locale: "FR", id: "-1" },
{ name: "English", locale: "IT", id: "-2" },
{ name: "Spanish", locale: "ES", id: "-3" },
{ name: "Zoulou", locale: "ZL", id: "-4" },
{ name: "Italian", locale: "EN", id: "-5" }
]
};
I'm using Mustache.js to generate language buttons:
function generateLanguages(languages) {
var output = $("#languages-output");
var template = "{{#languages}}<button id={{id}}><img src=#FLAG onclick='changeLanguage({{locale}})' /><p>{{name}}</p></button>{{/languages}}";
html = Mustache.render(template, languages);
output.append(html);}
I also have a folder containing all the flags images called flags (e.g flags/English.png).
I'm trying to generate the buttons by adding the corresponding flag.
I really have no idea how to do it, I thought to use the "locale" property of the Javascript object in a for loop and for each locale, create a big switch to choose the right image. If someone can help me ?
http://jsfiddle.net/lBrowz/7w5grype/
A few improvements and ideas:
Move templates to your HTML. That's where they should be. Reference them from JS by their ID. It's a lot easier to modify and think about templates when they are outside of the JavaScript.
Maybe using flags for languages isn't the best choice. Sometimes a language cannot accurately be represented by a flag (see Zulu, which I chose to represent by the flag of Mozambique, but that might not be what your users expect. The same is true for English, when you think about it.)
Anyway, instead of one flag image per language, use a single image that contains all flags and then use CSS sprites to display selected flags. I used this project here: http://tkrotoff.github.io/famfamfam_flags/
Use data-* attributes to store extra information in elements.
Use event delegation to handle clicks. Never use inline event handlers (like onclick).
var languages = {
languages: [
{ name: "French", locale: "FR", id: "-1", cls: "famfamfam-flag-fr" },
{ name: "English", locale: "IT", id: "-2", cls: "famfamfam-flag-gb" },
{ name: "Spanish", locale: "ES", id: "-3", cls: "famfamfam-flag-es" },
{ name: "Zoulou", locale: "ZL", id: "-4", cls: "famfamfam-flag-mz" },
{ name: "Italian", locale: "EN", id: "-5", cls: "famfamfam-flag-it" }
]
};
function changeLanguage(locale) {
$("#locale").text(locale);
}
$(function () {
var languagesTemplate = $("#languages-template").html();
$("#languages-output").html( Mustache.render(languagesTemplate, languages) );
$(document).on("click", ".language-button", function () {
var locale = $(this).data("locale");
changeLanguage(locale);
});
});
.language-button {
width: 5em;
height: 4em;
margin: 3px;
}
.language-button > span {
display: inline-block;
margin: 2px;
}
<link href="http://tkrotoff.github.io/famfamfam_flags/famfamfam-flags.css" rel="stylesheet"/>
<script src="http://cdnjs.cloudflare.com/ajax/libs/mustache.js/0.7.0/mustache.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div id="languages-output"></div>
<div id="locale"></div>
<script type="text/x-handlebars-template" id="languages-template">
{{#languages}}
<button id="{{id}}" class="language-button" data-locale="{{locale}}">
<span class="{{cls}}"></span>
<span>{{name}}</span>
</button>
{{/languages}}
</script>
Run the code snippet above to see it live.

AngularJS - parsing JSON feed with namespace

New to angular, and it is awesome.
One thing I am having a brain fart on is parsing a JSON feed that contains namespaces:
Example from JSON feed:
"title": {
"label": "Fuse"
},
"im:name": {
"label": "John Doe"
},
"im:image": [ {
"label": "70x70",
"attributes": {
"height": "55"
}
}, {
"label": "80x80",
"attributes": {
"height": "60",
"im:link": "www.google.com"
}
}, {
"label": "90x90",
"attributes": {
"height": "170"m
"im:link": "www.yahoo.com"
}
}],
I can successfully parse items without namespaces fine like so:
<p ng-repeat="item in results.feed['entry']">
title: {{item.title['label']}}
</p>
But cannot get the items with namespaces to display using:
name: {{item.['im:name']['label']}}
OR
name: {{item.['im-name']['label']}}
OR
name: {{item.['im->name']['label']}}
Since being a newbie, I thought something like this would work:
<div xmlns:im="http://www.aol.com" id="im-app" im-app="im">
<p ng-repeat="item in results.feed['entry']">
…namespace code in here…
</p>
</div>
But that did not help.
Extra bonus question: What if a namespace contains attributes, that also contain namespaces?
Any help would greatly be appreciated.
Thanks!
Roc.
Although Craig answered the question,
This is also for reference for others:
If you want to target a specific key inside of an object set:
"im:image":[
{
"label":google",
"attributes":{
"height":"55"
}
},
{
"label":"yahoo",
"attributes":{
"height":"60"
}
},
{
"label":"aol",
"attributes":{
"height":"170"
}
}
{{item['im:image'][2]['label']}}
Will get the 3rd key in that set.
Thanks.
Get rid of the dot after item
Working example: http://jsfiddle.net/bonza_labs/Kc2uk/
You access the properties exactly the same way as straight javascript (because angular is basically eval()-ing the expression as javascript**). item.['foo'] is not valid javascript. You are correct in using square-bracket notation as my:name is not valid for dot-notation.
valid:
item.foo
item['foo']
with non-standard property names:
item['foo:bar']
item['foo-bar']
and in your case:
{{item['im:name']['label']}}
** or close enough for understanding this solution

Programmatically Set Constructor Parameters in Javascript

I am trying to interact with a javascript api (bare in mind I have never done this before). An example of what I am attempting to work with is here:
SearchSpring.Catalog.init({
leaveInitialResults : true,
facets : '.leftNav',
results : '#results',
result_layout: 'list',
results_per_page : 12,
layout: 'top',
loadCSS: false,
filters: {
color: ['Blue']
},
backgroundFilters: {
category: ['Shirt', 'Shoes'],
department: ['Mens']
},
maxFacets: 5,
maxFacetOptions: 10,
sortText: 'Sort By ',
sortType: 'dropdown',
filterText: 'Refine Search Results',
previousText: 'Previous',
scrollType: 'scroll',
scrollTo: 'body',
backgroundSortField: 'price',
backgroundSortDir: 'desc',
compareText: 'Compare Items',
summaryText: 'Current Filters',
showSummary: true,
subSearchText: 'Subsearch:',
showSubSearch: true,
forwardSingle: false,
afterResultsChange: function() { $('.pagination').hide(); },
filterData: function(data) { console.debug(data); }
});
In the example I want to add a "backgroundFilter" to this with a value:
var cat="MyNewCategory";
cat.value="ANewValue;
How would I add this category and value to the backgroundFilters: listed above?
This is a very common framework initialization pattern when working with frameworks.
Your example code is passing a JavaScript Object {} as a parameter into a function () that is called init.
Taking out all definitions the pattern looks like this:
SomeFramework.frameworkFunction({});
In the above code the {} is an empty object used for initialization. There are two ways that you can work with that object in practice.
Regarding your first code snippet, you can add code into that 'object literal'.
backgroundFilters: {
category: ['Shirt', 'Shoes'],
department: ['Mens'],
cat: ['My value']
},
Notice the added comma, this is an important tripping point. This may or may not fit your needs, depending on a few factors.
Regarding your second code snippet, you can apply members to JavaScript objects at runtime. What I mean is, your var cat can be added to the anonymous object-literal that is being passed in. Hard to say, but a simple concept. Here is how:
//Say this is initialized in some separate way. //There is a bug here I'll describe later.
var cat="MyNewCategory";
cat.value="ANewValue";
//Extract and name the initialization object. It is verbatim at this point.
var initObject = {
leaveInitialResults : true,
facets : '.leftNav',
results : '#results',
result_layout: 'list',
results_per_page : 12,
layout: 'top',
loadCSS: false,
filters: {
color: ['Blue']
},
backgroundFilters: {
category: ['Shirt', 'Shoes'],
department: ['Mens']
},
maxFacets: 5,
maxFacetOptions: 10,
sortText: 'Sort By ',
sortType: 'dropdown',
filterText: 'Refine Search Results',
previousText: 'Previous',
scrollType: 'scroll',
scrollTo: 'body',
backgroundSortField: 'price',
backgroundSortDir: 'desc',
compareText: 'Compare Items',
summaryText: 'Current Filters',
showSummary: true,
subSearchText: 'Subsearch:',
showSubSearch: true,
forwardSingle: false,
afterResultsChange: function() { $('.pagination').hide(); },
filterData: function(data) { console.debug(data); }
};
//Now we can add variables (and functions) dynamically at runtime.
initObject.cat = cat;
//And pass them into the framework initialization in a separated way.
SearchSpring.Catalog.init(initObject);
Now for the bug. I don't know the solution because I do not know what it is intended to do, but I can point out what is potentially incorrect.
var cat="MyNewCategory";
cat.value="ANewValue;
This code is: 1 creating a String Object called cat. 2 changing the value to a new string.
I do not think this is what you really want.
To add a new backgroundFilter, in the separated way above, it would be:
initObject.backgroundFilters.cat = ['A', 'B'];
//Line above would give you this type of definition within the initObject (at runtime):
backgroundFilters: {
category: ['Shirt', 'Shoes'],
department: ['Mens'],
cat: ['A','B']
},
For this to work it will depend on what the framework is expecting regarding backgroundFilters.
Hope that helps.
All the best!
Nash
I don't quite understand - do you want to have the backgroundFilters categories as structured objects rather than plain strings? If you are in control of the entire API, you can do something like
...
backgroundFilters: {
category: [
new SearchSpring.Catalog.Category("Shirt"),
new SearchSpring.Catalog.Category("Shoes"),
new SearchSpring.Catalog.Category("MyNewCategory", "ANewValue")
],
department: 'Mens'
}
...

Categories