annotation-poller
Advanced tools
Comparing version 1.2.1 to 2.0.0
@@ -5,2 +5,17 @@ # Change Log | ||
<a name="2.0.0"></a> | ||
# [2.0.0](https://github.com/npm/annotation-poller/compare/v1.2.1...v2.0.0) (2016-04-20) | ||
### Features | ||
* implement new DSL for annotations ([#5](https://github.com/npm/annotation-poller/issues/5)) ([7e4b723](https://github.com/npm/annotation-poller/commit/7e4b723)) | ||
### BREAKING CHANGES | ||
* DSL for annotations is now different | ||
<a name="1.2.1"></a> | ||
@@ -7,0 +22,0 @@ ## [1.2.1](https://github.com/npm/annotation-poller/compare/v1.2.0...v1.2.1) (2016-04-19) |
88
index.js
var $ = require('jquery') | ||
var Mustache = require('mustache') | ||
var Handlebars = require('handlebars') | ||
function AnnotationPoller (opts) { | ||
this._installExtensions() | ||
this.pollInterval = opts.pollInterval || 3000 | ||
@@ -9,6 +11,48 @@ this.pkg = opts.pkg // what package should we load annotations for? | ||
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) { | ||
@@ -57,7 +101,7 @@ var _this = this | ||
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) { | ||
@@ -75,4 +119,40 @@ // don't render the element unless its fingerprint has changed. | ||
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) | ||
} |
{ | ||
"name": "annotation-poller", | ||
"version": "1.2.1", | ||
"version": "2.0.0", | ||
"description": "poll for annotations from external services, place them on packages", | ||
@@ -40,5 +40,5 @@ "main": "index.js", | ||
"dependencies": { | ||
"jquery": "^2.2.2", | ||
"mustache": "^2.2.1" | ||
"handlebars": "^4.0.5", | ||
"jquery": "^2.2.2" | ||
} | ||
} |
148
test.js
@@ -27,8 +27,15 @@ /* global describe, it, beforeEach */ | ||
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*!' | ||
}] | ||
}] | ||
@@ -51,8 +58,10 @@ }) | ||
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' | ||
} | ||
}] | ||
}] | ||
@@ -63,4 +72,4 @@ }) | ||
poller.start(function () { | ||
$('ul li').length.should.equal(1) | ||
$('ul li').text().should.match(/my awesome integration/) | ||
$('.addon-container').length.should.equal(1) | ||
$('.addon-container').text().should.match(/second integration/) | ||
poller.stop() | ||
@@ -71,2 +80,45 @@ 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 () { | ||
$('.addon-container:first').text().should.match(/link 1/) | ||
$('.addon-container:last').text().should.match(/link 2/) | ||
poller.stop() | ||
return done() | ||
}) | ||
}) | ||
it('replaces an existing annotation with new data from an integration', function (done) { | ||
@@ -77,8 +129,10 @@ $.mockjax({ | ||
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' | ||
} | ||
}] | ||
}] | ||
@@ -94,8 +148,10 @@ }) | ||
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' | ||
} | ||
}] | ||
}] | ||
@@ -105,4 +161,4 @@ }) | ||
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() | ||
@@ -119,8 +175,10 @@ return done() | ||
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' | ||
} | ||
}] | ||
}] | ||
@@ -135,9 +193,11 @@ }) | ||
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' | ||
} | ||
}] | ||
}] | ||
@@ -147,5 +207,5 @@ }) | ||
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() | ||
@@ -152,0 +212,0 @@ return done() |
13605
325
+ Addedhandlebars@^4.0.5
+ Addedhandlebars@4.7.8(transitive)
+ Addedminimist@1.2.8(transitive)
+ Addedneo-async@2.6.2(transitive)
+ Addedsource-map@0.6.1(transitive)
+ Addeduglify-js@3.17.4(transitive)
+ Addedwordwrap@1.0.0(transitive)
- Removedmustache@^2.2.1
- Removedmustache@2.3.2(transitive)