1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-11-27 03:57:21 +00:00

Introduce text-slicer plugin and edition

A first pass at a plugin that splits formatted text into individual
tiddlers according to lists and headings.
This commit is contained in:
Jermolene 2015-08-01 13:15:24 +01:00
parent 8f746f9dde
commit 8488a13761
12 changed files with 403 additions and 5 deletions

View File

@ -1,6 +1,6 @@
caption: 5.1.10
created: 20150705153725652
modified: 20150705153725652
created: 20150801123725652
modified: 20150801123725652
tags: ReleaseNotes
title: Release 5.1.10
type: text/vnd.tiddlywiki
@ -11,7 +11,21 @@ type: text/vnd.tiddlywiki
!! Performance Optimisations
This release includes several low-level performance optimisations that should improve speed and responsiveness in many common operations.
* [[Caching the results of parsing a tiddler|https://github.com/Jermolene/TiddlyWiki5/commit/b0cb17cd83dde89753ec159e27c920a7bf22bee1]]
* [[Loop optimisations|https://github.com/Jermolene/TiddlyWiki5/commit/c6e48ebc2db4af895f5b3935b3f575b8aab292fe]]
* [[Tiddler iteration optimisations|https://github.com/Jermolene/TiddlyWiki5/commit/8f63e2a959a7ac44533ae2b6192716ee17a1ce93]]
* [[State qualifier generation|https://github.com/Jermolene/TiddlyWiki5/commit/848a7f4e744c8f4dcb4ec88a0e99c4ae6aac25e5]]
* [[Caching data tiddlers|https://github.com/Jermolene/TiddlyWiki5/commit/32f6d7f1b01474b82debcbdd5d76c49c59303265]]
!! Résumé Builder Edition
The new [[Résumé Builder Edition]] by @inmysocks is a custom edition to guide you through the process of using TiddlyWiki to create a good looking résumé (or curriculum vitæ).
!! Text-Slicer Edition
The new [[Text-Slicer Edition]] is a custom edition with tools to help advanced users slice longer texts up into individual tiddlers.
! Other Improvements
@ -21,18 +35,26 @@ type: text/vnd.tiddlywiki
!! Usability Improvements
*
* [[Added|https://github.com/Jermolene/TiddlyWiki5/commit/d5e690a06d523a2047eaf9b623b633bb72c18af9]] ability to disable individual WikiText parser rules (see the ''Advanced'' tab of $:/ControlPanel). Also [[added|https://github.com/Jermolene/TiddlyWiki5/commit/58188cf8053bef87dbe97e4b05cdba67f75c615d]] a simple setting for disabling automatic linking of ~CamelCase words
* [[Extended|https://github.com/Jermolene/TiddlyWiki5/commit/2cb6400773096b02b71c1851fb0fac5dfefbbd6f]] support for automatically linked system tiddler titles to include digits and underscore
!! Hackability Improvements
*
* [[Added|https://github.com/Jermolene/TiddlyWiki5/commit/c4397792f5d396288048bcc3bb4ee8e95dbc2c5e]] added [[recent Operator]]
* [[Added|https://github.com/Jermolene/TiddlyWiki5/commit/e0aacc84d5f084ff7a53153c590fbff3d24f2e2c]] `publishFilter` to default save template
* [[Allow|https://github.com/Jermolene/TiddlyWiki5/commit/7dddc925ae93725552b98bc348a07572895da96c]] ''delete'' button to be used in the tiddler view-mode toolbar
!! Bug Fixes
*
* [[Fixed|https://github.com/Jermolene/TiddlyWiki5/issues/1882]] problem introduced in 5.1.9 with processing `tiddlywiki.info` files
* [[Fixed|https://github.com/Jermolene/TiddlyWiki5/commit/3fbf29093b32c00941b0c03951250de7c0cc8d6f]] problem with invisible icons in $:/AdvancedSearch
* [[Improved|https://github.com/Jermolene/TiddlyWiki5/commit/862e358b57fde74595420e7948bf44fdadf690dc]] check for required plugins in ServerCommand
!! Contributors
[[@Jermolene|https://github.com/Jermolene]] would like to thank the contributors to this release who have generously given their time to help improve TiddlyWiki:
* [[@danielo515|https://github.com/danielo515]]
* [[@idoine|https://github.com/idoine]]
* [[@inmysocks|https://github.com/inmysocks]]
* [[@nameanyone|https://github.com/nameanyone]]

View File

@ -0,0 +1,9 @@
created: 20150718123139133
modified: 20150718123255662
tags: Editions
title: Text-Slicer Edition
type: text/vnd.tiddlywiki
The ''Text-Slicer'' edition of TiddlyWiki contains tools to help advanced users slice long texts up into individual tiddlers.
<a href="editions/text-slicer/index.html" target="_blank">editions/text-slicer/index.html</a>

View File

@ -0,0 +1,25 @@
title: HelloThere
This edition of ~TiddlyWiki contains tools to help slice up long texts into individual tiddlers.
//It is currently only intended for advanced users. The tools are in the early stages of development, and likely to need some customisation to do what you need.//
The source document must first be marked up as an ordinary wikitext tiddler. Currently only the following formatting is recognised:
* Headings
* Ordered and unordered lists
* Paragraphs
To try it out:
# Click the "text slicer" icon on the [[Sample Text]] tiddler below
# Click the ''import'' button in the resulting import listing
# Open the tiddler [[Sliced up Sample Text]]
#* It should match the content of [[Sample Text]]
The table of contents at the top of the output tiddler shows how the document has been split up into individual tiddlers:
* A tiddler for each heading, with the children of the heading tagged with the title of the heading
* A tiddler for each paragraph
* A tiddler for each list, and a tiddler for each list item
* The list field of tag tiddlers is used to control ordering of children

View File

@ -0,0 +1,96 @@
title: Sample Text
! Introduction to TiddlyWiki
Lorem 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.
!! What is TiddlyWiki used for?
Dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 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.
!!! Who uses TiddlyWiki?
Lorem 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.
Consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
!! Who makes TiddlyWiki?
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.
!!! How open source projects work
Dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 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.
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 qui officia deserunt mollit anim id est laborum.
Consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
!!! How GitHub works
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.
!! Tiddlers
Lorem 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.
!!! Tiddler types
Dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 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.
!!! Fields
Consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
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 qui officia deserunt mollit anim id est laborum.
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.
!!!! Tags
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 qui officia deserunt mollit anim id est laborum.
!! Lists and Stories
Lorem 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.
!!! List widget
Dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 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.
!!! Story
* Lorem 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.
* Consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
!! Plugins
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.
!! Saving changes and generating HTML files
Dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 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.
* 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 qui officia deserunt mollit anim id est laborum
!!! TiddlyFox
Consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
!! Syncing tiddlers with servers
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.
!!! Browser Same Domain Origin Policy
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 qui officia deserunt mollit anim id est laborum.

View File

@ -0,0 +1,5 @@
title: $:/DefaultTiddlers
HelloThere
$:/Import
[[Sample Text]]

View File

@ -0,0 +1,17 @@
{
"description": "Tools for slicing up long texts into individual tiddlers",
"plugins": [
"tiddlywiki/text-slicer"
],
"languages": [
],
"themes": [
"tiddlywiki/vanilla",
"tiddlywiki/snowwhite"
],
"build": {
"index": [
"--rendertiddler","$:/core/save/all","index.html","text/plain"
]
}
}

View File

@ -0,0 +1,23 @@
title: $:/plugins/tiddlywiki/text-slicer/macros
tags: $:/tags/Macro
\define display-heading-tiddler(level:"h1")
<$level$><$view field="title"/></$level$>
<$list filter='[tag<currentTiddler>]'>
<$tiddler>
<$transclude mode='block'/>
</$tiddler>
</$list>
\end
\define display-list-tiddler(type:"ol")
<$type$>
<$list filter='[tag<currentTiddler>]'>
<li>
<$tiddler>
<$transclude mode='block'/>
</$tiddler>
</li>
</$list>
</$type$>
\end

View File

@ -0,0 +1,7 @@
{
"title": "$:/plugins/tiddlywiki/text-slicer",
"description": "Tools for slicing text into tiddlers",
"author": "JeremyRuston",
"core-version": ">=5.0.0",
"list": "readme"
}

View File

@ -0,0 +1,3 @@
title: $:/plugins/tiddlywiki/text-slicer/readme
This plugin contains tools to help slice up long texts into individual tiddlers.

View File

@ -0,0 +1,13 @@
title: $:/plugins/tiddlywiki/text-slicer/ui/slice-button
tags: $:/tags/ViewToolbar
caption: {{$:/plugins/tiddlywiki/text-slicer/text-slicer-icon}} Slice tiddler
description: Slice this tiddler by headings and lists
<$button message="tm-slice-tiddler" param=<<currentTiddler>> tooltip={{$:/language/Buttons/Clone/Hint}} aria-label={{$:/language/Buttons/Clone/Caption}} class=<<tv-config-toolbar-class>>>
<$list filter="[<tv-config-toolbar-icons>prefix[yes]]">
{{$:/plugins/tiddlywiki/text-slicer/text-slicer-icon}}
</$list>
<$list filter="[<tv-config-toolbar-text>prefix[yes]]">
<span class="tc-btn-text">Slice tiddler</span>
</$list>
</$button>

View File

@ -0,0 +1,157 @@
/*\
title: $:/plugins/tiddlywiki/text-slicer/slicer.js
type: application/javascript
module-type: startup
Setup the root widget event handlers
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
// Export name and synchronous status
exports.name = "slicer";
exports.platforms = ["browser"];
exports.after = ["startup"];
exports.synchronous = true;
var SLICER_OUTPUT_TITLE = "$:/Import";
// Install the root widget event handlers
exports.startup = function() {
$tw.rootWidget.addEventListener("tm-slice-tiddler",function(event) {
// Slice up and output the tiddler
outputTiddlers(sliceTiddler(event.param));
});
};
var currentId = 0;
function nextId() {
return ++currentId;
}
// Slice a tiddler into individual tiddlers
function sliceTiddler(title) {
var tiddlers = {},
parser = $tw.wiki.parseTiddler(title),
parentStack = [],
addTiddler = function(fields) {
if(fields.title) {
tiddlers[fields.title] = $tw.utils.extend({},tiddlers[fields.title],fields);
return fields.title;
} else {
return null;
}
},
addToList = function(parent,child) {
var parentTiddler = tiddlers[parent] || {},
parentList = parentTiddler.list || [];
parentList.push(child);
addTiddler($tw.utils.extend({title: parent},parentTiddler,{list: parentList}));
},
convertTypeToLevel = function(type) {
if(type.charAt(0) === "h") {
return parseInt(type.charAt(1),10);
} else {
return null;
}
},
popParentStackUntil = function(type) {
// Pop the stack to remove any entries at the same or lower level
var newLevel = convertTypeToLevel(type),
topIndex = parentStack.length - 1;
do {
var topLevel = convertTypeToLevel(parentStack[parentStack.length - 1].type);
if(topLevel !== null && topLevel < newLevel ) {
break;
}
parentStack.length--;
} while(true);
return parentStack[parentStack.length - 1].title;
},
processNodeList = function(nodeList) {
$tw.utils.each(nodeList,function(parseTreeNode) {
var parentTitle,
text = $tw.utils.getParseTreeText(parseTreeNode);
if(parseTreeNode.type === "element" && (parseTreeNode.tag === "h1" || parseTreeNode.tag === "h2" || parseTreeNode.tag === "h3" || parseTreeNode.tag === "h4")) {
parentTitle = popParentStackUntil(parseTreeNode.tag);
addToList(parentTitle,text);
parentStack.push({type: parseTreeNode.tag, title: addTiddler({
title: text,
text: "<<display-heading-tiddler level:'" + parseTreeNode.tag + "'>>",
list: [],
tags: [parentTitle]
})});
} else if(parseTreeNode.type === "element" && (parseTreeNode.tag === "ul" || parseTreeNode.tag === "ol")) {
var listTitle = title + "-list-" + nextId();
parentTitle = parentStack[parentStack.length - 1].title;
addToList(parentTitle,listTitle);
parentStack.push({type: parseTreeNode.tag, title: addTiddler({
title: listTitle,
text: "<<display-list-tiddler type:'" + parseTreeNode.tag + "'>>",
list: [],
tags: [parentTitle]
})});
processNodeList(parseTreeNode.children);
parentStack.pop();
} else if(parseTreeNode.type === "element" && parseTreeNode.tag === "li") {
var listItemTitle = title + "-listitem-" + nextId();
parentTitle = parentStack[parentStack.length - 1].title;
addToList(parentTitle,listItemTitle);
addTiddler({
title: listItemTitle,
text: text,
list: [],
tags: [parentTitle]
});
} else if(parseTreeNode.type === "element" && parseTreeNode.tag === "p") {
parentTitle = parentStack[parentStack.length - 1].title;
addToList(parentTitle,addTiddler({
title: title + "-para-" + nextId(),
text: text,
tags: [parentTitle]
}));
}
});
};
if(parser) {
parentStack.push({type: "h0", title: addTiddler({
title: "Sliced up " + title,
text: "<div class='tc-table-of-contents'>\n\n<<toc-selective-expandable 'Sliced up " + title + "'>>\n\n</div>\n<<display-heading-tiddler level:'h1'>>",
list: []
})});
processNodeList(parser.tree);
}
return tiddlers;
}
// Output to the output tiddler
function outputTiddlers(tiddlers) {
// Get the current slicer output tiddler
var slicerOutputTiddler = $tw.wiki.getTiddler(SLICER_OUTPUT_TITLE),
slicerOutputData = $tw.wiki.getTiddlerData(SLICER_OUTPUT_TITLE,{}),
newFields = new Object({
title: SLICER_OUTPUT_TITLE,
type: "application/json",
"plugin-type": "import",
"status": "pending"
});
// Process each tiddler
slicerOutputData.tiddlers = slicerOutputData.tiddlers || {};
$tw.utils.each(tiddlers,function(tiddlerFields) {
var title = tiddlerFields.title;
if(title) {
slicerOutputData.tiddlers[title] = tiddlerFields;
}
});
// Save the slicer output tiddler
newFields.text = JSON.stringify(slicerOutputData,null,$tw.config.preferences.jsonSpaces);
$tw.wiki.addTiddler(new $tw.Tiddler(slicerOutputTiddler,newFields));
// TBD: Navigate to $:/Import
}
})();

View File

@ -0,0 +1,21 @@
title: $:/plugins/tiddlywiki/text-slicer/text-slicer-icon
tags: $:/tags/Image
<svg class="tc-image-text-slicer tc-image-button" width="22pt" height="22pt" viewBox="0 0 128 128">
<g fill-rule="evenodd">
<path d="M80,51.0155285 L59.5940359,64.2673166 L125.897873,107.325532 C128.030065,108.710193 128.646102,111.578842 127.255156,113.720711 C125.867041,115.858221 123.001365,116.472016 120.860813,115.081925 L51.1036068,69.7810657 L26.8517133,85.5304295 L26.8517087,85.5304319 C28.1651844,86.2022039 29.4191834,87.0428514 30.5829398,88.0544894 C38.2922474,94.7560882 39.1219848,106.423679 32.4362103,114.114783 C25.7504358,121.805887 14.080916,122.608039 6.3716084,115.90644 C-1.33769921,109.204841 -2.1674367,97.5372504 4.51833784,89.8461466 C5.83807302,88.3279649 7.35199779,87.0782085 8.99132065,86.1016058 L42.6131777,64.2673166 L42.6131777,64.2673166 L8.54310096,42.14195 C8.24323991,41.947218 7.97336572,41.7231352 7.73484365,41.4758511 C7.26854012,41.1408688 6.81356667,40.7813413 6.3716084,40.3971528 C-1.33769921,33.695554 -2.1674367,22.0279631 4.51833784,14.3368593 C11.2041124,6.64575553 22.8736321,5.84360327 30.5829398,12.5452021 C38.2922474,19.246801 39.1219848,30.9143918 32.4362103,38.6054956 C30.8132523,40.4724953 28.8966192,41.9335527 26.8142483,42.9798736 L51.1036068,58.7535674 L80,39.9880303 L80,51.0155285 Z M13.0048429,34.0671776 C12.7764349,33.9024698 12.5534894,33.725961 12.336801,33.5375966 C8.48191699,30.186597 8.06702347,24.3524532 11.4101104,20.5066717 C14.7531973,16.6608901 20.5883056,16.25979 24.4431896,19.6107896 C28.2980736,22.9617891 28.7129671,28.7959329 25.3698802,32.6417145 C22.5492464,35.8864825 17.954628,36.6790998 14.2721287,34.8349262 L13.5801614,34.3855571 C13.3934198,34.2642857 13.2011595,34.1582654 13.0048429,34.0671776 L13.0048429,34.0671776 Z M11.4101104,96.0159589 C8.06702347,99.8617405 8.48191699,105.695884 12.336801,109.046884 C16.191685,112.397883 22.0267933,111.996783 25.3698802,108.151002 C28.7129671,104.30522 28.2980736,98.4710764 24.4431896,95.1200768 C20.5883056,91.7690773 14.7531973,92.1701774 11.4101104,96.0159589 Z"></path>
<g transform="translate(81.000000, 0.000000)">
<circle transform="translate(5.000000, 52.000000) rotate(-90.000000) translate(-5.000000, -52.000000) " cx="5" cy="52" r="4"></circle>
<circle transform="translate(5.000000, 112.000000) rotate(-90.000000) translate(-5.000000, -112.000000) " cx="5" cy="112" r="4"></circle>
<circle transform="translate(5.000000, 124.000000) rotate(-90.000000) translate(-5.000000, -124.000000) " cx="5" cy="124" r="4"></circle>
<circle transform="translate(5.000000, 40.000000) rotate(-90.000000) translate(-5.000000, -40.000000) " cx="5" cy="40" r="4"></circle>
<circle transform="translate(5.000000, 100.316987) rotate(-90.000000) translate(-5.000000, -100.316987) " cx="5" cy="100.316987" r="4"></circle>
<circle transform="translate(5.000000, 28.000000) rotate(-90.000000) translate(-5.000000, -28.000000) " cx="5" cy="28" r="4"></circle>
<circle transform="translate(5.000000, 88.316987) rotate(-90.000000) translate(-5.000000, -88.316987) " cx="5" cy="88.3169873" r="4"></circle>
<circle transform="translate(5.000000, 16.000000) rotate(-90.000000) translate(-5.000000, -16.000000) " cx="5" cy="16" r="4"></circle>
<circle transform="translate(5.000000, 76.316987) rotate(-90.000000) translate(-5.000000, -76.316987) " cx="5" cy="76.3169873" r="4"></circle>
<circle transform="translate(5.000000, 4.000000) rotate(-90.000000) translate(-5.000000, -4.000000) " cx="5" cy="4" r="4"></circle>
<circle transform="translate(5.000000, 64.316987) rotate(-90.000000) translate(-5.000000, -64.316987) " cx="5" cy="64.3169873" r="4"></circle>
</g>
</g>
</svg>