{"tiddlers":{"$:/config/Dynannotate/LegacySelectionTracker/Enable":{"title":"$:/config/Dynannotate/LegacySelectionTracker/Enable","text":"no\n"},"$:/config/Dynannotate/SelectionTracker/Enable":{"title":"$:/config/Dynannotate/SelectionTracker/Enable","text":"no\n"},"$:/plugins/tiddlywiki/dynannotate/readme":{"title":"$:/plugins/tiddlywiki/dynannotate/readme","text":"The ''Dynannotate'' plugin allows annotations on textual content to be created and displayed. It has several components:\n\n* The dynannotate widget draws clickable textual annotations, search highlights and search snippets as overlays over the top of the content that it contains\n* The selection tracker keeps track of changes to the selected text in the main browser window. It triggers an action string when the selection changes, passing it the details of the selection. It can be used to display a popup menu\n** The original legacy selection tracker is also provided for backwards compatibility. It is much more limited, and not recommended for new projects\n* The element spotlight highlights on screen elements using a spotlight animation\n\n!! Dynannotate Widget\n\nThe attributes of the `<$dynannotate>` widget describe annotations to be overlaid over the text contained within its child widgets. A single annotation can be directly applied using the attributes or multiple annotations can be applied by providing a filter identifying the \"annotation tiddlers\" that specify each annotation.\n\nThe content of the  `<$dynannotate>` widget should not contain HTML `<input>` or `<textarea>` text editing elements (and therefore should not contain TiddlyWiki's `<$edit-text>` widget)\n\nThe `<$dynannotate>` widget uses the selection tracker to support a popup that dynamically tracks selected text within it.\n\n!!! Attributes\n\n|!Attribute |!Description |\n|target |Optional text to be annotated |\n|targetPrefix |Optional prefix text to disambiguate the target |\n|targetSuffix |Optional suffix text to disambiguate the target |\n|filter |Filter identifying the annotation tiddlers applying to this content (see below) |\n|actions |Action string to be executed when an annotation is clicked. The variable `annotationTiddler` contains the title of the tiddler corresponding to the annotation that was clicked, and the variable `modifierKey` contains \"ctrl\", \"shift\", \"ctrl-shift\", \"normal\" according to which modifier keys were pressed |\n|popup |Popup state tiddler to be used to trigger a popup when an annotation is clicked |\n|floating |Setting to `yes` makes the popup need to be closed explicitly. |\n|search |Search text to be highlighted within the widget |\n|searchDisplay |\"overlay\" or \"snippet\" (see below) |\n|searchMode |\"literal\" (default), \"regexp\", \"whitespace\", \"words\" or \"some\" (see below) |\n|searchMinLength |Optional minimum length of search string |\n|searchCaseSensitive |\"yes\" (the default) for a case sensitive search, or \"no\" for a case insensitive search|\n|searchClass |Optional CSS class to be added to search overlays |\n|snippetContextLength |Optional length of search result contextual prefix/suffix |\n\nThe following attributes are only used with the legacy selection tracker:\n\n|!Attribute |!Description |\n|selection |Tiddler to which the currently selected text should be dynamically saved |\n|selectionPrefix |Tiddler to which up to 50 characters preceding the currently selected text should be dynamically saved |\n|selectionSuffix |Tiddler to which up to 50 characters succeeding the currently selected text should be dynamically saved |\n|selectionPopup |Popup state tiddler to be used to trigger a popup when text is selected |\n\nThe values supported by the `searchDisplay` attribute are:\n\n* `overlay` - display search results as overlays over the contained text\n* `snippet` - display search results as a sequence of highlighted snippets, and the original text is hidden. Selecting this option therefore disables the annotation functionality\n\nThe search modes supported by the `searchMode` attribute are:\n\n* `literal` or `normal` - a literal string of plain text to match (default).\n* `regexp` - a JavaScript-style regular expression (without the quoting backslashes and flags)\n* `whitespace` - a literal string to match while normalising runs of whitespace. Thus `A B` matches `A   B`.\n* `words` or `some` - treats the search string as a list of tokens separated by whitespace, and matches all tokens (regardless of ordering and whether there is other text in between)\n\nWhen the selection popup is triggered, the currently selected text can be found in the tiddler named in the `selection` attribute, with the disambiguating prefix and suffix in the tiddlers named in the `selectionPrefix` and `selectionPopup` tiddlers. Note that the selection text will be an empty string if the selection popup was triggered in response to a click (ie zero width selection).\n\nHere's a simple example that highlights the first occurrence of the word \"ut\" within the text contained within it:\n\n```\n<$dynannotate target=\"ut\">\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum\n</$dynannotate>\n```\n\nA prefix and/or suffix can be specified to disambiguate the annotation. For example, here we target the second occurrence of the word \"ut\":\n\n```\n<$dynannotate target=\"ut\" targetPrefix=\"ullamco laboris nisi \" targetSuffix=\" aliquip ex ea commodo consequat\">\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum\n</$dynannotate>\n```\n\nThe widget works by scanning the rendered text of its content, so it works even if the text is built dynamically:\n\n```\n<$dynannotate target=\"HelloThere\">\n<<list-links \"[tag[Work]]\">>\n</$dynannotate>\n```\n\n!!! Annotation Tiddlers\n\nAn annotation tiddler is a tiddler describing an annotation to be overlaid over another tiddler. Their fields are used as follows:\n\n|!Field |!Description |\n|title |By convention the prefix `$:/annotations/<username>/` is used, but any title can be used |\n|text |The text of the annotation |\n|created, creator, modified, modifier |As per TiddlyWiki normal behaviour |\n|annotate-tiddler |The title of the target tiddler being annotated (optional, see below) |\n|annotate-text |The text being annotated in the target tiddler |\n|annotate-prefix |Optional prefix to disambiguate the target annotation |\n|annotate-suffix |Optional suffix to disambiguate the target annotation |\n|annotate-colour |CSS colour for the annotation (defaults to `rgba(255,255,0,0.3)`) |\n|annotate-blend-mode |CSS [[mix blend mode|https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode]] for the annotation (defaults to `multiply`) |\n\nNote that using the `annotate-tiddler` field to associate an annotation with the annotated tiddler is a lightweight convention employed by the examples; it isn't actually required by any of the JavaScript code. Thus authors can experiment with other techniques for recording the association.\n\n!! Selection Trackers\n\nThe selection trackers are enabled when the following configuration tiddlers are set to ''yes''.\n\n* $:/config/Dynannotate/SelectionTracker/Enable for the main selection tracker\n* $:/config/Dynannotate/LegacySelectionTracker/Enable for the legacy selection tracker\n\nBoth selection trackers are enabled by default.\n\n!!! Main Selection Tracker\n\nThe selection tracker triggers an action string whenever the browser text selection changes. The actions are delayed until the selection has not changed for 500ms (this means that the selection tracker is only triggered when the user pauses after completing a selection, and is not continuously invoked as the user drags the selection).\n\nThe selection tracker works within DOM subtrees that have the following structure:\n\n* The outer wrapper element has the attribute `data-selection-actions-title` containing the title of the tiddler containing the action string to be invoked when the selection changes\n* Each child element of the outer element must have a unique `id` attribute to identify it\n\n```\n<div data-selection-actions-title=\"{tiddler title}\">\n\t<div id=\"{title}\">\n\t\tContent text\n\t</div>\n\t...\n</div>\n```\n\nThe action string is invoked with the following variables:\n\n|!Variable |!Description |\n|`selection` |A JSON object representing the selection (see below) |\n|`dom-*` |All DOM attributes of the outer wrapper node are made available as variables, with the prefix `dom-` |\n|`tv-selection-posx` |X position of the selection in pixels |\n|`tv-selection-posy` |Y position of the selection in pixels |\n|`tv-selection-width` |Width of the selection in pixels |\n|`tv-selection-height` |Height of the selection in pixels |\n|`tv-selection-coords` |A co-ordinate string that can be used with the ActionPopupWidget to trigger a popup at the selection |\n\nThe JSON representation of the selection is as follows:\n\n```\n{\n\t\"chunks\": [\n\t\t{\n\t\t\t\"id\": \"id-of-first-chunk-of-selection\",\n\t\t\t\"text\": \"text-of-first-chunk-of-selection\",\n\t\t\t\"prefix\": \"optional-prefix-before-first-chunk-of-selection\",\n\t\t\t\"suffix\": \"optional-suffix-after-last-chunk-of-selection\"\n\t\t}\n\t\t...\n\t],\n\t\"variables\": {\n\t\t<variables listed above>\n\t}\n}\n```\n\nNotes:\n\n* Only the first chunk of the selection may have a \"prefix\" field which will contain any text at the start of the chunk preceding the selection\n* Only the last chunk of the selection may have a \"suffix\" field which will contain any text at the end of the chunk following the selection\n\n!!! Legacy Selection Tracker\n\nThe selection tracker is incorporated within the `<$dynannotate>` widget via the ''selection'', ''selectionPrefix'' ''selectionSuffix'' and ''selectionPopup'' attributes. It can be used independently for specialised applications.\n\nEach selection container is marked with the class `tc-dynannotate-selection-container`, and should contain the following attributes:\n\n* `data-annotation-selection-save`: title of tiddler to which the selected text should be saved\n* `data-annotation-selection-prefix-save`: title of tiddler to which up to 50 characters preceding the currently selected text should be dynamically saved\n* `data-annotation-selection-suffix-save`: title of tiddler to which up to 50 characters succeeding the currently selected text should be dynamically saved\n* `data-annotation-selection-popup`: title of state tiddler used to trigger the selection popup\n\nNotes:\n\n* The selection popup will disappear if the selection is cancelled; this will happen if the user clicks on any other element apart than a button. Thus it is not possible to have any interactive controls within the popup apart from buttons\n\n!! Element Spotlight\n\nThe `tm-spotlight-element` message causes a spotlight effect to briefly appear to highlight a specified element. The message accepts the following parameters:\n\n|!Parameter |!Description |\n|`selector` |CSS selector of the element to highlight |\n|{//Any parameter names starting with `selector-`}// |Fallback CSS selectors to be used if the primary selector does not resolve to an element |\n\nThe fallback CSS selectors are case-insensitively sorted by title before use, with uppercase letters sorting before lower case letters. The usual convention is to use numeric suffixes: `selector-00`, `selector-01` etc.\n"},"$:/plugins/tiddlywiki/dynannotate/examples/combined":{"title":"$:/plugins/tiddlywiki/dynannotate/examples/combined","tags":"$:/tags/dynannotateExamples","caption":"Combined","text":"\\define click-annotation-actions()\n<$action-setfield $tiddler=\"$:/temp/dynannotate/demo/annotation-title\" $value=<<annotationTiddler>>/>\n\\end\n\n\\define create-annotation-actions()\n<$action-createtiddler\n\t$basetitle=\"$:/plugins/tiddlywiki/dynannotate/demo-annotation\"\n\t$savetitle={{{ [<chunk>addprefix[$:/state/dynannotate/temp-save-title/]] }}}\n\tannotate-tiddler=<<chunk>>\n\tannotate-text=<<text>>\n\tannotate-prefix=<<prefix>>\n\tannotate-suffix=<<suffix>>\n\tannotate-colour=<<colour>>\n/>\n<$set name=\"popup-coords\" value={{{ [<chunk>addprefix[$:/state/dynannotate/popup-selection/]get[text]] }}}>\n<$action-deletetiddler $tiddler={{{ [<chunk>addprefix[$:/state/dynannotate/popup-selection/]] }}}/>\n<$action-setfield $tiddler=\"$:/temp/dynannotate/demo/annotation-title\" $value={{{ [<chunk>addprefix[$:/state/dynannotate/temp-save-title/]get[text]] }}}/>\n<$action-popup $state={{{ [<chunk>addprefix[$:/state/dynannotate/popup-annotation/]] }}} $coords=<<popup-coords>>/>\n</$set>\n\\end\n\n<div class=\"tc-dynannotation-example-info\">\n\nThis example combines many of the features of the dynannotate plugin:\n\n* using annotation tiddlers to store the details of each annotation\n* triggering actions when the annotations are clicked\n* attaching a popup to the annotations\n* tracking the selection with another popup\n\nSee the [[source|$:/plugins/tiddlywiki/dynannotate/examples/combined]] for details\n\n</div>\n\nSearch: <$edit-text tiddler=\"$:/temp/search\" tag=\"input\"/>\n\n<$list filter=\"[all[tiddlers+shadows]tag[DynannotateDemo]sort[title]]\" variable=\"chunk\">\n<div style=\"position:relative;\"><!-- Needed for the popups to work -->\n<$dynannotate\n\tfilter=\"[all[shadows+tiddlers]!has[draft.of]annotate-tiddler<chunk>]\"\n\tactions=<<click-annotation-actions>>\n\tpopup={{{ [<chunk>addprefix[$:/state/dynannotate/popup-annotation/]] }}}\n\tselection={{{ [<chunk>addprefix[$:/state/dynannotate/selection/]] }}}\n\tselectionPrefix={{{ [<chunk>addprefix[$:/state/dynannotate/selection-prefix/]] }}}\n\tselectionSuffix={{{ [<chunk>addprefix[$:/state/dynannotate/selection-suffix/]] }}}\n\tselectionPopup={{{ [<chunk>addprefix[$:/state/dynannotate/popup-selection/]] }}}\n\tsearch={{$:/temp/search}}\n\tsearchClass=\"tc-dynannotation-search-overlay-blurred\"\n\tsearchMinLength={{$:/config/Search/MinLength}}\n>\n<$transclude tiddler=<<chunk>> mode=\"block\"/>\n</$dynannotate>\n<$reveal type=\"popup\" state={{{ [<chunk>addprefix[$:/state/dynannotate/popup-annotation/]] }}} position=\"belowright\" animate=\"yes\" retain=\"yes\" style=\"overflow-y:hidden;\">\n<div class=\"tc-drop-down-wrapper\">\n<div class=\"tc-drop-down tc-popup-keep\" style=\"max-width:550px;white-space: normal;overflow-y:hidden;\">\n<$tiddler tiddler={{$:/temp/dynannotate/demo/annotation-title}}>\n<p>\n<h2>\nThis is an annotation\n</h2>\n</p>\n<p>\nThe annotation is stored in the tiddler:\n</p>\n<p>\n<$link><$view field=\"title\"/></$link>\n</p>\n<p>\nThe annotated text is ''<$view field=\"annotate-text\"/>''.\n</p>\n<p>\nAnnotation Colour:\n<$macrocall $name='colour-picker' actions=\"\"\"\n<$action-setfield $field=\"annotate-colour\" $value=<<colour-picker-value>>/>\n\"\"\"/>\n</p>\n</$tiddler>\n</div>\n</div>\n</$reveal>\n<$reveal type=\"popup\" state={{{ [<chunk>addprefix[$:/state/dynannotate/popup-selection/]] }}} position=\"belowright\" animate=\"yes\" retain=\"yes\" style=\"overflow-y:hidden;\">\n<div class=\"tc-drop-down-wrapper\">\n<div class=\"tc-drop-down tc-popup-keep\" style=\"max-width:550px;white-space:normal;\">\n<$vars\n\ttext={{{ [<chunk>addprefix[$:/state/dynannotate/selection/]get[text]] }}}\n\tprefix={{{ [<chunk>addprefix[$:/state/dynannotate/selection-prefix/]get[text]] }}}\n\tsuffix={{{ [<chunk>addprefix[$:/state/dynannotate/selection-suffix/]get[text]] }}}\n\tcolour={{{ [<chunk>addprefix[$:/state/dynannotate/annotation-colour/]get[text]] }}}\n>\n<$button actions=<<create-annotation-actions>>>\nCreate annotation\n</$button>\n<p>\nText: <$text text=<<text>>/>\n</p>\n<p>\nPrefix: <$text text=<<prefix>>/>\n</p>\n<p>\nSuffix: <$text text=<<suffix>>/>\n</p>\n</$vars>\n</div>\n</div>\n</$reveal>\n</div>\n</$list>\n"},"$:/plugins/tiddlywiki/dynannotate/example-annotation-1":{"title":"$:/plugins/tiddlywiki/dynannotate/example-annotation-1","annotate-tiddler":"$:/plugins/tiddlywiki/dynannotate/example-text-1","annotate-text":"memory is transitory.  Yet the speed of action","annotate-colour":"SkyBlue","annotate-blend-mode":"multiply"},"$:/plugins/tiddlywiki/dynannotate/example-annotation-2":{"title":"$:/plugins/tiddlywiki/dynannotate/example-annotation-2","annotate-tiddler":"$:/plugins/tiddlywiki/dynannotate/example-text-3","annotate-text":"It needs a name, and to coin one at random, \"memex\" will do","annotate-colour":"rgba(255,0,255,0.45)","annotate-blend-mode":"multiply"},"$:/plugins/tiddlywiki/dynannotate/example-annotation-3":{"title":"$:/plugins/tiddlywiki/dynannotate/example-annotation-3","annotate-tiddler":"$:/plugins/tiddlywiki/dynannotate/example-text-5","annotate-text":"it would take him hundreds of years to fill the repository","annotate-colour":"#fff","annotate-blend-mode":"difference"},"$:/plugins/tiddlywiki/dynannotate/example-annotation-4":{"title":"$:/plugins/tiddlywiki/dynannotate/example-annotation-4","annotate-tiddler":"$:/plugins/tiddlywiki/dynannotate/example-text-1","annotate-text":"that","annotate-prefix":"It has other characteristics, of course; trails ","annotate-suffix":" are not frequently followed are prone to fade","annotate-colour":"rgba(255,0,255,0.45)","annotate-blend-mode":"difference","text":"(This tiddler is in .json format so that we can have field values that start with a whitespace"},"$:/plugins/tiddlywiki/dynannotate/example-text-1":{"title":"$:/plugins/tiddlywiki/dynannotate/example-text-1","source":"https://www.w3.org/History/1945/vbush/vbush.txt","tags":"DynannotateDemo","text":"The human mind does not work that way. It operates by association. With one item in its grasp, it snaps instantly to the next that is suggested by the association of thoughts, in accordance with some intricate web of trails carried by the cells of the brain.  It has other characteristics, of course; trails that are not frequently followed are prone to fade, items are not fully permanent, memory is ''transitory''.  Yet the speed of action, the intricacy of trails, the detail of mental pictures, is awe-inspiring beyond all else in nature."},"$:/plugins/tiddlywiki/dynannotate/example-text-2":{"title":"$:/plugins/tiddlywiki/dynannotate/example-text-2","source":"https://www.w3.org/History/1945/vbush/vbush.txt","tags":"DynannotateDemo","text":"Man cannot hope fully to duplicate this mental process artificially, but he certainly ought to be able to learn from it.  In minor ways he may even improve, for his records have relative permanency.  The first idea, however, to be drawn from the analogy concerns selection. Selection by association, rather than by indexing, may yet be mechanized.  One cannot hope thus to equal the speed and flexibility with which the mind follows an associative trail, but it should be possible to beat the mind decisively in regard to the permanence and clarity of the items resurrected from storage."},"$:/plugins/tiddlywiki/dynannotate/example-text-3":{"title":"$:/plugins/tiddlywiki/dynannotate/example-text-3","source":"https://www.w3.org/History/1945/vbush/vbush.txt","tags":"DynannotateDemo","text":"Consider a future device for individual use, which is a sort of mechanized private file and library.  It needs a name, and to coin one at random, \"memex\" will do.  A memex is a device in which an individual stores all his books, records, and communications, and which is mechanized so that it may be consulted with exceeding speed and flexibility.  It is an enlarged intimate supplement to his memory."},"$:/plugins/tiddlywiki/dynannotate/example-text-4":{"title":"$:/plugins/tiddlywiki/dynannotate/example-text-4","source":"https://www.w3.org/History/1945/vbush/vbush.txt","tags":"DynannotateDemo","text":"It consists of a desk, and while it can presumably be operated from a distance, it is primarily the piece of furniture at which he works. On the top are slanting translucent screens, on which material can be projected for convenient reading.  There is a keyboard, and sets of buttons and levers.  Otherwise it looks like an ordinary desk."},"$:/plugins/tiddlywiki/dynannotate/example-text-5":{"title":"$:/plugins/tiddlywiki/dynannotate/example-text-5","source":"https://www.w3.org/History/1945/vbush/vbush.txt","tags":"DynannotateDemo","text":"In one end is the stored material.  The matter of bulk is well taken care of by improved microfilm.  Only a small part of the interior of the memex is devoted to storage, the rest to mechanism.  Yet if the user inserted 5000 pages of material a day it would take him hundreds of years to fill the repository, so he can be profligate and enter material freely."},"$:/plugins/tiddlywiki/dynannotate/example-text-6":{"title":"$:/plugins/tiddlywiki/dynannotate/example-text-6","source":"https://www.w3.org/History/1945/vbush/vbush.txt","tags":"DynannotateDemo","text":"Most of the memex contents are purchased on microfilm ready for insertion.  Books of all sorts, pictures, current periodicals, newspapers, are thus obtained and dropped into place.  Business correspondence takes the same path.  And there is provision for direct entry.  On the top of the memex is a transparent platen.  On this are placed longhand notes, photographs, memoranda, all sort of things. When one is in place, the depression of a lever causes it to be photographed onto the next blank space in a section of the memex film, dry photography being employed."},"$:/plugins/tiddlywiki/dynannotate/example-text-7":{"title":"$:/plugins/tiddlywiki/dynannotate/example-text-7","source":"https://www.w3.org/History/1945/vbush/vbush.txt","tags":"DynannotateDemo","text":"There is, of course, provision for consultation of the record by the usual scheme of indexing.  If the user wishes to consult a certain book, he taps its code on the keyboard, and the title page of the book promptly appears before him, projected onto one of his viewing positions.  Frequently-used codes are mnemonic, so that he seldom consults his code book; but when he does, a single tap of a key projects it for his use.  Moreover, he has supplemental levers.  On deflecting one of these levers to the right he runs through the book before him, each page in turn being projected at a speed which just allows a recognizing glance at each.  If he deflects it further to the right, he steps through the book 10 pages at a time; still further at 100 pages at a time.  Deflection to the left gives him the same control backwards."},"$:/plugins/tiddlywiki/dynannotate/example-text-8":{"title":"$:/plugins/tiddlywiki/dynannotate/example-text-8","source":"https://www.w3.org/History/1945/vbush/vbush.txt","tags":"DynannotateDemo","text":"A special button transfers him immediately to the first page of the index.  Any given book of his library can thus be called up and consulted with far greater facility than if it were taken from a shelf.  As he has several projection positions, he can leave one item in position while he calls up another.  He can add marginal notes and comments, taking advantage of one possible type of dry photography, and it could even be arranged so that he can do this by a stylus scheme, such as is now employed in the telautograph seen in railroad waiting rooms, just as though he had the physical page before him."},"$:/plugins/tiddlywiki/dynannotate/examples":{"title":"$:/plugins/tiddlywiki/dynannotate/examples","text":"<<tabs \"[all[tiddlers+shadows]tag[$:/tags/dynannotateExamples]!has[draft.of]]\" \"$:/plugins/tiddlywiki/dynannotate/examples/snippets\">>\n\n"},"$:/plugins/tiddlywiki/dynannotate/examples/simple":{"title":"$:/plugins/tiddlywiki/dynannotate/examples/simple","tags":"$:/tags/dynannotateExamples","caption":"Simple","text":"\\define show-example(example)\n<$codeblock code=<<__example__>>/>\n\n//''Displays as:''//\n\n$example$\n\\end\n\n\\define search-example-code()\n<!-- \nThe inline filters update the displayed codeblock to reflect the choices the user has made in the UI.\nThe rules pragma is used here to evaluate only these filters, but leave everything else untouched.\nThe tabs and linebreaks within the parameters to addprefix, though maybe confusing, help with the readability of the codeblock output.\n-->\n\\rules only filteredtranscludeinline\n<$dynannotate\n\tsearch=\"{{{ [{$:/temp/dynannotate-example/search}!is[blank]else[The human mind]] }}}\"{{{ [{$:/temp/dynannotate-example/searchMode}!search::some[literal normal]addprefix[\n\tsearchMode=\"]addsuffix[\"]] }}}\n\tsearchCaseSensitive=\"{{{ [{$:/temp/dynannotate-example/searchCaseSensitive}!is[blank]else[yes]] }}}\"{{{ [{$:/temp/dynannotate-example/searchClass}!is[blank]addprefix[\n\tsearchClass=\"]addsuffix[\"]] }}}\n>\n<$transclude tiddler=\"$:/plugins/tiddlywiki/dynannotate/example-text-1\" mode=\"block\"/>\n<$transclude tiddler=\"$:/plugins/tiddlywiki/dynannotate/example-text-2\" mode=\"block\"/>\n</$dynannotate>\n\\end\n\n<div class=\"tc-dynannotation-example-info\">\n\n!! Annotations with `target`\n\nWe use the `target*` attributes to specify a target string for the annotation and optionally a prefix and suffix for disambiguating multiple occurences.\n\n</div>\n\n<<show-example \"\"\"<$dynannotate\n\ttarget=\"the\"\n\ttargetPrefix=\"Yet \"\n\ttargetSuffix=\" speed\"\n>\n<$transclude tiddler=\"$:/plugins/tiddlywiki/dynannotate/example-text-1\" mode=\"block\"/>\n</$dynannotate>\n\"\"\">>\n\n<div class=\"tc-dynannotation-example-info\">\n\n!! Highlights with `search`\n\nWe use the `search` attribute to specify a search string for highlighting.\n\nThe search type can be set with the `searchMode` and `searchCaseSensitive` attributes.\n\nWe can style the highlights with the `searchClass` attribute, which has some predefined values.\n\n</div>\n\n|tc-table-no-border tc-dynannotate-search-parameters|k\n|search term |<$edit-text tiddler=\"$:/temp/dynannotate-example/search\" field=\"text\" tag=\"input\" default=\"The human mind\" />|\n|search mode |<$select tiddler=\"$:/temp/dynannotate-example/searchMode\" field=\"text\" default=\"literal\"><option value=\"literal\">literal</option><option value=\"normal\">normal</option><option value=\"regexp\">regexp</option><option value=\"whitespace\">whitespace</option><option value=\"some\">some</option><option value=\"words\">words</option></$select> |\n|case sensitive |<$checkbox tiddler=\"$:/temp/dynannotate-example/searchCaseSensitive\" field=\"text\" checked=\"yes\" unchecked=\"no\" default=\"yes\"> <$text text={{{ [{$:/temp/dynannotate-example/searchCaseSensitive}!is[blank]else[yes]] }}} /></$checkbox> |\n|search class |<$select tiddler=\"$:/temp/dynannotate-example/searchClass\" field=\"text\" default=\"\"><option value=\"\">(none)</option><option value=\"tc-dynannotation-search-overlay-animated\">tc-dynannotation-search-overlay-animated</option><option value=\"tc-dynannotation-search-overlay-blurred\">tc-dynannotation-search-overlay-blurred</option></$select> |\n\n<$dynannotate\n\tsearch={{{ [{$:/temp/dynannotate-example/search}!is[blank]else[The human mind]] }}}\n\tsearchMode={{{ [{$:/temp/dynannotate-example/searchMode}] }}}\n\tsearchCaseSensitive={{{ [{$:/temp/dynannotate-example/searchCaseSensitive}!is[blank]else[yes]] }}}\n\tsearchClass={{{ [{$:/temp/dynannotate-example/searchClass}] }}}\n>\n<$transclude tiddler=\"$:/plugins/tiddlywiki/dynannotate/example-text-1\" mode=\"block\"/>\n<$transclude tiddler=\"$:/plugins/tiddlywiki/dynannotate/example-text-2\" mode=\"block\"/>\n</$dynannotate>\n\n<$wikify name=\"wikifiedCode\" text=<<search-example-code>> mode=\"block\" output=\"text\">\n<$codeblock code=<<wikifiedCode>>/>\n</$wikify>\n\n<div class=\"tc-dynannotation-example-info\">\n\n!! Using annotation tiddlers\n\nAnnotation tiddlers can be used to describe annotations. This example references the following annotation tiddlers:\n\n</div>\n\n<<list-links \"[all[shadows+tiddlers]annotate-tiddler[$:/plugins/tiddlywiki/dynannotate/example-text-1]]\">>\n\n<<show-example \"\"\"<$dynannotate\n\tfilter=\"[all[shadows+tiddlers]annotate-tiddler[$:/plugins/tiddlywiki/dynannotate/example-text-1]]\"\n>\n<$transclude tiddler=\"$:/plugins/tiddlywiki/dynannotate/example-text-1\" mode=\"block\"/>\n</$dynannotate>\n\"\"\">>\n\n"},"$:/plugins/tiddlywiki/dynannotate/examples/snippets":{"title":"$:/plugins/tiddlywiki/dynannotate/examples/snippets","tags":"$:/tags/dynannotateExamples","caption":"Snippets","text":"\\define show-example(example)\n<$codeblock code=<<__example__>>/>\n\n//''Displays as:''//\n\n$example$\n\\end\n\n<div class=\"tc-dynannotation-example-info\">\n\n!! Search result snippets\n\nThe `searchDisplay` attribute can be set to `snippet` (instead of the default `overlay`) in order to display contextual snippets around search results. \n\n</div>\n\n<<show-example \"\"\"\n<$dynannotate\n\tsearch=\"the\"\n\tsearchDisplay=\"snippet\"\n><$transclude tiddler=\"$:/plugins/tiddlywiki/dynannotate/example-text-1\" mode=\"block\"/>\n</$dynannotate>\n\"\"\">>\n\n<div class=\"tc-dynannotation-example-info\">\n\n!! Multiple search result snippets\n\nThis example searches across multiple tiddlers and shows snippets for those tiddlers that match.\n\n</div>\n\n<$macrocall $name=\"show-example\" example=\"\"\"\nSearch: <$edit-text tiddler=\"$:/temp/search\" tag=\"input\"/>\n<$list filter=\"[all[tiddlers+shadows]tag[DynannotateDemo]search:text{$:/temp/search}sort[title]]\">\n<dl>\n<dt>\n<$link>\n<$text text=<<currentTiddler>>/>\n</$link>\n</dt>\n<dd>\n<$dynannotate\n\tsearch={{$:/temp/search}}\n\tsearchMode=\"whitespace\"\n\tsearchDisplay=\"snippet\"\n><$transclude tiddler=<<currentTiddler>> mode=\"block\"/>\n</$dynannotate>\n</dd>\n</dl>\n</$list>\n\"\"\"/>\n"},"$:/plugins/tiddlywiki/dynannotate/examples/spotlight":{"title":"$:/plugins/tiddlywiki/dynannotate/examples/spotlight","tags":"$:/tags/dynannotateExamples","caption":"Spotlight","text":"\\define show-example(example)\n<$codeblock code=<<__example__>>/>\n\n//''Displays as:''//\n\n$example$\n\\end\n\n<div class=\"tc-dynannotation-example-info\">\n\n!! Spotlighting an Image\n\n</div>\n\n<<show-example \"\"\"\n<$button>\n<$action-sendmessage $message=\"tm-spotlight-element\" selector=\".tc-dynannotate-spotlight-image-example\"/>\nSpotlight this image\n</$button>\n<div class=\"tc-dynannotate-spotlight-image-example\" style=\"display:inline-block;\">\n{{$:/core/images/globe}}\n</div>\n\"\"\">>\n\n<div class=\"tc-dynannotation-example-info\">\n\n!! Spotlighting a Button\n\n</div>\n\n<<show-example \"\"\"\n<$button class=\"tc-dynannotate-spotlight-button-example\">\n<$action-sendmessage $message=\"tm-spotlight-element\" selector=\".tc-dynannotate-spotlight-button-example\"/>\nSpotlight this button\n</$button>\n\"\"\">>\n\n<div class=\"tc-dynannotation-example-info\">\n\n!! Spotlighting a Text Area\n\n</div>\n\n<<show-example \"\"\"\n<$button>\n<$action-sendmessage $message=\"tm-spotlight-element\" selector=\".tc-dynannotate-spotlight-textarea-example\"/>\nSpotlight this text area\n</$button>\n\n<$edit-text class=\"tc-dynannotate-spotlight-textarea-example\" tag=\"textarea\" tiddler=\"$:/temp/dynannotate/spotlight/demo/text\"/>\n\n\"\"\">>\n\n<div class=\"tc-dynannotation-example-info\">\n\n!! Spotlighting the Sidebar Search Input\n\nThis button will spotlight the sidebar search, but if the sidebar is hidden then it will spotlight the button for showing the sidebar.\n\n</div>\n\n<<show-example \"\"\"\n<$button>\n<$action-sendmessage $message=\"tm-spotlight-element\" selector=\".tc-sidebar-search .tc-popup-handle\" selector-fallback=\".tc-menubar .tc-show-sidebar-btn\"/>\nSpotlight the sidebar search input\n</$button>\n\"\"\">>"},"$:/plugins/tiddlywiki/dynannotate/examples/viewtemplate/text":{"title":"$:/plugins/tiddlywiki/dynannotate/examples/viewtemplate/text","text":"\\define click-annotation-actions()\n<$action-setfield $tiddler=\"$:/temp/dynannotate/demo/annotation-title\" $value=<<annotationTiddler>>/>\n\\end\n\n\\define create-annotation-actions()\n<$action-createtiddler\n\t$basetitle=\"$:/plugins/tiddlywiki/dynannotate/demo-annotation\"\n\t$savetitle={{{ [<currentTiddler>addprefix[$:/state/dynannotate/temp-save-title/]] }}}\n\tannotate-tiddler=<<currentTiddler>>\n\tannotate-text=<<text>>\n\tannotate-prefix=<<prefix>>\n\tannotate-suffix=<<suffix>>\n\tannotate-colour=<<colour>>\n/>\n<$set name=\"popup-coords\" value={{{ [<currentTiddler>addprefix[$:/state/dynannotate/popup-selection/]get[text]] }}}>\n<$action-deletetiddler $tiddler={{{ [<currentTiddler>addprefix[$:/state/dynannotate/popup-selection/]] }}}/>\n<$action-setfield $tiddler=\"$:/temp/dynannotate/demo/annotation-title\" $value={{{ [<currentTiddler>addprefix[$:/state/dynannotate/temp-save-title/]get[text]] }}}/>\n<$action-popup $state={{{ [<currentTiddler>addprefix[$:/state/dynannotate/popup-annotation/]] }}} $coords=<<popup-coords>>/>\n</$set>\n\\end\n\n<$reveal tag=\"div\" class=\"tc-tiddler-body\" type=\"nomatch\" stateTitle=<<folded-state>> text=\"hide\" retain=\"yes\" animate=\"yes\">\n<$list filter=\"[all[current]!has[plugin-type]!field:hide-body[yes]]\">\n<div style=\"position:relative;\"><!-- Needed for the popups to work -->\n<$dynannotate\n\tfilter=\"[all[shadows+tiddlers]!has[draft.of]annotate-tiddler<currentTiddler>]\"\n\tactions=<<click-annotation-actions>>\n\tpopup={{{ [<currentTiddler>addprefix[$:/state/dynannotate/popup-annotation/]] }}}\n\tselection={{{ [<currentTiddler>addprefix[$:/state/dynannotate/selection/]] }}}\n\tselectionPrefix={{{ [<currentTiddler>addprefix[$:/state/dynannotate/selection-prefix/]] }}}\n\tselectionSuffix={{{ [<currentTiddler>addprefix[$:/state/dynannotate/selection-suffix/]] }}}\n\tselectionPopup={{{ [<currentTiddler>addprefix[$:/state/dynannotate/popup-selection/]] }}}\n\tsearch={{$:/temp/search}}\n\tsearchClass=\"tc-dynannotation-search-overlay-blurred\"\n\tsearchCaseSensitive=\"no\"\n\tsearchMinLength={{$:/config/Search/MinLength}}\n>\n<$transclude mode=\"block\">\n<$transclude tiddler=\"$:/language/MissingTiddler/Hint\"/>\n</$transclude>\n</$dynannotate>\n<$reveal type=\"popup\" state={{{ [<currentTiddler>addprefix[$:/state/dynannotate/popup-annotation/]] }}} position=\"belowright\" animate=\"yes\" retain=\"yes\" style=\"overflow-y:hidden;\">\n<div class=\"tc-drop-down-wrapper\">\n<div class=\"tc-drop-down tc-popup-keep\" style=\"max-width:550px;white-space: normal;overflow-y:hidden;\">\n<$tiddler tiddler={{$:/temp/dynannotate/demo/annotation-title}}>\n<p>\n<h2>\nThis is an annotation\n</h2>\n</p>\n<p>\nThe annotation is stored in the tiddler:\n</p>\n<p>\n<$link><$view field=\"title\"/></$link>\n</p>\n<p>\nThe annotated text is ''<$view field=\"annotate-text\"/>''.\n</p>\n<p>\nAnnotation Colour:\n<$macrocall $name='colour-picker' actions=\"\"\"\n<$action-setfield $field=\"annotate-colour\" $value=<<colour-picker-value>>/>\n\"\"\"/>\n</p>\n</$tiddler>\n</div>\n</div>\n</$reveal>\n<$reveal type=\"popup\" state={{{ [<currentTiddler>addprefix[$:/state/dynannotate/popup-selection/]] }}} position=\"belowright\" animate=\"yes\" retain=\"yes\" style=\"overflow-y:hidden;\">\n<div class=\"tc-drop-down-wrapper\">\n<div class=\"tc-drop-down tc-popup-keep\" style=\"max-width:550px;white-space:normal;\">\n<$vars\n\ttext={{{ [<currentTiddler>addprefix[$:/state/dynannotate/selection/]get[text]] }}}\n\tprefix={{{ [<currentTiddler>addprefix[$:/state/dynannotate/selection-prefix/]get[text]] }}}\n\tsuffix={{{ [<currentTiddler>addprefix[$:/state/dynannotate/selection-suffix/]get[text]] }}}\n\tcolour={{{ [<currentTiddler>addprefix[$:/state/dynannotate/annotation-colour/]get[text]] }}}\n>\n<$button actions=<<create-annotation-actions>>>\nCreate annotation\n</$button>\n<p>\nText: <$text text=<<text>>/>\n</p>\n<p>\nPrefix: <$text text=<<prefix>>/>\n</p>\n<p>\nSuffix: <$text text=<<suffix>>/>\n</p>\n</$vars>\n</div>\n</div>\n</$reveal>\n</div>\n</$list>\n</$reveal>\n"},"$:/plugins/tiddlywiki/dynannotate/examples/viewtemplate":{"title":"$:/plugins/tiddlywiki/dynannotate/examples/viewtemplate","caption":"View Template","tags":"$:/tags/dynannotateExamples","text":"\\define show-example(example)\n<$codeblock code=<<__example__>>/>\n\n//''Displays as:''//\n\n$example$\n\\end\n\n<div class=\"tc-dynannotation-example-info\">\n\n!! Using Dynannotate in the view template\n\nThis example shows how to override the core view template with a custom template that includes dynannotate. It is disabled by default but can be enabled by clicking the corresponding button below. The example transcludes the [[Searching in TiddlyWiki|$:/plugins/tiddlywiki/dynannotate/searching-in-tiddlywiki]] tiddler for illustration purposes, but the chosen view template is applied to all open tiddlers.\n\nOnce enabled, this example demonstrates several features of Dynannotate:\n\n* Highlight text within tiddler bodies -- any text entered in the sidebar search input will be highlighted (in all tiddlers)\n* Create annotations (on any tiddler) by selecting text and then clicking //Create annotation// in the resulting dropdown\n\n(Clicking the buttons below either puts the custom body view template [[$:/plugins/tiddlywiki/dynannotate/examples/viewtemplate/text]] in front of the default in the [[View Template Body Cascade|https://tiddlywiki.com/#View%20Template%20Body%20Cascade]] or removes it from the cascade).\n\n</div>\n\n<$button>\n<$action-setfield $tiddler=\"$:/config/ViewTemplateBodyFilters/dynannotate\" tags=\"$:/tags/ViewTemplateBodyFilter\" text=\"[[$:/plugins/tiddlywiki/dynannotate/examples/viewtemplate/text]]\" list-before=\"$:/config/ViewTemplateBodyFilters/default\"/>\nClick here to use the custom view template\n</$button>\n\n<$button>\n<$action-deletetiddler $tiddler=\"$:/config/ViewTemplateBodyFilters/dynannotate\"/>\nClick here to revert to the core view template\n</$button>\n\n<p/>\n\n<div class=\"tc-dynannotation-example-info\">\n\n!! Using Dynannotate to highlight text on a per-tiddler basis\n\nUsing state tiddlers containing the text which is to be highlighted, Dynannotate highlights can be applied on a per-tiddler basis when used in the body view template. This can also be combined with the [[GenesisWidget]] to insert the Dynannotate widget into the DOM only when such a highlight is configured.\n\nThis example uses a transcluded tiddler, but Dynannotate could be added to the view template (to show highlights in all parts of a tiddler) or to the body view template (to show highlights only in the tiddler body) in the same way. Note how in this example, Dynannotate only affects the transcluded content.\n\n</div>\n\n<<show-example \"\"\"<$button set=\"$:/state/dynannotate/examples/viewtemplate\" setTo=\"tiddler\">Highlight \"tiddler\"</$button>\n<$button set=\"$:/state/dynannotate/examples/viewtemplate\" setTo=\"created\">Highlight \"created\"</$button>\n<$button set=\"$:/state/dynannotate/examples/viewtemplate\" setTo=\"\">Clear highlights</$button>\n<p/>\n<$let dynannotateText={{$:/state/dynannotate/examples/viewtemplate}}>\n    <$genesis $type={{{ [<dynannotateText>!is[blank]then[$dynannotate]] }}}\n              search=<<dynannotateText>>\n              searchDisplay=\"overlay\"\n              searchClass=\"tc-dynannotation-search-overlay-animated\"\n              searchCaseSensitive=\"no\">\n        <div class=\"tc-dynannotate-example-frame\">\n\n            !! `$:/core/ui/ControlPanel/TiddlerFields`:\n            <$transclude tiddler=\"$:/core/ui/ControlPanel/TiddlerFields\" mode=\"block\" />\n        </div>\n    </$genesis>\n</$let>\n\"\"\">>\n\nWhen used in the view template, the state tiddler title could be derived from the current tiddler, e.g. `<$let dynannotateText={{{ [<currentTiddler>addprefix[$:/state/dynannotate/]get[text]] }}}>`, to configure highlights for each tiddler separately.\n"},"$:/plugins/tiddlywiki/dynannotate/dynannotate.js":{"title":"$:/plugins/tiddlywiki/dynannotate/dynannotate.js","text":"/*\\\ntitle: $:/plugins/tiddlywiki/dynannotate/dynannotate.js\ntype: application/javascript\nmodule-type: widget\n\nDynannotate widget\n\n\\*/\n\n\"use strict\";\n\nvar TextMap = require(\"$:/plugins/tiddlywiki/dynannotate/textmap.js\").TextMap;\n\nvar Widget = require(\"$:/core/modules/widgets/widget.js\").widget;\n\nvar Popup = require(\"$:/core/modules/utils/dom/popup.js\");\n\nvar DynannotateWidget = function(parseTreeNode,options) {\n\tthis.initialise(parseTreeNode,options);\n};\n\n/*\nInherit from the base widget class\n*/\nDynannotateWidget.prototype = new Widget();\n\n/*\nRender this widget into the DOM\n*/\nDynannotateWidget.prototype.render = function(parent,nextSibling) {\n\tvar self = this;\n\tthis.parentDomNode = parent;\n\tthis.computeAttributes();\n\tthis.execute();\n\t// Create our DOM nodes\n\tvar isSnippetMode = this.isSnippetMode();\n\tthis.domContent = $tw.utils.domMaker(\"div\",{\n\t\t\"class\": \"tc-dynannotation-selection-container\",\n\t\tdocument: this.document\n\t});\n\tif(isSnippetMode) {\n\t\tthis.domContent.setAttribute(\"hidden\",\"hidden\");\n\t}\n\tthis.domAnnotations = $tw.utils.domMaker(\"div\",{\n\t\t\"class\": \"tc-dynannotation-annotation-wrapper\",\n\t\tdocument: this.document\n\t});\n\tthis.domSnippets = $tw.utils.domMaker(\"div\",{\n\t\t\"class\": \"tc-dynannotation-snippet-wrapper\",\n\t\tdocument: this.document\n\t});\n\tthis.domSearches = $tw.utils.domMaker(\"div\",{\n\t\t\"class\": \"tc-dynannotation-search-wrapper\",\n\t\tdocument: this.document\n\t});\n\tthis.domWrapper = $tw.utils.domMaker(\"div\",{\n\t\t\"class\": \"tc-dynannotation-wrapper\",\n\t\tchildren: [this.domContent,this.domAnnotations,this.domSnippets,this.domSearches],\n\t\tdocument: this.document\n\t})\n\tparent.insertBefore(this.domWrapper,nextSibling);\n\tthis.domNodes.push(this.domWrapper);\n\t// Apply the selection tracker data to the DOM\n\tif(!isSnippetMode) {\n\t\tthis.applySelectionTrackerData();\n\t}\n\t// Render our child widgets\n\tthis.renderChildren(this.domContent,null);\n\tif(!this.document.isTiddlyWikiFakeDom) {\n\t\tif(isSnippetMode) {\n\t\t\t// Apply search snippets\n\t\t\tthis.applySnippets();\n\t\t} else {\n\t\t\t// Get the list of annotation tiddlers\n\t\t\tthis.getAnnotationTiddlers();\n\t\t\t// Apply annotations\n\t\t\tthis.applyAnnotations();\n\t\t\t// Apply search overlays\n\t\t\tthis.applySearch();\n\t\t}\n\t}\n\t// Save the width of the wrapper so that we can tell when it changes\n\tthis.wrapperWidth = this.domWrapper.offsetWidth;\n};\n\n/*\nCompute the internal state of the widget\n*/\nDynannotateWidget.prototype.execute = function() {\n\t// Make the child widgets\n\tthis.makeChildWidgets();\n};\n\nDynannotateWidget.prototype.isSnippetMode = function() {\n\treturn this.getAttribute(\"searchDisplay\") === \"snippet\";\n}\n\n/*\nSave the data attributes required by the selection tracker\n*/\nDynannotateWidget.prototype.applySelectionTrackerData = function() {\n\tif(this.hasAttribute(\"selection\")) {\n\t\tthis.domContent.setAttribute(\"data-annotation-selection-save\",this.getAttribute(\"selection\"));\n\t} else {\n\t\tthis.domContent.removeAttribute(\"data-annotation-selection-save\");\n\t}\n\tif(this.hasAttribute(\"selectionPopup\")) {\n\t\tthis.domContent.setAttribute(\"data-annotation-selection-popup\",this.getAttribute(\"selectionPopup\"));\n\t} else {\n\t\tthis.domContent.removeAttribute(\"data-annotation-selection-popup\");\n\t}\n\tif(this.hasAttribute(\"selectionPrefix\")) {\n\t\tthis.domContent.setAttribute(\"data-annotation-selection-prefix-save\",this.getAttribute(\"selectionPrefix\"));\n\t} else {\n\t\tthis.domContent.removeAttribute(\"data-annotation-selection-prefix-save\");\n\t}\n\tif(this.hasAttribute(\"selectionSuffix\")) {\n\t\tthis.domContent.setAttribute(\"data-annotation-selection-suffix-save\",this.getAttribute(\"selectionSuffix\"));\n\t} else {\n\t\tthis.domContent.removeAttribute(\"data-annotation-selection-suffix-save\");\n\t}\n};\n\n/*\nCreate overlay dom elements to cover a specified range\n\noptions include:\n\tstartNode: Start node of range\n\tstartOffset: Start offset of range\n\tendNode: End node of range\n\tendOffset: End offset of range\n\tclassName: Optional classname for the overlay\n\twrapper: Wrapper dom node for the overlays\n\tcolour: Optional CSS colour for the overlay\n\tblendMode: Optional CSS mix blend mode for the overlay\n\tonclick: Optional click event handler for the overlay\n*/\nDynannotateWidget.prototype.createOverlay = function(options) {\n\tvar self = this;\n\t// Create a range covering the text\n\tvar range = this.document.createRange();\n\trange.setStart(options.startNode,options.startOffset);\n\trange.setEnd(options.endNode,options.endOffset);\n\t// Get the position of the range\n\tvar rects = range.getClientRects();\n\tif(rects) {\n\t\t// Paint each rectangle\n\t\tvar parentRect = this.domContent.getBoundingClientRect();\n\t\t$tw.utils.each(rects,function(rect) {\n\t\t\tvar domOverlay = self.document.createElement(\"div\");\n\t\t\tdomOverlay.className = (options.className || \"\") + \" tc-dynaview-request-refresh-on-resize\";\n\t\t\tdomOverlay.style.top = (rect.top - parentRect.top) + \"px\";\n\t\t\tdomOverlay.style.left = (rect.left - parentRect.left) + \"px\";\n\t\t\tdomOverlay.style.width = rect.width + \"px\";\n\t\t\tdomOverlay.style.height = rect.height + \"px\";\n\t\t\tdomOverlay.style.backgroundColor = options.colour;\n\t\t\tdomOverlay.style.mixBlendMode = options.blendMode;\n\t\t\tif(options.onclick) {\n\t\t\t\tdomOverlay.addEventListener(\"click\",function(event) {\n\t\t\t\t\tvar modifierKey = event.ctrlKey && !event.shiftKey ? \"ctrl\" : event.shiftKey && !event.ctrlKey ? \"shift\" : event.ctrlKey && event.shiftKey ? \"ctrl-shift\" : \"normal\";\n\t\t\t\t\toptions.onclick(event,domOverlay,modifierKey);\n\t\t\t\t},false);\n\t\t\t}\n\t\t\toptions.wrapper.appendChild(domOverlay);\n\t\t});\n\t}\n};\n\nDynannotateWidget.prototype.getAnnotationTiddlers = function() {\n\tthis.annotationTiddlers = this.wiki.filterTiddlers(this.getAttribute(\"filter\",\"\"),this);\n};\n\nDynannotateWidget.prototype.removeAnnotations = function() {\n\twhile(this.domAnnotations.hasChildNodes()) {\n\t\tthis.domAnnotations.removeChild(this.domAnnotations.firstChild);\n\t}\n};\n\nDynannotateWidget.prototype.applyAnnotations = function() {\n\tvar self = this;\n\t// Remove any previous annotation overlays\n\tthis.removeAnnotations();\n\t// Don't do anything if there are no annotations to apply\n\tif(this.annotationTiddlers.length === 0 && !this.hasAttribute(\"target\")) {\n\t\treturn;\n\t}\n\t// Build the map of the text content\n\tvar textMap = new TextMap(this.domContent);\n\t// We'll dynamically build the click event handler so that we can reuse it\n\tvar clickHandlerFn = function(title) {\n\t\treturn function(event,domOverlay,modifierKey) {\n\t\t\tvar bounds = domOverlay.getBoundingClientRect();\n\t\t\tself.invokeActionString(self.getAttribute(\"actions\"),self,event,{\n\t\t\t\tannotationTiddler: title,\n\t\t\t\tmodifier: modifierKey,\n\t\t\t\t\"tv-selection-posx\": (bounds.left).toString(),\n\t\t\t\t\"tv-selection-posy\": (bounds.top).toString(),\n\t\t\t\t\"tv-selection-width\": (bounds.width).toString(),\n\t\t\t\t\"tv-selection-height\": (bounds.height).toString(),\n\t\t\t\t\"tv-selection-coords\": Popup.buildCoordinates(Popup.coordinatePrefix.csOffsetParent,bounds)\n\t\t\t});\n\t\t\tif(self.hasAttribute(\"popup\")) {\n\t\t\t\t$tw.popup.triggerPopup({\n\t\t\t\t\tdomNode: domOverlay,\n                    title: self.getAttribute(\"popup\"),\n                    floating: self.getAttribute(\"floating\"),\n\t\t\t\t\twiki: self.wiki\n\t\t\t\t});\n\t\t\t}\n\t\t};\n\t};\n\t// Draw the overlay for the \"target\" attribute\n\tif(this.hasAttribute(\"target\")) {\n\t\tvar result = textMap.findText(this.getAttribute(\"target\"),this.getAttribute(\"targetPrefix\"),this.getAttribute(\"targetSuffix\"));\n\t\tif(result) {\n\t\t\tthis.createOverlay({\n\t\t\t\tstartNode: result.startNode,\n\t\t\t\tstartOffset: result.startOffset,\n\t\t\t\tendNode: result.endNode,\n\t\t\t\tendOffset: result.endOffset,\n\t\t\t\twrapper: self.domAnnotations,\n\t\t\t\tclassName: \"tc-dynannotation-annotation-overlay\",\n\t\t\t\tonclick: clickHandlerFn(null)\n\t\t\t});\n\t\t}\n\t}\n\t// Draw the overlays for each annotation tiddler\n\t$tw.utils.each(this.annotationTiddlers,function(title) {\n\t\tvar tiddler = self.wiki.getTiddler(title),\n\t\t\tannotateText = tiddler.fields[\"annotate-text\"],\n\t\t\tannotatePrefix = tiddler.fields[\"annotate-prefix\"],\n\t\t\tannotateSuffix = tiddler.fields[\"annotate-suffix\"];\n\t\tif(tiddler && annotateText) {\n\t\t\tvar result = textMap.findText(annotateText,annotatePrefix,annotateSuffix);\n\t\t\tif(result) {\n\t\t\t\tself.createOverlay({\n\t\t\t\t\tstartNode: result.startNode,\n\t\t\t\t\tstartOffset: result.startOffset,\n\t\t\t\t\tendNode: result.endNode,\n\t\t\t\t\tendOffset: result.endOffset,\n\t\t\t\t\twrapper: self.domAnnotations,\n\t\t\t\t\tclassName: \"tc-dynannotation-annotation-overlay\",\n\t\t\t\t\tcolour: tiddler.fields[\"annotate-colour\"],\n\t\t\t\t\tblendMode: tiddler.fields[\"annotate-blend-mode\"],\n\t\t\t\t\tonclick: clickHandlerFn(title)\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t});\n};\n\nDynannotateWidget.prototype.removeSearch = function() {\n\twhile(this.domSearches.hasChildNodes()) {\n\t\tthis.domSearches.removeChild(this.domSearches.firstChild);\n\t}\n};\n\nDynannotateWidget.prototype.applySearch = function() {\n\tvar self = this;\n\t// Remove any previous search overlays\n\tthis.removeSearch();\n\t// Gather parameters\n\tvar searchString = this.getAttribute(\"search\",\"\"),\n\t\tsearchMode = this.getAttribute(\"searchMode\"),\n\t\tsearchCaseSensitive = this.getAttribute(\"searchCaseSensitive\",\"yes\") === \"yes\",\n\t\tsearchMinLength = parseInt(this.getAttribute(\"searchMinLength\",\"1\"),10) || 1;\n\t// Bail if search string too short\n\tif(searchString.length < searchMinLength) {\n\t\treturn;\n\t}\n\t// Build the map of the text content\n\tvar textMap = new TextMap(this.domContent);\n\t// Search for the string\n\tvar matches = textMap.search(this.getAttribute(\"search\",\"\"),{\n\t\tmode: this.getAttribute(\"searchMode\"),\n\t\tcaseSensitive: this.getAttribute(\"searchCaseSensitive\",\"yes\") === \"yes\"\n\t});\n\t// Create overlays for each match\n\t$tw.utils.each(matches,function(match) {\n\t\tself.createOverlay({\n\t\t\tstartNode: match.startNode,\n\t\t\tstartOffset: match.startOffset,\n\t\t\tendNode: match.endNode,\n\t\t\tendOffset: match.endOffset,\n\t\t\twrapper: self.domSearches,\n\t\t\tclassName: \"tc-dynannotation-search-overlay \" + self.getAttribute(\"searchClass\",\"\")\n\t\t});\n\t});\n};\n\nDynannotateWidget.prototype.removeSnippets = function() {\n\twhile(this.domSnippets.hasChildNodes()) {\n\t\tthis.domSnippets.removeChild(this.domSnippets.firstChild);\n\t}\n};\n\nDynannotateWidget.prototype.applySnippets = function() {\n\tvar self = this,\n\t\tcontextLength = parseInt(this.getAttribute(\"snippetContextLength\",\"33\"),10) || 0;\n\t// Build the map of the text content\n\tvar textMap = new TextMap(this.domContent);\n\t// Remove any previous snippets\n\tthis.removeSnippets();\n\t// Gather parameters\n\tvar searchString = this.getAttribute(\"search\",\"\"),\n\t\tsearchMode = this.getAttribute(\"searchMode\"),\n\t\tsearchCaseSensitive = this.getAttribute(\"searchCaseSensitive\",\"yes\") === \"yes\",\n\t\tsearchMinLength = parseInt(this.getAttribute(\"searchMinLength\",\"1\"),10) || 1;\n\t// Build the map of the text content\n\tvar textMap = new TextMap(this.domContent);\n\t// Search for the string\n\tvar matches = textMap.search(this.getAttribute(\"search\",\"\"),{\n\t\tmode: this.getAttribute(\"searchMode\"),\n\t\tcaseSensitive: this.getAttribute(\"searchCaseSensitive\",\"no\") === \"yes\"\n\t});\n\t// Output a snippet for each match\n\tif(matches && matches.length > 0) {\n\t\tvar merged = false, // Keep track of whether the context of the previous match merges into this one\n\t\t\tellipsis = String.fromCharCode(8230),\n\t\t\tcontainer = null; // Track the container so that we can reuse the same container for merged matches\n\t\t$tw.utils.each(matches,function(match,index) {\n\t\t\t// Create a container if we're not reusing it\n\t\t\tif(!container) {\n\t\t\t\tcontainer = $tw.utils.domMaker(\"div\",{\n\t\t\t\t\t\"class\": \"tc-dynannotate-snippet\"\n\t\t\t\t});\n\t\t\t\tself.domSnippets.appendChild(container);\n\t\t\t}\n\t\t\t// Output the preceding context if it wasn't merged into the previous match\n\t\t\tif(!merged) {\n\t\t\t\tcontainer.appendChild($tw.utils.domMaker(\"span\",{\n\t\t\t\t\ttext: (match.startPos < contextLength ? \"\" : ellipsis) +\n\t\t\t\t\t\ttextMap.string.slice(Math.max(match.startPos - contextLength,0),match.startPos),\n\t\t\t\t\t\"class\": \"tc-dynannotate-snippet-context\"\n\t\t\t\t}));\n\t\t\t}\n\t\t\t// Output the match\n\t\t\tcontainer.appendChild($tw.utils.domMaker(\"span\",{\n\t\t\t\ttext: textMap.string.slice(match.startPos,match.endPos),\n\t\t\t\t\"class\": \"tc-dynannotate-snippet-highlight \" + self.getAttribute(\"searchClass\",\"\")\n\t\t\t}));\n\t\t\t// Does the context of this match merge into the next?\n\t\t\tmerged = index < matches.length - 1 && matches[index + 1].startPos - match.endPos <= 2 * contextLength;\n\t\t\tif(merged) {\n\t\t\t\t// If they're merged, use the context up until the next match\n\t\t\t\tcontainer.appendChild($tw.utils.domMaker(\"span\",{\n\t\t\t\t\ttext: textMap.string.slice(match.endPos,matches[index + 1].startPos),\n\t\t\t\t\t\"class\": \"tc-dynannotate-snippet-context\"\n\t\t\t\t}));\n\t\t\t} else {\n\t\t\t\t// If they're not merged, use the context up to the end\n\t\t\t\tcontainer.appendChild($tw.utils.domMaker(\"span\",{\n\t\t\t\t\ttext: textMap.string.slice(match.endPos,match.endPos + contextLength) +\n\t\t\t\t\t\t((match.endPos + contextLength) >= textMap.string.length ? \"\" : ellipsis),\n\t\t\t\t\t\"class\": \"tc-dynannotate-snippet-context\"\n\t\t\t\t}));\n\t\t\t}\n\t\t\t// Reuse the next container if we're merged\n\t\t\tif(!merged) {\n\t\t\t\tcontainer = null;\n\t\t\t}\n\t\t});\n\t}\n};\n\n/*\nSelectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering\n*/\nDynannotateWidget.prototype.refresh = function(changedTiddlers) {\n\t// Get the changed attributes\n\tvar changedAttributes = this.computeAttributes();\n\t// Refresh completely if the \"searchDisplay\" attribute has changed\n\tif(changedAttributes.searchDisplay) {\n\t\tthis.refreshSelf();\n\t\treturn true;\n\t}\n\t// Check whether we're in snippet mode\n\tvar isSnippetMode = this.isSnippetMode();\n\t// Refresh the child widgets\n\tvar childrenDidRefresh = this.refreshChildren(changedTiddlers);\n\t// Reapply the selection tracker data to the DOM\n\tif(changedAttributes.selection || changedAttributes.selectionPrefix || changedAttributes.selectionSuffix || changedAttributes.selectionPopup) {\n\t\tthis.applySelectionTrackerData();\n\t}\n\t// Reapply the annotations if the children refreshed or the main wrapper resized\n\tvar wrapperWidth = this.domWrapper.offsetWidth,\n\t\thasResized = wrapperWidth !== this.wrapperWidth || changedTiddlers[\"$:/state/DynaView/ViewportDimensions/ResizeCount\"],\n\t\toldAnnotationTiddlers = this.annotationTiddlers;\n\tthis.getAnnotationTiddlers();\n\tif(!isSnippetMode && (\n\t\tchildrenDidRefresh ||\n\t\thasResized ||\n\t\tchangedAttributes.target ||\n\t\tchangedAttributes.targetPrefix ||\n\t\tchangedAttributes.targetSuffix ||\n\t\tchangedAttributes.filter ||\n\t\tchangedAttributes.actions ||\n\t\tchangedAttributes.popup ||\n\t\t!$tw.utils.isArrayEqual(oldAnnotationTiddlers,this.annotationTiddlers) ||\n\t\tthis.annotationTiddlers.find(function(title) {\n\t\t\treturn changedTiddlers[title];\n\t\t}) !== undefined\n\t)) {\n\t\tthis.applyAnnotations();\n\t}\n\tif(!isSnippetMode && (\n\t\tchildrenDidRefresh ||\n\t\thasResized ||\n\t\tchangedAttributes.search ||\n\t\tchangedAttributes.searchMinLength ||\n\t\tchangedAttributes.searchClass ||\n\t\tchangedAttributes.searchMode ||\n\t\tchangedAttributes.searchCaseSensitive\n\t)) {\n\t\tthis.applySearch();\n\t}\n\tif(isSnippetMode && (\n\t\tchildrenDidRefresh ||\n\t\thasResized ||\n\t\tchangedAttributes.search ||\n\t\tchangedAttributes.searchMinLength ||\n\t\tchangedAttributes.searchClass ||\n\t\tchangedAttributes.searchMode ||\n\t\tchangedAttributes.searchCaseSensitive\n\t)) {\n\t\tthis.applySnippets();\n\t}\n\tthis.wrapperWidth = wrapperWidth;\n\treturn childrenDidRefresh;\n};\n\nexports.dynannotate = DynannotateWidget;\n","type":"application/javascript","module-type":"widget"},"$:/plugins/tiddlywiki/dynannotate/element-spotlight.js":{"title":"$:/plugins/tiddlywiki/dynannotate/element-spotlight.js","text":"/*\\\ntitle: $:/plugins/tiddlywiki/dynannotate/element-spotlight.js\ntype: application/javascript\nmodule-type: library\n\nManages the element spotlight effect\n\n\\*/\n\"use strict\";\n\nfunction ElementSpotlight() {\n\tthis.animationStartTime; // Undefined if no animation is in  progress\n\t// Create DOM nodes\n\tthis.spotlightElement = $tw.utils.domMaker(\"div\",{\n\t\t\"class\": \"tc-dynannotate-spotlight\"\n\t});\n\tthis.spotlightWrapper = $tw.utils.domMaker(\"div\",{\n\t\t\"class\": \"tc-dynannotate-spotlight-wrapper\",\n\t\tchildren: [\n\t\t\tthis.spotlightElement\n\t\t]\n\t});\n\tdocument.body.appendChild(this.spotlightWrapper);\n}\n\n/*\nReturn the first visible element that matches a selector\n*/\nElementSpotlight.prototype.querySelectorSafe = function(selector) {\n\tvar targetNodes;\n\t// Get the matching elements\n\ttry {\n\t\ttargetNodes = document.querySelectorAll(selector);\n\t} catch(e) {\n\t\tconsole.log(\"Error with selector: \" + selector);\n\t}\n\tif(!targetNodes) {\n\t\treturn undefined;\n\t}\n\t// Remove any elements from the start of the list that are hidden, or have hidden ancestors\n\tvar didRemoveFirstEntry;\n\tdo {\n\t\tdidRemoveFirstEntry = false;\n\t\tvar hasHiddenAncestor = false,\n\t\t\tn = targetNodes[0];\n\t\twhile(n) {\n\t\t\tif(n.hidden || (n instanceof Element && window.getComputedStyle(n).display === \"none\")) {\n\t\t\t\thasHiddenAncestor = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tn = n.parentNode;\n\t\t}\n\t\tif(hasHiddenAncestor) {\n\t\t\t// Remove first entry from targetNodes array\n\t\t\ttargetNodes = [].slice.call(targetNodes, 1); \n\t\t\tdidRemoveFirstEntry = true;\n\t\t}\n\t} while(didRemoveFirstEntry)\n\t// Return the first result\n\treturn targetNodes[0];\n};\n\nElementSpotlight.prototype.positionSpotlight = function(x,y,innerRadius,outerRadius,opacity) {\n\tthis.spotlightElement.style.display = \"block\";\n\tthis.spotlightElement.style.backgroundImage = \"radial-gradient(circle at \" + (x / document.documentElement.clientWidth * 100) + \"% \" + (y / document.documentElement.clientHeight * 100) + \"%, transparent \" + innerRadius + \"px, rgba(0, 0, 0, \" + opacity + \") \" + outerRadius + \"px)\";\n};\n\nElementSpotlight.prototype.easeInOut = function(v) {\n\treturn (Math.sin((v - 0.5) * Math.PI) + 1) / 2;\n};\n\n/*\nShine a spotlight on the first element that matches an array of selectors\n*/\nElementSpotlight.prototype.shineSpotlight = function(selectors) {\n\tvar self = this;\n\tfunction animationLoop(selectors) {\n\t\t// Calculate how far through the animation we are\n\t\t// 0...1 = zoom in\n\t\t// 1...2 = hold\n\t\t// 2...3 = fade out\n\t\tvar now = new Date(),\n\t\t\tt = (now - self.animationStartTime) / ($tw.utils.getAnimationDuration() * 2);\n\t\tt = t >= 3 ? 3 : t;\n\t\t// Query the selector for the target element\n\t\tvar targetNode, selectorIndex = 0;\n\t\twhile(!targetNode && selectorIndex < selectors.length) {\n\t\t\ttargetNode = self.querySelectorSafe(selectors[selectorIndex]);\n\t\t\tselectorIndex += 1;\n\t\t}\n\t\t// Position the spotlight if we've got the target\n\t\tif(targetNode) {\n\t\t\tvar rect = targetNode.getBoundingClientRect();\n\t\t\tvar innerRadius, outerRadius, opacity;\n\t\t\tif(t <= 1) {\n\t\t\t\tt = self.easeInOut(t);\n\t\t\t\tinnerRadius = rect.width / 2 + (window.innerWidth * 2 * (1 - t));\n\t\t\t\touterRadius = rect.width + (window.innerWidth * 3 * (1 - t));\n\t\t\t\topacity = 0.2 + t / 4;\n\t\t\t} else if(t <= 2) {\n\t\t\t\tinnerRadius = rect.width / 2;\n\t\t\t\touterRadius = rect.width;\n\t\t\t\topacity = 0.45;\n\t\t\t} else {\n\t\t\t\tt = self.easeInOut(3 - t);\n\t\t\t\tinnerRadius = rect.width / 2 + (window.innerWidth * 2 * (1 - t));\n\t\t\t\touterRadius = rect.width + (window.innerWidth * 3 * (1 - t));\n\t\t\t\topacity = t / 3;\n\t\t\t}\n\t\t\tself.positionSpotlight((rect.left + rect.right) / 2,(rect.top + rect.bottom) / 2,innerRadius,outerRadius,opacity);\n\t\t} else {\n\t\t\tself.spotlightElement.style.display = \"none\";\n\t\t}\n\t\t// Call the next frame unless we're at the end\n\t\tif(t <= 3) {\n\t\t\twindow.requestAnimationFrame(function () {\n\t\t\t\tanimationLoop(selectors);\n\t\t\t});\n\t\t} else {\n\t\t\t// End the animation if we've exceeded the time limit\n\t\t\tself.animationStartTime = undefined;\n\t\t}\n\t}\n\tthis.animationStartTime = new Date();\n\twindow.requestAnimationFrame(function () {\n\t\tanimationLoop(selectors);\n\t});\n};\n\nexports.ElementSpotlight = ElementSpotlight;\n","type":"application/javascript","module-type":"library"},"$:/plugins/tiddlywiki/dynannotate/legacy-selection-tracker.js":{"title":"$:/plugins/tiddlywiki/dynannotate/legacy-selection-tracker.js","text":"/*\\\ntitle: $:/plugins/tiddlywiki/dynannotate/legacy-selection-tracker.js\ntype: application/javascript\nmodule-type: library\n\nLegacy version of the dyannotate background daemon to track the selection\n\n\\*/\n\n\"use strict\";\n\nvar TextMap = require(\"$:/plugins/tiddlywiki/dynannotate/textmap.js\").TextMap;\n\nfunction LegacySelectionTracker(wiki,options) {\n\toptions = options || {};\n\tvar self = this;\n\tthis.wiki = wiki;\n\tthis.allowBlankSelectionPopup = options.allowBlankSelectionPopup;\n\tthis.selectionPopupTitle = null;\n\tdocument.addEventListener(\"selectionchange\",function(event) {\n\t\tvar selection = document.getSelection();\n\t\tif(selection && (selection.type === \"Range\" || (self.allowBlankSelectionPopup && !self.selectionPopupTitle))) {\n\t\t\t// Look for the selection containers for each of the two ends of the selection\n\t\t\tvar anchorContainer = self.findSelectionContainer(selection.anchorNode),\n\t\t\t\tfocusContainer = self.findSelectionContainer(selection.focusNode);\n\t\t\t// If either end of the selection then we ignore it\n\t\t\tif(!!anchorContainer || !!focusContainer) {\n\t\t\t\tvar selectionRange = selection.getRangeAt(0);\n\t\t\t\t// Check for the selection spilling outside the starting container\n\t\t\t\tif((anchorContainer !== focusContainer) || (selectionRange.startContainer.nodeType !== Node.TEXT_NODE && selectionRange.endContainer.nodeType !== Node.TEXT_NODE)) {\n\t\t\t\t\tif(self.selectionPopupTitle) {\n\t\t\t\t\t\tself.wiki.deleteTiddler(self.selectionPopupTitle);\n\t\t\t\t\t\tself.selectionPopupTitle = null;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tself.selectionSaveTitle = anchorContainer.getAttribute(\"data-annotation-selection-save\");\n\t\t\t\t\tself.selectionPrefixSaveTitle = anchorContainer.getAttribute(\"data-annotation-selection-prefix-save\");\n\t\t\t\t\tself.selectionSuffixSaveTitle = anchorContainer.getAttribute(\"data-annotation-selection-suffix-save\");\n\t\t\t\t\tself.selectionPopupTitle = anchorContainer.getAttribute(\"data-annotation-selection-popup\");\n\t\t\t\t\t// The selection is a range so we trigger the popup\n\t\t\t\t\tif(self.selectionPopupTitle) {\n\t\t\t\t\t\tvar selectionRectangle = selectionRange.getBoundingClientRect(),\n\t\t\t\t\t\t\ttrackingRectangle = anchorContainer.getBoundingClientRect();\n\t\t\t\t\t\t$tw.popup.triggerPopup({\n\t\t\t\t\t\t\tdomNode: null,\n\t\t\t\t\t\t\tdomNodeRect: {\n\t\t\t\t\t\t\t\tleft: selectionRectangle.left - trackingRectangle.left,\n\t\t\t\t\t\t\t\ttop: selectionRectangle.top - trackingRectangle.top,\n\t\t\t\t\t\t\t\twidth: selectionRectangle.width,\n\t\t\t\t\t\t\t\theight: selectionRectangle.height\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tforce: true,\n\t\t\t\t\t\t\tfloating: true,\n\t\t\t\t\t\t\ttitle: self.selectionPopupTitle,\n\t\t\t\t\t\t\twiki: self.wiki\n\t\t\t\t\t\t});\t\t\t\t\t\t\n\t\t\t\t\t}\n\t\t\t\t\t// Write the selection text to the specified tiddler\n\t\t\t\t\tif(self.selectionSaveTitle) {\n\t\t\t\t\t\t// Note that selection.toString() normalizes whitespace but selection.getRangeAt(0).toString() does not\n\t\t\t\t\t\tvar text = selectionRange.toString();\n\t\t\t\t\t\tself.wiki.addTiddler(new $tw.Tiddler({title: self.selectionSaveTitle, text: text}));\n\t\t\t\t\t\t// Build a textmap of the container so that we can find the prefix and suffix\n\t\t\t\t\t\tvar textMap = new TextMap(anchorContainer);\n\t\t\t\t\t\t// Find the selection start in the text map and hence extract the prefix and suffix\n\t\t\t\t\t\tvar context = textMap.extractContext(selectionRange.startContainer,selectionRange.startOffset,text);\n\t\t\t\t\t\t// Save the prefix and suffix\n\t\t\t\t\t\tif(context) {\n\t\t\t\t\t\t\tif(self.selectionPrefixSaveTitle) {\n\t\t\t\t\t\t\t\tself.wiki.addTiddler(new $tw.Tiddler({title: self.selectionPrefixSaveTitle, text: context.prefix}));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif(self.selectionSuffixSaveTitle) {\n\t\t\t\t\t\t\t\tself.wiki.addTiddler(new $tw.Tiddler({title: self.selectionSuffixSaveTitle, text: context.suffix}));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// If the selection is a caret we clear any active popup\n\t\t\tif(self.selectionPopupTitle) {\n\t\t\t\tself.wiki.deleteTiddler(self.selectionPopupTitle);\n\t\t\t\tself.selectionPopupTitle = null;\n\t\t\t}\n\t\t}\n\t});\n}\n\nLegacySelectionTracker.prototype.findSelectionContainer = function findSelectionContainer(domNode) {\n\tif(domNode && domNode.nodeType === Node.ELEMENT_NODE && domNode.classList.contains(\"tc-dynannotation-selection-container\")) {\n\t\treturn domNode;\n\t}\n\tif(domNode && domNode.parentNode) {\n\t\treturn findSelectionContainer(domNode.parentNode);\n\t}\n\treturn null;\n};\n\nexports.LegacySelectionTracker = LegacySelectionTracker;\n","type":"application/javascript","module-type":"library"},"$:/plugins/tiddlywiki/dynannotate/selection-tracker.js":{"title":"$:/plugins/tiddlywiki/dynannotate/selection-tracker.js","text":"/*\\\ntitle: $:/plugins/tiddlywiki/dynannotate/selection-tracker.js\ntype: application/javascript\nmodule-type: library\n\nBackground daemon to track the selection\n\n\\*/\n\n\"use strict\";\n\nfunction SelectionTracker(wiki,options) {\n\toptions = options || {};\n\tvar self = this;\n\tthis.wiki = wiki;\n\tvar timerId = null;\n\tdocument.addEventListener(\"selectionchange\",function(event) {\n\t\tif(timerId) {\n\t\t\tclearTimeout(timerId);\n\t\t}\n\t\ttimerId = setTimeout(function() {\n\t\t\ttimerId = null;\n\t\t\tself.handleSelectionChange();\n\t\t},500);\n\t});\n}\n\nSelectionTracker.prototype.handleSelectionChange = function() {\n\tvar selection = document.getSelection();\n\tif(selection && selection.type === \"Range\") {\n\t\t// Helper to get the tiddler title corresponding to a chunk container\n\t\tvar getIdOfContainer = function(domNode) {\n\t\t\treturn domNode.id;\n\t\t}\n\t\t// Get information about the selection anchor and focus\n\t\tvar getSelectionInfo = function(targetDomNode,targetOffset) {\n\t\t\t// Find the chunk container node\n\t\t\tvar domNode = targetDomNode;\n\t\t\tif(domNode.nodeType === Node.TEXT_NODE) {\n\t\t\t\tdomNode = domNode.parentNode;\n\t\t\t}\n\t\t\tvar container = domNode.closest(\".dynannotate-chunk\");\n\t\t\tif(!container) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\t// Find the index of the container within the child nodes of its parent\n\t\t\tvar childNodeIndex = Array.prototype.indexOf.call(container.parentNode.childNodes,container);\n\t\t\t// Walk through the chunk collecting the text before and after the specified domNode and offset\n\t\t\tvar beforeText = null, afterText = [];\n\t\t\tvar splitTextResult = function() {\n\t\t\t\t\tbeforeText = afterText;\n\t\t\t\t\tafterText = [];\n\t\t\t\t},\n\t\t\t\tprocessNode = function(domNode) {\n\t\t\t\t\t// Check for a text node\n\t\t\t\t\tif(domNode.nodeType === Node.TEXT_NODE) {\n\t\t\t\t\t\t// If this is the target node then perform the split\n\t\t\t\t\t\tif(domNode === targetDomNode) {\n\t\t\t\t\t\t\tafterText.push(domNode.textContent.substring(0,targetOffset));\n\t\t\t\t\t\t\tsplitTextResult();\n\t\t\t\t\t\t\tafterText.push(domNode.textContent.substring(targetOffset));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tafterText.push(domNode.textContent);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Process the child nodes\n\t\t\t\t\t\t$tw.utils.each(domNode.childNodes,function(childNode,childNodeIndex) {\n\t\t\t\t\t\t\t// Check whether we need to split on this child node\n\t\t\t\t\t\t\tif(domNode === targetDomNode && childNodeIndex === targetOffset) {\n\t\t\t\t\t\t\t\tsplitTextResult();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tprocessNode(childNode);\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\tprocessNode(container);\n\t\t\tif(beforeText === null) {\n\t\t\t\tsplitTextResult();\n\t\t\t}\n\t\t\t// Return results\n\t\t\treturn {\n\t\t\t\tcontainer: container,\n\t\t\t\tchildNodeIndex: childNodeIndex,\n\t\t\t\tbeforeText: beforeText.join(\"\"),\n\t\t\t\tafterText: afterText.join(\"\")\n\t\t\t}\n\n\t\t}\n\t\tvar anchor = getSelectionInfo(selection.anchorNode,selection.anchorOffset),\n\t\t\tfocus = getSelectionInfo(selection.focusNode,selection.focusOffset);\n\t\t// Check that the containers share a parent\n\t\tif(anchor && focus && anchor.container.parentNode === focus.container.parentNode) {\n\t\t\t// Make sure that the anchor is before the focus\n\t\t\tif((anchor.childNodeIndex > focus.childNodeIndex) || (anchor.container === focus.container && anchor.beforeText.length > focus.beforeText.length)) {\n\t\t\t\tvar temp = anchor; \n\t\t\t\tanchor = focus; \n\t\t\t\tfocus = temp;\n\t\t\t}\n\t\t\tvar chunks = [];\n\t\t\t// Check for the selection being all in one chunk\n\t\t\tif(anchor.container === focus.container) {\n\t\t\t\tchunks.push({\n\t\t\t\t\tid: getIdOfContainer(anchor.container),\n\t\t\t\t\tprefix: anchor.beforeText,\n\t\t\t\t\ttext: anchor.afterText.substring(0,anchor.afterText.length - focus.afterText.length),\n\t\t\t\t\tsuffix: focus.afterText\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\t// We span two or more chunks\n\t\t\t\tchunks.push({\n\t\t\t\t\tid: getIdOfContainer(anchor.container),\n\t\t\t\t\tprefix: anchor.beforeText,\n\t\t\t\t\ttext: anchor.afterText\n\t\t\t\t});\n\t\t\t\t// Get the titles and text of the intervening tiddlers\n\t\t\t\tvar domNode;\n\t\t\t\tif(anchor.container !== focus.container) {\n\t\t\t\t\tdomNode = anchor.container.nextElementSibling;\n\t\t\t\t\twhile(domNode && domNode !== focus.container) {\n\t\t\t\t\t\tchunks.push({\n\t\t\t\t\t\t\tid: getIdOfContainer(domNode),\n\t\t\t\t\t\t\ttext: domNode.textContent\n\t\t\t\t\t\t});\n\t\t\t\t\t\tdomNode = domNode.nextElementSibling;\n\t\t\t\t\t}\t\t\t\t\t\n\t\t\t\t}\n\t\t\t\tchunks.push({\n\t\t\t\t\tid: getIdOfContainer(focus.container),\n\t\t\t\t\ttext: focus.beforeText,\n\t\t\t\t\tsuffix: focus.afterText\n\t\t\t\t});\n\t\t\t}\n\t\t\t// Get the title of the tiddler containing the actions to be executed\n\t\t\tvar actionsTiddler = anchor.container.parentNode.getAttribute(\"data-selection-actions-title\");\n\t\t\t// Assemble the variables to be passed to the action\n\t\t\tvar variables = {};\n\t\t\t// Get the bounds of the container and the selection\n\t\t\tvar selectionRectangle = selection.getRangeAt(0).getBoundingClientRect(),\n\t\t\t\toffsetParentRectangle = anchor.container.offsetParent.getBoundingClientRect();\n\t\t\tvariables[\"tv-selection-posx\"] = (selectionRectangle.left - offsetParentRectangle.left).toString();\n\t\t\tvariables[\"tv-selection-posy\"] = (selectionRectangle.top - offsetParentRectangle.top).toString();\n\t\t\tvariables[\"tv-selection-width\"] = (selectionRectangle.width).toString();\n\t\t\tvariables[\"tv-selection-height\"] = (selectionRectangle.height).toString();\n\t\t\tvariables[\"tv-selection-coords\"] = \"(\" + variables[\"tv-selection-posx\"] + \",\" + variables[\"tv-selection-posy\"] + \",\" + variables[\"tv-selection-width\"] + \",\" + variables[\"tv-selection-height\"] + \")\";\n\t\t\t// Collect the attributes from the container\n\t\t\t$tw.utils.each(anchor.container.parentNode.attributes,function(attribute) {\n\t\t\t\tvariables[\"dom-\" + attribute.name] = attribute.value.toString();\n\t\t\t});\n\t\t\t// Action the selection\n\t\t\tthis.performSelectionActions(chunks,variables,actionsTiddler);\n\t\t}\n\t}\n};\n\nSelectionTracker.prototype.performSelectionActions = function(chunks,variables,actionsTiddler) {\n\t// Invoke the actions, passing the extract tiddler title as a variable\n\tif(actionsTiddler) {\n\t\tvar actions = $tw.wiki.getTiddlerText(actionsTiddler)\n\t\tif(actions) {\n\t\t\tvar selection = JSON.stringify({chunks: chunks,variables: variables});\n\t\t\t$tw.rootWidget.invokeActionString(actions,undefined,undefined,$tw.utils.extend({},variables,{selection: selection}));\n\t\t}\n\t}\n};\n\nexports.SelectionTracker = SelectionTracker;\n","type":"application/javascript","module-type":"library"},"$:/plugins/tiddlywiki/dynannotate/startup.js":{"title":"$:/plugins/tiddlywiki/dynannotate/startup.js","text":"/*\\\ntitle: $:/plugins/tiddlywiki/dynannotate/startup.js\ntype: application/javascript\nmodule-type: startup\n\nStartup the dyannotate background daemon to track the selection\n\n\\*/\n\"use strict\";\n\n// Export name and synchronous status\nexports.name = \"dyannotate-startup\";\nexports.platforms = [\"browser\"];\nexports.after = [\"render\"];\nexports.synchronous = true;\n\nvar CONFIG_SELECTION_TRACKER_TITLE = \"$:/config/Dynannotate/SelectionTracker/Enable\",\n\tCONFIG_LEGACY_SELECTION_TRACKER_TITLE = \"$:/config/Dynannotate/LegacySelectionTracker/Enable\";\n\nvar SelectionTracker = require(\"$:/plugins/tiddlywiki/dynannotate/selection-tracker.js\").SelectionTracker,\n\tLegacySelectionTracker = require(\"$:/plugins/tiddlywiki/dynannotate/legacy-selection-tracker.js\").LegacySelectionTracker,\n\tElementSpotlight = require(\"$:/plugins/tiddlywiki/dynannotate/element-spotlight.js\").ElementSpotlight;\n\nexports.startup = function() {\n\t$tw.dynannotate = {};\n\t// Setup selection tracker\n\tif($tw.wiki.getTiddlerText(CONFIG_SELECTION_TRACKER_TITLE,\"yes\") === \"yes\") {\n\t\t$tw.dynannotate.selectionTracker = new SelectionTracker($tw.wiki);\n\t}\n\t// Setup legacy selection tracker\n\tif($tw.wiki.getTiddlerText(CONFIG_LEGACY_SELECTION_TRACKER_TITLE,\"yes\") === \"yes\") {\n\t\t$tw.dynannotate.legacySelectionTracker = new LegacySelectionTracker($tw.wiki,{\n\t\t\tallowBlankSelectionPopup: true\n\t\t});\n\t}\n\t// Set up the element spotlight\n\t$tw.dynannotate.elementSpotlight = new ElementSpotlight();\n\t$tw.rootWidget.addEventListener(\"tm-spotlight-element\",function(event) {\n\t\tvar selectors = [];\n\t\tif(event.paramObject.selector) {\n\t\t\tselectors.push(event.paramObject.selector);\n\t\t}\n\t\t$tw.utils.each(Object.keys(event.paramObject).sort(),function(name) {\n\t\t\tvar SELECTOR_PROPERTY_PREFIX = \"selector-\";\n\t\t\tif($tw.utils.startsWith(name,SELECTOR_PROPERTY_PREFIX)) {\n\t\t\t\tselectors.push(event.paramObject[name]);\n\t\t\t}\n\t\t});\n\t\tif(event.paramObject[\"selector-fallback\"]) {\n\t\t\tselectors.push(event.paramObject[\"selector-fallback\"]);\n\t\t}\n\t\t$tw.dynannotate.elementSpotlight.shineSpotlight(selectors);\n\t});\n};\n","type":"application/javascript","module-type":"startup"},"$:/plugins/tiddlywiki/dynannotate/textmap.js":{"title":"$:/plugins/tiddlywiki/dynannotate/textmap.js","text":"/*\\\ntitle: $:/plugins/tiddlywiki/dynannotate/textmap.js\ntype: application/javascript\nmodule-type: library\n\nStructure for modelling mapping between a string and its representation in the DOM\n\n\\*/\n\n\"use strict\";\n\nvar PREFIX_SUFFIX_LENGTH = 50;\n\n/*\nBuild a map of the text content of a DOM node and its descendants:\n\nstring: concatenation of the text content of child nodes\nmetadata: array of {start,end,domNode} where start and end identify position in the string\n*/\nexports.TextMap = function(domNode) {\n\tvar self = this,\n\t\tstringChunks = [],\n\t\tp = 0;\n\tthis.metadata = [];\n\tvar processNode = function(domNode) {\n\t\t// Check for text nodes\n\t\tif(domNode.nodeType === 3) {\n\t\t\tvar text = domNode.textContent;\n\t\t\tstringChunks.push(text);\n\t\t\tself.metadata.push({\n\t\t\t\tstart: p,\n\t\t\t\tend: p + text.length,\n\t\t\t\tdomNode: domNode\n\t\t\t});\n\t\t\tp += text.length;\n\t\t} else {\n\t\t\t// Otherwise look within the child nodes\n\t\t\tif(domNode.childNodes) {\n\t\t\t\tfor(var t=0; t<domNode.childNodes.length; t++ ) {\n\t\t\t\t\tprocessNode(domNode.childNodes[t]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n\t// Process our text nodes\n\tprocessNode(domNode);\n\tthis.string = stringChunks.join(\"\");\n};\n\n/*\nLocate the metadata record corresponding to a given position in the string\n*/\nexports.TextMap.prototype.locateMetadata = function(position) {\n\treturn this.metadata.find(function(metadata) {\n\t\treturn position >= metadata.start && position < metadata.end;\n\t});\n};\n\n/*\nSearch for the first occurrence of a target string within the textmap of a DOM node\n\nReturns an object with the following properties:\n\tstartNode: node containing the start of the text\n\tstartOffset: offset of the start of the text within the node\n\tendNode: node containing the end of the text\n\tendOffset: offset of the end of the text within the node\n*/\nexports.TextMap.prototype.findText = function(targetString,targetPrefix,targetSuffix) {\n\tif(!targetString) {\n\t\treturn null;\n\t}\n\ttargetPrefix = targetPrefix || \"\";\n\ttargetSuffix = targetSuffix || \"\";\n\tvar startPos = this.string.indexOf(targetPrefix + targetString + targetSuffix);\n\tif(startPos !== -1) {\n\t\tstartPos += targetPrefix.length;\n\t\tvar startMetadata = this.locateMetadata(startPos),\n\t\t\tendMetadata = this.locateMetadata(startPos + targetString.length - 1);\n\t\tif(startMetadata && endMetadata) {\n\t\t\treturn {\n\t\t\t\tstartNode: startMetadata.domNode,\n\t\t\t\tstartOffset: startPos - startMetadata.start,\n\t\t\t\tendNode: endMetadata.domNode,\n\t\t\t\tendOffset: (startPos + targetString.length) - endMetadata.start\n\t\t\t}\t\t\t\n\t\t}\n\t}\n\treturn null;\n};\n\n/*\nSearch for all occurrences of a string within the textmap of a DOM node\n\nOptions include:\n\tmode: \"normal\", \"literal\", \"regexp\", \"whitespace\", \"some\" or \"words\"\n\tcaseSensitive: true if the search should be case sensitive\n\nReturns an array of objects with the following properties:\n\tstartPos: start position of the match within the string contained by this TextMap\n\tstartNode: node containing the start of the text\n\tstartOffset: offset of the start of the text within the node\n\tendPos: end position of the match within the string contained by this TextMap\n\tendNode: node containing the end of the text\n\tendOffset: offset of the end of the text within the node\n*/\nexports.TextMap.prototype.search = function(searchString,options) {\n\tif(!searchString) {\n\t\treturn [];\n\t}\n\toptions = options || {};\n\t// Compose the regexp\n\tvar regExpString,\n\t\tflags = options.caseSensitive ? \"g\" : \"gi\";\n\tif(options.mode === \"regexp\") {\n\t\tregExpString = \"(\" + searchString + \")\";\n\t} else if(options.mode === \"whitespace\") {\n\t\t// Normalise whitespace\n\t\tregExpString = \"(\" + searchString.split(/\\s+/g).filter(function(word) {\n\t\t\treturn !!word\n\t\t}).map($tw.utils.escapeRegExp).join(\"\\\\s+\") + \")\";\n\t} else if(options.mode === \"words\" || options.mode === \"some\") {\n\t\t// Match any word separated by whitespace\n\t\tregExpString = \"(\" + searchString.split(/\\s+/g).filter(function(word) {\n\t\t\treturn !!word\n\t\t}).map($tw.utils.escapeRegExp).join(\"|\") + \")\";\n\t} else {\n\t\t// Normal search\n\t\tregExpString = \"(\" + $tw.utils.escapeRegExp(searchString) + \")\";\n\t}\n\t// Compile the regular expression\n\tvar regExp;\n\ttry {\n\t\tregExp = RegExp(regExpString,flags);\n\t} catch(e) {\n\t}\n\tif(!regExp) {\n\t\treturn [];\n\t}\n\t// Find each match\n\tvar results = [],\n\t\tmatch;\n\tdo {\n\t\tmatch = regExp.exec(this.string);\n\t\tif(match) {\n\t\t\tvar metadataStart = this.locateMetadata(match.index),\n\t\t\t\tmetadataEnd = this.locateMetadata(match.index + match[0].length);\n\t\t\tif(metadataStart && metadataEnd) {\n\t\t\t\tresults.push({\n\t\t\t\t\tstartPos: match.index,\n\t\t\t\t\tstartNode: metadataStart.domNode,\n\t\t\t\t\tstartOffset: match.index - metadataStart.start,\n\t\t\t\t\tendPos: match.index + match[0].length,\n\t\t\t\t\tendNode: metadataEnd.domNode,\n\t\t\t\t\tendOffset: match.index + match[0].length - metadataEnd.start\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t} while(match);\n\treturn results;\n};\n\n/*\nGiven a start container and offset and a search string, return a prefix and suffix to disambiguate the text\n*/\nexports.TextMap.prototype.extractContext = function(startContainer,startOffset,text) {\n\tvar startMetadata = this.metadata.find(function(metadata) {\n\t\t\treturn metadata.domNode === startContainer\n\t\t});\n\tif(!startMetadata) {\n\t\treturn null;\n\t}\n\tvar startPos = startMetadata.start + startOffset;\n\treturn {\n\t\tprefix: this.string.slice(Math.max(startPos - PREFIX_SUFFIX_LENGTH, 0), startPos),\n\t\tsuffix: this.string.slice(startPos + text.length, Math.min(startPos + text.length + PREFIX_SUFFIX_LENGTH, this.string.length))\n\t};\n};\n","type":"application/javascript","module-type":"library"},"$:/plugins/tiddlywiki/dynannotate/styles":{"title":"$:/plugins/tiddlywiki/dynannotate/styles","tags":"[[$:/tags/Stylesheet]]","text":"\\rules only filteredtranscludeinline transcludeinline macrodef macrocallinline\n\n.tc-dynannotation-wrapper {\n\tposition: relative;\n}\n\n.tc-dynannotation-annotation-overlay {\n\tposition: absolute;\n\tbackground: rgba(255,255,0,0.3);\n\tmix-blend-mode: multiply;\n}\n\n.tc-dynannotation-search-overlay {\n\tposition: absolute;\n\tpointer-events: none;\n\tbackground: rgba(255,0,0,0.3);\n}\n\n.tc-dynannotation-search-overlay-blurred {\n\tbackground: rgba(255,0,0,0.3);\n\tmix-blend-mode: multiply;\n\tborder-radius: 4px;\n\tfilter: blur(2px);\n}\n\n@keyframes ta-dynannotation-search-overlay-animated { to { background-position: 100% 100% } }\n\n.tc-dynannotation-search-overlay-animated {\n\tmix-blend-mode: multiply;\n\tbackground: repeating-linear-gradient(-45deg, #ff8 0, #dd8 25%, transparent 0, transparent 50%) 0 / .6em .6em;\n\tanimation: ta-dynannotation-search-overlay-animated 12s linear infinite;\n}\n\n.tc-dynannotate-example-frame {\n    border: 1px solid black;\n    border-radius: 20px;\n    padding: 1em;\n}\n\n.tc-control-panel table.tc-dynannotate-search-parameters, .tc-control-panel table.tc-dynannotate-search-parameters input {\n    width: initial;\n}\n\n.tc-dynannotate-snippet-highlight {\n\tbackground: #efef53;\n}\n\n.tc-dynannotation-example-info {\n\tbackground: #ffa;\n\tpadding: 1em;\n}\n\n.tc-dynannotate-spotlight-wrapper {\n\tposition: fixed;\n\tleft: 0;\n\tright: 0;\n\ttop: 0;\n\tbottom: 0;\n\tz-index: 1000;\n\tpointer-events: none;\n}\n\n.tc-dynannotate-spotlight {\n\tposition: absolute;\n\theight: 100%;\n\twidth: 100%;\n\tdisplay: none;\n}\n"}}}