Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
coopwire-hypothesis
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
孙灵跃 Leon Sun
coopwire-hypothesis
Commits
d87b94bf
Commit
d87b94bf
authored
Mar 02, 2013
by
csillag
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added dom-text-* libraries. Annotations are now saved with full selectors.
parent
cbadb7cc
Changes
5
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
913 additions
and
18 deletions
+913
-18
host.coffee
h/js/inject/host.coffee
+6
-0
services.coffee
h/js/services.coffee
+8
-0
annotator.js
h/lib/annotator.js
+36
-18
dom_text_mapper.coffee
h/lib/dom_text_mapper.coffee
+661
-0
dom_text_matcher.coffee
h/lib/dom_text_matcher.coffee
+202
-0
No files found.
h/js/inject/host.coffee
View file @
d87b94bf
...
...
@@ -146,6 +146,11 @@ class Annotator.Host extends Annotator
scrollTop
:
(
y
)
=>
$
(
'html, body'
).
stop
().
animate
{
scrollTop
:
y
},
600
scanDocument
:
(
reason
=
"something happened"
)
=>
console
.
log
"Analyzing host frame, because "
+
reason
+
"..."
scanTime
=
@
domMatcher
.
prepareSearch
null
,
true
console
.
log
"Traversal+scan took "
+
scanTime
+
" ms."
remote
:
publish
:
{}
addPlugin
:
{}
...
...
@@ -161,6 +166,7 @@ class Annotator.Host extends Annotator
if
not
@
ignoreMouseup
setTimeout
=>
@
consumer
.
back
()
unless
@
selectedRanges
?
.
length
@
domMatcher
.
setRootNode
@
wrapper
[
0
]
this
_setupDocumentEvents
:
->
...
...
h/js/services.coffee
View file @
d87b94bf
...
...
@@ -53,6 +53,13 @@ class Hypothesis extends Annotator
id
:
a
.
id
references
:
a
.
thread
?
.
split
'/'
# After annotations were loaded (or none was found), scan the document
for
event
in
[
'annotationsLoaded'
,
'foundNoAnnotations'
]
this
.
subscribe
event
,
=>
$rootScope
.
$apply
=>
@
provider
.
scanDocument
"annotations were loaded (if any)"
# Update the thread when an annotation changes
this
.
subscribe
'annotationUpdated'
,
(
annotation
)
=>
$rootScope
.
$apply
->
...
...
@@ -210,6 +217,7 @@ class Hypothesis extends Annotator
getHref
:
{}
getMaxBottom
:
{}
scrollTop
:
{}
scanDocument
:
{}
_setupWrapper
:
->
@
wrapper
=
@
element
.
find
(
'#wrapper'
)
...
...
h/lib/annotator.js
View file @
d87b94bf
/*
** Annotator 1.2.5-dev-
95dc8ee
** Annotator 1.2.5-dev-
02598fb
** 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-0
2-28 16:15:11
Z
** Built at: 2013-0
3-02 22:47:44
Z
*/
(
function
()
{
...
...
@@ -379,6 +379,9 @@
while
(
nr
.
commonAncestor
.
nodeType
!==
1
)
{
nr
.
commonAncestor
=
nr
.
commonAncestor
.
parentNode
;
}
if
(
window
.
DomTextMapper
!=
null
)
{
window
.
DomTextMapper
.
changed
(
nr
.
commonAncestor
,
"range normalization"
);
}
return
new
Range
.
NormalizedRange
(
nr
);
};
...
...
@@ -654,6 +657,8 @@
this
.
getHref
=
__bind
(
this
.
getHref
,
this
);
Annotator
.
__super__
.
constructor
.
apply
(
this
,
arguments
);
this
.
plugins
=
{};
if
(
!
Annotator
.
supported
())
return
this
;
this
.
domMapper
=
new
DomTextMapper
();
this
.
domMatcher
=
new
DomTextMatcher
(
this
.
domMapper
);
if
(
!
this
.
options
.
readOnly
)
this
.
_setupDocumentEvents
();
this
.
_setupWrapper
().
_setupViewer
().
_setupEditor
();
this
.
_setupDynamicStyle
();
...
...
@@ -779,25 +784,29 @@
};
Annotator
.
prototype
.
getContextQuoteSelectorFromRange
=
function
(
range
)
{
var
quote
,
r
,
selector
;
r
=
range
.
normalize
(
this
.
wrapper
[
0
]);
quote
=
$
.
trim
(
r
.
text
());
var
endOffset
,
prefix
,
quote
,
selector
,
startOffset
,
suffix
,
_ref2
;
startOffset
=
(
this
.
domMapper
.
getMappingsForNode
(
range
.
start
)).
start
;
endOffset
=
(
this
.
domMapper
.
getMappingsForNode
(
range
.
end
)).
end
;
quote
=
this
.
domMapper
.
getContentForRange
(
startOffset
,
endOffset
);
_ref2
=
this
.
domMapper
.
getContextForRange
(
startOffset
,
endOffset
),
prefix
=
_ref2
[
0
],
suffix
=
_ref2
[
1
];
return
selector
=
{
source
:
this
.
getHref
(),
type
:
"context+quote"
,
exact
:
quote
,
prefix
:
"TODO prefix"
,
suffix
:
"TODO suffix"
prefix
:
prefix
,
suffix
:
suffix
};
};
Annotator
.
prototype
.
getPositionSelectorFromRange
=
function
(
range
)
{
var
selector
;
var
endOffset
,
selector
,
startOffset
;
startOffset
=
(
this
.
domMapper
.
getMappingsForNode
(
range
.
start
)).
start
;
endOffset
=
(
this
.
domMapper
.
getMappingsForNode
(
range
.
end
)).
end
;
return
selector
=
{
source
:
this
.
getHref
(),
type
:
"position"
,
start
:
"100"
,
end
:
"200"
start
:
startOffset
,
end
:
endOffset
};
};
...
...
@@ -928,11 +937,13 @@
};
Annotator
.
prototype
.
deleteAnnotation
=
function
(
annotation
)
{
var
h
,
_k
,
_len3
,
_ref2
;
var
child
,
h
,
_k
,
_len3
,
_ref2
;
_ref2
=
annotation
.
highlights
;
for
(
_k
=
0
,
_len3
=
_ref2
.
length
;
_k
<
_len3
;
_k
++
)
{
h
=
_ref2
[
_k
];
child
=
h
.
childNodes
[
0
];
$
(
h
).
replaceWith
(
h
.
childNodes
);
window
.
DomTextMapper
.
changed
(
child
.
parentNode
,
"removed hilite (annotation deleted)"
);
}
this
.
publish
(
'annotationDeleted'
,
[
annotation
]);
return
annotation
;
...
...
@@ -959,7 +970,11 @@
}
};
clone
=
annotations
.
slice
();
if
(
annotations
.
length
)
loader
(
annotations
);
if
(
annotations
.
length
)
{
loader
(
annotations
);
}
else
{
this
.
publish
(
'foundNoAnnotations'
);
}
return
this
;
};
...
...
@@ -972,7 +987,7 @@
};
Annotator
.
prototype
.
highlightRange
=
function
(
normedRange
,
cssClass
)
{
var
hl
,
node
,
white
,
_k
,
_len3
,
_ref2
,
_results
;
var
hl
,
node
,
r
,
white
,
_k
,
_len3
,
_ref2
,
_results
;
if
(
cssClass
==
null
)
cssClass
=
'annotator-hl'
;
white
=
/^
\s
*$/
;
hl
=
$
(
"<span class='"
+
cssClass
+
"'></span>"
);
...
...
@@ -980,9 +995,10 @@
_results
=
[];
for
(
_k
=
0
,
_len3
=
_ref2
.
length
;
_k
<
_len3
;
_k
++
)
{
node
=
_ref2
[
_k
];
if
(
!
white
.
test
(
node
.
nodeValue
))
{
_results
.
push
(
$
(
node
).
wrapAll
(
hl
).
parent
().
show
()[
0
]);
}
if
(
!
(
!
white
.
test
(
node
.
nodeValue
)))
continue
;
r
=
$
(
node
).
wrapAll
(
hl
).
parent
().
show
()[
0
];
window
.
DomTextMapper
.
changed
(
node
,
"created hilite"
);
_results
.
push
(
r
);
}
return
_results
;
};
...
...
@@ -1114,13 +1130,15 @@
return
_this
.
publish
(
'annotationCreated'
,
[
annotation
]);
};
cancel
=
function
()
{
var
h
,
_k
,
_len3
,
_ref2
,
_results
;
var
child
,
h
,
_k
,
_len3
,
_ref2
,
_results
;
cleanup
();
_ref2
=
annotation
.
highlights
;
_results
=
[];
for
(
_k
=
0
,
_len3
=
_ref2
.
length
;
_k
<
_len3
;
_k
++
)
{
h
=
_ref2
[
_k
];
_results
.
push
(
$
(
h
).
replaceWith
(
h
.
childNodes
));
child
=
h
.
childNodes
[
0
];
$
(
h
).
replaceWith
(
h
.
childNodes
);
_results
.
push
(
window
.
DomTextMapper
.
changed
(
child
.
parentNode
,
"removed hilite, edit cancelled"
));
}
return
_results
;
};
...
...
h/lib/dom_text_mapper.coffee
0 → 100644
View file @
d87b94bf
This diff is collapsed.
Click to expand it.
h/lib/dom_text_matcher.coffee
0 → 100644
View file @
d87b94bf
class
window
.
DomTextMatcher
# ===== Public methods =======
# Switch the library into "serializable-only" mode.
# If set to true, all public API calls will be restricted to return
# strictly serializable data structures.
# (References to DOM objects will be omitted.)
restrictToSerializable
:
(
value
=
true
)
->
@
mapper
.
restrictToSerializable
value
# Consider only the sub-tree beginning with the given node.
#
# This will be the root node to use for all operations.
setRootNode
:
(
rootNode
)
->
@
mapper
.
setRootNode
rootNode
# Consider only the sub-tree beginning with the node whose ID was given.
#
# This will be the root node to use for all operations.
setRootId
:
(
rootId
)
->
@
mapper
.
setRootId
rootId
# Use this iframe for operations.
#
# Call this when mapping content in an iframe.
setRootIframe
:
(
iframeId
)
->
@
mapper
.
setRootIframe
iframeId
# Work with the whole DOM tree
#
# (This is the default; you only need to call this, if you have configured
# a different root earlier, and now you want to restore the default setting.)
setRealRoot
:
->
@
mapper
.
setRealRoot
()
# Notify the library that the document has changed.
# This means that subsequent calls can not safely re-use previously cached
# data structures, so some calculations will be necessary again.
#
# The usage of this feature is not mandatorry; if not receiving change notifications,
# the library will just assume that the document can change anythime, and therefore
# will not assume any stability.
documentChanged
:
->
@
mapper
.
documentChanged
()
# The available paths which can be searched
#
# An map is returned, where the keys are the paths, and the values are objects with the following fields:
# path: the valid path value
# node: reference to the DOM node
# content: the text content of the node, as rendered by the browser
# length: the length of the next content
getAllPaths
:
->
t0
=
@
timestamp
()
paths
=
@
mapper
.
getAllPaths
()
t1
=
@
timestamp
()
return
time
:
t1
-
t0
,
paths
:
paths
# Return the default path
getDefaultPath
:
->
@
mapper
.
getDefaultPath
()
# Prepare for searching the specified path
#
# Returns the time (in ms) it took the scan the specified path
prepareSearch
:
(
path
)
->
t0
=
@
timestamp
()
@
mapper
.
scan
path
t1
=
@
timestamp
()
t1
-
t0
# Search for text using exact string matching
#
# Parameters:
# pattern: what to search for
#
# distinct: forbid overlapping matches? (defaults to true)
#
# caseSensitive: should the search be case sensitive? (defaults to false)
#
# path: the sub-tree inside the DOM you want to search.
# Must be an XPath expression, relative to the configured root node.
# You can check for valid input values using the getAllPaths method above.
# It's not necessary to submit path, if the search was prepared beforehand,
# with the prepareSearch() method
#
# For the details about the returned data structure, see the documentation of the search() method.
searchExact
:
(
pattern
,
distinct
=
true
,
caseSensitive
=
false
,
path
=
null
)
->
if
not
@
pm
then
@
pm
=
new
window
.
DTM_ExactMatcher
@
pm
.
setDistinct
(
distinct
)
@
pm
.
setCaseSensitive
(
caseSensitive
)
@
search
@
pm
,
pattern
,
null
,
path
# Search for text using regular expressions
#
# Parameters:
# pattern: what to search for
#
# caseSensitive: should the search be case sensitive? (defaults to false)
#
# path: the sub-tree inside the DOM you want to search.
# Must be an XPath expression, relative to the configured root node.
# You can check for valid input values using the getAllPaths method above.
# It's not necessary to submit path, if the search was prepared beforehand,
# with the prepareSearch() method
#
# For the details about the returned data structure, see the documentation of the search() method.
searchRegex
:
(
pattern
,
caseSensitive
=
false
,
path
=
null
)
->
if
not
@
rm
then
@
rm
=
new
window
.
DTM_RegexMatcher
@
rm
.
setCaseSensitive
(
caseSensitive
)
@
search
@
rm
,
pattern
,
null
,
path
# Search for text using fuzzy text matching
#
# Parameters:
# pattern: what to search for
#
# pos: where to start searching
#
# caseSensitive: should the search be case sensitive? (defaults to false)
#
# matchDistance and
# matchThreshold:
# fine-tuning parameters for the d-m-p library.
# See http://code.google.com/p/google-diff-match-patch/wiki/API for details.
#
# path: the sub-tree inside the DOM you want to search.
# Must be an XPath expression, relative to the configured root node.
# You can check for valid input values using the getAllPaths method above.
# It's not necessary to submit path, if the search was prepared beforehand,
# with the prepareSearch() method
#
# For the details about the returned data structure, see the documentation of the search() method.
searchFuzzy
:
(
pattern
,
pos
,
caseSensitive
=
false
,
matchDistance
=
1000
,
matchThreshold
=
0.5
,
path
=
null
)
->
if
not
@
dmp
?
then
@
dmp
=
new
window
.
DTM_DMPMatcher
@
dmp
.
setMatchDistance
matchDistance
@
dmp
.
setMatchThreshold
matchThreshold
@
dmp
.
setCaseSensitive
caseSensitive
@
search
@
dmp
,
pattern
,
pos
,
path
# ===== Private methods (never call from outside the module) =======
constructor
:
(
domTextMapper
)
->
@
mapper
=
domTextMapper
# Search for text with a custom matcher object
#
# Parameters:
# matcher: the object to use for doing the plain-text part of the search
# path: the sub-tree inside the DOM you want to search.
# Must be an XPath expression, relative to the configured root node.
# You can check for valid input values using the getAllPaths method above.
# pattern: what to search for
# pos: where do we expect to find it
#
# A list of matches is returned.
#
# , each element with "start", "end", "found" and "nodes" fields.
# start and end specify where the pattern was found; "found" is the matching slice.
# Nodes is the list of matching nodes, with details about the matches.
#
# If no match is found, null is returned. #
search
:
(
matcher
,
pattern
,
pos
,
path
=
null
)
->
# Prepare and check the pattern
unless
pattern
?
then
throw
new
Error
"Can't search for null pattern!"
pattern
=
pattern
.
trim
()
unless
pattern
?
then
throw
new
Error
"Can't search an for empty pattern!"
# Do some preparation, if required
t0
=
@
timestamp
()
#
if
path
?
then
@
prepareSearch
path
t1
=
@
timestamp
()
# Check preparations
unless
@
mapper
.
corpus
?
then
throw
new
Error
"Not prepared to search! (call PrepareSearch, or pass me a path)"
# Do the text search
textMatches
=
matcher
.
search
@
mapper
.
corpus
,
pattern
,
pos
t2
=
@
timestamp
()
# Collect the mappings
# Should work like a comprehension, but it does not. WIll fix later.
# matches = ($.extend {}, match, @mapper.getMappingsFor match.start, match.end) for match in textMatches
matches
=
[]
for
match
in
textMatches
do
(
match
)
=>
matches
.
push
$
.
extend
{},
match
,
@
analyzeMatch
(
pattern
,
match
),
@
mapper
.
getMappingsForRange
(
match
.
start
,
match
.
end
)
t3
=
@
timestamp
()
return
{
matches
:
matches
time
:
phase0_domMapping
:
t1
-
t0
phase1_textMatching
:
t2
-
t1
phase2_matchMapping
:
t3
-
t2
total
:
t3
-
t0
}
timestamp
:
->
new
Date
().
getTime
()
analyzeMatch
:
(
pattern
,
match
)
->
found
=
@
mapper
.
corpus
.
substr
match
.
start
,
match
.
end
-
match
.
start
return
{
found
:
found
exact
:
found
is
pattern
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment