{"title":"$:/plugins/tiddlywiki/tiddlyweb","name":"TiddlyWeb","description":"Sync changes from the browser to TW5 (node.js) or TiddlyWeb server","list":"readme","plugin-priority":10,"version":"5.2.2","plugin-type":"plugin","dependents":"","type":"application/json","tiddlers":{"GettingStarted":{"title":"GettingStarted","tags":"$:/tags/GettingStarted","caption":"Step 1<br>Syncing","text":"Welcome to ~TiddlyWiki and the ~TiddlyWiki community\n\nVisit https://tiddlywiki.com/ to find out more about ~TiddlyWiki and what it can do.\n\n! Syncing Changes to the Server\n\nBefore you can start storing important information in ~TiddlyWiki it is important to make sure that your changes are being reliably saved by the server.\n\n# Create a new tiddler using the {{$:/core/images/new-button}} button in the sidebar on the right\n# Click the {{$:/core/images/done-button}} button at the top right of the new tiddler\n# Check the ~TiddlyWiki command line for a message confirming the tiddler has been saved\n# Refresh the page in the browser to and verify that the new tiddler has been correctly saved\n"},"$:/config/SaveWikiButton/Template":{"title":"$:/config/SaveWikiButton/Template","text":"$:/plugins/tiddlywiki/tiddlyweb/save/offline"},"$:/config/Server/ExternalFilters/[all[tiddlers]] -[[$:/isEncrypted]] -[prefix[$:/temp/]] -[prefix[$:/status/]] -[[$:/boot/boot.js]] -[[$:/boot/bootprefix.js]] -[[$:/library/sjcl.js]] -[[$:/core]]":{"title":"$:/config/Server/ExternalFilters/[all[tiddlers]] -[[$:/isEncrypted]] -[prefix[$:/temp/]] -[prefix[$:/status/]] -[[$:/boot/boot.js]] -[[$:/boot/bootprefix.js]] -[[$:/library/sjcl.js]] -[[$:/core]]","text":"yes"},"$:/config/OfficialPluginLibrary":{"title":"$:/config/OfficialPluginLibrary","tags":"$:/tags/PluginLibrary","url":"https://tiddlywiki.com/library/v5.1.23/index.html","caption":"{{$:/language/OfficialPluginLibrary}}","enabled":"no","text":"The official plugin library is disabled when using the client-server configuration. Instead, plugins should be installed via the `tiddlywiki.info` file, as described [[here|https://tiddlywiki.com/#Installing%20a%20plugin%20from%20the%20plugin%20library]]."},"$:/core/templates/css-tiddler":{"title":"$:/core/templates/css-tiddler","text":"<!--\n\nThis template is used for saving CSS tiddlers as a style tag with data attributes representing the tiddler fields. This version includes the tiddler changecount as the field `revision`.\n\n-->`<style`<$fields template=' data-tiddler-$name$=\"$encoded_value$\"'></$fields>` data-tiddler-revision=\"`<<changecount>>`\" data-tiddler-bag=\"default\" type=\"text/css\">`<$view field=\"text\" format=\"text\" />`</style>`"},"$:/core/templates/html-div-skinny-tiddler":{"title":"$:/core/templates/html-div-skinny-tiddler","text":"<!--\n\nThis template is a variant of the tiddlyweb plugin's overridden version of $:/core/templates/html-div-tiddler used for saving skinny tiddlers (with no text field)\n\n-->`<div`<$fields exclude='text revision bag' template=' $name$=\"$encoded_value$\"'></$fields>` revision=\"`<<changecount>>`\" bag=\"default\" _is_skinny=\"\">\n<pre></pre>\n</div>`\n"},"$:/core/templates/html-div-tiddler":{"title":"$:/core/templates/html-div-tiddler","text":"<!--\n\nThis template is used for saving tiddlers as an HTML DIV tag with attributes representing the tiddler fields. This version includes the tiddler changecount as the field `revision`.\n\n-->`<div`<$fields exclude='text revision bag' template=' $name$=\"$encoded_value$\"'></$fields>` revision=\"`<<changecount>>`\" bag=\"default\">\n<pre>`<$view field=\"text\" format=\"htmltextencoded\" />`</pre>\n</div>`\n"},"$:/core/templates/html-json-skinny-tiddler":{"title":"$:/core/templates/html-json-skinny-tiddler","text":"<$list filter=\"[<numTiddlers>compare:number:gteq[1]] ~[<counter>!match[1]]\">`,`<$text text=<<newline>>/></$list>\n<$jsontiddler tiddler=<<currentTiddler>> exclude=\"text\" escapeUnsafeScriptChars=\"yes\" $revision=<<changecount>> $bag=\"default\" $_is_skinny=\"\"/>\n"},"$:/core/templates/html-json-tiddler":{"title":"$:/core/templates/html-json-tiddler","text":"<$list filter=\"[<counter>!match[1]]\">`,`<$text text=<<newline>>/></$list>\n<$jsontiddler tiddler=<<currentTiddler>> escapeUnsafeScriptChars=\"yes\" $revision=<<changecount>> $bag=\"default\">/>\n"},"$:/plugins/tiddlywiki/tiddlyweb/icon/cloud":{"title":"$:/plugins/tiddlywiki/tiddlyweb/icon/cloud","tags":"$:/tags/Image","text":"<svg class=\"tc-image-cloud tc-image-button\" width=\"22pt\" height=\"22pt\" viewBox=\"0 0 128 128\"><g><path d=\"M24 103C10.745 103 0 92.255 0 79c0-9.697 5.75-18.05 14.027-21.836A24.787 24.787 0 0114 56c0-13.255 10.745-24 24-24 1.373 0 2.718.115 4.028.337C48.628 24.2 58.707 19 70 19c19.882 0 36 16.118 36 36v.082c12.319 1.016 22 11.336 22 23.918 0 12.239-9.16 22.337-20.999 23.814L107 103H24z\"/><path class=\"tc-image-cloud-idle\" d=\"M57.929 84.698a6 6 0 01-8.485 0L35.302 70.556a6 6 0 118.485-8.485l9.9 9.9L81.97 43.686a6 6 0 018.485 8.486L57.929 84.698z\"/><path class=\"tc-image-cloud-progress tc-animate-rotate-slow\" d=\"M44.8 40a3.6 3.6 0 100 7.2h2.06A23.922 23.922 0 0040 64c0 13.122 10.531 23.785 23.603 23.997L64 88l.001-7.2c-9.171 0-16.626-7.348-16.798-16.477L47.2 64c0-5.165 2.331-9.786 5.999-12.868L53.2 55.6a3.6 3.6 0 107.2 0v-12a3.6 3.6 0 00-3.6-3.6h-12zM64 40v7.2c9.278 0 16.8 7.522 16.8 16.8 0 5.166-2.332 9.787-6 12.869V72.4a3.6 3.6 0 10-7.2 0v12a3.6 3.6 0 003.6 3.6h12a3.6 3.6 0 100-7.2l-2.062.001A23.922 23.922 0 0088 64c0-13.255-10.745-24-24-24z\"/></g></svg>"},"$:/core/templates/javascript-tiddler":{"title":"$:/core/templates/javascript-tiddler","text":"<!--\n\nThis template is used for saving JavaScript tiddlers as a script tag with data attributes representing the tiddler fields. This version includes the tiddler changecount as the field `revision`.\n\n-->`<script`<$fields template=' data-tiddler-$name$=\"$encoded_value$\"'></$fields>` data-tiddler-revision=\"`<<changecount>>`\" data-tiddler-bag=\"default\" type=\"text/javascript\">`<$view field=\"text\" format=\"text\" />`</script>`"},"$:/plugins/tiddlywiki/tiddlyweb/readme":{"title":"$:/plugins/tiddlywiki/tiddlyweb/readme","text":"This plugin runs in the browser to synchronise tiddler changes to and from a TiddlyWeb-compatible server (including TiddlyWiki 5 itself, running on Node.js). It is inert when run under Node.js. Disabling this plugin via the browser can not be undone via the browser since this plugin provides the mechanism to synchronize settings with the server.\n\n[[Source code|https://github.com/Jermolene/TiddlyWiki5/blob/master/plugins/tiddlywiki/tiddlyweb]]\n"},"$:/plugins/tiddlywiki/tiddlyweb/readonly":{"title":"$:/plugins/tiddlywiki/tiddlyweb/readonly","tags":"[[$:/tags/Stylesheet]]","text":"\\define button-selector(title)\nbutton.$title$, .tc-drop-down button.$title$, div.$title$\n\\end\n\n\\define hide-edit-controls()\n<$reveal state=\"$:/status/IsReadOnly\" type=\"match\" text=\"yes\" default=\"yes\">\n<<button-selector tc-btn-\\%24\\%3A\\%2Fcore\\%2Fui\\%2FButtons\\%2Fclone>>`,`\n<<button-selector tc-btn-\\%24\\%3A\\%2Fcore\\%2Fui\\%2FButtons\\%2Fdelete>>`,`\n<<button-selector tc-btn-\\%24\\%3A\\%2Fcore\\%2Fui\\%2FButtons\\%2Fedit>>`,`\n<<button-selector tc-btn-\\%24\\%3A\\%2Fcore\\%2Fui\\%2FButtons\\%2Fnew-here>>`,`\n<<button-selector tc-btn-\\%24\\%3A\\%2Fcore\\%2Fui\\%2FButtons\\%2Fnew-journal-here>>`,`\n<<button-selector tc-btn-\\%24\\%3A\\%2Fcore\\%2Fui\\%2FButtons\\%2Fimport>>`,`\n<<button-selector tc-btn-\\%24\\%3A\\%2Fcore\\%2Fui\\%2FButtons\\%2Fmanager>>`,`\n<<button-selector tc-btn-\\%24\\%3A\\%2Fcore\\%2Fui\\%2FButtons\\%2Fnew-image>>`,`\n<<button-selector tc-btn-\\%24\\%3A\\%2Fcore\\%2Fui\\%2FButtons\\%2Fnew-journal>>`,`\n<<button-selector tc-btn-\\%24\\%3A\\%2Fcore\\%2Fui\\%2FButtons\\%2Fnew-tiddler>> `{\n\tdisplay: none;\n}`\n</$reveal>\n\\end\n\n\\rules only filteredtranscludeinline transcludeinline macrodef macrocallinline macrocallblock\n\n<<hide-edit-controls>>\n"},"$:/plugins/tiddlywiki/tiddlyweb/save/offline":{"title":"$:/plugins/tiddlywiki/tiddlyweb/save/offline","text":"\\import [[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]\n\\define saveTiddlerFilter()\n[is[tiddler]] -[[$:/boot/boot.css]] -[prefix[$:/HistoryList]] -[status[pending]plugin-type[import]] -[type[application/javascript]library[yes]] -[[$:/boot/boot.js]] -[[$:/boot/bootprefix.js]] -[[$:/plugins/tiddlywiki/filesystem]] -[[$:/plugins/tiddlywiki/tiddlyweb]] -[prefix[$:/temp/]] +[sort[title]] $(publishFilter)$\n\\end\n{{$:/core/templates/tiddlywiki5.html}}\n"},"$:/core/ui/Buttons/save-wiki":{"title":"$:/core/ui/Buttons/save-wiki","tags":"$:/tags/PageControls","caption":"{{$:/plugins/tiddlywiki/tiddlyweb/icon/cloud}} Server status","description":"Status of synchronisation with server","text":"\\whitespace trim\n\\define config-title()\n$:/config/PageControlButtons/Visibility/$(listItem)$\n\\end\n<$button popup=<<qualify \"$:/state/popup/save-wiki\">> tooltip=\"Status of synchronisation with server\" aria-label=\"Server status\" class=<<tv-config-toolbar-class>> selectedClass=\"tc-selected\">\n<span class=\"tc-dirty-indicator\">\n<$list filter=\"[<tv-config-toolbar-icons>match[yes]]\">\n{{$:/plugins/tiddlywiki/tiddlyweb/icon/cloud}}\n</$list>\n<$list filter=\"[<tv-config-toolbar-text>match[yes]]\">\n<span class=\"tc-btn-text\"><$text text=\"Server status\"/></span>\n</$list>\n</span>\n</$button>\n<$reveal state=<<qualify \"$:/state/popup/save-wiki\">> type=\"popup\" position=\"below\" animate=\"yes\">\n<div class=\"tc-drop-down\">\n<$list filter=\"[all[shadows+tiddlers]tag[$:/tags/SyncerDropdown]!has[draft.of]]\" variable=\"listItem\">\n<$transclude tiddler=<<listItem>>/>\n</$list>\n</div>\n</$reveal>\n"},"$:/plugins/tiddlywiki/tiddlyweb/styles":{"title":"$:/plugins/tiddlywiki/tiddlyweb/styles","tags":"[[$:/tags/Stylesheet]]","text":"\\rules only filteredtranscludeinline transcludeinline macrodef macrocallinline macrocallblock\n\nbody.tc-dirty span.tc-dirty-indicator svg {\n\ttransition: fill 250ms ease-in-out;\n}\n\nbody .tc-image-cloud-idle {\n\tfill: <<colour background>>;\n\ttransition: opacity 250ms ease-in-out;\n\topacity: 1;\n\tdisplay: unset;\n}\n\nbody.tc-dirty .tc-image-cloud-idle {\n\topacity: 0;\n\tdisplay: none;\n}\n\nbody .tc-image-cloud-progress {\n\ttransition: opacity 250ms ease-in-out;\n\ttransform-origin: 50% 50%;\n\ttransform: rotate(359deg);\n\tanimation: animation-rotate-slow 2s infinite linear;\n\tfill: <<colour background>>;\n\tdisplay: none;\n\topacity: 0;\n}\n\nbody.tc-dirty .tc-image-cloud-progress {\n\topacity: 1;\n\tdisplay: unset;\n}\n\n@keyframes animation-rotate-slow {\n\tfrom {\n\t\ttransform: rotate(0deg);\n\t}\n\tto {\n\t\ttransform: scale(359deg);\n\t}\n}\n"},"$:/plugins/tiddlywiki/tiddlyweb/syncer-actions/copy-logs":{"title":"$:/plugins/tiddlywiki/tiddlyweb/syncer-actions/copy-logs","tags":"$:/tags/SyncerDropdown","text":"<$button message=\"tm-copy-syncer-logs-to-clipboard\" class=\"tc-btn-invisible\">\n{{$:/core/images/copy-clipboard}} Copy syncer logs to clipboard\n</$button>\n"},"$:/plugins/tiddlywiki/tiddlyweb/syncer-actions/login-status":{"title":"$:/plugins/tiddlywiki/tiddlyweb/syncer-actions/login-status","tags":"$:/tags/SyncerDropdown","text":"<$reveal state=\"$:/status/IsLoggedIn\" type=\"match\" text=\"yes\">\n<div class=\"tc-drop-down-info\">\nYou are logged in<$reveal state=\"$:/status/UserName\" type=\"nomatch\" text=\"\" default=\"\"> as <strong><$text text={{$:/status/UserName}}/></strong></$reveal><$reveal state=\"$:/status/IsReadOnly\" type=\"match\" text=\"yes\" default=\"no\"> (read-only)</$reveal>\n</div>\n<hr/>\n</$reveal>\n"},"$:/plugins/tiddlywiki/tiddlyweb/syncer-actions/login":{"title":"$:/plugins/tiddlywiki/tiddlyweb/syncer-actions/login","tags":"$:/tags/SyncerDropdown","text":"<$reveal state=\"$:/status/IsLoggedIn\" type=\"nomatch\" text=\"yes\">\n<$button message=\"tm-login\" class=\"tc-btn-invisible\">\n{{$:/core/images/unlocked-padlock}} Login\n</$button>\n</$reveal>\n"},"$:/plugins/tiddlywiki/tiddlyweb/syncer-actions/logout":{"title":"$:/plugins/tiddlywiki/tiddlyweb/syncer-actions/logout","tags":"$:/tags/SyncerDropdown","text":"<$reveal state=\"$:/status/IsLoggedIn\" type=\"match\" text=\"yes\">\n<$button message=\"tm-logout\" class=\"tc-btn-invisible\">\n{{$:/core/images/cancel-button}} Logout\n</$button>\n</$reveal>\n"},"$:/plugins/tiddlywiki/tiddlyweb/syncer-actions/refresh":{"title":"$:/plugins/tiddlywiki/tiddlyweb/syncer-actions/refresh","tags":"$:/tags/SyncerDropdown","text":"<$reveal state=\"$:/status/IsLoggedIn\" type=\"match\" text=\"yes\">\n<$button tooltip=\"Get latest changes from the server\" aria-label=\"Refresh from server\" class=\"tc-btn-invisible\">\n<$action-sendmessage $message=\"tm-server-refresh\"/>\n{{$:/core/images/refresh-button}}<span class=\"tc-btn-text\"><$text text=\"Get latest changes from the server\"/></span>\n</$button>\n</$reveal>\n"},"$:/plugins/tiddlywiki/tiddlyweb/syncer-actions/save-snapshot":{"title":"$:/plugins/tiddlywiki/tiddlyweb/syncer-actions/save-snapshot","tags":"$:/tags/SyncerDropdown","text":"<$button class=\"tc-btn-invisible\">\n<$wikify name=\"site-title\" text={{$:/config/SaveWikiButton/Filename}}>\n<$action-sendmessage $message=\"tm-download-file\" $param={{$:/config/SaveWikiButton/Template}} filename=<<site-title>>/>\n</$wikify>\n{{$:/core/images/download-button}} Save snapshot for offline use\n</$button>\n"},"$:/tags/SyncerDropdown":{"title":"$:/tags/SyncerDropdown","list":"$:/plugins/tiddlywiki/tiddlyweb/syncer-actions/login-status $:/plugins/tiddlywiki/tiddlyweb/syncer-actions/login $:/plugins/tiddlywiki/tiddlyweb/syncer-actions/refresh $:/plugins/tiddlywiki/tiddlyweb/syncer-actions/logout $:/plugins/tiddlywiki/tiddlyweb/syncer-actions/save-snapshot $:/plugins/tiddlywiki/tiddlyweb/syncer-actions/copy-logs"},"$:/plugins/tiddlywiki/tiddlyweb/tiddlywebadaptor.js":{"title":"$:/plugins/tiddlywiki/tiddlyweb/tiddlywebadaptor.js","text":"/*\\\ntitle: $:/plugins/tiddlywiki/tiddlyweb/tiddlywebadaptor.js\ntype: application/javascript\nmodule-type: syncadaptor\n\nA sync adaptor module for synchronising with TiddlyWeb compatible servers\n\n\\*/\n(function(){\n\n/*jslint node: true, browser: true */\n/*global $tw: false */\n\"use strict\";\n\nvar CONFIG_HOST_TIDDLER = \"$:/config/tiddlyweb/host\",\n\tDEFAULT_HOST_TIDDLER = \"$protocol$//$host$/\";\n\nfunction TiddlyWebAdaptor(options) {\n\tthis.wiki = options.wiki;\n\tthis.host = this.getHost();\n\tthis.recipe = undefined;\n\tthis.hasStatus = false;\n\tthis.logger = new $tw.utils.Logger(\"TiddlyWebAdaptor\");\n\tthis.isLoggedIn = false;\n\tthis.isReadOnly = false;\n}\n\nTiddlyWebAdaptor.prototype.name = \"tiddlyweb\";\n\nTiddlyWebAdaptor.prototype.supportsLazyLoading = true;\n\nTiddlyWebAdaptor.prototype.setLoggerSaveBuffer = function(loggerForSaving) {\n\tthis.logger.setSaveBuffer(loggerForSaving);\n};\n\nTiddlyWebAdaptor.prototype.isReady = function() {\n\treturn this.hasStatus;\n};\n\nTiddlyWebAdaptor.prototype.getHost = function() {\n\tvar text = this.wiki.getTiddlerText(CONFIG_HOST_TIDDLER,DEFAULT_HOST_TIDDLER),\n\t\tsubstitutions = [\n\t\t\t{name: \"protocol\", value: document.location.protocol},\n\t\t\t{name: \"host\", value: document.location.host}\n\t\t];\n\tfor(var t=0; t<substitutions.length; t++) {\n\t\tvar s = substitutions[t];\n\t\ttext = $tw.utils.replaceString(text,new RegExp(\"\\\\$\" + s.name + \"\\\\$\",\"mg\"),s.value);\n\t}\n\treturn text;\n};\n\nTiddlyWebAdaptor.prototype.getTiddlerInfo = function(tiddler) {\n\treturn {\n\t\tbag: tiddler.fields.bag\n\t};\n};\n\nTiddlyWebAdaptor.prototype.getTiddlerRevision = function(title) {\n\tvar tiddler = this.wiki.getTiddler(title);\n\treturn tiddler.fields.revision;\n};\n\n/*\nGet the current status of the TiddlyWeb connection\n*/\nTiddlyWebAdaptor.prototype.getStatus = function(callback) {\n\t// Get status\n\tvar self = this;\n\tthis.logger.log(\"Getting status\");\n\t$tw.utils.httpRequest({\n\t\turl: this.host + \"status\",\n\t\tcallback: function(err,data) {\n\t\t\tself.hasStatus = true;\n\t\t\tif(err) {\n\t\t\t\treturn callback(err);\n\t\t\t}\n\t\t\t// Decode the status JSON\n\t\t\tvar json = null;\n\t\t\ttry {\n\t\t\t\tjson = JSON.parse(data);\n\t\t\t} catch (e) {\n\t\t\t}\n\t\t\tif(json) {\n\t\t\t\tself.logger.log(\"Status:\",data);\n\t\t\t\t// Record the recipe\n\t\t\t\tif(json.space) {\n\t\t\t\t\tself.recipe = json.space.recipe;\n\t\t\t\t}\n\t\t\t\t// Check if we're logged in\n\t\t\t\tself.isLoggedIn = json.username !== \"GUEST\";\n\t\t\t\tself.isReadOnly = !!json[\"read_only\"];\n\t\t\t\tself.isAnonymous = !!json.anonymous;\n\t\t\t}\n\t\t\t// Invoke the callback if present\n\t\t\tif(callback) {\n\t\t\t\tcallback(null,self.isLoggedIn,json.username,self.isReadOnly,self.isAnonymous);\n\t\t\t}\n\t\t}\n\t});\n};\n\n/*\nAttempt to login and invoke the callback(err)\n*/\nTiddlyWebAdaptor.prototype.login = function(username,password,callback) {\n\tvar options = {\n\t\turl: this.host + \"challenge/tiddlywebplugins.tiddlyspace.cookie_form\",\n\t\ttype: \"POST\",\n\t\tdata: {\n\t\t\tuser: username,\n\t\t\tpassword: password,\n\t\t\ttiddlyweb_redirect: \"/status\" // workaround to marginalize automatic subsequent GET\n\t\t},\n\t\tcallback: function(err) {\n\t\t\tcallback(err);\n\t\t},\n\t\theaders: {\n\t\t\t\"accept\": \"application/json\",\n\t\t\t\"X-Requested-With\": \"TiddlyWiki\"\n\t\t}\n\t};\n\tthis.logger.log(\"Logging in:\",options);\n\t$tw.utils.httpRequest(options);\n};\n\n/*\n*/\nTiddlyWebAdaptor.prototype.logout = function(callback) {\n\tvar options = {\n\t\turl: this.host + \"logout\",\n\t\ttype: \"POST\",\n\t\tdata: {\n\t\t\tcsrf_token: this.getCsrfToken(),\n\t\t\ttiddlyweb_redirect: \"/status\" // workaround to marginalize automatic subsequent GET\n\t\t},\n\t\tcallback: function(err,data) {\n\t\t\tcallback(err);\n\t\t},\n\t\theaders: {\n\t\t\t\"accept\": \"application/json\",\n\t\t\t\"X-Requested-With\": \"TiddlyWiki\"\n\t\t}\n\t};\n\tthis.logger.log(\"Logging out:\",options);\n\t$tw.utils.httpRequest(options);\n};\n\n/*\nRetrieve the CSRF token from its cookie\n*/\nTiddlyWebAdaptor.prototype.getCsrfToken = function() {\n\tvar regex = /^(?:.*; )?csrf_token=([^(;|$)]*)(?:;|$)/,\n\t\tmatch = regex.exec(document.cookie),\n\t\tcsrf = null;\n\tif (match && (match.length === 2)) {\n\t\tcsrf = match[1];\n\t}\n\treturn csrf;\n};\n\n/*\nGet an array of skinny tiddler fields from the server\n*/\nTiddlyWebAdaptor.prototype.getSkinnyTiddlers = function(callback) {\n\tvar self = this;\n\t$tw.utils.httpRequest({\n\t\turl: this.host + \"recipes/\" + this.recipe + \"/tiddlers.json\",\n\t\tdata: {\n\t\t\tfilter: \"[all[tiddlers]] -[[$:/isEncrypted]] -[prefix[$:/temp/]] -[prefix[$:/status/]] -[[$:/boot/boot.js]] -[[$:/boot/bootprefix.js]] -[[$:/library/sjcl.js]] -[[$:/core]]\"\n\t\t},\n\t\tcallback: function(err,data) {\n\t\t\t// Check for errors\n\t\t\tif(err) {\n\t\t\t\treturn callback(err);\n\t\t\t}\n\t\t\t// Process the tiddlers to make sure the revision is a string\n\t\t\tvar tiddlers = JSON.parse(data);\n\t\t\tfor(var t=0; t<tiddlers.length; t++) {\n\t\t\t\ttiddlers[t] = self.convertTiddlerFromTiddlyWebFormat(tiddlers[t]);\n\t\t\t}\n\t\t\t// Invoke the callback with the skinny tiddlers\n\t\t\tcallback(null,tiddlers);\n\t\t}\n\t});\n};\n\n/*\nSave a tiddler and invoke the callback with (err,adaptorInfo,revision)\n*/\nTiddlyWebAdaptor.prototype.saveTiddler = function(tiddler,callback,options) {\n\tvar self = this;\n\tif(this.isReadOnly) {\n\t\treturn callback(null,options.tiddlerInfo.adaptorInfo);\n\t}\n\t$tw.utils.httpRequest({\n\t\turl: this.host + \"recipes/\" + encodeURIComponent(this.recipe) + \"/tiddlers/\" + encodeURIComponent(tiddler.fields.title),\n\t\ttype: \"PUT\",\n\t\theaders: {\n\t\t\t\"Content-type\": \"application/json\"\n\t\t},\n\t\tdata: this.convertTiddlerToTiddlyWebFormat(tiddler),\n\t\tcallback: function(err,data,request) {\n\t\t\tif(err) {\n\t\t\t\treturn callback(err);\n\t\t\t}\n\t\t\t// Save the details of the new revision of the tiddler\n\t\t\tvar etag = request.getResponseHeader(\"Etag\");\n\t\t\tif(!etag) {\n\t\t\t\tcallback(\"Response from server is missing required `etag` header\");\n\t\t\t} else {\n\t\t\t\tvar etagInfo = self.parseEtag(etag);\n\t\t\t\t// Invoke the callback\n\t\t\t\tcallback(null,{\n\t\t\t\t\tbag: etagInfo.bag\n\t\t\t\t},etagInfo.revision);\t\t\t\t\n\t\t\t}\n\t\t}\n\t});\n};\n\n/*\nLoad a tiddler and invoke the callback with (err,tiddlerFields)\n*/\nTiddlyWebAdaptor.prototype.loadTiddler = function(title,callback) {\n\tvar self = this;\n\t$tw.utils.httpRequest({\n\t\turl: this.host + \"recipes/\" + encodeURIComponent(this.recipe) + \"/tiddlers/\" + encodeURIComponent(title),\n\t\tcallback: function(err,data,request) {\n\t\t\tif(err) {\n\t\t\t\treturn callback(err);\n\t\t\t}\n\t\t\t// Invoke the callback\n\t\t\tcallback(null,self.convertTiddlerFromTiddlyWebFormat(JSON.parse(data)));\n\t\t}\n\t});\n};\n\n/*\nDelete a tiddler and invoke the callback with (err)\noptions include:\ntiddlerInfo: the syncer's tiddlerInfo for this tiddler\n*/\nTiddlyWebAdaptor.prototype.deleteTiddler = function(title,callback,options) {\n\tvar self = this;\n\tif(this.isReadOnly) {\n\t\treturn callback(null,options.tiddlerInfo.adaptorInfo);\n\t}\n\t// If we don't have a bag it means that the tiddler hasn't been seen by the server, so we don't need to delete it\n\tvar bag = options.tiddlerInfo.adaptorInfo && options.tiddlerInfo.adaptorInfo.bag;\n\tif(!bag) {\n\t\treturn callback(null,options.tiddlerInfo.adaptorInfo);\n\t}\n\t// Issue HTTP request to delete the tiddler\n\t$tw.utils.httpRequest({\n\t\turl: this.host + \"bags/\" + encodeURIComponent(bag) + \"/tiddlers/\" + encodeURIComponent(title),\n\t\ttype: \"DELETE\",\n\t\tcallback: function(err,data,request) {\n\t\t\tif(err) {\n\t\t\t\treturn callback(err);\n\t\t\t}\n\t\t\t// Invoke the callback & return null adaptorInfo\n\t\t\tcallback(null,null);\n\t\t}\n\t});\n};\n\n/*\nConvert a tiddler to a field set suitable for PUTting to TiddlyWeb\n*/\nTiddlyWebAdaptor.prototype.convertTiddlerToTiddlyWebFormat = function(tiddler) {\n\tvar result = {},\n\t\tknownFields = [\n\t\t\t\"bag\", \"created\", \"creator\", \"modified\", \"modifier\", \"permissions\", \"recipe\", \"revision\", \"tags\", \"text\", \"title\", \"type\", \"uri\"\n\t\t];\n\tif(tiddler) {\n\t\t$tw.utils.each(tiddler.fields,function(fieldValue,fieldName) {\n\t\t\tvar fieldString = fieldName === \"tags\" ?\n\t\t\t\t\t\t\t\ttiddler.fields.tags :\n\t\t\t\t\t\t\t\ttiddler.getFieldString(fieldName); // Tags must be passed as an array, not a string\n\n\t\t\tif(knownFields.indexOf(fieldName) !== -1) {\n\t\t\t\t// If it's a known field, just copy it across\n\t\t\t\tresult[fieldName] = fieldString;\n\t\t\t} else {\n\t\t\t\t// If it's unknown, put it in the \"fields\" field\n\t\t\t\tresult.fields = result.fields || {};\n\t\t\t\tresult.fields[fieldName] = fieldString;\n\t\t\t}\n\t\t});\n\t}\n\t// Default the content type\n\tresult.type = result.type || \"text/vnd.tiddlywiki\";\n\treturn JSON.stringify(result,null,$tw.config.preferences.jsonSpaces);\n};\n\n/*\nConvert a field set in TiddlyWeb format into ordinary TiddlyWiki5 format\n*/\nTiddlyWebAdaptor.prototype.convertTiddlerFromTiddlyWebFormat = function(tiddlerFields) {\n\tvar self = this,\n\t\tresult = {};\n\t// Transfer the fields, pulling down the `fields` hashmap\n\t$tw.utils.each(tiddlerFields,function(element,title,object) {\n\t\tif(title === \"fields\") {\n\t\t\t$tw.utils.each(element,function(element,subTitle,object) {\n\t\t\t\tresult[subTitle] = element;\n\t\t\t});\n\t\t} else {\n\t\t\tresult[title] = tiddlerFields[title];\n\t\t}\n\t});\n\t// Make sure the revision is expressed as a string\n\tif(typeof result.revision === \"number\") {\n\t\tresult.revision = result.revision.toString();\n\t}\n\t// Some unholy freaking of content types\n\tif(result.type === \"text/javascript\") {\n\t\tresult.type = \"application/javascript\";\n\t} else if(!result.type || result.type === \"None\") {\n\t\tresult.type = \"text/x-tiddlywiki\";\n\t}\n\treturn result;\n};\n\n/*\nSplit a TiddlyWeb Etag into its constituent parts. For example:\n\n```\n\"system-images_public/unsyncedIcon/946151:9f11c278ccde3a3149f339f4a1db80dd4369fc04\"\n```\n\nNote that the value includes the opening and closing double quotes.\n\nThe parts are:\n\n```\n<bag>/<title>/<revision>:<hash>\n```\n*/\nTiddlyWebAdaptor.prototype.parseEtag = function(etag) {\n\tvar firstSlash = etag.indexOf(\"/\"),\n\t\tlastSlash = etag.lastIndexOf(\"/\"),\n\t\tcolon = etag.lastIndexOf(\":\");\n\tif(firstSlash === -1 || lastSlash === -1 || colon === -1) {\n\t\treturn null;\n\t} else {\n\t\treturn {\n\t\t\tbag: $tw.utils.decodeURIComponentSafe(etag.substring(1,firstSlash)),\n\t\t\ttitle: $tw.utils.decodeURIComponentSafe(etag.substring(firstSlash + 1,lastSlash)),\n\t\t\trevision: etag.substring(lastSlash + 1,colon)\n\t\t};\n\t}\n};\n\nif($tw.browser && document.location.protocol.substr(0,4) === \"http\" ) {\n\texports.adaptorClass = TiddlyWebAdaptor;\n}\n\n})();\n","type":"application/javascript","module-type":"syncadaptor"}}}