Initial commit: shoot and reload

This commit is contained in:
Alexander Yakovlev 2015-12-01 13:42:52 +07:00
commit 9df72b5ae3
9 changed files with 1070 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
node_modules
build
dist
dist.zip

161
Gulpfile.js Normal file
View File

@ -0,0 +1,161 @@
'use strict';
/* Raconteur Gulpfile scaffold. */
/* Includes code adapted from Gulp documentation, among other sources. */
/* Imports */
var watchify = require('watchify'),
browserify = require('browserify'),
browserSync = require('browser-sync'),
gulp = require('gulp'),
source = require('vinyl-source-stream'),
gutil = require('gulp-util'),
coffeify = require('coffeeify'),
less = require('gulp-less'),
minifyCSS = require('gulp-minify-css'),
uglify = require('gulp-uglify'),
buffer = require('vinyl-buffer'),
zip = require('gulp-zip'),
_ = require('lodash');
var reload = browserSync.reload;
/* Tasks */
/* Trivial file copies */
function html (target) {
return function () {
return gulp.src('html/index.html')
.pipe(gulp.dest(target));
};
}
function img (target) {
return function () {
return gulp.src(['img/*.png', 'img/*.jpeg', 'img/*.jpg'])
.pipe(gulp.dest(target));
};
}
gulp.task('html', html('./build'));
gulp.task('img', img('./build/img'));
/* Less */
gulp.task('less', function () {
gulp.src('less/main.less')
.pipe(less())
.pipe(gulp.dest('./build/css'));
});
/* Bundle libraries */
var undumBundler = browserify({debug: true});
undumBundler.require('undum-commonjs');
gulp.task('buildUndum', function () {
return undumBundler.bundle().pipe(source('undum.js')).pipe(gulp.dest('./build/game'));
});
/* Generate JavaScript with browser sync. */
var customOpts = {
entries: ['./game/main.coffee'],
debug: true,
transform: [coffeify]
};
var opts = _.assign({}, watchify.args, customOpts);
var bundler = watchify(browserify(opts));
bundler.external('undum-commonjs');
gulp.task('coffee', ['buildUndum'], bundle); // `gulp coffee` will generate bundle
bundler.on('update', bundle); // Re-bundle on dep updates
bundler.on('log', gutil.log); // Output build logs to terminal
function bundle () {
return bundler.bundle()
.on('error', gutil.log.bind(gutil, 'Browserify Error'))
.pipe(source('bundle.js'))
.pipe(gulp.dest('./build/game'));
};
/* Make a development build */
gulp.task('build', ['html', 'img', 'less', 'coffee'], function () {
});
/* Start a development server */
gulp.task('serve', ['build'], function () {
browserSync({
server: {
baseDir: 'build'
}
});
var lessListener = function () {
reload('./build/css/main.css');
}
gulp.watch(['./html/*.html'], ['html']);
gulp.watch(['./less/*.less'], ['less']);
gulp.watch(['./img/*.png', './img/*.jpeg', './img/*.jpg'], ['img']);
gulp.watch(['./build/css/main.css'], lessListener);
gulp.watch(
['./build/game/bundle.js', './build/img/*', './build/index.html'],
browserSync.reload);
});
/* Distribution tasks */
var undumDistBundler = browserify();
undumDistBundler.require('undum-commonjs');
gulp.task('undum-dist', function () {
return undumDistBundler.bundle().pipe(source('undum.js'))
.pipe(buffer())
.pipe(uglify())
.pipe(gulp.dest('./dist/game'));
});
gulp.task('html-dist', html('./dist'));
gulp.task('img-dist', img('./dist/img'));
gulp.task('less-dist', function () {
return gulp.src('./less/main.less')
.pipe(less())
.pipe(minifyCSS())
.pipe(gulp.dest('./dist/css'));
});
var distBundler = browserify({
debug: false,
entries: ['./game/main.coffee'],
transform: ['coffeeify']
});
distBundler.external('undum-commonjs');
gulp.task('coffee-dist', ['undum-dist'], function () {
return distBundler.bundle()
.pipe(source('bundle.js'))
.pipe(buffer())
.pipe(uglify())
.on('error', gutil.log)
.pipe(gulp.dest('./dist/game'));
});
gulp.task('dist', ['html-dist', 'img-dist', 'less-dist', 'coffee-dist'],
function () {
return;
});
gulp.task('zip', ['dist'], function () {
return gulp.src('dist/**')
.pipe(zip('dist.zip'))
.pipe(gulp.dest('.'));
});

165
game/main.coffee Normal file
View File

@ -0,0 +1,165 @@
# copyright (c) Alexander Yakovlev 2015.
# Distributed under the MIT license. See LICENSE for information.
situation = require('raconteur')
undum = require('undum-commonjs')
$ = require('jquery')
oneOf = require('raconteur/lib/oneOf.js')
elements = require('raconteur/lib/elements.js')
qualities = require('raconteur/lib/qualities.js')
colour = require("color")
md = require('markdown-it')
markdown = new md({
typographer: true,
html: true
})
a = elements.a
span = elements.span
img = elements.img
undum.game.id = "7a1aba32-f0fd-4e3b-ba5a-59e3fa9e6012"
undum.game.version = "0.1"
way_to = (content, ref) -> a(content).class('way').ref(ref)
textlink = (content, ref) -> a(content).once().writer(ref)
is_visited = (situation) -> undum.game.situations[situation].visited == 1
writemd = (system, text) ->
if typeof text is Function
text = text()
system.write(markdown.render(text))
link_colour = "#B68000"
spend_bullet = (character, system) ->
bullets = character.sandbox.clips[character.sandbox.current_clip]
if bullets >= 1
character.sandbox.clips[character.sandbox.current_clip]--
system.setQuality("bullets", bullets - 1)
spend_clip = (character, system) ->
clips = character.sandbox.clips.length
bullets = character.sandbox.clips[character.sandbox.current_clip]
if clips == 0
return
if bullets == 0
character.sandbox.clips.splice(character.sandbox.current_clip, 1)
system.setQuality("bullets", character.sandbox.clips[character.sandbox.current_clip])
system.setQuality("clips", clips - 1)
writemd(system, "Я выбрасываю пустой картридж.")
else
if character.sandbox.current_clip < clips - 1
character.sandbox.current_clip++
else
character.sandbox.current_clip == 0
system.setQuality("bullets", character.sandbox.clips[character.sandbox.current_clip])
situation 'start',
content: """
-- Проклятье, они продолжают идти!
Узкий коридор, я и непрекращающаяся очередь сверкающих белоснежной кожей андроидов.
Я уверен, что я представлял этот Новый Год совершенно не так.
""",
choices: ["#shoot"],
situation "hit",
content: (character, system, from) ->
response = oneOf(
"Голова андроида взрывается снопом сверкающих искр.",
"Андроид пытается увернуться, но попадает точнёхонько под пулю. Он падает, разливая масло на пол."
).randomly(system)
return response()
choices: ["#shoot"]
before: (character, system, from) ->
system.setQuality("enemies", character.qualities.enemies - 1)
character.sandbox.nicked = 0
choices: ["#shoot"]
situation "nicked",
content: (character, system, from) ->
if character.sandbox.nicked == 1
system.setQuality("enemies", character.qualities.enemies - 1)
character.sandbox.nicked = 0
response = oneOf(
"Я добиваю андроида выстрелом в сердце.",
"Я добиваю андроида точным выстрелом",
"Пуля пробивает голову андроида, и он наконец падает на пол без движения.",
).randomly(system)
return response()
else
character.sandbox.nicked = 1
response = oneOf(
"Я отстреливаю ногу врага. Он падает, но продолжает медленно царапать путь ко мне руками."
"Я простреливаю руку андроида. Он пошатывается, но продолжает идти."
).randomly(system)
return response()
choices: ["#shoot"]
situation "miss",
content: (character, system, from) ->
response = oneOf(
"Пуля пролетает над левым плечом андроида.",
"Андроид вовремя уворачивается от выстрела. Ничего, в следующий раз я не промахнусь."
).randomly(system)
return response()
choices: ["#shoot"]
situation "shoot",
tags: ["shoot"],
optionText: (character, system, from) ->
response = oneOf(
"Выстрел",
"СТРЕЛЯТЬ!",
"УБИТЬ ИХ ИЗ ПИСТОЛЕТА",
"НЕНАВИЖУ АНДРОИДОВ"
).stopping(system)
return response()
canChoose: (character, system) ->
return character.qualities.bullets > 0
before: (character, system, from) ->
spend_bullet(character, system)
system.clearContent()
after: (character, system, from) ->
# d20 roll
# 1-14 - hit, 15-18 - nicked, 19-20 = miss
roll = system.rnd.randomInt(1,20)
switch
when roll < 15 then system.doLink("hit")
when roll > 18 then system.doLink("miss")
else system.doLink("nicked")
situation "reload",
tags: ["shoot"],
choices: ["#shoot"],
optionText: "Перезарядить пистолет",
canView: (character, system) ->
return character.qualities.bullets < 6
before: (character, system) ->
system.clearContent()
after: (character, system) ->
spend_clip(character, system)
writemd(system, "Я вставляю другой картридж в пистолет. Надеюсь, в нём есть патроны.")
return true
# А теперь plot twist: у пистолета есть шанс осечки и промаха. Ты теряешь патрон. Всего патронов у тебя 36, а врагов 35.
situation "finale",
content: """
""",
qualities
head:
bullets: qualities.integer('Патронов в картридже'),
clips: qualities.integer('Картриджей с патронами'),
enemies: qualities.integer('Врагов впереди'),
undum.game.init = (character, system) ->
system.setQuality("bullets", 6)
system.setQuality("clips", 6)
system.setQuality("enemies", 35)
character.sandbox.clips = [6,6,6,6]
character.sandbox.current_clip = 0
character.sandbox.nicked = 0
window.onload = undum.begin

94
html/index.html Normal file
View File

@ -0,0 +1,94 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Тридцать пять выстрелов</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/main.css">
</head>
<body>
<div id="toolbar">
<h1>Тридцать пять выстрелов</h1>
<div class="nav">
<a href="#" class="button" id="menu-button">Меню</a>
</div>
</div>
<ul id="menu">
<li><a href="#title, #content_wrapper">Рассказ</a></li>
<li><a href="#character_panel">Персонаж</a></li>
</ul>
<div id="page">
<div id="tools_wrapper">
<div id="character_panel" class="tools right">
<div id="character">
<div id="character_text">
<div id="character_text_content"></div>
</div>
<div id="qualities"></div>
</div>
<div class='buttons'>
<button id="save">Сохранить</button><button id="erase">Стереть</button>
</div>
</div>
</div> <!-- End of div.tools_wrapper -->
<div id="mid_panel">
<div id="title">
<div class="label">
<h1>Тридцать пять выстрелов</h1>
<noscript>
<p class="noscript_message">Эта игра требует Javascript.</p>
</noscript>
<p class="click_message">нажмите, чтобы начать</p>
</div>
</div>
<div id="content_wrapper">
<div id="content">
</div>
<a name="end_of_content"></a>
</div>
<div id="legal">
<p>Приблизительное время прохождения игры: пятнадцать минут. Но вы схватите идею за пять.</p>
<p>Написано при помощи <a href="http://undum.com">Undum</a> и <a href="http://sequitur.github.io/raconteur/">Raconteur</a>.</p>
</div>
</div>
</div> <!-- End of div.page -->
<div id="ui_library">
<div id="quality" class="quality">
<span class="name" data-attr="name"></span>
<span class="value" data-attr="value"></span>
</div>
<div id="quality_group" class="quality_group">
<h2 data-attr="title"></h2>
<div class="qualities_in_group">
</div>
</div>
<div id="progress_bar" class="progress_bar">
<span class="name" data-attr="name"></span>
<span class="value" data-attr="value"></span>
<div class="progress_bar_track">
<div class="progress_bar_color" data-attr="width">
</div>
</div>
<span class="left_label" data-attr="left_label"></span>
<span class="right_label" data-attr="right_label"></span>
</div>
<hr id="turn_separator">
</div>
<div id="content_library"></div>
<script type="text/javascript" src="game/undum.js"></script>
<script type="text/javascript" src="game/bundle.js"></script>
</body>
</html>

75
less/animations.less Normal file
View File

@ -0,0 +1,75 @@
/* Animate newly inserted DOM elements within content */
/* "Why don't we just use opacity?"
"Because opacity, creates massive jank and this doesn't. Browsers!"
*/
.fade-in() {
animation: fadeIn 500ms ease-in-out;
-webkit-animation: fadeIn 500ms ease-in-out;
//-moz-animation: fadeIn 500ms;
a {
animation: fadeInA 500ms ease-in-out;
-webkit-animation: fadeInA 500ms ease-in-out;
}
}
@keyframes fadeIn {
from {
color: rgba(0,0,0,0);
}
to {
color: rgba(0,0,0,1);
}
}
@-webkit-keyframes fadeIn {
from {
color: rgba(0,0,0,0);
}
to {
color: rgba(0,0,0,1);
}
}
@keyframes fadeInA {
0% {
color: transparentize(@anchor-colour, 1);
}
100% {
color: transparentize(@anchor-colour, 0);
}
}
@-webkit-keyframes fadeInA {
from {
color: transparentize(@anchor-colour, 1);
}
to {
color: transparentize(@anchor-colour, 0);
}
}
@keyframes fadeInS {
0% {
color: transparentize(@option-colour, 1);
}
100% {
color: @option-colour;
}
}
@-webkit-keyframes fadeInS {
from {
color: transparentize(@option-colour, 1);
}
to {
color: @option-colour;
}
}
.fade {
.fade-in();
}

57
less/grid.less Normal file
View File

@ -0,0 +1,57 @@
/////////////////
// Semantic.gs // for LESS: http://lesscss.org/
/////////////////
// Defaults which you can freely override
@column-width: 60;
@gutter-width: 20;
@columns: 12;
// Utility variable — you should never need to modify this
@gridsystem-width: (@column-width*@columns) + (@gutter-width*@columns) * 1px;
// Set @total-width to 100% for a fluid layout
@total-width: @gridsystem-width;
// The micro clearfix http://nicolasgallagher.com/micro-clearfix-hack/
.clearfix() {
*zoom:1;
&:before,
&:after {
content:"";
display:table;
}
&:after {
clear:both;
}
}
//////////
// GRID //
//////////
body {
width: 100%;
.clearfix;
}
.row(@columns:@columns) {
display: block;
width: @total-width*((@gutter-width + @gridsystem-width)/@gridsystem-width);
margin: 0 @total-width*(((@gutter-width*.5)/@gridsystem-width)*-1);
.clearfix;
}
.column(@x,@columns:@columns) {
display: inline;
float: left;
width: @total-width*((((@gutter-width+@column-width)*@x)-@gutter-width) / @gridsystem-width);
margin: 0 @total-width*((@gutter-width*.5)/@gridsystem-width);
}
.push(@offset:1) {
margin-left: @total-width*(((@gutter-width+@column-width)*@offset) / @gridsystem-width) + @total-width*((@gutter-width*.5)/@gridsystem-width);
}
.pull(@offset:1) {
margin-right: @total-width*(((@gutter-width+@column-width)*@offset) / @gridsystem-width) + @total-width*((@gutter-width*.5)/@gridsystem-width);
}

460
less/layout.less Normal file
View File

@ -0,0 +1,460 @@
@import 'grid.less';
@columns: 12;
@column-width: 60;
@gutter-width: 20;
@total-width: 100%;
#page {
.row();
}
.mid_panel {
.column(9);
}
.full_panel {
.column(10);
.push(1);
}
#tools_wrapper {
.column(3);
background: @text_background;
position: sticky;
top: 2em;
}
.tools {
margin: 1.1em auto;
padding: 0 1em;
}
#content_wrapper {
margin: 1.1em auto;
padding: 2.8em;
display: none; /* Shown by Javascript */
overflow: auto;
}
@media (min-width: 981px) {
#content_wrapper {
margin: 1.1em 15%;
}
}
@media (min-width: 1281px) {
#content_wrapper {
margin: 1.1em 25%;
}
}
@media screen and (max-width: 720px) {
.mid_panel,
.full_panel {
.column(12);
margin-bottom: 1em;
}
}
body {
background: @background;
color: @color;
font-family: @font-body;
font-size: 18px;
line-height: 1.6em;
background-attachment: fixed;
overflow-y: scroll;
overflow-x: hidden;
}
/* The title block */
#title, #title .label, #content, .tools {
border-radius: 2px;
}
#title {
max-width: 28em;
margin: 2.2em auto 1.1em auto;
padding: 1.7em;
cursor: pointer; /* Until we click to start. */
.label {
overflow: hidden;
padding: 2.0em;
margin: auto;
max-width: 18em;
position: relative;
text-align: center;
}
.subtitle {
font-size: smaller;
color: #aaa;
}
h1 {
font-size: 1.6em;
line-height: 1.4em;
font-family: @font-title;
letter-spacing: 0.2em;
font-weight: normal;
padding-bottom: 1.1em;
span.fancy {
font-size: 2.5em;
line-height: 0;
font-family: Tangerine, Palatino, Times, "Times New Roman", serif;
font-style: italic;
margin: 0 -0.2em;
}
}
h2 {
font-size: 1.2em;
font-weight: normal;
margin: 1.1em 0 0 0;
}
h3 {
font-size: 1.0em;
font-weight: normal;
margin: 1.1em 0 0 0;
}
h1,
h2,
h3 {
color: rgba(33,17,0,0.9);
text-shadow: rgba(255,255,255,0.5) 2px 2px 2px,
rgba(0,0,0,0.1) -1px -1px 2px;
}
.warnings {
font-size: small;
font-style: italic;
p {
margin-bottom: 1em;
}
}
.click_message {
display: none;
left: 0;
right: 0;
bottom: 0;
position: absolute;
font-size: 0.9em;
font-style: italic;
text-align: center;
color: #987;
}
.noscript_message {
left: 0;
right: 0;
bottom: 0;
position: absolute;
font-size: 0.9em;
font-style: italic;
text-align: center;
color: #943;
}
}
/* Main content */
#content_wrapper {
background: @text_background;
}
span.drop + p {
text-indent: -0.4em;
}
p {
margin: 0;
-webkit-transition: text-indent 0.25s ease;
transition: text-indent 0.25s ease;
}
hr {
border: none;
background-color: rgba(0,0,0,0.25);
margin: -1px 0 -1px -2.8em;
width: 1.1em;
height: 2px;
}
#content {
p {
text-indent: 1.6em;
}
section {
border-top: 1px dashed #bbb;
}
}
#content h1 + p:first-line,
#content h1 + img + p:first-line {
font-weight: bold;
color: rgba(0,0,0,0.85);
}
#content h1 + p:first-letter,
#content h1 + img + p:first-letter {
position: relative;
padding-top: 0.1em;
display: block;
float: left;
font-weight: normal;
font-size: 3.2em;
line-height: 0.8em;
color: #210;
}
ul {
margin: 0;
padding: 0 0 0 1em;
}
ul.options {
padding: 0;
text-align: center;
margin-top: 0.5em;
margin-bottom: 0.7em;
list-style-type: none;
border-radius: 4px;
li {
padding: 0.5em;
}
li:hover {
cursor: pointer;
}
li:last-child {
border-bottom: none;
}
}
h1 {
font-size: 1.0em;
text-transform: uppercase;
letter-spacing: 2px;
margin: 2.3em 0 1.1em 0;
color: #210;
text-align: center;
}
h1:first-child {
margin-top: 0;
}
a {
color: @links;
text-decoration: none;
border-bottom: 1px solid transparent;
}
a:hover {
border-bottom: 1px dotted #900;
}
img.right {
float: right;
margin: 1.1em 0 1.1em 1.1em;
}
img.left {
float: left;
margin: 1.1em 1.1em 1.1em 0;
}
#toolbar, #tools_wrapper {
display: none;
}
.tools {
p {
font-size: 0.95em;
line-height: 1.5em;
margin-top: 6px;
}
h1 {
font-size: 1.0em;
font-weight: normal;
margin-bottom: 0.6em;
}
}
.buttons {
padding-top: 0.6em;
text-align: center;
button {
font-size: 0.8em;
background: #876;
color: #e6e6c6;
border: none;
padding: 0.3em 1.0em;
cursor: pointer;
border-radius: 4px;
}
button:hover {
background: #987;
}
button + button {
margin-left: 0.3em;
}
button[disabled], button[disabled]:hover {
background: #ba9;
color: #dcb;
cursor: default;
}
}
#legal {
max-width: 33em;
color: #654;
margin: 1em auto 0 auto;
padding-bottom: 2.2em;
display: none; /* Shown by Javascript */
p {
font-size: 0.7em;
line-height: 1.3em;
margin-bottom: 0.5em;
}
p + p {
text-indent: 0;
}
}
#character {
font-size: 1.0em;
line-height: 1.4em;
}
#qualities .quality, #character_text {
position: relative;
clear: both;
overflow: hidden;
margin: 0 -0.25em;
padding: 0 0.25em;
}
#character_text {
margin-bottom: 0.6em;
#character_text_content {
position: relative;
font-size: smaller;
z-index: 100;
}
}
#qualities{
span {
position: relative;
z-index: 100;
}
span.name {
float: left;
}
span.value {
float: right;
}
h2 {
margin: 0.5em 0 0.25em 0;
font-size: 1.0em;
}
}
.highlight {
background: rgba(255, 255, 0, 0.75);
position: absolute;
left: -4px;
right: -4px;
top: 0;
bottom: 0;
}
#content_library,
#ui_library,
#menu {
display: none;
}
@media screen and (max-width: 640px) {
body {
margin: 0;
font-size: 18.5px;
line-height: 1.5em;
}
/* Title */
#title {
margin-top: -1.5em;
padding: 1.0em 0.5em;
.label {
font-size: 0.65em;
max-width: 25em;
padding: 2.0em;
}
}
/* Side panels */
#tools_wrapper {
position: static;
}
.tools {
background-image: url("../img/text_bg.jpg");
position: relative;
width: auto;
}
#character_panel,
#info_panel,
#menu {
display: none;
}
#tools_wrapper {
display: block;
}
/* Main content */
#content_wrapper {
width: auto;
padding: 2.0em;
}
#content {
font-size: 16px;
line-height: 1.5em;
}
/* Toolbar and menu */
#toolbar {
position: fixed;
z-index: 300;
left: 0;
right: 0;
top: 0;
background: transparent url("../img/toolbar_bg.jpg") repeat-x top left;
height: 36px;
padding: 8px;
overflow: hidden;
box-shadow: 0 0 16px rgba(0,0,0,0.75);
h1 {
float: left;
font-weight: normal;
font-size: 22px;
margin: 8px 0 0 0;
padding: 0 10px;
color: #fc6;
text-shadow: 0 -1px 0 rgba(0,0,0,0.4);
}
.nav {
float: right;
margin: 0;
a {
font-size: 16px;
line-height: 20px;
color: white;
padding: 4px 16px;
float: right;
text-decoration: none;
text-shadow: 0 1px 0 rgba(0,0,0,0.4);
-webkit-border-radius: 6px;
background-image: -webkit-gradient(linear, left top, left bottom,
from(#C00), color-stop(0.45, #a00),
color-stop(0.55, #900), to(#900));
border: 2px solid #600;
}
}
}
#menu {
position: fixed;
top: 52px;
left: 0;
right: 0;
font-size: 16px;
background-image: url("../img/tools_bg.jpg");
z-index: 200;
list-style-type: none;
padding: 10px 0 0 0;
margin: 0;
opacity: 0.95;
box-shadow: 0 0 16px rgba(0,0,0,0.75);
li {
border-bottom: 1px solid rgba(0,0,0,0.25);
}
li:last-child {
border-bottom: none;
}
a {
display: block;
padding: 10px 20px;
}
}
}
.center-block {
margin: 0 auto;
}

26
less/main.less Normal file
View File

@ -0,0 +1,26 @@
@text-colour: rgba(0,0,0,0.9);
@anchor-colour: #D29506;
@option-colour: #125D79;
@background: lightgrey;
@text_background: #e6e6c6;
@color: black;
@links: #B68000;
@font-title: 'PT Sans Caption', "PT Sans", sans-serif;
@font-body: 'PT Sans', 'Open Sans', sans-serif;
@import "animations.less";
@import "layout.less";
.way {
color: darkred;
}
ul.options {
border: 1px solid #876;
li {
border-bottom: 1px solid #876;
}
li:hover {
background-color: rgba(153,136,119,0.2);
}
}

28
package.json Normal file
View File

@ -0,0 +1,28 @@
{
"dependencies": {
"undum": "git://github.com/oreolek/undum-commonjs#commonjs",
"raconteur": "git://github.com/sequitur/raconteur.git#stable",
"jquery": "^2.1.3",
"markdown-it": "^4.1.0",
"color": "^0.10.1"
},
"private": true,
"devDependencies": {
"babelify": "^6.0.2",
"browser-sync": "^2.6.0",
"browserify": "^9.0.8",
"browserify-shim": "^3.8.8",
"coffeeify": "^1.0.0",
"gulp": "^3.8.11",
"gulp-gzip": "^1.1.0",
"gulp-less": "^3.0.2",
"gulp-minify-css": "^1.0.0",
"gulp-uglify": "^1.2.0",
"gulp-util": "^3.0.4",
"gulp-zip": "^3.0.2",
"lodash": "^3.6.0",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0",
"watchify": "^3.1.0"
}
}