Skip to content
This repository has been archived by the owner on Aug 11, 2021. It is now read-only.

Commit

Permalink
feat: implement new DSL for annotations (#5)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: DSL for annotations is now different
  • Loading branch information
bcoe committed Apr 20, 2016
1 parent c5292cd commit 7e4b723
Show file tree
Hide file tree
Showing 3 changed files with 191 additions and 51 deletions.
88 changes: 84 additions & 4 deletions index.js
@@ -1,15 +1,59 @@
var $ = require('jquery')
var Mustache = require('mustache')
var Handlebars = require('handlebars')

function AnnotationPoller (opts) {
this._installExtensions()

this.pollInterval = opts.pollInterval || 3000
this.pkg = opts.pkg // what package should we load annotations for?
this.endpoint = '/api/v1/annotations/' + this.pkg
this.annotations = {}
this.template = '<li id="annotation-{{id}}" style="{{status}}" data-fingerprint={{fingerprint}}><span>{{description}}</span><a href="{{{external-link}}}">{{external-link-text}}</a></li>'
this.template = Handlebars.compile(
'<li id="annotation-{{id}}" style="{{status}}" data-fingerprint={{fingerprint}}>' +
'<ul class="addon-container">' +
' <li><h3>{{name}}</h3></li>' +
' {{#each rows}}' +
' <li>' +
' {{#hasKey this "image"}}' +
' <img src="{{{image.url}}}" alt="{{image.text}}" />' +
' {{/hasKey}}' +
' {{#hasKey this "link"}}' +
' {{#isArray this "link"}}' +
' {{#each link}}' +
' <a href="{{{url}}}">{{text}}</a>{{#unless @last}},{{/unless}}' +
' {{/each}}' +
' {{else}}' +
' <a href="{{{link.url}}}">{{link.text}}</a>' +
' {{/isArray}}' +
' {{/hasKey}}' +
' {{#hasKey this "text"}}' +
' <span>{{{text}}}</span>' +
' {{/hasKey}}' +
' </li>' +
' {{/each}}' +
'</ul>' +
'</li>')
this.addonSelector = '#npm-addon-box'
}

AnnotationPoller.prototype._installExtensions = function () {
Handlebars.registerHelper('hasKey', function (obj, key, options) {
if (typeof obj === 'object' && obj[key]) {
return options.fn(this)
} else {
return options.inverse(this)
}
})

Handlebars.registerHelper('isArray', function (obj, key, options) {
if ($.isArray(obj[key])) {
return options.fn(this)
} else {
return options.inverse(this)
}
})
}

AnnotationPoller.prototype.start = function (loaded) {
var _this = this
var updating = false
Expand Down Expand Up @@ -53,11 +97,11 @@ AnnotationPoller.prototype.renderAnnotations = function () {
var addonBox = $(this.addonSelector)

Object.keys(this.annotations).forEach(function (key) {
annotation = _this.annotations[key]
annotation = _this._applyReplacements(_this.annotations[key])
if (annotation._rendered) return

annotationElement = $('#annotation-' + annotation.id)
newAnnotationElement = Mustache.render(_this.template, annotation)
newAnnotationElement = _this.template(annotation)
if (annotationElement.length) {
// don't render the element unless its fingerprint has changed.
if (annotationElement.data('fingerprint') !== annotation.fingerprint) {
Expand All @@ -70,6 +114,42 @@ AnnotationPoller.prototype.renderAnnotations = function () {
})
}

AnnotationPoller.prototype._applyReplacements = function (obj) {
var _this = this
if ($.isArray(obj.rows)) {
obj.rows.forEach(function (row) {
// bold any text in between *foo*.
if (row.text) {
row.text = _this._escape(row.text)
row.text = row.text.replace(/\*(.+)\*/, '<b>$1</b>')
}

// escape any HTML in links.
if ($.isArray(row.link)) {
row.link.forEach(function (l) {
if (l.url) l.url = _this._escape(l.url)
})
} else if (row.link) {
if (row.link.url) row.link.url = _this._escape(row.link.url)
}

// escape any HTML in image links.
if (row.image) {
if (row.image.url) row.image.url = _this._escape(row.image.url)
}
})
} else {
// we shouldn't allow obj.rows
// to be a non-array value.
obj.rows = []
}
return obj
}

AnnotationPoller.prototype._escape = function (text) {
return $('<div>').text(text).html()
}

module.exports = function (opts) {
return new AnnotationPoller(opts)
}
6 changes: 3 additions & 3 deletions package.json
Expand Up @@ -37,7 +37,7 @@
"standard-version": "^2.1.2"
},
"dependencies": {
"jquery": "^2.2.2",
"mustache": "^2.2.1"
"handlebars": "^4.0.5",
"jquery": "^2.2.2"
}
}
}
148 changes: 104 additions & 44 deletions test.js
Expand Up @@ -24,12 +24,19 @@ describe('annotation-poller', function () {
url: endpoint,
responseText: [{
id: 'abc-123-abc',
status: 'warn',
'status-message': 'module not yet scanned',
description: 'foo security integration',
'external-link': 'http://example.com/foo-package/audit',
'external-link-text': 'start audit',
fingerprint: 'foo'
name: 'Awesome Integration',
fingerprint: 'a',
rows: [{
image: {
url: 'http://www.example.com/img',
text: 'image alt'
},
link: {
url: 'http://www.example.com',
text: 'my awesome link'
},
text: 'hello *world*!'
}]
}]
})

Expand All @@ -47,19 +54,64 @@ describe('annotation-poller', function () {
url: endpoint,
responseText: [{
id: 'abc-123-abc',
status: 'warn',
'status-message': 'module not yet scanned',
description: 'my awesome integration',
'external-link': 'http://example.com/foo-package/audit',
'external-link-text': 'start audit',
fingerprint: 'bar'
name: 'second integration',
fingerprint: 'b',
rows: [{
link: {
url: 'http://www.example.com',
text: 'my awesome link'
}
}]
}]
})

var poller = annotationPoller({pollInterval: 50, pkg: pkg})
poller.start(function () {
$('.addon-container').length.should.equal(1)
$('.addon-container').text().should.match(/second integration/)
poller.stop()
return done()
})
})

it('replaces *text* with bold', function (done) {
$.mockjax({
url: endpoint,
responseText: [{
id: 'abc-123-abc',
name: 'second integration',
fingerprint: 'c',
rows: [{
text: 'my *awesome* <b>message</b>'
}]
}]
})

var poller = annotationPoller({pollInterval: 50, pkg: pkg})
poller.start(function () {
$('b').text().should.equal('awesome')
poller.stop()
return done()
})
})

it('handles an array of links', function (done) {
$.mockjax({
url: endpoint,
responseText: [{
id: 'abc-123-abc',
name: 'second integration',
fingerprint: 'd',
rows: [{
link: [{url: 'http://example.com', text: 'link 1'}, {url: 'http://2.example.com', text: 'link 2'}]
}]
}]
})

var poller = annotationPoller({pollInterval: 50, pkg: pkg})
poller.start(function () {
$('ul li').length.should.equal(1)
$('ul li').text().should.match(/my awesome integration/)
$('.addon-container:first').text().should.match(/link 1/)
$('.addon-container:last').text().should.match(/link 2/)
poller.stop()
return done()
})
Expand All @@ -70,12 +122,14 @@ describe('annotation-poller', function () {
url: endpoint,
responseText: [{
id: 'abc-123-abc',
status: 'warn',
'status-message': 'module not yet scanned',
description: 'my awesome integration',
'external-link': 'http://example.com/foo-package/audit',
'external-link-text': 'start audit',
fingerprint: 'foo'
name: 'third integration',
fingerprint: 'foo',
rows: [{
link: {
url: 'http://www.example.com',
text: 'initial link'
}
}]
}]
})

Expand All @@ -86,18 +140,20 @@ describe('annotation-poller', function () {
url: endpoint,
responseText: [{
id: 'abc-123-abc',
status: 'green',
'status-message': 'module scanned',
description: 'my awesome integration',
'external-link': 'http://example.com/foo-package/audit',
'external-link-text': 'view details',
fingerprint: 'bar'
name: 'third integration',
fingerprint: 'bar',
rows: [{
link: {
url: 'http://www.example.com',
text: 'replaced link'
}
}]
}]
})

setTimeout(function () {
$('ul li').length.should.equal(1)
$('ul li').text().should.match(/view details/)
$('.addon-container').length.should.equal(1)
$('.addon-container').text().should.match(/replaced link/)
poller.stop()
return done()
}, 1000)
Expand All @@ -109,12 +165,14 @@ describe('annotation-poller', function () {
url: endpoint,
responseText: [{
id: 'abc-123-abc',
status: 'warn',
'status-message': 'module not yet scanned',
description: 'my awesome integration',
'external-link': 'http://example.com/foo-package/audit',
'external-link-text': 'start audit',
fingerprint: 'foo'
name: 'third integration',
fingerprint: 'foo',
rows: [{
link: {
url: 'http://www.example.com',
text: 'initial link 1'
}
}]
}]
})

Expand All @@ -124,20 +182,22 @@ describe('annotation-poller', function () {
$.mockjax({
url: endpoint,
responseText: [{
id: 'fed-234-abc',
status: 'green',
'status-message': 'module licensed',
description: 'my second integration',
'external-link': 'http://example.com/foo-package/audit',
'external-link-text': 'view details',
fingerprint: 'foo'
id: 'fed-123-abc',
name: 'third integration',
fingerprint: 'foo',
rows: [{
link: {
url: 'http://www.example.com',
text: 'initial link 2'
}
}]
}]
})

setTimeout(function () {
$('ul li').length.should.equal(2)
$('ul li:first').text().should.match(/my awesome integration/)
$('ul li:last').text().should.match(/my second integration/)
$('.addon-container').length.should.equal(2)
$('.addon-container:first').text().should.match(/initial link 1/)
$('.addon-container:last').text().should.match(/initial link 2/)
poller.stop()
return done()
}, 1000)
Expand Down

0 comments on commit 7e4b723

Please sign in to comment.