1
0
Fork 0
mirror of https://github.com/futurepress/epub.js.git synced 2025-10-03 14:59:18 +02:00
epub.js/libs/annotator/lib/spec/annotator_spec.js

1149 lines
43 KiB
JavaScript

// Generated by CoffeeScript 1.6.3
describe('Annotator', function() {
var annotator, mock;
annotator = null;
mock = null;
beforeEach(function() {
return annotator = new Annotator($('<div></div>')[0], {});
});
afterEach(function() {
return $(document).unbind();
});
describe("events", function() {
it("should call Annotator#onAdderClick() when adder is clicked", function() {
var stub;
stub = sinon.stub(annotator, 'onAdderClick');
annotator.element.find('.annotator-adder button').click();
return assert(stub.calledOnce);
});
it("should call Annotator#onAdderMousedown() when mouse button is held down on adder", function() {
var stub;
stub = sinon.stub(annotator, 'onAdderMousedown');
annotator.element.find('.annotator-adder button').mousedown();
return assert(stub.calledOnce);
});
it("should call Annotator#onHighlightMouseover() when mouse moves over a highlight", function() {
var highlight, stub;
stub = sinon.stub(annotator, 'onHighlightMouseover');
highlight = $('<span class="annotator-hl" />').appendTo(annotator.element);
highlight.mouseover();
return assert(stub.calledOnce);
});
return it("should call Annotator#startViewerHideTimer() when mouse moves off a highlight", function() {
var highlight, stub;
stub = sinon.stub(annotator, 'startViewerHideTimer');
highlight = $('<span class="annotator-hl" />').appendTo(annotator.element);
highlight.mouseout();
return assert(stub.calledOnce);
});
});
describe("constructor", function() {
beforeEach(function() {
sinon.stub(annotator, '_setupWrapper').returns(annotator);
sinon.stub(annotator, '_setupViewer').returns(annotator);
sinon.stub(annotator, '_setupEditor').returns(annotator);
sinon.stub(annotator, '_setupDocumentEvents').returns(annotator);
return sinon.stub(annotator, '_setupDynamicStyle').returns(annotator);
});
it("should have a jQuery wrapper as @element", function() {
Annotator.prototype.constructor.call(annotator, annotator.element[0]);
return assert.instanceOf(annotator.element, $);
});
it("should create an empty @plugin object", function() {
Annotator.prototype.constructor.call(annotator, annotator.element[0]);
return assert.isTrue(annotator.hasOwnProperty('plugins'));
});
it("should create the adder properties from the @html strings", function() {
Annotator.prototype.constructor.call(annotator, annotator.element[0]);
return assert.instanceOf(annotator.adder, $);
});
it("should call Annotator#_setupWrapper()", function() {
Annotator.prototype.constructor.call(annotator, annotator.element[0]);
return assert(annotator._setupWrapper.called);
});
it("should call Annotator#_setupViewer()", function() {
Annotator.prototype.constructor.call(annotator, annotator.element[0]);
return assert(annotator._setupViewer.called);
});
it("should call Annotator#_setupEditor()", function() {
Annotator.prototype.constructor.call(annotator, annotator.element[0]);
return assert(annotator._setupEditor.called);
});
it("should call Annotator#_setupDocumentEvents()", function() {
Annotator.prototype.constructor.call(annotator, annotator.element[0]);
return assert(annotator._setupDocumentEvents.called);
});
it("should NOT call Annotator#_setupDocumentEvents() if options.readOnly is true", function() {
Annotator.prototype.constructor.call(annotator, annotator.element[0], {
readOnly: true
});
return assert.isFalse(annotator._setupDocumentEvents.called);
});
return it("should call Annotator#_setupDynamicStyle()", function() {
Annotator.prototype.constructor.call(annotator, annotator.element[0]);
return assert(annotator._setupDynamicStyle.called);
});
});
describe("#destroy()", function() {
it("should unbind Annotator's events from the page", function() {
var stub;
stub = sinon.stub(annotator, 'checkForStartSelection');
annotator._setupDocumentEvents();
annotator.destroy();
$(document).mousedown();
assert.isFalse(stub.called);
return $(document).unbind('mousedown');
});
return it("should remove Annotator's elements from the page", function() {
annotator.destroy();
return assert.equal(annotator.element.find('[class^=annotator-]').length, 0);
});
});
describe("_setupDocumentEvents", function() {
({
beforeEach: function() {
return $(document).unbind('mouseup').unbind('mousedown');
}
});
it("should call Annotator#checkForStartSelection() when mouse button is pressed", function() {
var stub;
stub = sinon.stub(annotator, 'checkForStartSelection');
annotator._setupDocumentEvents();
$(document).mousedown();
return assert(stub.calledOnce);
});
return it("should call Annotator#checkForEndSelection() when mouse button is lifted", function() {
var stub;
stub = sinon.stub(annotator, 'checkForEndSelection');
annotator._setupDocumentEvents();
$(document).mouseup();
return assert(stub.calledOnce);
});
});
describe("_setupWrapper", function() {
it("should wrap children of @element in the @html.wrapper element", function() {
annotator.element = $('<div><span>contents</span></div>');
annotator._setupWrapper();
return assert.equal(annotator.wrapper.html(), '<span>contents</span>');
});
return it("should remove all script elements prior to wrapping", function() {
var div;
div = document.createElement('div');
div.appendChild(document.createElement('script'));
annotator.element = $(div);
annotator._setupWrapper();
return assert.equal(annotator.wrapper[0].innerHTML, '');
});
});
describe("_setupViewer", function() {
var mockViewer;
mockViewer = null;
beforeEach(function() {
var element;
element = $('<div />');
mockViewer = {
fields: [],
element: element
};
mockViewer.on = function() {
return mockViewer;
};
mockViewer.hide = function() {
return mockViewer;
};
mockViewer.addField = function(options) {
mockViewer.fields.push(options);
return mockViewer;
};
sinon.spy(mockViewer, 'on');
sinon.spy(mockViewer, 'hide');
sinon.spy(mockViewer, 'addField');
sinon.stub(element, 'bind').returns(element);
sinon.stub(element, 'appendTo').returns(element);
sinon.stub(Annotator, 'Viewer').returns(mockViewer);
return annotator._setupViewer();
});
afterEach(function() {
return Annotator.Viewer.restore();
});
it("should create a new instance of Annotator.Viewer and set Annotator#viewer", function() {
return assert.strictEqual(annotator.viewer, mockViewer);
});
it("should hide the annotator on creation", function() {
return assert(mockViewer.hide.calledOnce);
});
it("should setup the default text field", function() {
var args;
args = mockViewer.addField.lastCall.args[0];
assert(mockViewer.addField.calledOnce);
return assert.equal(typeof args.load, "function");
});
it("should set the contents of the field on load", function() {
var annotation, field;
field = document.createElement('div');
annotation = {
text: "test"
};
annotator.viewer.fields[0].load(field, annotation);
return assert.equal(jQuery(field).html(), "test");
});
it("should set the contents of the field to placeholder text when empty", function() {
var annotation, field;
field = document.createElement('div');
annotation = {
text: ""
};
annotator.viewer.fields[0].load(field, annotation);
return assert.equal(jQuery(field).html(), "<i>No Comment</i>");
});
it("should setup the default text field to publish an event on load", function() {
var annotation, callback, field;
field = document.createElement('div');
annotation = {
text: "test"
};
callback = sinon.spy();
annotator.on('annotationViewerTextField', callback);
annotator.viewer.fields[0].load(field, annotation);
return assert(callback.calledWith(field, annotation));
});
it("should subscribe to custom events", function() {
assert(mockViewer.on.calledWith('edit', annotator.onEditAnnotation));
return assert(mockViewer.on.calledWith('delete', annotator.onDeleteAnnotation));
});
it("should bind to browser mouseover and mouseout events", function() {
return assert(mockViewer.element.bind.calledWith({
'mouseover': annotator.clearViewerHideTimer,
'mouseout': annotator.startViewerHideTimer
}));
});
return it("should append the Viewer#element to the Annotator#wrapper", function() {
return assert(mockViewer.element.appendTo.calledWith(annotator.wrapper));
});
});
describe("_setupEditor", function() {
var mockEditor;
mockEditor = null;
beforeEach(function() {
var element;
element = $('<div />');
mockEditor = {
element: element
};
mockEditor.on = function() {
return mockEditor;
};
mockEditor.hide = function() {
return mockEditor;
};
mockEditor.addField = function() {
return document.createElement('li');
};
sinon.spy(mockEditor, 'on');
sinon.spy(mockEditor, 'hide');
sinon.spy(mockEditor, 'addField');
sinon.stub(element, 'appendTo').returns(element);
sinon.stub(Annotator, 'Editor').returns(mockEditor);
return annotator._setupEditor();
});
afterEach(function() {
return Annotator.Editor.restore();
});
it("should create a new instance of Annotator.Editor and set Annotator#editor", function() {
return assert.strictEqual(annotator.editor, mockEditor);
});
it("should hide the annotator on creation", function() {
return assert(mockEditor.hide.calledOnce);
});
it("should add the default textarea field", function() {
var options;
options = mockEditor.addField.lastCall.args[0];
assert(mockEditor.addField.calledOnce);
assert.equal(options.type, 'textarea');
assert.equal(options.label, 'Comments\u2026');
assert.typeOf(options.load, 'function');
return assert.typeOf(options.submit, 'function');
});
it("should subscribe to custom events", function() {
assert(mockEditor.on.calledWith('hide', annotator.onEditorHide));
return assert(mockEditor.on.calledWith('save', annotator.onEditorSubmit));
});
return it("should append the Editor#element to the Annotator#wrapper", function() {
return assert(mockEditor.element.appendTo.calledWith(annotator.wrapper));
});
});
describe("_setupDynamicStyle", function() {
var $fix;
$fix = null;
beforeEach(function() {
addFixture('annotator');
return $fix = $(fix());
});
afterEach(function() {
return clearFixtures();
});
return it('should ensure Annotator z-indices are larger than others in the page', function() {
var $adder, $filter, check;
$fix.show();
$adder = $('<div style="position:relative;" class="annotator-adder">&nbsp;</div>').appendTo($fix);
$filter = $('<div style="position:relative;" class="annotator-filter">&nbsp;</div>').appendTo($fix);
check = function(minimum) {
var adderZ, filterZ;
adderZ = parseInt($adder.css('z-index'), 10);
filterZ = parseInt($filter.css('z-index'), 10);
assert.isTrue(adderZ > minimum);
assert.isTrue(filterZ > minimum);
return assert.isTrue(adderZ > filterZ);
};
check(1000);
$fix.append('<div style="position: relative; z-index: 2000"></div>');
annotator._setupDynamicStyle();
check(2000);
$fix.append('<div style="position: relative; z-index: 10000"></div>');
annotator._setupDynamicStyle();
check(10000);
return $fix.hide();
});
});
describe("getSelectedRanges", function() {
var mockBrowserRange, mockGlobal, mockRange, mockSelection;
mockGlobal = null;
mockSelection = null;
mockRange = null;
mockBrowserRange = null;
beforeEach(function() {
mockBrowserRange = {
cloneRange: sinon.stub()
};
mockBrowserRange.cloneRange.returns(mockBrowserRange);
mockRange = {
limit: sinon.stub(),
normalize: sinon.stub(),
toRange: sinon.stub().returns('range')
};
mockRange.limit.returns(mockRange);
mockRange.normalize.returns(mockRange);
mockSelection = {
getRangeAt: sinon.stub().returns(mockBrowserRange),
removeAllRanges: sinon.spy(),
addRange: sinon.spy(),
rangeCount: 1
};
mockGlobal = {
getSelection: sinon.stub().returns(mockSelection)
};
sinon.stub(Util, 'getGlobal').returns(mockGlobal);
return sinon.stub(Range, 'BrowserRange').returns(mockRange);
});
afterEach(function() {
Util.getGlobal.restore();
return Range.BrowserRange.restore();
});
it("should retrieve the global object and call getSelection()", function() {
annotator.getSelectedRanges();
return assert(mockGlobal.getSelection.calledOnce);
});
it("should retrieve the global object and call getSelection()", function() {
var ranges;
ranges = annotator.getSelectedRanges();
return assert.deepEqual(ranges, [mockRange]);
});
it("should remove any failed calls to NormalizedRange#limit(), but re-add them to the global selection", function() {
var ranges;
mockRange.limit.returns(null);
ranges = annotator.getSelectedRanges();
assert.deepEqual(ranges, []);
return assert.isTrue(mockSelection.addRange.calledWith(mockBrowserRange));
});
it("should return an empty array if selection.isCollapsed is true", function() {
var ranges;
mockSelection.isCollapsed = true;
ranges = annotator.getSelectedRanges();
return assert.deepEqual(ranges, []);
});
it("should deselect all current ranges", function() {
var ranges;
ranges = annotator.getSelectedRanges();
return assert(mockSelection.removeAllRanges.calledOnce);
});
return it("should reassign the newly normalized ranges", function() {
var ranges;
ranges = annotator.getSelectedRanges();
assert(mockSelection.addRange.calledOnce);
return assert.isTrue(mockSelection.addRange.calledWith('range'));
});
});
describe("createAnnotation", function() {
it("should return an empty annotation", function() {
return assert.deepEqual(annotator.createAnnotation(), {});
});
return it("should fire the 'beforeAnnotationCreated' event providing the annotation", function() {
sinon.spy(annotator, 'publish');
annotator.createAnnotation();
return assert.isTrue(annotator.publish.calledWith('beforeAnnotationCreated', [{}]));
});
});
describe("setupAnnotation", function() {
var annotation, annotationObj, comment, element, normalizedRange, quote, sniffedRange;
annotation = null;
quote = null;
comment = null;
element = null;
annotationObj = null;
normalizedRange = null;
sniffedRange = null;
beforeEach(function() {
quote = 'This is some annotated text';
comment = 'This is a comment on an annotation';
element = $('<span />');
normalizedRange = {
text: sinon.stub().returns(quote),
serialize: sinon.stub().returns({})
};
sniffedRange = {
normalize: sinon.stub().returns(normalizedRange)
};
sinon.stub(Range, 'sniff').returns(sniffedRange);
sinon.stub(annotator, 'highlightRange').returns(element);
sinon.spy(annotator, 'publish');
annotationObj = {
text: comment,
ranges: [1]
};
return annotation = annotator.setupAnnotation(annotationObj);
});
afterEach(function() {
return Range.sniff.restore();
});
it("should return the annotation object with a comment", function() {
return assert.equal(annotation.text, comment);
});
it("should return the annotation object with the quoted text", function() {
return assert.equal(annotation.quote, quote);
});
it("should trim whitespace from start and end of quote", function() {
normalizedRange.text.returns('\n\t ' + quote + ' \n');
annotation = annotator.setupAnnotation(annotationObj);
return assert.equal(annotation.quote, quote);
});
it("should set the annotation.ranges", function() {
return assert.deepEqual(annotation.ranges, [{}]);
});
it("should exclude any ranges that could not be normalized", function() {
var e;
e = new Range.RangeError("typ", "msg");
sniffedRange.normalize.throws(e);
annotation = annotator.setupAnnotation({
text: comment,
ranges: [1]
});
return assert.deepEqual(annotation.ranges, []);
});
it("should trigger rangeNormalizeFail for each range that can't be normalized", function() {
var e;
e = new Range.RangeError("typ", "msg");
sniffedRange.normalize.throws(e);
annotator.publish = sinon.spy();
annotation = annotator.setupAnnotation({
text: comment,
ranges: [1]
});
return assert.isTrue(annotator.publish.calledWith('rangeNormalizeFail', [annotation, 1, e]));
});
it("should call Annotator#highlightRange() with the normed range", function() {
return assert.isTrue(annotator.highlightRange.calledWith(normalizedRange));
});
return it("should store the annotation in the highlighted element's data store", function() {
return assert.equal(element.data('annotation'), annotation);
});
});
describe("updateAnnotation", function() {
return it("should publish the 'beforeAnnotationUpdated' and 'annotationUpdated' events", function() {
var annotation;
annotation = {
text: "my annotation comment"
};
sinon.spy(annotator, 'publish');
annotator.updateAnnotation(annotation);
assert.isTrue(annotator.publish.calledWith('beforeAnnotationUpdated', [annotation]));
return assert.isTrue(annotator.publish.calledWith('annotationUpdated', [annotation]));
});
});
describe("deleteAnnotation", function() {
var annotation, div;
annotation = null;
div = null;
beforeEach(function() {
annotation = {
text: "my annotation comment",
highlights: $('<span><em>Hats</em></span><span><em>Gloves</em></span>')
};
return div = $('<div />').append(annotation.highlights);
});
it("should remove the highlights from the DOM", function() {
annotation.highlights.each(function() {
return assert.lengthOf($(this).parent(), 1);
});
annotator.deleteAnnotation(annotation);
return annotation.highlights.each(function() {
return assert.lengthOf($(this).parent(), 0);
});
});
it("should leave the content of the highlights in place", function() {
annotator.deleteAnnotation(annotation);
return assert.equal(div.html(), '<em>Hats</em><em>Gloves</em>');
});
it("should not choke when there are no highlights", function() {
return assert.doesNotThrow((function() {
return annotator.deleteAnnotation({});
}), Error);
});
return it("should publish the 'annotationDeleted' event", function() {
sinon.spy(annotator, 'publish');
annotator.deleteAnnotation(annotation);
return assert.isTrue(annotator.publish.calledWith('annotationDeleted', [annotation]));
});
});
describe("loadAnnotations", function() {
beforeEach(function() {
sinon.stub(annotator, 'setupAnnotation');
return sinon.spy(annotator, 'publish');
});
it("should call Annotator#setupAnnotation for each annotation in the Array", function() {
var annotations;
annotations = [{}, {}, {}, {}];
annotator.loadAnnotations(annotations);
return assert.equal(annotator.setupAnnotation.callCount, 4);
});
it("should publish the annotationsLoaded event with all loaded annotations", function() {
var annotations;
annotations = [{}, {}, {}, {}];
annotator.loadAnnotations(annotations.slice());
return assert.isTrue(annotator.publish.calledWith('annotationsLoaded', [annotations]));
});
return it("should break the annotations into blocks of 10", function() {
var annotations, clock;
clock = sinon.useFakeTimers();
annotations = [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}];
annotator.loadAnnotations(annotations);
assert.equal(annotator.setupAnnotation.callCount, 10);
while (annotations.length > 0) {
clock.tick(10);
}
assert.equal(annotator.setupAnnotation.callCount, 13);
return clock.restore();
});
});
describe("dumpAnnotations", function() {
it("returns false and prints a warning if no Store plugin is active", function() {
sinon.stub(console, 'warn');
assert.isFalse(annotator.dumpAnnotations());
return assert(console.warn.calledOnce);
});
return it("returns the results of the Store plugins dumpAnnotations method", function() {
annotator.plugins.Store = {
dumpAnnotations: function() {
return [1, 2, 3];
}
};
return assert.deepEqual(annotator.dumpAnnotations(), [1, 2, 3]);
});
});
describe("highlightRange", function() {
it("should return a highlight element for every textNode in the range", function() {
var elements, mockRange, text, textNodes;
textNodes = (function() {
var _i, _len, _ref, _results;
_ref = ['hello', 'world'];
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
text = _ref[_i];
_results.push(document.createTextNode(text));
}
return _results;
})();
mockRange = {
textNodes: function() {
return textNodes;
}
};
elements = annotator.highlightRange(mockRange);
assert.lengthOf(elements, 2);
assert.equal(elements[0].className, 'annotator-hl');
assert.equal(elements[0].firstChild, textNodes[0]);
return assert.equal(elements[1].firstChild, textNodes[1]);
});
it("should ignore textNodes that contain only whitespace", function() {
var elements, mockRange, text, textNodes;
textNodes = (function() {
var _i, _len, _ref, _results;
_ref = ['hello', '\n ', ' '];
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
text = _ref[_i];
_results.push(document.createTextNode(text));
}
return _results;
})();
mockRange = {
textNodes: function() {
return textNodes;
}
};
elements = annotator.highlightRange(mockRange);
assert.lengthOf(elements, 1);
assert.equal(elements[0].className, 'annotator-hl');
return assert.equal(elements[0].firstChild, textNodes[0]);
});
return it("should set highlight element class names to its second argument", function() {
var elements, mockRange, text, textNodes;
textNodes = (function() {
var _i, _len, _ref, _results;
_ref = ['hello', 'world'];
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
text = _ref[_i];
_results.push(document.createTextNode(text));
}
return _results;
})();
mockRange = {
textNodes: function() {
return textNodes;
}
};
elements = annotator.highlightRange(mockRange, 'monkeys');
return assert.equal(elements[0].className, 'monkeys');
});
});
describe("highlightRanges", function() {
it("should return a list of highlight elements all highlighted ranges", function() {
var elements, mockRange, ranges, text, textNodes;
textNodes = (function() {
var _i, _len, _ref, _results;
_ref = ['hello', 'world'];
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
text = _ref[_i];
_results.push(document.createTextNode(text));
}
return _results;
})();
mockRange = {
textNodes: function() {
return textNodes;
}
};
ranges = [mockRange, mockRange, mockRange];
elements = annotator.highlightRanges(ranges);
assert.lengthOf(elements, 6);
return assert.equal(elements[0].className, 'annotator-hl');
});
return it("should set highlight element class names to its second argument", function() {
var elements, mockRange, ranges, text, textNodes;
textNodes = (function() {
var _i, _len, _ref, _results;
_ref = ['hello', 'world'];
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
text = _ref[_i];
_results.push(document.createTextNode(text));
}
return _results;
})();
mockRange = {
textNodes: function() {
return textNodes;
}
};
ranges = [mockRange, mockRange, mockRange];
elements = annotator.highlightRanges(ranges, 'monkeys');
return assert.equal(elements[0].className, 'monkeys');
});
});
describe("addPlugin", function() {
var plugin;
plugin = null;
beforeEach(function() {
plugin = {
pluginInit: sinon.spy()
};
return Annotator.Plugin.Foo = sinon.stub().returns(plugin);
});
it("should add and instantiate a plugin of the specified name", function() {
annotator.addPlugin('Foo');
return assert.isTrue(Annotator.Plugin.Foo.calledWith(annotator.element[0], void 0));
});
it("should pass on the provided options", function() {
var options;
options = {
foo: 'bar'
};
annotator.addPlugin('Foo', options);
return assert.isTrue(Annotator.Plugin.Foo.calledWith(annotator.element[0], options));
});
it("should attach the Annotator instance", function() {
annotator.addPlugin('Foo');
return assert.equal(plugin.annotator, annotator);
});
it("should call Plugin#pluginInit()", function() {
annotator.addPlugin('Foo');
return assert(plugin.pluginInit.calledOnce);
});
it("should complain if you try and instantiate a plugin twice", function() {
sinon.stub(console, 'error');
annotator.addPlugin('Foo');
annotator.addPlugin('Foo');
assert.equal(Annotator.Plugin.Foo.callCount, 1);
assert(console.error.calledOnce);
return console.error.restore();
});
return it("should complain if you try and instantiate a plugin that doesn't exist", function() {
sinon.stub(console, 'error');
annotator.addPlugin('Bar');
assert.isFalse(annotator.plugins['Bar'] != null);
assert(console.error.calledOnce);
return console.error.restore();
});
});
describe("showEditor", function() {
beforeEach(function() {
sinon.spy(annotator, 'publish');
sinon.spy(annotator, 'deleteAnnotation');
sinon.spy(annotator.editor, 'load');
return sinon.spy(annotator.editor.element, 'css');
});
it("should call Editor#load() on the Annotator#editor", function() {
var annotation;
annotation = {
text: 'my annotation comment'
};
annotator.showEditor(annotation, {});
return assert.isTrue(annotator.editor.load.calledWith(annotation));
});
it("should set the top/left properties of the Editor#element", function() {
var location;
location = {
top: 20,
left: 20
};
annotator.showEditor({}, location);
return assert.isTrue(annotator.editor.element.css.calledWith(location));
});
return it("should publish the 'annotationEditorShown' event passing the editor and annotations", function() {
var annotation;
annotation = {
text: 'my annotation comment'
};
annotator.showEditor(annotation, {});
return assert(annotator.publish.calledWith('annotationEditorShown', [annotator.editor, annotation]));
});
});
describe("onEditorHide", function() {
it("should publish the 'annotationEditorHidden' event and provide the Editor and annotation", function() {
sinon.spy(annotator, 'publish');
annotator.onEditorHide();
return assert(annotator.publish.calledWith('annotationEditorHidden', [annotator.editor]));
});
return it("should set the Annotator#ignoreMouseup property to false", function() {
annotator.ignoreMouseup = true;
annotator.onEditorHide();
return assert.isFalse(annotator.ignoreMouseup);
});
});
describe("onEditorSubmit", function() {
var annotation;
annotation = null;
beforeEach(function() {
annotation = {
"text": "bah"
};
sinon.spy(annotator, 'publish');
sinon.spy(annotator, 'setupAnnotation');
return sinon.spy(annotator, 'updateAnnotation');
});
return it("should publish the 'annotationEditorSubmit' event and pass the Editor and annotation", function() {
annotator.onEditorSubmit(annotation);
return assert(annotator.publish.calledWith('annotationEditorSubmit', [annotator.editor, annotation]));
});
});
describe("showViewer", function() {
beforeEach(function() {
sinon.spy(annotator, 'publish');
sinon.spy(annotator.viewer, 'load');
return sinon.spy(annotator.viewer.element, 'css');
});
it("should call Viewer#load() on the Annotator#viewer", function() {
var annotations;
annotations = [
{
text: 'my annotation comment'
}
];
annotator.showViewer(annotations, {});
return assert.isTrue(annotator.viewer.load.calledWith(annotations));
});
it("should set the top/left properties of the Editor#element", function() {
var location;
location = {
top: 20,
left: 20
};
annotator.showViewer([], location);
return assert.isTrue(annotator.viewer.element.css.calledWith(location));
});
return it("should publish the 'annotationViewerShown' event passing the viewer and annotations", function() {
var annotations;
annotations = [
{
text: 'my annotation comment'
}
];
annotator.showViewer(annotations, {});
return assert(annotator.publish.calledWith('annotationViewerShown', [annotator.viewer, annotations]));
});
});
describe("startViewerHideTimer", function() {
beforeEach(function() {
return sinon.spy(annotator.viewer, 'hide');
});
it("should call Viewer.hide() on the Annotator#viewer after 250ms", function() {
var clock;
clock = sinon.useFakeTimers();
annotator.startViewerHideTimer();
clock.tick(250);
assert(annotator.viewer.hide.calledOnce);
return clock.restore();
});
return it("should NOT call Viewer.hide() on the Annotator#viewer if @viewerHideTimer is set", function() {
var clock;
clock = sinon.useFakeTimers();
annotator.viewerHideTimer = 60;
annotator.startViewerHideTimer();
clock.tick(250);
assert.isFalse(annotator.viewer.hide.calledOnce);
return clock.restore();
});
});
describe("clearViewerHideTimer", function() {
return it("should clear the @viewerHideTimer property", function() {
annotator.viewerHideTimer = 456;
annotator.clearViewerHideTimer();
return assert.isFalse(annotator.viewerHideTimer);
});
});
describe("checkForStartSelection", function() {
beforeEach(function() {
sinon.spy(annotator, 'startViewerHideTimer');
annotator.mouseIsDown = false;
return annotator.checkForStartSelection();
});
it("should call Annotator#startViewerHideTimer()", function() {
return assert(annotator.startViewerHideTimer.calledOnce);
});
it("should NOT call #startViewerHideTimer() if mouse is over the annotator", function() {
annotator.startViewerHideTimer.reset();
annotator.checkForStartSelection({
target: annotator.viewer.element
});
return assert.isFalse(annotator.startViewerHideTimer.called);
});
return it("should set @mouseIsDown to true", function() {
return assert.isTrue(annotator.mouseIsDown);
});
});
describe("checkForEndSelection", function() {
var mockEvent, mockOffset, mockRanges;
mockEvent = null;
mockOffset = null;
mockRanges = null;
beforeEach(function() {
mockEvent = {
target: document.createElement('span')
};
mockOffset = {
top: 0,
left: 0
};
mockRanges = [{}];
sinon.stub(Util, 'mousePosition').returns(mockOffset);
sinon.stub(annotator.adder, 'show').returns(annotator.adder);
sinon.stub(annotator.adder, 'hide').returns(annotator.adder);
sinon.stub(annotator.adder, 'css').returns(annotator.adder);
sinon.stub(annotator, 'getSelectedRanges').returns(mockRanges);
annotator.mouseIsDown = true;
annotator.selectedRanges = [];
return annotator.checkForEndSelection(mockEvent);
});
afterEach(function() {
return Util.mousePosition.restore();
});
it("should get the current selection from Annotator#getSelectedRanges()", function() {
return assert(annotator.getSelectedRanges.calledOnce);
});
it("should set @mouseIsDown to false", function() {
return assert.isFalse(annotator.mouseIsDown);
});
it("should set the Annotator#selectedRanges property", function() {
return assert.equal(annotator.selectedRanges, mockRanges);
});
it("should display the Annotator#adder if valid selection", function() {
assert(annotator.adder.show.calledOnce);
assert.isTrue(annotator.adder.css.calledWith(mockOffset));
return assert.isTrue(Util.mousePosition.calledWith(mockEvent, annotator.wrapper[0]));
});
it("should hide the Annotator#adder if NOT valid selection", function() {
annotator.adder.hide.reset();
annotator.adder.show.reset();
annotator.getSelectedRanges.returns([]);
annotator.checkForEndSelection(mockEvent);
assert(annotator.adder.hide.calledOnce);
return assert.isFalse(annotator.adder.show.called);
});
it("should hide the Annotator#adder if target is part of the annotator", function() {
var mockNode;
annotator.adder.hide.reset();
annotator.adder.show.reset();
mockNode = document.createElement('span');
mockEvent.target = annotator.viewer.element[0];
sinon.stub(annotator, 'isAnnotator').returns(true);
annotator.getSelectedRanges.returns([
{
commonAncestor: mockNode
}
]);
annotator.checkForEndSelection(mockEvent);
assert.isTrue(annotator.isAnnotator.calledWith(mockNode));
assert.isFalse(annotator.adder.hide.called);
return assert.isFalse(annotator.adder.show.called);
});
return it("should return if @ignoreMouseup is true", function() {
annotator.getSelectedRanges.reset();
annotator.ignoreMouseup = true;
annotator.checkForEndSelection(mockEvent);
return assert.isFalse(annotator.getSelectedRanges.called);
});
});
describe("isAnnotator", function() {
it("should return true if the element is part of the annotator", function() {
var element, elements, _i, _len, _results;
elements = [annotator.viewer.element, annotator.adder, annotator.editor.element.find('ul')];
_results = [];
for (_i = 0, _len = elements.length; _i < _len; _i++) {
element = elements[_i];
_results.push(assert.isTrue(annotator.isAnnotator(element)));
}
return _results;
});
return it("should return false if the element is NOT part of the annotator", function() {
var element, elements, _i, _len, _results;
elements = [null, annotator.element.parent(), document.createElement('span'), annotator.wrapper];
_results = [];
for (_i = 0, _len = elements.length; _i < _len; _i++) {
element = elements[_i];
_results.push(assert.isFalse(annotator.isAnnotator(element)));
}
return _results;
});
});
describe("onHighlightMouseover", function() {
var annotation, element, mockEvent, mockOffset;
element = null;
mockEvent = null;
mockOffset = null;
annotation = null;
beforeEach(function() {
annotation = {
text: "my comment"
};
element = $('<span />').data('annotation', annotation);
mockEvent = {
target: element[0]
};
mockOffset = {
top: 0,
left: 0
};
sinon.stub(Util, 'mousePosition').returns(mockOffset);
sinon.spy(annotator, 'showViewer');
annotator.viewerHideTimer = 60;
return annotator.onHighlightMouseover(mockEvent);
});
afterEach(function() {
return Util.mousePosition.restore();
});
it("should clear the current @viewerHideTimer", function() {
return assert.isFalse(annotator.viewerHideTimer);
});
it("should fetch the current mouse position", function() {
return assert.isTrue(Util.mousePosition.calledWith(mockEvent, annotator.wrapper[0]));
});
return it("should display the Annotation#viewer with annotations", function() {
return assert.isTrue(annotator.showViewer.calledWith([annotation], mockOffset));
});
});
describe("onAdderMousedown", function() {
return it("should set the @ignoreMouseup property to true", function() {
annotator.ignoreMouseup = false;
annotator.onAdderMousedown();
return assert.isTrue(annotator.ignoreMouseup);
});
});
describe("onAdderClick", function() {
var annotation, element, mockOffset, mockSubscriber, normalizedRange, quote, sniffedRange;
annotation = null;
mockOffset = null;
mockSubscriber = null;
quote = null;
element = null;
normalizedRange = null;
sniffedRange = null;
beforeEach(function() {
annotation = {
text: "test"
};
quote = 'This is some annotated text';
element = $('<span />').addClass('annotator-hl');
mockOffset = {
top: 0,
left: 0
};
mockSubscriber = sinon.spy();
annotator.subscribe('annotationCreated', mockSubscriber);
normalizedRange = {
text: sinon.stub().returns(quote),
serialize: sinon.stub().returns({})
};
sniffedRange = {
normalize: sinon.stub().returns(normalizedRange)
};
sinon.stub(annotator.adder, 'hide');
sinon.stub(annotator.adder, 'position').returns(mockOffset);
sinon.stub(annotator, 'createAnnotation').returns(annotation);
sinon.spy(annotator, 'setupAnnotation');
sinon.stub(annotator, 'deleteAnnotation');
sinon.stub(annotator, 'showEditor');
sinon.stub(Range, 'sniff').returns(sniffedRange);
sinon.stub(annotator, 'highlightRange').returns(element);
sinon.spy(element, 'addClass');
annotator.selectedRanges = ['foo'];
return annotator.onAdderClick();
});
afterEach(function() {
return Range.sniff.restore();
});
it("should hide the Annotation#adder", function() {
return assert(annotator.adder.hide.calledOnce);
});
it("should create a new annotation", function() {
return assert(annotator.createAnnotation.calledOnce);
});
it("should set up the annotation", function() {
return assert.isTrue(annotator.setupAnnotation.calledWith(annotation));
});
it("should display the Annotation#editor in the same place as the Annotation#adder", function() {
assert(annotator.adder.position.calledOnce);
return assert.isTrue(annotator.showEditor.calledWith(annotation, mockOffset));
});
it("should add temporary highlights to the document to show the user what they selected", function() {
assert.isTrue(annotator.highlightRange.calledWith(normalizedRange));
return assert.equal(element[0].className, 'annotator-hl annotator-hl-temporary');
});
it("should persist the temporary highlights if the annotation is saved", function() {
annotator.publish('annotationEditorSubmit');
return assert.equal(element[0].className, 'annotator-hl');
});
it("should trigger the 'annotationCreated' event if the edit is saved", function() {
annotator.onEditorSubmit(annotation);
return assert.isTrue(mockSubscriber.calledWith(annotation));
});
return it("should call Annotator#deleteAnnotation if editing is cancelled", function() {
annotator.onEditorHide();
annotator.onEditorSubmit();
assert.isFalse(mockSubscriber.calledWith('annotationCreated'));
return assert.isTrue(annotator.deleteAnnotation.calledWith(annotation));
});
});
describe("onEditAnnotation", function() {
var annotation, mockOffset, mockSubscriber;
annotation = null;
mockOffset = null;
mockSubscriber = null;
beforeEach(function() {
annotation = {
text: "my mock annotation"
};
mockOffset = {
top: 0,
left: 0
};
mockSubscriber = sinon.spy();
sinon.spy(annotator, "showEditor");
sinon.spy(annotator.viewer, "hide");
sinon.stub(annotator.viewer.element, "position").returns(mockOffset);
sinon.spy(annotator, "updateAnnotation");
return annotator.onEditAnnotation(annotation);
});
it("should display the Annotator#editor in the same positions as Annotatorviewer", function() {
assert(annotator.viewer.hide.calledOnce);
return assert.isTrue(annotator.showEditor.calledWith(annotation, mockOffset));
});
it("should call 'updateAnnotation' event if the edit is saved", function() {
annotator.onEditorSubmit(annotation);
return assert.isTrue(annotator.updateAnnotation.calledWith(annotation));
});
return it("should not call 'updateAnnotation' if editing is cancelled", function() {
annotator.onEditorHide();
annotator.onEditorSubmit(annotation);
return assert.isFalse(annotator.updateAnnotation.calledWith(annotation));
});
});
return describe("onDeleteAnnotation", function() {
return it("should pass the annotation on to Annotator#deleteAnnotation()", function() {
var annotation;
annotation = {
text: "my mock annotation"
};
sinon.spy(annotator, "deleteAnnotation");
sinon.spy(annotator.viewer, "hide");
annotator.onDeleteAnnotation(annotation);
assert(annotator.viewer.hide.calledOnce);
return assert.isTrue(annotator.deleteAnnotation.calledWith(annotation));
});
});
});
describe("Annotator.noConflict()", function() {
var _Annotator;
_Annotator = null;
beforeEach(function() {
return _Annotator = Annotator;
});
afterEach(function() {
return window.Annotator = _Annotator;
});
it("should restore the value previously occupied by window.Annotator", function() {
Annotator.noConflict();
return assert.isUndefined(window.Annotator);
});
return it("should return the Annotator object", function() {
var result;
result = Annotator.noConflict();
return assert.equal(result, _Annotator);
});
});
describe("Annotator.supported()", function() {
it("should return true if the browser has window.getSelection method", function() {
window.getSelection = function() {};
return assert.isTrue(Annotator.supported());
});
return xit("should return false if the browser has no window.getSelection method", function() {
window.getSelection = void 0;
return assert.isFalse(Annotator.supported());
});
});
/*
//@ sourceMappingURL=annotator_spec.map
*/