@@ -8,35 +8,70 @@ import Foundation
88
99/// A default implementation of the `LocatorService` using the `PositionsService` to locate its inputs.
1010open class DefaultLocatorService : LocatorService , Loggable {
11-
12- let readingOrder : [ Link ]
13- let positionsByReadingOrder : ( ) -> [ [ Locator ] ]
14-
15- public init ( readingOrder: [ Link ] , positionsByReadingOrder: @escaping ( ) -> [ [ Locator ] ] ) {
16- self . readingOrder = readingOrder
17- self . positionsByReadingOrder = positionsByReadingOrder
18- }
1911
20- public convenience init ( readingOrder: [ Link ] , publication: Weak < Publication > ) {
21- self . init ( readingOrder: readingOrder, positionsByReadingOrder: { publication ( ) ? . positionsByReadingOrder ?? [ ] } )
12+ public let publication : Weak < Publication >
13+
14+ public init ( publication: Weak < Publication > ) {
15+ self . publication = publication
2216 }
2317
18+ /// Locates the target of the given `locator`.
19+ ///
20+ /// If `locator.href` can be found in the links, `locator` will be returned directly.
21+ /// Otherwise, will attempt to find the closest match using `totalProgression`, `position`,
22+ /// `fragments`, etc.
2423 open func locate( _ locator: Locator ) -> Locator ? {
25- guard readingOrder . firstIndex ( withHREF : locator . href ) != nil else {
24+ guard let publication = publication ( ) else {
2625 return nil
2726 }
28-
29- return locator
27+
28+ if publication. link ( withHREF: locator. href) != nil {
29+ return locator
30+ }
31+
32+ if let totalProgression = locator. locations. totalProgression, let target = locate ( progression: totalProgression) {
33+ return target. copy (
34+ title: locator. title,
35+ text: { $0 = locator. text }
36+ )
37+ }
38+
39+ return nil
3040 }
31-
41+
42+ open func locate( _ link: Link ) -> Locator ? {
43+ let components = link. href. split ( separator: " # " , maxSplits: 1 ) . map ( String . init)
44+ let href = components. first ?? link. href
45+ let fragment = components. getOrNil ( 1 )
46+
47+ guard
48+ let resourceLink = publication ( ) ? . link ( withHREF: href) ,
49+ let type = resourceLink. type
50+ else {
51+ return nil
52+ }
53+
54+ return Locator (
55+ href: href,
56+ type: type,
57+ title: resourceLink. title ?? link. title,
58+ locations: Locator . Locations (
59+ fragments: Array ( ofNotNil: fragment) ,
60+ progression: ( fragment == nil ) ? 0.0 : nil
61+ )
62+ )
63+ }
64+
3265 open func locate( progression totalProgression: Double ) -> Locator ? {
3366 guard 0.0 ... 1.0 ~= totalProgression else {
3467 log ( . error, " Progression must be between 0.0 and 1.0, received \( totalProgression) " )
3568 return nil
3669 }
3770
38- let positions = positionsByReadingOrder ( )
39- guard let ( readingOrderIndex, position) = findClosest ( to: totalProgression, in: positions) else {
71+ guard
72+ let positions = publication ( ) ? . positionsByReadingOrder,
73+ let ( readingOrderIndex, position) = findClosest ( to: totalProgression, in: positions)
74+ else {
4075 return nil
4176 }
4277
0 commit comments