imbroglio
Version:
a tool for making browser-based interactive fiction
235 lines (222 loc) • 6.98 kB
JavaScript
// Generated by IcedCoffeeScript 1.8.0-d
(function() {
var $, assert, choiceChars, compile, elem, hashchange, newGame, normalize, prepare, quote, restore, stdlib, turn, _ref;
choiceChars = 'abcdefghijklmnopqrstuvwxyz0123456789';
assert = require('assert');
$ = require('jquery');
_ref = require('./compiler'), quote = _ref.quote, stdlib = _ref.stdlib, prepare = _ref.prepare;
elem = stdlib().elem;
normalize = function(s) {
return s.replace(/^\s+/, '').replace(/\s+$/, '').replace(/\s+/, ' ');
};
exports.compile = compile = function(src, opts) {
var firstPassage, handleError, passages, render;
if (opts == null) {
opts = {};
}
handleError = opts.handleError || function(msg) {
return console.log(msg);
};
passages = {};
firstPassage = null;
(function() {
var lastPassage, m, re;
re = /(?:^\s*\n?|\n\n)#(?!\{)\s*([^\n]*\S)\s*\n/g;
lastPassage = null;
while (m = re.exec(src)) {
if (lastPassage) {
lastPassage.endIndex = m.index;
}
lastPassage = {
name: normalize(m[1]),
startIndex: re.lastIndex
};
assert(!(lastPassage.name in passages), lastPassage.name);
passages[lastPassage.name] = lastPassage;
firstPassage || (firstPassage = lastPassage);
}
if (!lastPassage) {
handleError('no passages found');
}
lastPassage.endIndex = src.length;
})();
(function() {
var k, v, _fn;
_fn = function() {
v.src = src.substring(v.startIndex, v.endIndex);
v.mungedSrc = v.src.replace(/\[\[([^\]]*)\]\]/g, function(outer, inner, index) {
var m, offset, target, text;
offset = index + v.startIndex;
text = target = inner;
if (m = /^(.*)->\s*([^<>]*[^\s<>])\s*$/.exec(inner)) {
text = m[1];
target = m[2];
} else if (m = /^\s*([^<>]*[^\s<>])\s*<-(.*)$/.exec(inner)) {
target = m[1];
text = m[2];
}
target = normalize(target);
if (!(target in passages)) {
handleError("bad link target '" + target + "' at " + outer + ", passage '" + k + "', offset " + offset);
}
return "#\{imbroglio.mkLink " + (quote(target)) + ", " + (quote(text)) + "}";
});
v.prepared = prepare(v.mungedSrc, {
argNames: ['imbroglio'],
thisVar: 'imbroglio.state',
handleError: function(e) {
console.log(e);
if (e.error instanceof Error) {
throw e.error;
} else if (e.error) {
throw new Error(e.error);
} else {
throw new Error(e);
}
}
});
};
for (k in passages) {
v = passages[k];
_fn();
}
})();
render = function(passage, result) {
var linkCount, links, mkLink, moves, state;
if (result == null) {
result = {};
}
state = result.state || {};
delete result.state;
moves = result.moves || (result.moves = '');
links = {};
linkCount = 0;
mkLink = function(target, text) {
var choiceChar, el;
if (!(target in passages)) {
return elem('span', {
"class": 'error'
}, "ERROR: bad link target '" + target + "' in passage '" + passage.name + "': " + text);
} else if (linkCount >= choiceChars.length) {
return elem('span', {
"class": 'error'
}, "ERROR: too many links, passage '" + passage.name + "', target '" + target + "': " + text);
}
choiceChar = choiceChars[linkCount++];
el = elem('a', {
"class": 'choice',
href: "#!" + moves + choiceChar
}, text);
links[choiceChar] = {
el: el,
target: target
};
return el;
};
result.passageElem = passage.prepared(stdlib({
mkLink: mkLink,
state: state
}));
result.choose = function(ch) {
var link;
if (!(link = links[ch])) {
console.log("invalid move " + ch + " from passage " + passage.name);
return null;
}
$(link.el).addClass('chosen');
return render(passages[link.target], {
moves: "" + moves + ch,
state: state
});
};
return result;
};
return function() {
return render(firstPassage);
};
};
newGame = turn = null;
restore = function(moves) {
var $chosen, $output, $p, ch, last, prevTurn, scrollPos, _i, _len, _ref1;
if ((turn != null ? turn.moves : void 0) === moves) {
return;
}
$('#loading').show();
$('.pane').hide();
$('#game').hide();
$output = $('#output');
last = function() {
var children;
children = $output.children();
if (children.length) {
return $(children.get(children.length - 1));
} else {
return children;
}
};
if (!turn || turn.moves !== moves.slice(0, turn.moves.length)) {
turn = newGame();
$output.empty();
$output.append(turn.passageElem);
}
_ref1 = moves.slice(turn.moves.length);
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
ch = _ref1[_i];
prevTurn = turn;
if (!(turn = turn.choose(ch))) {
$('#404-pane').show();
$('#loading').hide();
return;
}
turn.prevTurn = prevTurn;
$output.append(turn.passageElem);
}
$output.children().removeClass('current');
last().addClass('current');
$('#game').show();
$('#loading').hide();
$p = $(turn.passageElem);
$chosen = $('.chosen');
scrollPos = !$chosen.length ? 0 : $($chosen.get($chosen.length - 1)).offset().top;
console.log("XXX scrollPos = " + scrollPos);
window.scrollTo(0, scrollPos);
};
hashchange = function() {
var hash, m, target;
hash = window.location.hash.replace(/^#/, '');
if (m = /^!(.*)$/.exec(hash)) {
return restore(m[1]);
}
turn = target = null;
if (!hash) {
target = $('#home');
} else if (m = /^\/([a-z][a-z0-9-]*)$/.exec(hash)) {
target = $("#" + m[1] + "-pane");
}
if (!(target != null ? target.length : void 0)) {
target = $('#404-pane');
}
$('#game').hide();
$('#output').empty();
$('.pane').hide();
target.show();
$('#loading').hide();
window.scrollTo(0, 0);
};
exports.start = function(src) {
newGame = compile(src);
return $(function() {
(function() {
var m, _ref1;
m = /([^\/]+)$/.exec(((_ref1 = window.location) != null ? _ref1.pathname : void 0) || '');
$('a[href="#"], a[href="#/"]').attr('href', m ? m[1] : '.');
})();
$(window).on('hashchange', function(e) {
e.preventDefault();
hashchange();
return true;
});
hashchange();
});
};
}).call(this);