View
@@ -21,80 +21,23 @@ class Annotator.Plugin.TextSelection extends Annotator.Plugin
})
super
# Code used to create annotations around text ranges =====================
# Gets the current selection excluding any nodes that fall outside of
# the @wrapper. Then returns and Array of NormalizedRange instances.
#
# Examples
#
# # A selection inside @wrapper
# annotation.getSelectedRanges()
# # => Returns [NormalizedRange]
#
# # A selection outside of @wrapper
# annotation.getSelectedRanges()
# # => Returns []
#
# Returns Array of NormalizedRange instances.
_getSelectedRanges: ->
selection = Annotator.Util.getGlobal().getSelection()
ranges = []
rangesToIgnore = []
unless selection.isCollapsed
ranges = for i in [0...selection.rangeCount]
r = selection.getRangeAt(i)
browserRange = new Annotator.Range.BrowserRange(r)
normedRange = browserRange.normalize().limit @annotator.wrapper[0]
# If the new range falls fully outside the wrapper, we
# should add it back to the document but not return it from
# this method
rangesToIgnore.push(r) if normedRange is null
normedRange
# BrowserRange#normalize() modifies the DOM structure and deselects the
# underlying text as a result. So here we remove the selected ranges and
# reapply the new ones.
selection.removeAllRanges()
for r in rangesToIgnore
selection.addRange(r)
# Remove any ranges that fell outside of @wrapper.
$.grep ranges, (range) ->
# Add the normed range back to the selection if it exists.
selection.addRange(range.toRange()) if range
range
# This is called when the mouse is released.
# Checks to see if a selection has been made on mouseup and if so,
# Checks to see if a selection been made on mouseup and if so,
# calls Annotator's onSuccessfulSelection method.
#
# event - The event triggered this. Usually it's a mouseup Event,
# but that's not necessary. The coordinates will be used,
# if they are present. If the event (or the coordinates)
# are missing, new coordinates will be generated, based on the
# selected ranges.
# but that's not necessary.
#
# Returns nothing.
checkForEndSelection: (event = {}) =>
# Get the currently selected ranges.
selectedRanges = @_getSelectedRanges()
for range in selectedRanges
container = range.commonAncestor
return if @annotator.isAnnotator(container)
if selectedRanges.length
event.segments = []
for r in selectedRanges
event.segments.push
type: "text range"
range: r
selection = Annotator.Util.getGlobal().getSelection()
ranges = for i in [0...selection.rangeCount]
r = selection.getRangeAt(0)
if r.collapsed then continue else r
if ranges.length
event.ranges = ranges
@annotator.onSuccessfulSelection event
else
@annotator.onFailedSelection event
View
@@ -1,6 +1,5 @@
Promise = require('es6-promise').Promise
Promise = global.Promise ? require('es6-promise').Promise
Annotator = require('annotator')
require('../monkey')
Guest = require('../guest')
assert = chai.assert
@@ -23,23 +22,6 @@ describe 'Guest', ->
on: sandbox.stub()
sync: sandbox.stub()
# Mock out the anchoring plugin. Oh how I wish I didn't have to do crazy
# shit like this.
Annotator.Plugin.EnhancedAnchoring = -> {
pluginInit: ->
@annotator.anchoring = this
_scan: sandbox.stub()
getHighlights: sandbox.stub().returns([])
getAnchors: sandbox.stub().returns([])
createAnchor: sandbox.spy (annotation, target) ->
anchor = "anchor for " + target
annotation.anchors.push anchor
result: anchor
}
Annotator.Plugin.CrossFrame = -> fakeCrossFrame
sandbox.spy(Annotator.Plugin, 'CrossFrame')
@@ -195,36 +177,38 @@ describe 'Guest', ->
describe 'on "focusAnnotations" event', ->
it 'focuses any annotations with a matching tag', ->
highlight0 = {setFocused: sandbox.stub()}
highlight1 = {setFocused: sandbox.stub()}
guest = createGuest()
highlights = [
{annotation: {$$tag: 'tag1'}, setFocused: sandbox.stub()}
{annotation: {$$tag: 'tag2'}, setFocused: sandbox.stub()}
guest.anchored = [
{annotation: {$$tag: 'tag1'}, highlight: highlight0}
{annotation: {$$tag: 'tag2'}, highlight: highlight1}
]
guest.anchoring.getHighlights.returns(highlights)
emitGuestEvent('focusAnnotations', 'ctx', ['tag1'])
assert.called(highlights[0].setFocused)
assert.calledWith(highlights[0].setFocused, true)
assert.called(highlight0.setFocused)
assert.calledWith(highlight0.setFocused, true)
it 'unfocuses any annotations without a matching tag', ->
highlight0 = {setFocused: sandbox.stub()}
highlight1 = {setFocused: sandbox.stub()}
guest = createGuest()
highlights = [
{annotation: {$$tag: 'tag1'}, setFocused: sandbox.stub()}
{annotation: {$$tag: 'tag2'}, setFocused: sandbox.stub()}
guest.anchored = [
{annotation: {$$tag: 'tag1'}, highlight: highlight0}
{annotation: {$$tag: 'tag2'}, highlight: highlight1}
]
guest.anchoring.getHighlights.returns(highlights)
emitGuestEvent('focusAnnotations', 'ctx', ['tag1'])
assert.called(highlights[1].setFocused)
assert.calledWith(highlights[1].setFocused, false)
assert.called(highlight1.setFocused)
assert.calledWith(highlight1.setFocused, false)
describe 'on "scrollToAnnotation" event', ->
it 'scrolls to the anchor with the matching tag', ->
highlight = {scrollToView: sandbox.stub()}
guest = createGuest()
anchors = [
{annotation: {$$tag: 'tag1'}, scrollToView: sandbox.stub()}
guest.anchored = [
{annotation: {$$tag: 'tag1'}, highlight: highlight}
]
guest.anchoring.getAnchors.returns(anchors)
emitGuestEvent('scrollToAnnotation', 'ctx', 'tag1')
assert.called(anchors[0].scrollToView)
assert.called(highlight.scrollToView)
describe 'on "getDocumentInfo" event', ->
guest = null
@@ -294,28 +278,36 @@ describe 'Guest', ->
guest.createAnnotation({})
assert.called(fakeCrossFrame.sync)
it 'calls sync for setupAnnotation', ->
it 'calls sync for setupAnnotation', (done) ->
guest = createGuest()
guest.setupAnnotation({ranges: []})
assert.called(fakeCrossFrame.sync)
describe 'Annotator monkey patch', ->
describe 'setupAnnotation()', ->
it "doesn't declare annotation without targets as orphans", ->
guest = createGuest()
annotation = target: []
guest.setupAnnotation(annotation)
guest.plugins.Document = {uri: -> 'http://example.com'}
guest.setupAnnotation({})
setTimeout ->
assert.called(fakeCrossFrame.sync)
done()
describe 'setupAnnotation()', ->
it "doesn't declare annotation without targets as orphans", (done) ->
guest = createGuest()
annotation = target: []
guest.setupAnnotation(annotation)
setTimeout ->
assert.isFalse !!annotation.$orphan
done()
it "doesn't declare annotations with a working target as orphans", ->
guest = createGuest()
annotation = target: ["test target"]
guest.setupAnnotation(annotation)
it "doesn't declare annotations with a working target as orphans", (done) ->
guest = createGuest()
annotation = target: ["test target"]
guest.setupAnnotation(annotation)
setTimeout ->
assert.isFalse !!annotation.$orphan
done()
it "declares annotations with broken targets as orphans", ->
guest = createGuest()
guest.anchoring.createAnchor = -> result: null
annotation = target: ["broken target"]
guest.setupAnnotation(annotation)
it "declares annotations with broken targets as orphans", (done) ->
guest = createGuest()
sandbox.stub(guest, 'anchorTarget').returns(Promise.reject())
annotation = target: [{selector: 'broken selector'}]
guest.setupAnnotation(annotation)
setTimeout ->
assert !!annotation.$orphan
done()
View
@@ -1,32 +1,22 @@
Annotator = require('annotator')
$ = Annotator.$
th = require('../texthighlights')
highlight = require('../highlight')
assert = chai.assert
sinon.assert.expose(assert, prefix: '')
describe 'Annotator.Plugin.TextHighlight', ->
describe 'TextHighlight', ->
sandbox = null
scrollTarget = null
createTestHighlight = ->
anchor =
id: "test anchor"
annotation: "test annotation"
anchoring:
id: "test anchoring manager"
annotator:
id: "test annotator"
element:
delegate: sinon.spy()
new th.TextHighlight anchor, "test page", "test range"
new highlight.TextHighlight "test range"
beforeEach ->
sandbox = sinon.sandbox.create()
sandbox.stub th.TextHighlight, 'highlightRange',
sandbox.stub highlight.TextHighlight, 'highlightRange',
(normedRange, cssClass) ->
hl = document.createElement "hl"
hl.appendChild document.createTextNode "test highlight span"
@@ -43,17 +33,12 @@ describe 'Annotator.Plugin.TextHighlight', ->
describe "constructor", ->
it 'wraps a highlight span around the given range', ->
hl = createTestHighlight()
assert.calledWith th.TextHighlight.highlightRange, "test range"
assert.calledWith highlight.TextHighlight.highlightRange, "test range"
it 'stores the created highlight spans in _highlights', ->
hl = createTestHighlight()
assert.equal hl._highlights.textContent, "test highlight span"
it "assigns the annotation as data to the highlight span", ->
hl = createTestHighlight()
annotation = Annotator.$(hl._highlights).data "annotation"
assert.equal annotation, "test annotation"
describe "getBoundingClientRect", ->
it 'returns the bounding box of all the highlight client rectangles', ->
@@ -86,7 +71,7 @@ describe 'Annotator.Plugin.TextHighlight', ->
fakeHighlights = rects.map (r) ->
return getBoundingClientRect: -> r
hl = _highlights: fakeHighlights
result = th.TextHighlight.prototype.getBoundingClientRect.call(hl)
result = highlight.TextHighlight.prototype.getBoundingClientRect.call(hl)
assert.equal(result.left, 10)
assert.equal(result.top, 10)
assert.equal(result.right, 30)
View
@@ -1,4 +1,4 @@
Promise = require('es6-promise').Promise
Promise = global.Promise ? require('es6-promise').Promise
{module, inject} = require('angular-mock')
assert = chai.assert
View

This file was deleted.

Oops, something went wrong.
View

This file was deleted.

Oops, something went wrong.
View

This file was deleted.

Oops, something went wrong.
View

This file was deleted.

Oops, something went wrong.
View

This file was deleted.

Oops, something went wrong.
View
@@ -134,6 +134,13 @@ $base-font-size: 14px;
@include transition(none !important);
}
.annotator-placeholder {
opacity: 0;
position: absolute;
top: 50%;
z-index: -1;
}
//CONTROLBAR STUFF////////////////////////////////
.annotator-frame .annotator-toolbar {
View
@@ -1,27 +1,8 @@
@import "variables";
// Newer versions of PDFjs use apply a transparency to the textLayer element.
// In order for our highlights to be visible we need to use solid colors. The
// PDFjs plugin provides a compatibility class to the PDFjs container that
// we can use to detect this.
.has-transparent-text-layer {
.annotator-highlights-always-on & .annotator-hl {
background: rgba($highlight-color, 1);
.annotator-hl {
background-color: rgba($highlight-color-second, 1);
}
.annotator-hl .annotator-hl {
background-color: rgba($highlight-color-third, 1);
}
}
.annotator-hl.annotator-hl-focused {
background-color: rgb(192, 236, 253);
.annotator-hl {
background-color: transparent !important;
}
}
// In order for our highlights to be visible we need to use solid colors.
#viewer.has-transparent-text-layer .textLayer {
opacity: 1;
::selection { background: rgba(0, 0, 255, .2); }
::-moz-selection { background: rgba(0 , 0 , 255, .2); }
}
View
@@ -1,4 +1,52 @@
<div class="tab-pane" title="Account">
<form class="account-form form"
name="changeEmailForm"
ng-submit="changeEmailSubmit(changeEmailForm)"
novalidate form-validate>
<h2 class="form-heading"><span>Change Your Email Address</span></h2>
<p class="form-description">Your current email address is: <strong ng-bind="email"></strong>.</p>
<div class="form-field">
<label class="form-label" for="field-email">New Email Address:</label>
<input id="field-email" class="form-input" type="email" name="email" required ng-model="changeEmail.email" />
<ul class="form-error-list">
<li class="form-error" ng-show="changeEmailForm.email.$error.required">Please enter your new email address.</li>
<li class="form-error" ng-show="changeEmailForm.email.$error.email">Please enter a valid email address.</li>
<li class="form-error" ng-show="changeEmailForm.email.$error.response">{{changeEmailForm.email.responseErrorMessage}}</li>
</ul>
</div>
<div class="form-field">
<label class="form-label" for="field-emailAgain">Enter Your New Email Address Again:</label>
<input id="field-emailAgain" class="form-input" type="email" name="emailAgain" required ng-model="changeEmail.emailAgain" />
<ul class="form-error-list">
<li class="form-error" ng-show="changeEmailForm.emailAgain.$error.required">Please enter your new email address twice.</li>
<li class="form-error" ng-show="changeEmailForm.emailAgain.$error.email">Please enter a valid email address.</li>
<li class="form-error" ng-show="changeEmailForm.emailAgain.$error.response">{{changeEmailForm.emailAgain.responseErrorMessage}}</li>
</ul>
</div>
<div class="form-field">
<label class="form-label" for="field-pwd">Password:</label>
<input id="field-pwd" class="form-input" type="password" name="pwd" required ng-model="changeEmail.pwd" />
<ul class="form-error-list">
<li class="form-error" ng-show="changeEmailForm.pwd.$error.required">Please enter your password.</li>
<li class="form-error" ng-show="changeEmailForm.pwd.$error.minlength">Your password does not match the one we have on record.</li>
<li class="form-error" ng-show="changeEmailForm.pwd.$error.response">{{changeEmailForm.pwd.responseErrorMessage}}</li>
</ul>
</div>
<div class="form-actions">
<div class="form-actions-buttons">
<button class="btn" type="submit"
status-button="changeEmailForm">Update</button>
</div>
</div>
</form>
<form class="account-form form"
name="changePasswordForm"
ng-submit="submit(changePasswordForm)"
View
@@ -9,9 +9,12 @@
"clean-css": "2.2.2",
"coffee-script": "1.7.1",
"coffeeify": "^1.0.0",
"diff-match-patch": "^1.0.0",
"dom-seek": "^1.0.0-beta.1",
"es6-promise": "^2.1.0",
"extend": "^2.0.0",
"hammerjs": "^2.0.4",
"node-iterator-shim": "^1.0.0-beta.3",
"node-uuid": "^1.4.3",
"raf": "^2.0.4",
"strip-bomify": "^0.1.0",
@@ -54,16 +57,11 @@
"annotator-auth": "./h/static/scripts/vendor/annotator.auth.js",
"angular": "./h/static/scripts/vendor/angular.js",
"angular-mock": "./h/static/scripts/vendor/angular-mocks.js",
"diff-match-patch": "./h/static/scripts/vendor/diff_match_patch_uncompressed.js",
"dom-text-mapper": "./h/static/scripts/vendor/dom_text_mapper.js",
"dom-text-matcher": "./h/static/scripts/vendor/dom_text_matcher.js",
"es6-promise": "./node_modules/es6-promise/dist/es6-promise.js",
"hammerjs": "./node_modules/hammerjs/hammer.js",
"jquery": "./h/static/scripts/vendor/jquery.js",
"jquery-scrollintoview": "./h/static/scripts/vendor/jquery.scrollintoview.js",
"jschannel": "./h/static/scripts/vendor/jschannel.js",
"page-text-mapper-core": "./h/static/scripts/vendor/page_text_mapper_core.js",
"text-match-engines": "./h/static/scripts/vendor/text_match_engines.js"
"jschannel": "./h/static/scripts/vendor/jschannel.js"
},
"browserify-shim": {
"annotator": {
@@ -85,9 +83,6 @@
]
},
"angular-mock": "global:angular.mock",
"diff-match-patch": "diff_match_patch",
"dom-text-mapper": "DomTextMapper",
"dom-text-matcher": "DomTextMatcher",
"es6-promise": "ES6Promise",
"hammerjs": "Hammer",
"jquery": "$",
@@ -97,7 +92,6 @@
]
},
"jschannel": "Channel",
"page-text-mapper-core": "PageTextMapperCore",
"text-match-engines": "TextMatchEngines"
}
}
View
@@ -46,6 +46,7 @@ def run_tests(self):
'cryptography>=0.7',
'deform>=0.9,<1.0',
'elasticsearch>=1.1.0',
'gevent>=1.0.2,<1.1.0',
'gnsq>=0.2.0,<0.3.0',
'gunicorn>=19.2,<20',
'horus>=0.9.15',
@@ -62,7 +63,7 @@ def run_tests(self):
'python-statsd>=1.7.0,<1.8.0',
'pyramid_webassets>=0.9,<1.0',
'pyramid-jinja2>=2.3.3',
'raven>=5.1.1,<5.2.0',
'raven>=5.3.0,<5.4.0',
'requests>=2.2.1',
'ws4py>=0.3,<0.4',