Commit d217c54e authored by Randall Leeds's avatar Randall Leeds

Merge branch 'fuzzy-anchoring-3' into develop

Conflicts:
	h/assets.py
	h/js/inject/host.coffee
	h/js/services.coffee
parents b053edc3 10777813
......@@ -34,20 +34,24 @@ class Annotator.Host extends Annotator
if not @plugins[name]
this.addPlugin(name, opts)
# Scan the document text with the DOM Text libraries
this.scanDocument "Annotator initialized"
_setupXDM: ->
# Set up the bridge plugin, which bridges the main annotation methods
# between the host page and the panel widget.
whitelist = ['diffHTML', 'quote', 'ranges', 'target']
this.addPlugin 'Bridge',
origin: '*'
window: @frame[0].contentWindow
formatter: (annotation) =>
formatted = {}
for k, v of annotation when k in ['quote', 'ranges']
for k, v of annotation when k in whitelist
formatted[k] = v
formatted
parser: (annotation) =>
parsed = {}
for k, v of annotation when k in ['quote', 'ranges']
for k, v of annotation when k in whitelist
parsed[k] = v
parsed
......@@ -104,16 +108,7 @@ class Annotator.Host extends Annotator
$(this).removeClass('annotator-hl-active')
)
.bind('getHref', =>
uri = decodeURIComponent document.location.href
if document.location.hash
uri = uri.slice 0, (-1 * location.hash.length)
$('meta[property^="og:url"]').each ->
uri = decodeURIComponent this.content
$('link[rel^="canonical"]').each ->
uri = decodeURIComponent this.href
return uri
)
.bind('getHref', => this.getHref())
.bind('getMaxBottom', =>
sel = '*' + (":not(.annotator-#{x})" for x in [
......@@ -140,12 +135,24 @@ class Annotator.Host extends Annotator
$('html, body').stop().animate {scrollTop: y}, 600
)
scanDocument: (reason = "something happened") =>
try
console.log "Analyzing host frame, because " + reason + "..."
r = @domMatcher.scan()
scanTime = r.time
console.log "Traversal+scan took " + scanTime + " ms."
catch e
console.log e.message
console.log e.stack
_setupWrapper: ->
@wrapper = @element
.on 'mouseup', =>
if not @ignoreMouseup
setTimeout =>
unless @selectedRanges?.length then @panel?.notify method: 'back'
this._setupMatching()
@domMatcher.setRootNode @wrapper[0]
this
_setupDocumentEvents: ->
......
......@@ -61,17 +61,20 @@ class Hypothesis extends Annotator
$window = @element.injector().get '$window'
drafts = @element.injector().get 'drafts'
# Set up the bridge plugin, which bridges the main annotation methods
# between the host page and the panel widget.
whitelist = ['diffHTML', 'quote', 'ranges', 'target']
this.addPlugin 'Bridge',
origin: $location.search().xdm
window: $window.parent
formatter: (annotation) =>
formatted = {}
for k, v of annotation when k in ['quote', 'ranges']
for k, v of annotation when k in whitelist
formatted[k] = v
formatted
parser: (annotation) =>
parsed = {}
for k, v of annotation when k in ['quote', 'ranges']
for k, v of annotation when k in whitelist
parsed[k] = v
parsed
......@@ -203,12 +206,15 @@ class Hypothesis extends Annotator
_setupViewer: -> this
_setupEditor: -> this
# (Optionally) put some HTML formatting around a quote
getHtmlQuote: (quote) -> quote
setupAnnotation: (annotation) ->
# This is needed until Annotator stops assuming ranges and highlights
# are always added.
unless annotation.ranges?
# Delagate to Annotator implementation after we give it a valid array of
# targets. This is needed until Annotator stops assuming targets need to be
unless annotation.target?
annotation.highlights = []
annotation.ranges = []
annotation.target = []
@plugins.Threading.thread annotation
annotation
......
/*
** Annotator 1.2.6-dev-c4fcdfe
** Annotator 1.2.5-dev-5a5e03a
** https://github.com/okfn/annotator/
**
** Copyright 2012 Aron Carroll, Rufus Pollock, and Nick Stenning.
** Dual licensed under the MIT and GPLv3 licenses.
** https://github.com/okfn/annotator/blob/master/LICENSE
**
** Built at: 2013-01-29 11:20:24Z
** Built at: 2013-02-25 17:23:26Z
*/
(function() {
......
This diff is collapsed.
/*
** Annotator 1.2.6-dev-c4fcdfe
** Annotator 1.2.5-dev-5a5e03a
** https://github.com/okfn/annotator/
**
** Copyright 2012 Aron Carroll, Rufus Pollock, and Nick Stenning.
** Dual licensed under the MIT and GPLv3 licenses.
** https://github.com/okfn/annotator/blob/master/LICENSE
**
** Built at: 2013-01-29 11:20:26Z
** Built at: 2013-02-25 17:23:22Z
*/
(function() {
......
/*
** Annotator 1.2.6-dev-c4fcdfe
** Annotator 1.2.5-dev-859d4e3
** https://github.com/okfn/annotator/
**
** Copyright 2012 Aron Carroll, Rufus Pollock, and Nick Stenning.
** Dual licensed under the MIT and GPLv3 licenses.
** https://github.com/okfn/annotator/blob/master/LICENSE
**
** Built at: 2013-01-29 11:20:27Z
** Built at: 2013-03-13 14:59:29Z
*/
(function() {
......@@ -65,10 +65,17 @@
};
Store.prototype.annotationCreated = function(annotation) {
var _this = this;
var quote, quoteHTML,
_this = this;
if (__indexOf.call(this.annotations, annotation) < 0) {
this.registerAnnotation(annotation);
quote = annotation.quote;
quoteHTML = annotation.quoteHTML;
delete annotation.quote;
delete annotation.quoteHTML;
return this._apiRequest('create', annotation, function(data) {
annotation.quote = quote;
annotation.quoteHTML = quoteHTML;
if (!(data.id != null)) {
console.warn(Annotator._t("Warning: No ID returned from server for annotation "), annotation);
}
......@@ -80,9 +87,16 @@
};
Store.prototype.annotationUpdated = function(annotation) {
var _this = this;
var quote, quoteHTML,
_this = this;
if (__indexOf.call(this.annotations, annotation) >= 0) {
quote = annotation.quote;
quoteHTML = annotation.quoteHTML;
delete annotation.quote;
delete annotation.quoteHTML;
return this._apiRequest('update', annotation, (function(data) {
annotation.quote = quote;
annotation.quoteHTML = quoteHTML;
return _this.updateAnnotation(annotation, data);
}));
}
......@@ -180,7 +194,7 @@
Store.prototype._urlFor = function(action, id) {
var replaceWith, url;
replaceWith = id != null ? '/' + id : '';
url = this.options.prefix != null ? this.options.prefix : '';
url = this.options.prefix || '/';
url += this.options.urls[action];
url = url.replace(/\/:id/, replaceWith);
return url;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
# Naive text matcher
class window.DTM_ExactMatcher
constructor: ->
@distinct = true
@caseSensitive = false
setDistinct: (value) -> @distinct = value
setCaseSensitive: (value) -> @caseSensitive = value
search: (text, pattern) ->
# console.log "Searching for '" + pattern + "' in '" + text + "'."
pLen = pattern.length
results = []
index = 0
unless @caseSensitive
text = text.toLowerCase()
pattern = pattern.toLowerCase()
while (i = text.indexOf pattern) > -1
do =>
# console.log "Found '" + pattern + "' @ " + i +
# " (=" + (index + i) + ")"
results.push
start: index + i
end: index + i + pLen
if @distinct
text = text.substr i + pLen
index += i + pLen
else
text = text.substr i + 1
index += i + 1
results
class window.DTM_RegexMatcher
constructor: ->
@caseSensitive = false
setCaseSensitive: (value) -> @caseSensitive = value
search: (text, pattern) ->
re = new RegExp pattern, if @caseSensitive then "g" else "gi"
{ start: m.index, end: m.index + m[0].length } while m = re.exec text
# diff-match-patch - based text matcher
class window.DTM_DMPMatcher
constructor: ->
@dmp = new diff_match_patch
@dmp.Diff_Timeout = 0
@caseSensitive = false
_reverse: (text) -> text.split("").reverse().join ""
# Use this to get the max allowed pattern length.
# Trying to use a longer pattern will give an error.
getMaxPatternLength: -> @dmp.Match_MaxBits
# The following example is a classic dilemma.
# There are two potential matches, one is close to the expected location
# but contains a one character error, the other is far from the expected
# location but is exactly the pattern sought after:
#
# match_main("abc12345678901234567890abbc", "abc", 26)
#
# Which result is returned (0 or 24) is determined by the
# MatchDistance property.
#
# An exact letter match which is 'distance' characters away
# from the fuzzy location would score as a complete mismatch.
# For example, a distance of '0' requires the match be at the exact
# location specified, whereas a threshold of '1000' would require
# a perfect match to be within 800 characters of the expected location
# to be found using a 0.8 threshold (see below).
#
# The larger MatchDistance is, the slower search may take to compute.
#
# This variable defaults to 1000.
setMatchDistance: (distance) -> @dmp.Match_Distance = distance
getMatchDistance: -> @dmp.Match_Distance
# MatchThreshold determines the cut-off value for a valid match.
#
# If Match_Threshold is closer to 0, the requirements for accuracy
# increase. If Match_Threshold is closer to 1 then it is more likely
# that a match will be found. The larger Match_Threshold is, the slower
# search may take to compute.
#
# This variable defaults to 0.5.
setMatchThreshold: (threshold) -> @dmp.Match_Threshold = threshold
getMatchThreshold: -> @dmp.Match_Threshold
getCaseSensitive: -> caseSensitive
setCaseSensitive: (value) -> @caseSensitive = value
# Given a text to search, a pattern to search for and an
# expected location in the text near which to find the pattern,
# return the location which matches closest.
#
# The function will search for the best match based on both the number
# of character errors between the pattern and the potential match,
# as well as the distance between the expected location and the
# potential match.
#
# If no match is found, the function returns null.
search: (text, pattern, expectedStartLoc = 0, options = {}) ->
# console.log "In dtm search. text: '" + text + "', pattern: '" + pattern +
# "', expectedStartLoc: " + expectedStartLoc + ", options:"
# console.log options
if expectedStartLoc < 0 then throw new Error "Can't search at negative indices!"
unless @caseSensitive
text = text.toLowerCase()
pattern = pattern.toLowerCase()
pLen = pattern.length
maxLen = @getMaxPatternLength()
if pLen <= maxLen
result = @searchForSlice text, pattern, expectedStartLoc
else
startSlice = pattern.substr 0, maxLen
startPos = @searchForSlice text, startSlice, expectedStartLoc
if startPos?
startLen = startPos.end - startPos.start
endSlice = pattern.substr pLen - maxLen, maxLen
endLoc = startPos.start + pLen - maxLen
endPos = @searchForSlice text, endSlice, endLoc
if endPos?
endLen = endPos.end - endPos.start
matchLen = endPos.end - startPos.start
startIndex = startPos.start
endIndex = endPos.end
if pLen*0.5 <= matchLen <= pLen*1.5
result =
start: startIndex
end: endPos.end
# data:
# startError: startPos.data.error
# endError: endPos.data.error
# uncheckedMidSection: Math.max 0, matchLen - startLen - endLen
# lengthError: matchLen - pLen
# else
# console.log "Sorry, matchLen (" + matchLen + ") is not between " +
# 0.5*pLen + " and " + 1.5*pLen
# else
# console.log "endSlice ('" + endSlice + "') not found"
# else
# console.log "startSlice ('" + startSlice + "') not found"
unless result? then return []
if options.withLevenhstein or options.withDiff
found = text.substr result.start, result.end - result.start
result.diff = @dmp.diff_main pattern, found
if options.withLevenshstein
result.lev = @dmp.diff_levenshtein result.diff
if options.withDiff
@dmp.diff_cleanupSemantic result.diff
result.diffHTML = @dmp.diff_prettyHtml result.diff
[result]
# Compare two string slices, get Levenhstein and visual diff
compare: (text1, text2) ->
unless (text1? and text2?)
throw new Error "Can not compare non-existing strings!"
result = {}
result.diff = @dmp.diff_main text1, text2
result.lev = @dmp.diff_levenshtein result.diff
result.errorLevel = result.lev / text1.length
@dmp.diff_cleanupSemantic result.diff
result.diffHTML = @dmp.diff_prettyHtml result.diff
result
# ============= Private part ==========================================
# You don't need to call the functions below this point manually
searchForSlice: (text, slice, expectedStartLoc) ->
# console.log "searchForSlice: '" + text + "', '" + slice + "', " +
# expectedStartLoc
r1 = @dmp.match_main text, slice, expectedStartLoc
startIndex = r1.index
if startIndex is -1 then return null
txet = @_reverse text
nrettap = @_reverse slice
expectedEndLoc = startIndex + slice.length
expectedDneLoc = text.length - expectedEndLoc
r2 = @dmp.match_main txet, nrettap, expectedDneLoc
dneIndex = r2.index
endIndex = text.length - dneIndex
result =
start: startIndex
end: endIndex
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment