PK 58Im$! vgo-latest/.buildinfo# Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
config:
tags:
PK 58IW vgo-latest/objects.inv# Sphinx inventory version 2
# Project: vgo
# Version: 0.0.1
# The remainder of this file is compressed using zlib.
xKOKIP(.IILJQ5THe(+x\)XT$}SJsRS3P@Ġ!
S D1PK 58Iw-G G vgo-latest/index.html
Verifiable go or vgo for short is a subset of the go programming
language introduced by Google that has been extended so that the
programmer can use a temporal logic (CTL) to express assumptions and
guarantees directly within the source code.
The tool is invoked like the go build system itself. It verifies that
the code has the desired properties and produces a compiled library or
aborts with an appropriate error message.
This has a number of advantages:
The tool fits very well into the usual development practices.
Verification failures prevent compilation of the code.
The very same code that has been model checked is compiled, there is
no need to translate the code to an intermediate language.
The produced library is a standard go module that other code written
in go can use. This makes it possible to divide the code into parts
that can be verified (like the core logic) and parts that is not
easily verifiable (any kind of I/O, user interfaces, etc.).
to provide an implementation of a modular model checking framework
for further experimentation and
to provide a tool capable of checking properties of programs written
in a modern imperative programming language while focusing on
usability aspects, i.e. to provide a tool that can be used by
programmers without intimate knowledge of the theoretical background
of model checking.
All of vgos functionality can be invoked using the vgo
binary. Running it without arguments to get a list of available
commands:
vgo is a frontend for the verifiable go toolchain.
Usage:
vgo [vgo options] command [command options] [command args]
The commands are:
install verifies and installs the given vgo package
inspect vgo reports interactively
runtests runs the vgo test suite
config prints the vgo configuration
version prints the vgo version
help prints usage and flags of a given command
Use "vgo help [command]" for more information about a command.
Additional help topics:
options global vgo options
Use "vgo help [topic]" for more information about that topic.
vgo needs its source code to translate model checking stubs in the
verification step. It uses the environment variable VGOROOT to
locate it. If VGOROOT is not set, it defaults to /usr/lib/vgo. As
a convenience if you run vgo directly from the source tree it uses
this location automatically.
If you have installed vgo to ${PREFIX}, you must set VGOROOT to
${PREFIX}/lib/vgo. Make sure that the vgo frontend vgo is in your
${PATH}. With bourne shell like shells this can be done using:
Welcome to the vgo tutorial. You will need a working vgo
installation and either a vgo source checkout or the vgo-doc
package installed. To generate graphs the graphviz package is
needed. On Debian like systems you can install it using aptitude
install graphviz.
You will find the source code for each example here either in the
source tree under docs/tutorial or
/usr/share/doc/vgo-doc/tutorial.
The source for this example is in the directory tutorial0. Copy this
directory and fire up vgo:
% cp --recursive /usr/share/doc/vgo-doc/tutorial/tutorial0 /tmp
% cd /tmp/tutorial0
% vgo install -report gettingstarted
02:35:31 [Info] Welcome to the one and only model checker!
02:35:31 [Info] vgo version 0.0.1.44202be27a4b+
02:35:31 [Info] Writing report into /tmp/vgo-install126016440
02:35:32 [Info] Constructing model for type sometype
02:35:32 [Info] sometype: Exploring state space
02:35:32 [Info] Found 2 distinct states connected by 1 edges
02:35:32 [Info] sometype: State space explored
02:35:32 [Info] Package gettingstarted installed successfully
vgo explores the graph of reachable states, also known as Kripke
structure, and renders it as a scalable vector graphic if asked to do
so using the -report flag. Let us have a look at this image:
% xdg-open /tmp/vgo-install126016440/sometype.svg
The image should look like this:
The each node in this diagram is a state of the program. The state 0
is special though, it is there to be a common parent to all objects
created by the constructors. There is only one constructor and it
allocates sometype objects using new. Since the memory is zeroed,
the field a is set to false (the zeroish value of type bool).
So this is not a very interesting program, it has just one state since
there is no way to modify the non-exported field a. But it is a
start. Let us look at it in more detail.
Each node is a reachable state and has a unique number. It is also
labeled with the string representation of the object representing that
state. This string representation uses an optimization for boolean
fields, if a field is true, its name is included, if it is false,
it is omitted. Since the string representation is sometype{}, the
field a must be false in this state.
Every edge indicates a possible state transition and is annotated with
the method that invokes this state transition. In this example the
only edge is the one of the New() constructor.
Let us look at the source. Open src/gettingstarted/main.vgo with
your favorite editor and enable the go syntax highlighting mode. You
will see this:
This is both a valid vgo and a valid go program. vgo is a
superset of a subset of go. It starts with a package declaration and
defines a type sometype with a private member a of type bool.
Let us now add a method to make the program more interesting. Add the
following to the file main.vgo:
func(s*sometype)SetA(){s.a=true}
If you recompile the program using vgo, you will see:
So we added a method SetA() that sets the member a to
true. Consequently we now see a second state that can be
reached from the initially constructed state by invoking the
SetA method. If we are in state and invoke this method
again, nothing changes.
Now let us annotate our type with guarantees that can be verified
using the model checking engine in vgo. Let us specify, that on all
(infinite) paths there is some point in the future where the member
a is true. The corresponding CTL formula is . Extend
the vgo type declaration to read:
typesometypestruct{abool}satisfying{AF`s.a`}
In vgo atomic propositions are enclosed in backticks (`) and must be
go expressions of type boolean. The current object is bound to s,
so s.a accesses the member a of type boolean (in case you are not
familiar with go note that it is customary to name this reference
like the first letter of your type converted to lower case, so it is a
s because the type is called sometype). If you recompile the
program you will see:
% vgo install -report gettingstarted
03:30:08 [Info] Welcome to the one and only model checker!
03:30:08 [Info] vgo version 0.0.1.44202be27a4b+
03:30:08 [Info] Writing report into /tmp/vgo-install608100277
03:30:09 [Info] Constructing model for type sometype
03:30:09 [Info] sometype: Exploring state space
03:30:09 [Info] Found 3 distinct states connected by 3 edges
03:30:09 [Info] sometype: State space explored
03:30:09 [Info] conventional.Check(sometype, AF`s.a`) successful.
03:30:09 [Info] Package gettingstarted installed successfully
So the program was found to be fulfilling the specification. Go ahead
and add another guarantee like .
Microwave ovens are a canonical model checking showcase, so let us
implement one. The source for this example is in tutorial1, you can
compile it using vgo install microwave. If you get stuck in this
section, you can look at the microwave example shipped with vgo under
src/vgo/tests/microwave.
If you look at the source you will find the following type
declaration:
typemicrowavestruct{onbool// is the microwave on?openbool// is the door open?errorstring// was the microwave used incorrectly? how?}satisfying{AG!(`m.DoorOpen()`&`m.On()`)// the microwave is never on if the// door is openAG!(`m.Error()`&`m.On()`)// the microwave is never on if the// error flag is setAGEF!(`m.Error()`|(`m.On()`|`m.DoorOpen()`))// resettableAGAF!`m.On()`// the microwave is never on forever}
Currently the microwave fulfills the specification, but it is a not a
very useful microwave since one cannot turn it on. Please implement
the methods Start(), Tick(), Open(), Close(), Reset() and
setError(s). Some remarks:
It should be considered an error if one invokes Start() while the
door is open.
Tick() models elapsing time. Cooking should require one unit of
time. The bool field on models this just fine.
There are two kinds of errors here:
Imagine a microwave with the door closed. Now a user can open the
door and she gets a microwave with the door being open. It is not
possible to open the door again, since it is already open. The
Open() function should reflect this by returning an error. Note
that by returning an error you indicate to vgo that you do not
change state of the object.
But if on the other hand she presses the start button while the
door is still open, we want to inform her of this error by
displaying it and make her acknowledge the error by resetting the
microwave. The Start() function should set the error field to
an appropriate message using setError(s).
The vgo frontend has a switch to display verification reports using
your browser:
% vgo install -inspect microwave
For reference, here is the Kripke structure of the microwave in
src/vgo/examples/microwave which fulfills the specification:
So far we only used types that were structs containing booleans. But
one of the cool features of go (and thus vgo) is the fact that you
can define specialized versions of primitive data types and bind
methods to them.
Model Checking suffers from the so called state explosion
problem. This problem gets worse if we use numeric data types because
there are possible 32 bit integers and thus possible
states instead of the two possible values of a boolean. Exploring the
state space of a program using a single 32 bit integer is not feasible
if done naively, but there are ways to work around that
(e.g. abstract interpretation). Currently vgo handles only small
integers, but we can do some interesting things nonetheless.
Let us have a look at tutorial2/src/collatz/collatz.vgo:
packagecollatzimport"fmt"typecollatzuint8satisfying{AGAF`*c == 1`}// New constructs objects of type collatz.funcNew(nuint8)(*collatz,error){ifn==0||n>9{returnnil,fmt.Errorf("too large: %v",n)}c:=collatz(n)return&c,nil}// Step computes one step of the collatz conjecture, i.e. it changes c// to c/2 if c is even else c*3+1.func(c*collatz)Step(){// implement this function}
Some notes:
In the declaration of Step(), c is called the receiver.
As before the receiver is a pointer type (c *Collatz), so you can
use the dereference operator * to access and modify its value
(e.g. *c = 5).
If you haven’t heard of the Collatz conjecture, you can consult
Wikipedia.
You can use the -compact-graphs flag to make vgo create more
compact graphs by omitting the numerical node ids and edge labels.
Tasks:
Implement Step(). Check whether your work fulfills the specification.
So far the examples have been somewhat constructed and we haven’t seen
the code run. So let us consider a more interesting example and
implement the Nim game. The code for this section is in
tutorial/tutorial3, it contains a Nim engine written in vgo
(src/engine) and a console application written in go using the
engine (src/nim). If you get stuck in this section, you can look at
src/vgo/tests/engine.
This example highlights an important concept of vgo, namely the
separation of programs into parts that can be model checked (i.e. the
game engine) and parts that cannot easily be model checked (i.e. the
frontend).
The most interesting part is in src/engine/engine.vgo. The type
declaration reads:
typenimstruct{heaps[3]uint8// the three heapsturnPlayer// turn indicates whos turn it is}satisfying{!EF`n.Evaluate() == OPPONENT`// the engines opponent never winsAGEF`n.Evaluate() == ENGINE`// there is always a path where// eventually the engine has won}
Some notes:
Move(*Move) error is used to execute moves for the engines
opponent. If the move is not valid in the current game state, it
returns an error.
Ponder() (*Move, error) uses ponder() (*Move, error) to generate
a move for the engine and executes and returns the move.
The type nim has two guarantees stating that there is no reachable
state in which the opponent (i.e. you) has won and that in every
state there is always a path where in the future the engine has won.
ponder() currently generates an arbitrary valid move.
There is a Makefile, you can execute make to build the engine and
(provided the verification was successful) the frontend bin/nim.
In go and thus vgo the operator for exclusive or is ^.
Tasks:
Implement the winning strategy described below in ponder().
(optional) Create a modified version for the misère version of the
game.
(optional) Modify the game to handle arbitrary starting positions.
Some theory:
The key to winning the game is to finish every move with a nim sum
over all stacks of 0. The nim sum is binary exclusive or.
If the nim sum of all the stacks is not 0, it is always possible
to make a move to make the sum 0. In the default starting position
(3, 4, 5) the nim sum is not 0, so the first player has a winning
strategy.
The winning strategy is:
Compute s, the nim sum of all heaps.
Iterate over the heaps
test whether count ^ s < count. If this is the case, return the
move that removes count - (count ^ s) objects from that
heap. This results in a state where the nim sum is 0.
')
.appendTo($('#searchbox'));
}
},
/**
* init the domain index toggle buttons
*/
initIndexTable : function() {
var togglers = $('img.toggler').click(function() {
var src = $(this).attr('src');
var idnum = $(this).attr('id').substr(7);
$('tr.cg-' + idnum).toggle();
if (src.substr(-9) == 'minus.png')
$(this).attr('src', src.substr(0, src.length-9) + 'plus.png');
else
$(this).attr('src', src.substr(0, src.length-8) + 'minus.png');
}).css('display', '');
if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) {
togglers.click();
}
},
/**
* helper function to hide the search marks again
*/
hideSearchWords : function() {
$('#searchbox .highlight-link').fadeOut(300);
$('span.highlighted').removeClass('highlighted');
},
/**
* make the url absolute
*/
makeURL : function(relativeURL) {
return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL;
},
/**
* get the current relative url
*/
getCurrentURL : function() {
var path = document.location.pathname;
var parts = path.split(/\//);
$.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() {
if (this == '..')
parts.pop();
});
var url = parts.join('/');
return path.substring(url.lastIndexOf('/') + 1, path.length - 1);
}
};
// quick alias for translations
_ = Documentation.gettext;
$(document).ready(function() {
Documentation.init();
});
PK 8I8c c vgo-latest/_static/websupport.js/*
* websupport.js
* ~~~~~~~~~~~~~
*
* sphinx.websupport utilties for all documentation.
*
* :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
(function($) {
$.fn.autogrow = function() {
return this.each(function() {
var textarea = this;
$.fn.autogrow.resize(textarea);
$(textarea)
.focus(function() {
textarea.interval = setInterval(function() {
$.fn.autogrow.resize(textarea);
}, 500);
})
.blur(function() {
clearInterval(textarea.interval);
});
});
};
$.fn.autogrow.resize = function(textarea) {
var lineHeight = parseInt($(textarea).css('line-height'), 10);
var lines = textarea.value.split('\n');
var columns = textarea.cols;
var lineCount = 0;
$.each(lines, function() {
lineCount += Math.ceil(this.length / columns) || 1;
});
var height = lineHeight * (lineCount + 1);
$(textarea).css('height', height);
};
})(jQuery);
(function($) {
var comp, by;
function init() {
initEvents();
initComparator();
}
function initEvents() {
$(document).on("click", 'a.comment-close', function(event) {
event.preventDefault();
hide($(this).attr('id').substring(2));
});
$(document).on("click", 'a.vote', function(event) {
event.preventDefault();
handleVote($(this));
});
$(document).on("click", 'a.reply', function(event) {
event.preventDefault();
openReply($(this).attr('id').substring(2));
});
$(document).on("click", 'a.close-reply', function(event) {
event.preventDefault();
closeReply($(this).attr('id').substring(2));
});
$(document).on("click", 'a.sort-option', function(event) {
event.preventDefault();
handleReSort($(this));
});
$(document).on("click", 'a.show-proposal', function(event) {
event.preventDefault();
showProposal($(this).attr('id').substring(2));
});
$(document).on("click", 'a.hide-proposal', function(event) {
event.preventDefault();
hideProposal($(this).attr('id').substring(2));
});
$(document).on("click", 'a.show-propose-change', function(event) {
event.preventDefault();
showProposeChange($(this).attr('id').substring(2));
});
$(document).on("click", 'a.hide-propose-change', function(event) {
event.preventDefault();
hideProposeChange($(this).attr('id').substring(2));
});
$(document).on("click", 'a.accept-comment', function(event) {
event.preventDefault();
acceptComment($(this).attr('id').substring(2));
});
$(document).on("click", 'a.delete-comment', function(event) {
event.preventDefault();
deleteComment($(this).attr('id').substring(2));
});
$(document).on("click", 'a.comment-markup', function(event) {
event.preventDefault();
toggleCommentMarkupBox($(this).attr('id').substring(2));
});
}
/**
* Set comp, which is a comparator function used for sorting and
* inserting comments into the list.
*/
function setComparator() {
// If the first three letters are "asc", sort in ascending order
// and remove the prefix.
if (by.substring(0,3) == 'asc') {
var i = by.substring(3);
comp = function(a, b) { return a[i] - b[i]; };
} else {
// Otherwise sort in descending order.
comp = function(a, b) { return b[by] - a[by]; };
}
// Reset link styles and format the selected sort option.
$('a.sel').attr('href', '#').removeClass('sel');
$('a.by' + by).removeAttr('href').addClass('sel');
}
/**
* Create a comp function. If the user has preferences stored in
* the sortBy cookie, use those, otherwise use the default.
*/
function initComparator() {
by = 'rating'; // Default to sort by rating.
// If the sortBy cookie is set, use that instead.
if (document.cookie.length > 0) {
var start = document.cookie.indexOf('sortBy=');
if (start != -1) {
start = start + 7;
var end = document.cookie.indexOf(";", start);
if (end == -1) {
end = document.cookie.length;
by = unescape(document.cookie.substring(start, end));
}
}
}
setComparator();
}
/**
* Show a comment div.
*/
function show(id) {
$('#ao' + id).hide();
$('#ah' + id).show();
var context = $.extend({id: id}, opts);
var popup = $(renderTemplate(popupTemplate, context)).hide();
popup.find('textarea[name="proposal"]').hide();
popup.find('a.by' + by).addClass('sel');
var form = popup.find('#cf' + id);
form.submit(function(event) {
event.preventDefault();
addComment(form);
});
$('#s' + id).after(popup);
popup.slideDown('fast', function() {
getComments(id);
});
}
/**
* Hide a comment div.
*/
function hide(id) {
$('#ah' + id).hide();
$('#ao' + id).show();
var div = $('#sc' + id);
div.slideUp('fast', function() {
div.remove();
});
}
/**
* Perform an ajax request to get comments for a node
* and insert the comments into the comments tree.
*/
function getComments(id) {
$.ajax({
type: 'GET',
url: opts.getCommentsURL,
data: {node: id},
success: function(data, textStatus, request) {
var ul = $('#cl' + id);
var speed = 100;
$('#cf' + id)
.find('textarea[name="proposal"]')
.data('source', data.source);
if (data.comments.length === 0) {
ul.html('
No comments yet.
');
ul.data('empty', true);
} else {
// If there are comments, sort them and put them in the list.
var comments = sortComments(data.comments);
speed = data.comments.length * 100;
appendComments(comments, ul);
ul.data('empty', false);
}
$('#cn' + id).slideUp(speed + 200);
ul.slideDown(speed);
},
error: function(request, textStatus, error) {
showError('Oops, there was a problem retrieving the comments.');
},
dataType: 'json'
});
}
/**
* Add a comment via ajax and insert the comment into the comment tree.
*/
function addComment(form) {
var node_id = form.find('input[name="node"]').val();
var parent_id = form.find('input[name="parent"]').val();
var text = form.find('textarea[name="comment"]').val();
var proposal = form.find('textarea[name="proposal"]').val();
if (text == '') {
showError('Please enter a comment.');
return;
}
// Disable the form that is being submitted.
form.find('textarea,input').attr('disabled', 'disabled');
// Send the comment to the server.
$.ajax({
type: "POST",
url: opts.addCommentURL,
dataType: 'json',
data: {
node: node_id,
parent: parent_id,
text: text,
proposal: proposal
},
success: function(data, textStatus, error) {
// Reset the form.
if (node_id) {
hideProposeChange(node_id);
}
form.find('textarea')
.val('')
.add(form.find('input'))
.removeAttr('disabled');
var ul = $('#cl' + (node_id || parent_id));
if (ul.data('empty')) {
$(ul).empty();
ul.data('empty', false);
}
insertComment(data.comment);
var ao = $('#ao' + node_id);
ao.find('img').attr({'src': opts.commentBrightImage});
if (node_id) {
// if this was a "root" comment, remove the commenting box
// (the user can get it back by reopening the comment popup)
$('#ca' + node_id).slideUp();
}
},
error: function(request, textStatus, error) {
form.find('textarea,input').removeAttr('disabled');
showError('Oops, there was a problem adding the comment.');
}
});
}
/**
* Recursively append comments to the main comment list and children
* lists, creating the comment tree.
*/
function appendComments(comments, ul) {
$.each(comments, function() {
var div = createCommentDiv(this);
ul.append($(document.createElement('li')).html(div));
appendComments(this.children, div.find('ul.comment-children'));
// To avoid stagnating data, don't store the comments children in data.
this.children = null;
div.data('comment', this);
});
}
/**
* After adding a new comment, it must be inserted in the correct
* location in the comment tree.
*/
function insertComment(comment) {
var div = createCommentDiv(comment);
// To avoid stagnating data, don't store the comments children in data.
comment.children = null;
div.data('comment', comment);
var ul = $('#cl' + (comment.node || comment.parent));
var siblings = getChildren(ul);
var li = $(document.createElement('li'));
li.hide();
// Determine where in the parents children list to insert this comment.
for(i=0; i < siblings.length; i++) {
if (comp(comment, siblings[i]) <= 0) {
$('#cd' + siblings[i].id)
.parent()
.before(li.html(div));
li.slideDown('fast');
return;
}
}
// If we get here, this comment rates lower than all the others,
// or it is the only comment in the list.
ul.append(li.html(div));
li.slideDown('fast');
}
function acceptComment(id) {
$.ajax({
type: 'POST',
url: opts.acceptCommentURL,
data: {id: id},
success: function(data, textStatus, request) {
$('#cm' + id).fadeOut('fast');
$('#cd' + id).removeClass('moderate');
},
error: function(request, textStatus, error) {
showError('Oops, there was a problem accepting the comment.');
}
});
}
function deleteComment(id) {
$.ajax({
type: 'POST',
url: opts.deleteCommentURL,
data: {id: id},
success: function(data, textStatus, request) {
var div = $('#cd' + id);
if (data == 'delete') {
// Moderator mode: remove the comment and all children immediately
div.slideUp('fast', function() {
div.remove();
});
return;
}
// User mode: only mark the comment as deleted
div
.find('span.user-id:first')
.text('[deleted]').end()
.find('div.comment-text:first')
.text('[deleted]').end()
.find('#cm' + id + ', #dc' + id + ', #ac' + id + ', #rc' + id +
', #sp' + id + ', #hp' + id + ', #cr' + id + ', #rl' + id)
.remove();
var comment = div.data('comment');
comment.username = '[deleted]';
comment.text = '[deleted]';
div.data('comment', comment);
},
error: function(request, textStatus, error) {
showError('Oops, there was a problem deleting the comment.');
}
});
}
function showProposal(id) {
$('#sp' + id).hide();
$('#hp' + id).show();
$('#pr' + id).slideDown('fast');
}
function hideProposal(id) {
$('#hp' + id).hide();
$('#sp' + id).show();
$('#pr' + id).slideUp('fast');
}
function showProposeChange(id) {
$('#pc' + id).hide();
$('#hc' + id).show();
var textarea = $('#pt' + id);
textarea.val(textarea.data('source'));
$.fn.autogrow.resize(textarea[0]);
textarea.slideDown('fast');
}
function hideProposeChange(id) {
$('#hc' + id).hide();
$('#pc' + id).show();
var textarea = $('#pt' + id);
textarea.val('').removeAttr('disabled');
textarea.slideUp('fast');
}
function toggleCommentMarkupBox(id) {
$('#mb' + id).toggle();
}
/** Handle when the user clicks on a sort by link. */
function handleReSort(link) {
var classes = link.attr('class').split(/\s+/);
for (var i=0; iThank you! Your comment will show up '
+ 'once it is has been approved by a moderator.');
}
// Prettify the comment rating.
comment.pretty_rating = comment.rating + ' point' +
(comment.rating == 1 ? '' : 's');
// Make a class (for displaying not yet moderated comments differently)
comment.css_class = comment.displayed ? '' : ' moderate';
// Create a div for this comment.
var context = $.extend({}, opts, comment);
var div = $(renderTemplate(commentTemplate, context));
// If the user has voted on this comment, highlight the correct arrow.
if (comment.vote) {
var direction = (comment.vote == 1) ? 'u' : 'd';
div.find('#' + direction + 'v' + comment.id).hide();
div.find('#' + direction + 'u' + comment.id).show();
}
if (opts.moderator || comment.text != '[deleted]') {
div.find('a.reply').show();
if (comment.proposal_diff)
div.find('#sp' + comment.id).show();
if (opts.moderator && !comment.displayed)
div.find('#cm' + comment.id).show();
if (opts.moderator || (opts.username == comment.username))
div.find('#dc' + comment.id).show();
}
return div;
}
/**
* A simple template renderer. Placeholders such as <%id%> are replaced
* by context['id'] with items being escaped. Placeholders such as <#id#>
* are not escaped.
*/
function renderTemplate(template, context) {
var esc = $(document.createElement('div'));
function handle(ph, escape) {
var cur = context;
$.each(ph.split('.'), function() {
cur = cur[this];
});
return escape ? esc.text(cur || "").html() : cur;
}
return template.replace(/<([%#])([\w\.]*)\1>/g, function() {
return handle(arguments[2], arguments[1] == '%' ? true : false);
});
}
/** Flash an error message briefly. */
function showError(message) {
$(document.createElement('div')).attr({'class': 'popup-error'})
.append($(document.createElement('div'))
.attr({'class': 'error-message'}).text(message))
.appendTo('body')
.fadeIn("slow")
.delay(2000)
.fadeOut("slow");
}
/** Add a link the user uses to open the comments popup. */
$.fn.comment = function() {
return this.each(function() {
var id = $(this).attr('id').substring(1);
var count = COMMENT_METADATA[id];
var title = count + ' comment' + (count == 1 ? '' : 's');
var image = count > 0 ? opts.commentBrightImage : opts.commentImage;
var addcls = count == 0 ? ' nocomment' : '';
$(this)
.append(
$(document.createElement('a')).attr({
href: '#',
'class': 'sphinx-comment-open' + addcls,
id: 'ao' + id
})
.append($(document.createElement('img')).attr({
src: image,
alt: 'comment',
title: title
}))
.click(function(event) {
event.preventDefault();
show($(this).attr('id').substring(2));
})
)
.append(
$(document.createElement('a')).attr({
href: '#',
'class': 'sphinx-comment-close hidden',
id: 'ah' + id
})
.append($(document.createElement('img')).attr({
src: opts.closeCommentImage,
alt: 'close',
title: 'close'
}))
.click(function(event) {
event.preventDefault();
hide($(this).attr('id').substring(2));
})
);
});
};
var opts = {
processVoteURL: '/_process_vote',
addCommentURL: '/_add_comment',
getCommentsURL: '/_get_comments',
acceptCommentURL: '/_accept_comment',
deleteCommentURL: '/_delete_comment',
commentImage: '/static/_static/comment.png',
closeCommentImage: '/static/_static/comment-close.png',
loadingImage: '/static/_static/ajax-loader.gif',
commentBrightImage: '/static/_static/comment-bright.png',
upArrow: '/static/_static/up.png',
downArrow: '/static/_static/down.png',
upArrowPressed: '/static/_static/up-pressed.png',
downArrowPressed: '/static/_static/down-pressed.png',
voting: false,
moderator: false
};
if (typeof COMMENT_OPTIONS != "undefined") {
opts = jQuery.extend(opts, COMMENT_OPTIONS);
}
var popupTemplate = '\
\ Sort by:\ best rated\ newest\ oldest\
\\
Add a comment\ (markup):
\``code``
, \ code blocks:::
and an indented block after blank line