1
0
Fork 0
mirror of https://github.com/futurepress/epub.js.git synced 2025-10-04 15:09:16 +02:00

Added several usage examples, fixed bugs, turned off restore by default

This commit is contained in:
Fred Chasen 2013-07-15 22:14:52 -07:00
parent 0e08b734ec
commit 416f7ab6a0
112 changed files with 23903 additions and 3348 deletions

View file

@ -0,0 +1,178 @@
// Generated by CoffeeScript 1.6.3
describe('Annotator.Plugin.AnnotateItPermissions', function() {
var el, permissions;
el = null;
permissions = null;
beforeEach(function() {
el = $("<div class='annotator-viewer'></div>").appendTo('body')[0];
return permissions = new Annotator.Plugin.AnnotateItPermissions(el);
});
afterEach(function() {
return $(el).remove();
});
it("it should set user for newly created annotations on beforeAnnotationCreated", function() {
var ann;
ann = {};
permissions.setUser({
userId: 'alice',
consumerKey: 'fookey'
});
$(el).trigger('beforeAnnotationCreated', [ann]);
return assert.equal(ann.user, 'alice');
});
it("it should set consumer for newly created annotations on beforeAnnotationCreated", function() {
var ann;
ann = {};
permissions.setUser({
userId: 'alice',
consumerKey: 'fookey'
});
$(el).trigger('beforeAnnotationCreated', [ann]);
return assert.equal(ann.consumer, 'fookey');
});
return describe('authorize', function() {
var annotations;
annotations = null;
beforeEach(function() {
return annotations = [
{}, {
user: 'alice'
}, {
user: 'alice',
consumer: 'annotateit'
}, {
permissions: {}
}, {
permissions: {
'read': ['group:__world__']
}
}, {
permissions: {
'update': ['group:__authenticated__']
}
}, {
consumer: 'annotateit',
permissions: {
'read': ['group:__consumer__']
}
}
];
});
it('should NOT allow any action for an annotation with no owner info and no permissions', function() {
var a;
a = annotations[0];
assert.isFalse(permissions.authorize(null, a));
assert.isFalse(permissions.authorize('foo', a));
permissions.setUser({
userId: 'alice',
consumerKey: 'annotateit'
});
assert.isFalse(permissions.authorize(null, a));
return assert.isFalse(permissions.authorize('foo', a));
});
it('should NOT allow any action if an annotation has only user set (but no consumer)', function() {
var a;
a = annotations[1];
assert.isFalse(permissions.authorize(null, a));
assert.isFalse(permissions.authorize('foo', a));
permissions.setUser({
userId: 'alice',
consumerKey: 'annotateit'
});
assert.isFalse(permissions.authorize(null, a));
return assert.isFalse(permissions.authorize('foo', a));
});
it('should allow any action if the current auth info identifies the owner of the annotation', function() {
var a;
a = annotations[2];
permissions.setUser({
userId: 'alice',
consumerKey: 'annotateit'
});
assert.isTrue(permissions.authorize(null, a));
return assert.isTrue(permissions.authorize('foo', a));
});
it('should NOT allow any action for an annotation with no owner info and empty permissions field', function() {
var a;
a = annotations[3];
assert.isFalse(permissions.authorize(null, a));
assert.isFalse(permissions.authorize('foo', a));
permissions.setUser({
userId: 'alice',
consumerKey: 'annotateit'
});
assert.isFalse(permissions.authorize(null, a));
return assert.isFalse(permissions.authorize('foo', a));
});
it('should allow an action when the action field contains the world group', function() {
var a;
a = annotations[4];
assert.isTrue(permissions.authorize('read', a));
permissions.setUser({
userId: 'alice',
consumerKey: 'annotateit'
});
return assert.isTrue(permissions.authorize('read', a));
});
it('should allow an action when the action field contains the authenticated group and the plugin has auth info', function() {
var a;
a = annotations[5];
assert.isFalse(permissions.authorize('update', a));
permissions.setUser({
userId: 'anyone',
consumerKey: 'anywhere'
});
return assert.isTrue(permissions.authorize('update', a));
});
it('should allow an action when the action field contains the consumer group and the plugin has auth info with a matching consumer', function() {
var a;
a = annotations[6];
assert.isFalse(permissions.authorize('read', a));
permissions.setUser({
userId: 'anyone',
consumerKey: 'anywhere'
});
assert.isFalse(permissions.authorize('read', a));
permissions.setUser({
userId: 'anyone',
consumerKey: 'annotateit'
});
return assert.isTrue(permissions.authorize('read', a));
});
it('should allow an action when the action field contains the consumer group and the plugin has auth info with a matching consumer', function() {
var a;
a = annotations[6];
assert.isFalse(permissions.authorize('read', a));
permissions.setUser({
userId: 'anyone',
consumerKey: 'anywhere'
});
assert.isFalse(permissions.authorize('read', a));
permissions.setUser({
userId: 'anyone',
consumerKey: 'annotateit'
});
return assert.isTrue(permissions.authorize('read', a));
});
return it('should allow an action when the user is an admin of the annotation\'s consumer', function() {
var a;
a = annotations[2];
permissions.setUser({
userId: 'anyone',
consumerKey: 'anywhere',
admin: true
});
assert.isFalse(permissions.authorize('read', a));
permissions.setUser({
userId: 'anyone',
consumerKey: 'annotateit',
admin: true
});
return assert.isTrue(permissions.authorize('read', a));
});
});
});
/*
//@ sourceMappingURL=annotateitpermissions_spec.map
*/

View file

@ -0,0 +1,10 @@
{
"version": 3,
"file": "annotateitpermissions_spec.js",
"sourceRoot": "../../..",
"sources": [
"test/spec/plugin/annotateitpermissions_spec.coffee"
],
"names": [],
"mappings": "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"
}

View file

@ -0,0 +1,131 @@
// Generated by CoffeeScript 1.6.3
var B64, base64Encode, base64UrlEncode, makeToken;
Date.prototype.toISO8601String = DateToISO8601String;
B64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
base64Encode = function(data) {
var ac, bits, enc, h1, h2, h3, h4, i, o1, o2, o3, r, tmp_arr;
if (typeof btoa !== "undefined" && btoa !== null) {
return btoa(data);
} else {
i = 0;
ac = 0;
enc = "";
tmp_arr = [];
if (!data) {
return data;
}
data += '';
while (i < data.length) {
o1 = data.charCodeAt(i++);
o2 = data.charCodeAt(i++);
o3 = data.charCodeAt(i++);
bits = o1 << 16 | o2 << 8 | o3;
h1 = bits >> 18 & 0x3f;
h2 = bits >> 12 & 0x3f;
h3 = bits >> 6 & 0x3f;
h4 = bits & 0x3f;
tmp_arr[ac++] = B64.charAt(h1) + B64.charAt(h2) + B64.charAt(h3) + B64.charAt(h4);
}
enc = tmp_arr.join('');
r = data.length % 3;
return (r ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3);
}
};
base64UrlEncode = function(data) {
var chop;
data = base64Encode(data);
chop = data.indexOf('=');
if (chop !== -1) {
data = data.slice(0, chop);
}
data = data.replace(/\+/g, '-');
data = data.replace(/\//g, '_');
return data;
};
makeToken = function() {
var rawToken;
rawToken = {
consumerKey: "key",
issuedAt: new Date().toISO8601String(),
ttl: 300,
userId: "testUser"
};
return {
rawToken: rawToken,
encodedToken: 'header.' + base64UrlEncode(JSON.stringify(rawToken)) + '.signature'
};
};
describe('Annotator.Plugin.Auth', function() {
var encodedToken, mock, mockAuth, rawToken;
mock = null;
rawToken = null;
encodedToken = null;
mockAuth = function(options) {
var a, el;
el = $('<div></div>')[0];
a = new Annotator.Plugin.Auth(el, options);
return {
elem: el,
auth: a
};
};
beforeEach(function() {
var _ref;
_ref = makeToken(), rawToken = _ref.rawToken, encodedToken = _ref.encodedToken;
return mock = mockAuth({
token: encodedToken,
autoFetch: false
});
});
it("uses token supplied in options by default", function() {
return assert.equal(mock.auth.token, encodedToken);
});
xit("makes an ajax request to tokenUrl to retrieve token otherwise");
it("sets annotator:headers data on its element with token data", function() {
var data;
data = $(mock.elem).data('annotator:headers');
assert.isNotNull(data);
return assert.equal(data['x-annotator-auth-token'], encodedToken);
});
it("should call callbacks given to #withToken immediately if it has a valid token", function() {
var callback;
callback = sinon.spy();
mock.auth.withToken(callback);
return assert.isTrue(callback.calledWith(rawToken));
});
xit("should call callbacks given to #withToken after retrieving a token");
return describe("#haveValidToken", function() {
it("returns true when the current token is valid", function() {
return assert.isTrue(mock.auth.haveValidToken());
});
it("returns false when the current token is missing a consumerKey", function() {
delete mock.auth._unsafeToken.consumerKey;
return assert.isFalse(mock.auth.haveValidToken());
});
it("returns false when the current token is missing an issuedAt", function() {
delete mock.auth._unsafeToken.issuedAt;
return assert.isFalse(mock.auth.haveValidToken());
});
it("returns false when the current token is missing a ttl", function() {
delete mock.auth._unsafeToken.ttl;
return assert.isFalse(mock.auth.haveValidToken());
});
return it("returns false when the current token expires in the past", function() {
mock.auth._unsafeToken.ttl = 0;
assert.isFalse(mock.auth.haveValidToken());
mock.auth._unsafeToken.ttl = 86400;
mock.auth._unsafeToken.issuedAt = "1970-01-01T00:00";
return assert.isFalse(mock.auth.haveValidToken());
});
});
});
/*
//@ sourceMappingURL=auth_spec.map
*/

View file

@ -0,0 +1,10 @@
{
"version": 3,
"file": "auth_spec.js",
"sourceRoot": "../../..",
"sources": [
"test/spec/plugin/auth_spec.coffee"
],
"names": [],
"mappings": "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"
}

View file

@ -0,0 +1,110 @@
// Generated by CoffeeScript 1.6.3
describe('Annotator.Plugin.Document', function() {
var $fix, annotator;
$fix = null;
annotator = null;
beforeEach(function() {
annotator = new Annotator($('<div></div>')[0], {});
return annotator.addPlugin('Document');
});
afterEach(function() {
return $(document).unbind();
});
describe('has an annotator', function() {
return it('should have an annotator', function() {
return assert.ok(annotator);
});
});
describe('has the plugin', function() {
return it('should have Document plugin', function() {
return assert.ok('Document' in annotator.plugins);
});
});
return describe('annotation should have some metadata', function() {
var annotation, head;
head = $("head");
head.append('<link rel="alternate" href="foo.pdf" type="application/pdf"></link>');
head.append('<link rel="alternate" href="foo.doc" type="application/msword"></link>');
head.append('<link rel="bookmark" href="http://example.com/bookmark"></link>');
head.append('<meta name="citation_doi" content="10.1175/JCLI-D-11-00015.1">');
head.append('<meta name="citation_title" content="Foo">');
head.append('<meta name="citation_pdf_url" content="foo.pdf">');
head.append('<meta name="dc.identifier" content="doi:10.1175/JCLI-D-11-00015.1">');
head.append('<meta name="dc.identifier" content="isbn:123456789">');
head.append('<meta name="DC.type" content="Article">');
head.append('<meta property="og:url" content="http://example.com">');
head.append('<meta name="twitter:site" content="@okfn">');
head.append('<link rel="icon" href="http://example.com/images/icon.ico"></link>');
head.append('<meta name="eprints.title" content="Computer Lib / Dream Machines">');
head.append('<meta name="prism.title" content="Literary Machines">');
annotation = null;
beforeEach(function() {
return annotation = annotator.createAnnotation();
});
it('can create annotation', function() {
return assert.ok(annotation);
});
it('should have a document', function() {
return assert.ok(annotation.document);
});
it('should have a title, derived from highwire metadata if possible', function() {
return assert.equal(annotation.document.title, 'Foo');
});
it('should have links with absoulte hrefs and types', function() {
assert.ok(annotation.document.link);
assert.equal(annotation.document.link.length, 7);
assert.match(annotation.document.link[0].href, /^.+runner.html(\?.*)?$/);
assert.equal(annotation.document.link[1].rel, "alternate");
assert.match(annotation.document.link[1].href, /^.+foo\.pdf$/);
assert.equal(annotation.document.link[1].type, "application/pdf");
assert.equal(annotation.document.link[2].rel, "alternate");
assert.match(annotation.document.link[2].href, /^.+foo\.doc$/);
assert.equal(annotation.document.link[2].type, "application/msword");
assert.equal(annotation.document.link[3].rel, "bookmark");
assert.equal(annotation.document.link[3].href, "http://example.com/bookmark");
assert.equal(annotation.document.link[4].href, "doi:10.1175/JCLI-D-11-00015.1");
assert.match(annotation.document.link[5].href, /.+foo\.pdf$/);
assert.equal(annotation.document.link[5].type, "application/pdf");
return assert.equal(annotation.document.link[6].href, "doi:10.1175/JCLI-D-11-00015.1");
});
it('should have highwire metadata', function() {
assert.ok(annotation.document.highwire);
assert.deepEqual(annotation.document.highwire.pdf_url, ['foo.pdf']);
assert.deepEqual(annotation.document.highwire.doi, ['10.1175/JCLI-D-11-00015.1']);
return assert.deepEqual(annotation.document.highwire.title, ['Foo']);
});
it('should have dublincore metadata', function() {
assert.ok(annotation.document.dc);
assert.deepEqual(annotation.document.dc.identifier, ["doi:10.1175/JCLI-D-11-00015.1", "isbn:123456789"]);
return assert.deepEqual(annotation.document.dc.type, ["Article"]);
});
it('should have facebook metadata', function() {
assert.ok(annotation.document.facebook);
return assert.deepEqual(annotation.document.facebook.url, ["http://example.com"]);
});
it('should have eprints metadata', function() {
assert.ok(annotation.document.eprints);
return assert.deepEqual(annotation.document.eprints.title, ['Computer Lib / Dream Machines']);
});
it('should have prism metadata', function() {
assert.ok(annotation.document.prism);
return assert.deepEqual(annotation.document.prism.title, ['Literary Machines']);
});
it('should have twitter card metadata', function() {
assert.ok(annotation.document.twitter);
return assert.deepEqual(annotation.document.twitter.site, ['@okfn']);
});
it('should have unique uris', function() {
var uris;
uris = annotator.plugins.Document.uris();
return assert.equal(uris.length, 5);
});
return it('should have a favicon', function() {
return assert.equal(annotation.document.favicon, 'http://example.com/images/icon.ico');
});
});
});
/*
//@ sourceMappingURL=document_spec.map
*/

View file

@ -0,0 +1,10 @@
{
"version": 3,
"file": "document_spec.js",
"sourceRoot": "../../..",
"sources": [
"test/spec/plugin/document_spec.coffee"
],
"names": [],
"mappings": "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"
}

View file

@ -0,0 +1,468 @@
// Generated by CoffeeScript 1.6.3
describe("Filter", function() {
var element, plugin;
plugin = null;
element = null;
beforeEach(function() {
var annotator;
element = $('<div />');
annotator = {
subscribe: sinon.spy(),
element: {
find: sinon.stub().returns($())
}
};
plugin = new Annotator.Plugin.Filter(element[0]);
return plugin.annotator = annotator;
});
afterEach(function() {
return plugin.element.remove();
});
describe("events", function() {
var filterElement;
filterElement = null;
beforeEach(function() {
filterElement = $(plugin.html.filter);
return plugin.element.append(filterElement);
});
afterEach(function() {
return filterElement.remove();
});
it("should call Filter#_onFilterFocus when a filter input is focussed", function() {
sinon.spy(plugin, '_onFilterFocus');
filterElement.find('input').focus();
return assert(plugin._onFilterFocus.calledOnce);
});
it("should call Filter#_onFilterBlur when a filter input is blurred", function() {
sinon.spy(plugin, '_onFilterBlur');
filterElement.find('input').blur();
return assert(plugin._onFilterBlur.calledOnce);
});
return it("should call Filter#_onFilterKeyup when a key is pressed in an input", function() {
sinon.spy(plugin, '_onFilterKeyup');
filterElement.find('input').keyup();
return assert(plugin._onFilterKeyup.calledOnce);
});
});
describe("constructor", function() {
it("should have an empty filters array", function() {
return assert.deepEqual(plugin.filters, []);
});
it("should have an filter element wrapped in jQuery", function() {
assert.isTrue(plugin.filter instanceof jQuery);
return assert.lengthOf(plugin.filter, 1);
});
return it("should append the toolbar to the @options.appendTo selector", function() {
var parent;
assert.isTrue(plugin.element instanceof jQuery);
assert.lengthOf(plugin.element, 1);
parent = $(plugin.options.appendTo);
return assert.equal(plugin.element.parent()[0], parent[0]);
});
});
describe("pluginInit", function() {
beforeEach(function() {
sinon.stub(plugin, 'updateHighlights');
sinon.stub(plugin, '_setupListeners').returns(plugin);
sinon.stub(plugin, '_insertSpacer').returns(plugin);
return sinon.stub(plugin, 'addFilter');
});
it("should call Filter#updateHighlights()", function() {
plugin.pluginInit();
return assert(plugin.updateHighlights.calledOnce);
});
it("should call Filter#_setupListeners()", function() {
plugin.pluginInit();
return assert(plugin._setupListeners.calledOnce);
});
it("should call Filter#_insertSpacer()", function() {
plugin.pluginInit();
return assert(plugin._insertSpacer.calledOnce);
});
return it("should load any filters in the Filter#options.filters array", function() {
var filter, filters, _i, _len, _results;
filters = [
{
label: 'filter1'
}, {
label: 'filter2'
}, {
label: 'filter3'
}
];
plugin.options.filters = filters;
plugin.pluginInit();
_results = [];
for (_i = 0, _len = filters.length; _i < _len; _i++) {
filter = filters[_i];
_results.push(assert.isTrue(plugin.addFilter.calledWith(filter)));
}
return _results;
});
});
describe("_setupListeners", function() {
return it("should subscribe to all relevant events on the annotator", function() {
var event, events, _i, _len, _results;
plugin._setupListeners();
events = ['annotationsLoaded', 'annotationCreated', 'annotationUpdated', 'annotationDeleted'];
_results = [];
for (_i = 0, _len = events.length; _i < _len; _i++) {
event = events[_i];
_results.push(assert.isTrue(plugin.annotator.subscribe.calledWith(event, plugin.updateHighlights)));
}
return _results;
});
});
describe("addFilter", function() {
var filter;
filter = null;
beforeEach(function() {
filter = {
label: 'Tag',
property: 'tags'
};
return plugin.addFilter(filter);
});
it("should add a filter object to Filter#plugins", function() {
return assert.ok(plugin.filters[0]);
});
it("should append the html to Filter#toolbar", function() {
filter = plugin.filters[0];
return assert.equal(filter.element[0], plugin.element.find('#annotator-filter-tags').parent()[0]);
});
it("should store the filter in the elements data store under 'filter'", function() {
filter = plugin.filters[0];
return assert.equal(filter.element.data('filter'), filter);
});
return it("should not add a filter for a property that has already been loaded", function() {
plugin.addFilter({
label: 'Tag',
property: 'tags'
});
return assert.lengthOf(plugin.filters, 1);
});
});
describe("updateFilter", function() {
var annotations, filter;
filter = null;
annotations = null;
beforeEach(function() {
filter = {
id: 'text',
label: 'Annotation',
property: 'text',
element: $('<span><input value="ca" /></span>'),
annotations: [],
isFiltered: function(value, text) {
return text.indexOf('ca') !== -1;
}
};
annotations = [
{
text: 'cat'
}, {
text: 'dog'
}, {
text: 'car'
}
];
plugin.filters = {
'text': filter
};
plugin.highlights = {
map: function() {
return annotations;
}
};
sinon.stub(plugin, 'updateHighlights');
sinon.stub(plugin, 'resetHighlights');
return sinon.stub(plugin, 'filterHighlights');
});
it("should call Filter#updateHighlights()", function() {
plugin.updateFilter(filter);
return assert(plugin.updateHighlights.calledOnce);
});
it("should call Filter#resetHighlights()", function() {
plugin.updateFilter(filter);
return assert(plugin.resetHighlights.calledOnce);
});
it("should filter the cat and car annotations", function() {
plugin.updateFilter(filter);
return assert.deepEqual(filter.annotations, [annotations[0], annotations[2]]);
});
it("should call Filter#filterHighlights()", function() {
plugin.updateFilter(filter);
return assert(plugin.filterHighlights.calledOnce);
});
return it("should NOT call Filter#filterHighlights() if there is no input", function() {
filter.element.find('input').val('');
plugin.updateFilter(filter);
return assert.isFalse(plugin.filterHighlights.called);
});
});
describe("updateHighlights", function() {
beforeEach(function() {
plugin.highlights = null;
return plugin.updateHighlights();
});
it("should fetch the visible highlights from the Annotator#element", function() {
return assert.isTrue(plugin.annotator.element.find.calledWith('.annotator-hl:visible'));
});
return it("should set the Filter#highlights property", function() {
return assert.ok(plugin.highlights);
});
});
describe("filterHighlights", function() {
var div;
div = null;
beforeEach(function() {
var match;
plugin.highlights = $('<span /><span /><span /><span /><span />');
match = {
highlights: [plugin.highlights[1]]
};
plugin.filters = [
{
annotations: [
{
highlights: [plugin.highlights[0]]
}, match
]
}, {
annotations: [
{
highlights: [plugin.highlights[4]]
}, match, {
highlights: [plugin.highlights[2]]
}
]
}
];
return div = $('<div>').append(plugin.highlights);
});
it("should hide all highlights not whitelisted by _every_ filter", function() {
plugin.filterHighlights();
return assert.lengthOf(div.find('.' + plugin.classes.hl.hide), 4);
});
it("should hide all highlights not whitelisted by _every_ filter if every filter is active", function() {
plugin.filters[1].annotations = [];
plugin.filterHighlights();
return assert.lengthOf(div.find('.' + plugin.classes.hl.hide), 3);
});
return it("should hide all highlights not whitelisted if only one filter", function() {
plugin.filters = plugin.filters.slice(0, 1);
plugin.filterHighlights();
return assert.lengthOf(div.find('.' + plugin.classes.hl.hide), 3);
});
});
describe("resetHighlights", function() {
return it("should remove the filter-hide class from all highlights", function() {
plugin.highlights = $('<span /><span /><span />').addClass(plugin.classes.hl.hide);
plugin.resetHighlights();
return assert.lengthOf(plugin.highlights.filter('.' + plugin.classes.hl.hide), 0);
});
});
return describe("group: filter input actions", function() {
var filterElement;
filterElement = null;
beforeEach(function() {
filterElement = $(plugin.html.filter);
return plugin.element.append(filterElement);
});
describe("_onFilterFocus", function() {
return it("should add an active class to the element", function() {
plugin._onFilterFocus({
target: filterElement.find('input')[0]
});
return assert.isTrue(filterElement.hasClass(plugin.classes.active));
});
});
describe("_onFilterBlur", function() {
it("should remove the active class from the element", function() {
filterElement.addClass(plugin.classes.active);
plugin._onFilterBlur({
target: filterElement.find('input')[0]
});
return assert.isFalse(filterElement.hasClass(plugin.classes.active));
});
return it("should NOT remove the active class from the element if it has a value", function() {
filterElement.addClass(plugin.classes.active);
plugin._onFilterBlur({
target: filterElement.find('input').val('filtered')[0]
});
return assert.isTrue(filterElement.hasClass(plugin.classes.active));
});
});
describe("_onFilterKeyup", function() {
beforeEach(function() {
plugin.filters = [
{
label: 'My Filter'
}
];
return sinon.stub(plugin, 'updateFilter');
});
it("should call Filter#updateFilter() with the relevant filter", function() {
filterElement.data('filter', plugin.filters[0]);
plugin._onFilterKeyup({
target: filterElement.find('input')[0]
});
return assert.isTrue(plugin.updateFilter.calledWith(plugin.filters[0]));
});
return it("should NOT call Filter#updateFilter() if no filter is found", function() {
plugin._onFilterKeyup({
target: filterElement.find('input')[0]
});
return assert.isFalse(plugin.updateFilter.called);
});
});
describe("navigation", function() {
var annotation1, annotation2, annotation3, element1, element2, element3;
element1 = null;
element2 = null;
element3 = null;
annotation1 = null;
annotation2 = null;
annotation3 = null;
beforeEach(function() {
element1 = $('<span />');
annotation1 = {
text: 'annotation1',
highlights: [element1[0]]
};
element1.data('annotation', annotation1);
element2 = $('<span />');
annotation2 = {
text: 'annotation2',
highlights: [element2[0]]
};
element2.data('annotation', annotation2);
element3 = $('<span />');
annotation3 = {
text: 'annotation3',
highlights: [element3[0]]
};
element3.data('annotation', annotation3);
plugin.highlights = $([element1[0], element2[0], element3[0]]);
return sinon.spy(plugin, '_scrollToHighlight');
});
describe("_onNextClick", function() {
it("should advance to the next element", function() {
element2.addClass(plugin.classes.hl.active);
plugin._onNextClick();
return assert.isTrue(plugin._scrollToHighlight.calledWith([element3[0]]));
});
it("should loop back to the start once it gets to the end", function() {
element3.addClass(plugin.classes.hl.active);
plugin._onNextClick();
return assert.isTrue(plugin._scrollToHighlight.calledWith([element1[0]]));
});
it("should use the first element if there is no current element", function() {
plugin._onNextClick();
return assert.isTrue(plugin._scrollToHighlight.calledWith([element1[0]]));
});
it("should only navigate through non hidden elements", function() {
element1.addClass(plugin.classes.hl.active);
element2.addClass(plugin.classes.hl.hide);
plugin._onNextClick();
return assert.isTrue(plugin._scrollToHighlight.calledWith([element3[0]]));
});
return it("should do nothing if there are no annotations", function() {
plugin.highlights = $();
plugin._onNextClick();
return assert.isFalse(plugin._scrollToHighlight.called);
});
});
return describe("_onPreviousClick", function() {
it("should advance to the previous element", function() {
element3.addClass(plugin.classes.hl.active);
plugin._onPreviousClick();
return assert.isTrue(plugin._scrollToHighlight.calledWith([element2[0]]));
});
it("should loop to the end once it gets to the beginning", function() {
element1.addClass(plugin.classes.hl.active);
plugin._onPreviousClick();
return assert.isTrue(plugin._scrollToHighlight.calledWith([element3[0]]));
});
it("should use the last element if there is no current element", function() {
plugin._onPreviousClick();
return assert.isTrue(plugin._scrollToHighlight.calledWith([element3[0]]));
});
it("should only navigate through non hidden elements", function() {
element3.addClass(plugin.classes.hl.active);
element2.addClass(plugin.classes.hl.hide);
plugin._onPreviousClick();
return assert.isTrue(plugin._scrollToHighlight.calledWith([element1[0]]));
});
return it("should do nothing if there are no annotations", function() {
plugin.highlights = $();
plugin._onPreviousClick();
return assert.isFalse(plugin._scrollToHighlight.called);
});
});
});
describe("_scrollToHighlight", function() {
var mockjQuery;
mockjQuery = null;
beforeEach(function() {
plugin.highlights = $();
mockjQuery = {
addClass: sinon.spy(),
animate: sinon.spy(),
offset: sinon.stub().returns({
top: 0
})
};
sinon.spy(plugin.highlights, 'removeClass');
return sinon.stub(jQuery.prototype, 'init').returns(mockjQuery);
});
afterEach(function() {
return jQuery.prototype.init.restore();
});
it("should remove active class from currently active element", function() {
plugin._scrollToHighlight({});
return assert.isTrue(plugin.highlights.removeClass.calledWith(plugin.classes.hl.active));
});
it("should add active class to provided elements", function() {
plugin._scrollToHighlight({});
return assert.isTrue(mockjQuery.addClass.calledWith(plugin.classes.hl.active));
});
return it("should animate the scrollbar to the highlight offset", function() {
plugin._scrollToHighlight({});
assert(mockjQuery.offset.calledOnce);
return assert(mockjQuery.animate.calledOnce);
});
});
return describe("_onClearClick", function() {
var mockjQuery;
mockjQuery = null;
beforeEach(function() {
mockjQuery = {};
mockjQuery.val = sinon.stub().returns(mockjQuery);
mockjQuery.prev = sinon.stub().returns(mockjQuery);
mockjQuery.keyup = sinon.stub().returns(mockjQuery);
mockjQuery.blur = sinon.stub().returns(mockjQuery);
sinon.stub(jQuery.prototype, 'init').returns(mockjQuery);
return plugin._onClearClick({
target: {}
});
});
afterEach(function() {
return jQuery.prototype.init.restore();
});
it("should clear the input", function() {
return assert.isTrue(mockjQuery.val.calledWith(''));
});
it("should trigger the blur event", function() {
return assert(mockjQuery.blur.calledOnce);
});
return it("should trigger the keyup event", function() {
return assert(mockjQuery.keyup.calledOnce);
});
});
});
});
/*
//@ sourceMappingURL=filter_spec.map
*/

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,150 @@
// Generated by CoffeeScript 1.6.3
var MockPlugin,
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
MockPlugin = (function() {
function MockPlugin() {}
MockPlugin.prototype.pluginInit = function() {};
return MockPlugin;
})();
describe('Annotator::setupPlugins', function() {
var $fix, annotator;
annotator = null;
$fix = null;
beforeEach(function() {
var p, _i, _len, _ref;
_ref = ['AnnotateItPermissions', 'Auth', 'Markdown', 'Store', 'Tags', 'Unsupported'];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
p = _ref[_i];
Annotator.Plugin[p] = MockPlugin;
}
addFixture('kitchensink');
return $fix = $(fix());
});
afterEach(function() {
return clearFixtures();
});
it('should added to the Annotator prototype', function() {
return assert.equal(typeof Annotator.prototype.setupPlugins, 'function');
});
it('should be callable via jQuery.fn.Annotator', function() {
sinon.spy(Annotator.prototype, 'setupPlugins');
$fix.annotator().annotator('setupPlugins', {}, {
Filter: {
appendTo: fix()
}
});
return assert(Annotator.prototype.setupPlugins.calledOnce);
});
describe('called with no parameters', function() {
var _Showdown;
_Showdown = null;
beforeEach(function() {
_Showdown = window.Showdown;
annotator = new Annotator(fix());
return annotator.setupPlugins({}, {
Filter: {
appendTo: fix()
}
});
});
afterEach(function() {
return window.Showdown = _Showdown;
});
describe('it includes the Unsupported plugin', function() {
return it('should add the Unsupported plugin by default', function() {
return assert.isDefined(annotator.plugins.Unsupported);
});
});
describe('it includes the Tags plugin', function() {
return it('should add the Tags plugin by default', function() {
return assert.isDefined(annotator.plugins.Tags);
});
});
describe('it includes the Filter plugin', function() {
var filterPlugin;
filterPlugin = null;
beforeEach(function() {
return filterPlugin = annotator.plugins.Filter;
});
it('should add the Filter plugin by default', function() {
return assert.isDefined(filterPlugin);
});
return it('should have filters for annotations, tags and users', function() {
var expectedFilters, f, filter, _i, _len, _results;
expectedFilters = ['text', 'user', 'tags'];
_results = [];
for (_i = 0, _len = expectedFilters.length; _i < _len; _i++) {
filter = expectedFilters[_i];
_results.push(assert.isTrue(__indexOf.call((function() {
var _j, _len1, _ref, _results1;
_ref = filterPlugin.filters;
_results1 = [];
for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
f = _ref[_j];
_results1.push(f.property);
}
return _results1;
})(), filter) >= 0));
}
return _results;
});
});
return describe('and with Showdown loaded in the page', function() {
return it('should add the Markdown plugin', function() {
return assert.isDefined(annotator.plugins.Markdown);
});
});
});
describe('called with AnnotateIt config', function() {
beforeEach(function() {
sinon.stub(Annotator.Plugin.Store.prototype, 'pluginInit');
annotator = new Annotator(fix());
return annotator.setupPlugins();
});
afterEach(function() {
return Annotator.Plugin.Store.prototype.pluginInit.restore();
});
it('should add the Store plugin', function() {
return assert.isDefined(annotator.plugins.Store);
});
it('should add the AnnotateItPermissions plugin', function() {
return assert.isDefined(annotator.plugins.AnnotateItPermissions);
});
return it('should add the Auth plugin', function() {
return assert.isDefined(annotator.plugins.Auth);
});
});
return describe('called with plugin options', function() {
beforeEach(function() {
return annotator = new Annotator(fix());
});
it('should override default plugin options', function() {
annotator.setupPlugins(null, {
AnnotateItPermissions: false,
Filter: {
filters: null,
addAnnotationFilter: false,
appendTo: fix()
}
});
return assert.lengthOf(annotator.plugins.Filter.filters, 0);
});
return it('should NOT load a plugin if its key is set to null OR false', function() {
annotator.setupPlugins(null, {
Filter: false,
Tags: null
});
assert.isUndefined(annotator.plugins.Tags);
return assert.isUndefined(annotator.plugins.Filter);
});
});
});
/*
//@ sourceMappingURL=kitchensink_spec.map
*/

View file

@ -0,0 +1,10 @@
{
"version": 3,
"file": "kitchensink_spec.js",
"sourceRoot": "../../..",
"sources": [
"test/spec/plugin/kitchensink_spec.coffee"
],
"names": [],
"mappings": "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"
}

View file

@ -0,0 +1,72 @@
// Generated by CoffeeScript 1.6.3
describe('Annotator.Plugin.Markdown', function() {
var input, output, plugin;
input = 'Is **this** [Markdown](http://daringfireball.com)?';
output = '<p>Is <strong>this</strong> <a href="http://daringfireball.com">Markdown</a>?</p>';
plugin = null;
beforeEach(function() {
return plugin = new Annotator.Plugin.Markdown($('<div />')[0]);
});
describe("events", function() {
return it("should call Markdown#updateTextField() when annotationViewerTextField event is fired", function() {
var annotation, field;
field = $('<div />')[0];
annotation = {
text: 'test'
};
sinon.spy(plugin, 'updateTextField');
plugin.publish('annotationViewerTextField', [field, annotation]);
return assert.isTrue(plugin.updateTextField.calledWith(field, annotation));
});
});
describe("constructor", function() {
it("should create a new instance of Showdown", function() {
return assert.ok(plugin.converter);
});
return it("should log an error if Showdown is not loaded", function() {
var converter;
sinon.stub(console, 'error');
converter = Showdown.converter;
Showdown.converter = null;
plugin = new Annotator.Plugin.Markdown($('<div />')[0]);
assert(console.error.calledOnce);
Showdown.converter = converter;
return console.error.restore();
});
});
describe("updateTextField", function() {
var annotation, field;
field = null;
annotation = null;
beforeEach(function() {
field = $('<div />')[0];
annotation = {
text: input
};
sinon.stub(plugin, 'convert').returns(output);
sinon.stub(Annotator.Util, 'escape').returns(input);
return plugin.updateTextField(field, annotation);
});
afterEach(function() {
return Annotator.Util.escape.restore();
});
it('should process the annotation text as Markdown', function() {
return assert.isTrue(plugin.convert.calledWith(input));
});
it('should update the content in the field', function() {
return assert.equal($(field).html(), output);
});
return it("should escape any existing HTML to prevent XSS", function() {
return assert.isTrue(Annotator.Util.escape.calledWith(input));
});
});
return describe("convert", function() {
return it("should convert the provided text into markdown", function() {
return assert.equal(plugin.convert(input), output);
});
});
});
/*
//@ sourceMappingURL=markdown_spec.map
*/

View file

@ -0,0 +1,10 @@
{
"version": 3,
"file": "markdown_spec.js",
"sourceRoot": "../../..",
"sources": [
"test/spec/plugin/markdown_spec.coffee"
],
"names": [],
"mappings": ";AAAA,CAAS,CAA6B,CAAA,KAAtC,CAAsC,kBAAtC;CACE,KAAA,eAAA;CAAA,CAAA,CAAS,EAAT,+CAAA;CAAA,CACA,CAAS,GAAT,6EADA;CAAA,CAEA,CAAS,CAFT,EAEA;CAFA,CAKA,CAAW,MAAA,CAAX;CACyB,EAAV,CAAA,EAAb,EAAa,CAAS,EAAtB;CADF,EAAW;CALX,CAQA,CAAmB,KAAnB,CAAmB;CACd,CAAH,CAA2F,MAAA,EAA3F,2EAAA;CACE,SAAA,OAAA;CAAA,EAAQ,EAAR,CAAA,GAAQ;CAAR,EACa,GAAb,IAAA;CAAa,CAAO,EAAN,EAAD,EAAC;CADd,OAAA;CAAA,CAGkB,CAAlB,EAAK,CAAL,WAAA;CAHA,CAI4C,GAAA,CAA5C,CAAA,GAA4C,iBAA5C;CACO,CAAgD,GAAzC,CAAR,IAAQ,GAAd,EAAoC;CANtC,IAA2F;CAD7F,EAAmB;CARnB,CAiBA,CAAwB,KAAxB,CAAwB,IAAxB;CACE,CAAA,CAA+C,CAA/C,KAA+C,iCAA/C;CACS,CAAP,IAAM,GAAN,IAAA;CADF,IAA+C;CAG5C,CAAH,CAAoD,MAAA,EAApD,oCAAA;CACE,QAAA,CAAA;CAAA,CAAoB,EAApB,CAAK,CAAL,CAAA;CAAA,EAEY,GAAZ,EAAoB,CAApB;CAFA,EAGqB,CAHrB,EAGA,EAAQ,CAAR;CAHA,EAKa,CAAA,EAAb,EAAa,CAAS;CALtB,IAMoB,CAApB,CAAc,GAAd;CANA,EAQqB,GAArB,EAAQ,CAAR;CACQ,IAAK,EAAN,MAAP;CAVF,IAAoD;CAJtD,EAAwB;CAjBxB,CAiCA,CAA4B,KAA5B,CAA4B,QAA5B;CACE,OAAA,SAAA;CAAA,EAAa,CAAb,CAAA;CAAA,EACa,CAAb,MAAA;CADA,EAGW,CAAX,KAAW,CAAX;CACE,EAAQ,EAAR,CAAA,GAAQ;CAAR,EACa,GAAb,IAAA;CAAa,CAAO,EAAN,CAAD,GAAC;CADd,OAAA;CAAA,CAEmB,EAAnB,CAAK,CAAL,CAAA,EAAA;CAFA,CAG2B,EAA3B,CAAK,CAAL,CAAA,CAAA,CAAoB;CAEb,CAAuB,GAA9B,CAAM,IAAN,GAAA,EAAA;CANF,IAAW;CAHX,EAWU,CAAV,KAAA;CACY,GAAI,EAAO,CAArB,EAAS,IAAT;CADF,IAAU;CAXV,CAcA,CAAqD,CAArD,KAAqD,uCAArD;CACS,IAAO,CAAR,CAAsB,GAAd,GAAd;CADF,IAAqD;CAdrD,CAiBA,CAA6C,CAA7C,KAA6C,+BAA7C;CACS,CAAuB,EAAjB,CAAb,CAAM,OAAN;CADF,IAA6C;CAG1C,CAAH,CAAqD,MAAA,EAArD,qCAAA;CACS,GAAqB,CAAd,CAAR,GAAiB,CAAT,GAAd;CADF,IAAqD;CArBvD,EAA4B;CAwBnB,CAAW,CAAA,KAApB,CAAA;CACK,CAAH,CAAqD,MAAA,EAArD,qCAAA;CACS,CAA6B,GAApC,CAAM,CAAO,MAAb;CADF,IAAqD;CADvD,EAAoB;CA1DgB"
}

View file

@ -0,0 +1,456 @@
// Generated by CoffeeScript 1.6.3
var __slice = [].slice,
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
describe('Annotator.Plugin.Permissions', function() {
var el, permissions;
el = null;
permissions = null;
beforeEach(function() {
el = $("<div class='annotator-viewer'></div>").appendTo('body')[0];
return permissions = new Annotator.Plugin.Permissions(el);
});
afterEach(function() {
return $(el).remove();
});
it("it should add the current user object to newly created annotations on beforeAnnotationCreated", function() {
var ann;
ann = {};
$(el).trigger('beforeAnnotationCreated', [ann]);
assert.isUndefined(ann.user);
ann = {};
permissions.setUser('alice');
$(el).trigger('beforeAnnotationCreated', [ann]);
assert.equal(ann.user, 'alice');
ann = {};
permissions.setUser({
id: 'alice'
});
permissions.options.userId = function(user) {
return user.id;
};
$(el).trigger('beforeAnnotationCreated', [ann]);
return assert.deepEqual(ann.user, {
id: 'alice'
});
});
it("it should add permissions to newly created annotations on beforeAnnotationCreated", function() {
var ann;
ann = {};
$(el).trigger('beforeAnnotationCreated', [ann]);
assert.ok(ann.permissions);
ann = {};
permissions.options.permissions = {};
$(el).trigger('beforeAnnotationCreated', [ann]);
return assert.deepEqual(ann.permissions, {});
});
describe('pluginInit', function() {
beforeEach(function() {
return permissions.annotator = {
viewer: {
addField: sinon.spy()
},
editor: {
addField: sinon.spy()
},
plugins: {}
};
});
it("should register a field with the Viewer", function() {
permissions.pluginInit();
return assert(permissions.annotator.viewer.addField.calledOnce);
});
it("should register an two checkbox fields with the Editor", function() {
permissions.pluginInit();
return assert.equal(permissions.annotator.editor.addField.callCount, 2);
});
it("should register an 'anyone can view' field with the Editor if showEditPermissionsCheckbox is true", function() {
permissions.options.showViewPermissionsCheckbox = true;
permissions.options.showEditPermissionsCheckbox = false;
permissions.pluginInit();
return assert.equal(permissions.annotator.editor.addField.callCount, 1);
});
it("should register an 'anyone can edit' field with the Editor if showViewPermissionsCheckbox is true", function() {
permissions.options.showViewPermissionsCheckbox = false;
permissions.options.showEditPermissionsCheckbox = true;
permissions.pluginInit();
return assert.equal(permissions.annotator.editor.addField.callCount, 1);
});
return it("should register a filter if the Filter plugin is loaded", function() {
permissions.annotator.plugins.Filter = {
addFilter: sinon.spy()
};
permissions.pluginInit();
return assert(permissions.annotator.plugins.Filter.addFilter.calledOnce);
});
});
describe('authorize', function() {
var annotations;
annotations = null;
describe('Basic usage', function() {
beforeEach(function() {
return annotations = [
{}, {
user: 'alice'
}, {
permissions: {}
}, {
permissions: {
'update': []
}
}
];
});
it('should allow any action for an annotation with no authorisation info', function() {
var a;
a = annotations[0];
assert.isTrue(permissions.authorize(null, a));
assert.isTrue(permissions.authorize('foo', a));
permissions.setUser('alice');
assert.isTrue(permissions.authorize(null, a));
return assert.isTrue(permissions.authorize('foo', a));
});
it('should NOT allow any action if annotation.user and no @user is set', function() {
var a;
a = annotations[1];
assert.isFalse(permissions.authorize(null, a));
return assert.isFalse(permissions.authorize('foo', a));
});
it('should allow any action if @options.userId(@user) == annotation.user', function() {
var a;
a = annotations[1];
permissions.setUser('alice');
assert.isTrue(permissions.authorize(null, a));
return assert.isTrue(permissions.authorize('foo', a));
});
it('should NOT allow any action if @options.userId(@user) != annotation.user', function() {
var a;
a = annotations[1];
permissions.setUser('bob');
assert.isFalse(permissions.authorize(null, a));
return assert.isFalse(permissions.authorize('foo', a));
});
it('should allow any action if annotation.permissions == {}', function() {
var a;
a = annotations[2];
assert.isTrue(permissions.authorize(null, a));
assert.isTrue(permissions.authorize('foo', a));
permissions.setUser('alice');
assert.isTrue(permissions.authorize(null, a));
return assert.isTrue(permissions.authorize('foo', a));
});
return it('should allow an action if annotation.permissions[action] == []', function() {
var a;
a = annotations[3];
assert.isTrue(permissions.authorize('update', a));
permissions.setUser('bob');
return assert.isTrue(permissions.authorize('update', a));
});
});
return describe('Custom options.userAuthorize() callback', function() {
beforeEach(function() {
permissions.setUser(null);
permissions.options.userAuthorize = function(action, annotation, user) {
var token, tokenTest, tokens, userGroups, _i, _len;
userGroups = function(user) {
return (user != null ? user.groups : void 0) || ['public'];
};
tokenTest = function(token, user) {
var groups, key, value, values, _ref;
if (/^(?:group|user):/.test(token)) {
_ref = token.split(':'), key = _ref[0], values = 2 <= _ref.length ? __slice.call(_ref, 1) : [];
value = values.join(':');
if (key === 'group') {
groups = userGroups(user);
return __indexOf.call(groups, value) >= 0;
} else if (user && key === 'user') {
return value === user.id;
}
}
};
if (annotation.permissions) {
tokens = annotation.permissions[action] || [];
for (_i = 0, _len = tokens.length; _i < _len; _i++) {
token = tokens[_i];
if (tokenTest(token, user)) {
return true;
}
}
}
return false;
};
return annotations = [
{
permissions: {
'update': ['group:public']
}
}, {
permissions: {
'update': ['user:alice']
}
}, {
permissions: {
'update': ['user:alice', 'user:bob']
}
}, {
permissions: {
'update': ['user:alice', 'user:bob', 'group:admin']
}
}
];
});
afterEach(function() {
return delete permissions.options.userAuthorize;
});
it('should (by default) allow an action if annotation.permissions[action] includes "group:public"', function() {
var a;
a = annotations[0];
assert.isTrue(permissions.authorize('update', a));
permissions.setUser({
id: 'bob'
});
return assert.isTrue(permissions.authorize('update', a));
});
it('should (by default) allow an action if annotation.permissions[action] includes "user:@user"', function() {
var a;
a = annotations[1];
assert.isFalse(permissions.authorize('update', a));
permissions.setUser({
id: 'bob'
});
assert.isFalse(permissions.authorize('update', a));
permissions.setUser({
id: 'alice'
});
assert.isTrue(permissions.authorize('update', a));
a = annotations[2];
permissions.setUser(null);
assert.isFalse(permissions.authorize('update', a));
permissions.setUser({
id: 'bob'
});
assert.isTrue(permissions.authorize('update', a));
permissions.setUser({
id: 'alice'
});
return assert.isTrue(permissions.authorize('update', a));
});
it('should allow an action if annotation.permissions[action] includes "user:@options.userId(@user)"', function() {
var a;
a = annotations[1];
permissions.options.userId = function(user) {
return (user != null ? user.id : void 0) || null;
};
assert.isFalse(permissions.authorize('update', a));
permissions.setUser({
id: 'alice'
});
return assert.isTrue(permissions.authorize('update', a));
});
return it('should allow an action if annotation.permissions[action] includes "user:@options.userId(@user)"', function() {
var a;
a = annotations[3];
assert.isFalse(permissions.authorize('update', a));
permissions.setUser({
id: 'foo',
groups: ['other']
});
assert.isFalse(permissions.authorize('update', a));
permissions.setUser({
id: 'charlie',
groups: ['admin']
});
return assert.isTrue(permissions.authorize('update', a));
});
});
});
describe('updateAnnotationPermissions', function() {
var annotation, checkbox, field;
field = null;
checkbox = null;
annotation = null;
beforeEach(function() {
checkbox = $('<input type="checkbox" />');
field = $('<li />').append(checkbox)[0];
return annotation = {
permissions: {
'update': ['Alice']
}
};
});
it("should NOT be world editable when 'Anyone can edit' checkbox is unchecked", function() {
checkbox.removeAttr('checked');
permissions.updateAnnotationPermissions('update', field, annotation);
return assert.isFalse(permissions.authorize('update', annotation, null));
});
it("should be world editable when 'Anyone can edit' checkbox is checked", function() {
checkbox.attr('checked', 'checked');
permissions.updateAnnotationPermissions('update', field, annotation);
return assert.isTrue(permissions.authorize('update', annotation, null));
});
return it("should NOT be world editable when 'Anyone can edit' checkbox is unchecked for a second time", function() {
checkbox.attr('checked', 'checked');
permissions.updateAnnotationPermissions('update', field, annotation);
assert.isTrue(permissions.authorize('update', annotation, null));
checkbox.removeAttr('checked');
permissions.updateAnnotationPermissions('update', field, annotation);
return assert.isFalse(permissions.authorize('update', annotation, null));
});
});
describe('updatePermissionsField', function() {
var annotations, checkbox, field;
field = null;
checkbox = null;
annotations = [
{}, {
permissions: {
'update': ['user:Alice']
}
}, {
permissions: {
'update': ['user:Alice']
}
}, {
permissions: {
'update': ['Alice'],
'admin': ['Alice']
}
}, {
permissions: {
'update': ['Alice'],
'admin': ['Bob']
}
}
];
beforeEach(function() {
checkbox = $('<input type="checkbox" />');
field = $('<li />').append(checkbox).appendTo(permissions.element);
permissions.setUser('Alice');
return permissions.updatePermissionsField('update', field, annotations.shift());
});
afterEach(function() {
return field.remove();
});
it("should have a checked checkbox when there are no permissions", function() {
return assert.isTrue(checkbox.is(':checked'));
});
it("should have an unchecked checkbox when there are permissions", function() {
return assert.isFalse(checkbox.is(':checked'));
});
it("should enable the checkbox by default", function() {
return assert.isTrue(checkbox.is(':enabled'));
});
it("should display the field if the current user has 'admin' permissions", function() {
return assert.isTrue(field.is(':visible'));
});
return it("should NOT display the field if the current user does not have 'admin' permissions", function() {
return assert.isFalse(field.is(':visible'));
});
});
return describe('updateViewer', function() {
var controls, field;
controls = null;
field = null;
beforeEach(function() {
field = $('<div />').appendTo('<div />')[0];
return controls = {
showEdit: sinon.spy(),
hideEdit: sinon.spy(),
showDelete: sinon.spy(),
hideDelete: sinon.spy()
};
});
describe('coarse grained updates based on user', function() {
var annotations;
annotations = null;
beforeEach(function() {
permissions.setUser('alice');
return annotations = [
{
user: 'alice'
}, {
user: 'bob'
}, {}
];
});
it("it should display annotations' users in the viewer element", function() {
permissions.updateViewer(field, annotations[0], controls);
assert.equal($(field).html(), 'alice');
return assert.lengthOf($(field).parent(), 1);
});
it("it should remove the field if annotation has no user", function() {
permissions.updateViewer(field, {}, controls);
return assert.lengthOf($(field).parent(), 0);
});
it("it should remove the field if annotation has no user string", function() {
permissions.options.userString = function() {
return null;
};
permissions.updateViewer(field, annotations[1], controls);
return assert.lengthOf($(field).parent(), 0);
});
it("it should remove the field if annotation has empty user string", function() {
permissions.options.userString = function() {
return '';
};
permissions.updateViewer(field, annotations[1], controls);
return assert.lengthOf($(field).parent(), 0);
});
it("should hide controls for users other than the current user", function() {
permissions.updateViewer(field, annotations[0], controls);
assert.isFalse(controls.hideEdit.called);
assert.isFalse(controls.hideDelete.called);
permissions.updateViewer(field, annotations[1], controls);
assert(controls.hideEdit.calledOnce);
return assert(controls.hideDelete.calledOnce);
});
return it("should show controls for annotations without a user", function() {
permissions.updateViewer(field, annotations[2], controls);
assert.isFalse(controls.hideEdit.called);
return assert.isFalse(controls.hideDelete.called);
});
});
return describe('fine-grained use (user and permissions)', function() {
var annotations;
annotations = null;
beforeEach(function() {
annotations = [
{
user: 'alice',
permissions: {
'update': ['alice'],
'delete': ['alice']
}
}, {
user: 'bob',
permissions: {
'update': ['bob'],
'delete': ['bob']
}
}
];
return permissions.setUser('bob');
});
it("it should should hide edit button if user cannot update", function() {
permissions.updateViewer(field, annotations[0], controls);
return assert(controls.hideEdit.calledOnce);
});
it("it should should show edit button if user can update", function() {
permissions.updateViewer(field, annotations[1], controls);
return assert.isFalse(controls.hideEdit.called);
});
it("it should should hide delete button if user cannot delete", function() {
permissions.updateViewer(field, annotations[0], controls);
return assert(controls.hideDelete.calledOnce);
});
return it("it should should show delete button if user can delete", function() {
permissions.updateViewer(field, annotations[1], controls);
return assert.isFalse(controls.hideDelete.called);
});
});
});
});
/*
//@ sourceMappingURL=permissions_spec.map
*/

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,640 @@
// Generated by CoffeeScript 1.6.3
describe("Annotator.Plugin.Store", function() {
var store;
store = null;
beforeEach(function() {
var element;
element = $('<div></div>')[0];
store = new Annotator.Plugin.Store(element, {
autoFetch: false
});
return store.annotator = {
plugins: {},
loadAnnotations: sinon.spy()
};
});
describe("events", function() {
it("should call Store#annotationCreated when the annotationCreated is fired", function() {
sinon.stub(store, 'annotationCreated');
store.element.trigger('annotationCreated', ['annotation1']);
return assert.isTrue(store.annotationCreated.calledWith('annotation1'));
});
it("should call Store#annotationUpdated when the annotationUpdated is fired", function() {
sinon.stub(store, 'annotationUpdated');
store.element.trigger('annotationUpdated', ['annotation1']);
return assert.isTrue(store.annotationUpdated.calledWith('annotation1'));
});
return it("should call Store#annotationDeleted when the annotationDeleted is fired", function() {
sinon.stub(store, 'annotationDeleted');
store.element.trigger('annotationDeleted', ['annotation1']);
return assert.isTrue(store.annotationDeleted.calledWith('annotation1'));
});
});
describe("pluginInit", function() {
it("should call Store#_getAnnotations() if no Auth plugin is loaded", function() {
sinon.stub(store, '_getAnnotations');
store.pluginInit();
return assert(store._getAnnotations.calledOnce);
});
return it("should call Auth#withToken() if Auth plugin is loaded", function() {
var authMock;
authMock = {
withToken: sinon.spy()
};
store.annotator.plugins.Auth = authMock;
store.pluginInit();
return assert.isTrue(authMock.withToken.calledWith(store._getAnnotations));
});
});
describe("_getAnnotations", function() {
it("should call Store#loadAnnotations() if @options.loadFromSearch is not present", function() {
sinon.stub(store, 'loadAnnotations');
store._getAnnotations();
return assert(store.loadAnnotations.calledOnce);
});
return it("should call Store#loadAnnotationsFromSearch() if @options.loadFromSearch is present", function() {
sinon.stub(store, 'loadAnnotationsFromSearch');
store.options.loadFromSearch = {};
store._getAnnotations();
return assert.isTrue(store.loadAnnotationsFromSearch.calledWith(store.options.loadFromSearch));
});
});
describe("annotationCreated", function() {
var annotation;
annotation = null;
beforeEach(function() {
annotation = {};
sinon.stub(store, 'registerAnnotation');
sinon.stub(store, 'updateAnnotation');
return sinon.stub(store, '_apiRequest');
});
it("should call Store#registerAnnotation() with the new annotation", function() {
store.annotationCreated(annotation);
return assert.isTrue(store.registerAnnotation.calledWith(annotation));
});
it("should call Store#_apiRequest('create') with the new annotation", function() {
var args;
store.annotationCreated(annotation);
args = store._apiRequest.lastCall.args;
assert(store._apiRequest.calledOnce);
assert.equal(args[0], 'create');
return assert.equal(args[1], annotation);
});
return it("should call Store#updateAnnotation() if the annotation already exists in @annotations", function() {
store.annotations = [annotation];
store.annotationCreated(annotation);
assert(store.updateAnnotation.calledOnce);
return assert.equal(store.updateAnnotation.lastCall.args[0], annotation);
});
});
describe("annotationUpdated", function() {
var annotation;
annotation = null;
beforeEach(function() {
annotation = {};
return sinon.stub(store, '_apiRequest');
});
it("should call Store#_apiRequest('update') with the annotation and data", function() {
var args, data;
store.annotations = [annotation];
store.annotationUpdated(annotation);
args = store._apiRequest.lastCall.args;
assert(store._apiRequest.calledOnce);
assert.equal(args[0], 'update');
assert.equal(args[1], annotation);
assert.equal(typeof args[2], 'function');
sinon.stub(store, 'updateAnnotation');
data = {
text: "Dummy response data"
};
args[2](data);
return assert.isTrue(store.updateAnnotation.calledWith(annotation, data));
});
return it("should NOT call Store#_apiRequest() if the annotation is unregistered", function() {
store.annotations = [];
store.annotationUpdated(annotation);
return assert.isFalse(store._apiRequest.called);
});
});
describe("annotationDeleted", function() {
var annotation;
annotation = null;
beforeEach(function() {
annotation = {};
return sinon.stub(store, '_apiRequest');
});
it("should call Store#_apiRequest('destroy') with the annotation and data", function() {
var args;
store.annotations = [annotation];
store.annotationDeleted(annotation);
args = store._apiRequest.lastCall.args;
assert(store._apiRequest.calledOnce);
assert.equal(args[0], 'destroy');
return assert.equal(args[1], annotation);
});
return it("should NOT call Store#_apiRequest() if the annotation is unregistered", function() {
store.annotations = [];
store.annotationDeleted(annotation);
return assert.isFalse(store._apiRequest.called);
});
});
describe("registerAnnotation", function() {
return it("should add the annotation to the @annotations array", function() {
var annotation;
annotation = {};
store.annotations = [];
store.registerAnnotation(annotation);
return assert.equal($.inArray(annotation, store.annotations), 0);
});
});
describe("unregisterAnnotation", function() {
return it("should remove the annotation from the @annotations array", function() {
var annotation;
annotation = {};
store.annotations = [annotation];
store.unregisterAnnotation(annotation);
return assert.equal($.inArray(annotation, store.annotations), -1);
});
});
describe("updateAnnotation", function() {
var annotation;
annotation = {};
beforeEach(function() {
sinon.stub(console, 'error');
annotation = {
text: "my annotation text",
range: []
};
return store.annotations = [annotation];
});
afterEach(function() {
return console.error.restore();
});
it("should extend the annotation with the data provided", function() {
store.updateAnnotation(annotation, {
id: "myid",
text: "new text"
});
return assert.deepEqual(annotation, {
id: "myid",
text: "new text",
range: []
});
});
it("should NOT extend the annotation if it is not registered with the Store", function() {
store.annotations = [];
store.updateAnnotation(annotation, {
id: "myid",
text: "new text"
});
return assert.equal(annotation, annotation);
});
return it("should update the data stored on the annotation highlight", function() {
var data;
data = {};
annotation.highlight = $('<span />').data('annotation', annotation);
store.updateAnnotation(annotation, data);
return assert.equal(annotation.highlight.data('annotation'), annotation);
});
});
describe("loadAnnotations", function() {
return it("should call Store#_apiRequest()", function() {
sinon.stub(store, '_apiRequest');
store.loadAnnotations();
return assert.isTrue(store._apiRequest.calledWith('read', null, store._onLoadAnnotations));
});
});
describe("loadAnnotationsFromSearch", function() {
return it("should call Store#_apiRequest()", function() {
var options;
options = {};
sinon.stub(store, '_apiRequest');
store.loadAnnotationsFromSearch(options);
return assert.isTrue(store._apiRequest.calledWith('search', options, store._onLoadAnnotationsFromSearch));
});
});
describe("_onLoadAnnotations", function() {
it("should set the Store#annotations property with received annotations", function() {
var data;
data = [1, 2, 3];
store._onLoadAnnotations(data);
return assert.deepEqual(store.annotations, data);
});
it("should default to an empty array if no data is provided", function() {
store._onLoadAnnotations();
return assert.deepEqual(store.annotations, []);
});
it("should call Annotator#loadAnnotations()", function() {
store._onLoadAnnotations();
return assert(store.annotator.loadAnnotations.calledOnce);
});
it("should call Annotator#loadAnnotations() with clone of provided data", function() {
var data;
data = [];
store._onLoadAnnotations(data);
assert.notStrictEqual(store.annotator.loadAnnotations.lastCall.args[0], data);
return assert.deepEqual(store.annotator.loadAnnotations.lastCall.args[0], data);
});
return it("should add, dedupe and update annotations when called for the 2nd time", function() {
var data1, data2, dataAll;
data1 = [
{
id: 1
}, {
id: 2
}
];
data2 = [
{
id: 1,
foo: "bar"
}, {
id: 3
}
];
dataAll = [
{
id: 1,
foo: "bar"
}, {
id: 2
}, {
id: 3
}
];
store._onLoadAnnotations(data1);
store._onLoadAnnotations(data2);
return assert.deepEqual(store.annotations, dataAll);
});
});
describe("_onLoadAnnotationsFromSearch", function() {
it("should call Store#_onLoadAnnotations() with data.rows", function() {
var data;
sinon.stub(store, '_onLoadAnnotations');
data = {
rows: [{}, {}, {}]
};
store._onLoadAnnotationsFromSearch(data);
return assert.deepEqual(store._onLoadAnnotations.lastCall.args[0], data.rows);
});
return it("should default to an empty array if no data.rows are provided", function() {
sinon.stub(store, '_onLoadAnnotations');
store._onLoadAnnotationsFromSearch();
return assert.deepEqual(store._onLoadAnnotations.lastCall.args[0], []);
});
});
describe("dumpAnnotations", function() {
it("returns a list of its annotations", function() {
store.annotations = [
{
text: "Foobar"
}, {
user: "Bob"
}
];
return assert.deepEqual(store.dumpAnnotations(), [
{
text: "Foobar"
}, {
user: "Bob"
}
]);
});
return it("removes the highlights properties from the annotations", function() {
store.annotations = [
{
highlights: "abc"
}, {
highlights: [1, 2, 3]
}
];
return assert.deepEqual(store.dumpAnnotations(), [{}, {}]);
});
});
describe("_apiRequest", function() {
var mockOptions, mockUri;
mockUri = 'http://mock.com';
mockOptions = {};
beforeEach(function() {
sinon.stub(store, '_urlFor').returns(mockUri);
sinon.stub(store, '_apiRequestOptions').returns(mockOptions);
return sinon.stub($, 'ajax').returns({});
});
afterEach(function() {
return $.ajax.restore();
});
it("should call Store#_urlFor() with the action", function() {
var action;
action = 'read';
store._apiRequest(action);
return assert.isTrue(store._urlFor.calledWith(action, void 0));
});
it("should call Store#_urlFor() with the action and id extracted from the data", function() {
var action, data;
data = {
id: 'myId'
};
action = 'read';
store._apiRequest(action, data);
return assert.isTrue(store._urlFor.calledWith(action, data.id));
});
it("should call Store#_apiRequestOptions() with the action, data and callback", function() {
var action, callback, data;
data = {
id: 'myId'
};
action = 'read';
callback = function() {};
store._apiRequest(action, data, callback);
return assert.isTrue(store._apiRequestOptions.calledWith(action, data, callback));
});
it("should call jQuery#ajax()", function() {
store._apiRequest();
return assert.isTrue($.ajax.calledWith(mockUri, mockOptions));
});
return it("should return the jQuery XHR object with action and id appended", function() {
var action, data, request;
data = {
id: 'myId'
};
action = 'read';
request = store._apiRequest(action, data);
assert.equal(request._id, data.id);
return assert.equal(request._action, action);
});
});
describe("_apiRequestOptions", function() {
beforeEach(function() {
return sinon.stub(store, '_dataFor').returns('{}');
});
it("should call Store#_methodFor() with the action", function() {
var action;
sinon.stub(store, '_methodFor').returns('GET');
action = 'read';
store._apiRequestOptions(action);
return assert.isTrue(store._methodFor.calledWith(action));
});
it("should return options for jQuery.ajax()", function() {
var action, callback, data, options;
sinon.stub(store, '_methodFor').returns('GET');
action = 'read';
data = {};
callback = function() {};
options = store._apiRequestOptions(action, data, callback);
return assert.deepEqual(options, {
type: 'GET',
headers: void 0,
dataType: "json",
success: callback,
error: store._onError,
data: '{}',
contentType: "application/json; charset=utf-8"
});
});
it("should set custom headers from the data property 'annotator:headers'", function() {
var action, data, options;
sinon.stub(store, '_methodFor').returns('GET');
sinon.stub(store.element, 'data').returns({
'x-custom-header-one': 'mycustomheader',
'x-custom-header-two': 'mycustomheadertwo',
'x-custom-header-three': 'mycustomheaderthree'
});
action = 'read';
data = {};
options = store._apiRequestOptions(action, data);
return assert.deepEqual(options.headers, {
'x-custom-header-one': 'mycustomheader',
'x-custom-header-two': 'mycustomheadertwo',
'x-custom-header-three': 'mycustomheaderthree'
});
});
it("should call Store#_dataFor() with the data if action is NOT search", function() {
var action, data;
sinon.stub(store, '_methodFor').returns('GET');
action = 'read';
data = {};
store._apiRequestOptions(action, data);
return assert.isTrue(store._dataFor.calledWith(data));
});
it("should NOT call Store#_dataFor() if action is search", function() {
var action, data;
sinon.stub(store, '_methodFor').returns('GET');
action = 'search';
data = {};
store._apiRequestOptions(action, data);
return assert.isFalse(store._dataFor.called);
});
it("should NOT add the contentType property if the action is search", function() {
var action, data, options;
sinon.stub(store, '_methodFor').returns('GET');
action = 'search';
data = {};
options = store._apiRequestOptions(action, data);
assert.isUndefined(options.contentType);
return assert.equal(options.data, data);
});
it("should emulate new-fangled HTTP if emulateHTTP is true", function() {
var options;
sinon.stub(store, '_methodFor').returns('DELETE');
store.options.emulateHTTP = true;
options = store._apiRequestOptions('destroy', {
id: 4
});
assert.equal(options.type, 'POST');
return assert.deepEqual(options.headers, {
'X-HTTP-Method-Override': 'DELETE'
});
});
it("should emulate proper JSON handling if emulateJSON is true", function() {
var options;
sinon.stub(store, '_methodFor').returns('DELETE');
store.options.emulateJSON = true;
options = store._apiRequestOptions('destroy', {});
assert.deepEqual(options.data, {
json: '{}'
});
return assert.isUndefined(options.contentType);
});
return it("should append _method to the form data if emulateHTTP and emulateJSON are both true", function() {
var options;
sinon.stub(store, '_methodFor').returns('DELETE');
store.options.emulateHTTP = true;
store.options.emulateJSON = true;
options = store._apiRequestOptions('destroy', {});
return assert.deepEqual(options.data, {
_method: 'DELETE',
json: '{}'
});
});
});
describe("_urlFor", function() {
it("should generate RESTful URLs by default", function() {
assert.equal(store._urlFor('create'), '/store/annotations');
assert.equal(store._urlFor('read'), '/store/annotations');
assert.equal(store._urlFor('read', 'foo'), '/store/annotations/foo');
assert.equal(store._urlFor('update', 'bar'), '/store/annotations/bar');
return assert.equal(store._urlFor('destroy', 'baz'), '/store/annotations/baz');
});
it("should generate URLs as specified by its options otherwise", function() {
store.options.prefix = '/some/prefix';
store.options.urls.create = '/createMe';
store.options.urls.read = '/:id/readMe';
store.options.urls.update = '/:id/updateMe';
store.options.urls.destroy = '/:id/destroyMe';
assert.equal(store._urlFor('create'), '/some/prefix/createMe');
assert.equal(store._urlFor('read'), '/some/prefix/readMe');
assert.equal(store._urlFor('read', 'foo'), '/some/prefix/foo/readMe');
assert.equal(store._urlFor('update', 'bar'), '/some/prefix/bar/updateMe');
return assert.equal(store._urlFor('destroy', 'baz'), '/some/prefix/baz/destroyMe');
});
it("should generate URLs correctly with an empty prefix", function() {
store.options.prefix = '';
store.options.urls.create = '/createMe';
store.options.urls.read = '/:id/readMe';
store.options.urls.update = '/:id/updateMe';
store.options.urls.destroy = '/:id/destroyMe';
assert.equal(store._urlFor('create'), '/createMe');
assert.equal(store._urlFor('read'), '/readMe');
assert.equal(store._urlFor('read', 'foo'), '/foo/readMe');
assert.equal(store._urlFor('update', 'bar'), '/bar/updateMe');
return assert.equal(store._urlFor('destroy', 'baz'), '/baz/destroyMe');
});
return it("should generate URLs with substitution markers in query strings", function() {
store.options.prefix = '/some/prefix';
store.options.urls.read = '/read?id=:id';
store.options.urls.update = '/update?foo&id=:id';
store.options.urls.destroy = '/delete?id=:id&foo';
assert.equal(store._urlFor('read'), '/some/prefix/read?id=');
assert.equal(store._urlFor('read', 'foo'), '/some/prefix/read?id=foo');
assert.equal(store._urlFor('update', 'bar'), '/some/prefix/update?foo&id=bar');
return assert.equal(store._urlFor('destroy', 'baz'), '/some/prefix/delete?id=baz&foo');
});
});
describe("_methodFor", function() {
return it("should return the appropriate method for the action", function() {
var action, method, table, _i, _len, _results;
table = {
'create': 'POST',
'read': 'GET',
'update': 'PUT',
'destroy': 'DELETE',
'search': 'GET'
};
_results = [];
for (method = _i = 0, _len = table.length; _i < _len; method = ++_i) {
action = table[method];
_results.push(assert.equal(store._methodFor(action, method)));
}
return _results;
});
});
describe("_dataFor", function() {
it("should stringify the annotation into JSON", function() {
var annotation, data;
annotation = {
id: 'bill'
};
data = store._dataFor(annotation);
return assert.equal(data, '{"id":"bill"}');
});
it("should NOT stringify the highlights property", function() {
var annotation, data;
annotation = {
id: 'bill',
highlights: {}
};
data = store._dataFor(annotation);
return assert.equal(data, '{"id":"bill"}');
});
it("should NOT append a highlights property if the annotation does not have one", function() {
var annotation;
annotation = {
id: 'bill'
};
store._dataFor(annotation);
return assert.isFalse(annotation.hasOwnProperty('highlights'));
});
return it("should extend the annotation with @options.annotationData", function() {
var annotation, data;
annotation = {
id: "cat"
};
store.options.annotationData = {
custom: 'value',
customArray: []
};
data = store._dataFor(annotation);
assert.equal(data, '{"id":"cat","custom":"value","customArray":[]}');
return assert.deepEqual(annotation, {
"id": "cat",
"custom": "value",
"customArray": []
});
});
});
return describe("_onError", function() {
var message, requests;
message = null;
requests = [
{}, {}, {
_action: 'read',
_id: 'jim'
}, {
_action: 'search'
}, {
_action: 'read'
}, {
status: 401,
_action: 'delete',
'_id': 'cake'
}, {
status: 404,
_action: 'delete',
'_id': 'cake'
}, {
status: 500,
_action: 'delete',
'_id': 'cake'
}
];
beforeEach(function() {
sinon.stub(Annotator, 'showNotification');
sinon.stub(console, 'error');
store._onError(requests.shift());
return message = Annotator.showNotification.lastCall.args[0];
});
afterEach(function() {
Annotator.showNotification.restore();
return console.error.restore();
});
it("should call call Annotator.showNotification() with a message and error style", function() {
assert(Annotator.showNotification.calledOnce);
return assert.equal(Annotator.showNotification.lastCall.args[1], Annotator.Notification.ERROR);
});
it("should call console.error with a message", function() {
return assert(console.error.calledOnce);
});
it("should give a default message if xhr.status id not provided", function() {
return assert.equal(message, "Sorry we could not read this annotation");
});
it("should give a default specific message if xhr._action is 'search'", function() {
return assert.equal(message, "Sorry we could not search the store for annotations");
});
it("should give a default specific message if xhr._action is 'read' and there is no xhr._id", function() {
return assert.equal(message, "Sorry we could not read the annotations from the store");
});
it("should give a specific message if xhr.status == 401", function() {
return assert.equal(message, "Sorry you are not allowed to delete this annotation");
});
it("should give a specific message if xhr.status == 404", function() {
return assert.equal(message, "Sorry we could not connect to the annotations store");
});
return it("should give a specific message if xhr.status == 500", function() {
return assert.equal(message, "Sorry something went wrong with the annotation store");
});
});
});
/*
//@ sourceMappingURL=store_spec.map
*/

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,108 @@
// Generated by CoffeeScript 1.6.3
describe('Annotator.Plugin.Tags', function() {
var annotator, plugin;
annotator = null;
plugin = null;
beforeEach(function() {
var el;
el = $("<div><div class='annotator-editor-controls'></div></div>")[0];
annotator = new Annotator($('<div/>')[0]);
plugin = new Annotator.Plugin.Tags(el);
plugin.annotator = annotator;
return plugin.pluginInit();
});
it("should parse whitespace-delimited tags into an array", function() {
var str;
str = 'one two three\tfourFive';
return assert.deepEqual(plugin.parseTags(str), ['one', 'two', 'three', 'fourFive']);
});
it("should stringify a tags array into a space-delimited string", function() {
var ary;
ary = ['one', 'two', 'three'];
return assert.equal(plugin.stringifyTags(ary), "one two three");
});
describe("pluginInit", function() {
it("should add a field to the editor", function() {
sinon.spy(annotator.editor, 'addField');
plugin.pluginInit();
return assert(annotator.editor.addField.calledOnce);
});
return it("should register a filter if the Filter plugin is loaded", function() {
plugin.annotator.plugins.Filter = {
addFilter: sinon.spy()
};
plugin.pluginInit();
return assert(plugin.annotator.plugins.Filter.addFilter.calledOnce);
});
});
describe("updateField", function() {
it("should set the value of the input", function() {
var annotation;
annotation = {
tags: ['apples', 'oranges', 'pears']
};
plugin.updateField(plugin.field, annotation);
return assert.equal(plugin.input.val(), 'apples oranges pears');
});
return it("should set the clear the value of the input if there are no tags", function() {
var annotation;
annotation = {};
plugin.input.val('apples pears oranges');
plugin.updateField(plugin.field, annotation);
return assert.equal(plugin.input.val(), '');
});
});
describe("setAnnotationTags", function() {
return it("should set the annotation's tags", function() {
var annotation;
annotation = {};
plugin.input.val('apples oranges pears');
plugin.setAnnotationTags(plugin.field, annotation);
return assert.deepEqual(annotation.tags, ['apples', 'oranges', 'pears']);
});
});
return describe("updateViewer", function() {
it("should insert the tags into the field", function() {
var annotation, field;
annotation = {
tags: ['foo', 'bar', 'baz']
};
field = $('<div />')[0];
plugin.updateViewer(field, annotation);
return assert.deepEqual($(field).html(), ['<span class="annotator-tag">foo</span>', '<span class="annotator-tag">bar</span>', '<span class="annotator-tag">baz</span>'].join(' '));
});
return it("should remove the field if there are no tags", function() {
var annotation, field;
annotation = {
tags: []
};
field = $('<div />')[0];
plugin.updateViewer(field, annotation);
assert.lengthOf($(field).parent(), 0);
annotation = {};
field = $('<div />')[0];
plugin.updateViewer(field, annotation);
return assert.lengthOf($(field).parent(), 0);
});
});
});
describe('Annotator.Plugin.Tags.filterCallback', function() {
var filter;
filter = null;
beforeEach(function() {
return filter = Annotator.Plugin.Tags.filterCallback;
});
it('should return true if all tags are matched by keywords', function() {
assert.isTrue(filter('cat dog mouse', ['cat', 'dog', 'mouse']));
return assert.isTrue(filter('cat dog', ['cat', 'dog', 'mouse']));
});
return it('should NOT return true if all tags are NOT matched by keywords', function() {
assert.isFalse(filter('cat dog', ['cat']));
return assert.isFalse(filter('cat dog', []));
});
});
/*
//@ sourceMappingURL=tags_spec.map
*/

View file

@ -0,0 +1,10 @@
{
"version": 3,
"file": "tags_spec.js",
"sourceRoot": "../../..",
"sources": [
"test/spec/plugin/tags_spec.coffee"
],
"names": [],
"mappings": "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"
}

View file

@ -0,0 +1,6 @@
// Generated by CoffeeScript 1.6.3
/*
//@ sourceMappingURL=unsupported_spec.map
*/

View file

@ -0,0 +1,10 @@
{
"version": 3,
"file": "unsupported_spec.js",
"sourceRoot": "../../..",
"sources": [
"test/spec/plugin/unsupported_spec.coffee"
],
"names": [],
"mappings": ";AAwFyC"
}