2016-01-24 08:33:39 +02:00
|
|
|
###
|
|
|
|
Salet interface configuration.
|
|
|
|
In a typical MVC structure, this is the View.
|
|
|
|
Only it knows about the DOM structure.
|
|
|
|
Other modules just use its API or prepare the HTML for insertion.
|
2016-01-24 09:53:03 +02:00
|
|
|
You don't need to call this module from the game directly.
|
|
|
|
|
|
|
|
The abstraction goal here is to provide the author with a freedom to style his
|
|
|
|
game as he wants to. The save and erase buttons are not necessary buttons,
|
|
|
|
but they could be something else entirely. (That's why IDs are hardcoded.)
|
2016-01-24 08:33:39 +02:00
|
|
|
###
|
|
|
|
|
|
|
|
class SaletView
|
|
|
|
init: () ->
|
2016-01-25 18:05:32 +02:00
|
|
|
$("#content, #ways").on("click", "a", (event) ->
|
2016-01-24 08:33:39 +02:00
|
|
|
event.preventDefault()
|
2016-01-24 09:53:03 +02:00
|
|
|
salet.processClick($(this).attr("href"))
|
2016-01-24 08:33:39 +02:00
|
|
|
)
|
|
|
|
$("#inventory").on("click", "a", (event) ->
|
|
|
|
event.preventDefault()
|
2016-01-25 18:05:32 +02:00
|
|
|
alert("Not done yet")
|
2016-01-24 08:33:39 +02:00
|
|
|
)
|
|
|
|
$("#load").on("click", "a", (event) ->
|
|
|
|
window.location.reload()
|
|
|
|
)
|
|
|
|
|
|
|
|
disableSaving: () ->
|
|
|
|
$("#save").addClass('disabled')
|
|
|
|
enableSaving: () ->
|
|
|
|
$("#save").removeClass('disabled')
|
|
|
|
enableErasing: () ->
|
|
|
|
$("#erase").removeClass('disabled')
|
|
|
|
disableErasing: () ->
|
|
|
|
$("#erase").addClass('disabled')
|
|
|
|
enableLoading: () ->
|
|
|
|
$("#load").removeClass('disabled')
|
|
|
|
disableLoading: () ->
|
|
|
|
$("#load").addClass('disabled')
|
|
|
|
|
|
|
|
# Scrolls the top of the screen to the specified point
|
|
|
|
scrollTopTo: (value) ->
|
|
|
|
$('html,body').animate({scrollTop: value}, 500)
|
|
|
|
|
|
|
|
# Scrolls the bottom of the screen to the specified point
|
|
|
|
scrollBottomTo: (value) ->
|
|
|
|
scrollTopTo(value - $(window).height())
|
|
|
|
|
|
|
|
# Scrolls all the way to the bottom of the screen
|
|
|
|
scrollToBottom: () ->
|
|
|
|
scrollTopTo($('html').height() - $(window).height());
|
|
|
|
|
|
|
|
###
|
|
|
|
Removes all content from the page, clearing the main content area.
|
|
|
|
|
|
|
|
If an elementSelector is given, then only that selector will be
|
|
|
|
cleared. Note that all content from the cleared element is removed,
|
|
|
|
but the element itself remains, ready to be filled again using @write.
|
|
|
|
###
|
|
|
|
clearContent: (elementSelector = "#content") ->
|
|
|
|
if (elementSelector == "#content") # empty the intro with the content
|
2016-01-24 09:53:03 +02:00
|
|
|
document.getElementById("intro").innerHTML = ""
|
2016-01-24 08:33:39 +02:00
|
|
|
document.querySelector(elementSelector).innerHTML = ""
|
|
|
|
|
|
|
|
# Write content to current room
|
|
|
|
write: (content) ->
|
|
|
|
if content == ""
|
|
|
|
return
|
|
|
|
if typeof content == "function"
|
|
|
|
content = content()
|
|
|
|
block = document.getElementById("current-room")
|
|
|
|
if block
|
|
|
|
block.innerHTML = block.innerHTML + markdown(content)
|
|
|
|
else
|
|
|
|
console.error("No current situation found.")
|
|
|
|
|
|
|
|
###
|
|
|
|
Turns any links that target the given href into plain
|
|
|
|
text. This can be used to remove action options when an action
|
|
|
|
is no longer available. It is used automatically when you give
|
|
|
|
a link the 'once' class.
|
|
|
|
###
|
|
|
|
clearLinks: (code) ->
|
|
|
|
for a in $("a[href='" + code + "']")
|
|
|
|
a.replaceWith($("<span>").addClass("ex_link").html(a.html()))
|
|
|
|
|
|
|
|
###
|
|
|
|
Given a list of situation ids, this outputs a standard option
|
|
|
|
block with the situation choices in the given order.
|
|
|
|
|
|
|
|
The contents of each choice will be a link to the situation,
|
|
|
|
the text of the link will be given by the situation's
|
|
|
|
outputText property. Note that the canChoose function is
|
|
|
|
called, and if it returns false, then the text will appear, but
|
|
|
|
the link will not be clickable.
|
|
|
|
|
|
|
|
Although canChoose is honored, canView and displayOrder are
|
|
|
|
not. If you need to honor these, you should either do so
|
|
|
|
manually, ot else use the `getSituationIdChoices` method to
|
|
|
|
return an ordered list of valid viewable situation ids.
|
|
|
|
###
|
|
|
|
writeChoices: (listOfIds) ->
|
|
|
|
if (listOfIds.length == 0)
|
|
|
|
return
|
|
|
|
|
|
|
|
currentSituation = getCurrentSituation();
|
|
|
|
$options = $("<ul>").addClass("options");
|
|
|
|
for situationId in listOfIds
|
|
|
|
situation = game.situations[situationId]
|
|
|
|
assert(situation, "unknown_situation".l({id:situationId}))
|
|
|
|
if (situation == currentSituation)
|
|
|
|
continue
|
|
|
|
|
|
|
|
optionText = situation.optionText.fcall(this, character, this, currentSituation)
|
|
|
|
if (!optionText)
|
|
|
|
optionText = "choice".l({number:i+1})
|
|
|
|
$option = $("<li>")
|
|
|
|
$a = $("<span>")
|
|
|
|
if (situation.canChoose(character, this, currentSituation))
|
|
|
|
$a = $("<a>").attr({href: situationId})
|
|
|
|
$a.html(optionText)
|
|
|
|
$option.html($a)
|
|
|
|
$options.append($option)
|
|
|
|
@write($options)
|
|
|
|
|
|
|
|
# Marks all links as old. This gets called in a `processLink` function.
|
|
|
|
mark_all_links_old: () ->
|
|
|
|
$('.new').removeClass('new')
|
|
|
|
|
|
|
|
# Removes links and transient sections.
|
|
|
|
# Arguments:
|
|
|
|
# interactive - if we're working in interactive mode (or we're loading a save)
|
|
|
|
remove_transient: (interactive = false) ->
|
|
|
|
for a in $('#content a')
|
|
|
|
if (a.hasClass('sticky') || a.attr("href").match(/[?&]sticky[=&]?/))
|
|
|
|
return;
|
|
|
|
a.replaceWith($("<span>").addClass("ex_link").html(a.html()))
|
|
|
|
contentToHide = $('#content .transient, #content ul.options')
|
|
|
|
contentToHide.add($("#content a").filter(() ->
|
|
|
|
return $(this).attr("href").match(/[?&]transient[=&]?/)
|
|
|
|
))
|
|
|
|
if (interactive)
|
|
|
|
contentToHide.animate({opacity: 0}, 350).
|
|
|
|
slideUp(500, () ->
|
|
|
|
$(this).remove()
|
|
|
|
)
|
|
|
|
else
|
|
|
|
contentToHide.remove()
|
|
|
|
|
|
|
|
# At last, we scroll the view so that .new objects are in view.
|
|
|
|
endOutputTransaction: () =>
|
|
|
|
if !@interactive
|
|
|
|
return; # We're loading a save; do nothing at all.
|
|
|
|
$new = $('.new')
|
|
|
|
viewHeight = $(window).height()
|
|
|
|
newTop = newBottom = newHeight = optionHeight = 0
|
|
|
|
|
|
|
|
if $new.length == 0
|
|
|
|
return; # Somehow, there's nothing new.
|
|
|
|
|
|
|
|
newTop = $new.first().offset().top
|
|
|
|
newBottom = $new.last().offset().top + $new.last().height()
|
|
|
|
newHeight = newBottom - newTop
|
|
|
|
|
|
|
|
# We take the options list into account, because we don't want the new
|
|
|
|
# content to scroll offscreen when the list disappears. So we calculate
|
|
|
|
# scroll points as though the option list was already gone.
|
|
|
|
if ($('.options').not('.new').length)
|
|
|
|
optionHeight = $('.options').not('new').height()
|
|
|
|
|
|
|
|
if (newHeight > (viewHeight - optionHeight - 50))
|
|
|
|
# The new content is too long for our viewport, so we scroll the
|
|
|
|
# top of the new content to roughly 75% of the way up the viewport's
|
|
|
|
# height.
|
|
|
|
scrollTopTo(newTop-(viewHeight*0.25) - optionHeight);
|
|
|
|
else
|
|
|
|
if (newTop > $('body').height() - viewHeight)
|
|
|
|
# If we scroll right to the bottom, the new content will be in
|
|
|
|
# view. So we do that.
|
|
|
|
scrollToBottom();
|
|
|
|
else
|
|
|
|
# Our new content is too far up the page. So we scroll to place
|
|
|
|
# it somewhere near the bottom.
|
|
|
|
scrollBottomTo(newBottom+100 - optionHeight);
|
|
|
|
|
2016-01-24 09:53:03 +02:00
|
|
|
# Feature detection
|
|
|
|
hasLocalStorage: () ->
|
|
|
|
hasStorage = false
|
|
|
|
try
|
|
|
|
hasStorage = ('localStorage' in window) &&
|
|
|
|
window.localStorage != null &&
|
|
|
|
window.localStorage != undefined;
|
|
|
|
catch err
|
|
|
|
hasStorage = false
|
|
|
|
return hasStorage
|
|
|
|
|
2016-01-25 18:05:32 +02:00
|
|
|
module.exports = SaletView
|