tow.html 98 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064
  1. <!DOCTYPE html>
  2. <!--[if lte IE 8]><html class="lt-ie9"><![endif]-->
  3. <!--[if gt IE 8]><!--><html><!--<![endif]-->
  4. <head>
  5. <meta charset="utf-8">
  6. <title>tow</title>
  7. <meta name="viewport" content="width=device-width, initial-scale=1">
  8. <!--
  9. Made in Twee 1.4.3
  10. Built on 14 Dec 2014 at 12:07:26, -0500
  11. This is a Sugarcane edit by @modgethanc.
  12. Sugarcane is based on:
  13. TiddlyWiki 1.2.39 by Jeremy Ruston, (jeremy [at] osmosoft [dot] com)
  14. Published under a BSD open source license
  15. Copyright (c) Osmosoft Limited 2005
  16. Redistribution and use in source and binary forms, with or without modification,
  17. are permitted provided that the following conditions are met:
  18. Redistributions of source code must retain the above copyright notice, this
  19. list of conditions and the following disclaimer.
  20. Redistributions in binary form must reproduce the above copyright notice, this
  21. list of conditions and the following disclaimer in the documentation and/or other
  22. materials provided with the distribution.
  23. Neither the name of the Osmosoft Limited nor the names of its contributors may be
  24. used to endorse or promote products derived from this software without specific
  25. prior written permission.
  26. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
  27. EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  28. OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
  29. SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  30. INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
  31. TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
  32. BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  33. CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  34. ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  35. DAMAGE.
  36. -->
  37. <script title="engine">
  38. (function(){
  39. /*
  40. **
  41. ** Basic utility functions
  42. **
  43. */
  44. function clone(a) {
  45. var constructor, b, proto;
  46. // Primitive types and functions are flat-out passed by value.
  47. if (!a || typeof a != "object") {
  48. return a;
  49. }
  50. constructor = a.constructor;
  51. if (constructor == Date || constructor == RegExp) {
  52. b = new constructor(a);
  53. }
  54. else if (constructor == Array) {
  55. b = [];
  56. }
  57. else if (a.nodeType && typeof a.cloneNode == "function") {
  58. b = a.cloneNode(true);
  59. }
  60. else {
  61. proto = (typeof Object.getPrototypeOf == "function" ? Object.getPrototypeOf(a) : a.__proto__);
  62. b = proto ? Object.create(proto) : {};
  63. }
  64. // This should work on both arrays and objects equally -
  65. // even copying expando properties foolishly added to arrays.
  66. for (var property in a) {
  67. if (Object.prototype.hasOwnProperty.call(a,property) && !isCyclic(a[property])) {
  68. if (typeof a[property] == "object") {
  69. try {
  70. b[property] = clone(a[property]);
  71. continue;
  72. }
  73. catch(e) {}
  74. }
  75. b[property] = a[property];
  76. }
  77. }
  78. return b;
  79. }
  80. function isCyclic(obj) {
  81. var properties = [];
  82. return (function recurse(obj) {
  83. var key, i, ownProps = [];
  84. if (obj && typeof obj == "object") {
  85. if (properties.indexOf(obj) > -1) {
  86. return true;
  87. }
  88. properties.push(obj);
  89. for (key in obj) {
  90. if (Object.prototype.hasOwnProperty.call(obj,key) && recurse(obj[key])) {
  91. return true;
  92. }
  93. }
  94. }
  95. return false;
  96. }(obj));
  97. }
  98. function insertElement(a, d, f, c, e) {
  99. var b = document.createElement(d);
  100. if (f) {
  101. b.id = f
  102. }
  103. if (c) {
  104. b.className = c
  105. }
  106. if (e) {
  107. insertText(b, e)
  108. }
  109. if (a) {
  110. a.appendChild(b)
  111. }
  112. return b
  113. }
  114. function addClickHandler(el, fn) {
  115. if (el.addEventListener) {
  116. el.addEventListener('click', fn);
  117. } else if (el.attachEvent) {
  118. el.attachEvent('onclick', fn);
  119. }
  120. }
  121. function insertText(a, b) {
  122. return a.appendChild(document.createTextNode(b))
  123. }
  124. function removeChildren(a) {
  125. while (a.hasChildNodes()) {
  126. a.removeChild(a.firstChild)
  127. }
  128. }
  129. function findPassageParent(el) {
  130. while(el && el != document.body && !~el.className.indexOf("passage")) {
  131. el = el.parentNode;
  132. }
  133. return el == document.body ? null : el;
  134. }
  135. function setPageElement(c, b, a) {
  136. var place;
  137. if (place = (typeof c == "string" ? document.getElementById(c) : c)) {
  138. removeChildren(place);
  139. if (tale.has(b)) {
  140. new Wikifier(place, tale.get(b).processText())
  141. } else {
  142. new Wikifier(place, a)
  143. }
  144. }
  145. }
  146. var scrollWindowInterval;
  147. function scrollWindowTo(e, margin) {
  148. var d = window.scrollY ? window.scrollY : document.documentElement.scrollTop,
  149. m = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight,
  150. g = k(e),
  151. j = (d > g) ? -1 : 1,
  152. b = 0,
  153. c = Math.abs(d - g);
  154. scrollWindowInterval && window.clearInterval(scrollWindowInterval);
  155. if (c) {
  156. scrollWindowInterval = window.setInterval(h, 25);
  157. }
  158. function h() {
  159. b += 0.1;
  160. window.scrollTo(0, d + j * (c * Math.easeInOut(b)));
  161. if (b >= 1) {
  162. window.clearInterval(scrollWindowInterval)
  163. }
  164. }
  165. function k(o) {
  166. var p = a(o),
  167. h = o.offsetHeight,
  168. n = d + m;
  169. p = Math.min(Math.max(p + (margin || 0) * ( p < d ? -1 : 1), 0), n);
  170. if (p < d) {
  171. return p
  172. } else {
  173. if (p+h > n) {
  174. if (h < m) {
  175. return (p - (m - h) + 20)
  176. } else {
  177. return p
  178. }
  179. } else {
  180. return p
  181. }
  182. }
  183. }
  184. function a(l) {
  185. var m = 0;
  186. while (l.offsetParent) {
  187. m += l.offsetTop;
  188. l = l.offsetParent
  189. }
  190. return m
  191. }
  192. }
  193. // Returns an object containing the properties in neu which differ from old.
  194. function delta(old,neu) {
  195. var vars, ret = {};
  196. if (old && neu) {
  197. for (vars in neu) {
  198. if (neu[vars] !== old[vars]) {
  199. ret[vars] = neu[vars];
  200. }
  201. }
  202. }
  203. return ret;
  204. }
  205. // Convert various exotic objects into JSON-serialisable plain objects.
  206. function decompile(val) {
  207. var i, ret;
  208. if ((typeof val != "object" && typeof val != "function") || !val) {
  209. return val;
  210. }
  211. // Passage objects: store only their name
  212. else if (val instanceof Passage) {
  213. return { "[[Passage]]" : val.id };
  214. }
  215. else if (Array.isArray(val)) {
  216. ret = [];
  217. }
  218. else {
  219. ret = {};
  220. }
  221. // Deep-copy own properties
  222. for (i in val) {
  223. if (Object.prototype.hasOwnProperty.call(val,i) && !isCyclic(val[i])) {
  224. ret[i] = decompile(val[i]);
  225. }
  226. }
  227. // Functions: store the decompiled function body as a property
  228. // arbitrarily called "[[Call]]".
  229. if (typeof val == "function" || val instanceof RegExp) {
  230. try {
  231. // Check if it can be recompiled (i.e. isn't a bound or native)
  232. internalEval(val+"");
  233. ret["[[Call]]"] = val+"";
  234. } catch(e) {
  235. // Silently fail
  236. ret["[[Call]]"] = "function(){}"
  237. }
  238. }
  239. return ret;
  240. }
  241. // Reverses the effects of decompile()
  242. // Takes recently JSON.parse()d objects and makes them exotic again.
  243. function recompile(val) {
  244. var i, ret = val;
  245. if (val && typeof val == "object") {
  246. // Passages
  247. if (typeof val["[[Passage]]"] == "number") {
  248. return tale.get(val["[[Passage]]"]);
  249. }
  250. // Functions/RegExps
  251. if (typeof val["[[Call]]"] == "string") {
  252. try {
  253. ret = internalEval(val["[[Call]]"]);
  254. }
  255. catch(e){}
  256. }
  257. // Recursively recompile all properties
  258. // or, if a function/regexp, deep-copy them to the new function.
  259. for (i in val) {
  260. if (Object.prototype.hasOwnProperty.call(val,i)) {
  261. ret[i] = recompile(val[i]);
  262. }
  263. }
  264. }
  265. return ret;
  266. }
  267. function addStyle(b) {
  268. if (document.createStyleSheet) {
  269. document.getElementsByTagName("head")[0].insertAdjacentHTML("beforeEnd", "&nbsp;<style>" + b + "</style>")
  270. } else {
  271. var a = document.createElement("style");
  272. a.appendChild(document.createTextNode(b));
  273. document.getElementsByTagName("head")[0].appendChild(a)
  274. }
  275. }
  276. function alterCSS(text) {
  277. var temp = '', imgPassages = tale.lookup("tags", "Twine.image");
  278. // Remove comments
  279. text = text.replace(/\/\*(?:[^\*]|\*(?!\/))*\*\//g,'');
  280. // Replace :link
  281. text = text.replace(/:link/g,"[class*=Link]");
  282. // Replace :visited
  283. text = text.replace(/:visited/g,".visitedLink");
  284. // Hoist @import
  285. text = text.replace(/@import\s+(?:url\s*\(\s*['"]?|['"])[^"'\s]+(?:['"]?\s*\)|['"])\s*([\w\s\(\)\d\:,\-]*);/g, function(e) {
  286. temp += e; return '';
  287. });
  288. text = temp + text;
  289. // Add images
  290. return text.replace(new RegExp(Wikifier.imageFormatter.lookahead, "gim"), function(m,p1,p2,p3,src) {
  291. for (var i = 0; i < imgPassages.length; i++) {
  292. if (imgPassages[i].title == src) {
  293. src = imgPassages[i].text;
  294. break;
  295. }
  296. }
  297. return "url(" + src + ")"
  298. });
  299. }
  300. function setTransitionCSS(styleText) {
  301. styleText = alterCSS(styleText);
  302. var style = document.getElementById("transitionCSS");
  303. style.styleSheet ? (style.styleSheet.cssText = styleText) : (style.innerHTML = styleText);
  304. }
  305. function throwError(a, b, tooltip) {
  306. if (a) {
  307. var elem = insertElement(a, "span", null, "marked", b);
  308. tooltip && elem.setAttribute("title", tooltip);
  309. } else {
  310. alert("Regrettably, this " + tale.identity() + "'s code just ran into a problem:\n" + b + ".\n"+softErrorMessage);
  311. }
  312. }
  313. Math.easeInOut = function (a) {
  314. return (1 - ((Math.cos(a * Math.PI) + 1) / 2))
  315. };
  316. String.prototype.readMacroParams = function (keepquotes) {
  317. var exec, re = /(?:\s*)(?:(?:"([^"]*)")|(?:'([^']*)')|(?:\[\[((?:[^\]]|\](?!\]))*)\]\])|([^"'\s]\S*))/mg,
  318. params = [];
  319. do {
  320. var val;
  321. exec = re.exec(this);
  322. if (exec) {
  323. if (exec[1]) {
  324. val = exec[1];
  325. keepquotes && (val = '"' + val + '"');
  326. } else if (exec[2]) {
  327. val = exec[2];
  328. keepquotes && (val = "'" + val + "'");
  329. } else if (exec[3]) {
  330. val = exec[3];
  331. keepquotes && (val = '"' + val.replace('"','\\"') + '"');
  332. } else if (exec[4]) {
  333. val = exec[4];
  334. }
  335. val && params.push(val);
  336. }
  337. } while (exec);
  338. return params
  339. };
  340. String.prototype.readBracketedList = function () {
  341. var c, b = "\\[\\[([^\\]]+)\\]\\]",
  342. a = "[^\\s$]+",
  343. e = "(?:" + b + ")|(" + a + ")",
  344. d = new RegExp(e, "mg"),
  345. f = [];
  346. do {
  347. c = d.exec(this);
  348. if (c) {
  349. if (c[1]) {
  350. f.push(c[1])
  351. } else {
  352. if (c[2]) {
  353. f.push(c[2])
  354. }
  355. }
  356. }
  357. } while (c);
  358. return (f)
  359. };
  360. function rot13(s) {
  361. return s.replace(/[a-zA-Z]/g, function(c) {
  362. return String.fromCharCode((c<="Z" ? 90 : 122) >= (c=c.charCodeAt()+13) ? c : c-26);
  363. });
  364. }
  365. /*
  366. **
  367. ** Polyfills
  368. **
  369. */
  370. Object.create || (function() {
  371. var F = function(){};
  372. Object.create = function (o) {
  373. if (typeof o != 'object') throw TypeError();
  374. F.prototype = o;
  375. return new F();
  376. };
  377. })();
  378. String.prototype.trim || (String.prototype.trim = function () {
  379. return this.replace(/^\s\s*/, "").replace(/\s\s*$/, "")
  380. });
  381. Array.isArray || (Array.isArray = function(arg) {
  382. return Object.prototype.toString.call(arg) === '[object Array]';
  383. });
  384. Array.prototype.indexOf || (Array.prototype.indexOf = function (b, d) {
  385. d = (d == null) ? 0 : d;
  386. var a = this.length;
  387. for (var c = d; c < a; c++) {
  388. if (this[c] == b) {
  389. return c
  390. }
  391. }
  392. return -1
  393. });
  394. Array.prototype.forEach || (Array.prototype.forEach=function(fun){if(this==null){throw TypeError()}var t=Object(this);var len=+t.length;if(typeof fun!="function"){throw TypeError()}var thisArg=arguments.length>=2?arguments[1]:void 0;for(var i=0;i<len;i++){if(i in t){fun.call(thisArg,t[i],i,t)}}});
  395. /* btoa/atob polyfill by github.com/davidchambers */
  396. (function(){function t(t){this.message=t}var e=window,r="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";t.prototype=Error(),t.prototype.name="InvalidCharacterError",e.btoa||(e.btoa=function(e){for(var o,n,a=0,i=r,c="";e.charAt(0|a)||(i="=",a%1);c+=i.charAt(63&o>>8-8*(a%1))){if(n=e.charCodeAt(a+=.75),n>255)throw new t();o=o<<8|n}return c}),e.atob||(e.atob=function(e){if(e=e.replace(/=+$/,""),1==e.length%4)throw new t();for(var o,n,a=0,i=0,c="";n=e.charAt(i++);~n&&(o=a%4?64*o+n:n,a++%4)?c+=String.fromCharCode(255&o>>(6&-2*a)):0)n=r.indexOf(n);return c})})();
  397. /* Polyfill for fade transition */
  398. var hasTransition = 'transition' in document.documentElement.style || '-webkit-transition' in document.documentElement.style;
  399. function fade(f, c) {
  400. var h;
  401. var e = f.cloneNode(true);
  402. var g = (c.fade == "in") ? 1 : -1;
  403. f.parentNode.replaceChild(e, f);
  404. if (c.fade == "in") {
  405. h = 0;
  406. e.style.visibility = "visible"
  407. } else {
  408. h = 1
  409. }
  410. b(e, h);
  411. var a = window.setInterval(d, 25);
  412. function d() {
  413. h += 0.05 * g;
  414. b(e, Math.easeInOut(h));
  415. if (((g == 1) && (h >= 1)) || ((g == -1) && (h <= 0))) {
  416. f.style.visibility = (c.fade == "in") ? "visible" : "hidden";
  417. if (e.parentNode) {
  418. e.parentNode.replaceChild(f, e);
  419. }
  420. window.clearInterval(a);
  421. if (c.onComplete) {
  422. c.onComplete.call(f)
  423. }
  424. }
  425. }
  426. function b(k, j) {
  427. var l = Math.floor(j * 100);
  428. k.style.zoom = 1;
  429. k.style.filter = "alpha(opacity=" + l + ")";
  430. k.style.opacity = j
  431. }
  432. }
  433. /*
  434. **
  435. ** History object
  436. **
  437. */
  438. function History() {
  439. this.history = [{
  440. passage: null,
  441. variables: {}
  442. }]
  443. // Unique identifier for this game session
  444. this.id = new Date().getTime()+'';
  445. // URL of the bookmark link
  446. this.hash = '';
  447. }
  448. History.prototype.encodeHistory = function(b, noVars) {
  449. var ret = ".", vars, type, hist = this.history[b],
  450. d = this.history[b+1] ? delta(this.history[b+1].variables, hist.variables) : hist.variables;
  451. function vtob(val) {
  452. try {
  453. return window.btoa(unescape(encodeURIComponent(JSON.stringify(decompile(val)))));
  454. } catch(e) {
  455. return "0";
  456. }
  457. }
  458. if (!hist.passage || hist.passage.id == null) {
  459. return ""
  460. }
  461. ret += hist.passage.id.toString(36);
  462. if (noVars) {
  463. return ret;
  464. }
  465. for (vars in d) {
  466. type = typeof d[vars];
  467. if (type != "undefined") {
  468. ret += "$" + vtob(vars) + "," + vtob(d[vars]);
  469. }
  470. }
  471. for (vars in hist.linkVars) {
  472. type = typeof hist.linkVars[vars];
  473. if (type != "function" && type != "undefined") {
  474. ret += "[" + vtob(vars) + "," + vtob(hist.linkVars[vars]);
  475. }
  476. }
  477. return ret
  478. };
  479. History.decodeHistory = function(str, prev) {
  480. var name, splits, variable, c, d,
  481. ret = { variables: clone(prev.variables) || {} },
  482. match = /([a-z0-9]+)((?:\$[A-Za-z0-9\+\/=]+,[A-Za-z0-9\+\/=]+)*)((?:\[[A-Za-z0-9\+\/=]+,[A-Za-z0-9\+\/=]+)*)/g.exec(str);
  483. function btov(str) {
  484. try {
  485. return recompile(JSON.parse(decodeURIComponent(escape(window.atob(str)))));
  486. } catch(e) {
  487. return 0;
  488. }
  489. }
  490. if (match) {
  491. name = parseInt(match[1], 36);
  492. if (!tale.has(name)) {
  493. return false
  494. }
  495. if (match[2]) {
  496. ret.variables || (ret.variables = {});
  497. splits = match[2].split('$');
  498. for (c = 0; c < splits.length; c++) {
  499. variable = splits[c].split(",");
  500. d = btov(variable[0]);
  501. if (d) {
  502. ret.variables[d]=btov(variable[1]);
  503. }
  504. }
  505. }
  506. if (match[3]) {
  507. ret.linkVars || (ret.linkVars = {});
  508. splits = match[3].split('[');
  509. for (c = 0; c < splits.length; c++) {
  510. variable = splits[c].split(",");
  511. d = btov(variable[0]);
  512. if (d) {
  513. ret.linkVars[d]=btov(variable[1]);
  514. }
  515. }
  516. }
  517. ret.passage = tale.get(name);
  518. return ret;
  519. }
  520. };
  521. History.prototype.save = function() {
  522. var hist, b, a = "";
  523. for (b = this.history.length - 1; b >= 0; b--) {
  524. hist = this.history[b];
  525. if (!hist) {
  526. break;
  527. }
  528. a += this.encodeHistory(b);
  529. }
  530. return "#" + a
  531. };
  532. History.prototype.restore = function () {
  533. var a, b, c, vars;
  534. try {
  535. if (!window.location.hash || (window.location.hash == "#")) {
  536. if (testplay) {
  537. if (tale.has("StoryInit")) {
  538. new Wikifier(insertElement(null, "span"), tale.get("StoryInit").text);
  539. }
  540. this.display(testplay, null, 'quietly');
  541. return true
  542. }
  543. return false
  544. }
  545. if (window.location.hash.substr(0, 2) == '#!') {
  546. c = window.location.hash.substr(2).split('_').join(' ');
  547. this.display(c, null, 'quietly');
  548. return true
  549. }
  550. a = window.location.hash.replace("#", "").split(".");
  551. for (b = 0; b < a.length; b++) {
  552. vars = History.decodeHistory(a[b], vars || {});
  553. if (vars) {
  554. if (b == a.length - 1) {
  555. vars.variables = clone(this.history[0].variables);
  556. for (c in this.history[0].linkVars) {
  557. vars.variables[c] = clone(this.history[0].linkVars[c]);
  558. }
  559. this.history.unshift(vars);
  560. this.display(vars.passage.title, null, "back");
  561. }
  562. else {
  563. this.history.unshift(vars);
  564. }
  565. }
  566. }
  567. return true
  568. } catch (d) {
  569. return false
  570. }
  571. };
  572. History.prototype.saveVariables = function(c, el, callback) {
  573. if (typeof callback == "function") {
  574. callback.call(el);
  575. }
  576. this.history.unshift({
  577. passage: c,
  578. variables: clone(this.history[0].variables)
  579. });
  580. };
  581. var restart = History.prototype.restart = function () {
  582. if (typeof window.history.replaceState == "function") {
  583. (typeof this.pushState == "function") && this.pushState(true, window.location.href.replace(/#.*$/,''));
  584. window.location.reload()
  585. }
  586. else {
  587. window.location.hash = "";
  588. }
  589. };
  590. /*
  591. **
  592. ** Macros
  593. **
  594. */
  595. var version = {
  596. major: 4,
  597. minor: 3,
  598. revision: 0,
  599. date: new Date("2014"),
  600. extensions: {}
  601. };
  602. var testplay, tale, state, prerender = {}, postrender = {}, macros = window.macros = {};
  603. version.extensions.displayMacro = {
  604. major: 2,
  605. minor: 0,
  606. revision: 0
  607. };
  608. macros.display = {
  609. // Used by parameter() and parameterValue()
  610. parameters: [],
  611. handler: function (place, macroName, params, parser) {
  612. var t, j, output, oldDisplayParams, name = parser.fullArgs();
  613. if (macroName != "display") {
  614. output = macroName;
  615. // Shorthand displays can have parameters
  616. params = parser.fullMatch().replace(/^\S*|>>$/g,'').readMacroParams(true);
  617. // The above line recreates params, with the quotes.
  618. try {
  619. for(j=0; j < params.length; j++) {
  620. params[j] = internalEval(Wikifier.parse(params[j]));
  621. }
  622. } catch (e) {
  623. throwError(place, parser.fullMatch() + " bad argument: " + params[j], parser.fullMatch());
  624. return
  625. }
  626. }
  627. else {
  628. try {
  629. output = internalEval(name);
  630. }
  631. catch(e) {
  632. }
  633. if (output == null) {
  634. // Last-ditch attempt
  635. if (tale.has(name)) {
  636. output = name;
  637. }
  638. }
  639. }
  640. if (!output) {
  641. throwError(place, '<<' + macroName + '>>: "' +name + "\" did not evaluate to a passage name", parser.fullMatch());
  642. } else if (!tale.has(output+"")) {
  643. throwError(place, '<<' + macroName + ">>: The \"" + output + "\" passage does not exist", parser.fullMatch());
  644. } else {
  645. oldDisplayParams = this.parameters;
  646. this.parameters = params;
  647. t = tale.get(output+"");
  648. if (t.tags.indexOf("script") > -1) {
  649. scriptEval(t);
  650. }
  651. else {
  652. new Wikifier(place, t.processText());
  653. }
  654. this.parameters = oldDisplayParams;
  655. }
  656. }
  657. };
  658. version.extensions.actionsMacro = {
  659. major: 1,
  660. minor: 2,
  661. revision: 0
  662. };
  663. macros.actions = {
  664. handler: function (a, f, g) {
  665. var v = state.history[0].variables, e = insertElement(a, "ul");
  666. if (!v["actions clicked"]) {
  667. v["actions clicked"] = {}
  668. }
  669. for (var b = 0; b < g.length; b++) {
  670. if (v["actions clicked"][g[b]]) {
  671. continue
  672. }
  673. var d = insertElement(e, "li");
  674. var c = Wikifier.createInternalLink(d, g[b], (function(link) {
  675. return function() { state.history[0].variables["actions clicked"][link] = true; }
  676. }(g[b])));
  677. insertText(c, g[b]);
  678. }
  679. }
  680. };
  681. version.extensions.printMacro = {
  682. major: 1,
  683. minor: 1,
  684. revision: 1
  685. };
  686. macros.print = {
  687. handler: function (place, macroName, params, parser) {
  688. var args = parser.fullArgs(macroName != "print"), output;
  689. try {
  690. // See comment within macros.display
  691. output = internalEval(args);
  692. if (output != null && (typeof output != "number" || !isNaN(output))) {
  693. new Wikifier(place, ''+output);
  694. }
  695. } catch (e) {
  696. throwError(place, "<<print>> bad expression: " + params.join(' '), parser.fullMatch());
  697. }
  698. }
  699. };
  700. version.extensions.setMacro = {
  701. major: 1,
  702. minor: 1,
  703. revision: 0
  704. };
  705. macros.set = {
  706. handler: function (a, b, c, parser) {
  707. macros.set.run(a, parser.fullArgs(), parser, c.join(' '))
  708. },
  709. run: function (a,expression, parser, original) {
  710. try {
  711. return internalEval(expression);
  712. } catch (e) {
  713. throwError(a, "bad expression: " + (original || expression), parser ? parser.fullMatch() : expression)
  714. }
  715. }
  716. };
  717. version.extensions.ifMacros = {
  718. major: 2,
  719. minor: 0,
  720. revision: 0
  721. };
  722. macros["if"] = {
  723. handler: function (place, macroName, params, parser) {
  724. var conditions = [],
  725. clauses = [],
  726. rawConds = [],
  727. srcOffset = parser.source.indexOf(">>", parser.matchStart) + 2,
  728. src = parser.source.slice(srcOffset),
  729. endPos = -1,
  730. rawCond = params.join(' '),
  731. currentCond = parser.fullArgs(),
  732. currentClause = "",
  733. t = 0,
  734. nesting = 0,
  735. i = 0;
  736. for (; i < src.length; i++) {
  737. if ((src.substr(i, 6) == "<<else") && !nesting) {
  738. rawConds.push(rawCond);
  739. conditions.push(currentCond.trim());
  740. clauses.push(currentClause);
  741. currentClause="";
  742. t = src.indexOf(">>",i+6);
  743. if(src.substr(i+6,4)==" if " || src.substr(i+6,3)=="if ") {
  744. rawCond = src.slice(i+9,t);
  745. currentCond = Wikifier.parse(rawCond);
  746. }
  747. else {
  748. rawCond = "";
  749. currentCond = "true";
  750. }
  751. i = t+2;
  752. }
  753. if (src.substr(i, 5) == "<<if ") {
  754. nesting++;
  755. }
  756. if (src.substr(i, 9) == "<<endif>>") {
  757. nesting--;
  758. if (nesting < 0) {
  759. endPos = srcOffset + i + 9;
  760. rawConds.push(rawCond);
  761. conditions.push(currentCond.trim());
  762. clauses.push(currentClause);
  763. break;
  764. }
  765. }
  766. currentClause += src.charAt(i);
  767. }
  768. if (endPos != -1) {
  769. parser.nextMatch = endPos;
  770. try {
  771. for(i=0;i<clauses.length;i++) {
  772. if (internalEval(conditions[i])) {
  773. new Wikifier(place, clauses[i]);
  774. break;
  775. }
  776. }
  777. } catch (e) {
  778. throwError(place, "<<" + (i ? "else " : "") + "if>> bad condition: " + rawConds[i], !i ? parser.fullMatch()
  779. : "<<else if " + rawConds[i] + ">>");
  780. }
  781. } else {
  782. throwError(place, "I can't find a matching <<endif>>", parser.fullMatch());
  783. }
  784. }
  785. };
  786. macros["else"] = macros.elseif = macros.endif = {
  787. handler: function () {}
  788. };
  789. version.extensions.rememberMacro = {
  790. major: 2,
  791. minor: 0,
  792. revision: 0
  793. };
  794. macros.remember = {
  795. handler: function (place, macroName, params, parser) {
  796. var variable, value, re, match,
  797. statement = params.join(" ");
  798. macros.set.run(place, parser.fullArgs(), null, params.join(' '));
  799. if (!window.localStorage) {
  800. throwError(place, "<<remember>> can't be used "
  801. + (window.location.protocol == "file:" ? " by local HTML files " : "") + " in this browser.",parser.fullMatch());
  802. return;
  803. }
  804. re = new RegExp(Wikifier.textPrimitives.variable, "g");
  805. while (match = re.exec(statement)) {
  806. variable = match[1];
  807. value = state.history[0].variables[variable];
  808. try {
  809. value = JSON.stringify(value);
  810. } catch (e) {
  811. throwError(place, "can't <<remember>> the variable $" + variable + " (" + (typeof value) + ")", parser.fullMatch());
  812. return;
  813. }
  814. window.localStorage[this.prefix + variable] = value;
  815. }
  816. },
  817. init: function () {
  818. var i, variable, value;
  819. this.prefix = "Twine." + tale.defaultTitle + ".";
  820. for (i in window.localStorage) {
  821. if (i.indexOf(this.prefix) == 0) {
  822. variable = i.substr(this.prefix.length);
  823. value = window.localStorage[i];
  824. try {
  825. value = JSON.parse(value);
  826. state.history[0].variables[variable]=value;
  827. } catch (e) {
  828. }
  829. }
  830. }
  831. },
  832. expire: null,
  833. prefix: null
  834. };
  835. version.extensions.forgetMacro = {
  836. major: 1,
  837. minor: 0,
  838. revision: 0
  839. };
  840. macros.forget = {
  841. handler: function (place, macroName, params) {
  842. var re, match, variable,
  843. statement = params.join(" ");
  844. re = new RegExp(Wikifier.textPrimitives.variable, "g");
  845. while (match = re.exec(statement)) {
  846. variable = match[1] + ""
  847. delete state.history[0].variables[variable];
  848. delete window.localStorage[macros.remember.prefix + variable];
  849. }
  850. }
  851. };
  852. version.extensions.SilentlyMacro = {
  853. major: 1,
  854. minor: 1,
  855. revision: 0
  856. };
  857. macros.nobr = macros.silently = {
  858. handler: function (place, macroName, f, parser) {
  859. var i, h = insertElement(null, 'div'),
  860. k = parser.source.indexOf('>>', parser.matchStart) + 2,
  861. a = parser.source.slice(k),
  862. d = -1,
  863. c = '',
  864. l = 0;
  865. for (i = 0; i < a.length; i++) {
  866. if (a.substr(i, macroName.length+7) == '<<end' + macroName + '>>') {
  867. if (l == 0) {
  868. d = k + i + macroName.length+7;
  869. break;
  870. } else {
  871. l--;
  872. }
  873. } else if (a.substr(i, macroName.length+4) == '<<' + macroName + '>>') {
  874. l++;
  875. }
  876. if (macroName == "nobr" && a.charAt(i) == '\n') {
  877. c += "\u200c"; // Zero-width space
  878. }
  879. else {
  880. c += a.charAt(i);
  881. }
  882. }
  883. if (d != -1) {
  884. new Wikifier(macroName == "nobr" ? place : h, c);
  885. parser.nextMatch = d;
  886. } else {
  887. throwError(place, "can't find matching <<end" + macroName+">>", parser.fullMatch());
  888. }
  889. }
  890. };
  891. macros.endsilently = {
  892. handler: function () {}
  893. };
  894. version.extensions.choiceMacro = {
  895. major: 2,
  896. minor: 0,
  897. revision: 0
  898. };
  899. macros.choice = {
  900. callback: function() {
  901. var i, other, passage = findPassageParent(this);
  902. if (passage) {
  903. other = passage.querySelectorAll(".choice");
  904. for (i = 0; i < other.length; i++) {
  905. other[i].outerHTML = "<span class=disabled>" + other[i].innerHTML + "</span>";
  906. }
  907. state.history[0].variables["choice clicked"][passage.id.replace(/\|[^\]]*$/,'')] = true;
  908. }
  909. },
  910. handler: function (A, C, D, parser) {
  911. var link, id, match,
  912. text = D[1] || D[0].split("|")[0],
  913. passage = findPassageParent(A);
  914. // Get ID of the "choice clicked" entry
  915. if (!passage) {
  916. throwError(A, "<<"+C+">> can't be used here.",parser.fullMatch());
  917. return;
  918. }
  919. id = (passage && passage.id.replace(/\|[^\]]*$/,''));
  920. if (id && (state.history[0].variables["choice clicked"] ||
  921. (state.history[0].variables["choice clicked"] = {}))[id]) {
  922. insertElement(A, "span", null, "disabled", text);
  923. }
  924. else {
  925. match = new RegExp(Wikifier.linkFormatter.lookahead).exec(parser.fullMatch());
  926. if (match) {
  927. link = Wikifier.linkFormatter.makeLink(A,match,this.callback);
  928. }
  929. else {
  930. link = Wikifier.linkFormatter.makeLink(A,[0,text,D[0]],this.callback);
  931. }
  932. link.className += " " + C;
  933. }
  934. }
  935. };
  936. version.extensions.backMacro = {
  937. major: 2,
  938. minor: 0,
  939. revision: 0
  940. };
  941. macros.back = {
  942. labeltext: '&#171; back',
  943. handler: function (a, b, e, parser) {
  944. var labelParam, c, el,
  945. labeltouse = this.labeltext,
  946. steps = 1,
  947. stepsParam = e.indexOf("steps"),
  948. stepsParam2 = "";
  949. // Steps parameter
  950. if(stepsParam > 0) {
  951. stepsParam2 = e[stepsParam - 1];
  952. if(stepsParam2[0] == '$') {
  953. try {
  954. stepsParam2 = internalEval(Wikifier.parse(stepsParam2));
  955. }
  956. catch(r) {
  957. throwError(a, parser.fullMatch() + " bad expression: " + r.message, parser.fullMatch())
  958. return;
  959. }
  960. }
  961. // Previously, trying to go back more steps than were present in the
  962. // history would silently revert to just 1 step.
  963. // Instead, let's just go back to the start.
  964. steps = +stepsParam2;
  965. if(steps >= state.history.length - 1) {
  966. steps = state.history.length - 2;
  967. }
  968. e.splice(stepsParam - 1, 2);
  969. }
  970. // Label parameter
  971. labelParam = e.indexOf("label");
  972. if(labelParam > -1) {
  973. if(!e[labelParam + 1]) {
  974. throwError(a, parser.fullMatch() + ": " + e[labelParam] + ' keyword needs an additional label parameter', parser.fullMatch());
  975. return;
  976. }
  977. labeltouse = e[labelParam + 1];
  978. e.splice(labelParam, 2);
  979. }
  980. // What's left is the passage name parameter
  981. if(stepsParam <= 0) {
  982. if(e[0]) {
  983. if(e[0].charAt(0) == '$') {
  984. try {
  985. e = internalEval(Wikifier.parse(e[0]));
  986. }
  987. catch(r) {
  988. throwError(a, parser.fullMatch() + " bad expression: " + r.message, parser.fullMatch())
  989. return;
  990. }
  991. }
  992. else {
  993. e = e[0];
  994. }
  995. if(!tale.has(e)) {
  996. throwError(a, "The \"" + e + "\" passage does not exist",parser.fullMatch());
  997. return;
  998. }
  999. for(c = 0; c < state.history.length; c++) {
  1000. if(state.history[c].passage.title == e) {
  1001. steps = c;
  1002. break;
  1003. }
  1004. }
  1005. }
  1006. }
  1007. el = document.createElement("a");
  1008. el.className = b;
  1009. addClickHandler(el, (function(b) { return function () {
  1010. return macros.back.onclick(b == "back", steps, el)
  1011. }}(b)));
  1012. el.innerHTML = labeltouse;
  1013. a.appendChild(el);
  1014. }
  1015. };
  1016. version.extensions.returnMacro = {
  1017. major: 2,
  1018. minor: 0,
  1019. revision: 0
  1020. };
  1021. macros["return"] = {
  1022. labeltext: '&#171; return',
  1023. handler: function(a,b,e) {
  1024. macros.back.handler.call(this,a,b,e);
  1025. }
  1026. };
  1027. version.extensions.textInputMacro = {
  1028. major: 2,
  1029. minor: 0,
  1030. revision: 0
  1031. };
  1032. macros.checkbox = macros.radio = macros.textinput = {
  1033. handler: function (A, C, D, parser) {
  1034. var match,
  1035. class_ = C.replace('input','Input'),
  1036. q = A.querySelectorAll('input'),
  1037. id = class_ + "|" + ((q && q.length) || 0);
  1038. input = insertElement(null, 'input', id, class_);
  1039. input.name=D[0];
  1040. input.type=C.replace('input','');
  1041. // IE 8 support - delay insertion until now
  1042. A.appendChild(input);
  1043. if (C == "textinput" && D[1]) {
  1044. match = new RegExp(Wikifier.linkFormatter.lookahead).exec(parser.fullMatch());
  1045. if (match) {
  1046. Wikifier.linkFormatter.makeLink(A,match, macros.button.callback, 'button');
  1047. }
  1048. else {
  1049. Wikifier.linkFormatter.makeLink(A,[0,(D[2] || D[1]),D[1]], macros.button.callback, 'button');
  1050. }
  1051. }
  1052. else if ((C == "radio" || C == "checkbox") && D[1]) {
  1053. input.value = D[1];
  1054. insertElement(A, 'label','', '', D[1]).setAttribute('for',id);
  1055. if (D[2]) {
  1056. insertElement(A,'br');
  1057. D.splice(1,1);
  1058. macros[C].handler(A,C,D)
  1059. }
  1060. }
  1061. }
  1062. };
  1063. version.extensions.buttonMacro = {
  1064. major: 1,
  1065. minor: 0,
  1066. revision: 0
  1067. };
  1068. macros.button = {
  1069. callback: function() {
  1070. var el = findPassageParent(this);
  1071. if (el) {
  1072. var inputs = el.querySelectorAll("input");
  1073. for (i = 0; i < inputs.length; i++) {
  1074. if (inputs[i].type!="checkbox" && (inputs[i].type!="radio" || inputs[i].checked)) {
  1075. macros.set.run(null, Wikifier.parse(inputs[i].name+' = "'+inputs[i].value.replace(/"/g,'\\"')+'"'));
  1076. }
  1077. else if (inputs[i].type=="checkbox" && inputs[i].checked) {
  1078. macros.set.run(null, Wikifier.parse(
  1079. inputs[i].name+' = [].concat('+inputs[i].name+' || []);'));
  1080. macros.set.run(null, Wikifier.parse(
  1081. inputs[i].name+'.push("'+inputs[i].value.replace(/"/g,'\\"')+'")'));
  1082. }
  1083. }
  1084. }
  1085. },
  1086. handler: function (A, C, D, parser) {
  1087. var link,
  1088. match = new RegExp(Wikifier.linkFormatter.lookahead).exec(parser.fullMatch());
  1089. if (match) {
  1090. Wikifier.linkFormatter.makeLink(A, match, this.callback, 'button');
  1091. }
  1092. else {
  1093. Wikifier.linkFormatter.makeLink(A,[0,D[1] || D[0], D[0]], this.callback, 'button');
  1094. }
  1095. }
  1096. };
  1097. /*
  1098. **
  1099. ** Passage object
  1100. **
  1101. */
  1102. function Passage(c, b, a, ofunc) {
  1103. var t;
  1104. if (!this || this.constructor != Passage) {
  1105. throw new ReferenceError("passage() must be in lowercase");
  1106. }
  1107. this.title = c;
  1108. ofunc = typeof ofunc == 'function' && ofunc;
  1109. if (b) {
  1110. this.id = a;
  1111. // Load tags
  1112. this.tags = b.getAttribute("tags");
  1113. if (typeof this.tags == "string") {
  1114. if (ofunc) {
  1115. this.tags = ofunc(this.tags);
  1116. }
  1117. this.tags = this.tags.readBracketedList();
  1118. } else this.tags = [];
  1119. // Load text
  1120. t = b.firstChild ? b.firstChild.nodeValue : "";
  1121. if (ofunc && !this.isImage()) {
  1122. this.text = ofunc(Passage.unescapeLineBreaks(t));
  1123. } else {
  1124. this.text = Passage.unescapeLineBreaks(t);
  1125. }
  1126. // Preload linked images
  1127. if (!this.isImage()) {
  1128. this.preloadImages();
  1129. }
  1130. // Check for the .char selector, or the [data-char] selector
  1131. // False positives aren't a big issue.
  1132. if (/\.char\b|\[data\-char\b/.exec(this.text) && Wikifier.charSpanFormatter) {
  1133. Wikifier.formatters.push(Wikifier.charSpanFormatter);
  1134. delete Wikifier.charSpanFormatter;
  1135. }
  1136. } else {
  1137. this.text = '@@This passage does not exist: ' + c + '@@';
  1138. this.tags = [];
  1139. }
  1140. }
  1141. Passage.prototype.isImage = function() {
  1142. return !!~(this.tags.indexOf("Twine.image"));
  1143. };
  1144. Passage.prototype.preloadImages = function() {
  1145. // Don't preload URLs containing '$' - suspect that they are variables.
  1146. var u = "\\s*['\"]?([^\"'$]+\\.(jpe?g|a?png|gif|bmp|webp|svg))['\"]?\\s*",
  1147. k = function(c, e) {
  1148. var i,d;
  1149. do {
  1150. d = c.exec(this.text);
  1151. if(d) {
  1152. i = new Image();
  1153. i.src = d[e];
  1154. }
  1155. } while (d);
  1156. return k;
  1157. };
  1158. k.call(this, new RegExp(Wikifier.imageFormatter.lookahead.replace("[^\\[\\]\\|]+",u), "mg"), 4)
  1159. .call(this, new RegExp("url\\s*\\(" + u + "\\)", "mig"), 1)
  1160. .call(this, new RegExp("src\\s*=" + u, "mig"), 1);
  1161. };
  1162. Passage.unescapeLineBreaks = function (a) {
  1163. if (a && typeof a == "string") {
  1164. return a.replace(/\\n/mg, "\n").replace(/\\t/mg, "\t").replace(/\\s/mg, "\\").replace(/\\/mg, "\\").replace(/\r/mg, "")
  1165. } else {
  1166. return ""
  1167. }
  1168. };
  1169. Passage.prototype.setTags = function(b) {
  1170. var t = this.tags != null && this.tags.length ? this.tags.join(' ') : "";
  1171. if (t) {
  1172. b.setAttribute('data-tags', this.tags.join(' '));
  1173. }
  1174. document.body.setAttribute("data-tags", t);
  1175. };
  1176. Passage.prototype.processText = function() {
  1177. var ret = this.text;
  1178. if (~this.tags.indexOf("nobr")) {
  1179. ret = ret.replace(/\n/g,"\u200c");
  1180. }
  1181. if (this.isImage()) {
  1182. ret = "[img[" + ret + "]]"
  1183. }
  1184. return ret;
  1185. };
  1186. /*
  1187. **
  1188. ** Tale object
  1189. **
  1190. */
  1191. function Tale() {
  1192. var a,b,c,lines,i,kv,nsc,isImage,
  1193. settings = this.storysettings = {
  1194. lookup: function(a, dfault) {
  1195. // The two runtime settings (undo and bookmark) default to true.
  1196. if (!(a in this)) return dfault;
  1197. return (this[a]+"") != "off";
  1198. }
  1199. },
  1200. HTMLtitle = document.querySelector('title');
  1201. tiddlerTitle = '';
  1202. this.defaultTitle = (HTMLtitle && (HTMLtitle.textContent || HTMLtitle.innerText)) || "Untitled Story";
  1203. this.passages = {};
  1204. //Look for and load the StorySettings
  1205. if (document.normalize) document.normalize();
  1206. a = document.getElementById("storeArea").children;
  1207. for (b = 0; b < a.length; b++) {
  1208. c = a[b];
  1209. if (c.getAttribute && c.getAttribute("tiddler") == 'StorySettings') {
  1210. lines = new Passage('StorySettings', c, 0, null, null).text.split('\n');
  1211. for (i in lines) {
  1212. if (typeof lines[i] == "string" && lines[i].indexOf(':') > -1) {
  1213. kv = lines[i].toLowerCase().split(':');
  1214. kv[0] = kv[0].replace(/^\s+|\s+$/g, '');
  1215. kv[1] = kv[1].replace(/^\s+|\s+$/g, '');
  1216. if (kv[0] != "lookup") {
  1217. settings[kv[0]] = kv[1];
  1218. }
  1219. }
  1220. }
  1221. }
  1222. }
  1223. //Load in the passages
  1224. if (settings.obfuscate == 'rot13') {
  1225. for (b = 0; b < a.length; b++) {
  1226. c = a[b];
  1227. if (c.getAttribute && (tiddlerTitle = c.getAttribute("tiddler"))) {
  1228. isImage = (c.getAttribute("tags")+"").indexOf("Twine.image")>-1;
  1229. if (tiddlerTitle != 'StorySettings' && !isImage)
  1230. tiddlerTitle = rot13(tiddlerTitle);
  1231. this.passages[tiddlerTitle] = new Passage(tiddlerTitle, c, b+1, !isImage && rot13);
  1232. }
  1233. }
  1234. } else {
  1235. for (b = 0; b < a.length; b++) {
  1236. c = a[b];
  1237. if (c.getAttribute && (tiddlerTitle = c.getAttribute("tiddler"))) {
  1238. this.passages[tiddlerTitle] = new Passage(tiddlerTitle, c, b, null, null)
  1239. }
  1240. }
  1241. }
  1242. }
  1243. Tale.prototype.has = function (a) {
  1244. if (typeof a == "string") {
  1245. return (this.passages[a] != null)
  1246. } else {
  1247. for (var i in this.passages) {
  1248. if (this.passages[i].id == a) {
  1249. return true
  1250. }
  1251. }
  1252. return false
  1253. }
  1254. };
  1255. Tale.prototype.get = function (a) {
  1256. if (typeof a == "string") {
  1257. return this.passages[a] || new Passage(a)
  1258. } else {
  1259. for (var i in this.passages) {
  1260. if (this.passages[i].id == a) {
  1261. return this.passages[i]
  1262. }
  1263. }
  1264. }
  1265. };
  1266. Tale.prototype.lookup = function (h, g, a) {
  1267. var d = [];
  1268. for (var c in this.passages) {
  1269. var f = this.passages[c];
  1270. for (var b = 0; b < f[h].length; b++) {
  1271. if (f[h][b] == g) {
  1272. d.push(f)
  1273. }
  1274. }
  1275. }
  1276. if (!a) {
  1277. a = "title"
  1278. }
  1279. d.sort(function (k, j) {
  1280. if (k[a] == j[a]) {
  1281. return (0)
  1282. } else {
  1283. return (k[a] < j[a]) ? -1 : +1
  1284. }
  1285. });
  1286. return d
  1287. };
  1288. Tale.prototype.canUndo = function() {
  1289. return this.storysettings.lookup('undo',true);
  1290. };
  1291. Tale.prototype.identity = function () {
  1292. var meta = document.querySelector("meta[name='identity']"),
  1293. identity = meta ? meta.getAttribute("content") : "story";
  1294. return (Tale.prototype.identity = function() {
  1295. return identity;
  1296. })();
  1297. };
  1298. Tale.prototype.forEachStylesheet = function(tags, callback) {
  1299. var passage, i;
  1300. tags = tags || [];
  1301. if (typeof callback != "function")
  1302. return;
  1303. for (passage in this.passages) {
  1304. passage = tale.passages[passage];
  1305. if (passage && ~passage.tags.indexOf("stylesheet")) {
  1306. for (i = 0; i < tags.length; i++) {
  1307. if (~passage.tags.indexOf(tags[i])) {
  1308. callback(passage);
  1309. break;
  1310. }
  1311. }
  1312. }
  1313. }
  1314. };
  1315. Tale.prototype.setPageElements = function() {
  1316. var storyTitle;
  1317. setPageElement("storyTitle", "StoryTitle", this.defaultTitle);
  1318. storyTitle = document.getElementById("storyTitle");
  1319. document.title = this.title = (storyTitle && (storyTitle.textContent || storyTitle.innerText)) || this.defaultTitle;
  1320. setPageElement("storySubtitle", "StorySubtitle", "");
  1321. if (tale.has("StoryAuthor")) {
  1322. setPageElement("titleSeparator", null, "\n");
  1323. setPageElement("storyAuthor", "StoryAuthor", "");
  1324. }
  1325. if (tale.has("StoryMenu")) {
  1326. document.getElementById("storyMenu").setAttribute("style","");
  1327. setPageElement("storyMenu", "StoryMenu", "");
  1328. }
  1329. };
  1330. /*
  1331. **
  1332. ** Wikifier object
  1333. **
  1334. */
  1335. function Wikifier(place, source) {
  1336. this.source = source;
  1337. this.output = place;
  1338. this.nextMatch = 0;
  1339. this.assembleFormatterMatches(Wikifier.formatters);
  1340. this.subWikify(this.output);
  1341. }
  1342. Wikifier.textPrimitives = {
  1343. upperLetter: "[A-Z\u00c0-\u00de\u0150\u0170]",
  1344. lowerLetter: "[a-z\u00df-\u00ff_0-9\\-\u0151\u0171]",
  1345. anyLetter: "[A-Za-z\u00c0-\u00de\u00df-\u00ff_0-9\\-\u0150\u0170\u0151\u0171]"
  1346. }
  1347. Wikifier.textPrimitives.variable = "\\$((?:"+Wikifier.textPrimitives.anyLetter.replace("\\-", "")+"*"+
  1348. Wikifier.textPrimitives.anyLetter.replace("0-9\\-", "")+"+"+
  1349. Wikifier.textPrimitives.anyLetter.replace("\\-", "")+"*)+)";
  1350. Wikifier.textPrimitives.unquoted = "(?=(?:[^\"'\\\\]*(?:\\\\.|'(?:[^'\\\\]*\\\\.)*[^'\\\\]*'|\"(?:[^\"\\\\]*\\\\.)*[^\"\\\\]*\"))*[^'\"]*$)";
  1351. Wikifier.prototype.assembleFormatterMatches = function (formatters) {
  1352. this.formatters = [];
  1353. var pattern = [];
  1354. for (var n = 0; n < formatters.length; n++) {
  1355. pattern.push("(" + formatters[n].match + ")");
  1356. this.formatters.push(formatters[n]);
  1357. };
  1358. this.formatterRegExp = new RegExp(pattern.join("|"), "mg");
  1359. };
  1360. Wikifier.prototype.subWikify = function (output, terminator) {
  1361. // Temporarily replace the output pointer
  1362. var terminatorMatch, formatterMatch, oldOutput = this.output;
  1363. this.output = output;
  1364. // Prepare the terminator RegExp
  1365. var terminatorRegExp = terminator ? new RegExp("(" + terminator + ")", "mg") : null;
  1366. do {
  1367. // Prepare the RegExp match positions
  1368. this.formatterRegExp.lastIndex = this.nextMatch;
  1369. if (terminatorRegExp) terminatorRegExp.lastIndex = this.nextMatch;
  1370. // Get the first matches
  1371. formatterMatch = this.formatterRegExp.exec(this.source);
  1372. terminatorMatch = terminatorRegExp ? terminatorRegExp.exec(this.source) : null;
  1373. // Check for a terminator match
  1374. if (terminatorMatch && (!formatterMatch || terminatorMatch.index <= formatterMatch.index)) {
  1375. // Output any text before the match
  1376. if (terminatorMatch.index > this.nextMatch) this.outputText(this.output, this.nextMatch, terminatorMatch.index);
  1377. // Set the match parameters
  1378. this.matchStart = terminatorMatch.index;
  1379. this.matchLength = terminatorMatch[1].length;
  1380. this.matchText = terminatorMatch[1];
  1381. this.nextMatch = terminatorMatch.index + terminatorMatch[1].length;
  1382. // Restore the output pointer and exit
  1383. this.output = oldOutput;
  1384. return;
  1385. }
  1386. // Check for a formatter match
  1387. else if (formatterMatch) {
  1388. // Output any text before the match
  1389. if (formatterMatch.index > this.nextMatch) this.outputText(this.output, this.nextMatch, formatterMatch.index);
  1390. // Set the match parameters
  1391. this.matchStart = formatterMatch.index;
  1392. this.matchLength = formatterMatch[0].length;
  1393. this.matchText = formatterMatch[0];
  1394. this.nextMatch = this.formatterRegExp.lastIndex;
  1395. // Figure out which formatter matched
  1396. var matchingFormatter = -1;
  1397. for (var t = 1; t < formatterMatch.length; t++) {
  1398. if (formatterMatch[t]) {
  1399. matchingFormatter = t - 1;
  1400. break;
  1401. }
  1402. }
  1403. // Call the formatter
  1404. if (matchingFormatter != -1) { this.formatters[matchingFormatter].handler(this); }
  1405. }
  1406. }
  1407. while (terminatorMatch || formatterMatch);
  1408. // Output any text after the last match
  1409. if (this.nextMatch < this.source.length) {
  1410. this.outputText(this.output, this.nextMatch, this.source.length);
  1411. this.nextMatch = this.source.length;
  1412. }
  1413. // Restore the output pointer
  1414. this.output = oldOutput;
  1415. };
  1416. Wikifier.prototype.outputText = function (place, startPos, endPos) {
  1417. if (place) {
  1418. insertText(place, this.source.substring(startPos, endPos));
  1419. }
  1420. };
  1421. Wikifier.prototype.fullMatch = function() {
  1422. return this.source.slice(this.matchStart, this.source.indexOf('>>', this.matchStart)+2);
  1423. };
  1424. Wikifier.prototype.fullArgs = function (includeName) {
  1425. var source = this.source.replace(/\u200c/g," "),
  1426. endPos = this.nextMatch-2,
  1427. startPos = source.indexOf(includeName ? '<<' : ' ', this.matchStart);
  1428. if (!~startPos || !~endPos || endPos <= startPos) {
  1429. return "";
  1430. }
  1431. return Wikifier.parse(source.slice(startPos + (includeName ? 2 : 1), endPos).trim());
  1432. };
  1433. Wikifier.parse = function (input) {
  1434. var m, re, b = input, found = [],
  1435. g = Wikifier.textPrimitives.unquoted;
  1436. function alter(from,to) {
  1437. b = b.replace(new RegExp(from+g,"gim"),to);
  1438. return alter;
  1439. }
  1440. // Extract all the variables, and set them to 0 if undefined.
  1441. re = new RegExp(Wikifier.textPrimitives.variable+g,"gi");
  1442. while (m = re.exec(input)) {
  1443. if (!~found.indexOf(m[0])) {
  1444. // This deliberately contains a 'null or undefined' check
  1445. b = m[0]+" == null && ("+m[0]+" = 0);"+b;
  1446. found.push(m[0]);
  1447. }
  1448. }
  1449. alter(Wikifier.textPrimitives.variable, "state.history[0].variables.$1")
  1450. // Old operators
  1451. ("\\beq\\b", " == ")
  1452. ("\\bneq\\b", " != ")
  1453. ("\\bgt\\b", " > ")
  1454. ("\\bgte\\b", " >= ")
  1455. ("\\blt\\b", " < ")
  1456. ("\\blte\\b", " <= ")
  1457. ("\\band\\b", " && ")
  1458. ("\\bor\\b", " || ")
  1459. ("\\bnot\\b", " ! ")
  1460. // New operators
  1461. ("\\bis\\b", " == ")
  1462. ("\\bto\\b", " = ");
  1463. return b
  1464. };
  1465. Wikifier.formatHelpers = {
  1466. charFormatHelper: function (a) {
  1467. var b = insertElement(a.output, this.element);
  1468. a.subWikify(b, this.terminator)
  1469. },
  1470. inlineCssHelper: function (w) {
  1471. var s, v, lookaheadMatch, gotMatch,
  1472. styles = [],
  1473. lookahead = Wikifier.styleByCharFormatter.lookahead,
  1474. lookaheadRegExp = new RegExp(lookahead, "mg"),
  1475. hadStyle = false,
  1476. unDash = function (str) {
  1477. var s = str.split("-");
  1478. if (s.length > 1) for (var t = 1; t < s.length; t++)
  1479. s[t] = s[t].substr(0, 1).toUpperCase() + s[t].substr(1);
  1480. return s.join("");
  1481. };
  1482. styles.className = "";
  1483. do {
  1484. lookaheadRegExp.lastIndex = w.nextMatch;
  1485. lookaheadMatch = lookaheadRegExp.exec(w.source);
  1486. gotMatch = lookaheadMatch && lookaheadMatch.index == w.nextMatch;
  1487. if (gotMatch) {
  1488. hadStyle = true;
  1489. if (lookaheadMatch[5]) {
  1490. styles.className += lookaheadMatch[5].replace(/\./g," ") + " ";
  1491. } else if (lookaheadMatch[1]) {
  1492. s = unDash(lookaheadMatch[1]);
  1493. v = lookaheadMatch[2];
  1494. } else {
  1495. s = unDash(lookaheadMatch[3]);
  1496. v = lookaheadMatch[4];
  1497. }
  1498. switch (s) {
  1499. case "bgcolor":
  1500. s = "backgroundColor";
  1501. break;
  1502. case "float":
  1503. s = "cssFloat";
  1504. break
  1505. }
  1506. styles.push({
  1507. style: s,
  1508. value: v
  1509. });
  1510. w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
  1511. }
  1512. } while (gotMatch);
  1513. return styles;
  1514. },
  1515. monospacedByLineHelper: function (w) {
  1516. var lookaheadRegExp = new RegExp(this.lookahead, "mg");
  1517. lookaheadRegExp.lastIndex = w.matchStart;
  1518. var lookaheadMatch = lookaheadRegExp.exec(w.source);
  1519. if (lookaheadMatch && lookaheadMatch.index == w.matchStart) {
  1520. insertElement(w.output, "pre", null, null, lookaheadMatch[1]);
  1521. w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
  1522. }
  1523. }
  1524. };
  1525. Wikifier.formatters = [
  1526. {
  1527. name: "table",
  1528. match: "^\\|(?:[^\\n]*)\\|(?:[fhc]?)$",
  1529. lookahead: "^\\|([^\\n]*)\\|([fhc]?)$",
  1530. rowTerminator: "\\|(?:[fhc]?)$\\n?",
  1531. cellPattern: "(?:\\|([^\\n\\|]*)\\|)|(\\|[fhc]?$\\n?)",
  1532. cellTerminator: "(?:\\x20*)\\|",
  1533. rowTypes: {
  1534. "c": "caption",
  1535. "h": "thead",
  1536. "": "tbody",
  1537. "f": "tfoot"
  1538. },
  1539. handler: function (w) {
  1540. var rowContainer, rowElement,lookaheadMatch, matched,
  1541. table = insertElement(w.output, "table"),
  1542. lookaheadRegExp = new RegExp(this.lookahead, "mg"),
  1543. currRowType = null,
  1544. nextRowType,
  1545. prevColumns = [],
  1546. rowCount = 0;
  1547. w.nextMatch = w.matchStart;
  1548. do {
  1549. lookaheadRegExp.lastIndex = w.nextMatch;
  1550. lookaheadMatch = lookaheadRegExp.exec(w.source),
  1551. matched = lookaheadMatch && lookaheadMatch.index == w.nextMatch;
  1552. if (matched) {
  1553. nextRowType = lookaheadMatch[2];
  1554. if (nextRowType != currRowType) rowContainer = insertElement(table, this.rowTypes[nextRowType]);
  1555. currRowType = nextRowType;
  1556. if (currRowType == "c") {
  1557. if (rowCount == 0) rowContainer.setAttribute("align", "top");
  1558. else rowContainer.setAttribute("align", "bottom");
  1559. w.nextMatch = w.nextMatch + 1;
  1560. w.subWikify(rowContainer, this.rowTerminator);
  1561. } else {
  1562. rowElement = insertElement(rowContainer, "tr");
  1563. this.rowHandler(w, rowElement, prevColumns);
  1564. }
  1565. rowCount++;
  1566. }
  1567. } while (matched);
  1568. },
  1569. rowHandler: function (w, e, prevColumns) {
  1570. var cellMatch, matched, col = 0,
  1571. currColCount = 1,
  1572. cellRegExp = new RegExp(this.cellPattern, "mg");
  1573. do {
  1574. cellRegExp.lastIndex = w.nextMatch;
  1575. cellMatch = cellRegExp.exec(w.source);
  1576. matched = cellMatch && cellMatch.index == w.nextMatch;
  1577. if (matched) {
  1578. if (cellMatch[1] == "~") {
  1579. var last = prevColumns[col];
  1580. if (last) {
  1581. last.rowCount++;
  1582. last.element.setAttribute("rowSpan", last.rowCount);
  1583. last.element.setAttribute("rowspan", last.rowCount);
  1584. last.element.valign = "center";
  1585. }
  1586. w.nextMatch = cellMatch.index + cellMatch[0].length - 1;
  1587. } else if (cellMatch[1] == ">") {
  1588. currColCount++;
  1589. w.nextMatch = cellMatch.index + cellMatch[0].length - 1;
  1590. } else if (cellMatch[2]) {
  1591. w.nextMatch = cellMatch.index + cellMatch[0].length;
  1592. break;
  1593. } else {
  1594. var spaceLeft = false,
  1595. spaceRight = false,
  1596. lastColCount, lastColElement, styles, cell, t;
  1597. w.nextMatch++;
  1598. styles = Wikifier.formatHelpers.inlineCssHelper(w);
  1599. while (w.source.substr(w.nextMatch, 1) == " ") {
  1600. spaceLeft = true;
  1601. w.nextMatch++;
  1602. }
  1603. if (w.source.substr(w.nextMatch, 1) == "!") {
  1604. cell = insertElement(e, "th");
  1605. w.nextMatch++;
  1606. } else cell = insertElement(e, "td");
  1607. prevColumns[col] = {
  1608. rowCount: 1,
  1609. element: cell
  1610. };
  1611. lastColCount = 1;
  1612. lastColElement = cell;
  1613. if (currColCount > 1) {
  1614. cell.setAttribute("colSpan", currColCount);
  1615. cell.setAttribute("colspan", currColCount);
  1616. currColCount = 1;
  1617. }
  1618. for (t = 0; t < styles.length; t++)
  1619. cell.style[styles[t].style] = styles[t].value;
  1620. w.subWikify(cell, this.cellTerminator);
  1621. if (w.matchText.substr(w.matchText.length - 2, 1) == " ") spaceRight = true;
  1622. if (spaceLeft && spaceRight) cell.align = "center";
  1623. else if (spaceLeft) cell.align = "right";
  1624. else if (spaceRight) cell.align = "left";
  1625. w.nextMatch = w.nextMatch - 1;
  1626. }
  1627. col++;
  1628. }
  1629. } while (matched);
  1630. }
  1631. },
  1632. {
  1633. name: "rule",
  1634. match: "^----$\\n?",
  1635. handler: function (w) {
  1636. insertElement(w.output, "hr");
  1637. }
  1638. },
  1639. {
  1640. name: "emdash",
  1641. match: "--",
  1642. becomes: String.fromCharCode(8212),
  1643. handler: function (a) {
  1644. insertElement(a.output, "span", null, "char", this.becomes).setAttribute("data-char","emdash");
  1645. }
  1646. },
  1647. {
  1648. name: "heading",
  1649. match: "^!{1,5}",
  1650. terminator: "\\n",
  1651. handler: function (w) {
  1652. var e = insertElement(w.output, "h" + w.matchLength);
  1653. w.subWikify(e, this.terminator);
  1654. }
  1655. },
  1656. {
  1657. name: "monospacedByLine",
  1658. match: "^\\{\\{\\{\\n",
  1659. lookahead: "^\\{\\{\\{\\n((?:^[^\\n]*\\n)+?)(^\\}\\}\\}$\\n?)",
  1660. handler: Wikifier.formatHelpers.monospacedByLineHelper
  1661. },
  1662. {
  1663. name: "quoteByBlock",
  1664. match: "^<<<\\n",
  1665. terminator: "^<<<\\n",
  1666. handler: function (w) {
  1667. var e = insertElement(w.output, "blockquote");
  1668. w.subWikify(e, this.terminator);
  1669. }
  1670. },
  1671. {
  1672. name: "list",
  1673. match: "^(?:(?:\\*+)|(?:#+))",
  1674. lookahead: "^(?:(\\*+)|(#+))",
  1675. terminator: "\\n",
  1676. handler: function (w) {
  1677. var newType, newLevel, t, len, bulletType, lookaheadMatch, matched,
  1678. lookaheadRegExp = new RegExp(this.lookahead, "mg"),
  1679. placeStack = [w.output],
  1680. currType = null,
  1681. currLevel = 0;
  1682. w.nextMatch = w.matchStart;
  1683. do {
  1684. lookaheadRegExp.lastIndex = w.nextMatch;
  1685. lookaheadMatch = lookaheadRegExp.exec(w.source);
  1686. matched = lookaheadMatch && lookaheadMatch.index == w.nextMatch;
  1687. if (matched) {
  1688. newLevel = lookaheadMatch[0].length;
  1689. if (lookaheadMatch[1]) {
  1690. bulletType = lookaheadMatch[1].slice(-1);
  1691. newType = "ul";
  1692. }
  1693. else if (lookaheadMatch[2]) {
  1694. newType = "ol";
  1695. }
  1696. w.nextMatch += newLevel;
  1697. if (newLevel > currLevel) {
  1698. for (t = currLevel; t < newLevel; t++) {
  1699. placeStack.push(insertElement(placeStack[placeStack.length - 1], newType));
  1700. }
  1701. } else if (newLevel < currLevel) {
  1702. for (t = currLevel; t > newLevel; t--)
  1703. placeStack.pop();
  1704. } else if (newLevel == currLevel && newType != currType) {
  1705. placeStack.pop();
  1706. placeStack.push(insertElement(placeStack[placeStack.length - 1], newType));
  1707. }
  1708. currLevel = newLevel;
  1709. currType = newType;
  1710. t = insertElement(placeStack[placeStack.length - 1], "li");
  1711. // Currently unused
  1712. if (bulletType && bulletType != "*") {
  1713. t.setAttribute("data-bullet", bulletType);
  1714. }
  1715. w.subWikify(t, this.terminator);
  1716. }
  1717. } while (matched);
  1718. }
  1719. },
  1720. (Wikifier.urlFormatter = {
  1721. name: "urlLink",
  1722. match: "(?:https?|mailto|javascript|ftp|data):[^\\s'\"]+(?:/|\\b)",
  1723. handler: function (w) {
  1724. var e = Wikifier.createExternalLink(w.output, w.matchText);
  1725. w.outputText(e, w.matchStart, w.nextMatch);
  1726. }
  1727. }),
  1728. (Wikifier.linkFormatter = {
  1729. name: "prettyLink",
  1730. match: "\\[\\[",
  1731. lookahead: "\\[\\[([^\\|]*?)(?:\\|(.*?))?\\](?:\\[(.*?)\])?\\]",
  1732. makeInternalOrExternal: function(out,title,callback,type) {
  1733. if (title && !tale.has(title) && (title.match(Wikifier.urlFormatter.match,"g") || ~title.search(/[\.\\\/#]/)))
  1734. return Wikifier.createExternalLink(out, title, callback, type);
  1735. else
  1736. return Wikifier.createInternalLink(out, title, callback, type);
  1737. },
  1738. // This base callback executes the code in a setter
  1739. makeCallback: function(code,callback) {
  1740. return function() {
  1741. macros.set.run(null, Wikifier.parse(code), null, code);
  1742. typeof callback == "function" && callback.call(this);
  1743. }
  1744. },
  1745. makeLink: function(out, match, callback2, type) {
  1746. var link, title, callback;
  1747. if (match[3]) { // Code
  1748. callback = this.makeCallback(match[3],callback2);
  1749. }
  1750. else {
  1751. typeof callback2 == "function" && (callback = callback2);
  1752. }
  1753. title = Wikifier.parsePassageTitle(match[2] || match[1]);
  1754. link = this.makeInternalOrExternal(out,title,callback, type);
  1755. setPageElement(link, null, match[2] ? match[1] : title);
  1756. return link;
  1757. },
  1758. handler: function (w) {
  1759. var lookaheadRegExp = new RegExp(this.lookahead, "mg");
  1760. lookaheadRegExp.lastIndex = w.matchStart;
  1761. var lookaheadMatch = lookaheadRegExp.exec(w.source)
  1762. if (lookaheadMatch && lookaheadMatch.index == w.matchStart) {
  1763. this.makeLink(w.output, lookaheadMatch)
  1764. w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
  1765. }
  1766. }
  1767. }),
  1768. (Wikifier.imageFormatter = {
  1769. name: "image",
  1770. match: "\\[(?:[<]{0,1})(?:[>]{0,1})[Ii][Mm][Gg]\\[",
  1771. lookahead: "\\[([<]?)(>?)img\\[(?:([^\\|\\]]+)\\|)?([^\\[\\]\\|]+)\\](?:\\[([^\\]]*)\\]?)?(\\])",
  1772. importedImage: function(img, passageName) {
  1773. var imgPassages, imgname;
  1774. // Try to parse it as a variable
  1775. try {
  1776. imgname = internalEval(Wikifier.parse(passageName));
  1777. }
  1778. catch(e) {
  1779. }
  1780. if (!imgname) {
  1781. imgname = passageName;
  1782. }
  1783. // Base64 passage transclusion
  1784. imgPassages = tale.lookup("tags", "Twine.image");
  1785. for (j = 0; j < imgPassages.length; j++) {
  1786. if (imgPassages[j].title == imgname) {
  1787. img.src = imgPassages[j].text;
  1788. break;
  1789. }
  1790. }
  1791. },
  1792. handler: function (w) {
  1793. var e, img, j, lookaheadMatch,
  1794. lookaheadRegExp = new RegExp(this.lookahead, "mig");
  1795. lookaheadRegExp.lastIndex = w.matchStart;
  1796. lookaheadMatch = lookaheadRegExp.exec(w.source);
  1797. if (lookaheadMatch && lookaheadMatch.index == w.matchStart)
  1798. {
  1799. e = w.output, title = Wikifier.parsePassageTitle(lookaheadMatch[5])
  1800. if (title) {
  1801. e = Wikifier.linkFormatter.makeInternalOrExternal(w.output, title);
  1802. }
  1803. img = insertElement(e, "img");
  1804. if (lookaheadMatch[1]) img.align = "left";
  1805. else if (lookaheadMatch[2]) img.align = "right";
  1806. if (lookaheadMatch[3]) img.title = lookaheadMatch[3];
  1807. // Setup the image if it's referencing an image passage.
  1808. this.importedImage(img,lookaheadMatch[4]);
  1809. w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
  1810. }
  1811. }
  1812. }),
  1813. {
  1814. name: "macro",
  1815. match: "<<",
  1816. lookahead: /<<([^>\s]+)(?:\s*)((?:\\.|'(?:[^'\\]*\\.)*[^'\\]*'|"(?:[^"\\]*\\.)*[^"\\]*"|[^'"\\>]|>(?!>))*)>>/mg,
  1817. handler: function (w) {
  1818. var lookaheadRegExp = new RegExp(this.lookahead);
  1819. lookaheadRegExp.lastIndex = w.matchStart;
  1820. var lookaheadMatch = lookaheadRegExp.exec(w.source.replace(/\u200c/g,'\n'));
  1821. if (lookaheadMatch && lookaheadMatch.index == w.matchStart && lookaheadMatch[1]) {
  1822. var params = lookaheadMatch[2].readMacroParams();
  1823. w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
  1824. var name = lookaheadMatch[1];
  1825. try {
  1826. var macro = macros[name];
  1827. if (macro && typeof macro == "object" && macro.handler) {
  1828. macro.handler(w.output, name, params, w);
  1829. }
  1830. // Variable?
  1831. else if (name[0] == '$') {
  1832. macros.print.handler(w.output, name, [name].concat(params), w);
  1833. }
  1834. // Passage
  1835. else if (tale.has(name)) {
  1836. macros.display.handler(w.output, name, [name].concat(params), w);
  1837. }
  1838. else throwError(w.output, 'No macro or passage called "' + name + '"', w.fullMatch());
  1839. } catch (e) {
  1840. throwError(w.output, 'Error executing macro ' + name + ': ' + e.toString(), w.fullMatch());
  1841. }
  1842. }
  1843. }
  1844. },
  1845. {
  1846. name: "html",
  1847. match: "<html>",
  1848. lookahead: "<html>((?:.|\\n)*?)</html>",
  1849. handler: function (w) {
  1850. var lookaheadRegExp = new RegExp(this.lookahead, "mg");
  1851. lookaheadRegExp.lastIndex = w.matchStart;
  1852. var lookaheadMatch = lookaheadRegExp.exec(w.source)
  1853. if (lookaheadMatch && lookaheadMatch.index == w.matchStart) {
  1854. var e = insertElement(w.output, "span");
  1855. e.innerHTML = lookaheadMatch[1];
  1856. w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
  1857. }
  1858. }
  1859. },
  1860. {
  1861. name: "commentByBlock",
  1862. match: "/%",
  1863. lookahead: "/%((?:.|\\n)*?)%/",
  1864. handler: function (w) {
  1865. var lookaheadRegExp = new RegExp(this.lookahead, "mg");
  1866. lookaheadRegExp.lastIndex = w.matchStart;
  1867. var lookaheadMatch = lookaheadRegExp.exec(w.source)
  1868. if (lookaheadMatch && lookaheadMatch.index == w.matchStart) w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
  1869. }
  1870. },
  1871. {
  1872. name: "boldByChar",
  1873. match: "''",
  1874. terminator: "''",
  1875. element: "strong",
  1876. handler: Wikifier.formatHelpers.charFormatHelper
  1877. },
  1878. {
  1879. name: "strikeByChar",
  1880. match: "==",
  1881. terminator: "==",
  1882. element: "strike",
  1883. handler: Wikifier.formatHelpers.charFormatHelper
  1884. },
  1885. {
  1886. name: "underlineByChar",
  1887. match: "__",
  1888. terminator: "__",
  1889. element: "u",
  1890. handler: Wikifier.formatHelpers.charFormatHelper
  1891. },
  1892. {
  1893. name: "italicByChar",
  1894. match: "//",
  1895. terminator: "//",
  1896. element: "em",
  1897. handler: Wikifier.formatHelpers.charFormatHelper
  1898. },
  1899. {
  1900. name: "subscriptByChar",
  1901. match: "~~",
  1902. terminator: "~~",
  1903. element: "sub",
  1904. handler: Wikifier.formatHelpers.charFormatHelper
  1905. },
  1906. {
  1907. name: "superscriptByChar",
  1908. match: "\\^\\^",
  1909. terminator: "\\^\\^",
  1910. element: "sup",
  1911. handler: Wikifier.formatHelpers.charFormatHelper
  1912. },
  1913. {
  1914. name: "monospacedByChar",
  1915. match: "\\{\\{\\{",
  1916. lookahead: "\\{\\{\\{((?:.|\\n)*?)\\}\\}\\}",
  1917. handler: function (w) {
  1918. var lookaheadRegExp = new RegExp(this.lookahead, "mg");
  1919. lookaheadRegExp.lastIndex = w.matchStart;
  1920. var lookaheadMatch = lookaheadRegExp.exec(w.source)
  1921. if (lookaheadMatch && lookaheadMatch.index == w.matchStart) {
  1922. var e = insertElement(w.output, "code", null, null, lookaheadMatch[1]);
  1923. w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
  1924. }
  1925. }
  1926. },
  1927. (Wikifier.styleByCharFormatter = {
  1928. name: "styleByChar",
  1929. match: "@@",
  1930. terminator: "@@",
  1931. lookahead: "(?:([^\\(@]+)\\(([^\\)\\|\\n]+)(?:\\):))|(?:([^\\.:@]+):([^;\\|\\n]+);)|(?:\\.([^;\\|\\n]+);)",
  1932. handler: function (w) {
  1933. var e = insertElement(w.output, "span", null, null, null);
  1934. var styles = Wikifier.formatHelpers.inlineCssHelper(w);
  1935. if (styles.length == 0) {
  1936. e.className = "marked";
  1937. }
  1938. else {
  1939. for (var t = 0; t < styles.length; t++) {
  1940. e.style[styles[t].style] = styles[t].value;
  1941. }
  1942. if (typeof styles.className == "string") {
  1943. e.className = styles.className;
  1944. }
  1945. }
  1946. w.subWikify(e, this.terminator);
  1947. }
  1948. }),
  1949. {
  1950. name: "lineBreak",
  1951. match: "\\n",
  1952. handler: function (w) {
  1953. insertElement(w.output, "br");
  1954. }
  1955. },
  1956. {
  1957. name: "continuedLine",
  1958. match: "\\\\\\s*?\\n",
  1959. handler: function(a) {
  1960. a.nextMatch = a.matchStart+2;
  1961. }
  1962. },
  1963. {
  1964. name: "htmlCharacterReference",
  1965. match: "(?:(?:&#?[a-zA-Z0-9]{2,8};|.)(?:&#?(?:x0*(?:3[0-6][0-9a-fA-F]|1D[c-fC-F][0-9a-fA-F]|20[d-fD-F][0-9a-fA-F]|FE2[0-9a-fA-F])|0*(?:76[89]|7[7-9][0-9]|8[0-7][0-9]|761[6-9]|76[2-7][0-9]|84[0-3][0-9]|844[0-7]|6505[6-9]|6506[0-9]|6507[0-1]));)+|&#?[a-zA-Z0-9]{2,8};)",
  1966. handler: function(w)
  1967. {
  1968. var el = document.createElement("div");
  1969. el.innerHTML = w.matchText;
  1970. insertText(w.output, el.textContent);
  1971. }
  1972. },
  1973. {
  1974. name: "htmltag",
  1975. match: "<(?:\\/?[\\w\\-]+|[\\w\\-]+(?:(?:\\s+[\\w\\-]+(?:\\s*=\\s*(?:\\\".*?\\\"|'.*?'|[^'\\\">\\s]+))?)+\\s*|\\s*)\\/?)>",
  1976. tagname: "<(\\w+)",
  1977. voids: ["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr"],
  1978. tableElems: ["table","thead","tbody","tfoot","th","tr","td","colgroup","col","caption","figcaption"],
  1979. cleanupTables: function (e) {
  1980. var i, name, elems = [].slice.call(e.children);
  1981. // Remove non-table child elements that aren't children of <td>s
  1982. for (i = 0; i < elems.length; i++) {
  1983. if (elems[i].tagName) {
  1984. name = elems[i].tagName.toLowerCase();
  1985. if (this.tableElems.indexOf(name)==-1) {
  1986. elems[i].outerHTML = '';
  1987. }
  1988. else if (['col','caption','figcaption','td','th'].indexOf(name)==-1) {
  1989. this.cleanupTables.call(this,elems[i]);
  1990. }
  1991. }
  1992. }
  1993. },
  1994. handler: function (a) {
  1995. var tmp, passage, setter, e, isvoid, isstyle, lookaheadRegExp, lookaheadMatch, lookahead,
  1996. re = new RegExp(this.tagname).exec(a.matchText),
  1997. tn = re && re[1] && re[1].toLowerCase();
  1998. if(tn && tn != "html") {
  1999. lookahead = "<\\/\\s*" + tn + "\\s*>";
  2000. isvoid = (this.voids.indexOf(tn) != -1);
  2001. isstyle = tn == "style" || tn == "script";
  2002. lookaheadRegExp = new RegExp(lookahead, "mg");
  2003. lookaheadRegExp.lastIndex = a.matchStart;
  2004. lookaheadMatch = lookaheadRegExp.exec(a.source);
  2005. if (lookaheadMatch || isvoid) {
  2006. if (isstyle) {
  2007. e = document.createElement(tn);
  2008. e.type = "text/css"; // IE8 compatibility
  2009. tmp = a.source.slice(a.nextMatch, lookaheadMatch.index);
  2010. e.styleSheet ? (e.styleSheet.cssText = tmp) : (e.innerHTML = tmp);
  2011. a.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
  2012. }
  2013. else {
  2014. // Creating a loose <tr> element creates it wrapped inside a <tbody> element.
  2015. e = document.createElement(a.output.tagName);
  2016. e.innerHTML = a.matchText;
  2017. while(e.firstChild) {
  2018. e = e.firstChild;
  2019. }
  2020. if (!isvoid) {
  2021. a.subWikify(e, lookahead);
  2022. }
  2023. }
  2024. if (e.tagName.toLowerCase() == 'table') {
  2025. this.cleanupTables.call(this,e);
  2026. }
  2027. // Special data-setter attribute
  2028. if (setter = e.getAttribute("data-setter")) {
  2029. setter = Wikifier.linkFormatter.makeCallback(setter);
  2030. }
  2031. // Special data-passage attribute
  2032. if (passage = e.getAttribute("data-passage")) {
  2033. if (tn != "img") {
  2034. addClickHandler(e, Wikifier.linkFunction(Wikifier.parsePassageTitle(passage), e, setter));
  2035. if (tn == "area" || tn == "a") {
  2036. e.setAttribute("href", "javascript:;");
  2037. }
  2038. }
  2039. else {
  2040. Wikifier.imageFormatter.importedImage(e, passage);
  2041. }
  2042. }
  2043. a.output.appendChild(e);
  2044. } else {
  2045. throwError(a.output,"HTML tag '"+tn+"' wasn't closed.", a.matchText);
  2046. }
  2047. }
  2048. }
  2049. }];
  2050. // Optional - included if the ".char" selector appears anywhere in the story.
  2051. Wikifier.charSpanFormatter = {
  2052. name: "char",
  2053. match: "[^\n]",
  2054. handler: function (a) {
  2055. // Line breaks do NOT get their own charspans
  2056. insertElement(a.output, "span", null, "char", a.matchText).setAttribute("data-char", a.matchText == " " ? "space" :
  2057. a.matchText == "\t" ? "tab" : a.matchText);
  2058. }
  2059. };
  2060. Wikifier.parsePassageTitle = function(title) {
  2061. if (title && !tale.has(title)) {
  2062. try {
  2063. title = (internalEval(this.parse(title)) || title)+"";
  2064. }
  2065. catch(e) {}
  2066. }
  2067. return title;
  2068. };
  2069. Wikifier.linkFunction = function(title, el, callback) {
  2070. return function() {
  2071. if (state.rewindTo) {
  2072. var passage = findPassageParent(el);
  2073. if (passage && passage.parentNode.lastChild != passage) {
  2074. state.rewindTo(passage, true);
  2075. }
  2076. }
  2077. if (title) {
  2078. state.display(title, el, null, callback);
  2079. }
  2080. else if (typeof callback == "function") {
  2081. callback();
  2082. }
  2083. };
  2084. };
  2085. Wikifier.createInternalLink = function (place, title, callback, type) {
  2086. var tag = (type == "button" ? 'button' : 'a'),
  2087. suffix = (type == "button" ? "Button" : "Link"),
  2088. el = insertElement(place, tag);
  2089. if (tale.has(title)) {
  2090. el.className = 'internal'+suffix;
  2091. if (visited(title)) el.className += ' visited'+suffix;
  2092. }
  2093. else el.className = 'broken'+suffix;
  2094. addClickHandler(el, Wikifier.linkFunction(title, el, callback));
  2095. if (place) place.appendChild(el);
  2096. return el;
  2097. };
  2098. Wikifier.createExternalLink = function (place, url, callback, type) {
  2099. var tag = (type == "button" ? 'button' : 'a'),
  2100. el = insertElement(place, tag);
  2101. el.href = url;
  2102. el.className = "external"+(type == "button" ? "Button" : "Link");
  2103. el.target = "_blank";
  2104. if (typeof callback == "function") {
  2105. addClickHandler(el,callback);
  2106. }
  2107. if (place) place.appendChild(el);
  2108. return el;
  2109. };
  2110. // Functions usable solely by custom scripts
  2111. // This includes restart(), above.
  2112. function visited(e) {
  2113. var ret = 0, i = 0;
  2114. if (!state) {
  2115. return 0;
  2116. }
  2117. e = e || state.history[0].passage.title;
  2118. if (arguments.length > 1) {
  2119. for (ret = state.history.length; i<arguments.length; i++) {
  2120. ret = Math.min(ret, visited(arguments[i]));
  2121. }
  2122. }
  2123. else for(; i<state.history.length && state.history[i].passage; i++) {
  2124. if(e == state.history[i].passage.title) {
  2125. ret++;
  2126. }
  2127. }
  2128. return ret;
  2129. }
  2130. // Returns the number of times the player visited passages containing ALL
  2131. // provided tags.
  2132. // The tags can be several strings, or a single string with space-delimited tags.
  2133. function visitedTag() {
  2134. var i, j, sh, ret = 0, tags = Array.prototype.slice.call(arguments);
  2135. if (tags.length == 1 && typeof tags[0] == "string") {
  2136. tags = tags.split(" ");
  2137. }
  2138. if (!state) {
  2139. return 0;
  2140. }
  2141. sh = state.history;
  2142. for(i = 0; i<sh.length && sh[i].passage; i++) {
  2143. for (j = 0; j < tags.length || void ret++; j++) {
  2144. if(sh[i].passage.tags.indexOf(tags[j])==-1) {
  2145. break;
  2146. }
  2147. }
  2148. }
  2149. return ret;
  2150. }
  2151. var visitedTags = visitedTag;
  2152. function turns() {
  2153. return state.history.length-1;
  2154. }
  2155. function passage() {
  2156. return state.history[0].passage.title
  2157. }
  2158. function tags(e) {
  2159. var ret = [], i = 0;
  2160. if (!state) {
  2161. return 0;
  2162. }
  2163. e = e || state.history[0].passage.title;
  2164. if (arguments.length > 1) {
  2165. for (i = arguments.length-1; i >= 1; i--) {
  2166. ret = ret.concat(tags(arguments[i]));
  2167. }
  2168. }
  2169. ret = ret.concat(tale.get(e).tags);
  2170. return ret;
  2171. }
  2172. function previous() {
  2173. if (state && state.history[1]) {
  2174. for (var d = 1; d < state.history.length && state.history[d].passage; d++) {
  2175. if (state.history[d].passage.title != state.history[0].passage.title) {
  2176. return state.history[d].passage.title
  2177. }
  2178. }
  2179. }
  2180. return ""
  2181. }
  2182. // A random integer function
  2183. // 1 argument: random int from 0 to a inclusive
  2184. // 2 arguments: random int from a to b inclusive (order irrelevant)
  2185. function random(a, b) {
  2186. var from, to;
  2187. if (!b) {
  2188. from = 0;
  2189. to = a;
  2190. } else {
  2191. from = Math.min(a, b);
  2192. to = Math.max(a, b);
  2193. }
  2194. to += 1;
  2195. return ~~((Math.random() * (to - from))) + from;
  2196. }
  2197. function either() {
  2198. if (Array.isArray(arguments[0]) && arguments.length == 1) {
  2199. return either.apply(this,arguments[0]);
  2200. }
  2201. return arguments[~~(Math.random()*arguments.length)];
  2202. }
  2203. function parameter(n) {
  2204. n = n || 0;
  2205. if (macros.display.parameters[n]) {
  2206. return macros.display.parameters[n];
  2207. }
  2208. return 0
  2209. }
  2210. function bookmark() {
  2211. return state.hash || "#";
  2212. }
  2213. /* Used to eval within Twine, but outside the context of a particular function */
  2214. function internalEval(s) {
  2215. // eval("{x:1,y:1}") fails miserably unless the obj. literal
  2216. // is not the first expression in the line (hence, "0,").
  2217. return eval("0,"+s);
  2218. }
  2219. /* Used to execute script passages */
  2220. function scriptEval(s) {
  2221. try {
  2222. eval(s.text);
  2223. } catch (e) {
  2224. alert("There is a technical problem with this " + tale.identity() + " (" + s.title + ": " + e.message + ")."+softErrorMessage);
  2225. }
  2226. }
  2227. /* Unload prompt */
  2228. window.onbeforeunload = function() {
  2229. if (tale && tale.storysettings.lookup("exitprompt",false) && state && state.history.length > 1) {
  2230. return "You are about to end this " + tale.identity() + ".";
  2231. }
  2232. };
  2233. /* Error reporting */
  2234. var oldOnError = window.onerror || null,
  2235. softErrorMessage = "You may be able to continue playing, but some parts may not work properly.";
  2236. window.onerror = function (msg, a, b, c, error) {
  2237. var s = (error && (".\n\n" + error.stack.replace(/\([^\)]+\)/g,'') + "\n\n")) || (" (" + msg + ").\n");
  2238. alert("Sorry to interrupt, but this " + ((tale && tale.identity && tale.identity()) || "page") + "'s code has got itself in a mess" + s + softErrorMessage.slice(1));
  2239. // Restore previous onerror
  2240. window.onerror = oldOnError;
  2241. if (typeof window.onerror == "function") {
  2242. window.onerror(msg, a, b, c, error);
  2243. }
  2244. };
  2245. /* Init function */
  2246. var $;
  2247. function main() {
  2248. // Used by old custom scripts.
  2249. // Cedes to jQuery if it exists.
  2250. // Since jQuery is inserted after this script element,
  2251. // wait until main() is called before doing this.
  2252. $ = window.$ || function(a) {
  2253. return (typeof a == "string" ? document.getElementById(a) : a);
  2254. }
  2255. var imgs, scripts, macro, style, i,
  2256. styleText = "",
  2257. passages = document.getElementById("passages");
  2258. // Run checks after custom macros are installed
  2259. function sanityCheck(thing) {
  2260. var i, j, s = "NOTE: The " + thing,
  2261. checks = { prerender: prerender, postrender: postrender, macros: macros };
  2262. // Warn if prerender/postrender/macros aren't objects, and
  2263. // prerender/postrender's own properties aren't functions.
  2264. for (i in checks) {
  2265. if (Object.prototype.hasOwnProperty.call(checks, i) && !sanityCheck[i]) {
  2266. if (!checks[i] || typeof checks[i] != "object") {
  2267. alert(s + " seems to have corrupted the " + i + " object."+softErrorMessage);
  2268. sanityCheck[i] = true;
  2269. continue;
  2270. }
  2271. if (i != "macros") {
  2272. for (j in checks[i]) {
  2273. if (Object.prototype.hasOwnProperty.call(checks[i], j)
  2274. && typeof checks[i][j] != "function") {
  2275. alert(s + " added a property '" + j + "' to " + i + ", "
  2276. +"which is a "+typeof checks[i][j]+", not a function."+softErrorMessage);
  2277. sanityCheck[i] = true;
  2278. break;
  2279. }
  2280. }
  2281. }
  2282. }
  2283. }
  2284. }
  2285. // Check for basic compatibility
  2286. if (!window.JSON || !document.querySelector) {
  2287. return (passages.innerHTML = "This " + tale.identity() + " requires a newer web browser. Sorry.");
  2288. } else {
  2289. passages.innerHTML = "";
  2290. }
  2291. // Initialise Tale
  2292. tale = window.tale = new Tale();
  2293. // Check for IE8 image compatibility
  2294. if (~document.documentElement.className.indexOf("lt-ie9")) {
  2295. imgs = tale.lookup("tags", "Twine.image");
  2296. for (i = 0; i < imgs.length; i++) {
  2297. if (imgs[i].text.length >= 32768) {
  2298. alert("NOTE: This " + tale.identity() + "'s HTML file contains embedded images that may be too large for this browser to display."+softErrorMessage);
  2299. break;
  2300. }
  2301. }
  2302. }
  2303. // Run all script passages
  2304. scripts = tale.lookup("tags", "script");
  2305. for (i = 0; i < scripts.length; i++) {
  2306. scriptEval(scripts[i]);
  2307. sanityCheck('script passage "'+scripts[i].title+'"');
  2308. }
  2309. // Need to create state now, since it's used by remember.init()
  2310. state = window.state = new History();
  2311. for (i in macros) {
  2312. macro = macros[i];
  2313. if (typeof macro.init == "function") {
  2314. macro.init();
  2315. sanityCheck('init() of the custom macro "'+i+'"');
  2316. }
  2317. }
  2318. // Run all stylesheet passages
  2319. style = document.getElementById("storyCSS");
  2320. for (i in tale.passages) {
  2321. i = tale.passages[i];
  2322. if (i.tags.indexOf("stylesheet")==-1) {
  2323. continue;
  2324. }
  2325. // No other tags?
  2326. if (i.tags + "" == "stylesheet") {
  2327. styleText += i.text;
  2328. }
  2329. else if (i.tags.length == 2 && i.tags.indexOf("transition") >-1) {
  2330. setTransitionCSS(i.text);
  2331. }
  2332. }
  2333. styleText = alterCSS(styleText);
  2334. style.styleSheet ? (style.styleSheet.cssText = styleText) : (style.innerHTML = styleText);
  2335. state.init();
  2336. }
  2337. setTimeout(function f() {
  2338. var size, bar = document.getElementById("loadingbar"), store = document.getElementById("storeArea");
  2339. if (!bar) {
  2340. return;
  2341. }
  2342. if (store) {
  2343. size = store.getAttribute("data-size");
  2344. if (store.children.length <= size && !tale) {
  2345. // +1 so that the bar can reach the end
  2346. bar.style.width = ~~((store.children.length+1)/size*100)+"%";
  2347. }
  2348. else {
  2349. bar.outerHTML = "";
  2350. return;
  2351. }
  2352. }
  2353. setTimeout(f,5);
  2354. },5);
  2355. /*
  2356. **
  2357. ** Sugarcane/Responsive specific code follows
  2358. **
  2359. */
  2360. var hasPushState = !!window.history && (typeof window.history.pushState == "function") && (function(a) {
  2361. // iOS Safari: setItem throws in private mode
  2362. try {
  2363. a.setItem("test", '1');
  2364. a.removeItem("test");
  2365. return true;
  2366. } catch (e) {
  2367. return false;
  2368. }
  2369. }(window.sessionStorage));
  2370. Tale.prototype.canBookmark = function() {
  2371. return this.canUndo() && !this.storysettings.lookup('hash') && (this.storysettings.lookup('bookmark',true) || !hasPushState);
  2372. };
  2373. History.prototype.init = function () {
  2374. var a = this;
  2375. if (!this.restore()) {
  2376. if (tale.has("StoryInit")) {
  2377. new Wikifier(insertElement(null, "span"), tale.get("StoryInit").text);
  2378. }
  2379. this.display("Start", null)
  2380. }
  2381. if (!hasPushState) {
  2382. this.hash = window.location.hash;
  2383. this.interval = window.setInterval(function () {
  2384. a.watchHash()
  2385. }, 250)
  2386. }
  2387. };
  2388. hasPushState && (History.prototype.pushState = function(replace, uri) {
  2389. window.history[replace ? "replaceState" : "pushState"]({ id: this.id, length: this.history.length }, document.title, uri);
  2390. });
  2391. History.prototype.display = function (title, source, type, callback) {
  2392. var i, e, q, bookmark, hash, c = tale.get(title), p = document.getElementById("passages");
  2393. if (c==null) {
  2394. return;
  2395. }
  2396. if (type != "back") {
  2397. this.saveVariables(c, source, callback);
  2398. hash = (tale.storysettings.lookup('hash') && this.save()) || "";
  2399. if (hasPushState && tale.canUndo()) {
  2400. try {
  2401. sessionStorage.setItem("Twine.History"+this.id, JSON.stringify(decompile(this.history)));
  2402. this.pushState(this.history.length <= 2 && window.history.state == "", hash);
  2403. } catch(e) {
  2404. alert("Your browser couldn't save the state of the " + tale.identity() +".\n"+
  2405. "You may continue playing, but it will no longer be possible to undo moves from here on in.");
  2406. tale.storysettings.undo="off";
  2407. }
  2408. }
  2409. }
  2410. this.hash = hash || this.save();
  2411. e = c.render();
  2412. if (type != "quietly") {
  2413. if (hasTransition) {
  2414. for(i = 0; i < p.childNodes.length; i += 1) {
  2415. q = p.childNodes[i];
  2416. q.classList.add("transition-out");
  2417. setTimeout((function(a) { return function () {
  2418. if(a.parentNode) a.parentNode.removeChild(a);
  2419. }}(q)), 1000);
  2420. }
  2421. e.classList.add("transition-in");
  2422. setTimeout(function () { e.classList.remove("transition-in"); }, 1);
  2423. e.style.visibility = "visible";
  2424. p.appendChild(e);
  2425. } else {
  2426. removeChildren(p);
  2427. p.appendChild(e);
  2428. fade(e, {
  2429. fade: "in"
  2430. })
  2431. }
  2432. }
  2433. else {
  2434. p.appendChild(e);
  2435. e.style.visibility = "visible"
  2436. }
  2437. tale.setPageElements();
  2438. if (tale.canUndo()) {
  2439. if (!hasPushState && type != "back") {
  2440. window.location.hash = this.hash;
  2441. } else if (tale.canBookmark()) {
  2442. bookmark = document.getElementById("bookmark");
  2443. bookmark && (bookmark.href = this.hash);
  2444. }
  2445. }
  2446. window.scroll(0, 0)
  2447. return e
  2448. };
  2449. History.prototype.watchHash = function () {
  2450. if (window.location.hash != this.hash) {
  2451. if (window.location.hash && (window.location.hash != "#")) {
  2452. this.history = [{
  2453. passage: null,
  2454. variables: {}
  2455. }];
  2456. removeChildren(document.getElementById("passages"));
  2457. if (!this.restore()) {
  2458. alert("The passage you had previously visited could not be found.")
  2459. }
  2460. } else {
  2461. window.location.reload()
  2462. }
  2463. this.hash = window.location.hash
  2464. }
  2465. };
  2466. History.prototype.loadLinkVars = function() {
  2467. for (var c in this.history[0].linkVars) {
  2468. this.history[0].variables[c] = clone(this.history[0].linkVars[c]);
  2469. }
  2470. };
  2471. Passage.prototype.render = function () {
  2472. var b = insertElement(null, 'div', 'passage' + this.title, 'passage');
  2473. b.style.visibility = 'hidden';
  2474. this.setTags(b);
  2475. this.setCSS();
  2476. insertElement(b, 'div', '', 'header');
  2477. var a = insertElement(b, 'div', '', 'body content');
  2478. for (var i in prerender) {
  2479. (typeof prerender[i] == "function") && prerender[i].call(this,a);
  2480. }
  2481. new Wikifier(a, this.processText());
  2482. insertElement(b, 'div', '', 'footer');
  2483. for (i in postrender) {
  2484. (typeof postrender[i] == "function") && postrender[i].call(this,a);
  2485. }
  2486. return b;
  2487. };
  2488. Passage.prototype.excerpt = function () {
  2489. var b = this.text.replace(/<<.*?>>/g, "");
  2490. b = b.replace(/!.*?\n/g, "");
  2491. b = b.replace(/[\[\]\/]/g, "");
  2492. var a = b.split("\n");
  2493. while (a.length && a[0].length == 0) a.shift();
  2494. var c = '';
  2495. if (a.length == 0 || a[0].length == 0) c = this.title;
  2496. else c = a[0].substr(0, 30) + '...';
  2497. return c;
  2498. };
  2499. Passage.transitionCache = "";
  2500. Passage.prototype.setCSS = function() {
  2501. var trans = false, text = "", tags = this.tags || [],
  2502. c = document.getElementById('tagCSS'),
  2503. c2 = document.getElementById('transitionCSS');
  2504. if (c && c.getAttribute('data-tags') != tags.join(' ')) {
  2505. tale.forEachStylesheet(tags, function(passage) {
  2506. if (~passage.tags.indexOf("transition")) {
  2507. if (!Passage.transitionCache && c2)
  2508. Passage.transitionCache = c2.innerHTML;
  2509. setTransitionCSS(passage.text);
  2510. trans = true;
  2511. }
  2512. else text += alterCSS(passage.text);
  2513. });
  2514. if (!trans && Passage.transitionCache && c2) {
  2515. setTransitionCSS(Passage.transitionCache);
  2516. trans = false;
  2517. Passage.transitionCache = "";
  2518. }
  2519. c.styleSheet ? (c.styleSheet.cssText = text) : (c.innerHTML = text);
  2520. c.setAttribute('data-tags', tags.join(' '));
  2521. }
  2522. };
  2523. var Interface = {
  2524. init: function () {
  2525. var snapback = document.getElementById("snapback"),
  2526. restart = document.getElementById("restart"),
  2527. bookmark = document.getElementById("bookmark");
  2528. main();
  2529. if (!tale) {
  2530. return;
  2531. }
  2532. if (snapback) {
  2533. if (!tale.lookup("tags", "bookmark").length) {
  2534. snapback.parentNode.removeChild(snapback);
  2535. } else addClickHandler(snapback, Interface.showSnapback);
  2536. }
  2537. if (bookmark && (!tale.canBookmark() || !hasPushState)) {
  2538. bookmark.parentNode.removeChild(bookmark);
  2539. }
  2540. restart && addClickHandler(restart, Interface.restart);
  2541. },
  2542. restart: function () {
  2543. if (confirm("Are you sure you want to restart this " + tale.identity() + "?")) {
  2544. state.restart()
  2545. }
  2546. },
  2547. showSnapback: function (a) {
  2548. Interface.hideAllMenus();
  2549. Interface.buildSnapback();
  2550. Interface.showMenu(a, document.getElementById("snapbackMenu"))
  2551. },
  2552. buildSnapback: function () {
  2553. var b, c = false,
  2554. menuelem = document.getElementById("snapbackMenu");
  2555. while (menuelem.hasChildNodes()) {
  2556. menuelem.removeChild(menuelem.firstChild)
  2557. }
  2558. for(var a = state.history.length - 1; a >= 0; a--) {
  2559. if(state.history[a].passage && state.history[a].passage.tags.indexOf("bookmark") != -1) {
  2560. b = document.createElement("div");
  2561. b.pos = a;
  2562. addClickHandler(b, function () {
  2563. return macros.back.onclick(true, this.pos);
  2564. });
  2565. b.innerHTML = state.history[a].passage.excerpt();
  2566. menuelem.appendChild(b);
  2567. c = true
  2568. }
  2569. }
  2570. b = null
  2571. if(!c) {
  2572. b = document.createElement("div");
  2573. b.innerHTML = "<i>No passages available</i>";
  2574. document.getElementById("snapbackMenu").appendChild(b)
  2575. }
  2576. },
  2577. hideAllMenus: function () {
  2578. document.getElementById("snapbackMenu").style.display = "none"
  2579. },
  2580. showMenu: function (b, a) {
  2581. if (!b) {
  2582. b = window.event
  2583. }
  2584. var c = {
  2585. x: 0,
  2586. y: 0
  2587. };
  2588. if (b.pageX || b.pageY) {
  2589. c.x = b.pageX;
  2590. c.y = b.pageY
  2591. } else {
  2592. if (b.clientX || b.clientY) {
  2593. c.x = b.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
  2594. c.y = b.clientY + document.body.scrollTop + document.documentElement.scrollTop
  2595. }
  2596. }
  2597. a.style.top = c.y + "px";
  2598. a.style.left = c.x + "px";
  2599. a.style.display = "block";
  2600. addClickHandler(document, Interface.hideAllMenus);
  2601. b.cancelBubble = true;
  2602. if (b.stopPropagation) {
  2603. b.stopPropagation()
  2604. }
  2605. }
  2606. };
  2607. window.onload = Interface.init;
  2608. macros.back.onclick = function(back, steps) {
  2609. var title;
  2610. if (back) {
  2611. if (tale.canUndo()) {
  2612. window.history.go(-steps);
  2613. return;
  2614. }
  2615. while(steps-- >= 0 && state.history.length>1) {
  2616. title = state.history[0].passage.title;
  2617. state.history.shift();
  2618. }
  2619. state.loadLinkVars();
  2620. state.saveVariables(tale.get(title));
  2621. state.display(title, null, "back");
  2622. }
  2623. else {
  2624. state.display(state.history[steps].passage.title);
  2625. }
  2626. };
  2627. window.onpopstate = function(e) {
  2628. var title, hist, steps, i, s = e && e.state;
  2629. if (s && s.id && s.length != null) {
  2630. hist = recompile(JSON.parse(sessionStorage.getItem("Twine.History"+s.id)));
  2631. if (hist) {
  2632. steps = hist.length-s.length;
  2633. }
  2634. }
  2635. if (steps != null) {
  2636. state.history = hist;
  2637. // Shift the position of history to match how far back we've gone
  2638. while(steps-- >= 0 && state.history.length>1) {
  2639. title = state.history[0].passage.title;
  2640. state.history.shift();
  2641. }
  2642. state.loadLinkVars();
  2643. state.saveVariables(tale.get(title));
  2644. state.display(title, null, "back");
  2645. }
  2646. };
  2647. testplay = "";
  2648. }());
  2649. </script>
  2650. <script title="modules">
  2651. </script>
  2652. <style id="baseCSS">
  2653. /* Sidebar */
  2654. #sidebar {
  2655. position: fixed;
  2656. list-style: none;
  2657. width: 12em;
  2658. }
  2659. #sidebar #title, #sidebar #credits {
  2660. cursor: auto;
  2661. }
  2662. #sidebar #storySubtitle, #sidebar #storyMenu {
  2663. display: block;
  2664. }
  2665. .menu {
  2666. position: absolute;
  2667. display: none;
  2668. z-index: 5;
  2669. }
  2670. /* Passages container */
  2671. #passages {
  2672. margin-left: 8.2em;
  2673. position:relative;
  2674. font-size: 1.5em;
  2675. }
  2676. /* Links */
  2677. .passage a {
  2678. color: #558888;
  2679. }
  2680. a.internalLink, a.externalLink, a.back, a.return, [data-passage], .menu div {
  2681. cursor: pointer;
  2682. }
  2683. a.brokenLink {
  2684. background-color: red;
  2685. color: #000;
  2686. }
  2687. .marked {
  2688. background-color: #f66;
  2689. color: #000;
  2690. }
  2691. .marked[title] {
  2692. cursor: help;
  2693. }
  2694. .passage li[data-bullet] {
  2695. list-style-type: none;
  2696. }
  2697. .passage li[data-bullet]:before {
  2698. content: attr(data-bullet);
  2699. position: relative;
  2700. left: -1em;
  2701. }
  2702. #storeArea {
  2703. display: none;
  2704. }
  2705. #noscript {
  2706. margin-left: 18.2em;
  2707. font-size: 1.2em;
  2708. font-weight: bold;
  2709. }
  2710. /* HTML4 compatibility */
  2711. img {
  2712. vertical-align:bottom;
  2713. }
  2714. @media screen and (max-width: 640px) {
  2715. #sidebar {
  2716. position: static;
  2717. margin: 0 auto;
  2718. padding: 0;
  2719. }
  2720. body #sidebar li {
  2721. text-align: center;
  2722. }
  2723. #passages {
  2724. min-height: 100vh;
  2725. margin-left: 0em;
  2726. }
  2727. }
  2728. #loadingbar {
  2729. position:fixed;
  2730. top:0;
  2731. left:0;
  2732. border-top: solid #4d6ad8 6px;
  2733. transition: width 0.5s;
  2734. }
  2735. </style>
  2736. <style id="defaultCSS">
  2737. body {
  2738. background-color: #000;
  2739. color: #fff;
  2740. font-family: Verdana,sans-serif;
  2741. font-size: 62.5%;
  2742. margin: 4em 15% 5% 5em;
  2743. }
  2744. #sidebar {
  2745. left: 7.5em;
  2746. margin: 0;
  2747. padding: 0 1em 0 0;
  2748. font: bold 1.1em Verdana,sans-serif;
  2749. }
  2750. #sidebar ul {
  2751. padding: 0;
  2752. }
  2753. #sidebar li {
  2754. color: #333;
  2755. text-align: right;
  2756. background-repeat: no-repeat;
  2757. margin-bottom: 1em;
  2758. line-height: 1.4em;
  2759. list-style: none;
  2760. }
  2761. #sidebar li a {
  2762. color: #333;
  2763. text-decoration: none;
  2764. }
  2765. #sidebar li a:hover, #sidebar #title a:hover, #snapback:hover, #restart:hover {
  2766. color: #fff;
  2767. cursor: pointer;
  2768. text-decoration: none;
  2769. }
  2770. #sidebar #title {
  2771. font-size: 150%;
  2772. }
  2773. #sidebar #title, #sidebar #title:hover, #sidebar #title a {
  2774. color: #999;
  2775. }
  2776. #sidebar #storySubtitle {
  2777. font-size: 75%;
  2778. }
  2779. #storyAuthor {
  2780. font-size: 50%;
  2781. }
  2782. #sidebar #storyMenu {
  2783. line-height: 2.5em;
  2784. margin-bottom: .5em;
  2785. color: #999;
  2786. cursor: auto;
  2787. }
  2788. #sidebar #credits {
  2789. padding-top: 2em;
  2790. font-weight: normal;
  2791. font-size: 80%;
  2792. }
  2793. #sidebar #credits:hover {
  2794. color: #333;
  2795. }
  2796. #sidebar #credits a {
  2797. text-decoration: none;
  2798. }
  2799. #passages {
  2800. padding-left: 1.5em;
  2801. }
  2802. .menu {
  2803. background-color: #343434;
  2804. color: #fff;
  2805. opacity: .9;
  2806. border: 1px solid #fff;
  2807. text-align: left;
  2808. font: 1.1em Verdana;
  2809. line-height: 2em;
  2810. }
  2811. .menu div {
  2812. padding: 0 .4em;
  2813. }
  2814. .menu div:hover {
  2815. cursor: pointer;
  2816. background-color: #fff;
  2817. color: #343434;
  2818. }
  2819. .passage {
  2820. font-size: 1.2em;
  2821. line-height: 175%;
  2822. margin-bottom: 2em;
  2823. text-align: left;
  2824. }
  2825. .passage a {
  2826. font-weigh8ea6fft: bold;
  2827. text-decoration: none;
  2828. }
  2829. .passage a:hover {
  2830. color: #c0c0c0;
  2831. text-decoration: underline;
  2832. }
  2833. .content > ul {
  2834. padding-top: 1.3em;
  2835. }
  2836. .passage ul, .passage ol {
  2837. margin-left: .5em;
  2838. padding-left: 1.5em;
  2839. }
  2840. .passage li {
  2841. margin-right: 6em;
  2842. }
  2843. .passage table {
  2844. border-collapse: collapse;
  2845. font-size: 100%;
  2846. margin: .8em 1.0em;
  2847. }
  2848. .passage th,.passage td,.passage tr,.passage caption {
  2849. padding: 3px;
  2850. }
  2851. .passage hr {
  2852. height: 1px;
  2853. }
  2854. .passage center {
  2855. max-width:50%;
  2856. margin:auto;
  2857. }
  2858. .marked {
  2859. margin-right: 12px;
  2860. padding: 3px;
  2861. }
  2862. .disabled {
  2863. font-weight: bold;
  2864. color: #333;
  2865. }
  2866. @media screen and (max-width: 640px) {
  2867. body {
  2868. margin: 5%;
  2869. }
  2870. #sidebar {
  2871. width:100%;
  2872. margin: 0;
  2873. border-bottom: 1px solid #333;
  2874. }
  2875. #passages {
  2876. padding-top: 2em;
  2877. border-left: 0;
  2878. }
  2879. }
  2880. </style>
  2881. <style id="transitionCSS">
  2882. .transition-in {
  2883. opacity:0;
  2884. position:absolute;
  2885. }
  2886. .passage:not(.transition-out) {
  2887. transition: 1s;
  2888. -webkit-transition: 1s;
  2889. }
  2890. .transition-out {
  2891. opacity:0 !important;
  2892. position:absolute;
  2893. }
  2894. </style>
  2895. <style id="storyCSS"></style>
  2896. <style id="tagCSS"></style>
  2897. </head>
  2898. <body>
  2899. <div id="loadingbar"></div>
  2900. <!--<ul id="sidebar">
  2901. <li id="title" class="storyElement"><span id="storyTitle" class="storyElement"></span><span id="storySubtitle" class="storyElement"></span><span id="titleSeparator"></span><span id="storyAuthor" class="storyElement"></span></li>
  2902. <li id="storyMenu" class="storyElement" style="display:none"></li>
  2903. <li>...</li>
  2904. <li><a href="javascript:;" id="restart">start over</a></li>
  2905. </ul>
  2906. <div id="snapbackMenu" class="menu"></div>-->
  2907. <div id="passages">
  2908. <noscript><div id="noscript">Please enable Javascript to play this story!</div></noscript>
  2909. <style>
  2910. #sidebar{display:none;}
  2911. </style>
  2912. </div>
  2913. <div id="storeArea" data-size="24" hidden><div tiddler="StoryTitle" twine-position="0,0">tow</div><div tiddler="StoryAuthor" twine-position="0,0">@modgethanc</div><div tiddler="Start" twine-position="0,0">!tow\n\n//[[march 2012|one]]//</div><div tiddler="one" twine-position="0,0">When I close my eyes, \nit's winter again on the [[beach,|two]]</div><div tiddler="two" twine-position="0,0">and the dark grey waves are [[calling us home.|three]]</div><div tiddler="three" twine-position="0,0">The sand is whipping through the air, \ngetting into my [[eyes.|four]]</div><div tiddler="four" twine-position="0,0">There was one brave [[child|four2]]</div><div tiddler="four2" twine-position="0,0">trying to sculpt [[a wall|five]]</div><div tiddler="five" twine-position="0,0">to surround the body of a dead [[sea bird,|six]]</div><div tiddler="six" twine-position="0,0">but the wind kept toppling his [[piles|six2]]</div><div tiddler="six2" twine-position="0,0">onto the scattered [[feathers|six3]]</div><div tiddler="six3" twine-position="0,0">so he left in [[frustration.|seven]]</div><div tiddler="seven" twine-position="0,0">Now, there is no one on the beach \n[[except us,|eight]]</div><div tiddler="eight" twine-position="0,0">and you are standing ankle-deep and naked in the writhing [[surf.|nine]]</div><div tiddler="nine" twine-position="0,0">&quot;The undertow is sucking at my feet,&quot; you say, \nwindmilling your arms to keep [[upright.|ten]]</div><div tiddler="ten" twine-position="0,0">&quot;Here goes nothing,&quot; you [[say,|eleven]]</div><div tiddler="eleven" twine-position="0,0">and you [[drop.|twelve]]</div><div tiddler="twelve" twine-position="0,0">[[~|twelve2]]</div><div tiddler="twelve2" twine-position="0,0">When I open my eyes, you are no longer \nnaked and in front of [[me,|thirteen]]</div><div tiddler="thirteen" twine-position="0,0">and winter has [[melted away.|fourteen]]</div><div tiddler="fourteen" twine-position="0,0">There is still sand in my [[shoes|fifteen]]</div><div tiddler="fifteen" twine-position="0,0">and the wind still carries your breath across [[the water.|sixteen]]</div><div tiddler="sixteen" twine-position="0,0">[[...|end]]</div><div tiddler="end" twine-position="0,0">//@modgethanc//\n\n[[restart|Start]]</div></div></body></html>